📄 int0.asm
字号:
;==============================================================================
; Przerwanie INT0 zglaszane co 1sek. z DS1307
; Projekt: Miernik energii i mocy na ADE7759
;==============================================================================
;.listmac
; r0, r1, r2 -rejestry operacyjne, r2=0
.def wyn0 =r4
.def wyn1 =r5
.def wyn2 =r6
.def wyn3 =r7 ; wynik mnozenia
.def wsp_en0 =r8
.def wsp_en1 =r9 ; wspolczynnik korekcji probki energii
.def wsp_en2 =r3 ; uwaga! r3
.def aden0 =r10
.def aden1 =r11
.def aden2 =r12
.def aden3 =r13
.def aden4 =r14
.def aden5 =r15 ; probka energii z ADE7759 (po korekcji)
.def en0 =r18
.def en1 =r19
.def en2 =r20
.def en3 =r21
.def en4 =r22 ; en4::en0 probka energii z ADE7759
.def en5 =r23
.def en6 =r24 ; en6::en0 akumulator energii (ladowanej z RAM)
;;;;;;.def akum_en =r25
;.def spi_bufor =r28 ; deklaracja juz jest w int2.asm
; rejestr X wolny
; rejestru Y nie wolno uzyc
; rejestr Z trzyma wskaznik do energii (RAM)
;======== M A K R A ===========================================================
.macro SPI_ADE_ON ; uaktywnij SPI w ADE7759
cbi ADE_CS_PORT, ADE_CS
.endm
.macro SPI_ADE_OFF ; wylacz SPI w ADE7759
sbi ADE_CS_PORT, ADE_CS
.endm
;--------------------------------------
.macro MUL16x16 ; @0:@1 -mnozna, @2:@3 -mnoznik; wynik w rejestrach wynx
; oraz musi byc: r2=0
mul @0, @2
movw wyn2, r0
mul @1, @3
movw wyn0, r0
mul @0, @3
add wyn1, r0
adc wyn2, r1
adc wyn3, r2
mul @1, @2
add wyn1, r0
adc wyn2, r1
adc wyn3, r2
.endm
;--------------------------------------
.macro MUL16x8 ; @0:@1 -mnozna, @2 -mnoznik; wynik w rejestrach wyn2:wyn1:wyn0
clr wyn2
mul @1, @2
movw wyn0, r0
mul @0, @2
add wyn1, r0
adc wyn2, r1
.endm
;------------------------------------------------------------------------------
.cseg
;======== P R O G R A M =======================================================
INT0_INT: ; gdy przerwanie
; gdy przerwanie trzeba zapewnic ochrone rejestrow
; ...gdyz bedac w MENU przerwanie od INT0 rozwali rejestry
push temp
in temp, SREG ; zachowaj rejestr SREG
push temp
push r0
push r1
push r2
push r3
push r4
push r5
push r6
push r7
push r8
push r9
push r10
push r11
push r12
push r13
push r14
push r15
;push r16 temp na poczatku
;push r17 flagi nie
push r18
push r19
push r20
push r21
push r22
push r23
push r24
push r25
push r26
push r27
push r28
push r29
push r30
push r31
SPI_ADE_ON
ldi temp, ADE_RSTENERGY_ADR ; komenda odczytu energii
rcall SPI_TRX ; wyslij bajt z temp
;--1----- ; czytanie pierwszego bajtu
rcall SPI_TRX ; odczyt pierwszego bajtu energii (MSB) do temp
mov en4, temp ; ...i potem do wlasciwego rejestru
;--2-----
rcall SPI_TRX
mov en3, temp ; odczyt energii do en4::en0 - tymczasowo
; ...potem nastepuje zaladowanie energii
;--3----- ; ...do tych rejestrow
rcall SPI_TRX
mov en2, temp
;--4-----
rcall SPI_TRX
mov en1, temp
;--5-----
rcall SPI_TRX
mov en0, temp
SPI_ADE_OFF
;------------------------------------------------------------------------------
lds temp, Flagi3
sbrs temp, f3EN_RST ; flaga pierwszej probki po resecie
rjmp LE_OK
cbr temp, 1<<f3EN_RST ; zeruj
sts Flagi3, temp
clr en4 ; zerowanie probki energii
clr en3
clr en2
clr en1
clr en0
LE_OK: lds temp, Flagi2
cbr temp, 1<<f2EN_MI ; zeruj flagi
sts Flagi2, temp
; [2]
sbrs en4, 7 ; spr bitu znaku probki energii
rjmp LE_PLUS ; bit_znaku=0 --> dalej
; probka ujemna -ustawienie flagi...
sbr temp, 1<<f2EN_MI
sts Flagi2, temp
; ...i negacja probki (wystarczy negacja bitow)
com en0
com en1
com en2
com en3
com en4
LE_PLUS:
clr aden0
clr aden1
clr aden2
clr aden3
clr aden4
clr aden5
lds wsp_en0, Wsp_E
lds wsp_en1, Wsp_E+1
lds wsp_en2, Wsp_E+2 ; zaladuj wspolczynnik korekcji
clr r2
; [3] ; mnozenie probki (5B) przez wspolczynnik (3B), wynik (8B)
; ale poniewaz dwa najmlodsze bajty sa odrzucane to wynik miesci sie w (6B)
MUL16x16 wsp_en1, wsp_en0, en1, en0 ; mnozenie mlodszego slowa przez wsp.
movw aden0, wyn2 ; obciecie 2 najmlodszych bajtow [3a]
MUL16x16 wsp_en1, wsp_en0, en3, en2 ; mnozenie kolejnego slowa
add aden0, wyn0
adc aden1, wyn1
adc aden2, wyn2
adc aden3, wyn3 ; dodanie wyniku posredniego do rejestru probki energii
MUL16x8 wsp_en1, wsp_en0, en4 ; ostatnie mnozenie
add aden2, wyn0
adc aden3, wyn1
adc aden4, wyn2
; teraz mnozenie najstarszego bajtu wsp_en2 wraz z przesunieciem o 2 bajty
MUL16x8 en1, en0, wsp_en2
add aden0, wyn0
adc aden1, wyn1
adc aden2, wyn2
adc aden3, r2
adc aden4, r2
adc aden5, r2
MUL16x8 en3, en2, wsp_en2
add aden2, wyn0
adc aden3, wyn1
adc aden4, wyn2
adc aden5, r2
mul en4, wsp_en2 ; ostatnie mnozenie (najstarsze bajty)
add aden4, r0
adc aden5, r1
; [4] dwa najmlodsze bajty sa odrzucane - nie nalezy ich akumulowac
; aden5::aden0 => probka energii po korekcji (6B)
movw aden0, aden2
movw aden2, aden4 ; dzielenie przez 2^16
; wynik (4B): aden3::aden0
; zapis energii sekundowej (wymagane przy kalibracji)
lds temp, Flagi2
sbrc temp, f2EN_S ; jesli EN_S=0 to zapisz energie sek.
rjmp LE_NOS
sts Energia_sek, aden0
sts Energia_sek+1, aden1
sts Energia_sek+2, aden2
sts Energia_sek+3, aden3
; prog akumulacji energii
LE_NOS: ldi en0, low(PROG_AKUMULACJI)
ldi en1, byte2(PROG_AKUMULACJI)
ldi en2, byte3(PROG_AKUMULACJI)
ldi en3, byte4(PROG_AKUMULACJI)
cp aden0, en0
cpc aden1, en1
cpc aden2, en2
cpc aden3, en3
brlo LE_PRMN ; skacz, jesli en_sek mniejsza od progu
rjmp LE_TARYF ; probka wieksza od progu
LE_PRMN: sbrc flagi, fKAL ; =1 ==> tryb kalibracji...
rjmp LE_TARYF ; ...wiec nie ograniczaj akumulacji progiem
cbr temp, 1<<f2EN_MI ; kasuj flage energii ujemnej, aby nie migala
sts Flagi2, temp ; ...gwiazdka na LCD
rjmp LE_MOC_P ; i nie akumuluj energii
;--------------------------------------
; test biezacej taryfy
LE_TARYF: sbrs flagi, fTARYFA2 ; przeskocz, jesli jest taryfa 2
rjmp LE_T1 ; skocz bo taryfa 1
; taryfa 2 - ladowanie wskaznikow
ldi zl, low(Energia2_od)
ldi zh, high(Energia2_od) ; zaladuj wskaznik do zmiennej Energia2_od
movw wyn0, zl ; zapamietaj wskaznik w wyn1:wyn0
ldi zl, low(Energia2_oddo)
ldi zh, high(Energia2_oddo) ; zaladuj wskaznik do zmiennej Energia2_oddo
movw wyn2, zl ; zapamietaj wskaznik w wyn3:wyn2
ldi zl, low(Energia2_ses)
ldi zh, high(Energia2_ses) ; zaladuj wskaznik do zmiennej Energia2_ses
movw wsp_en0, zl ; zapamietaj wskaznik w wsp_en1:wsp_en0
ldi zl, low(Energia2_poz)
ldi zh, high(Energia2_poz) ; zaladuj wskaznik do zmiennej Energia2_poz
movw aden4, zl ; zapamietaj wskaznik w aden5:aden4
ldi zl, low(Energia2)
ldi zh, high(Energia2) ; zaladuj wskaznik do zmiennej Energia2
rjmp LE_TD
LE_T1: ; taryfa 1 - ladowanie wskaznikow
ldi zl, low(Energia1_od)
ldi zh, high(Energia1_od) ; zaladuj wskaznik do zmiennej Energia1_od
movw wyn0, zl
ldi zl, low(Energia1_oddo)
ldi zh, high(Energia1_oddo) ; zaladuj wskaznik do zmiennej Energia1_oddo
movw wyn2, zl
ldi zl, low(Energia1_ses)
ldi zh, high(Energia1_ses) ; zaladuj wskaznik do zmiennej Energia1_ses
movw wsp_en0, zl
ldi zl, low(Energia1_poz)
ldi zh, high(Energia1_poz) ; zaladuj wskaznik do zmiennej Energia1_poz
movw aden4, zl
ldi zl, low(Energia1)
ldi zh, high(Energia1) ; zaladuj wskaznik do zmiennej Energia1
; [5]
LE_TD: lds temp, Flagi2
; akumulacja (Energia)
rcall LE_AKUM
; akumulacja (Energia_ses)
movw zl, wsp_en0 ; zaladowanie nowego wskaznika
rcall LE_AKUM
; jesli data i czas sa odpowiednie to akumulowac energie_od i energie_oddo
sbrs temp, f2EN_O
rjmp LE_AOO ; EN_O=0
; akumulacja (Energia_od)
movw zl, wyn0 ; zaladowanie nowego wskaznika
rcall LE_AKUM
LE_AOO: sbrs temp, f2EN_OO
rjmp LE_AP ; EN_OO=0
; akumulacja (Energia_oddo)
movw zl, wyn2 ; zaladowanie nowego wskaznika
rcall LE_AKUM
LE_AP: sbrs temp, f2EN_S
rjmp LE_MOC_P ; EN_S=0
; akumulacja energii pozornej
movw zl, aden4
movw wyn0, aden0 ; ochrona energii sekundowej (bo jeszcze jest liczona moc)
movw wyn2, aden2
lds aden0, Energia_sek ; gdy EN_S=1, to Energia_sek przechowuje en. sek. pozorna
lds aden1, Energia_sek+1
lds aden2, Energia_sek+2
lds aden3, Energia_sek+3
rcall LE_AKUM
movw aden0, wyn0
movw aden2, wyn2
;------------------------------------------------------------------------------
LE_MOC_P: ; liczenie mocy P z energii
; mnozenie: r13::r10 x r25:r24 => r23::r18
; czyli: aden3:0 x 36000 (Wsp_P) => kwd5:0
lds r24, Wsp_P
lds r25, Wsp_P+1 ; laduj wspolczynnik mnozenia
movw aden4, aden0 ; kopia dwoch mlodszych bajtow energii
movw aden0, aden2 ; najpierw mnozenie dwoch starszych bajtow
call MUL_16_16 ; r11:r10 x r25:r24
movw wyn0, kwd0
movw wyn2, kwd2 ; kopia wyniku mnozenia
movw aden0, aden4 ; teraz mnozenie mlodszych bajtow energii
call MUL_16_16
clr kwd4
clr kwd5
add kwd2, wyn0
adc kwd3, wyn1
adc kwd4, wyn2
adc kwd5, wyn3
; kwd4:kwd3 zawiera moc w [0,1W]
sts Moc_P, kwd3
sts Moc_P+1, kwd4 ; zapis mocy do RAM
;--------------------------------------
; koniec przerwania
sbr flagi, 1<<fINT2 ; ustawienie flagi zezwalajacej na rozpoczecie zbierania probek
pop r31
pop r30
pop r29
pop r28
pop r27
pop r26
pop r25
pop r24
pop r23
pop r22
pop r21
pop r20
pop r19
pop r18
;pop r17 flagi
;pop r16 temp
pop r15
pop r14
pop r13
pop r12
pop r11
pop r10
pop r9
pop r8
pop r7
pop r6
pop r5
pop r4
pop r3
pop r2
pop r1
pop r0
pop temp
out SREG, temp
pop temp
reti ; koniec przerwania
;--------------------------------------
LE_AKUM: ; akumulacja energii - Z zawiera wskaznik do zmiennej
clr r2
ld en0, z+
ld en1, z+
ld en2, z+
ld en3, z+
ld en4, z+
ld en5, z+
ld en6, z+ ; odczyt akumulatora Energii
sbrc temp, f2EN_MI ; przeskocz, jesli probka dodatnia
rjmp LEA_MI
add en0, aden0 ; dodawanie probki energii (aden3:aden0)
adc en1, aden1
adc en2, aden2
adc en3, aden3
adc en4, r2
adc en5, r2
adc en6, r2
rjmp LEA_ST
LEA_MI: sbrs temp, f2EN_AKUM
ret ; f2EN_AKUM=0 --> nie akumuluj energi ujemnej
sub en0, aden0 ; odejmowanie probki energii (aden3:aden0)
sbc en1, aden1 ; ...jesli byla ujemna
sbc en2, aden2
sbc en3, aden3
sbc en4, r2
sbc en5, r2
sbc en6, r2
LEA_ST: st -z, en6
st -z, en5
st -z, en4
st -z, en3
st -z, en2
st -z, en1
st -z, en0 ; zapis Energii do RAM
ret
.exit
;------------------------------------------------------------------------------
Liczenie Energii:
1. pobranie probki (5B)
2. test polaryzacji (znaku), negacja jesli ujemna i ustawienie flagi polaryzacji energii EN_MI
3. mnozenie przez wspolczynnik korekcji (5B) x (3B) => (8B)
3a. odrzut dwoch najmlodszych bajtow przy pierwszym mnozeniu, a wiec probka po korekcji ma (6B)
4. dzielenie wyniku przez 2^16: (6B) / (2B) => (4B)
taka rozdzielczosc probki calkowicie wystarcza - blad jest minimalny:
i) 0,00008% przy najlepszym dobraniu wspolczynnika (roznica ok. 20Wh na rok przy pelnej mocy)
ii) 0,005% dla mocy 1000 razy mniejszej niz w i)
blad sie zwieksza dla mniejszych mocy (bo mniejsza rozdzielczosc probek)
iii) 0,0015% dla wpolczynnika odstrojonego o 10 od najlepszego
5. akumulacja [Ws] (4B) => (7B)
5a. jesli energia ujemna to odejmowanie od akumulatora
6. do wyswietlania odcinane sa 3 najmlodsze bajty, pozostale 4 sa wyskalowane w [Wh]
6a. akumulator energii jest w kodzie U2
7. Pierwsza probka energii po wlaczeniu zasilania jest bledna (jest pomijana)
INFO
Poniewaz dzielniki wejsc sa dobrane na 100% skali, to akumulator energii przepelni sie po
2,3s przy maksymalnych napieciach wejsciowych (20A- i 396V-). Nie jest to istotne, bowiem
miernik zasilany jest z sieci 230Vac
Przy napieciu 230Vac, dla maksymalnego pradu 14Aac, moc maksymalna wynosi 3220W.
Energia zuzyta przez godzine wyniesie wiec 3220Wh. Dalej, rok ma 8760 godzin:
3220W * 8760h = 28207200Wh = 28207kWh rocznie
Wielkosc akumulatora energii watogodzinnej (4B=32bit) to 31bit + (bit znaku):
2^31-1 / 28207200Wh = 76 lat! ciaglego mierzenia z moca maksymalna
;------------------------------------------------------------------------------
Liczenie mocy P:
Ze wzgledu na nieidealnosc filtru LPF2 moc P odczytana z ADE moze fluktuowac
(zalezy od czestotliwosci napiecia) wokol prawdziwej wartosci.
W najgorszym przypadku probka mocy moze zostac odczytana w "strzalce" sinusoidy (najwiekszy blad).
Przy 50Hz i odczycie probki mocy co sekunde moze sie tak dziac z kazdym odczytem (synchronizacja).
W zwiazku z tym odczytana moc bedzie moca chwilowa (mimo usredniania LPF2). Nalezaloby dodatkowo
usredniac probki mocy przez kilka (-nascie) sekund. Jednak to powoduje opoznienia.
Dlatego moc czynna oblicza sie z Energii sekundowej (Wsp_P = 36000):
P [0,1W] = 36000 * Esek / 2^24 (Esek - energia po korekcji ale przed akumulacja)
(2B) * (4B) => (6B) w praktyce (5B) / (3B) => (2B)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -