Управление системой полива

Автор: | 15.10.2024

Меня тут пристыдили в записи про прерывания, дескать, я не даю реальных примеров использования этих самых прерываний, а занимаюсь только переводом даташита в онлайн-переводчике. (На самом деле, вручную). И вот я решил решить с помощью Attiny13A гипотетическую задачку, которая очень волновала меня в детстве. Рассмотрим программу на ассемблере для управления системой полива огорода.

У нас была дача, на которую надо было ездить летом, не смотря ни на что. Потому что поливочную воду там давали не каждый день, и по часам: надо было набирать бак и бочки, чтобы потом, в периоды засухи, ходить с ведрами и лейками, и поливать насаждения. И вот я ходил и мечтал — как было бы здорово, если бы некая чудесная система сама наполняла бак с водой, когда работал водопровод. А другая система бы отслеживала, чтоб бак не переполнялся. А третья система включала бы время от времени насос, который бы через шланги с дырочками устраивал бы капельный полив помидоркам и огурчикам. И если в детстве это казалось чудом, то сейчас копеечный микроконтроллер с килобайтом памяти может управлять этой системой без особенных проблем. Я не берусь воплотить весь этот проект — там, понятное дело, нужен будет обвес, интерфейс между контроллером и датчиками, клапаном, насосом. Я только приведу пример использования прерываний, и их программирования на ассемблере.

Реализация предложенной задачи на ассемблере для микроконтроллера ATtiny13A потребует работы с таймерами, прерываниями и управлением состояниями. Мы будем использовать несколько основных концепций:

  1. Прерывания для обработки сигналов от датчика уровня воды и датчика течения воды.
  2. Таймер для управления включением и выключением насоса через каждые 6 часов и его работы в течение 1 часа.
  3. Порты ввода/вывода для управления клапаном, насосом и светодиодом.

Основные требования:

  1. Прерывание по датчику течения воды для открытия клапана, когда в магистральном водопроводе появляется вода, и его закрытия, если вода перестала поступать или бак заполнен.
  2. Прерывание по датчику уровня воды для открытия или закрытия клапана в зависимости от уровня в баке.
  3. Таймер для включения насоса через каждые 6 часов и его работы в течение 1 часа.
  4. Светодиод для отображения состояния системы.

Порты и задачи:

  • PB0 — датчик течения воды.
  • PB1 — датчик уровня воды.
  • PB2 — включение/выключение насоса.
  • PB3 — светодиод для индикации состояния.
  • PB4 — управление клапаном.

Псевдокод на ассемблере

  1. Прерывание по внешнему сигналу от датчика воды (PB0).
  2. Прерывание по уровню воды в баке (PB1).
  3. Таймер для управления насосом (PB2).
  4. Управление светодиодом (PB3) в зависимости от состояния системы.

Основные шаги:

  1. Настроить внешние прерывания для датчиков.
  2. Настроить таймер для включения насоса через 6 часов (управление на основе таймера с предделителем).
  3. Управление состояниями (включение клапана, насоса и светодиода) в зависимости от сигналов датчиков.

Пример кода на ассемблере для 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

Краткое объяснение:

  1. Прерывания:
    • Прерывание на PB0 для обработки сигнала от датчика тока воды. Когда вода появляется — открывается клапан, когда воды нет — клапан закрывается.
    • Прерывание на PB1 для обработки сигнала от датчика уровня воды. Когда бак полный — закрывается клапан.
  2. Таймер:
    • Таймер отсчитывает 6 часов для запуска насоса. При запуске насоса он работает в течение 1 часа, затем останавливается.
  3. Управление светодиодом:
    • Светодиод управляется состояниями системы: медленное моргание при наполнении бака, быстрое моргание при работе насоса, и непрерывное свечение, если воды в баке нет.

Этот код является гипотетическим шаблоном, который на практике может несколько отличаться. Однако если включить логику, то можно на основе него описать любое другое использование этих прерываний. Удачи вам!

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *