Доброго времени суток!
Сегодня будем разбираться с таймерами микроконтроллеров MSP430. Что для этого понадобится? Ну, лично я пользуюсь средой разработки IAR Embedded Workbench и отладочной платой MSP430 LaunchPad. Для начала рассмотрим теорию, регистры, отвечающие за работу таймеров в MSP430, а затем напишем небольшой пример.
Итак, на борту микроконтроллера MSP430G2533 имеется периферийный модуль Timer_A, представляющий из себя 16-битный таймер. Раз он шестнадцатибитный, то досчитать может до значения 0хFFFF (65535). Основные характеристики таймера:
- три разных режима работы
- возможность выбора и настройки источника тактирования
- 3 регистра захвата/сравнения
- возможность генерировать ШИМ сигнал
- ну и, естественно, прерывания на каждое событие
Итак режимы...
И первый из них - это режим прямого счета. В этом случае таймер считает от нуля до значения, записанного в регистре TACCR0. То есть, если в TACCR0 = 77, то таймер посчитает от нуля до 77, затем обнулится и начнет считать сначала.
Второй режим - режим непрерывного счета. В этом случае таймер считает от нуля до максимального значения (0хFFFF), сбрасывается и снова продолжает считать:
Ну и третий режим - режим прямого/обратного счета. Тут таймер считает до значения, записанного в регистр TACCR0, а достигнув этого значения, начинает считать в обратную сторону - то есть от значения TACCR0 до нуля:
Кроме того, возможна работа в режиме сравнения/захвата. Сейчас разберемся, что это значит. Мы можем установить определенное значение в регистре сравнения, которое будет сравниваться с текущим значением счетного регистра таймера. При совпадении значений контроллер узнает об этом и просигнализирует нам. А в режиме захвата таймер ждет определенного сигнала, например, с какого-нибудь порта, и когда этот сигнал приходит, таймер записывает свое текущее значение в специально для этого предназначенный регистр.
Перейдем теперь к регистрам, благодаря которым можно управлять работой таймера.
Регистр TACTL.
Регистр управления, здесь можно настроить все, что только угодно. А если конкретно, то тут настраиваются источник тактирования таймера и предделитель частоты. Кроме того, здесь задается режим работы таймера, для этого есть биты MCx:
Прерывания также включаются и отключаются в регистре управления TACTL.
Регистр TAR.
Это самый важный регистр таймера - регистр счета. В нем хранится текущее значение счетчика таймера.
Регистр TACCRx.
Регистр захвата/сравнения. В режиме сравнения в этот регистр мы записываем значение, с которым будет сравниваться значение счетного регистра таймера. А в режиме захвата значение счетного регистра TAR скопируется в регистр TACCRx, при выполнении условия захвата.
Регистр TACCTLx.
Регистр управления режимами захвата/сравнения таймера. Выбор режима (сравнение или захват), настройка сигнала, по которому происходит захват, обработка прерываний - все это настраивается в этом регистре.
Остался регистр TAIV. Значение этого регистра указывает на событие, которым было вызвано прерывание.
Вроде бы со всем разобрались, давайте теперь небольшой пример напишем для работы с таймером. Настроим его на работу в режиме прямого счета и в прерывании по переполнению таймера будем изменять состояние вывода микроконтроллера.
Создаем проект как в этой статье - проект для MSP430, и начинаем писать код:
/***************************************************************************************/ #include "io430.h" /***************************************************************************************/ int main(void) { // Stop watchdog timer to prevent time out reset WDTCTL = WDTPW + WDTHOLD; // Вывод P1.6 работает в качестве выхода P1DIR = BIT6; // Обнуляем значение регистра P1OUT P1OUT = 0x00; // Настраиваем таймер - источник тактирования - SMCLK, предделитель - 8, // режим работы - прямой счет, ну и разрешаем прерывания TACTL |= TASSEL1 | MC0 | TAIE | ID1 | ID0; // Досчитав до 0х90, таймер сбросится и начнет считать с нуля TACCR0 = 0x90; // Глобальное разрешение прерываний __bis_SR_register(GIE); while(1) { } return 0; } /***************************************************************************************/ // Так выглядит обработчик прерывания #pragma vector=TIMER0_A1_VECTOR __interrupt void Timer_A(void) { // Меняем состояние вывода P1.6 на противоположное P1OUT ^= BIT6; } /***************************************************************************************/
В отладчике можем видеть, как программа улетает на обработчик прерывания, когда таймер добирается до значения, указанного в регистре TACCR0. И на этом заканчиваем обсуждение таймеров на сегодня, в ближайшем будущем продолжим работать с микроконтроллерами MSP430!
Уважаемый автор, Вы не могли бы помочь решить проблему с таймером? Дело в том, что в отладчике значение счетного регистра сначала меняется в соответствии с тактовым сигналом, а потом вдруг туда набиваются случайные числа (но они идут по возрастанию). То есть я нажимаю "step into" или "step over" один раз, а значение счетного регистра меняется с 0x0002 на 0x01xx, со следующим нажатием становится больше раза в два.. и так далее. Может быть, что отладчик таймер не останавливает сразу? Или Вы другое направление подскажете, куда копать? Таймер конфигурировал только на непрерывный счет, предделитель тактового сигнала равен 8. Заранее спасибо за совет!
При нажатиях Step Into/Over программа делает шаг не на один тик таймера, а переходит, например, на следующую инструкцию. То есть при нажатии этих кнопок значение таймера может измениться на сколько угодно, в зависимости от программы.
Спасибо за ответ. Но ведь инструкция выполняется несколько тактов, но никак не тысячу. Кроме того, у меня тактирование таймера идет с частотой 1 МГц, а ядра - 16 МГц. И я пробовал ассемблерный шаг step into, over, проблема та же.
Не понял вот эту строку: TACTL |= TASSEL1 | MC0 | TAIE | ID1 | ID0. Как я понимаю, TASSELx, MCx, TAIE, IDx - это куски самого регистра TACTL. Получается, в регистр записываются куски этого самого регистра что ли? Как таким образом задается режим работы таймера?
Таким образом в регистре выставляются в 1 определенные биты.
MC0 - разве это не остановка таймера???
#pragma vector=TIMER0_A1_VECTOR - прерывания от TACCR1
для прямого счёта было бы правильно записать MC_1 и для тактирования от SMCLK записать TASSEL_2.
Поправте меня если я не прав.
#include "msp430g2553.h"
int main( void )
{
// Stop watchdog timer to prevent time out reset
WDTCTL = WDTPW + WDTHOLD;
BCSCTL1 = CALBC1_1MHZ; // Устанавливаем частоту DCO на калиброванные 1 MHz
DCOCTL = CALDCO_1MHZ;
// Вывод P1.6 работает в качестве выхода
P1DIR |= BIT6;
// Обнуляем значение регистра P1OUT
P1OUT = 0x00;
// Настраиваем таймер - источник тактирования - SMCLK,
// предделитель - 8, режим работы - прямой счет,
// ну и разрешаем прерывания
TACCR0 = 60000;
TACTL = TASSEL_2 + ID_3 + MC_1 + TACLR;
TACCTL0 = CCIE;
// Глобальное разрешение прерываний
_enable_interrupt();
while(1)
{
}
}
// Так выглядит обработчик прерывания
#pragma vector=TIMER0_A0_VECTOR
__interrupt void CCRO_ISR(void)
{
// Меняем состояние вывода P1.6 на противоположное
P1OUT ^= BIT6;
}
Radiolomator - Спасибо большое за код заработал сразу
mov.w#02D0h,&TACTL
mow.#01E0,&TACCTL0
mow.#01E0,&TACCTL1
bis.b#02h,&P1DIR
bis.b#02h,&P1EL
bic.b#02h,&P1SEL2
mow.w #50000, & TACCR0
mow.w#10,&TACCR1
bis.b#0010h,&TACTL
код для таймера B какой будет