Перейти к содержанию
    

LPC2368 Прерывание по совпадению таймера, помогите

Всем здравствуйте!

Помогите, пожалуйста, разобраться с прерыванием от таймера0 по совпадению.

Только начал осваивать 32 разрядные микроконтроллеры. Есть отладочная плата SK-MLPC1768/2387/2368, с установленным LPC2368.

Для обучения пользуюсь IAR 7.50.2.10505

main.txt

Вставлял ловушку в бесконечном цикле и проверял 0 бит регистра T0IR. Видно что таймер входит в прерывание (бит устанавливается), но дальше действий программа никаких не выполняет, а должна быть бегущая единица.

Как должна работать эта программа ясно, но почему не работает не понятно.

Исходник взят из интернета, кроме настройки PLL. PLL настраивается. Проверялось.

Подскажите, направьте пожалуйста!

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

1.Устанавливаете запрос синхронного сброса

T0TCR = 0x00000002;

но не дожидаетесь его окончания. Значит сброс у Вас реально может происходить после последующих записей конфигурации.

2.Не чистите регистр флагов запросов прерываний.

3.Неправильно рассчитываете период:

//Регистр совпадения = 1000 (1 Гц)
T0MR0 = 100;

Как можно было сделать столько ошибок в такой простейшей периферии???

Читайте внимательнее юзермануал!

Да и в CTCR неплохо бы соотв. значение записать.

После этого смотрите появляется ли флаг активного запроса прерывания в IR?

И если появляется, а входа в ISR нет - читайте про контроллер прерываний и всё что с ним связано.

 

По контроллеру прерываний:

1.А где у Вас глобальное разрешение прерываний IRQ? (Вы ведь вроде этому прерыванию от таймера назначаете линию IRQ?)

2.А где обработчик прерывания IRQ?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Читайте внимательнее юзермануал!

Ну Вы и не ленивый читать галиматью написанную в силе "= 0x00000002;" :) Я лично не стал.

Ну в Автору большой совет - если хотите что бы кто-то читал написанное Вами и разбирался - НЕ ПИШИТЕ в том "стиле", как написали. Все биты и константы должны иметь ИМЕНА.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Ну Вы и не ленивый читать галиматью написанную в силе "= 0x00000002;" :) Я лично не стал.

Я прочитал просто потому, что много работал с LPC-шной периферией и помню почти наизусть её, тем более таймера - почти самое простое там.

Тем более они не изменились при переходе на Cortex LPC17xx/LPC43xx.

Так бы да - и читать бы не стал.

 

Все биты и константы должны иметь ИМЕНА.

Насчёт первого (битов в конфигурационных регистрах периферии) не совсем с Вами согласен.

Как правило, когда прописываешь их значения в какой-то регистр, всегда перед глазами открытая страничка с описанием этого регистра.

И мне например удобнее соотносить описания полей в мануале с исходником по номерам битов, а не их именам (которых может и не быть или быть неудобными).

Так что именно для битовых полей в регистрах конфигурации периферии я отступаю от этого правила, и чаще всего прописываю так:

T0MCR = B0 | B1;

А точнее даже так:

concat(T, nTIM_nrf, MCR) = B0 | B1;

ну или через указатель.

Так как все назначения номеров таймеров, портов UART/SPI/... и пр. периферии у меня собраны в одном месте и нигде нет прямых ссылок на регистры периферии:

//Распределение UART-ов
#define nUART_service 3   //номер UART сервисного порта
//Распределение GP-таймеров
#define nTIM_cpu      0   //таймер коротких интервалов
#define nTIM_intmon   1   //таймер контролЯ длительности запретов прерываний (длЯ отладки)
#define nTIM_nrf      2   //таймер драйвера nRF24L01+
//Распределение SSP-портов
#define nSSP_nrf      0   //nRF24L01+

где concat - это текстовая склейка :

#define concatVOID
#define concat_SUBST2(a, b, c, d, e, f, g, h, i, j, k, l, ...) \
        a##b##c##d##e##f##g##h##i##j##k##l
#define concat_SUBST(...) concat_SUBST2(__VA_ARGS__)
#define concat(...) concat_SUBST(__VA_ARGS__, concatVOID, concatVOID, \
        concatVOID, concatVOID, concatVOID, concatVOID, concatVOID, concatVOID, \
        concatVOID, concatVOID)

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Я прочитал просто потому, что много работал с LPC-шной периферией и помню почти наизусть её, тем более таймера - почти самое простое там.

Тем более они не изменились при переходе на Cortex LPC17xx/LPC43xx.

Так бы да - и читать бы не стал.

Я тоже работал и работаю и 34xx и 17хх тоже помню многое, но помню ИМЕНА а не "второй бит справа". Более того, что значит "второй бит справа" если и знал, то в тот краткий миг, когда его описывал.

Никаких особых дополнительных наворотов в описании не использую.

Внешне получается где-то так:

//---------------------------------------------------------------------------
void init_mrt_timers(void)
{
// Enable MRT clock
SYSAHBCLKCTRL1 |= CTRL1_MRT;
// Peripheral reset control to MRT, a "1" bring it out of reset.
PRESETCTRL1 |=  ( CTRL1_MRT );
PRESETCTRL1 &= ~( CTRL1_MRT );

.......

MRT_INTVAL0 = 0;
MRT_INTVAL0 = MRT_INTVAL_LOAD;
 	MRT_CTRL0   = MRT_CTRL_ONE_SHOT|MRT_CTRL_INTEN;

VIC_EnableIRQ( MRT_IRQn );
}
.....
//---------------------------------------------------------------------------
void vlfx_tx_timeout_start( bint slots )
{
MRT_INTVAL0 = ((((CPU_CLOCK_HZ/CC1000_BAUD)*10) + 1)*slots);
MRT_INTVAL0 |= MRT_INTVAL_LOAD;

}

 

В случае, когда более-менее универсальный исходник планируется, тогда еще, да, можно алиасы назначить:

#define	TsPR_SYS  		T1PR
#define	TsIR_SYS			T1IR
#define	TsMR0_SYS 		T1MR0
#define	TsMCR_SYS 		T1MCR
#define	TsTCR_SYS 		T1TCR
#define	TsTC_SYS 		T1TC
#define	VIC_TIMERs_SYS	VIC_TIMER1

//---------------------------------------------------------------------------
static void init_systimer( bint ext_source )
{
ulong compare_match;

// Tick does not require the use of the timer prescale.  This is
// defaulted to zero but can be used if necessary
TsPR_SYS = 0;
// Calculate the match value required for our wanted tick rate
compare_match = CPU_CLOCK_HZ / BUS_DIVIDER / SYS_TICK_RATE_HZ;
TsMR0_SYS = compare_match - 1;
// Generate tick with timer compare match
TsMCR_SYS = (MCR_MR0INT | MCR_MR0RES);
TsTCR_SYS = 0;

if( ext_source )
{   // External source
}
else
{	// Start the timer
#if( configUSE_PREEMPTION == 1 )
	extern void ( vPortPreemptiveTickEntry )( void );
	install_irq( VIC_TIMERs_SYS, (void *)vPortPreemptiveTickEntry, VIC_SYSTEM_PRIOR, TRUE );
#else
	extern void ( vNonPreemptiveTick )( void );
	install_irq( VIC_TIMERs_SYS, (void *)vNonPreemptiveTick, VIC_SYSTEM_PRIOR, TRUE );

#endif
	TsTCR_SYS = TCR_ENABLE;
}
}

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

#define TsPR_SYS T1PR

#define TsIR_SYS T1IR

Мой вариант лучше, так как:

1. Короче - всего по одной строчке на каждый экземпляр периферии.

2. Позволяет связывать данным именем не только регистры периферии, но и биты в регистрах NVIC, регистрах разрешения тактирования периферии, адрес вектора прерывания.

Т.е. - я могу просто всего в одном месте изменить номера

#define nTIM_cpu      0   //таймер коротких интервалов
#define nTIM_intmon   1   //таймер контролЯ длительности запретов прерываний (длЯ отладки)
#define nTIM_nrf      2   //таймер драйвера nRF24L01+

скажем на:

#define nTIM_cpu      2   //таймер коротких интервалов
#define nTIM_intmon   0   //таймер контролЯ длительности запретов прерываний (длЯ отладки)
#define nTIM_nrf      1   //таймер драйвера nRF24L01+

и всё! Больше ничего в исходнике ни в одном месте трогать не надо - все конфигурирования битов в регистрах NVIC, регистрах SYSCFG.PCONP и вектора прерывания переедут на новые места соответствующие новому распределению периферии.

Типичный инит таймера у меня выглядит примерно так:

PeripheralPowerOn(concat(PERIPH_PCTIM, nTIM_nrf));	  //устанавливает нужный бит SYSCFG.PCONP
volatile HwRegsTIMER *p = &concat(TIMER, nTIM_nrf);
p->TCR = B1;
p->TC = p->PC = p->PR = p->CCR = p->CTCR = 0;
p->MCR = B0 | B1 | B2;
p->IR = p->IR;
IntClrEna(concat(NVIC_TIMER, nTIM_nrf));  //чистит нужный бит регистре активных запросов прерываний и устанавливает такой же бит в маске разрешения прерываний

Таблица назначения приоритетов INT-ов:

  static u8 const priority[] = { //список INT-ов по убыванию приоритетов
    concat(NVIC_TIMER, nTIM_intmon), 0,
    NVIC_ETHERNET, NVIC_GPDMA, concat(NVIC_SSP, nSSP_nrf),
    concat(NVIC_TIMER, nTIM_nrf), 0,
    concat(NVIC_UART, nUART_service), 0, 0
  };

Фрагмент asm-файла описания таблицы векторов прерываний:

...
		   #define  mapUART (1 << nUART_service)
		   #define  mapSSP (1 << nSSP_nrf)
		   #define  mapTIMER (1 << nTIM_intmon | 1 << nTIM_nrf)
isrPort		MACRO	port, portClass
		   IF	   map\2 & 1 << \1
		   EXTERN   isr\2\1
		   DCD	  isr\2\1
		   ELSE
		   DCD	  DefaultISR
		   ENDIF
		   ENDM
...
		   SECTION  .intvecTail:CONST:NOROOT(2)
		   DATA	;Продолжение таблицы прерываний
__vector_table_tail
		   DCD	  isrSVC			   ;SVCall Handler
		   DCD	  DefaultISR		   ;Debug Monitor Handler
		   DCD	  0					;Reserved
		   DCD	  OS_CPU_PendSVHandler ;PendSV Handler
		   DCD	  isrSysTick		   ;SysTick Handler
		   DCD	  isrWDT			   ;Watchdog timer
		   isrPort  0, TIMER			 ;Match 0 - 1 (MR0, MR1), Capture 0 - 1 (CR0, CR1)
		   isrPort  1, TIMER			 ;Match 0 - 2 (MR0, MR1, MR2), Capture 0 - 1 (CR0, CR1)
		   isrPort  2, TIMER			 ;Match 0-3, Capture 0-1
		   isrPort  3, TIMER			 ;Match 0-3, Capture 0-1
		   isrPort  0, UART			  ;UART0
		   isrPort  1, UART			  ;UART1
		   isrPort  2, UART			  ;UART2
		   isrPort  3, UART			  ;UART3
...

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Мой вариант лучше, так как:

Верю. Способов знаю, если не все, то много :). Просто описал, как сам делаю, поскольку, как написал "универсальные" использую редко. При малейшем желании можно и такую форму в одну строчку изменений обернуть - конкатенация всегда хорошо применима. Не проблема.

Ну и к форме "p->TCR" испытываю какую-то личную (будем считать ничем не обоснованную - просто глаз цепляется и понимаю, что можно тоже спрятать) неприязнь.

 

Как правило, когда прописываешь их значения в какой-то регистр, всегда перед глазами открытая страничка с описанием этого регистра.

И мне например удобнее соотносить описания полей в мануале с исходником по номерам битов, а не их именам (которых может и не быть или быть неудобными).

Так что именно для битовых полей в регистрах конфигурации периферии я отступаю от этого правила, и чаще всего прописываю так:

T0MCR = B0 | B1;

А для прочитать через день, или год опять мануал открывать? Нет, спасибо. Если я опишу пусть и пусть даже забуду аббревиатуру бита, то по этому имени я нажатием Alt+T в моем редакторе попаду в хидер, где оно определено и где У МЕНЯ есть комментарии, что это есть такое. И никаких лазаний по мануалам. Хотя все нужные мануалы у меня тоже подключены в проект и по Alt+H можно открыть сразу поиск. Для некоторых битов даю и дополнительные имена, КАК ПРИВЫК за многие годы с РАЗНЫМ оборудованием, например те-же регистры и биты 8250/82550 - у меня названы по первоисточнику :) так привычнее и уже намертво в голову вбиты.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

1.Устанавливаете запрос синхронного сброса

T0TCR = 0x00000002;

но не дожидаетесь его окончания. Значит сброс у Вас реально может происходить после последующих записей конфигурации.

jcxz, это значит что после операции T0TCR = 0x00000002; нужно проверить сбросился ли ТС и только потом продолжать конфигурацию?

 

2.Не чистите регистр флагов запросов прерываний.

Его обязательно чистить в конфигурации таймера? Читал в юзер мануал что по сбросу он очищен.

 

 

3.Неправильно рассчитываете период:

//Регистр совпадения = 1000 (1 Гц)
T0MR0 = 100;

 

jcxz, так правильно будет?

PCLKSEL0 |= 0x0000000E; //Timer0Clk=72Mhz/8= 9 MHz      
}

void Timer0_Init(void){
//Предделитель таймера = 8999
T0PR = 8999;
//Сбросить счетчик и делитель
T0TCR = 0x00000002;
//Режим таймера
Т0СТСR = 0;
//При совпадении сбрасываем таймер и вызываем прерывание
T0MCR = 0x00000003;
//Регистр совпадения = 1000 (1 Гц)
T0MR0 = 1000;

 

 

Как можно было сделать столько ошибок в такой простейшей периферии???

Читайте внимательнее юзермануал!

Да и в CTCR неплохо бы соотв. значение записать.

До этого я программировал только AVR (mega) на Ассемблере. В С, можно сказать, я полный балбес, только учусь.

 

После этого смотрите появляется ли флаг активного запроса прерывания в IR?

И если появляется, а входа в ISR нет - читайте про контроллер прерываний и всё что с ним связано.

 

В бесконечный цикл вставлял такой код

//if (T0IR & (1 << 0)) {FIO2CLR = (1<<13);      //Зажгли светодиод              |    
                    // T0TCR = 0x00000000;     //Остановили таймер - счётчик   |    
                    // T0IR = 0x00000001;      //сбросили флаг прерывания      |Ловушка                
                    // T0TCR = 0x00000002;     //сбросили таймер - счётчик     |    
                     // T0TCR = 0x00000001;}    //запустили таймер - счётчик    |
// else {FIO2SET = (1<<13);}

и по светодиоду видел, что 0 бит T0IR с определённым периодом устанавливается (таймер входит в прерывание).

 

По контроллеру прерываний:

1.А где у Вас глобальное разрешение прерываний IRQ? (Вы ведь вроде этому прерыванию от таймера назначаете линию IRQ?)

Это не оно VICIntEnable |= 0x00000010;?

 

2.А где обработчик прерывания IRQ?

Вроде это обработчик прерывания

__irq void Timer0_Int (void)
{

  
if (n!=0x2000000) {n<<=1;}
else {n = 0x04000000;}
T0IR = 0x00000001; /*Сбросить флаг прерывания в Timer0*/
//Бегущая единица
IOSET1 = 0x3C000000;
IOCLR1 = n; /* Установить состояние порта */
VICVectAddr = 0; /*Перевести VIC в исходное состояние*/
}

 

Пробовал я писать имена битов в регистрах, как в других примерах из интернета, но IAR выдаёт на них ошибки. Нужны, видимо какие-то библиотеки, ноя пока не пойму какие и где их брать.

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Пробовал я писать имена битов в регистрах, как в других примерах из интернета, но IAR выдаёт на них ошибки. Нужны, видимо какие-то библиотеки, ноя пока не пойму какие и где их брать.

Это называется заголовки, а не библиотеки. Есть в комплекте компилятора, или пишутся свои. У ИАР они очень специфичные под себя заточенные, посему абстракниые примеры их интернету не годятся. Либо свои пишите, либо надстройки над ИАРовскими допишите, либо на милость ИАР сдавайтесь (категорически не рекомендую).

Но САМОЕ главное начинайте не с таскания и усугубления всякого мусора из интернета, а изучения базовых понятий языка.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Это называется заголовки, а не библиотеки. Есть в комплекте компилятора, или пишутся свои. У ИАР они очень специфичные под себя заточенные, посему абстракниые примеры их интернету не годятся. Либо свои пишите, либо надстройки над ИАРовскими допишите, либо на милость ИАР сдавайтесь (категорически не рекомендую).

Но САМОЕ главное начинайте не с таскания и усугубления всякого мусора из интернета, а изучения базовых понятий языка.

Да Вы правы, не правильно я выразился, это заголовочные файлы. Спасибо за совет!

zltigo, если Вас не затруднит, можете в двух строчках написать, какие базовые понятия С языка нужно изучить прежде всего.

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Да Вы правы, не правильно я выразился, это заголовочные файлы. Спасибо за совет!

zltigo, если Вас не затруднит, можете в двух строчках написать, какие базовые понятия С языка нужно изучить прежде всего.

K&R второе издание

 

 

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

zltigo, спасибо! Уже скачал.

Проект мой получилось собрать и запустить в Keil. Прерывания от таймера по совпадению заработали.

Но! Для этого в проект был включён файл LPC2300.s (startup).

В IAR тоже нужно, наверное, типа такого файл подключать? Подскажите?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

jcxz, это значит что после операции T0TCR = 0x00000002; нужно проверить сбросился ли ТС и только потом продолжать конфигурацию?

Нет. Нужно проверить сбросился ли этот бит синхронного сброса в TCR. Прочитайте наконец описание TCR в юзермануале. У меня сейчас нет его под рукой, но насколько помню там описано поведение этого бита.

В общем случае частота тактирования таймера может быть ниже частоты процессора, а сброс этого бита - синхронный, т.е. - будет выполнен по ближайшему клоку. К этому времени процессор может успеть выполнить несколько команд.

Всё это описано в юзермануале.

 

Его обязательно чистить в конфигурации таймера? Читал в юзер мануал что по сбросу он очищен.

Сбросу чего? МК? Не вижу у Вас этого сброса и не уверен что он выполняется.

Да и в любом случае - лучше очистить.

 

jcxz, так правильно будет?

//Регистр совпадения = 1000 (1 Гц)

T0MR0 = 1000;[/code]

Вы на что хотите поделить? На 1000? Чтобы поделить на 1000 надо записать туда 999.

Опять же - RTFM.

 

Вроде это обработчик прерывания

__irq void Timer0_Int (void)

У Вас ядро ARM7. У этого ядра только два вектора прерывания - IRQ и FIQ, которые может генерить периферия.

Для каждой периферии можно выбрать - какое из этих 2х прерываний генерить. Вы насколько вижу выбираете IRQ для таймера.

Вот его обработчик Вам и необходим. И его нужно разрешить. А уже в нём будете вызывать эту функцию обслуживания прерывания от таймера.

В общем - прочитайте как устроена система прерываний ARM7.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

post-68333-1484769148_thumb.jpg

Я, кажется, понял. Выставил в первый бит 1, дождался когда она сбросится, а только потом продолжил выполнять дальше программу. Так?

post-68333-1484769625_thumb.jpg

В это скриншоте вижу что по сбросу микроконтроллера все прерывания сброшены. Допустим, я запишу туда 1 для сброса. Это не вызовет никакой неправильной реакции? Не установятся ли прерывания?

Вот последний код что я состряпал из разных кусков. И он работает! На данном этапе для меня это достижение.

unsigned int n;
void initClocks(void); 
void Timer0_Init(void);
__irq void timer0ISR(void);

/***************************************************************************/
/****************************************************************************/
int main (void) {
initClocks();
Timer0_Init(); /* Настроить таймер */    
//
IODIR1 = 0x3C000000;    /*Конфигурируем Р1.26 - Р1.29 на выход, остальное на вход*/
IOSET1 = 0x3C000000;    /* Устанавливаем ноль на выходах */
FIO2DIR = (1<<13);      //Configure Pin P2.13 as Output 
FIO2SET = (1<<13);    
//
n = 0x04000000;
IOCLR1 = n;          /* Установить состояние порта */

//Запустить таймер
T0TCR = 0x00000001;

    for (;;) {} //бесконечный цикл
  
}
/***************************************************************************/
/****************************************************************************/

void Timer0_Init(void)
{
  T0CTCR = 0x0; //Set Timer Mode
  T0PR = 8999; //Increment T0TC at every 9000 clock cycles
//9000 clock cycles @9 Mhz = 1 mS

  T0MR0 = 399;   //Zero Indexed Count-hence subtracting 1 (400 msec)
  T0MCR = (1<<0) | (1<<1);//Set bit0 & bit1 to Interrupt & Reset TC on MR0  

  VICVectAddr4 = (unsigned )timer0ISR; //Pointer Interrupt Function (ISR)
  VICVectCntl4 = 0xF;            //lowest priority
  VICIntEnable = (1<<4); // Enable timer0 interrupt

  T0TCR = (1<<1); // Reset Timer
}
/***************************************************************************/
/****************************************************************************/ 

__irq void timer0ISR(void)
{
if (n!=0x20000000) {n<<=1;}
else{n = 0x04000000;}
T0IR = 0x00000001; /*Сбросить флаг прерывания в Timer0*/
//Бегущая единица
IOSET1 = 0x3C000000;
IOCLR1 = n; /* Установить состояние порта */
VICVectAddr = 0; /*Перевести VIC в исходное состояние*/
}

Как работает таймер я понял.

Читаю RTFM.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Я, кажется, понял. Выставил в первый бит 1, дождался когда она сбросится, а только потом продолжил выполнять дальше программу. Так?

Да.

 

В это скриншоте вижу что по сбросу микроконтроллера все прерывания сброшены. Допустим, я запишу туда 1 для сброса. Это не вызовет никакой неправильной реакции? Не установятся ли прерывания?

Вы можете инициализировать таймер и не обязательно после сброса МК. Или например - подключение эмулятора без сигнала сброс. Или выполнение основного ПО после bootloader.

Вобщем - лучше вручную сбросить. Чтобы не было потом неожиданностей.

То же самое и с флагами запросов прерываний в регистрах контроллера прерываний перед их разрешением - вначале соотв. флаг лучше почистить.

Тут уже недавно были вопросы от тех, кто этого не делает о странных попаданиях в ISR когда они не ожидали...

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...