В данном уроке вы освоите основы работы с таймером 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 в своих проектах.