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

 
 
> Прерывания в 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
 
Start new topic
Ответов
const3
сообщение Feb 11 2011, 19:45
Сообщение #2


Участник
*

Группа: Участник
Сообщений: 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 Текстовая версия Сейчас: 22nd July 2025 - 16:13
Рейтинг@Mail.ru


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