Уроки программирования STM8. Урок 9. STVD + CXSTM8. Основы работы с таймером TIM4

В данном уроке вы освоите основы работы с таймером TIM4 и пройдете небольшую практику. Суть практической работы заключается в том, чтобы как и в самом начале поморгать светодиодом. Резонно назвать этот практикум "Hello, LED-2". В отличие от первого примитивного примера, где задержка (пауза) осуществлялась зависанием микроконтроллера на количество заданных итераций, здесь микроконтроллер зависать не будет. Он будет "слушать" события. Таймер будет создавать эти события в строго определенный промежуток времени. И по возникновении событий можно будет менять состояние линии порта, на которой висит светодиод. По-прежнему я использую отладочную плату "синяя пуля оптовых оценка STM8 доска" и данный пример будет работать вообще на любом STM8 линеек Value и Acces line.

Начиная с этого занятия разбор кода будет вестись прямо в комментариях к коду. А в конце урока будет даваться теоретическая основа. Таким образом, несложные занятия могут вполне умещаться в рамках одной статьи.

Открывайте все тот же проект из предыдущего занятия и меняйте в нем содержимое файла main.h

@far @interrupt void TIM4_int_handler (void); // Прототип обработчика прерывания TIM4

Меняйте содержимое файла stm8_interrupt_vector.c

 

#include "main.h"

typedef void @far (*interrupt_handler_t)(void);

 

struct interrupt_vector {

  unsigned char interrupt_instruction;

  interrupt_handler_t interrupt_handler;

};

 

@far @interrupt void NonHandledInterrupt (void)

{

  // Возникло неожиданное прерывание. Используйте breakpoint

  return;

}

 

extern void _stext(); /* startup routine */

 

struct interrupt_vector const _vectab[] = {

  {0x82, (interrupt_handler_t)_stext}, /* reset */

  {0x82, NonHandledInterrupt}, /* trap */

  {0x82, NonHandledInterrupt}, /* irq0 */

  {0x82, NonHandledInterrupt}, /* irq1 */

  {0x82, NonHandledInterrupt}, /* irq2 */

  {0x82, NonHandledInterrupt}, /* irq3 */

  {0x82, NonHandledInterrupt}, /* irq4 */

  {0x82, NonHandledInterrupt}, /* irq5 */

  {0x82, NonHandledInterrupt}, /* irq6 */

  {0x82, NonHandledInterrupt}, /* irq7 */

  {0x82, NonHandledInterrupt}, /* irq8 */

  {0x82, NonHandledInterrupt}, /* irq9 */

  {0x82, NonHandledInterrupt}, /* irq10 */

  {0x82, NonHandledInterrupt}, /* irq11 */

  {0x82, NonHandledInterrupt}, /* irq12 */

  {0x82, NonHandledInterrupt}, /* irq13 */

  {0x82, NonHandledInterrupt}, /* irq14 */

  {0x82, NonHandledInterrupt}, /* irq15 */

  {0x82, NonHandledInterrupt}, /* irq16 */

  {0x82, NonHandledInterrupt}, /* irq17 */

  {0x82, NonHandledInterrupt}, /* irq18 */

  {0x82, NonHandledInterrupt}, /* irq19 */

  {0x82, NonHandledInterrupt}, /* irq20 */

  {0x82, NonHandledInterrupt}, /* irq21 */

  {0x82, NonHandledInterrupt}, /* irq22 */

  {0x82, TIM4_int_handler}, /* irq23 прерывание таймера TIM4 */

  {0x82, NonHandledInterrupt}, /* irq24 */

  {0x82, NonHandledInterrupt}, /* irq25 */

  {0x82, NonHandledInterrupt}, /* irq26 */

  {0x82, NonHandledInterrupt}, /* irq27 */

  {0x82, NonHandledInterrupt}, /* irq28 */

  {0x82, NonHandledInterrupt}, /* irq29 */

};

Меняйте содержимое файла main.c

 

#include <iostm8s103.h>

int cntTicks = 0; // накопитель событий обновления таймера (тиков)

@far @interrupt void TIM4_int_handler (void) { // Код обработчика прерывания от таймера TIM4

  if (cntTicks++ > 60) { // Если кол-во тиков достигло значения 61, то

    PB_ODR ^= 1<<5; // инвертировать бит 5 порта B

    cntTicks = 0; // обнулить счетчик тиков

  }

  TIM4_SR = 0; // скинуть UIF (update interrupt flag)

  // если этот флаг не сбросить, то прерывание превратится в бесконечный цикл.

  return;

}

int main(void) {

  PB_DDR = 0b00100000; // PB5 - Дискретный выход

  TIM4_PSCR =0b00000111; // TIM4 тактовый предделитель на 128

  // По-умолчанию, частота тактирования ядра Fmaster = 2 MHz

  // Частота тактирования таймера будет 2000000/128=15625 Гц

  TIM4_IER = 1; // Разрешить прерывания от TIM4

  TIM4_CR1 = 1; // Включить TIM4

  TIM4_ARR = 255; // Это значение регистра AutoReload

  // оно такое и по умолчанию, но здесь вы можете его менять

  // при частоте тактирования 15625 Гц, таймер будет достигать значения 255

  // каждые 16,32 мсек и вызывать прерывание.

  // За 1 секунду произойдет 61 прерывание.

  _asm("rim"); // Разрешить прерывания вообще (по умолчанию запрещены)

  while(1); // Бесконечный цикл (режим "слушателя")
}

В микроконтроллерах семейства STM8 содержится несколько системных таймеров и самым простым из них является 8-битный инкрементный TIM4, который может считать от 0 до 255 и вызывать прерывание при обновлении. Таймер этот обновляется в трех случаях:
1) Переполнение, когда счетчик достиг своего предельного значения 255 и при следующем такте обнуляется.
2) Достижение значения регистра TIM4_ARR (по умолчанию таймер обновляется именно так).
3) При программной установке бита UG в регистре TIM4_EGR.

Когда таймер обновляется, то аппаратно выставляется флаг UIF в регистре TIM4_SR (в регистре всего один бит).

Принудительно вызвать обновление можно установкой бита UG в регистре TIM4_EGR, сбросится он аппаратно (в регистре всего один бит).

Прерывание при обновлении разрешается битом UIE в регистре TIM4_IER (в регистре всего один бит).

Регистр предделителя тактовой частоты таймера TIM4_PSCR (в регистре всего три бита)
000 - /1
001 - /2
010 - /4
011 - /8
100 - /16
101 - /32
110 - /64
111 - /128

Значение счетчика таймера, непосредственно, можно прочесть и установить в регистре TIM4_CNTR

Регистр TIM4_CR1 основной конфигурационный

ARPE - разрешить буферизацию регистра TIM4_ARR

OPM - включить режим одиночного импульса. При включенном режиме, таймер отключится (сбросом бита CEN) после первого же обновления.

URS - какое событие вызовет прерывание 1 - только переполнение, 0 - не только переполнение но и достижение значения регистра TIM4_ARR, и программная установка бита UG в регистре TIM4_EGR.

UDIS - обновления 0 - разрешены, 1 - запрещены.

CEN - бит включения таймера.

Этой информации вполне достаточно, чтобы начать использовать таймер TIM4 в своих проектах.