|
|
  |
Прерывания в linux, как написать обработчик |
|
|
|
Jan 29 2011, 15:21
|
Участник

Группа: Участник
Сообщений: 16
Регистрация: 29-01-11
Пользователь №: 62 560

|
Есть плата с мк AT91SAM9G20. На борту стоит linux. Пользуюсь компилятором toolchain openwrt. Перечитал на форумах много инфы, но конкретного не нашел. Я хочу сделать обработчик для таймера, который срабатывает в определенное время, так как linux не является системой с жестким контролем времени, а мне нужно выдавать сигнал строго в определенное время, то средства ОС не годятся. Привожу код: CODE #include <stdio.h> #include <stdlib.h> #include "at91sam9.h" #include <stdint.h> #include <stdlib.h> #include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/mman.h> #include <fcntl.h>
#define MAP_SIZE 4096Ul #define MAP_MASK (MAP_SIZE - 1) #define TIMER1_INTERRUPT_LEVEL 4 /*-----------------*/
/* Clock Selection */
/*-----------------*/
#define TC_CLKS 0x7
#define TC_CLKS_MCK2 0x0
#define TC_CLKS_MCK8 0x1
#define TC_CLKS_MCK32 0x2
#define TC_CLKS_MCK128 0x3
#define TC_CLKS_MCK1024 0x4 AT91S_TC *tc1; AT91S_AIC *aic_map(unsigned int aicbase) { int fd; void *base;
AT91S_AIC *aic;
off_t addr = aicbase;
if ((fd = open("/dev/mem", O_RDWR | O_SYNC)) == -1) { fprintf(stderr, "Cannot open /dev/mem.\n"); exit(EXIT_FAILURE); }
fprintf(stderr, "/dev/mem opened.\n");
base = mmap(0, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, addr & ~MAP_MASK);
if (base == (void *) -1) { fprintf(stderr, "Cannot open /dev/mem.\n"); exit(EXIT_FAILURE); }
fprintf(stderr, "Memory mapped at address %p.\n", base);
aic = base + (addr & MAP_MASK);
return aic; }
AT91S_TC *tc_map(unsigned int tcbase) { int fd; void *base;
AT91S_TC *tc;
off_t addr = tcbase;
if ((fd = open("/dev/mem", O_RDWR | O_SYNC)) == -1) { fprintf(stderr, "Cannot open /dev/mem.\n"); exit(EXIT_FAILURE); }
fprintf(stderr, "/dev/mem opened.\n");
base = mmap(0, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, addr & ~MAP_MASK);
if (base == (void *) -1) { fprintf(stderr, "Cannot open /dev/mem.\n"); exit(EXIT_FAILURE); }
fprintf(stderr, "Memory mapped at address %p.\n", base);
tc = base + (addr & MAP_MASK);
return tc; }
AT91S_PMC *pmc_map(unsigned int pmcbase) { int fd; void *base;
AT91S_PMC *pmc;
off_t addr = pmcbase;
if ((fd = open("/dev/mem", O_RDWR | O_SYNC)) == -1) { fprintf(stderr, "Cannot open /dev/mem.\n"); exit(EXIT_FAILURE); }
fprintf(stderr, "/dev/mem opened.\n");
base = mmap(0, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, addr & ~MAP_MASK);
if (base == (void *) -1) { fprintf(stderr, "Cannot open /dev/mem.\n"); exit(EXIT_FAILURE); }
fprintf(stderr, "Memory mapped at address %p.\n", base);
pmc = base + (addr & MAP_MASK);
return pmc; }
volatile int count_timer1_interrupt; volatile int y;
void timer1_c_irq_handler () //* Begin {
unsigned int dummy;
//* Acknowledge interrupt status
dummy = tc1->TC_SR;
//* Suppress warning variable "dummy" was set but never used
dummy = dummy;
count_timer1_interrupt++;
}
__inline void AT91F_PMC_EnablePeriphClock (
AT91PS_PMC pPMC, // \arg pointer to PMC controller
unsigned int periphIds) // \arg IDs of peripherals to enable
{
pPMC->PMC_PCER = periphIds;
}
void AT91F_TC_Open ( AT91PS_TC TC_pt,AT91PS_PMC pmc, unsigned int Mode, unsigned int TimerId)
//* Begin
{
unsigned int dummy;
//* First, enable the clock of the TIMER
AT91F_PMC_EnablePeriphClock ( pmc, 1<< TimerId ) ;
//* Disable the clock and the interrupts
TC_pt->TC_CCR = AT91C_TC_CLKDIS ;
TC_pt->TC_IDR = 0xFFFFFFFF ;
//* Clear status bit
dummy = TC_pt->TC_SR;
//* Suppress warning variable "dummy" was set but never used
dummy = dummy;
//* Set the Mode of the Timer Counter
TC_pt->TC_CMR = Mode ;
//* Enable the clock
TC_pt->TC_CCR = AT91C_TC_CLKEN ;
//* End
} __inline unsigned int AT91F_AIC_ConfigureIt (
AT91PS_AIC pAic, // \arg pointer to the AIC registers
unsigned int irq_id, // \arg interrupt number to initialize
unsigned int priority, // \arg priority to give to the interrupt
unsigned int src_type, // \arg activation and sense of activation
void (*newHandler) () ) // \arg address of the interrupt handler
{
unsigned int oldHandler;
unsigned int mask ;
oldHandler = pAic->AIC_SVR[irq_id];
mask = 0x1 << irq_id ;
//* Disable the interrupt on the interrupt controller
pAic->AIC_IDCR = mask ;
//* Save the interrupt handler routine pointer and the interrupt priority
pAic->AIC_SVR[irq_id] = (unsigned int) newHandler ;
//* Store the Source Mode Register
pAic->AIC_SMR[irq_id] = src_type | priority ;
//* Clear the interrupt on the interrupt controller
pAic->AIC_ICCR = mask ;
return oldHandler;
} __inline void AT91F_AIC_EnableIt (
AT91PS_AIC pAic, // \arg pointer to the AIC registers
unsigned int irq_id ) // \arg interrupt number to initialize
{
//* Enable the interrupt on the interrupt controller
pAic->AIC_IECR = 0x1 << irq_id ;
}
void timer_init(AT91PS_TC tc,AT91PS_AIC aic,AT91PS_PMC pmc,unsigned int mode,unsigned int timer_id) {
//init the timer interrupt counter
count_timer1_interrupt=0;
//* Open timer
AT91F_TC_Open(tc,pmc,mode,timer_id);
//* Open Timer interrupt
unsigned int old= AT91F_AIC_ConfigureIt ( aic, timer_id, TIMER1_INTERRUPT_LEVEL,AT91C_AIC_SRCTYPE_EXT_HIGH_LEVEL, timer1_c_irq_handler);
tc->TC_IER = AT91C_TC_CPCS; // IRQ enable CPC
AT91F_AIC_EnableIt (aic, timer_id);
//* Start timer1
tc->TC_CCR = AT91C_TC_SWTRG ;
}
int main() {
AT91S_AIC *aic; AT91S_PMC *pmc;
tc1=tc_map(AT91C_BASE_TC1); aic=aic_map(AT91C_BASE_AIC); pmc=pmc_map(AT91C_BASE_PMC);
timer_init(tc1,aic,pmc,TC_CLKS_MCK2,AT91C_ID_TC1);
int i=0; for( i=0;i<10000000;i++);
printf("\n%d\n",count_timer1_interrupt);
return 0; } ................................................................................ ..........
При выполнении этой команды //* Start timer1 tc->TC_CCR = AT91C_TC_SWTRG ; Запускается таймер. Но тут начинается бардак. В терминале начинает выдаватся сообщение о том, что пришло прерывание, обработчик которому не задан.: handle_irq() irq_num= номер desc=какой-то номер chip() и тд. Вывод я сделал такой: контроллер прерываний я запрограмировал правильно, таймер тоже. Но сам обработчик видно не разместился по адресу. Еще такое если старый обработчик не трогать то все работает, просто не делаются те действия, которые я хочу. Я думаю что все те примеры в Keil относятся к тем системам где нет ОС, и тогда все рабоотает норм, так как используется линейная физическая адресация памяти, а у меня используется виртуальная. Вот и вопрос как заменить обработчик на свой. Был еще вариант сделать модуль я дра, но тоже не могу найти примеров как это делается , подскажите кто сможет ,,, спс
Сообщение отредактировал IgorKossak - Jan 29 2011, 20:17
|
|
|
|
|
Jan 29 2011, 21:26
|
Знающий
   
Группа: Участник
Сообщений: 783
Регистрация: 22-11-08
Пользователь №: 41 858

|
Цитата(const3 @ Jan 29 2011, 18:21)  хочу сделать обработчик для таймера, который срабатывает в определенное время, так как linux не является системой с жестким контролем времени, а мне нужно выдавать сигнал строго в определенное время, то средства ОС не годятся. Если средства ОС не годятся - зачем Вы выбрали для своих задач именно эту ОС ? Цитата Привожу код: Что тут сказать - это ужасно  и никогда работать не будет. Если вам нужно обрабатывать прерывания в юзерспейс посмотрите для начала презентацию Using UIO in an embedded platform Katsuya MATSUBARA Igel Co., Ltd Hisao MUNAKATA Renesas Solution Corp. легко ищется в гугле, но одно могу сказать - вы таким образом реалтайма не добьетесь, латентность прерываний никуда не исчезнет.
|
|
|
|
|
Jan 30 2011, 00:28
|
Участник

Группа: Участник
Сообщений: 16
Регистрация: 29-01-11
Пользователь №: 62 560

|
Извините за оформление. Просто первый раз создал тему на этом форуме. ОС я не выбирал, а она стояла уже. Мне дали такое задание разобратся с этим контроллером в универе. Никаких примеров не дали. Спасибо за совет. Буду читать. Код работать не будет? Для контроллера GPIO работает. И еще, если реального времени не добьюсь, а как это сделать посоветуйте. Я хочу сделать видеоадаптер, для начала просто нужно выводить синхроимпульсы и белый экран.
|
|
|
|
|
Jan 30 2011, 10:14
|
Знающий
   
Группа: Участник
Сообщений: 783
Регистрация: 22-11-08
Пользователь №: 41 858

|
Цитата(const3 @ Jan 30 2011, 03:28)  ОС я не выбирал, а она стояла уже. Мне дали такое задание разобратся с этим контроллером в универе. Никаких примеров не дали На сайте atmel достаточно информации в примерах разной степени паршивости как работать с их контроллерами. Цитата Код работать не будет? Для контроллера GPIO работает. Доступ к регистрам через mmap работает - кто же тут спорит, а то ка вы пытаетесь вынести обработчик прерывания напрямую в пользовательский процесс никогда не заработает в linux. Цитата как это сделать посоветуйте. Я хочу сделать видеоадаптер Тут могу только посоветовать не заниматься ерундой  Полезные ссылки по linux (в частности там можно найти презентации по некоторым ключевым моментам) http://kernelnewbies.org/FAQhttp://free-electrons.com/docs/
|
|
|
|
|
Jan 31 2011, 10:28
|

developer
   
Группа: Свой
Сообщений: 902
Регистрация: 12-04-06
Из: Казань
Пользователь №: 16 032

|
Цитата(const3 @ Jan 30 2011, 03:28)  Извините за оформление. Просто первый раз создал тему на этом форуме. ОС я не выбирал, а она стояла уже. Мне дали такое задание разобратся с этим контроллером в универе. Никаких примеров не дали. Так вам разобраться с контроллером или ОСью было задание? Если с контроллером, то зачем вам ОСь вообще? А видеоадаптер без Linux разве не может быть реализован?
--------------------
Все может быть и быть все может, и лишь того не может быть-чего уж точно быть не может, хотя..и это может быть.
|
|
|
|
|
Jan 31 2011, 12:47
|
Участник

Группа: Участник
Сообщений: 16
Регистрация: 29-01-11
Пользователь №: 62 560

|
Linux стоит потому что там много разной периферии таких как ethernet, usb и т.д. Адаптер я буду делать простой, который отображает память на экран монитора. Адаптер будет сделан на ПЛИС Altera MAX II. Я уже написал проект в quartus и просимулировал его, пока все работает, но жду пока мне спаяяют настоящюю плату. Так как это магистерская работа, я должен доказать, почему выгоднее использовать плис чем мк. А потому что если использовать МК, то все ресурсы будут уходить на вывод графики. Теперь пока я жду плату и мне нужно что-то делать, пытаюсь делать что -то с мк. Сначала я пробовал выводить сигнал на пины как показано на форумах, по AVR, оно не работало, я понял что не соблюдается синхронизация. Так как под рукой у меня нету осцилографа, и нет возможности проверить тайминг, я хочу реализовать это все по прерываниям. Тут мне на форуме сказали что это невозможно, но все равно это хорошо, потому что будет что написать в магистерской. Например: реализовать в ОС синхронизацию адаптера нельзя, потому что ... Почему я взял этот мк, да потому что такое у меня задание. Извини те за воду, но я описал просто как и почему я задаю такие вопросы.
|
|
|
|
|
Jan 31 2011, 12:54
|
Местный
  
Группа: Участник
Сообщений: 406
Регистрация: 1-03-06
Пользователь №: 14 821

|
Цитата(const3 @ Jan 30 2011, 01:28)  И еще, если реального времени не добьюсь, а как это сделать посоветуйте. прерывание,"реальное" время, Вам вниз уходить надо - Кернел.
|
|
|
|
|
Feb 11 2011, 19:45
|
Участник

Группа: Участник
Сообщений: 16
Регистрация: 29-01-11
Пользователь №: 62 560

|
Спасибо всем еще раз. Наконец то у меня получилось сделать ,то что я хотел. Точнее настроить прерывания в linux. Читал много информации в интернете по программированию модулей ядра, и уже кое-что начало получаться. Хочу поделится скромным опытом для тех кому-будет интересно. Полезные книги: Linux Device Drivers O'Reily Understanding the Linux Kernel O'Reily Ссылки: http://rus-linux.net/lib.php?name=/MyLDP/indexhttp://www.at91.com/forumВот код который настраивает прерывание от таймера 0 для мк AT91xxxx CODE #include <linux/module.h> #include <linux/fs.h> #include <linux/cdev.h> #include <linux/clk.h> #include <linux/irq.h> #include <linux/interrupt.h> #include <mach/at91_tc.h> #include <asm/uaccess.h>
struct file_operations at91adc_fops;
void __iomem *at91tc0_base; struct clk *at91tc0_clk;
/******************************************************************************** ********* | Timer counter 0 ISR: ******************************************************************************** *********/ static int status; static int y=0; static irqreturn_t at91tc0_isr (int irq, void *dev_id) { // Read TC0 status register to reset RC compare status. status = ioread32(at91tc0_base + AT91_TC_SR); if(y<1000000) y++;else y=0; return IRQ_HANDLED; }
int init_module() { int ret=0; // Initialize Timer Counter module 0. The following two lines set the appropriate // PMC bit for TC0. Easier than mapping PMC registers and then setting the bit. at91tc0_clk = clk_get(NULL, // Device pointer - not required. "tc0_clk"); // Clock name. clk_enable(at91tc0_clk);
// Map TC0 registers to the current address space. at91tc0_base = ioremap_nocache(AT91SAM9260_BASE_TC0, // Physical address 64); // Number of bytes to be mapped. if (at91tc0_base == NULL) { printk(KERN_INFO "at91adc: TC0 memory mapping failed\n"); ret = -EACCES; return ret; } // Configure TC0 in waveform mode, TIMER_CLK1 and to generate interrupt on RC compare. // Load 50000 to RC so that with TIMER_CLK1 = MCK/2 = 50MHz, the interrupt will be // generated every 1/50MHz * 50000 = 20nS * 50000 = 1 milli second. // NOTE: Even though AT91_TC_RC is a 32-bit register, only 16-bits are programmble.
iowrite32(50000, (at91tc0_base + AT91_TC_RC)); iowrite32((AT91_TC_WAVE | AT91_TC_WAVESEL_UP_AUTO), (at91tc0_base + AT91_TC_CMR)); iowrite32(AT91_TC_CPCS, (at91tc0_base + AT91_TC_IER));
// Install interrupt for TC0. ret = request_irq(AT91SAM9260_ID_TC0, // Interrupt number at91tc0_isr, // Pointer to the interrupt sub-routine 0, // Flags - fast, shared or contributing to entropy pool "at91adc", // Device name to show as owner in /proc/interrupts NULL); // Private data for shared interrupts if (ret != 0) { printk(KERN_INFO "at91adc: Timer interrupt request failed\n"); ret = -EBUSY; return ret; } iowrite32((AT91_TC_SWTRG | AT91_TC_CLKEN), (at91tc0_base + AT91_TC_CCR));//timer start
return 0;
} void cleanup_module() {
clk_disable(at91tc0_clk);
// Free TC0 IRQ. free_irq(AT91SAM9260_ID_TC0, // Interrupt number NULL); // Private data for shared interrupts
iounmap(at91tc0_base);
printk(KERN_INFO "at91adc: Unloaded module\n"); }
MODULE_LICENSE("GPL"); Для роботы с таймером используются порты отображенные в память. Для того чтобы получить к ним доступ нужно проделать команду маппинга: CODE at91tc0_base = ioremap_nocache(AT91SAM9260_BASE_TC0, // Physical address 64); // Number of bytes to be mapped. Для чтения и записи в ячейки используют команды: CODE iowrite32(int value, (void*)address); int read32((void*)address); Для более подробной информации об этих операциях смотрите файл <asm/io.h> Сначала настраивается таймер: CODE // Configure TC0 in waveform mode, TIMER_CLK1 and to generate interrupt on RC compare. // Load 50000 to RC so that with TIMER_CLK1 = MCK/2 = 50MHz, the interrupt will be // generated every 1/50MHz * 50000 = 20nS * 50000 = 1 milli second. // NOTE: Even though AT91_TC_RC is a 32-bit register, only 16-bits are programmble.
iowrite32(50000, (at91tc0_base + AT91_TC_RC)); iowrite32((AT91_TC_WAVE | AT91_TC_WAVESEL_UP_AUTO), (at91tc0_base + AT91_TC_CMR)); iowrite32(AT91_TC_CPCS, (at91tc0_base + AT91_TC_IER));
Потом устанавливается обработчик прерываний: CODE // Install interrupt for TC0. ret = request_irq(AT91SAM9260_ID_TC0, // Interrupt number at91tc0_isr, // Pointer to the interrupt sub-routine 0, // Flags - fast, shared or contributing to entropy pool "at91adc", // Device name to show as owner in /proc/interrupts NULL); // Private data for shared interrupts if (ret != 0) { printk(KERN_INFO "at91adc: Timer interrupt request failed\n"); ret = -EBUSY; return ret; } Обработчик по требованиям должен выполнять минимум операций: подтверждение прерывания, и регистрация нижней части обработчика(если нужно ищите Bottom half). Нижняя часть вызывается ядром в удобный момент, ставятся очереди и т.п. Но в данном примере я просто инкрементирую счетчик и все , чтобы проверить работоспособность. CODE static irqreturn_t at91tc0_isr (int irq, void *dev_id) { // Read TC0 status register to reset RC compare status. status = ioread32(at91tc0_base + AT91_TC_SR); if(y<1000000) y++;else y=0; return IRQ_HANDLED; }
Вот и все. В модуле Makefile : CODE MODULENAME := name obj-m += $(MODULENAME).o
KERNELDIR=/path/to/uncompressed/kernel
all:$(MODULENAME).ko
$(MODULENAME).ko:$(MODULENAME).c make -C $(KERNELDIR) SUBDIRS=$(PWD) modules CC=arm-linux-gcc LD=arm-linux-ld ARCH=arm cp $(MODULENAME).ko /home/kostya/kernel_modules .PHONY clean: make -C $(KERNELDIR) M=$(PWD) clean
Максимально что я достиг ,это срабатывание раз в 10мкс, и то терминал уже тормозил. Теперь буду делать синхро сигналы. И выводить данные. P.S: Хочу спросить. Напряжение питания микроконтроллера 3.3В, если напрямую подсоединить к монитору, тоесть припаять к контактам vga разьема все сигналы будет ли работать? ну например амплитуда r,g,b от 0 до +5В и 3.3В будет показывать какой-то цвет, ну это неважно, можно будет что то увидеть. Мне интересно какое напряжение у HSYNC и VSYNC,5В? Или можно и 3.3?
Сообщение отредактировал const3 - Feb 11 2011, 19:47
|
|
|
|
|
  |
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|