avm.asm 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517
  1. ; Автор: shilow@ukr.net
  2. ; Дата: июнь 2015
  3. ; Название: avm
  4. ; Версия: 4
  5. ; Имя файла: avm.asm, avm.spl7, avm.lay6
  6. ; Для AVR: ATtiny24A
  7. ; Тактовая частота: 8MHz, внутренний RC-генератор
  8. ; Выполняемые функции: Автомобильный вольтемтр
  9. ; см. ReadMe.txt
  10. ;******************************
  11. ; инклуды
  12. .nolist
  13. .include "tn24Adef.inc"
  14. .list
  15. ;******************************
  16. ; определения
  17. .def job0 = r0
  18. .def job1 = r1
  19. .def job2 = r2
  20. .def job3 = r3
  21. .def LED1 = r4
  22. .def LED2 = r5
  23. .def LED3 = r6
  24. .def LED4 = r7
  25. ; r8
  26. ; r9
  27. ; r10
  28. ; r11
  29. ; r12
  30. ; r13
  31. .def msrL = r14 ; результат измерений
  32. .def msrH = r15
  33. .def temp = r16 ; рабочая переменная
  34. .def tmp1 = r17
  35. .def cnt1 = r18 ; рабочий счётчик, счётчик циклов АЦП
  36. .def cnt2 = r19 ; счётчик циклов Led_Out
  37. .def LEDN = r20
  38. .def mIdx = r21 ; индекс буфера измерений
  39. ; r22
  40. ;.def = r23 ;
  41. ;.def = r24 ;
  42. .def flags = r25
  43. ; r26 XL OUT_NIBBL
  44. ; r27 XH
  45. ; r28 YL вывод на ндикатор
  46. ; r29 YH
  47. ; r30 ZL OUT_NIBBL, BIN2BCD, mBuffer
  48. ; r31 ZH
  49. ;******************************
  50. ; константы
  51. .equ AtBCD0 = 0 ;address of job0
  52. .equ AtBCD2 = 2 ;address of job2
  53. .equ AtLED1 = 4 ;address of LED1
  54. .equ MEASURE = 7 ; 7bit of flags -- пора мерять
  55. .equ NEED_DOT = 6 ; 6bit of flags -- нужна точка
  56. .equ DISP_PORT = PORTA
  57. .equ DISP_DDR = DDRA
  58. .equ LA1 = 4
  59. .equ LA2 = 5
  60. .equ LA3 = 6
  61. .equ LA4 = 7
  62. .equ IND_LA = (1<<LA1)|(1<<LA2)|(1<<LA3)|(1<<LA4)
  63. .equ FIRST_LED = 0b11101111
  64. .equ SER_PORT = PORTB
  65. .equ SER_DDR = DDRB
  66. .equ SCK = 0
  67. .equ DATA = 1
  68. .equ LOCK = 2
  69. .equ T1DIVL = 0x8F
  70. .equ T1DIVH = 0xFD ; 0x10000-8MHz/64/200Hz
  71. ;.equ T0DIV = 0x83 ; 0x100-8MHz/256/250Hz
  72. .equ VREF = 4989 ; опорное напряжение, миливольты
  73. .equ SPH = 0x3e
  74. .equ DOT_BIT = 5 ; bit5 -- точка
  75. .equ BuffShift = 4 ; сколько раз сдвинуть вправо сумму буфера для усреднения
  76. .equ mBuffSize = 8 ; ёмкось буфера измерений
  77. ;******************************
  78. ; макросы
  79. ; сохраняем в стек SREG и R16
  80. .MACRO PUSHF
  81. PUSH R16
  82. IN R16,SREG
  83. PUSH R16
  84. .ENDM
  85. ; восстанавливаем из стека SREG и R16
  86. .MACRO POPF
  87. POP R16
  88. OUT SREG,R16
  89. POP R16
  90. .ENDM
  91. ;******************************
  92. ; ячейки в СОЗУ
  93. .DSEG
  94. .ORG SRAM_START
  95. mBuffer: .byte 2*mBuffSize ; буфер измерений
  96. ;Display: .byte 4 ; 4 байта для индикатора
  97. ;******************************
  98. ; константы в EEPROM
  99. ;.ESEG
  100. ;smpl1: .DW 0x0000 ; sample 1
  101. ;smpl2: .DB 0x05 ; sample 2
  102. ;******************************
  103. ; память программ
  104. .CSEG
  105. .ORG 0
  106. ;******************************
  107. ; Таблица векторов прерываний
  108. rjmp RESET ; Reset Handler
  109. reti;jmp INT0 ; IRQ0 Handler
  110. reti;jmp PCINT0 ; PCINT0 Handler
  111. reti;jmp PCINT1 ; PCINT1 Handler
  112. reti;jmp WDT ; Watchdog Interrupt Handler
  113. reti;jmp TIM1_CAPT ; Timer1 Capture Handler
  114. reti;jmp TIM1_COMPA ; Timer1 Compare A Handler
  115. reti;jmp TIM1_COMPB ; Timer1 Compare B Handler
  116. rjmp TIM1_OVF ; Timer1 Overflow Handler
  117. reti;jmp TIM0_COMPA ; Timer0 Compare A Handler
  118. reti;jmp TIM0_COMPB ; Timer0 Compare B Handler
  119. reti;rjmp TIM0_OVF ; Timer0 Overflow Handler
  120. reti;jmp ANA_COMP ; Analog Comparator Handler
  121. rjmp ADCC ; ADC Conversion Handler
  122. ; rjmp EE_RDY ; EEPROM Ready Handler
  123. ; rjmp USI_STR ; USI STart Handler
  124. ; rjmp USI_OVF ; USI Overflow Handler
  125. ;******************************
  126. ;;;;; Первичная инициализация
  127. RESET:
  128. ldi temp,high(RAMEND); Main program start
  129. out SPH,temp
  130. ldi temp,low(RAMEND)
  131. out SPL,temp; Set Stack Pointer to top of RAM
  132. ;;; выкл. аналог, компаратор
  133. ldi temp,1<<ACD
  134. out ACSR,temp
  135. ;;; настроим АЦП. опора - Vcc, канал 1, 62.5 kHz
  136. ldi temp,1<<MUX0
  137. out ADMUX,temp
  138. ldi temp,(1<<ADEN)|(1<<ADSC)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)
  139. out ADCSRA,temp
  140. ldi temp,(1<<ADC1D)
  141. out DIDR0,temp
  142. ;;; таймер0 интервалы в 4 мсек (250 Hz). прескалер = 256
  143. ; ldi temp,(1<<CS02)
  144. ; out TCCR0B,temp
  145. ; ldi temp,T0DIV
  146. ; out TCNT0,temp ; запустили таймер
  147. ; прерывания от таймеров
  148. ; ldi temp,(1<<TOIE0)
  149. ; out TIMSK0,temp ; разрешим прерывание по переполнению
  150. ;; timer1 - частота 200 гц, при тактовой 8МГц, прескалер=64
  151. ldi temp,(3<<CS10)
  152. out TCCR1B,temp
  153. ldi temp,T1DIVH
  154. out TCNT1H,temp
  155. ldi temp,T1DIVL
  156. out TCNT1L,temp ; запустили таймер 1
  157. ; разрешаем прерывание от таймера
  158. ldi temp,1<<TOIE1
  159. out TIMSK1,temp
  160. ;;; init
  161. ldi LEDN,FIRST_LED ; первый индикатор
  162. ldi temp,IND_LA
  163. out DISP_DDR,temp
  164. out DISP_PORT,temp
  165. ldi temp,(1<<SCK)|(1<<DATA)|(1<<LOCK)
  166. out SER_DDR,temp
  167. clr YH
  168. ldi YL,AtLED1 ; загружаем адрес 1-го индикатора
  169. ldi temp,0x30
  170. mov LED1,temp
  171. mov LED2,temp
  172. mov LED3,temp
  173. mov LED4,temp
  174. ;;; Enable interrupts
  175. sei
  176. ;******************************
  177. ;;;;; Основная программа
  178. BEGIN:
  179. ;;; Подготовимся к новым измерениям
  180. clr msrL
  181. clr msrH ; очистили хранилище результата
  182. ldi cnt1,64 ; загрузили счётчик измерений
  183. sbi ADCSRA,ADIE ; разрешаем прерывания от АЦП
  184. ;;; Конец цикла, в начале :-)
  185. MAIN_END:
  186. rcall WAIT_IRQ
  187. ;;; Если при выходе из прерывания стоит флаг -- запускаем измерения
  188. sbrs flags,MEASURE ; пора мерять?
  189. rjmp CHECK_MEASURE ; если нет -- идём дальше
  190. cbr flags,1<<MEASURE ; сбросим флаг
  191. ;;; запускаем новый цикл измерений
  192. START_MEASURE:
  193. sbi ADCSRA,ADSC ; запуск нов. преобразования
  194. rjmp MAIN_END ; и спим дальше
  195. ;;; Если нет флага, значит прерывание было от АЦП
  196. CHECK_MEASURE:
  197. tst cnt1 ; закончили измерения?
  198. brne MAIN_END ; если нет -- спим дальше
  199. ;; закончили, отключим прерывания
  200. cbi ADCSRA,ADIE
  201. ;;; Здесь происходят все преобразования
  202. ML1:
  203. ;; у нас 64 измерения, оверсэмплинг. пока делим на 8
  204. lsr msrH
  205. ror msrL
  206. lsr msrH
  207. ror msrL
  208. lsr msrH
  209. ror msrL
  210. ; поделили результат на 2^3
  211. ;; сохраняем текущий результат в кольцевой буфер
  212. ldi ZL,low(mBuffer)
  213. ldi ZH,0
  214. mov temp,mIdx
  215. lsl temp
  216. add ZL,temp
  217. st Z+,msrL
  218. st Z,msrH
  219. inc mIdx
  220. cpi mIdx,mBuffSize
  221. brlo ML2
  222. clr mIdx
  223. ;; берём среднее за mBuffSize измерений
  224. ML2: ldi ZL,low(mBuffer)
  225. clr ZH
  226. clr msrH
  227. clr msrL
  228. clr cnt1
  229. ML2a: ld temp,Z+
  230. add msrL,temp
  231. ld temp,Z+
  232. adc msrH,temp
  233. inc cnt1
  234. cpi cnt1,mBuffSize
  235. brlo ML2a
  236. ; закончили суммировать, делим на 8
  237. lsr msrH
  238. ror msrL
  239. lsr msrH
  240. ror msrL
  241. lsr msrH
  242. ror msrL
  243. ;; теперь нужно умножить на Vref и поделить на 8192 -- получим миливольты на входе АЦП
  244. ;; а затем умножить на 4, чтобы получить напряжение на входе делителя.
  245. ;; т.е. делим на 2048
  246. ;; Но... результат умножения - 4 байта, мы получим только три старших, т.е. на 256 уже
  247. ;; поделено, остаётся доделить на 8 и получим 2 байта результата.
  248. ldi temp,low(VREF)
  249. ldi tmp1,high(VREF)
  250. rcall MPY16U
  251. lsr job2
  252. ror job1
  253. ror job0
  254. lsr job2
  255. ror job1
  256. ror job0
  257. lsr job2
  258. ror job1
  259. ror job0
  260. ;; в реале нужно будет множить например на 400 и делить на 100 -- под реальный делитель.
  261. ; тут можно добавить коррекцию усиления.
  262. ; сейчас текущий результат измерний в job1:job0. Перенесём в msr
  263. mov msrH,job1
  264. mov msrL,job0
  265. ; наш результат, это по сути среднее за 64 измерения, в миливольтах, по входу.
  266. ;;; преобразуем msr в десятичный вид и положим в буфер индикатора
  267. rcall bin2BCD16
  268. ; 5 значащих цифр в job2:job1:job0
  269. ldi XL,AtLED1
  270. clr XH
  271. ;; первый, старший разряд -- десятки вольт, младший нибл job2
  272. mov temp,job2
  273. ; проверим на ведущий 0
  274. andi temp,0x0F ; отсекаем ст ниббл
  275. tst temp
  276. brne MakeDigit3
  277. ldi temp,0xFF ; код пустого индикатора
  278. st X+,temp
  279. rjmp MakeDigit2
  280. MakeDigit3:
  281. rcall OUT_NIBBL ; ниббл 3, десятки
  282. ;; второй разряд -- единицы вольт, старший нибл job1
  283. MakeDigit2:
  284. mov temp,job1
  285. swap temp
  286. sbr flags,1<<NEED_DOT
  287. rcall OUT_NIBBL
  288. cbr flags,1<<NEED_DOT
  289. ;; третий разряд -- десятые вольта, младший нибл job1
  290. mov temp,job1
  291. rcall OUT_NIBBL
  292. ;; четвёртый разряд -- сотые вольта, старший нибл job0
  293. mov temp,job0
  294. swap temp
  295. rcall OUT_NIBBL
  296. ;;; и на новый виток
  297. rjmp BEGIN
  298. ;******************************
  299. ;;;;; Подпрограммы
  300. ;******************************
  301. ; делать нечего - поспим... ждём любое прерывание
  302. WAIT_IRQ:
  303. ldi temp,1<<SE
  304. out MCUCR,temp ; простой режим сна
  305. sleep ; спим.
  306. clr temp
  307. out MCUCR,temp ; со сна
  308. ret
  309. ;******************************
  310. ;** переводим младший ниббл temp в код для 7-сгм инд и кладём его в озу по адр X
  311. OUT_NIBBL:
  312. ldi ZL,low(LEDnd*2)
  313. ldi ZH,high(LEDnd*2); адрес кодов для индикатора
  314. andi temp,0x0F ; отсекаем ст ниббл. получаем смещение
  315. add ZL,temp
  316. adc ZH,XH ; добавили смещение
  317. lpm temp,Z ; прочитали код
  318. sbrc flags,NEED_DOT ; нужна точка?
  319. cbr temp,1<<DOT_BIT ; включаем точку
  320. st X+,temp ; записали код в позицию индикатора
  321. ret
  322. ;******************************
  323. ; перемножение двух 16-разрядных величин, результат 3 байта, рабочая
  324. ; множимое msrH:msrL, множитель tmp1:temp, результат job2:job1:job0
  325. ; использует cnt1
  326. MPY16U:
  327. clr job2 ;clear 1 highest bytes of result
  328. ldi cnt1,16 ;init loop counter
  329. m16u_1: lsr tmp1
  330. ror temp
  331. brcc noad8 ;if bit 0 of multiplier set
  332. add job1,msrL ;add multiplicand Low to byte 1 of res
  333. adc job2,msrH ;add multiplicand high to byte 2 of res
  334. noad8: ror job2 ;shift right result byte 2
  335. ror job1 ;rotate right result byte 1
  336. ror job0 ;rotate result byte 0 and multiplier Low
  337. dec cnt1 ;decrement loop counter
  338. brne m16u_1 ;if not done, loop more
  339. ret
  340. ;******************************
  341. ;* This subroutine converts a 16-bit number (msrH:msrL) to a 5-digit
  342. ;* packed BCD number represented by 3 bytes (job2:job1:job0).
  343. ;* MSD of the 5-digit number is placed in the lowermost nibble of job2.
  344. bin2BCD16:
  345. ldi cnt1,16 ;Init loop counter
  346. clr job2 ;clear result (3 bytes)
  347. clr job1
  348. clr job0
  349. clr ZH ;clear ZH
  350. bBCDx_1:lsl msrL ;shift input value
  351. rol msrH ;through all bytes
  352. rol job0
  353. rol job1
  354. rol job2
  355. dec cnt1 ;decrement loop counter
  356. brne bBCDx_2 ;if counter not zero
  357. ret ; return
  358. bBCDx_2:ldi ZL,AtBCD2+1 ;Z points to result MSB + 1
  359. bBCDx_3:ld temp,-Z ;get (Z) with pre-decrement
  360. subi temp,-$03 ;add 0x03
  361. sbrc temp,3 ;if bit 3 not clear
  362. st Z,temp ; store back
  363. ld temp,Z ;get (Z)
  364. subi temp,-$30 ;add 0x30
  365. sbrc temp,7 ;if bit 7 not clear
  366. st Z,temp ; store back
  367. cpi ZL,AtBCD0 ;done all three?
  368. brne bBCDx_3 ;loop again if not
  369. rjmp bBCDx_1
  370. ;******************************
  371. ;;;;; Обработчики прерываний
  372. ;******************************
  373. ; Timer1 Overflow Handler
  374. TIM1_OVF:
  375. ; выводим данные на 7-ми сегментные индикаторы
  376. PUSHF
  377. ldi temp,T1DIVH
  378. out TCNT1H,temp
  379. ldi temp,T1DIVL
  380. out TCNT1L,temp ; перезапустили таймер 1
  381. ;***************************
  382. ; вывод на индикатор очередной цифры
  383. in temp,DISP_PORT
  384. ori temp,IND_LA ; изменяем только нужные пины
  385. out DISP_PORT,temp ; погасили все индикаторы
  386. cbi SER_PORT,LOCK ; подгтовили защёлку
  387. ld temp,Y+ ; в темп - текущая цифра, адрес=+1
  388. ;;; выпихнем очередной байт в регистр
  389. ldi cnt2,8 ; счетчик бит
  390. TLO1: cbi SER_PORT,SCK ; SCK=0
  391. lsl temp ; старший бит в перенос
  392. brcc TLO0 ; если в переносе 0 — перейти
  393. sbi SER_PORT,DATA ; выдали 1
  394. rjmp TLOE
  395. TLO0: cbi SER_PORT,DATA ; выдали 0
  396. TLOE: sbi SER_PORT,SCK ; SCK=1, сдвиг данных
  397. dec cnt2
  398. brne TLO1
  399. sbi SER_PORT,LOCK ; из 0 в 1 -- защёлкивание данных в регистре
  400. in temp,DISP_PORT
  401. ori temp,IND_LA
  402. and temp,LEDN
  403. out DISP_PORT,temp ; включаем очередной индикатор
  404. lsl LEDN ; сдвинули - следующий индикатор
  405. brlo T1L2 ; вышли за границы? проверка
  406. ldi LEDN,FIRST_LED ; да, загружаем начальное значение
  407. ldi YL,AtLED1 ; загружаем адрес 1-го индикатора
  408. T1L2:
  409. sbr flags,1<<MEASURE ; пора мерять
  410. ;;; выход
  411. POPF
  412. reti
  413. ;***************************
  414. ; ADC Conversion Handler
  415. ; суммируем результаты измерений, уменьшаем счётчик.
  416. ADCC:
  417. in temp,ADCL
  418. add msrL,temp
  419. in temp,ADCH
  420. adc msrH,temp
  421. dec cnt1
  422. reti
  423. ;***************************
  424. ; байты для вывода 0-9 на индикаторы с ОА, без точки. точка вкл установкой бита 5
  425. LEDnd: .DB 0x30,0xF3,0x2A,0xA2,0xE1,0xA4,0x24,0xF2,0x20,0xA0
  426. .exit
  427. INT0: ; IRQ0 Handler
  428. reti
  429. PCINT0: ; PCINT0 Handler
  430. reti
  431. PCINT1: ; PCINT1 Handler
  432. reti
  433. WDT: ; Watchdog Interrupt Handler
  434. reti
  435. TIM1_CAPT: ; Timer1 Capture Handler
  436. reti
  437. TIM1_COMPA: ; Timer1 Compare A Handler
  438. reti
  439. TIM1_COMPB: ; Timer1 Compare B Handler
  440. reti
  441. TIM1_OVF: ; Timer1 Overflow Handler
  442. reti
  443. TIM0_COMPA: ; Timer0 Compare A Handler
  444. reti
  445. TIM0_COMPB: ; Timer0 Compare B Handler
  446. reti
  447. ANA_COMP: ; Analog Comparator Handler
  448. reti
  449. EE_RDY: ; EEPROM Ready Handler
  450. reti
  451. USI_STR: ; USI STart Handler
  452. reti
  453. USI_OVF: ; USI Overflow Handler
  454. reti
  455. ;
  456. .exit