Меня тут пристыдили в записи про прерывания, дескать, я не даю реальных примеров использования этих самых прерываний, а занимаюсь только переводом даташита в онлайн-переводчике. (На самом деле, вручную). И вот я решил решить с помощью Attiny13A гипотетическую задачку, которая очень волновала меня в детстве. Рассмотрим программу на ассемблере для управления системой полива огорода.
У нас была дача, на которую надо было ездить летом, не смотря ни на что. Потому что поливочную воду там давали не каждый день, и по часам: надо было набирать бак и бочки, чтобы потом, в периоды засухи, ходить с ведрами и лейками, и поливать насаждения. И вот я ходил и мечтал — как было бы здорово, если бы некая чудесная система сама наполняла бак с водой, когда работал водопровод. А другая система бы отслеживала, чтоб бак не переполнялся. А третья система включала бы время от времени насос, который бы через шланги с дырочками устраивал бы капельный полив помидоркам и огурчикам. И если в детстве это казалось чудом, то сейчас копеечный микроконтроллер с килобайтом памяти может управлять этой системой без особенных проблем. Я не берусь воплотить весь этот проект — там, понятное дело, нужен будет обвес, интерфейс между контроллером и датчиками, клапаном, насосом. Я только приведу пример использования прерываний, и их программирования на ассемблере.
Реализация предложенной задачи на ассемблере для микроконтроллера ATtiny13A потребует работы с таймерами, прерываниями и управлением состояниями. Мы будем использовать несколько основных концепций:
- Прерывания для обработки сигналов от датчика уровня воды и датчика течения воды.
- Таймер для управления включением и выключением насоса через каждые 6 часов и его работы в течение 1 часа.
- Порты ввода/вывода для управления клапаном, насосом и светодиодом.
Основные требования:
- Прерывание по датчику течения воды для открытия клапана, когда в магистральном водопроводе появляется вода, и его закрытия, если вода перестала поступать или бак заполнен.
- Прерывание по датчику уровня воды для открытия или закрытия клапана в зависимости от уровня в баке.
- Таймер для включения насоса через каждые 6 часов и его работы в течение 1 часа.
- Светодиод для отображения состояния системы.
Порты и задачи:
- PB0 — датчик течения воды.
- PB1 — датчик уровня воды.
- PB2 — включение/выключение насоса.
- PB3 — светодиод для индикации состояния.
- PB4 — управление клапаном.
Псевдокод на ассемблере
- Прерывание по внешнему сигналу от датчика воды (PB0).
- Прерывание по уровню воды в баке (PB1).
- Таймер для управления насосом (PB2).
- Управление светодиодом (PB3) в зависимости от состояния системы.
Основные шаги:
- Настроить внешние прерывания для датчиков.
- Настроить таймер для включения насоса через 6 часов (управление на основе таймера с предделителем).
- Управление состояниями (включение клапана, насоса и светодиода) в зависимости от сигналов датчиков.
Пример кода на ассемблере для ATtiny13A:
.include "tn13def.inc" ; Включаем определения регистров для ATtiny13A
.equ F_CPU = 9600000 ; Частота работы микроконтроллера (9.6 МГц)
; Назначение пинов
.equ WATER_FLOW_SENSOR = PB0 ; Датчик тока воды
.equ WATER_LEVEL_SENSOR = PB1 ; Датчик уровня воды в баке
.equ PUMP_CONTROL = PB2 ; Управление насосом
.equ LED_CONTROL = PB3 ; Светодиод для индикации состояния
.equ VALVE_CONTROL = PB4 ; Управление клапаном
; Константы времени (рассчитываются на основе частоты работы таймера)
.equ SIX_HOURS_TICKS = 215 ; Количество циклов для отсчета 6 часов (например)
.equ ONE_HOUR_TICKS = 36 ; Количество циклов для 1 часа работы насоса
; Режимы работы
.equ NO_WATER = 0 ; Состояние: воды нет
.equ FILLING_TANK = 1 ; Состояние: наполнение бака
.equ PUMP_RUNNING = 2 ; Состояние: насос включен
.equ LED_FAST_BLINK = 50 ; Быстрое моргание светодиода
.equ LED_SLOW_BLINK = 100 ; Медленное моргание светодиода
.dseg
.def temp = r16 ; Временный регистр для операций
.def state = r17 ; Хранение текущего состояния системы
.def led_counter = r18 ; Счетчик для моргания светодиода
.def pump_timer = r19 ; Счетчик времени работы насоса
.def water_check_timer = r20 ; Таймер для проверки наличия воды
.def tick_counter = r21 ; Счетчик для работы таймера
.cseg
.org 0x0000 ; Начало программы
rjmp RESET ; Переход на начало программы
; --- Обработка прерываний ---
.org INT0addr ; Прерывание по датчику тока воды (PB0)
; Проверяем, есть ли сигнал от датчика воды
sbis PINB, WATER_FLOW_SENSOR ; Пропускаем, если воды нет
rjmp NO_WATER_FLOW ; Если воды нет
; Если вода есть, открываем клапан
sbi PORTB, VALVE_CONTROL ; Открыть клапан
ldi state, FILLING_TANK ; Состояние: наполнение бака
rjmp END_INTERRUPT
NO_WATER_FLOW:
; Если вода прекратилась или бак полный - закрываем клапан
cbi PORTB, VALVE_CONTROL ; Закрыть клапан
ldi state, NO_WATER ; Состояние: воды нет
END_INTERRUPT:
reti ; Завершить прерывание
.org PCINT0addr ; Прерывание по уровню воды (PB1)
; Проверка уровня воды
sbis PINB, WATER_LEVEL_SENSOR ; Пропустить, если бак не полон
rjmp END_INTERRUPT ; Если бак не полон, выходим
; Если бак полон, закрываем клапан
cbi PORTB, VALVE_CONTROL ; Закрыть клапан
ldi state, NO_WATER ; Состояние: воды нет
reti ; Завершить прерывание
; --- Основная программа ---
RESET:
; Инициализация портов
ldi temp, (1<<VALVE_CONTROL) | (1<<PUMP_CONTROL) | (1<<LED_CONTROL) ; Настроить PB2, PB3, PB4 как выходы
out DDRB, temp ; Выходы
; Инициализация состояния
ldi state, NO_WATER ; По умолчанию: воды нет
; Настройка таймера
ldi temp, (1<<CS02) | (1<<CS00) ; Предделитель таймера = 1024
out TCCR0B, temp
; Включение прерываний
sei ; Разрешить глобальные прерывания
sbi GIMSK, INT0 ; Включить внешнее прерывание INT0 (датчик воды)
sbi GIMSK, PCIE ; Включить PCINT прерывания (датчик уровня воды)
sbi PCMSK, WATER_LEVEL_SENSOR ; Включить прерывание на уровне воды
MAIN_LOOP:
; Проверяем состояние системы
cpi state, NO_WATER
breq CHECK_WATER ; Если воды нет, проверяем воду
cpi state, FILLING_TANK
breq FILL_TANK ; Если идет наполнение, проверяем уровень
cpi state, PUMP_RUNNING
breq RUN_PUMP ; Если насос включен, следим за его временем
CHECK_WATER:
; Если воды нет, выключаем насос
cbi PORTB, PUMP_CONTROL
rjmp LED_CONTROL_LOOP ; Переход к управлению светодиодом
FILL_TANK:
; Если наполняется бак, следим за уровнем
sbis PINB, WATER_LEVEL_SENSOR ; Если бак заполнен, выходим
rjmp END_INTERRUPT
RUN_PUMP:
; Управляем насосом, если он работает
dec pump_timer
brne LED_CONTROL_LOOP ; Если таймер не истек, продолжаем
cbi PORTB, PUMP_CONTROL ; Останавливаем насос, когда время истекло
ldi state, NO_WATER ; Возвращаемся в состояние ожидания воды
LED_CONTROL_LOOP:
; Логика моргания светодиода
inc led_counter
cpi led_counter, LED_FAST_BLINK
brne MAIN_LOOP ; Моргание не требуется, продолжаем
; Моргание в зависимости от состояния
cpi state, FILLING_TANK
breq TOGGLE_LED_SLOW
cpi state, PUMP_RUNNING
breq TOGGLE_LED_FAST
; Непрерывное свечение
sbi PORTB, LED_CONTROL ; Включить светодиод
rjmp MAIN_LOOP
TOGGLE_LED_SLOW:
sbis PINB, LED_CONTROL
cbi PORTB, LED_CONTROL
rjmp MAIN_LOOP
TOGGLE_LED_FAST:
sbi PORTB, LED_CONTROL
rjmp MAIN_LOOP
Краткое объяснение:
- Прерывания:
- Прерывание на PB0 для обработки сигнала от датчика тока воды. Когда вода появляется — открывается клапан, когда воды нет — клапан закрывается.
- Прерывание на PB1 для обработки сигнала от датчика уровня воды. Когда бак полный — закрывается клапан.
- Таймер:
- Таймер отсчитывает 6 часов для запуска насоса. При запуске насоса он работает в течение 1 часа, затем останавливается.
- Управление светодиодом:
- Светодиод управляется состояниями системы: медленное моргание при наполнении бака, быстрое моргание при работе насоса, и непрерывное свечение, если воды в баке нет.
Этот код является гипотетическим шаблоном, который на практике может несколько отличаться. Однако если включить логику, то можно на основе него описать любое другое использование этих прерываний. Удачи вам!