Управляющая программа для цифрового генератора опорного напряжения

В этой статье приводится пример простейшей программы управления микроконтроллером для цифрового генератора опорного напряжения (генератор у нас, если помните, сделан на ATtiny13).

Что наш генератор умеет с такой программой? С такой программой он умеет устанавливать любое выходное напряжение в пределах от 0 до 5 Вольт с шагом 19,6 мВ, умеет изменять выходное напряжение при нажатии на кнопки (одна кнопка — для увеличения выходного напряжения, другая — для уменьшения), а также ограничивать минимальное и максимальное значение выходного напряжения (то есть диапазон 0-5В можно сузить, скажем, до величины 0.98-4.9В).

В контроллере при нажатии на кнопки изменяется не сразу сам выход, а сначала изменяется внутренняя переменная (setup — уставка), к которой затем постепенно подтягивается выход. Сделано это для того, чтобы можно было менять выход с заданной плавностью, независимо от скорости изменения уставки, поскольку уставку можно и вообще мгновенно задавать, скажем по интерфейсу или с пульта.

Из интересного отмечу также алгоритм обработки нажатий кнопок. В данном случае вам не нужно жать кнопку столько же раз, на сколько шагов нужно изменить выход. Всё то время, пока вы держите кнопку нажатой — изменяется уставка (и вместе с ней, естественно, — выходное напряжение). Причём изменение уставки реализовано с двумя скоростями — сразу после нажатия на кнопку уставка изменяется медленно, а при удержании кнопки дольше определённого времени — она начинает изменяться гораздо быстрее. (Пришлось сделать такую фишку, поскольку шагов много и тыкать 256 раз на кнопку или держать её 3 часа нажатой — довольно утомительно).

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

Для того, чтобы определить что нужно и чего не нужно делать программе в текущем цикле — нам понадобятся три флага, под которые мы выделим специальный регистр (flags). Установленный нулевой бит этого регистра будет говорить о том, что мы находимся в режиме изменения задания, установленный первый бит — о том, что выходное напряжение не равно уставке и его нужно корректировать, установленный второй бит — о том, что мы держим кнопку уже достаточно долго и уставку нужно менять с повышенной скоростью.

Алгоритм:

Алгоритм работы контроллера цифрового генератора опорного напряжения

Текст программы под катом

.device ATtiny13
.include "tn13def.inc"
.list
;-- определяем свои переменные
.def     w=r16        ; это будет наш аккумулятор
.def     setup=r17    ; это задание
.def     current=r18  ; здесь текущее значение ширины импульсов
.def     maximum=r19  ; максимальное значение
.def     minimum=r20  ; минимальное значение
.def     flags=r21    ; флаги
;(0-идёт изменение задания, 1-выход не равен уставке, 2 - slow/fast)
.def     setup_counter=r22   ; счётчики для паузы между опросом кнопок
.def     setup_counter2=r23
.def     setup_counter3=r24
.def     output_counter=r25  ; счётчики для паузы между изменениями выхода
.def     output_counter2=r26
.def     counter_sch=r27     ; cчётчик для определения удержания кнопок
;--------------------------
.equ     slow_setup=5     ; задержка на изменение уставки медленная
.equ     fast_setup=1     ; задержка на изменение уставки быстрая
.equ     output_time=1    ; задержка на изменение выхода
.equ     s_maximum=238    ; начальное ограничение максимального выхода
.equ     s_minimum=24     ; начальное ограничение минимального выхода
.equ     s_setup=128      ; начальное значение уставки
;--- Используемые выходы
; PB0 - выход ШИМ, PB1 - вход КН2, PB2- вход КН1, PB3 - выход, PB4 - вход
;--- начало программного кода
       .cseg
       .org 0
       rjmp Init  ; переход на начало программы
;-- вектора прерываний
       reti       ; INT0
       rjmp Pch   ; Pin Change
       reti       ; Timer
       reti       ; EEPROM
       reti       ; comparator
       reti       ; timer compare match A
       reti       ; timer compare match B
       reti       ; watchdog
       reti       ; ADC
;-- начало программы
Init:  ldi w,RAMEND     ; устанавливаем указатель вершины
       out SPL,w        ; стека на старший байт RAM
       sbi ACSR,7       ; выключаем компаратор
       ldi w,0b00001001 ; определяем входы и выходы порта (1-выход, 0-вход)
       out DDRB,w
       ldi w,0b11100110 ; включаем подтягивающие резисторы
       out PORTB,w      ; и определяем начальное состояние выходов
;-- записываем начальные значения
       ldi maximum,s_maximum
       ldi minimum,s_minimum
       ldi setup,s_setup    ; ставим начальное задание
       ldi output_counter,255
       ldi output_counter2,output_time
;-- включаем ШИМ на выходе PB0 (OC0A)
       ldi w,128        ; записываем начальную ширину импульсов
       out OCR0A,w
       ldi w,0b10000011 ; fastPWM, set at TOP, clear at compare
       out TCCR0A,w
       ldi w,0b00000001 ; fastPWM, no_prescale
       out TCCR0B,w
;-- разрешить прерывание от входов
       ldi w,0b00100000
       out GIMSK,w      ; разрешаем прерывание от входов
;-- разрешить прерывания на входах PB1, PB2
Start: ldi w,0b00100000
       out GIFR,w       ; сбросить флаг прерываний от входов
       ldi w,0b00000110
       out PCMSK,w      ; разрешаем прерывания на входах PB2, PB1
       sei              ; разрешить глобальные прерывания
 
;----------------------------------------------------------------------
Scan:  sbrc flags,0     ; если флаг сброшен - пропускаем команду
       rcall Setup_update
       sbrc flags,1     ; если флаг сброшен - выход не нужно изменять
       rcall Change_output
       rjmp Scan
 
;--- Меняем выход ---------------
Change_output:
       dec output_counter
       brne End_cho          ; если не отсчитали нужный интервал - прыгаем
       ldi output_counter,255
       dec output_counter2
       brne End_cho          ; если не отсчитали нужный интервал - прыгаем
        ;--- Отсчитали время, через которое можно менять выход
       in current,OCR0A      ; читаем текущее установленное значение
       cp current,setup      ; current - setup
       breq Equal            ; текущее значение равно уставке
       brcc SetupIsLower; если переноса не было - setup меньше
SetupIsHigher:
       inc current           ; увеличиваем
       out OCR0A,current     ; устанавливаем новое значение
       rjmp Exit
SetupIsLower:
       dec current
       out OCR0A,current     ; пишем новое значение
       rjmp Exit
Equal: cbr flags,0b00000010  ; выход равен уставке, сбрасываем флаг
       rjmp End_cho          ; и выходим
Exit:  ldi output_counter,255
       ldi output_counter2,output_time
End_cho:
       ret
 
;--- Меняем уставку -----------
Setup_update:
       dec  setup_counter
       brne End_chs   ; если счётчик не отсчитал интервал - прыгаем
       ldi  setup_counter,255
       dec  setup_counter2
       brne End_chs   ; если счётчик не отсчитал интервал - прыгаем
       ldi  setup_counter2,255
       dec  setup_counter3
       brne End_chs   ; если счётчик не отсчитал интервал - прыгаем
Setup_change:
       sbis PINB,1    ; если первая кнопка не нажата - пропустить
       rjmp Dec_setup
       sbis PINB,2    ; если вторая кнопка не нажата - пропустить
       rjmp Inc_setup
       cbr  flags,0b00000001 ; отменяем изменение задания
       rjmp End_chs
Dec_setup:
       dec setup
       cp  setup,minimum
       brlo SetMin    ; если новое задание меньше минимального - ставим минимум
       rjmp ExitSetup
SetMin: mov setup,minimum
       rjmp ExitSetup
Inc_Setup:
       inc setup
       cp maximum,setup
       brlo SetMax    ; если максимум меньше нового задания - ставим максимум
       rjmp ExitSetup
SetMax: mov setup,maximum
ExitSetup:
       sbr flags,0b00000010   ; выход нужно изменить
       ldi setup_counter, 255 ; готовимся отсчитывать
       ldi setup_counter2,255 ; следующий интервал
       sbrc flags,2           ; если флаг поднят - fast режим
       rjmp Fast_change
Slow_change:
       inc  counter_sch       ; увеличиваем счётчик изменений задания
       sbrc counter_sch,2     ; если 2-й бит поднят - перекл. в быстрый режим
       sbr  flags,0b00000100  ; fast change
       ldi  setup_counter3,slow_setup
       rjmp End_chs
Fast_change:
       ldi setup_counter3,fast_setup
End_chs:
       ret
 
;-- ОБРАБОТЧИК ПРЕРЫВАНИЯ ПО ВХОДУ
Pch:   sbr  flags,0b00000001  ; ставим флаг изменения задания
       ldi  setup_counter,255
       ldi  setup_counter2,1
       ldi  setup_counter3,1
       clr  counter_sch       ; обнуляем счётчик изменений
       cbr  flags,0b00000100  ; slow change
       reti                   ; и выходим

[свернуть]

Вот и вся программа. Чтобы всё корректно работало — в контроллере должны быть «запрограммированы» фьюзы SPIEN, SUT0 и CKSEL0 (то есть в PonyProg напротив них должны стоять галочки).

Скачать одним архивом алгоритм, исходники и прошивку

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