⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 int0.asm

📁 实现对ADE7759电能芯片的读写和校准,AVR单片机源码与电路图,单相电能表读写程序和初始化程序
💻 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 + -