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

Прерывания (interrupts) приостанавливают выполнение текущей программной инструкции и вызывают совсем другую подпрограмму - обработчик прерывания. После того как обработчик выполнит свои инструкции, то вернет управление обратно в то место, откуда была прервана основная программа. Это может быть полезно, например, если приложение должно синхронизироваться по времени (при срабатывании прерывания таймера в строго заданный интервал времени), если нужно мгновенно отреагировать на внешнее событие (нажали на кнопку), оперативно прочесть значение ADC сразу по завершении преобразования и так далее.

Прерывания от каждого источника прерываний будут вызывать разные обработчики прерываний. В STM8 есть так называемые векторы прерываний, их 32, но фактически могут быть задействованы только 23 из них, так как 9 зарезервированы. Таким образом можно создать до 23 обработчиков прерываний. Примером вектора прерываний может послужить вектор IRQ22 "ADC1", он вызывает подпрограмму-обработчик прерывания, возникшего от источника "ADC1" при окончании преобразования (выставление флага EOC - end of conversion и вызов прерывания) или при возникновении события AWD (Analog watchdog, флаг AWD будет выставлен и вызвано то же самое прерывание, что и при выставлении флага EOC). Таким образом, по флагам можно понять, почему "сработал" этот вектор.

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

Продолжите тот проект, который мы ведем вот уже третью практику, Lesson1. Сейчас понадобится кое-что добавить.

Через меню File -> New text file создайте пустой файл и сохраните его как main.h в папке с проектом.

На каталоге Include files проводника проекта, кликните правой кнопкой мыши и добавьте только что созданный файл main.h

Внутри файла main.h напишите следующий код

/* main.h */

@far @interrupt void ADC_int_handler (void);

Внутри файла stm8_interrupt_vector.c внесите кое какие исправления

/* BASIC INTERRUPT VECTOR TABLE FOR STM8 devices

* Copyright (c) 2007 STMicroelectronics

*/

#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) { 

/* in order to detect unexpected events during development,

it is recommended to set a breakpoint on the following instruction

*/

  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, ADC_int_handler},     /* irq22 */

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

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

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

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

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

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

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

};

Здесь можно заметить, что внесен ADC_int_handler в вектор прерывания IRQ22 и подключен файл main.h

В файле main.c впишите вот такой код:

#include

 

int adcTotalRes;

 

int delay(unsigned int it) {

  while(it--);

}

 

@far @interrupt void ADC_int_handler (void) {

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

  adcTotalRes = (ADC_DRH<<8)|ADC_DRL;

  ADC_CR1 |= 0x01; // adon again

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

  }

  return;

}

 

int main(void) {

  PB_DDR = 0b00100000; // PB5 - dig. output

  ADC_CSR = 0b00100010; // AIN2 selected, EOCIE enabled

  ADC_CR2 = 0b00001000; // adcres align right

  ADC_CR1 |= 0x01; // adc pwr on

  ADC_CR1 |= 0x01; // adc start

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

  while(1) {

    PB_ODR = 0xFF; // all PB high

    delay(500); // pause

    PB_ODR = 0x00; // all PB low

    delay(adcTotalRes); // pause

  }

}

В работе приложения практически ничего не изменится и в таком преобразовании целесообразности практически не было, однако это иллюстрирует работу прерываний и показывает как можно ими пользоваться вообще. Детальное объяснение примеров последует в другой статье.