avm.asm 12 KB

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