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

В предыдущем занятии мы создали обработчик прерывания IRQ22, для этого понадобилось создать взаимосвязь между таблицей векторов прерываний и кодом основной программы. Таким связующим звеном послужил файл main.h, в котором был определен прототип функции ADC_int_handler - которая и является обработчиком прерывания. Алгоритм работы функции был помещен в main.c, а ссылку на функцию я вписал в таблицу векторов из файла stm8_interrupt_vector.c.

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

Таблица векторов прерываний находится в памяти программ (flash) в диапазоне адресов 0x008000 - 0x00807F. Каждый вектор занимает 4 байта. Всего векторов 32 и под них отведено 128 байт.

Вектор прерывания представляет из себя блок из 4 байт, где первый байт - ассемблерная инструкция INT (машинный код 0x82), а остальные 3 байта - абсолютный 24-битный адрес функции обработчика прерываний.

При вызове прерывания происходит переход по вектору прерывания (при этом программный счетчик PC не меняется), в стек загружаются регистры ядра (счетчик команд, регистры X и Y, аккумулятор, регистр состояния), затем в счетчик команд загружается 24-битный адрес из вектора прерывания. Выход из обработчика прерывания выполняется ассемблерной инструкцией IRET. Если в ассемблерном коде с векторами прерываний все понятно и просто, то в языке Си реализация векторов прерываний выглядит немного ужасающей и непонятной. Но компилятору передаются специальные директивы, указывающие на файл таблицы векторов и все это на основании соглашения помещается как нужно и куда нужно.
Модификаторы типа функции @far @interrupt для void позволяют: @far: получить именно 24-битное значение адреса функции в адресном пространстве программного кода. @interrupt: выполнять выход из функции инструкцией IRET.
Как вы уже догадались, в описании вектора (в структуре таблицы векторов) первый байт 0x82 - есть ни что иное как машинный код инструкции вызова прерывания INT.

_vectab[] - это уже соглашение. Данные именно этого массива компилятор запишет по адресу 0x8000.

_stext - и это соглашение. Это встроенная функция обработки прерывания Reset, "вшитая" в память чипа еще на заводе. Она инициализирует регистры до состояния Default и переносит счетчик команд на адрес 0x8080 : точка входа main().

В программном коде файла есть еще и такая функция:

@far @interrupt void NonHandledInterrupt (void)

{

/* in order to detect unexpected events during development,

it is recommended to set a breakpoint on the following instruction

*/

return;

}

Это реакция на все прерывания, которые могут возникнуть, но не содержат обработчика. По сути, неожиданные прерывания нежелательны, и когда они возникают, то это говорит о том, что программист где-то что-то неправильно настроил или забыл создать/подключить обработчик прерывания. Должна последовать какая-то реакция вроде выдачи ошибки каким либо способом. Но по-умолчанию указана лишь процедура возврата из прерывания для создания breakpoint в этом месте для отладки - очень полезная вещь.

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

ADC_CSR = 0b00100010;

Выставлен бит 5 - EOCIE - разрешить прерывание по завершению преобразования ADC, и выбран канал 2 (CH[3:0] = 0b0010)

ADC_CR1 |= 0x01; // adc pwr on

ADC_CR1 |= 0x01; // adc start

Как известно из основ работы с АЦП микроконтроллеров STM8, для включения модуля выставляем бит ADON, для первого или повторного запуска преобразования, так же записываем 1 в бит ADON.

_asm("rim"); // Enable Interrupts Global

Ассемблерная вставка глобального включения (разрешения) прерываний. RIM - разрешает прерывания, SIM - запрещает прерывания.

@far @interrupt void ADC_int_handler (void)

Это, непосредственно, описание самого обработчика (или функции обработчика, как угодно)

if (ADC_CSR & (1<<7)) { // if flag is set

Если прерывание возникло с флагом окончания EOC (7-ой бит регистра ADC_CSR) преобразования, то.. Это нужно для того, чтобы отсеять это событие (EOC) от других, которые могут возникнуть в векторе. В данном практическом занятии никаких других прерываний в этом векторе быть не может, однако это иллюстрирует способ сортировки источников прерывания. Вот, например, если разрешить прерывание от AWD, то это событие тоже будет вызывать вектор IRQ22 с выставлением флага AWD.

ADC_CSR &= ~(1<<7); // reset EOC flag

Заключительным этапом обработки прерываний является программный сброс флага EOC.

Таблица всех векторов прерываний контроллеров STM8 Value line и Access line.

Адрес

Номер

Имя

Пояснение

0x8000

-

RESET

Сброс

0x8004

-

TRAP

Программное прерывание

0x8008

IRQ_0

TLI

Внешнее прерывание высшего уровня

0x800C

IRQ_1

AWU

Пробуждение из режима HALT

0x8010

IRQ_2

CLK

Прерывание модуля тактирования

0x8014

IRQ_3

EXTI0

Внешнее прерывание порта A

0x8018

IRQ_4

EXTI1

Внешнее прерывание порта B

0x801C

IRQ_5

EXTI2

Внешнее прерывание порта C

0x8020

IRQ_6

EXTI3

Внешнее прерывание порта D

0x8024

IRQ_7

EXTI4

Внешнее прерывание порта E

0x8028

IRQ_8

-

-

0x802C

IRQ_9

-

-

0x8030

IRQ_10

SPI

Окончание передачи SPI

0x8034

IRQ_11

TIM1

Переполнение TIM1

0x8038

IRQ_12

TIM1CC

CAPCOM TIM1

0x803C

IRQ_13

TIM2

Переполнение TIM2

0x8040

IRQ_14

TIM2CC

CAPCOM TIM2

0x8044

IRQ_15

TIM3

Переполнение TIM3

0x8048

IRQ_16

TIM3CC

CAPCOM TIM3

0x804C

IRQ_17

-

-

0x8050

IRQ_18

-

-

0x8054

IRQ_19

I2C

События интерфейса I2C

0x8058

IRQ_20

UART2TX

Завершена передача по UART

0x805C

IRQ_21

UART2RX

Завершен прием по UART

0x8060

IRQ_22

ADC1

Возник флаг AWD или EOC

0x8064

IRQ_23

TIM4

Переполнение TIM4

0x8068

IRQ_24

FLASH

Окончена запись flash EOP/WR_PG_DIS

0x806C

-

-

-

...

...

...

...

0x807C

-

-

-

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