;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; .include "tn26def.inc" ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; Альтернативный вариант (1) прошивки. ; Изменены входные пины для измерения напряжения. ; Изменены выходные пины для управления ЖКИ (запись и А0). ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Распиновка: ; PA0-PA3: данные LCD ; PB4: A0 LCD ; PB3: WR LCD ; + - ; 10111 ADC6-5 x20 (0x18) ; 10011 ADC4-3 x1 (0x13) ; ; PA5/ADC4 - собственно выход ; PA4/ADC3 - второй выход, земляной ; PA7/ADC6 - плюс шунта, земляной выход ; PA6/ADC5 - минус шунта, внутренняя масса устройства ; ; ADC3 и ADC6 соединяются вместе ; ; АЦП: ; Частота обработки 100*2 (100Гц после выпрямителя, два канала). ; Один период для канала 1, другой - для канала 2. ; Частота ядра 8МГц. Преобразование происходит за 13 тактов. ; 100*2*13 = 2600Гц. 8МГц / 2600 = 3076. ; Итог: делаем самое медленное преобразование. ; Для получения частоты в 200Гц (период в 5мс) вводим искусственные задержки. ; Медленное преобразование идёт на частоте CK/128 = 62500. ; Частота преобразований: 62500 / 13 = 4807. ; Длительность преобразования: 13 / 62500 = 208мкс ~ 0.2мс ; Величина задержки 5 - 0.2 = 4.8мс. ; Число тактов для задержки в 4.8мс = 4.8*0.001*8000000 = 38400. ; После переключения каналов/усиления надо ждать 125мкс. ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; .equ LCD_DATA =0x0F ; маска шины данных LCD (PA0 - PA3) .equ LCD_A0 =PORTB4 ; маска линии A0 LCD .equ LCD_WR =PORTB3 ; линия записи в LCD .equ LCD_UNBLOCK =0x01 ; сигнал разблокирования LCD .equ LCD_UNADDR =0x0F ; адрес для разблокирования LCD .equ DDRA_MASK =0x0F ; маска на выходные пины (PA0 - PA3) .equ DDRB_MASK =0x18 ; маска на выходные пины (PB3 - PB4) .equ ADC_256V =1 << REFS1 ; 2.56В, без внешних пинов (без PA3) .equ ADC_ADLAR =1 << ADLAR .equ ADC_4_3x1 =0x13 .equ ADC_6_5x20 =0x18 .equ ADC_SWITCH =0x04 ; один битик, для переключения между парой каналов ADC_4_3x1 и ADC_6_5x20 .equ ADC_CK128 =0x07 .equ BLANK =0x0F .equ DECIMAL =0x10 ; десятичная точка .equ AVG_POINT =16 ; число точек усреднения ; регистры с 0 по 9 отведены под буфер LCD индикатора ; 0 1 2 3 4 5 6 7 8 9 ; 1 9.9 В 1.9 9 А .equ lcd_start =0 ; R0 .equ u_pos =0 .equ i_pos =6 .def lim =R11 .def u_vall =R12 .def u_valh =R13 .def i_vall =R14 .def i_valh =R15 ; Считаем, что блок 1 и блок 2 одновременно использоваться не могут. ; блок 1 .def mc8u =r16 ;multiplicand .def mp8u =r17 ;multiplier .def m8uL =r17 ;result Low byte .def m8uH =r18 ;result High byte .def mcnt8u =r19 ;loop counter ; блок 2 .def temp1 =R16 .def temp2 =R17 .def odata =R18 .def sym =R19 .def pos =R20 .def adcv =R21 .def param =R22 .def cnt =R23 .def lcd_lim =R24 .def u_norm =R25 .def i_norm =R26 .def ptr =R30 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; .CSEG ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Главная процедура. Обработчиков прерывания нет. И не будет!!! main: ; инициализация указателя стека ldi temp1, RAMEND out SP, temp1 set_osccal: ; калибровка тактового генератора ldi temp1, 0x00 out OSCCAL, temp1 ; значения для нормирования ldi u_norm, 0x95 ldi i_norm, 0x80 ; инициализируем всё подряд clr ZL clr ZH ; количество сохраняемых точек при усреднении (для двух каналов) ldi temp1, AVG_POINT mov lim, temp1 ; число символов на индикаторе ldi lcd_lim, 10 ; инициализировать порты ldi temp1, DDRA_MASK out DDRA, temp1 ldi temp1, DDRB_MASK out DDRB, temp1 ; разблокируем шину индикатора ldi odata, LCD_UNADDR rcall OutQuad ldi odata, LCD_UNBLOCK | LCD_A0 rcall OutQuad ; чистим индикатор rcall ClearBuf rcall OutBuf ; инициализировать АЦП ldi temp1, ADC_CK128 | 1 << ADEN out ADCSR, temp1 ; задаём частоту и включаем АЦП ldi temp1, ADC_256V | ADC_ADLAR | ADC_4_3x1 out ADMUX, temp1 init_conv_loop: ; указатель ставим в начало буфера (==начало RAM) ldi ptr, SRAM_START ; сбрасываем счетчик clr cnt conv_loop: ; запустить АЦП sbi ADCSR, ADSC ; ждать готовности rcall WaitForADCReady ; читать АЦП ;in adcv, ADCL in adcv, ADCH ; положить в буфер, увеличить указатель st Z+, adcv ; переключить канал rcall SwitchADC ; задержка на 38400 тактов, или 4.8мс rcall ADCDelay ; увеличить счетчик inc cnt ; если счётчик меньше 8, повторить cpse cnt, lim rjmp conv_loop ; найти среднее по обоим каналам clr u_vall clr u_valh clr i_vall clr i_valh clr temp1 clr cnt ; указатель ставим в начало буфера (==начало RAM) ldi ptr, SRAM_START ; собственно цикл усреднения avg_loop: ld temp2, Z+ add u_vall, temp2 adc u_valh, temp1 ld temp2, Z+ add i_vall, temp2 adc i_valh, temp1 inc cnt inc cnt cpse cnt, lim rjmp avg_loop ; считаем средне-арфиметическое ; u_val /= 8 clc ror u_valh ror u_vall clc ror u_valh ror u_vall clc ror u_valh ror u_vall ; i_val /= 8 clc ror i_valh ror i_vall clc ror i_valh ror i_vall clc ror i_valh ror i_vall ; нормировать mov mc8u, u_vall mov mp8u, u_norm rcall norm_val mov u_vall, m8uH mov mc8u, i_vall mov mp8u, i_norm rcall norm_val mov i_vall, m8uH ; очистка буфера rcall ClearBuf ; преобразовать число 1 (напряжение) в BCD ; вывалить в буфер mov param, u_vall ; число ldi ptr, u_pos ; адрес начала буфера rcall itoa ; преобразовать число 2 (ток) в BCD ; вывалить в буфер mov param, i_vall ; число ldi ptr, i_pos ; адрес начала буфера rcall itoa ; вывалить буфер на индикатор rcall OutBuf ; и наша песенка, поехала с начала :) rjmp init_conv_loop ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Функция преобразования числа в строку. ; На входе число в param, указатель на строку в Z. itoa: ; Обработка сотней, если число > 100. clr temp2 sub100_loop: cpi param, 100 brlo sub100_stop subi param, 100 inc temp2 rjmp sub100_loop sub100_stop: st Z+, temp2 skip100: ; Обработка десятков, если число > 10. clr temp2 sub10_loop: cpi param, 10 brlo sub10_stop subi param, 10 inc temp2 rjmp sub10_loop sub10_stop: st Z+, temp2 skip10: ; Обработка единиц :) st Z+, param ; Выход. ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; вывод тетрады на LCD + A0 OutQuad: out PORTA, odata rcall OutWriteImpulse ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; OutChar: ; пишем адрес mov odata, pos andi odata, LCD_DATA cbi PORTB, LCD_A0 rcall OutQuad ; пишем собственно символ ; первую половину mov odata, sym andi odata, LCD_DATA sbi PORTB, LCD_A0 rcall OutQuad ; вторую половину mov odata, sym swap odata andi odata, LCD_DATA sbi PORTB, LCD_A0 rcall OutQuad ; выходим ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; преобразование BCD в код, пригодный для индикатора BCDTo7Seg: EERead: sbic eecr, eewe rjmp EERead out eear, sym sbi eecr, eere in sym, eedr ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; выдача сигнала записи на контакт PD6 OutWriteImpulse: sbi PORTB, LCD_WR ; установка битика WR (запись) nop ; сигнал записи надо держать не менее 100нс nop ; пауза между сигналами записи - 200 нс nop ; 6МГц, это период в 166нс cbi PORTB, LCD_WR ; снимаем сигнал записи nop nop nop ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ClearBuf: ldi temp1, BLANK clr ptr clr_buf: st Z+, temp1 cpse ptr, lcd_lim rjmp clr_buf ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; OutBuf: clr ptr clr pos out_buf: ld sym, Z+ rcall BCDTo7Seg cpi pos, 1 brne skip_u_dp ori sym, DECIMAL skip_u_dp: cpi pos, 6 brne skip_i_dp ori sym, DECIMAL skip_i_dp: rcall OutChar inc pos cpse pos, lcd_lim rjmp out_buf ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; WaitForADCReady: in temp1, ADCSR andi temp1, 1 << ADSC brne WaitForADCReady ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; SwitchADC: in temp1, ADMUX ldi temp2, ADC_SWITCH eor temp1, temp2 out ADMUX, temp1 ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ADCDelay: ldi temp1, 50 delay2: ldi temp2, 255 delay1: subi temp2, 1 ; 1 brne delay1 ; 2 subi temp1, 1 brne delay2 ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; norm_val: rcall mpy8u clc rol m8uL rol m8uH ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; mpy8u: clr m8uH ;clear result High byte ldi mcnt8u,8 ;init loop counter lsr mp8u ;rotate multiplier m8u_1: brcc m8u_2 ;carry set add m8uH,mc8u ; add multiplicand to result High byte m8u_2: ror m8uH ;rotate right result High byte ror m8uL ;rotate right result L byte and multiplier dec mcnt8u ;decrement loop counter brne m8u_1 ;if not done, loop more ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; .ESEG ; 0 1 2 3 4 5 6 7 8 9 E Empty digits: .db 0xEE, 0x60, 0x2F, 0x6D, 0xE1, 0xCD, 0xCF, 0xE8, 0xEF, 0xED, 0x8F, 0, 0, 0, 0, 0, 1 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;