Flytande medelvärde i avr

PIC, AVR, Arduino, Raspberry Pi, Basic Stamp, PLC mm.
sodjan
EF Sponsor
Inlägg: 43251
Blev medlem: 10 maj 2005, 16:29:20
Ort: Söderköping

Inlägg av sodjan »

Jag vet inte med AVR, men på PIC har varje interrupt-källa även en
"enable" flagga. Om den inte är "satt" så sker inget interrupt från den
källan. Borde rimligtsvis vara likadant på AVR...
Användarvisningsbild
Swech
EF Sponsor
Inlägg: 4750
Blev medlem: 6 november 2006, 21:43:35
Ort: Munkedal, Sverige (Sweden)
Kontakt:

Oönskade interrupt

Inlägg av Swech »

Det stämmer bra det Sodjan skriver att interrupts bör vara maskbara även på AVR.
Det jag menar är att vara på den säkra sidan. Om systemet hickar till, om du får en brownout eller något annat fenomen så kan det hända, jag säger inte att det händer. Det är lite som att jag har säkerhetsbälte på mig när jag kör trots att jag inte planerar att krocka..

Anledning nr 2 är att kod går mycket fort att skriva... men att underhålla, modifiera kod 6 månader - 1 år efter man skrev den tar MYCKET längre tid
För att slippa oroa sig.. t.ex. "stängde jag av UART interrupten?"
så är det lättast att vara konsekvent. Definiera upp alla interrupt,
tar 20-30 bytes som man lätt kan avvara och du har ett problem mindre att
tänka på.

Anledning nr 3. Klipp och klistra stadiet. När man skapat sig en hög med
bra rutiner man vill återanvända... ju mindre av "måste ligga på adress 0100h" för att funka destå bättre går det.

Obs. endast goda råd... vill inte "knäppa någon på näsan"...
är bara glad att kunna dela med mig av AVR tips :)

Jonas / Swech
Användarvisningsbild
exile
EF Sponsor
Inlägg: 496
Blev medlem: 21 oktober 2005, 23:32:07

Inlägg av exile »

Nja, ska man tänka så är nog en hopp till reset vektorn bättre efter som något troligt viss är någe galet. Dess utom bör allt oanvänd mine oxå hoppa till rest, om man ska vara riktigt "petig"... ^^

Men om du har några fler goda råd så får du gärna komma med dem, då det är roligt veta hur andra resonerar ^^
Användarvisningsbild
Nisse
Inlägg: 908
Blev medlem: 9 juli 2006, 23:25:46
Ort: Kumla

Inlägg av Nisse »

Nu har jag fördjupat mig lite i ditt inlägg Swech.

En fråga angående punkt 4. Förmodar att du med "statusregistret" menar SREG. Men Push SREG ger följande fel error: invalid register vid kompilering.

Vilka andra register tänker du kan vara bra att spara undan?

Mvh
Nisse
Användarvisningsbild
exile
EF Sponsor
Inlägg: 496
Blev medlem: 21 oktober 2005, 23:32:07

Inlägg av exile »

Du måste först spar vanligt register först, vilket du sedan använder för att läsa in SREG, som du sedan push:ar på stacken.

typ

Kod: Markera allt

push r16
in     r16,SREG
push r16

massa kod

pop  r16
out  SREG, r16
pop  r16
Sedan är det bara att spara unda de register som du använder i din rutin... ^^
Användarvisningsbild
Nisse
Inlägg: 908
Blev medlem: 9 juli 2006, 23:25:46
Ort: Kumla

Inlägg av Nisse »

Sådär nu har jag rotat runt lite i koden igen.
Vad det gället att spara undan register på stacken har jag en fundering. Det finns väl inget som hindrar att man skapar två subrutiner, en för att spara ut på stacken och en för att hämta från stacken? Dessa anropar man sedan först i rutinen, respektive precis innan RETI. Skulle underlätta en del om man gör så. Har interuptrutiner som kan sluta på flera ställen och skulle man vilja ändra vilka register man sparar undan så kan man lätt missa på något ställe. Får prova helt enkelt.


Nu har det uppstått en annan fråga. Det gäller programmets presentation på RS232. När jag skrev ihop de bitar som skickar ut text via seriedata så läste jag ett exempel och använde det mer eller mindre rakt av. Därför uppstår nu en lite fråga kring följande
ldi ZL, LOW(text_seconds*2) ;set Low memory pointer
ldi ZH, HIGH(text_seconds*2) ;set High memory pointer

Varför skall det vara *2 med?


Passar återigen på att rikta ett stort tack till alla som tar sig tid att komma med förslag, ideér, synpunkter, osv. Det är helt ovärderligt. TACK!

Mvh
Nisse


Koden i sin helhet:

Kod: Markera allt


.include "m88def.inc" 

; 8 MHz external chrystal

.org	0x000
rjmp	Reset		; Reser Vector

.org	INT0addr	; Interuptvector external INT0
rjmp	startcalib

.org	OC1Aaddr	; Interuptvektor timer 1 match A
rjmp	timer1


;--------------------------
; initialise Stack Pointer
;--------------------------
reset:
ldi 	r16, 	low(RAMEND)
out 	SPL, 	r16
ldi		r16,	high(RAMEND)
out		SPH,	r16

;--------------------------
; Set definitions
;--------------------------
.def 	data = 	r0		; Used when Data is read from Flash
.def	calibtime = r14	; Counts down calibration time > 0 means calibration running
.def	switch = r15	; CPS switchpoint
.def	temp =	r16		; Temp
.def	temp2 =	r17		; Temp
.def	cps =	r18		; Counts per second
.def	avg =	r19		; Avrage counts per second over 8 seconds
.def	hex =	r20		; hex value to convert to ascii (could be substiuted for temp)
.def	dig1 =	r21		; Digit 1 of ascii value
.def	dig2 =	r22		; Digit 2 of ascii value
.def	dig3 =	r23		; Digit 3 of ascii value
.def	sum =	r24		; The sum of all numbers in buffer. Used to calculate avrage

.equ	buff_length = 8		; Length of buffer






;--------------------------
; Set buffer pointers and zero buffer
;--------------------------
ldi		XH,		high(avg_buffer)	; Set buffer pointers
ldi		XL,		low(avg_buffer) 
ldi		temp,	0			; Set temp to Zero
zero_buffer:
st		X+,		temp		; Load 0 into buffer and increase pointer
cpi		XL,		buff_length	; Check if buffer end
brne	zero_buffer			; Repeat if not end
ldi		XH,		high(avg_buffer)	; Set buffer pointers
ldi		XL,		low(avg_buffer) 
ldi		sum,	0			; Set sum to zero

;--------------------------
; USART init
;--------------------------
;set baudrate
ldi		temp,	(0<<U2X0)
sts		UCSR0A,	temp
ldi		temp,	51 ; 51 - 9600 Baud @ 8 MHz
sts		UBRR0L, temp
;enable transmitter
ldi		temp,	(1<<TXEN0)
sts		UCSR0B,	temp
;set frame format 8n1
ldi		temp,	(1<<UCSZ00)|(1<<UCSZ01)
sts		UCSR0C,	temp


;--------------------------
; Set timer 0 to count CPS via pin T0
;--------------------------

ldi		temp,	(1<<CS02)|(1<<CS01)|(1<<CS00)  ;Pin T0 on rising edge
out		TCCR0B,	temp
ldi		temp,	0
out		TCNT0,	temp

;--------------------------
; Set interupts
;--------------------------

; Extarnal interupt 0 used to start calibration
ldi		temp,	(1<<INT0) ; Extern interupt INT0
out		EIMSK,	temp
ldi		temp,	(1<<ISC01)|(1<<ISC00) ; Rising edge
sts		EICRA,	temp

; Timer 1 compare interupt used for seconds
ldi		temp,	30		; Set timer compare
ldi		temp2,	255		; 30x255 gives about 1 second @ 8 MHz
sts		OCR1AH,	temp	
sts		OCR1AL,	temp2
ldi		temp,	(1<<OCIE1A) ; Timer match A interupt
sts		TIMSK1,	temp
ldi		temp,	(1<<CS12)|(1<<CS10)|(1<<WGM12) ; Timer1 prescaler 1024 & CTC
sts		TCCR1B,	temp

sei							; Global interupt on


;--------------------------
; Main program
;--------------------------

main:


rjmp main

;--------------------------
; Calilbration routine
;--------------------------
; During 32 seconds counts are read each second from counter 0
; and added to register YL, YH that serves as a 16 bit register together.
; After 32 seconds the sum in YL, YH is divided in 32 to get an avrage
; and then divided in 2 to get the switchpoint
calibrate:
in		cps,	TCNT0	;Get pulse count from counter 0
ldi		temp,	0
out		TCNT0,	temp	;Clear counter 0

add		YL,		cps
adc		YH,		temp

ldi 	ZL, 	LOW(text_cps*2) ;set Low memory pointer
ldi 	ZH, 	HIGH(text_cps*2) ;set High memory pointer
rcall	transmitt
mov		hex,	cps
rcall	bcd

ldi 	ZL, 	LOW(text_seconds*2) ;set Low memory pointer
ldi 	ZH, 	HIGH(text_seconds*2) ;set High memory pointer
rcall	transmitt
mov		hex,	calibtime
rcall	bcd

ldi 	ZL, 	LOW(text_crlf*2) ;set Low memory pointer
ldi 	ZH, 	HIGH(text_crlf*2) ;set High memory pointer
rcall	transmitt

dec		calibtime
ldi 	temp,	0
cp		calibtime,	temp
breq	calibrate_1

pop		temp			; Get vital registers back from stack
out		SREG,	temp
pop		temp2
pop		temp

reti

calibrate_1: ;Calculate average and divide by 2 to get switchpoint
clc
ror		YH
ror		YL
clc
ror		YH
ror		YL
clc
ror		YH
ror		YL
clc
ror		YH
ror		YL
clc
ror		YH
ror		YL		; Avrage after divide by 2,5 times (2^5=32)
clc
ror		YH		; Divide by 2 to get switchpoint
ror		YL
mov		switch,	YL

pop		temp			; Get vital registers back from stack
out		SREG,	temp
pop		temp2
pop		temp

reti

;--------------------------
; Timer interupt 1
;--------------------------
; In this interupt most of the action is.
; The counter is read and zeroed, the rotating buffer is keept updated and avrage CPS is calculated
; Presentation och RS232.
; And if Calibflag is < 0 the calibration is done
timer1:

push	temp			; Push vital registers to stack
push	temp2
in		temp,	SREG
push	temp


ldi 	temp,	0
cp		calibtime,	temp
brne	calibrate


in		cps,	TCNT0	;Get pulse count from counter 0
ldi		temp,	0
out		TCNT0,	temp	;Clear counter 0

; Avrage using rotating buffer
ld		temp,	X		;fetch old value from buffer
sub		sum,	temp	;subtract from sum
st		X+,		cps		;store new value in buffer and increase pointer
add		sum,	cps		;add new value to sum
andi	XL,		buff_length - 1	;check if pointer reached buffer end
mov		temp,	sum
; Divide by 8
lsr		temp
lsr		temp
lsr		temp
mov		avg,	temp


; Present via RS232

ldi 	ZL, 	LOW(text_cps*2) ;set Low memory pointer
ldi 	ZH, 	HIGH(text_cps*2) ;set High memory pointer
rcall	transmitt

mov		hex,	cps
rcall	bcd


ldi 	ZL, 	LOW(text_cpsavg*2) ;set Low memory pointer
ldi 	ZH, 	HIGH(text_cpsavg*2) ;set High memory pointer
rcall	transmitt


mov		hex,	avg
rcall	bcd

ldi 	ZL, 	LOW(text_cpsswitch*2) ;set Low memory pointer
ldi 	ZH, 	HIGH(text_cpsswitch*2) ;set High memory pointer
rcall	transmitt


mov		hex,	switch
rcall	bcd

ldi 	ZL, 	LOW(text_crlf*2) ;set Low memory pointer
ldi 	ZH, 	HIGH(text_crlf*2) ;set High memory pointer
rcall	transmitt


pop		temp			; Get vital registers back from stack
out		SREG,	temp
pop		temp2
pop		temp

reti



;--------------------------
; Transmitt via RS232
;--------------------------
transmitt:
rcall	ready_tx
lpm		temp2,	Z+
cpi		temp2,	0
breq	transmitt1
sts 	UDR0,	temp2
rjmp	transmitt
transmitt1:
ret

;--------------------------
; waits for tx ready
;--------------------------
ready_tx:
lds		temp,	UCSR0A
sbrs 	temp,	UDRE0
rjmp	ready_tx
ret

 
;--------------------------
; Hex to ascii and out rs232
;--------------------------
;input: hex = 8 bit value 0 ... 255 
;output: dig1, dig2, dig3 = digits 
bcd: 
ldi		dig1,	-1 + '0' 
_bcd1: 
inc 	dig1 
subi 	hex,	100 
brcc	_bcd1 
ldi 	dig2,	10 + '0' 
_bcd2: 
dec		dig2
subi 	hex,	-10 
brcs 	_bcd2 
sbci 	hex, 	-'0' 
mov		dig3,	hex

rcall	ready_tx
cpi		dig1,	'0'
brne	_bcd3
ldi		dig1,	' '
_bcd3:
sts		UDR0,	dig1

rcall	ready_tx
cpi		dig2,	'0'
brne	_bcd4
cpi		dig1,	' '
brne	_bcd4
ldi		dig2,	' '
_bcd4:
sts		UDR0,	dig2

rcall	ready_tx
sts		UDR0,	dig3

ret 

;--------------------------
; Calibration start
;--------------------------
; Set the flag calibtime to start the calibration routine.
startcalib:
push	temp				; Store the register on the stack
ldi		temp,	32			; Count pulses during 32 seconds
mov		calibtime,	temp
pop		temp				; Get the register from stack
reti



;--------------------------
; RAM address
;--------------------------

.DSEG
avg_buffer:		.BYTE buff_length


;--------------------------
; Dialog text
;--------------------------
.CSEG

text_cps:
.db "cps: ",0


text_cpsavg:
.db "     cps avg: ",0

text_cpsswitch:
.db "     cps switch: ",0

text_seconds:
.db "     seconds: ",0

text_crlf:
.db 10,13,0

Användarvisningsbild
vfr
EF Sponsor
Inlägg: 3515
Blev medlem: 31 mars 2005, 17:55:45
Ort: Kungsbacka

Inlägg av vfr »

Att göra en subrutin för att förändra stacken brukar inte vara någon bra idé. På stacken ligger returadressen för återhoppet från din subrutin och färändrar du stackpekaren så krachar programmet när den skall hoppa tillbaka från subrutinen. Detta _går_ att komma runt om man verkligen vill göra på det sättet men det är varken snyggt eller överskådligt.

Däremot är det väl inget som hindrar att du stackar undan registerna innan du testar dom olika avbrottsflaggorna och det grenar ut sig i olika avbrottsrutiner. Samma sak vid återhopet. Du kan ha en gemensam återhoppspunkt där all återställning av registerna sker för att sedan returnera.

Det blir nästan samma sak fast utan subrutinerna.
Användarvisningsbild
Nisse
Inlägg: 908
Blev medlem: 9 juli 2006, 23:25:46
Ort: Kumla

Inlägg av Nisse »

Ahaa, visst i tusan, återhoppspekaren för subrutinen läggs ju också på stacken.... Hade nästan på känn att det var något jag missat.
Men att ha en gemensam "återhoppspunkt" är ju en enkel och bra lösning. Istället för att ha Reti på flera ställen i rutinen så har man ett hopp till slutet av rutinen där man hämtar tillbaka register från stacken och sedan kör Reti.

Det här är bra, jag lär mig massor... Fortsätt komma med ideér och synpunkter.

Mvh
Nisse
sodjan
EF Sponsor
Inlägg: 43251
Blev medlem: 10 maj 2005, 16:29:20
Ort: Söderköping

Inlägg av sodjan »

> Men att ha en gemensam "återhoppspunkt" är ju en enkel och bra lösning.

Gäller även subrutiner, inte bara interruptrutiner.

Det blir mycket enklare att förstå och modifiera koden senare om
man håller den "ren" där varje rutin (sub- eller interrupt-) har en enda
returpunkt.

Däremot är det ibland praktiskt att ha flera olika "enter" punkter på
samma subrutin...
Användarvisningsbild
Nisse
Inlägg: 908
Blev medlem: 9 juli 2006, 23:25:46
Ort: Kumla

Inlägg av Nisse »

Sodjan -> Jo jag insåg fördelen med att använda det även i subrutiner. Man liksom "knyter ihop" hela rutinen till ett mer lättförståligt paket.


Ingen som har någon bra förklaring till min andra fråga:
Nu har det uppstått en annan fråga. Det gäller programmets presentation på RS232. När jag skrev ihop de bitar som skickar ut text via seriedata så läste jag ett exempel och använde det mer eller mindre rakt av. Därför uppstår nu en lite fråga kring följande
ldi ZL, LOW(text_seconds*2) ;set Low memory pointer
ldi ZH, HIGH(text_seconds*2) ;set High memory pointer

Varför skall det vara *2 med?
Mvh
Nils
Användarvisningsbild
exile
EF Sponsor
Inlägg: 496
Blev medlem: 21 oktober 2005, 23:32:07

Inlägg av exile »

Svaret är enklare än du tror, instruktionerna på avr är 16/32bitar, vilket gör att man inte ha behöver ha addresserna till varje byte, så vid hopp skulle sista biten vara onödig och efter som man normalt endast använder addreserna till att hoppa med så är sista biten onödig, undantaget är när man använder program minet som rom då man läser varje byte för sej, vilket kräver den sista address biten.

Jag hoppas att de rättar ut några fråge tecken... ^^

Angånde pop och push kan man göra makron som man sedan anväder

typ push_all, pop_all


Sedan ser jag att du fortfarande använder

clc
ror YH
ror YL

om du användere
lsr YH
ror YL

så sparar du en instruktion ^^
Användarvisningsbild
Nisse
Inlägg: 908
Blev medlem: 9 juli 2006, 23:25:46
Ort: Kumla

Inlägg av Nisse »

exile -> Tror det var en av de längsta meningar jag läst på länge :) Tyvärr så rätade den inte riktigt ut frågetecknet. Jag greppar nästan vad du menar med adresseringen, men vad innebär just skrivsättet med *2?


Och så var det CLC och ROR

Det var helt enkelt så att jag missförstått funktionen på LSR lite. Jag trodde inte den använde Carry-flaggan alls. Men efter att ha läst lite nogrannare i databladet så ser jag ju att det stämmer inte.

Mvh
Nils
Användarvisningsbild
exile
EF Sponsor
Inlägg: 496
Blev medlem: 21 oktober 2005, 23:32:07

Inlägg av exile »

Att man multipliserar med 2
man kunde lika gärna ha skrivit ex text_seconds<<1

man shiftar helt enkelt addresen ett steg höger

Eller vad det någe annat du inte förstod?


Edit:
Tja, nu när du säger det så är meningen lite väl lång :oops:
Användarvisningsbild
Swech
EF Sponsor
Inlägg: 4750
Blev medlem: 6 november 2006, 21:43:35
Ort: Munkedal, Sverige (Sweden)
Kontakt:

*2

Inlägg av Swech »

Varfär *2 .. JO....
AVR processorerna läser in 16 bitar för varje instruktion.
Varje instruktion är alltid minst 16 bitar (2 bytes) lång. Vissa är längre.
Är de längre är de multiplar av 2 bytes.. 2..4..6..8 osv..
det innebär att programräknaren.. dvs det "register" som håller reda på vilken instruktion som skall utföras alltid pekar på en grupp av 2 bytes..

så programräknaren räknar 0.1.2.3.4.5.6.7 - i programminnet pekar
programräknaren på 0.2.4.6.8.10.12.14 .....

Så ett program..

Kod: Markera allt

ldi r16,0               instr 0 minne 0,1
lpm                     instr 1 minne 2,3 
adiw  #4,zl           instr 2 minne 4,5   
Loop1:                instr 3 minne 6,7   osv.
i exemplet ovan ligger label Loop1 på instruktionsposition 3..
i programminnet är den på adress 6

kompilatorn översätter alltid loop1 till 3
vill man ladda t.ex ZL med adressen till Loop1 så måste man alltså
tänka till.
skriver du ldi zl,loop1 så får du 3 i ZL men man vill ju ha byte adressen som är 6
därför..... skriv ldi zl, 2*loop1 och voilla du får 6.. som är byte adressen till loop1

Vad gäller interrupt har Exile redan svarat alldeles förträffligt..

Koda på nisse... ^^

M.v.h
Jonas /Swech trading
Användarvisningsbild
Swech
EF Sponsor
Inlägg: 4750
Blev medlem: 6 november 2006, 21:43:35
Ort: Munkedal, Sverige (Sweden)
Kontakt:

ytterligare ett tips

Inlägg av Swech »

Ett generellt tips bara..
Snart börjar ditt program bli stort nisse... ju större program destå fler
spännande problem...
När jag började med AVR skrev jag på samma sätt som du. Jag definierade
upp fler och fler register .
.def data = r0 ; Used when Data is read from Flash
.def calibtime = r14 ; Counts down calibration time > 0 means calibration running
.def switch = r15 ; CPS switchpoint
.def temp = r16 ; Temp
.def temp2 = r17 ; Temp
.def cps = r18 ; Counts per second
.def avg = r19 ; Avrage counts per second over 8 seconds
.def hex = r20 ; hex value to convert to ascii (could be substiuted for temp)
.def dig1 = r21 ; Digit 1 of ascii value
.def dig2 = r22 ; Digit 2 of ascii value
.def dig3 = r23 ; Digit 3 of ascii value
.def sum = r24 ; The sum of all numbers in buffer. Used to calculate
Det problem jag stötte på var att jag aldrig kunde komma ihåg om t.ex. hex var register R20 eller R19.. (vad jag menar var attt jag inte visste villka som var upptagna utan att kolla i definitionen) och att registrena mycket snart tog slut.
Använd dig istället av RAM minnet... t.ex i ditt exempel skulle dig1,dig2 och
dig3 läggas där istället för att låsa upp 3 register.
Och snart inser man hur värefulla R16 till R31 är och hur värdelösa R1.. R15
är...
Kör man med Ram får man istället register över.. som man då kan låsa upp
till bättre grejer. Jag nämde detta tidigare.. att ha ett register som alltid är 0
och ett som är -1 är mycket hjälpsamt...

Jonas / Swech
Skriv svar