123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502 |
- ; Автор: shilow@ukr.net
- ; Дата: октябрь 2014
- ; Название: avm
- ; Версия: 3
- ; Имя файла: avm.asm, avm.spl7, avm.lay6
- ; Для AVR: ATtiny24A
- ; Тактовая частота: 8MHz, внутренний RC-генератор
- ; Выполняемые функции: Автомобильный вольтемтр
- ; см. ReadMe.txt
- ; схема по "Вариант 5"
- ;******************************
- ; инклуды
- .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
- ; r21
- ; r22
- .def bAddr = r23 ; адрес текущей позиции в буфере измерений
- .def bIdx = r24 ; индекс буфера
- .def flags = r25
- ; r26 XL OUT_NIBBL
- ; r27 XH
- ; r28 YL вывод на ндикатор
- ; r29 YH
- ; r30 ZL OUT_NIBBL, BIN2BCD
- ; r31 ZH
- ;******************************
- ; константы
- .equ AtBCD0 = 0 ;address of job0
- .equ AtBCD2 = 2 ;address of job2
- .equ AtLED1 = 4 ;address of LED1
- .equ MSEK100 = 7 ; 7bit of flags == 0.1 sek
- .equ NEED_DOT = 6 ; нужна точка
- .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 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 = 498 ; опорное напряжение, десятки миливольт
- .equ SPH = 0x3e
- .equ DOT_BIT = 5 ; bit5 -- точка
- .equ BuffSize = 16 ; глубина буфера результатов измерений
- .equ BuffShift = 4 ; сколько раз сдвинуть вправо сумму буфера для усреднения
- ;******************************
- ; макросы
- ; сохраняем в стек 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
- ;Display: .byte 4 ; 4 байта для индикатора
- Buffer: .byte 2*BuffSize
- ;******************************
- ; константы в 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
- reti;jmp TIM1_OVF ; Timer1 Overflow Handler
- reti;jmp TIM0_COMPA ; Timer0 Compare A Handler
- reti;jmp TIM0_COMPB ; Timer0 Compare B Handler
- 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, непрерывное преобразование. temp=0b00100010
- ldi temp,1<<MUX0
- out ADMUX,temp
- ldi temp,(1<<ADEN)|(1<<ADSC)|(6<<ADPS0)
- out ADCSRA,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,0b11101111 ; первый индикатор
- 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
- ldi bAddr,low(Buffer)
- clr bIdx
- ;;; Enable interrupts
- sei
- ;******************************
- ;;;;; Основная программа
- BEGIN:
- ;;; запускаем измерения и преобразования
- clr msrL
- clr msrH ; очистили хранилище результата
- ldi cnt1,16 ; загрузили счётчик измерений
- sbi ADCSRA,ADATE ; запуск free running mode
- sbi ADCSRA,ADIE ; разрешаем прерывания от АЦП
- sbi ADCSRA,ADSC ; запуск нов. преобразования
- ML1:
- ; rjmp PC+1 ; nop x 2 -- 1 world 2 takta. или sleep?
- rcall WAIT_IRQ ; один цикл измерения, ЕМНИП больше 100 мкс...
- tst cnt1 ; гоняем пустой цикл пока будут происходить 16 измерений
- brne ML1
- cbi ADCSRA,ADIE ; запрещаем прерывания от АЦП
- cbi ADCSRA,ADATE ; stop free running mode
- lsr msrH
- ror msrL
- lsr msrH
- ror msrL ; поделили результат на 2^2
- ; теперь множим на Vref и делим на 4096
- ldi temp,low(VREF)
- ldi tmp1,high(VREF)
- rcall MPY16U
- lsr job1 ; выбросили job0 == поделили на 256
- ror job0
- lsr job1
- ror job0
- ;; lsr job1
- ;; ror job0
- ;; lsr job1
- ;; ror job0 ; доделили на 16, итого поделили на 4096
- ; ldi cnt1,4
- ;ML2: lsr job2
- ; ror job1
- ; dec cnt1
- ; brne ML2
- ; mov msrL,job0
- ; mov msrH,job1 ; в MSR - напряжение в 10мВ
- ;!!! умножить на входной делитель
- ;; -- не доделили на 4 == умножили теперь на 4.
- ;; в реале нужно будет множить например на 400 и делить на 100 -- под реальный делитель.
- ; тут можно добавить коррекцию усиления.
- ; сейчас текущий результат измерний в job1:job0
- ;;; усреднения
- ; сохраним текущий результат
- mov XL,bAddr
- clr XH
- st X+,job0
- st X,job1 ; сохранили
- sbiw X,1 ; буфер в старой позиции -- на текущем результате
- clr msrL
- clr msrH
- ; теперь как-то нужно просуммировать содержимое буфера
- ldi cnt1,BuffSize
- BuffSummarize:
- ld temp,X+
- add msrL,temp
- ld temp,X+
- adc msrH,temp
- inc bIdx
- cpi bIdx,BuffSize
- brne BuffChkCnt
- ldi XL,low(Buffer)
- clr bIdx
- BuffChkCnt:
- dec cnt1
- brne BuffSummarize
- adiw X,2 ; адрес буфера - на следующую ячейку
- inc bIdx
- cpi bIdx,BuffSize
- brne BuffAddrSave
- ldi XL,low(Buffer)
- clr bIdx
- BuffAddrSave:
- mov bAddr,XL ; сохранили текущий адрес буфера
- ; тут имеем в msr сумму всех элементов массива. делим:
- ldi cnt1,BuffShift
- BuffDivide:
- lsr msrH
- ror msrL
- dec cnt1
- brne BuffDivide
- ; теперь в msr среднее от содержимого буфера.
- ; адрес и индекс буфера показывают следующую позицию.
-
- ;;; преобразуем msr в десятичный вид и положим в буфер индикатора
- rcall bin2BCD16
- ; 4 значащих цифры в job1:job0
- ldi XL,AtLED1
- clr XH
- mov temp,job1
- swap temp
- ; проверим на ведущий 0
- andi temp,0x0F ; отсекаем ст ниббл
- tst temp
- brne MakeDigit3
- ldi temp,0xFF ; код пустого индикатора
- st X+,temp
- rjmp MakeDigit2
- MakeDigit3:
- rcall OUT_NIBBL ; ниббл 3, десятки
- MakeDigit2:
- mov temp,job1
- sbr flags,1<<NEED_DOT
- rcall OUT_NIBBL ; ниббл 2, единицы
- cbr flags,1<<NEED_DOT
- mov temp,job0
- swap temp
- rcall OUT_NIBBL ; ниббл 1, десятые
- mov temp,job0
- rcall OUT_NIBBL ; ниббл 0, сотые
- ;;; Конец цикла
- rcall WAIT_IRQ
- 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
- ;******************************
- ;;;;; Обработчики прерываний
- ;******************************
- ; Timer0 Overflow Handler
- TIM0_OVF:
- ; выводим данные на 7-ми сегментные индикаторы
- PUSHF
- ldi temp,T0DIV
- out TCNT0,temp ; перезапустили таймер 0
- ;***************************
- ; вывод на индикатор очередной цифры
- in temp,DISP_PORT
- ori temp,IND_LA ; изменяем только нужные пины
- out DISP_PORT,temp ; погасили все индикаторы
- cbi SER_PORT,LOCK ; подгтовили защёлку
- ld temp,Y+ ; в темп - текущая цифра, адрес=+1
- ;;; выпихнем очередной байт в регистр
- ldi cnt2,8 ; счетчик бит
- LO1: cbi SER_PORT,SCK ; SCK=0
- lsl temp ; старший бит в перенос
- brcc LO0 ; если в переносе 0 — перейти
- sbi SER_PORT,DATA ; выдали 1
- rjmp LOE
- LO0: cbi SER_PORT,DATA ; выдали 0
- LOE: sbi SER_PORT,SCK ; SCK=1, сдвиг данных
- dec cnt2
- brne LO1
- sbi SER_PORT,LOCK ; из 0 в 1 -- защёлкивание данных в регистре
- in temp,DISP_PORT
- ori temp,IND_LA
- and temp,LEDN
- out DISP_PORT,temp ; включаем очередной индикатор
- lsl LEDN ; сдвинули - следующий индикатор
- brlo T0L2 ; вышли за границы? проверка
- ldi LEDN,0b11101111 ; да, загружаем начальное значение
- ldi YL,AtLED1 ; загружаем адрес 1-го индикатора
- T0L2:
- 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
|