;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; .include "tn26def.inc" ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; Альтернативный вариант (2) прошивки. ; Больше не используется память для хранения списка точек для усреднения. ; Добавлена функция деления, теперь число точек может быть произвольным. ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Распиновка: ; 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 =8 ; число точек усреднения ; адреса RAM с 0 по 9 отведены под буфер LCD индикатора ; 0 1 2 3 4 5 6 7 8 9 ; 1 9.9 В 1.9 9 А .equ LCD_START =SRAM_START .equ U_POS =0 .equ I_POS =6 ; Регистры .def lim =R1 .def u_vall =R2 .def u_valh =R3 .def i_vall =R4 .def i_valh =R5 ; Считаем, что блоки 1, 2, 3 одновременно использоваться не могут. ; блок 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 ; блок 3, для деления .def drem16uL =R14 ; остаток .def drem16uH =R15 .def dres16uL =R16 ; результат .def dres16uH =R17 .def dd16uL =R16 ; делимое .def dd16uH =R17 .def dv16uL =R18 ; делитель .def dv16uH =R19 .def dcnt16u =R20 ; прочие регистры .def adcv =R21 .def param =R22 .def cnt =R23 .def lcd_lim =R24 .def u_norm =R25 .def i_norm =R26 //.def ptrL =R30 #define ptrL YL #define ptrH YH #define ptr Y #define fncL ZL #define fncH ZH #define fnc Z ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; .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 ptrL clr ptrH ; количество сохраняемых точек при усреднении 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 ; чистим индикатор ldi param, 0 rcall SetBuf 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: ; сбрасываем счетчик clr cnt clr u_vall clr u_valh clr i_vall clr i_valh conv_loop: ; запустить АЦП, подождать и прочитать rcall RunAndReadADC ; сохраняем значение напряжения add u_vall, adcv adc u_valh, temp2 ; переключить канал rcall SwitchADC ; запустить АЦП, подождать и прочитать rcall RunAndReadADC ; сохраняем значение тока add i_vall, adcv adc i_valh, temp2 ; переключить канал rcall SwitchADC ; увеличить счетчик inc cnt ; если счётчик меньше предельного значения - повторить cpse cnt, lim rjmp conv_loop ; найти среднее mov dd16uL, u_vall mov dd16uH, u_valh ldi dv16uL, AVG_POINT clr dv16uH rcall div16u ; нормировать mov mc8u, dres16uL mov mp8u, u_norm rcall norm_val mov u_vall, m8uH ; найти среднее mov dd16uL, i_vall mov dd16uH, i_valh ldi dv16uL, AVG_POINT clr dv16uH rcall div16u ; нормировать mov mc8u, dres16uL mov mp8u, i_norm rcall norm_val mov i_vall, m8uH ; очистка буфера ldi param, BLANK rcall SetBuf ; преобразовать число 1 (напряжение) в BCD ; вывалить в буфер mov param, u_vall ; число ldi ptrL, LCD_START + U_POS ; адрес начала буфера rcall itoa ; преобразовать число 2 (ток) в BCD ; вывалить в буфер mov param, i_vall ; число ldi ptrL, LCD_START + I_POS ; адрес начала буфера rcall itoa ; вывалить буфер на индикатор rcall ConvertBuf rcall SetDecimal rcall OutBuf ; и наша песенка, поехала с начала :) rjmp init_conv_loop ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Функция преобразования числа в строку. ; На входе число в param, указатель на строку в ptr. itoa: ; Обработка сотней, если число > 100. clr temp2 sub100_loop: cpi param, 100 brlo sub100_stop subi param, 100 inc temp2 rjmp sub100_loop sub100_stop: st ptr+, temp2 skip100: ; Обработка десятков, если число > 10. clr temp2 sub10_loop: cpi param, 10 brlo sub10_stop subi param, 10 inc temp2 rjmp sub10_loop sub10_stop: st ptr+, temp2 skip10: ; Обработка единиц :) st ptr+, 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 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ldi fncL, LOW(test1) ; ldi fncH, HIGH(test1) ; icall ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; SetBuf: ldi ptrL, LCD_START clr cnt clr_buf: st ptr+, param inc cnt cpse cnt, lcd_lim rjmp clr_buf ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ConvertBuf: ldi ptrL, LCD_START clr cnt cnv_buf: ld sym, ptr rcall BCDTo7Seg st ptr+, sym inc cnt cpse cnt, lcd_lim rjmp cnv_buf ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; OutBuf: ldi ptrL, LCD_START clr pos out_buf: ld sym, ptr+ rcall OutChar inc pos cpse pos, lcd_lim rjmp out_buf ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; SetDecimal: ldi ptrL, LCD_START + U_POS + 1 ld sym, ptr ori sym, DECIMAL st ptr, sym ldi ptrL, LCD_START + I_POS ld sym, ptr ori sym, DECIMAL st ptr, sym ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; WaitForADCReady: in temp1, ADCSR andi temp1, 1 << ADSC brne WaitForADCReady ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; RunAndReadADC: ; запустить АЦП sbi ADCSR, ADSC ; ждать готовности rcall WaitForADCReady ; читать АЦП ;in adcv, ADCL in adcv, ADCH clr temp2 ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; SwitchADC: in temp1, ADMUX ldi temp2, ADC_SWITCH eor temp1, temp2 out ADMUX, temp1 rcall ADCDelay ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; задержка на 38400 тактов, или 4.8мс 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 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; div16u: clr drem16uL ;clear remainder Low byte sub drem16uH,drem16uH;clear remainder High byte and carry ldi dcnt16u,17 ;init loop counter d16u_1: rol dd16uL ;shift left dividend rol dd16uH dec dcnt16u ;decrement counter brne d16u_2 ;if done ret ; return d16u_2: rol drem16uL ;shift dividend into remainder rol drem16uH sub drem16uL,dv16uL ;remainder = remainder - divisor sbc drem16uH,dv16uH ; brcc d16u_3 ;if result negative add drem16uL,dv16uL ; restore remainder adc drem16uH,dv16uH clc ; clear carry to be shifted into result rjmp d16u_1 ;else d16u_3: sec ; set carry to be shifted into result rjmp d16u_1 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 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;