реклама на сайте
подробности

 
 
 
Reply to this topicStart new topic
> Прерывания в linux, как написать обработчик
const3
сообщение Jan 29 2011, 15:21
Сообщение #1


Участник
*

Группа: Участник
Сообщений: 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
Go to the top of the page
 
+Quote Post
IgorKossak
сообщение Jan 29 2011, 20:18
Сообщение #2


Шаман
******

Группа: Модераторы
Сообщений: 3 064
Регистрация: 30-06-04
Из: Киев, Украина
Пользователь №: 221



Научитесь оформлять код. В следующий раз удалю без предупреждения.
Модератор
Go to the top of the page
 
+Quote Post
sasamy
сообщение Jan 29 2011, 21:26
Сообщение #3


Знающий
****

Группа: Участник
Сообщений: 783
Регистрация: 22-11-08
Пользователь №: 41 858



Цитата(const3 @ Jan 29 2011, 18:21) *
хочу сделать обработчик для таймера, который срабатывает в определенное время, так как linux не является системой с жестким контролем времени, а мне нужно выдавать сигнал строго в определенное время, то средства ОС не годятся.


Если средства ОС не годятся - зачем Вы выбрали для своих задач именно эту ОС ?

Цитата
Привожу код:


Что тут сказать - это ужасно sm.gif и никогда работать не будет. Если вам нужно обрабатывать прерывания в юзерспейс посмотрите для начала презентацию

Using UIO in an embedded platform
Katsuya MATSUBARA Igel Co., Ltd Hisao MUNAKATA Renesas Solution Corp.

легко ищется в гугле, но одно могу сказать - вы таким образом реалтайма не добьетесь, латентность прерываний никуда не исчезнет.
Go to the top of the page
 
+Quote Post
const3
сообщение Jan 30 2011, 00:28
Сообщение #4


Участник
*

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



Извините за оформление. Просто первый раз создал тему на этом форуме.
ОС я не выбирал, а она стояла уже. Мне дали такое задание разобратся с этим контроллером в универе. Никаких примеров не дали. Спасибо за совет. Буду читать. Код работать не будет? Для контроллера GPIO работает. И еще, если реального времени не добьюсь, а как это сделать посоветуйте. Я хочу сделать видеоадаптер, для начала просто нужно выводить синхроимпульсы и белый экран.
Go to the top of the page
 
+Quote Post
sasamy
сообщение Jan 30 2011, 10:14
Сообщение #5


Знающий
****

Группа: Участник
Сообщений: 783
Регистрация: 22-11-08
Пользователь №: 41 858



Цитата(const3 @ Jan 30 2011, 03:28) *
ОС я не выбирал, а она стояла уже. Мне дали такое задание разобратся с этим контроллером в универе. Никаких примеров не дали


На сайте atmel достаточно информации в примерах разной степени паршивости как работать с их контроллерами.

Цитата
Код работать не будет? Для контроллера GPIO работает.


Доступ к регистрам через mmap работает - кто же тут спорит, а то ка вы пытаетесь вынести обработчик прерывания напрямую в пользовательский процесс никогда не заработает в linux.

Цитата
как это сделать посоветуйте. Я хочу сделать видеоадаптер


Тут могу только посоветовать не заниматься ерундой sm.gif
Полезные ссылки по linux (в частности там можно найти презентации по некоторым ключевым моментам)
http://kernelnewbies.org/FAQ
http://free-electrons.com/docs/
Go to the top of the page
 
+Quote Post
dimka76
сообщение Jan 31 2011, 10:28
Сообщение #6


developer
****

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



Цитата(const3 @ Jan 30 2011, 03:28) *
Извините за оформление. Просто первый раз создал тему на этом форуме.
ОС я не выбирал, а она стояла уже. Мне дали такое задание разобратся с этим контроллером в универе. Никаких примеров не дали.


Так вам разобраться с контроллером или ОСью было задание?
Если с контроллером, то зачем вам ОСь вообще?

А видеоадаптер без Linux разве не может быть реализован?


--------------------
Все может быть и быть все может, и лишь того не может быть-чего уж точно быть не может, хотя..и это может быть.
Go to the top of the page
 
+Quote Post
const3
сообщение Jan 31 2011, 12:47
Сообщение #7


Участник
*

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



Linux стоит потому что там много разной периферии таких как ethernet, usb и т.д. Адаптер я буду делать простой, который отображает память на экран монитора. Адаптер будет сделан на ПЛИС Altera MAX II. Я уже написал проект в quartus и просимулировал его, пока все работает, но жду пока мне спаяяют настоящюю плату. Так как это магистерская работа, я должен доказать, почему выгоднее использовать плис чем мк. А потому что если использовать МК, то все ресурсы будут уходить на вывод графики. Теперь пока я жду плату и мне нужно что-то делать, пытаюсь делать что -то с мк. Сначала я пробовал выводить сигнал на пины как показано на форумах, по AVR, оно не работало, я понял что не соблюдается синхронизация. Так как под рукой у меня нету осцилографа, и нет возможности проверить тайминг, я хочу реализовать это все по прерываниям. Тут мне на форуме сказали что это невозможно, но все равно это хорошо, потому что будет что написать в магистерской. Например: реализовать в ОС синхронизацию адаптера нельзя, потому что ...
Почему я взял этот мк, да потому что такое у меня задание. Извини те за воду, но я описал просто как и почему я задаю такие вопросы.
Go to the top of the page
 
+Quote Post
andron86
сообщение Jan 31 2011, 12:54
Сообщение #8


Местный
***

Группа: Участник
Сообщений: 406
Регистрация: 1-03-06
Пользователь №: 14 821



Цитата(const3 @ Jan 30 2011, 01:28) *
И еще, если реального времени не добьюсь, а как это сделать посоветуйте.

прерывание,"реальное" время, Вам вниз уходить надо - Кернел.
Go to the top of the page
 
+Quote Post
AVR
сообщение Feb 7 2011, 21:07
Сообщение #9


фанат Linux'а
*****

Группа: Свой
Сообщений: 1 353
Регистрация: 23-10-05
Из: SPB.RU
Пользователь №: 10 008



Я правильно понимаю, что автор темы собирается при помощи GPIO генерировать видеосигнал? Я конечно слышал что делают PAL/VGA/USB на AVRках, но там выверяют каждый такт и 100% под одну задачу.

Задача и в правду не реализуема, не будет синхронизации по вполне очевидным причинам.
Разве что кадр в аппаратную реализацию подобного адаптера гнать через небольшой буферчик, если у него памяти мало, я такие видел...


--------------------
Go to the top of the page
 
+Quote Post
Allregia
сообщение Feb 8 2011, 13:54
Сообщение #10


Профессионал
*****

Группа: Свой
Сообщений: 1 047
Регистрация: 28-06-07
Из: Israel
Пользователь №: 28 763



Цитата(AVR @ Feb 7 2011, 23:07) *
Я правильно понимаю, что автор темы собирается при помощи GPIO генерировать видеосигнал? Я конечно слышал что делают PAL/VGA/USB на AVRках, но там выверяют каждый такт и 100% под одну задачу.


С ARM/Cortex проще - гнать строку через DMA в SPI после ССИ, т.о. во время прямого хода процессор может заниматься другими делами.
Линух тут ясное дело ни причем, или лезть на самое его дно.
Go to the top of the page
 
+Quote Post
const3
сообщение Feb 11 2011, 19:45
Сообщение #11


Участник
*

Группа: Участник
Сообщений: 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/index
http://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
Go to the top of the page
 
+Quote Post

Reply to this topicStart new topic
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0

 


RSS Текстовая версия Сейчас: 18th July 2025 - 14:04
Рейтинг@Mail.ru


Страница сгенерированна за 0.01511 секунд с 7
ELECTRONIX ©2004-2016