|
|
  |
Прерывание таймера Cortex-M3, Нужна помощь с организацией прерывания сравнения TC0 |
|
|
|
Dec 22 2014, 11:45
|
Группа: Новичок
Сообщений: 9
Регистрация: 18-12-14
Из: Саратов
Пользователь №: 84 179

|
Уважаемые специалисты, я имею некоторый опыт работы с AVR и теперь пытаюсь освоить ARM. У меня AT91SAM3X8E и IAR EWARM 7.3. Благодаря вашей помощи мне удалось продвинуться до рабочего проекта, переключающего состояние линии ввода-вывода, при последовательном опросе события сравнения "RC compare" таймера TC0. Теперь мне нужно заменить последовательный опрос прерыванием, но я пока не понимаю, как это сделать. На основании изучения немногочисленных примеров и документации у меня сложились такие представления: 1) нужно сказать TC0->TC_CHANNEL->TC_IER|=0x00000010; // Разрешено прерывание RC compare. 2) Нужно установить глобальный флаг маскируемых перрываний, используя, например, intrinsic функцию (-и) "__enable_interrupt();" и/или "__enable_fiq();" (не понимаю пока, какую или обе ) или ассемблерную вставку (типа asm("sei") в AVR) 3) Нужно написать функцию обработчик, причем пока совершенно не понимаю, как она оформляется. Найденные мной примеры инкапсулируют функционал и предлагают подключать некие библиотеки и действовать через функции в этих библиотеках, чего мне совсем не хотелось бы делать на этапе освоения железа. 4) Правильно ли я понимаю, что за всем блоком TC0 закреплено одно и только одно прерывание, а понять, какое конкретное событие (например, сравнение RC или переполнение), возбудило это прерывание можно только анализируя флаги в регистре статуса внутри обработчика этого прерывания? Если это так, то для всех ли ARM Cortex-Mxx это характерно? Привожу свой рабочий код, который нужно модифицировать: Код #define __SAM3X8E__ #include <iosam3xa.h>
#define BITON(X, N) (X)|=(1<<(N)) #define BITOFF(X, N) (X)&=~(1<<(N)) #define GETBIT(X, N) ((X&(1<<(N)))&&1)
void main(){ WDT->WDT_MR=0x00008FFF; //Выключить сторожевой таймер // Переключаемся на внешний кварц (12МГц) PMC->CKGR_MOR=0x0137FF01; // Включить внешний кварц 3-20 MHz (у нас 12 MHz) и выключить внутренний RC-генератор while (!(0x00000001&PMC->PMC_SR)); // Дождаться стабилизации кварца // Настройка ФАПЧ PMC->CKGR_PLLAR = 0x200A3F03; // MUL=10, DIV=3 PLLACLK= 12 MHz*(MUL+1)/DIV = 44 MHz (но потом делится пополам в PMC_MCKR - PLLADIV2=1) while (!(0x00000002&PMC->PMC_SR)); // Дождаться захвата ФАПЧ // Выбираем вход Master Clock - PLLA без деления. Установка в 2 этапа - сначала смена делителей, потом смена источника такта PMC->PMC_MCKR=0x00001001; // Выход Master Clock = Main Clock c делителем PLL на 2 (бит PLLADIV2=1) while (!(0x00000008&PMC->PMC_SR)); // Дождаться установки Master Clock PMC->PMC_MCKR=0x00001002; // Выход Master Clock = PLLA c делителем PLL на 2 (бит PLLADIV2=1) while (!(0x00000008&PMC->PMC_SR)); // Дождаться установки Master Clock PMC->PMC_PCER0=1<<13; // Тактировать PIOC PMC->PMC_PCER0=1<<27; // Тактировать TC0 PIOC->PIO_OER=1<<12; // PC12 as output pin PIOC->PIO_ODR=1<<13; // PC13 as input pin PIOC->PIO_PER=3<<12; // PC12, PC13 as PIO pins TC0->TC_CHANNEL->TC_RC=1000; // Загружаем регистр сравнения RC (11MHz/1000 =11000 раз в сек событие сравнения RC) TC0->TC_CHANNEL->TC_CMR=0x0000C4000; // Timer_Clock1 selected (MCK/2=11MHz), Waveform Enabled, Up mode with autotrigger (autoreset) on RC
while (GETBIT(PIOC->PIO_PDSR, 13)); // Ждем, пока не замкнута на GND тактовая кнопка на PC13 TC0->TC_CHANNEL->TC_CCR=0x00000005; //CLKEN=1, SWTRG=1 - RESET+Start while(1){ if (TC0->TC_CHANNEL->TC_SR&0x00000010){ // Если было сравнение с RC (флаг автоматически сбрасывается после чтения регистра) if (GETBIT(PIOC->PIO_ODSR, 12)) // переключаем состояние ноги PC12 PIOC->PIO_CODR=1<<12; else PIOC->PIO_SODR=1<<12; } } }
|
|
|
|
|
Dec 23 2014, 06:49
|
Группа: Новичок
Сообщений: 9
Регистрация: 18-12-14
Из: Саратов
Пользователь №: 84 179

|
Мужики, ну подскажите же кто-нибудь, неужели никто на IAR не писал прерывания?! Затык полный, брожу третьи сутки в 3 соснах. Не уверен, что понимаю, как объявлять обработчик. Судя по тому, что пишут (на сколько я понял), надо объявить функцию-обработчик со строго определенным названием, которое можно почерпнуть в некоем h-файле. Нашел в "sam3x8e.h" раздел "/* Peripherals handlers */", взял оттуда название, написал функцию, но не работает код. Может я неверно разрешаю прерывания? (Разрешить TC0_IRQn в NVIC + поставить флаг разрешения прерывания RC compare в регистрах TC0 + глобально разрешить прерывания - вроде я все выполнил). Мой неработающий код (настройки портов и тактирования правильные - проверил в режиме последовательного опроса таймера): Код #define __SAM3X8E__ #include <iosam3xa.h>
#define BITON(X, N) (X)|=(1<<(N)) #define BITOFF(X, N) (X)&=~(1<<(N)) #define GETBIT(X, N) ((X&(1<<(N)))&&1)
void TC0_Handler(void){ if (GETBIT(PIOC->PIO_ODSR, 12)) // переключаем состояние ноги PC12 PIOC->PIO_CODR=1<<12; else PIOC->PIO_SODR=1<<12; //NVIC_ClearPendingIRQ(TC0_IRQn); }
void main(){ WDT->WDT_MR=0x00008FFF; //Выключить сторожевой таймер // Переключаемся на внешний кварц (12МГц) PMC->CKGR_MOR=0x0137FF01; // Включить внешний кварц 3-20 MHz (у нас 12 MHz) и выключить внутренний RC-генератор while (!(0x00000001&PMC->PMC_SR)); // Дождаться стабилизации кварца // Настройка ФАПЧ PMC->CKGR_PLLAR = 0x200A3F03; // MUL=10, DIV=3 PLLACLK= 12 MHz*(MUL+1)/DIV = 44 MHz (но потом делится пополам в PMC_MCKR - PLLADIV2=1) while (!(0x00000002&PMC->PMC_SR)); // Дождаться захвата ФАПЧ // Выбираем вход Master Clock - PLLA без деления. Установка в 2 этапа - сначала смена делителей, потом смена источника такта PMC->PMC_MCKR=0x00001001; // Выход Master Clock = Main Clock c делителем PLL на 2 (бит PLLADIV2=1) while (!(0x00000008&PMC->PMC_SR)); // Дождаться установки Master Clock PMC->PMC_MCKR=0x00001002; // Выход Master Clock = PLLA c делителем PLL на 2 (бит PLLADIV2=1) while (!(0x00000008&PMC->PMC_SR)); // Дождаться установки Master Clock PMC->PMC_PCER0=1<<13; // Тактировать PIOC PMC->PMC_PCER0=1<<27; // Тактировать TC0 PIOC->PIO_OER=1<<12; // PC12 as output pin PIOC->PIO_ODR=1<<13; // PC13 as input pin PIOC->PIO_PER=3<<12; // PC12, PC13 as PIO pins TC0->TC_CHANNEL->TC_RC=1000; // Загружаем регистр сравнения RC (11MHz/1000 =11000 раз в сек событие сравнения RC) TC0->TC_CHANNEL->TC_CMR=0x0000C4000; // Timer_Clock1 selected (MCK/2=11MHz), Waveform Enabled, Up mode with autotrigger (autoreset) on RC
while (GETBIT(PIOC->PIO_PDSR, 13)); // Ждем, пока не замкнута на GND тактовая кнопка на PC13 TC0->TC_CHANNEL->TC_CCR=0x00000005; //CLKEN=1, SWTRG=1 - RESET+Start // Разрешить прерывания TC0->TC_CHANNEL->TC_IER|=0x00000010; // Разрешено прерывание RC compare NVIC_EnableIRQ(TC0_IRQn); // Разрешено прерывание TC0 в NVIC __enable_interrupt(); // Глобально разрешить маскируемые прерывания while(1){} }
|
|
|
|
|
Dec 23 2014, 06:56
|

Профессионал
    
Группа: Свой
Сообщений: 1 080
Регистрация: 16-11-04
Из: СПб
Пользователь №: 1 143

|
Цитата(karavaevas @ Dec 23 2014, 10:49)  Мужики, ну подскажите же кто-нибудь, неужели никто на IAR не писал прерывания?! Затык полный, брожу третьи сутки в 3 соснах. выложите свой ВЕСЬ проект. Или хотя бы файл что-то вроде sam3x8e.sдам должно быть типо (пример по LPC1778): Код __vector_table_0x1c ... DCD TIMER0_IRQHandler ... PUBWEAK TIMER0_IRQHandler SECTION .text:CODE:REORDER(1) TIMER0_IRQHandler B TIMER0_IRQHandler
--------------------
Марс - единственная планета, полностью населенная роботами (около 7 штук).
|
|
|
|
|
Dec 24 2014, 03:26
|
Группа: Новичок
Сообщений: 9
Регистрация: 18-12-14
Из: Саратов
Пользователь №: 84 179

|
Цитата(megajohn @ Dec 23 2014, 10:56)  выложите свой ВЕСЬ проект. Или хотя бы файл что-то вроде sam3x8e.sдам должно быть типо (пример по LPC1778): Код __vector_table_0x1c ... DCD TIMER0_IRQHandler ... PUBWEAK TIMER0_IRQHandler SECTION .text:CODE:REORDER(1) TIMER0_IRQHandler B TIMER0_IRQHandler Боюсь, что я весь выложил, больше ничего нет в проекте, все остальное по-умолчанию (ну еще настроил пути к папкам с h-файлами для SAM3X, которые идут в комплекте с IAR). Обнаружил в яровском "C/C++ Development Guide", что нужно взять за основу файл cstartup_M.c, который с IARом устанавливается, скопировать в папку с проектом, добавить в проект и в файле дописать в таблице векторов имена нужных обработчиков прерываний периферии. Файл нашел (привожу весь ниже), а вот как туда дописывать нужные имена и как их в основной программе использовать не понимаю. Там еще какую-то "__iar_program_start()" предлагают вызывать, она мне совершенно не нужна, а переход на нее в самом начале таблицы векторов описан. Тоже не понимаю, зачем мне это и можно ли ее выкинуть. Код /************************************************** * * This file contains an interrupt vector for Cortex-M written in C. * The actual interrupt functions must be provided by the application developer. * * Copyright 2007 IAR Systems. All rights reserved. * * $Revision: 66254 $ * **************************************************/
#pragma language=extended #pragma segment="CSTACK"
extern void __iar_program_start( void );
extern void NMI_Handler( void ); extern void HardFault_Handler( void ); extern void MemManage_Handler( void ); extern void BusFault_Handler( void ); extern void UsageFault_Handler( void ); extern void SVC_Handler( void ); extern void DebugMon_Handler( void ); extern void PendSV_Handler( void ); extern void SysTick_Handler( void );
typedef void( *intfunc )( void ); typedef union { intfunc __fun; void * __ptr; } intvec_elem;
// The vector table is normally located at address 0. // When debugging in RAM, it can be located in RAM, aligned to at least 2^6. // If you need to define interrupt service routines, // make a copy of this file and include it in your project. // The name "__vector_table" has special meaning for C-SPY, which // is where to find the SP start value. // If vector table is not located at address 0, the user has to initialize // the NVIC vector table register (VTOR) before using interrupts.
#pragma location = ".intvec" const intvec_elem __vector_table[] = { { .__ptr = __sfe( "CSTACK" ) }, __iar_program_start,
NMI_Handler, HardFault_Handler, MemManage_Handler, BusFault_Handler, UsageFault_Handler, 0, 0, 0, 0, SVC_Handler, DebugMon_Handler, 0, PendSV_Handler, SysTick_Handler
};
#pragma call_graph_root = "interrupt" __weak void NMI_Handler( void ) { while (1) {} } #pragma call_graph_root = "interrupt" __weak void HardFault_Handler( void ) { while (1) {} } #pragma call_graph_root = "interrupt" __weak void MemManage_Handler( void ) { while (1) {} } #pragma call_graph_root = "interrupt" __weak void BusFault_Handler( void ) { while (1) {} } #pragma call_graph_root = "interrupt" __weak void UsageFault_Handler( void ) { while (1) {} } #pragma call_graph_root = "interrupt" __weak void SVC_Handler( void ) { while (1) {} } #pragma call_graph_root = "interrupt" __weak void DebugMon_Handler( void ) { while (1) {} } #pragma call_graph_root = "interrupt" __weak void PendSV_Handler( void ) { while (1) {} } #pragma call_graph_root = "interrupt" __weak void SysTick_Handler( void ) { while (1) {} }
void __cmain( void ); __weak void __iar_init_core( void ); __weak void __iar_init_vfp( void );
#pragma required=__vector_table void __iar_program_start( void ) { __iar_init_core(); __iar_init_vfp(); __cmain(); }
|
|
|
|
|
Dec 24 2014, 18:13
|

Профессионал
    
Группа: Свой
Сообщений: 1 032
Регистрация: 13-03-08
Из: Маськва
Пользователь №: 35 877

|
Сразу предупреждаю, что IAR я видел лет 5 назад, и то это был IAR AVR ;-) Цитата(karavaevas @ Dec 24 2014, 06:26)  Обнаружил в яровском "C/C++ Development Guide", что нужно взять за основу файл cstartup_M.c, который с IARом устанавливается, скопировать в папку с проектом, добавить в проект и в файле дописать в таблице векторов имена нужных обработчиков прерываний периферии. Всё правильно, только одно НО: не надо дописывать типовой файл, надо поискать где-то рядом такой же файл, уже модифицированный под Ваш AT91SAM3X8E (или линейку AT91SAM3X.. ). Просто даташит подсказывает, что у этого контроллера 44 вектора прерываний. Аккуратно заполнить их все можно, но зачем эта обезьянья работа? У Keil'а, кстати, нужное Вам прерывание обзывается TC0_IRQHandler. И ещё. FIQ у Cortex'ов отсутствует, для разрешения прерываний (если их запретил стандартный стартап, опять же не знаю специфики IAR'а) нужен __enable_irq().
--------------------
Тут обсуждается творческий порыв, а не соответствие каким-либо стандартам ©
|
|
|
|
|
Dec 25 2014, 03:07
|
Группа: Новичок
Сообщений: 9
Регистрация: 18-12-14
Из: Саратов
Пользователь №: 84 179

|
Всем большое спасибо, с вашей помощью разобрался! Пришлось еще крепко почитать, но наводки здорово помогли, т.к. информацию пришлось собирать из разных источников на основе наводок (datasheet, programmers guide от IAR, книжка Ю Дж., тупое рассматривание содержимого файлов в папках IAR и т.п.  ) Оказалось: после ресета по умолчанию Cortex-M3 (и вроде бы M0 и M4 аналогично) ожидает предоставления информации в жестком формате: начиная с позиции со смещением 0 в памяти находится 32 битное слово - адрес стека, следующее слово - точка входа в программу, сразу за ним таблица векторов прерываний, которая включает для M3 сначала вектора предопределенных немаскируемых исключений (9 штук+ зарезервированные пропуски) потом несколько прерываний периферии, которых может быть до 240 в зависимости от конкретной микросхемы (в моей, действительно, 44 шт), включая нужное сейчас мне: Итого, я взял за основу cstartup_M.c, как советовали в мануале, потребовалось дописать объявления этих 44 функций (с любыми названиями) по образу и подобию исключений: Код #pragma language=extended #pragma segment="CSTACK" extern void NMI_Handler( void ); ... extern void TC0_IrqHandler( void ); /* 27 Timer Counter 0 */ ... #pragma call_graph_root = "interrupt" __weak void TC0_IrqHandler( void ) { while (1) {} } /* 27 Timer Counter 0 */ ... а от смущавшей меня "__iar_program_start()" я оставил в результате: Код void main( void ); #pragma required=__vector_table void __iar_program_start( void ){main();} //Просто вызываем main() Оказалось (откровением для меня) что точкой входа в программы для Cortex-M3 у IAR является в общем случае не "main()" а как раз функция "__iar_program_start()", которая содержит по умолчанию каркас для "правильного стиля программирования". Каркас успешно вымарался, чтобы не смущать нежную душу неофита и на мой вкус теперь все логично. В основной программе написал функцию "TC0_IrqHandler()", трясущую выводами PIO, которая перекрыла "слабое" (__weak) описание и активировал прерывание: Код TC0->TC_CHANNEL->TC_IER|=0x00000010; // Разрешено прерывание RC compare NVIC_EnableIRQ(TC0_IRQn); // Разрешено прерывание TC0 в NVIC __enable_interrupt(); // Глобально разрешить маскируемые прерывания Как обсуждалось на форуме, в обработчике потребовалось явно сбросить флаг события, вызвавшего прерывание в регистре статуса таймера и флаг отложенного прерывания "NVIC_ClearPendingIRQ(TC0_IRQn);" Заработало! Всем еще раз спасибо, удачи!
Сообщение отредактировал karavaevas - Dec 25 2014, 03:11
|
|
|
|
|
  |
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|