avm.asm 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502
  1. ; Автор: shilow@ukr.net
  2. ; Дата: октябрь 2014
  3. ; Название: avm
  4. ; Версия: 3
  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 ; счётчик циклов Led_Out
  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 T0DIV = 0x83 ; 0x100-8MHz/256/250Hz
  72. .equ VREF = 498 ; опорное напряжение, десятки миливольт
  73. .equ SPH = 0x3e
  74. .equ DOT_BIT = 5 ; bit5 -- точка
  75. .equ BuffSize = 16 ; глубина буфера результатов измерений
  76. .equ BuffShift = 4 ; сколько раз сдвинуть вправо сумму буфера для усреднения
  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. ;Display: .byte 4 ; 4 байта для индикатора
  96. Buffer: .byte 2*BuffSize
  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. reti;jmp TIM1_OVF ; Timer1 Overflow Handler
  117. reti;jmp TIM0_COMPA ; Timer0 Compare A Handler
  118. reti;jmp TIM0_COMPB ; Timer0 Compare B Handler
  119. 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, непрерывное преобразование. temp=0b00100010
  136. ldi temp,1<<MUX0
  137. out ADMUX,temp
  138. ldi temp,(1<<ADEN)|(1<<ADSC)|(6<<ADPS0)
  139. out ADCSRA,temp
  140. ;;; таймер0 интервалы в 4 мсек (250 Hz). прескалер = 256
  141. ldi temp,(1<<CS02)
  142. out TCCR0B,temp
  143. ldi temp,T0DIV
  144. out TCNT0,temp ; запустили таймер
  145. ; прерывания от таймеров
  146. ldi temp,(1<<TOIE0)
  147. out TIMSK0,temp ; разрешим прерывание по переполнению
  148. ;; timer1 - частота 200 гц, при тактовой 8МГц, прескалер=64
  149. ; ldi temp,(3<<CS10)
  150. ; out TCCR1B,temp
  151. ; ldi temp,T1DIVH
  152. ; out TCNT1H,temp
  153. ; ldi temp,T1DIVL
  154. ; out TCNT1L,temp ; запустили таймер 1
  155. ; разрешаем прерывание от таймера
  156. ; ldi temp,1<<TOIE1
  157. ; out TIMSK1,temp
  158. ;;; init
  159. ldi LEDN,0b11101111 ; первый индикатор
  160. ldi temp,IND_LA
  161. out DISP_DDR,temp
  162. out DISP_PORT,temp
  163. ldi temp,(1<<SCK)|(1<<DATA)|(1<<LOCK)
  164. out SER_DDR,temp
  165. clr YH
  166. ldi YL,AtLED1 ; загружаем адрес 1-го индикатора
  167. ldi temp,0x30
  168. mov LED1,temp
  169. mov LED2,temp
  170. mov LED3,temp
  171. mov LED4,temp
  172. ldi bAddr,low(Buffer)
  173. clr bIdx
  174. ;;; Enable interrupts
  175. sei
  176. ;******************************
  177. ;;;;; Основная программа
  178. BEGIN:
  179. ;;; запускаем измерения и преобразования
  180. clr msrL
  181. clr msrH ; очистили хранилище результата
  182. ldi cnt1,16 ; загрузили счётчик измерений
  183. sbi ADCSRA,ADATE ; запуск free running mode
  184. sbi ADCSRA,ADIE ; разрешаем прерывания от АЦП
  185. sbi ADCSRA,ADSC ; запуск нов. преобразования
  186. ML1:
  187. ; rjmp PC+1 ; nop x 2 -- 1 world 2 takta. или sleep?
  188. rcall WAIT_IRQ ; один цикл измерения, ЕМНИП больше 100 мкс...
  189. tst cnt1 ; гоняем пустой цикл пока будут происходить 16 измерений
  190. brne ML1
  191. cbi ADCSRA,ADIE ; запрещаем прерывания от АЦП
  192. cbi ADCSRA,ADATE ; stop free running mode
  193. lsr msrH
  194. ror msrL
  195. lsr msrH
  196. ror msrL ; поделили результат на 2^2
  197. ; теперь множим на Vref и делим на 4096
  198. ldi temp,low(VREF)
  199. ldi tmp1,high(VREF)
  200. rcall MPY16U
  201. lsr job1 ; выбросили job0 == поделили на 256
  202. ror job0
  203. lsr job1
  204. ror job0
  205. ;; lsr job1
  206. ;; ror job0
  207. ;; lsr job1
  208. ;; ror job0 ; доделили на 16, итого поделили на 4096
  209. ; ldi cnt1,4
  210. ;ML2: lsr job2
  211. ; ror job1
  212. ; dec cnt1
  213. ; brne ML2
  214. ; mov msrL,job0
  215. ; mov msrH,job1 ; в MSR - напряжение в 10мВ
  216. ;!!! умножить на входной делитель
  217. ;; -- не доделили на 4 == умножили теперь на 4.
  218. ;; в реале нужно будет множить например на 400 и делить на 100 -- под реальный делитель.
  219. ; тут можно добавить коррекцию усиления.
  220. ; сейчас текущий результат измерний в job1:job0
  221. ;;; усреднения
  222. ; сохраним текущий результат
  223. mov XL,bAddr
  224. clr XH
  225. st X+,job0
  226. st X,job1 ; сохранили
  227. sbiw X,1 ; буфер в старой позиции -- на текущем результате
  228. clr msrL
  229. clr msrH
  230. ; теперь как-то нужно просуммировать содержимое буфера
  231. ldi cnt1,BuffSize
  232. BuffSummarize:
  233. ld temp,X+
  234. add msrL,temp
  235. ld temp,X+
  236. adc msrH,temp
  237. inc bIdx
  238. cpi bIdx,BuffSize
  239. brne BuffChkCnt
  240. ldi XL,low(Buffer)
  241. clr bIdx
  242. BuffChkCnt:
  243. dec cnt1
  244. brne BuffSummarize
  245. adiw X,2 ; адрес буфера - на следующую ячейку
  246. inc bIdx
  247. cpi bIdx,BuffSize
  248. brne BuffAddrSave
  249. ldi XL,low(Buffer)
  250. clr bIdx
  251. BuffAddrSave:
  252. mov bAddr,XL ; сохранили текущий адрес буфера
  253. ; тут имеем в msr сумму всех элементов массива. делим:
  254. ldi cnt1,BuffShift
  255. BuffDivide:
  256. lsr msrH
  257. ror msrL
  258. dec cnt1
  259. brne BuffDivide
  260. ; теперь в msr среднее от содержимого буфера.
  261. ; адрес и индекс буфера показывают следующую позицию.
  262. ;;; преобразуем msr в десятичный вид и положим в буфер индикатора
  263. rcall bin2BCD16
  264. ; 4 значащих цифры в job1:job0
  265. ldi XL,AtLED1
  266. clr XH
  267. mov temp,job1
  268. swap temp
  269. ; проверим на ведущий 0
  270. andi temp,0x0F ; отсекаем ст ниббл
  271. tst temp
  272. brne MakeDigit3
  273. ldi temp,0xFF ; код пустого индикатора
  274. st X+,temp
  275. rjmp MakeDigit2
  276. MakeDigit3:
  277. rcall OUT_NIBBL ; ниббл 3, десятки
  278. MakeDigit2:
  279. mov temp,job1
  280. sbr flags,1<<NEED_DOT
  281. rcall OUT_NIBBL ; ниббл 2, единицы
  282. cbr flags,1<<NEED_DOT
  283. mov temp,job0
  284. swap temp
  285. rcall OUT_NIBBL ; ниббл 1, десятые
  286. mov temp,job0
  287. rcall OUT_NIBBL ; ниббл 0, сотые
  288. ;;; Конец цикла
  289. rcall WAIT_IRQ
  290. rjmp BEGIN
  291. ;******************************
  292. ;;;;; Подпрограммы
  293. ;******************************
  294. ; делать нечего - поспим... ждём любое прерывание
  295. WAIT_IRQ:
  296. ldi temp,1<<SE
  297. out MCUCR,temp ; простой режим сна
  298. sleep ; спим.
  299. clr temp
  300. out MCUCR,temp ; со сна
  301. ret
  302. ;******************************
  303. ;** переводим младший ниббл temp в код для 7-сгм инд и кладём его в озу по адр X
  304. OUT_NIBBL:
  305. ldi ZL,low(LEDnd*2)
  306. ldi ZH,high(LEDnd*2); адрес кодов для индикатора
  307. andi temp,0x0F ; отсекаем ст ниббл. получаем смещение
  308. add ZL,temp
  309. adc ZH,XH ; добавили смещение
  310. lpm temp,Z ; прочитали код
  311. sbrc flags,NEED_DOT ; нужна точка?
  312. cbr temp,1<<DOT_BIT ; включаем точку
  313. st X+,temp ; записали код в позицию индикатора
  314. ret
  315. ;******************************
  316. ; перемножение двух 16-разрядных величин, результат 3 байта, рабочая
  317. ; множимое msrH:msrL, множитель tmp1:temp, результат job2:job1:job0
  318. ; использует cnt1
  319. MPY16U:
  320. clr job2 ;clear 1 highest bytes of result
  321. ldi cnt1,16 ;init loop counter
  322. m16u_1: lsr tmp1
  323. ror temp
  324. brcc noad8 ;if bit 0 of multiplier set
  325. add job1,msrL ;add multiplicand Low to byte 1 of res
  326. adc job2,msrH ;add multiplicand high to byte 2 of res
  327. noad8: ror job2 ;shift right result byte 2
  328. ror job1 ;rotate right result byte 1
  329. ror job0 ;rotate result byte 0 and multiplier Low
  330. dec cnt1 ;decrement loop counter
  331. brne m16u_1 ;if not done, loop more
  332. ret
  333. ;******************************
  334. ;* This subroutine converts a 16-bit number (msrH:msrL) to a 5-digit
  335. ;* packed BCD number represented by 3 bytes (job2:job1:job0).
  336. ;* MSD of the 5-digit number is placed in the lowermost nibble of job2.
  337. bin2BCD16:
  338. ldi cnt1,16 ;Init loop counter
  339. clr job2 ;clear result (3 bytes)
  340. clr job1
  341. clr job0
  342. clr ZH ;clear ZH
  343. bBCDx_1:lsl msrL ;shift input value
  344. rol msrH ;through all bytes
  345. rol job0
  346. rol job1
  347. rol job2
  348. dec cnt1 ;decrement loop counter
  349. brne bBCDx_2 ;if counter not zero
  350. ret ; return
  351. bBCDx_2:ldi ZL,AtBCD2+1 ;Z points to result MSB + 1
  352. bBCDx_3:ld temp,-Z ;get (Z) with pre-decrement
  353. subi temp,-$03 ;add 0x03
  354. sbrc temp,3 ;if bit 3 not clear
  355. st Z,temp ; store back
  356. ld temp,Z ;get (Z)
  357. subi temp,-$30 ;add 0x30
  358. sbrc temp,7 ;if bit 7 not clear
  359. st Z,temp ; store back
  360. cpi ZL,AtBCD0 ;done all three?
  361. brne bBCDx_3 ;loop again if not
  362. rjmp bBCDx_1
  363. ;******************************
  364. ;;;;; Обработчики прерываний
  365. ;******************************
  366. ; Timer0 Overflow Handler
  367. TIM0_OVF:
  368. ; выводим данные на 7-ми сегментные индикаторы
  369. PUSHF
  370. ldi temp,T0DIV
  371. out TCNT0,temp ; перезапустили таймер 0
  372. ;***************************
  373. ; вывод на индикатор очередной цифры
  374. in temp,DISP_PORT
  375. ori temp,IND_LA ; изменяем только нужные пины
  376. out DISP_PORT,temp ; погасили все индикаторы
  377. cbi SER_PORT,LOCK ; подгтовили защёлку
  378. ld temp,Y+ ; в темп - текущая цифра, адрес=+1
  379. ;;; выпихнем очередной байт в регистр
  380. ldi cnt2,8 ; счетчик бит
  381. LO1: cbi SER_PORT,SCK ; SCK=0
  382. lsl temp ; старший бит в перенос
  383. brcc LO0 ; если в переносе 0 — перейти
  384. sbi SER_PORT,DATA ; выдали 1
  385. rjmp LOE
  386. LO0: cbi SER_PORT,DATA ; выдали 0
  387. LOE: sbi SER_PORT,SCK ; SCK=1, сдвиг данных
  388. dec cnt2
  389. brne LO1
  390. sbi SER_PORT,LOCK ; из 0 в 1 -- защёлкивание данных в регистре
  391. in temp,DISP_PORT
  392. ori temp,IND_LA
  393. and temp,LEDN
  394. out DISP_PORT,temp ; включаем очередной индикатор
  395. lsl LEDN ; сдвинули - следующий индикатор
  396. brlo T0L2 ; вышли за границы? проверка
  397. ldi LEDN,0b11101111 ; да, загружаем начальное значение
  398. ldi YL,AtLED1 ; загружаем адрес 1-го индикатора
  399. T0L2:
  400. POPF
  401. reti
  402. ;***************************
  403. ; ADC Conversion Handler
  404. ; суммируем результаты измерений, уменьшаем счётчик.
  405. ADCC:
  406. in temp,ADCL
  407. add msrL,temp
  408. in temp,ADCH
  409. adc msrH,temp
  410. dec cnt1
  411. reti
  412. ;***************************
  413. ; байты для вывода 0-9 на индикаторы с ОА, без точки. точка вкл установкой бита 5
  414. LEDnd: .DB 0x30,0xF3,0x2A,0xA2,0xE1,0xA4,0x24,0xF2,0x20,0xA0
  415. .exit
  416. INT0: ; IRQ0 Handler
  417. reti
  418. PCINT0: ; PCINT0 Handler
  419. reti
  420. PCINT1: ; PCINT1 Handler
  421. reti
  422. WDT: ; Watchdog Interrupt Handler
  423. reti
  424. TIM1_CAPT: ; Timer1 Capture Handler
  425. reti
  426. TIM1_COMPA: ; Timer1 Compare A Handler
  427. reti
  428. TIM1_COMPB: ; Timer1 Compare B Handler
  429. reti
  430. TIM1_OVF: ; Timer1 Overflow Handler
  431. reti
  432. TIM0_COMPA: ; Timer0 Compare A Handler
  433. reti
  434. TIM0_COMPB: ; Timer0 Compare B Handler
  435. reti
  436. ANA_COMP: ; Analog Comparator Handler
  437. reti
  438. EE_RDY: ; EEPROM Ready Handler
  439. reti
  440. USI_STR: ; USI STart Handler
  441. reti
  442. USI_OVF: ; USI Overflow Handler
  443. reti
  444. ;
  445. .exit