avm.asm 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509
  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 = 4980 ; опорное напряжение, миливольты
  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,high(mBuffSize)
  214. st Z+,msrL
  215. st Z+,msrH
  216. inc mIdx
  217. cpi mIdx,mBuffSize
  218. brlo ML2
  219. clr mIdx
  220. ;; берём среднее за mBuffSize измерений
  221. ML2: ldi ZL,low(mBuffer)
  222. ldi ZH,high(mBuffSize)
  223. clr msrH
  224. clr msrL
  225. clr cnt1
  226. ML2a: ld temp,Z+
  227. add msrL,temp
  228. ld temp,Z+
  229. adc msrH,temp
  230. inc cnt1
  231. cpi cnt1,mBuffSize
  232. brlo ML2a
  233. ; закончили суммировать, делим на 8
  234. lsr msrH
  235. ror msrL
  236. lsr msrH
  237. ror msrL
  238. lsr msrH
  239. ror msrL
  240. ;; теперь нужно умножить на Vref и поделить на 8192 -- получим миливольты на входе АЦП
  241. ;; а затем умножить на 4, чтобы получить напряжение на входе делителя.
  242. ;; т.е. делим на 2048
  243. ldi temp,low(VREF)
  244. ldi tmp1,high(VREF)
  245. rcall MPY16U ; отбросили job0 == поделили на 256
  246. lsr job2
  247. ror job1
  248. lsr job2
  249. ror job1
  250. lsr job2
  251. ror job1
  252. ;; в реале нужно будет множить например на 400 и делить на 100 -- под реальный делитель.
  253. ; тут можно добавить коррекцию усиления.
  254. ; сейчас текущий результат измерний в job2:job1. Перенесём в msr
  255. mov msrH,job2
  256. mov msrL,job1
  257. ; наш результат, это по сути среднее за 64 измерения, в миливольтах, по входу.
  258. ;;; преобразуем msr в десятичный вид и положим в буфер индикатора
  259. rcall bin2BCD16
  260. ; 5 значащих цифр в job2:job1:job0
  261. ldi XL,AtLED1
  262. clr XH
  263. ;; первый, старший разряд -- десятки вольт, младший нибл job2
  264. mov temp,job2
  265. ; проверим на ведущий 0
  266. andi temp,0x0F ; отсекаем ст ниббл
  267. tst temp
  268. brne MakeDigit3
  269. ldi temp,0xFF ; код пустого индикатора
  270. st X+,temp
  271. rjmp MakeDigit2
  272. MakeDigit3:
  273. rcall OUT_NIBBL ; ниббл 3, десятки
  274. ;; второй разряд -- единицы вольт, старший нибл job1
  275. MakeDigit2:
  276. mov temp,job1
  277. swap temp
  278. sbr flags,1<<NEED_DOT
  279. rcall OUT_NIBBL
  280. cbr flags,1<<NEED_DOT
  281. ;; третий разряд -- десятые вольта, младший нибл job1
  282. mov temp,job1
  283. rcall OUT_NIBBL
  284. ;; четвёртый разряд -- сотые вольта, старший нибл job0
  285. mov temp,job0
  286. swap temp
  287. rcall OUT_NIBBL
  288. ;;; и на новый виток
  289. rjmp BEGIN
  290. ;******************************
  291. ;;;;; Подпрограммы
  292. ;******************************
  293. ; делать нечего - поспим... ждём любое прерывание
  294. WAIT_IRQ:
  295. ldi temp,1<<SE
  296. out MCUCR,temp ; простой режим сна
  297. sleep ; спим.
  298. clr temp
  299. out MCUCR,temp ; со сна
  300. ret
  301. ;******************************
  302. ;** переводим младший ниббл temp в код для 7-сгм инд и кладём его в озу по адр X
  303. OUT_NIBBL:
  304. ldi ZL,low(LEDnd*2)
  305. ldi ZH,high(LEDnd*2); адрес кодов для индикатора
  306. andi temp,0x0F ; отсекаем ст ниббл. получаем смещение
  307. add ZL,temp
  308. adc ZH,XH ; добавили смещение
  309. lpm temp,Z ; прочитали код
  310. sbrc flags,NEED_DOT ; нужна точка?
  311. cbr temp,1<<DOT_BIT ; включаем точку
  312. st X+,temp ; записали код в позицию индикатора
  313. ret
  314. ;******************************
  315. ; перемножение двух 16-разрядных величин, результат 3 байта, рабочая
  316. ; множимое msrH:msrL, множитель tmp1:temp, результат job2:job1:job0
  317. ; использует cnt1
  318. MPY16U:
  319. clr job2 ;clear 1 highest bytes of result
  320. ldi cnt1,16 ;init loop counter
  321. m16u_1: lsr tmp1
  322. ror temp
  323. brcc noad8 ;if bit 0 of multiplier set
  324. add job1,msrL ;add multiplicand Low to byte 1 of res
  325. adc job2,msrH ;add multiplicand high to byte 2 of res
  326. noad8: ror job2 ;shift right result byte 2
  327. ror job1 ;rotate right result byte 1
  328. ror job0 ;rotate result byte 0 and multiplier Low
  329. dec cnt1 ;decrement loop counter
  330. brne m16u_1 ;if not done, loop more
  331. ret
  332. ;******************************
  333. ;* This subroutine converts a 16-bit number (msrH:msrL) to a 5-digit
  334. ;* packed BCD number represented by 3 bytes (job2:job1:job0).
  335. ;* MSD of the 5-digit number is placed in the lowermost nibble of job2.
  336. bin2BCD16:
  337. ldi cnt1,16 ;Init loop counter
  338. clr job2 ;clear result (3 bytes)
  339. clr job1
  340. clr job0
  341. clr ZH ;clear ZH
  342. bBCDx_1:lsl msrL ;shift input value
  343. rol msrH ;through all bytes
  344. rol job0
  345. rol job1
  346. rol job2
  347. dec cnt1 ;decrement loop counter
  348. brne bBCDx_2 ;if counter not zero
  349. ret ; return
  350. bBCDx_2:ldi ZL,AtBCD2+1 ;Z points to result MSB + 1
  351. bBCDx_3:ld temp,-Z ;get (Z) with pre-decrement
  352. subi temp,-$03 ;add 0x03
  353. sbrc temp,3 ;if bit 3 not clear
  354. st Z,temp ; store back
  355. ld temp,Z ;get (Z)
  356. subi temp,-$30 ;add 0x30
  357. sbrc temp,7 ;if bit 7 not clear
  358. st Z,temp ; store back
  359. cpi ZL,AtBCD0 ;done all three?
  360. brne bBCDx_3 ;loop again if not
  361. rjmp bBCDx_1
  362. ;******************************
  363. ;;;;; Обработчики прерываний
  364. ;******************************
  365. ; Timer1 Overflow Handler
  366. TIM1_OVF:
  367. ; выводим данные на 7-ми сегментные индикаторы
  368. PUSHF
  369. ldi temp,T1DIVH
  370. out TCNT1H,temp
  371. ldi temp,T1DIVL
  372. out TCNT1L,temp ; перезапустили таймер 1
  373. ;***************************
  374. ; вывод на индикатор очередной цифры
  375. in temp,DISP_PORT
  376. ori temp,IND_LA ; изменяем только нужные пины
  377. out DISP_PORT,temp ; погасили все индикаторы
  378. cbi SER_PORT,LOCK ; подгтовили защёлку
  379. ld temp,Y+ ; в темп - текущая цифра, адрес=+1
  380. ;;; выпихнем очередной байт в регистр
  381. ldi cnt2,8 ; счетчик бит
  382. TLO1: cbi SER_PORT,SCK ; SCK=0
  383. lsl temp ; старший бит в перенос
  384. brcc TLO0 ; если в переносе 0 — перейти
  385. sbi SER_PORT,DATA ; выдали 1
  386. rjmp TLOE
  387. TLO0: cbi SER_PORT,DATA ; выдали 0
  388. TLOE: sbi SER_PORT,SCK ; SCK=1, сдвиг данных
  389. dec cnt2
  390. brne TLO1
  391. sbi SER_PORT,LOCK ; из 0 в 1 -- защёлкивание данных в регистре
  392. in temp,DISP_PORT
  393. ori temp,IND_LA
  394. and temp,LEDN
  395. out DISP_PORT,temp ; включаем очередной индикатор
  396. lsl LEDN ; сдвинули - следующий индикатор
  397. brlo T1L2 ; вышли за границы? проверка
  398. ldi LEDN,FIRST_LED ; да, загружаем начальное значение
  399. ldi YL,AtLED1 ; загружаем адрес 1-го индикатора
  400. T1L2:
  401. sbr flags,1<<MEASURE ; пора мерять
  402. ;;; выход
  403. POPF
  404. reti
  405. ;***************************
  406. ; ADC Conversion Handler
  407. ; суммируем результаты измерений, уменьшаем счётчик.
  408. ADCC:
  409. in temp,ADCL
  410. add msrL,temp
  411. in temp,ADCH
  412. adc msrH,temp
  413. dec cnt1
  414. reti
  415. ;***************************
  416. ; байты для вывода 0-9 на индикаторы с ОА, без точки. точка вкл установкой бита 5
  417. LEDnd: .DB 0x30,0xF3,0x2A,0xA2,0xE1,0xA4,0x24,0xF2,0x20,0xA0
  418. .exit
  419. INT0: ; IRQ0 Handler
  420. reti
  421. PCINT0: ; PCINT0 Handler
  422. reti
  423. PCINT1: ; PCINT1 Handler
  424. reti
  425. WDT: ; Watchdog Interrupt Handler
  426. reti
  427. TIM1_CAPT: ; Timer1 Capture Handler
  428. reti
  429. TIM1_COMPA: ; Timer1 Compare A Handler
  430. reti
  431. TIM1_COMPB: ; Timer1 Compare B Handler
  432. reti
  433. TIM1_OVF: ; Timer1 Overflow Handler
  434. reti
  435. TIM0_COMPA: ; Timer0 Compare A Handler
  436. reti
  437. TIM0_COMPB: ; Timer0 Compare B Handler
  438. reti
  439. ANA_COMP: ; Analog Comparator Handler
  440. reti
  441. EE_RDY: ; EEPROM Ready Handler
  442. reti
  443. USI_STR: ; USI STart Handler
  444. reti
  445. USI_OVF: ; USI Overflow Handler
  446. reti
  447. ;
  448. .exit