123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517 |
- ; Автор: shilow@ukr.net
- ; Дата: июнь 2015
- ; Название: avm
- ; Версия: 4
- ; Имя файла: avm.asm, avm.spl7, avm.lay6
- ; Для AVR: ATtiny24A
- ; Тактовая частота: 8MHz, внутренний RC-генератор
- ; Выполняемые функции: Автомобильный вольтемтр
- ; см. ReadMe.txt
- ;******************************
- ; инклуды
- .nolist
- .include "tn24Adef.inc"
- .list
- ;******************************
- ; определения
- .def job0 = r0
- .def job1 = r1
- .def job2 = r2
- .def job3 = r3
- .def LED1 = r4
- .def LED2 = r5
- .def LED3 = r6
- .def LED4 = r7
- ; r8
- ; r9
- ; r10
- ; r11
- ; r12
- ; r13
- .def msrL = r14 ; результат измерений
- .def msrH = r15
- .def temp = r16 ; рабочая переменная
- .def tmp1 = r17
- .def cnt1 = r18 ; рабочий счётчик, счётчик циклов АЦП
- .def cnt2 = r19 ; счётчик циклов Led_Out
- .def LEDN = r20
- .def mIdx = r21 ; индекс буфера измерений
- ; r22
- ;.def = r23 ;
- ;.def = r24 ;
- .def flags = r25
- ; r26 XL OUT_NIBBL
- ; r27 XH
- ; r28 YL вывод на ндикатор
- ; r29 YH
- ; r30 ZL OUT_NIBBL, BIN2BCD, mBuffer
- ; r31 ZH
- ;******************************
- ; константы
- .equ AtBCD0 = 0 ;address of job0
- .equ AtBCD2 = 2 ;address of job2
- .equ AtLED1 = 4 ;address of LED1
- .equ MEASURE = 7 ; 7bit of flags -- пора мерять
- .equ NEED_DOT = 6 ; 6bit of flags -- нужна точка
- .equ DISP_PORT = PORTA
- .equ DISP_DDR = DDRA
- .equ LA1 = 4
- .equ LA2 = 5
- .equ LA3 = 6
- .equ LA4 = 7
- .equ IND_LA = (1<<LA1)|(1<<LA2)|(1<<LA3)|(1<<LA4)
- .equ FIRST_LED = 0b11101111
- .equ SER_PORT = PORTB
- .equ SER_DDR = DDRB
- .equ SCK = 0
- .equ DATA = 1
- .equ LOCK = 2
- .equ T1DIVL = 0x8F
- .equ T1DIVH = 0xFD ; 0x10000-8MHz/64/200Hz
- ;.equ T0DIV = 0x83 ; 0x100-8MHz/256/250Hz
- .equ VREF = 4989 ; опорное напряжение, миливольты
- .equ SPH = 0x3e
- .equ DOT_BIT = 5 ; bit5 -- точка
- .equ BuffShift = 4 ; сколько раз сдвинуть вправо сумму буфера для усреднения
- .equ mBuffSize = 8 ; ёмкось буфера измерений
- ;******************************
- ; макросы
- ; сохраняем в стек SREG и R16
- .MACRO PUSHF
- PUSH R16
- IN R16,SREG
- PUSH R16
- .ENDM
- ; восстанавливаем из стека SREG и R16
- .MACRO POPF
- POP R16
- OUT SREG,R16
- POP R16
- .ENDM
- ;******************************
- ; ячейки в СОЗУ
- .DSEG
- .ORG SRAM_START
- mBuffer: .byte 2*mBuffSize ; буфер измерений
- ;Display: .byte 4 ; 4 байта для индикатора
- ;******************************
- ; константы в EEPROM
- ;.ESEG
- ;smpl1: .DW 0x0000 ; sample 1
- ;smpl2: .DB 0x05 ; sample 2
- ;******************************
- ; память программ
- .CSEG
- .ORG 0
- ;******************************
- ; Таблица векторов прерываний
- rjmp RESET ; Reset Handler
- reti;jmp INT0 ; IRQ0 Handler
- reti;jmp PCINT0 ; PCINT0 Handler
- reti;jmp PCINT1 ; PCINT1 Handler
- reti;jmp WDT ; Watchdog Interrupt Handler
- reti;jmp TIM1_CAPT ; Timer1 Capture Handler
- reti;jmp TIM1_COMPA ; Timer1 Compare A Handler
- reti;jmp TIM1_COMPB ; Timer1 Compare B Handler
- rjmp TIM1_OVF ; Timer1 Overflow Handler
- reti;jmp TIM0_COMPA ; Timer0 Compare A Handler
- reti;jmp TIM0_COMPB ; Timer0 Compare B Handler
- reti;rjmp TIM0_OVF ; Timer0 Overflow Handler
- reti;jmp ANA_COMP ; Analog Comparator Handler
- rjmp ADCC ; ADC Conversion Handler
- ; rjmp EE_RDY ; EEPROM Ready Handler
- ; rjmp USI_STR ; USI STart Handler
- ; rjmp USI_OVF ; USI Overflow Handler
- ;******************************
- ;;;;; Первичная инициализация
- RESET:
- ldi temp,high(RAMEND); Main program start
- out SPH,temp
- ldi temp,low(RAMEND)
- out SPL,temp; Set Stack Pointer to top of RAM
- ;;; выкл. аналог, компаратор
- ldi temp,1<<ACD
- out ACSR,temp
- ;;; настроим АЦП. опора - Vcc, канал 1, 62.5 kHz
- ldi temp,1<<MUX0
- out ADMUX,temp
- ldi temp,(1<<ADEN)|(1<<ADSC)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)
- out ADCSRA,temp
- ldi temp,(1<<ADC1D)
- out DIDR0,temp
- ;;; таймер0 интервалы в 4 мсек (250 Hz). прескалер = 256
- ; ldi temp,(1<<CS02)
- ; out TCCR0B,temp
- ; ldi temp,T0DIV
- ; out TCNT0,temp ; запустили таймер
- ; прерывания от таймеров
- ; ldi temp,(1<<TOIE0)
- ; out TIMSK0,temp ; разрешим прерывание по переполнению
- ;; timer1 - частота 200 гц, при тактовой 8МГц, прескалер=64
- ldi temp,(3<<CS10)
- out TCCR1B,temp
- ldi temp,T1DIVH
- out TCNT1H,temp
- ldi temp,T1DIVL
- out TCNT1L,temp ; запустили таймер 1
- ; разрешаем прерывание от таймера
- ldi temp,1<<TOIE1
- out TIMSK1,temp
- ;;; init
- ldi LEDN,FIRST_LED ; первый индикатор
- ldi temp,IND_LA
- out DISP_DDR,temp
- out DISP_PORT,temp
- ldi temp,(1<<SCK)|(1<<DATA)|(1<<LOCK)
- out SER_DDR,temp
- clr YH
- ldi YL,AtLED1 ; загружаем адрес 1-го индикатора
- ldi temp,0x30
- mov LED1,temp
- mov LED2,temp
- mov LED3,temp
- mov LED4,temp
- ;;; Enable interrupts
- sei
- ;******************************
- ;;;;; Основная программа
- BEGIN:
- ;;; Подготовимся к новым измерениям
- clr msrL
- clr msrH ; очистили хранилище результата
- ldi cnt1,64 ; загрузили счётчик измерений
- sbi ADCSRA,ADIE ; разрешаем прерывания от АЦП
- ;;; Конец цикла, в начале :-)
- MAIN_END:
- rcall WAIT_IRQ
- ;;; Если при выходе из прерывания стоит флаг -- запускаем измерения
- sbrs flags,MEASURE ; пора мерять?
- rjmp CHECK_MEASURE ; если нет -- идём дальше
- cbr flags,1<<MEASURE ; сбросим флаг
- ;;; запускаем новый цикл измерений
- START_MEASURE:
- sbi ADCSRA,ADSC ; запуск нов. преобразования
- rjmp MAIN_END ; и спим дальше
- ;;; Если нет флага, значит прерывание было от АЦП
- CHECK_MEASURE:
- tst cnt1 ; закончили измерения?
- brne MAIN_END ; если нет -- спим дальше
- ;; закончили, отключим прерывания
- cbi ADCSRA,ADIE
- ;;; Здесь происходят все преобразования
- ML1:
- ;; у нас 64 измерения, оверсэмплинг. пока делим на 8
- lsr msrH
- ror msrL
- lsr msrH
- ror msrL
- lsr msrH
- ror msrL
- ; поделили результат на 2^3
- ;; сохраняем текущий результат в кольцевой буфер
- ldi ZL,low(mBuffer)
- ldi ZH,0
- mov temp,mIdx
- lsl temp
- add ZL,temp
- st Z+,msrL
- st Z,msrH
- inc mIdx
- cpi mIdx,mBuffSize
- brlo ML2
- clr mIdx
- ;; берём среднее за mBuffSize измерений
- ML2: ldi ZL,low(mBuffer)
- clr ZH
- clr msrH
- clr msrL
- clr cnt1
- ML2a: ld temp,Z+
- add msrL,temp
- ld temp,Z+
- adc msrH,temp
- inc cnt1
- cpi cnt1,mBuffSize
- brlo ML2a
- ; закончили суммировать, делим на 8
- lsr msrH
- ror msrL
- lsr msrH
- ror msrL
- lsr msrH
- ror msrL
- ;; теперь нужно умножить на Vref и поделить на 8192 -- получим миливольты на входе АЦП
- ;; а затем умножить на 4, чтобы получить напряжение на входе делителя.
- ;; т.е. делим на 2048
- ;; Но... результат умножения - 4 байта, мы получим только три старших, т.е. на 256 уже
- ;; поделено, остаётся доделить на 8 и получим 2 байта результата.
- ldi temp,low(VREF)
- ldi tmp1,high(VREF)
- rcall MPY16U
- lsr job2
- ror job1
- ror job0
- lsr job2
- ror job1
- ror job0
- lsr job2
- ror job1
- ror job0
- ;; в реале нужно будет множить например на 400 и делить на 100 -- под реальный делитель.
- ; тут можно добавить коррекцию усиления.
- ; сейчас текущий результат измерний в job1:job0. Перенесём в msr
- mov msrH,job1
- mov msrL,job0
- ; наш результат, это по сути среднее за 64 измерения, в миливольтах, по входу.
- ;;; преобразуем msr в десятичный вид и положим в буфер индикатора
- rcall bin2BCD16
- ; 5 значащих цифр в job2:job1:job0
- ldi XL,AtLED1
- clr XH
- ;; первый, старший разряд -- десятки вольт, младший нибл job2
- mov temp,job2
- ; проверим на ведущий 0
- andi temp,0x0F ; отсекаем ст ниббл
- tst temp
- brne MakeDigit3
- ldi temp,0xFF ; код пустого индикатора
- st X+,temp
- rjmp MakeDigit2
- MakeDigit3:
- rcall OUT_NIBBL ; ниббл 3, десятки
- ;; второй разряд -- единицы вольт, старший нибл job1
- MakeDigit2:
- mov temp,job1
- swap temp
- sbr flags,1<<NEED_DOT
- rcall OUT_NIBBL
- cbr flags,1<<NEED_DOT
- ;; третий разряд -- десятые вольта, младший нибл job1
- mov temp,job1
- rcall OUT_NIBBL
- ;; четвёртый разряд -- сотые вольта, старший нибл job0
- mov temp,job0
- swap temp
- rcall OUT_NIBBL
- ;;; и на новый виток
- rjmp BEGIN
- ;******************************
- ;;;;; Подпрограммы
- ;******************************
- ; делать нечего - поспим... ждём любое прерывание
- WAIT_IRQ:
- ldi temp,1<<SE
- out MCUCR,temp ; простой режим сна
- sleep ; спим.
- clr temp
- out MCUCR,temp ; со сна
- ret
- ;******************************
- ;** переводим младший ниббл temp в код для 7-сгм инд и кладём его в озу по адр X
- OUT_NIBBL:
- ldi ZL,low(LEDnd*2)
- ldi ZH,high(LEDnd*2); адрес кодов для индикатора
- andi temp,0x0F ; отсекаем ст ниббл. получаем смещение
- add ZL,temp
- adc ZH,XH ; добавили смещение
- lpm temp,Z ; прочитали код
- sbrc flags,NEED_DOT ; нужна точка?
- cbr temp,1<<DOT_BIT ; включаем точку
- st X+,temp ; записали код в позицию индикатора
- ret
- ;******************************
- ; перемножение двух 16-разрядных величин, результат 3 байта, рабочая
- ; множимое msrH:msrL, множитель tmp1:temp, результат job2:job1:job0
- ; использует cnt1
- MPY16U:
- clr job2 ;clear 1 highest bytes of result
- ldi cnt1,16 ;init loop counter
- m16u_1: lsr tmp1
- ror temp
- brcc noad8 ;if bit 0 of multiplier set
- add job1,msrL ;add multiplicand Low to byte 1 of res
- adc job2,msrH ;add multiplicand high to byte 2 of res
- noad8: ror job2 ;shift right result byte 2
- ror job1 ;rotate right result byte 1
- ror job0 ;rotate result byte 0 and multiplier Low
- dec cnt1 ;decrement loop counter
- brne m16u_1 ;if not done, loop more
- ret
- ;******************************
- ;* This subroutine converts a 16-bit number (msrH:msrL) to a 5-digit
- ;* packed BCD number represented by 3 bytes (job2:job1:job0).
- ;* MSD of the 5-digit number is placed in the lowermost nibble of job2.
- bin2BCD16:
- ldi cnt1,16 ;Init loop counter
- clr job2 ;clear result (3 bytes)
- clr job1
- clr job0
- clr ZH ;clear ZH
- bBCDx_1:lsl msrL ;shift input value
- rol msrH ;through all bytes
- rol job0
- rol job1
- rol job2
- dec cnt1 ;decrement loop counter
- brne bBCDx_2 ;if counter not zero
- ret ; return
- bBCDx_2:ldi ZL,AtBCD2+1 ;Z points to result MSB + 1
- bBCDx_3:ld temp,-Z ;get (Z) with pre-decrement
- subi temp,-$03 ;add 0x03
- sbrc temp,3 ;if bit 3 not clear
- st Z,temp ; store back
- ld temp,Z ;get (Z)
- subi temp,-$30 ;add 0x30
- sbrc temp,7 ;if bit 7 not clear
- st Z,temp ; store back
- cpi ZL,AtBCD0 ;done all three?
- brne bBCDx_3 ;loop again if not
- rjmp bBCDx_1
- ;******************************
- ;;;;; Обработчики прерываний
- ;******************************
- ; Timer1 Overflow Handler
- TIM1_OVF:
- ; выводим данные на 7-ми сегментные индикаторы
- PUSHF
- ldi temp,T1DIVH
- out TCNT1H,temp
- ldi temp,T1DIVL
- out TCNT1L,temp ; перезапустили таймер 1
- ;***************************
- ; вывод на индикатор очередной цифры
- in temp,DISP_PORT
- ori temp,IND_LA ; изменяем только нужные пины
- out DISP_PORT,temp ; погасили все индикаторы
- cbi SER_PORT,LOCK ; подгтовили защёлку
- ld temp,Y+ ; в темп - текущая цифра, адрес=+1
- ;;; выпихнем очередной байт в регистр
- ldi cnt2,8 ; счетчик бит
- TLO1: cbi SER_PORT,SCK ; SCK=0
- lsl temp ; старший бит в перенос
- brcc TLO0 ; если в переносе 0 — перейти
- sbi SER_PORT,DATA ; выдали 1
- rjmp TLOE
- TLO0: cbi SER_PORT,DATA ; выдали 0
- TLOE: sbi SER_PORT,SCK ; SCK=1, сдвиг данных
- dec cnt2
- brne TLO1
- sbi SER_PORT,LOCK ; из 0 в 1 -- защёлкивание данных в регистре
- in temp,DISP_PORT
- ori temp,IND_LA
- and temp,LEDN
- out DISP_PORT,temp ; включаем очередной индикатор
- lsl LEDN ; сдвинули - следующий индикатор
- brlo T1L2 ; вышли за границы? проверка
- ldi LEDN,FIRST_LED ; да, загружаем начальное значение
- ldi YL,AtLED1 ; загружаем адрес 1-го индикатора
- T1L2:
- sbr flags,1<<MEASURE ; пора мерять
- ;;; выход
- POPF
- reti
- ;***************************
- ; ADC Conversion Handler
- ; суммируем результаты измерений, уменьшаем счётчик.
- ADCC:
- in temp,ADCL
- add msrL,temp
- in temp,ADCH
- adc msrH,temp
- dec cnt1
- reti
- ;***************************
- ; байты для вывода 0-9 на индикаторы с ОА, без точки. точка вкл установкой бита 5
- LEDnd: .DB 0x30,0xF3,0x2A,0xA2,0xE1,0xA4,0x24,0xF2,0x20,0xA0
- .exit
- INT0: ; IRQ0 Handler
- reti
- PCINT0: ; PCINT0 Handler
- reti
- PCINT1: ; PCINT1 Handler
- reti
- WDT: ; Watchdog Interrupt Handler
- reti
- TIM1_CAPT: ; Timer1 Capture Handler
- reti
- TIM1_COMPA: ; Timer1 Compare A Handler
- reti
- TIM1_COMPB: ; Timer1 Compare B Handler
- reti
- TIM1_OVF: ; Timer1 Overflow Handler
- reti
- TIM0_COMPA: ; Timer0 Compare A Handler
- reti
- TIM0_COMPB: ; Timer0 Compare B Handler
- reti
- ANA_COMP: ; Analog Comparator Handler
- reti
- EE_RDY: ; EEPROM Ready Handler
- reti
- USI_STR: ; USI STart Handler
- reti
- USI_OVF: ; USI Overflow Handler
- reti
- ;
- .exit
|