Прерывания (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
}
}
В работе приложения практически ничего не изменится и в таком преобразовании целесообразности практически не было, однако это иллюстрирует работу прерываний и показывает как можно ими пользоваться вообще. Детальное объяснение примеров последует в другой статье.