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 »

OK, bara så att du inte får overflow i summan...

Förklara vad du menar med :

> problemet som det ser ut just nu är att summan liksom aldrig hinner "byggas upp".

När då ? Och byggas upp till vadå ?

Och angående din kod, kommentera *varje* rad, så att det framgår vad *du tror* att du gör.
Det är inte säkert att det stämmer överens med vad koden faktiskt gör... :-)
sodjan
EF Sponsor
Inlägg: 43251
Blev medlem: 10 maj 2005, 16:29:20
Ort: Söderköping

Inlägg av sodjan »

> Summan blir alltså alltid lika med sista värdet.
> Hur kommer jag runt det?

Jag kan inte AVR kod, men är du säker på att du får
indexerad adressering med den kod du har ?
Och ger X+ post- eller pre-increment ? Måste vara post för att det ska fungera...

Är "buff_length" verkligen *längden* på bufferten ?
Inte *adressen* till sista position+1 ?
(Jag kan ha missförstått hur cpi fungerar...)
Användarvisningsbild
Nisse
Inlägg: 908
Blev medlem: 9 juli 2006, 23:25:46
Ort: Kumla

Inlägg av Nisse »

X+ ger post increment.

CPI jämför register

buff_length är satt till 8.
.equ buff_length = 8
Det är ju iof ett mer än verkliga längden eftersom man börjar på 0, men efter som det är post increment så blir det rätt.

> problemet som det ser ut just nu är att summan liksom aldrig hinner "byggas upp".

Jo, sum skall ju vara summan av alla åtta värden. Men eftersom det första jag gör är att drar ifrån det gamla värdet summeras aldrig alla värden in.

Eller vänta nu..... Hmmm, tankevurpa... I början är ju de gamla värdena som dras ifrån 0. Alltså skall det ju funka. Vad är det då som blir galet. Hmmm, kanske skall fylla på lite i glaset som skymtar på föregående bild....

Kod: Markera allt

timer1:                   ; Interupt som utförs en gång per sekund med hjälp av timer1 match A
ld      temp,   X    ; Hämtar gammalt värde som finns i adress X
sub      sum,   temp      ; Drar ifrån gammalt värde från summan
st      X+,      cps          ; Stoppar in aktuellt (nytt) värde på det gamla värdets plats och flyttar fram pekaren
add      sum,   cps        ; Lägger till aktuellt (nytt) värde till summan
cpi      XL,      buff_length  ; Jämför pekaren med buffertlängden
brne   _timer1                     ;Är bufferten inte slut? Hoppa till timer
ldi      XL,      avg_buff_L      ; Annars återställ pekaren
_timer1: 
mov      avg,   sum    ; Flytta summan till avg

ror      avg   ; Dividera med 8 genom att dividera med två tre gånger.
clc 
ror      avg 
clc 
ror      avg 
clc 
reti
Användarvisningsbild
Nisse
Inlägg: 908
Blev medlem: 9 juli 2006, 23:25:46
Ort: Kumla

Inlägg av Nisse »

Här är definitionerna också

Kod: Markera allt

.def 	data = 	r0
.def	temp =	r16
.def	temp2 =	r17
.def	cps =	r18
.def	avg =	r19
.def	hex =	r20
.def	dig1 =	r21
.def	dig2 =	r22
.def	dig3 =	r23
.def	sum =	r24

.equ	avg_buff_H =	0x01
.equ	avg_buff_L =	0x00
.equ	buff_length = 8

ldi		XH,		avg_buff_H
ldi		XL,		avg_buff_L 


Mvh
Nils
Användarvisningsbild
Rohan
Inlägg: 1066
Blev medlem: 7 april 2004, 08:24:39
Ort: Eksjö, Småland
Kontakt:

Inlägg av Rohan »

Nollställer du verkligen alla data i ringbufferten först? Det måste du nog göra för att vara säker.
sodjan
EF Sponsor
Inlägg: 43251
Blev medlem: 10 maj 2005, 16:29:20
Ort: Söderköping

Inlägg av sodjan »

Det bör vara så att det är viktigt att "sum" från början verkligen
stämmer överens med den faktiska summan i hela bufferten
(oavsett om den är noll eller något annat). Annars fär man ju fel
utgångsvärde och det blir en "skevhet" som kommer att följa med hela tiden.

Jag skulle börja med att "nolla" hela bufferten och sätta sum = 0.

I och för sig speler det igentligen ingen roll vad som ligger i bufferten
från början, bara det stämmer överens med startvärdet på "sum".
Men, eftersom din sum inte kan hålla summan av alla tänkbara
värden i bufferten (d.v.s max 255 x 8), så är det bättre att initiera
det hela till ett känt läge...
Användarvisningsbild
Nisse
Inlägg: 908
Blev medlem: 9 juli 2006, 23:25:46
Ort: Kumla

Inlägg av Nisse »

->Jag skulle börja med att "nolla" hela bufferten och sätta sum = 0.

Jo, det tyckte jag med, så det gör jag.

Börjar bli fundersam på om jag använder något register ovarsamt någonstans i koden. Här är hela koden:

Kod: Markera allt

.include "m88def.inc" 

; 8 MHz external chrystal

.org	0x000
rjmp	Reset

.org	INT0addr	; Interuptvector external INT0
rjmp	count

.org	OC1Aaddr	; Interuptvektor timer 1 match A
rjmp	timer1


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

;--------------------------
; Set definitions
;--------------------------
.def 	data = 	r0
.def	temp =	r16
.def	temp2 =	r17
.def	cps =	r18
.def	avg =	r19
.def	hex =	r20
.def	dig1 =	r21
.def	dig2 =	r22
.def	dig3 =	r23
.def	sum =	r24

.equ	avg_buff_H =	0x01
.equ	avg_buff_L =	0x00
.equ	buff_length = 8



ldi		XH,		avg_buff_H
ldi		XL,		avg_buff_L 
ldi		temp,	0
zero_buffer:
st		X+,		temp
cpi		XL,		buff_length
brne	zero_buffer
ldi		XH,		avg_buff_H
ldi		XL,		avg_buff_L

ldi		sum,	0

;--------------------------
; 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 interupts
;--------------------------

ldi		temp,	(1<<INT0) ; Extern interupt INT0
out		EIMSK,	temp
ldi		temp,	(1<<ISC01)|(1<<ISC00) ; Rising edge
sts		EICRA,	temp

ldi		temp,	75	; Sätter timer compare
ldi		temp2,	255	; 75 ger ungefär 2,5 sek
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




;--------------------------
; Timer interupt 1
;--------------------------
timer1:

;Provar medelvärdesbildning....



ld		temp,	X
sub		sum,	temp
st		X+,		cps
add		sum,	cps	
cpi		XL,		buff_length
brne	_timer1
ldi		XH,		avg_buff_H
ldi		XL,		avg_buff_L
_timer1:



mov		avg,	sum
ror		avg
clc
ror		avg
clc
ror		avg
clc



;Slut prov medelvärdesbildning


;Presentera på 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_crlf*2) ;set Low memory pointer
ldi 	ZH, 	HIGH(text_crlf*2) ;set High memory pointer
rcall	transmitt


ldi		cps,	0
reti



;--------------------------
; Transmitt via RS232
;--------------------------
transmitt:
rcall	ready_tx
lpm
mov		temp,	data
cpi		temp,	'#'
breq	transmitt1
sts 	UDR0,	data
adiw 	ZL,1
rjmp	transmitt
transmitt1:
ret


;--------------------------
; Count pulse
;--------------------------
count:
inc		cps
reti

 
;--------------------------
; 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 

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



;--------------------------
; Dialog text
;--------------------------
.CSEG
text_cps:
.db "cps: #"
text_cpsavg:
.db "     cps avg: #"
text_crlf:
.db 10,13,"#"


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

Inlägg av sodjan »

Måste du inte "nolla" cps i timer-interruptet ?
Annars kommer den väll bara att "räkna på"...

EDIT: Äh, du gör ju det, det låg bara inte där *jag* hade lagt det... :-)

EDIT2: En annan sak...
Jag skulle inte lägga något som tar så rellativt lång tid som UART
kommunikation i timer-interruptet. Bättre att ha det i main, så att du
inte missar någon puls.
Användarvisningsbild
Nisse
Inlägg: 908
Blev medlem: 9 juli 2006, 23:25:46
Ort: Kumla

Inlägg av Nisse »

Förmodligen kommer main-loopen vara smockfull med annat jox innan detta är klart. Utadatat kommer förmodligen skickas från en egen interuptrutin som körs på egen timer och utförs betyligt mer sällan, kanske var 20 sekund eller så. Alternativt kollar jag statusen på en in-pinne och skriver bara ut när den är aktiverad.

Men jag blir knäpp, varför funkar det inte? :cry: Nä. nu är det dags att raka sig och se om duschen vill ta imot mig. Lika bra att ge upp nu och titta med "färska ögon" vid ett senare tillfälle.

Och till alla som skrivit svar: Mycket tack! Jag lär mig massor. Fler svar och ideér är mycket välkomna.

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

Inlägg av sodjan »

Ett tips, "simulera" medelvärdesberäkningen med en liten buffert (säg 4 pos)
med papper och penna...

En annan detalj, har du provat med en extra clc före första ror ?
Jag antar att du har med clc för att ror inte ska shifta in "skräp"... (?)

En annan liten detalj...
Jag tror inte att du behöver ladda om XH mer än i initialiseringen,
XH kommer väl aldrig att räknas upp ?

Men det låter konstigt att summan (sum) alltid är samma som *sista* värdet...

Nä, jag är "lost"... :-)
Användarvisningsbild
Andax
Inlägg: 4379
Blev medlem: 4 juli 2005, 23:27:38
Ort: Jönköping

Inlägg av Andax »

Eftersom buffern alltid är en potens av 2, pga av den enkla divisionen då, behöver du inte ha en jämförelse och återladdning utan det går att lösa med ett AND #07h (för length 8) etc.

Sedan vet jag inte om om count interrupten har lägre prioritet än timer1. Om inte kan cps teoretiskt ändras mellan då du lagrar undan cps i buffern och du addedar cps till sum.

Sedan, precis som Sodjan sa, behövs nog ett clc innan första ror också...
Användarvisningsbild
Nisse
Inlägg: 908
Blev medlem: 9 juli 2006, 23:25:46
Ort: Kumla

Inlägg av Nisse »

-> Sedan, precis som Sodjan sa, behövs nog ett clc innan första ror också...

TACK! Där satt problemet. Jag hade ju en clc till varje ror, men hade dom efter istället för före *Doooh*. Då kom ju carry-biten med likaförbaskat i första svängen.


-> utan det går att lösa med ett AND #07h

Okej, om det är någon som ser en herrelös tankeförmåga som springer runt kan ni väl hänvisa den till mig. Andax, det där får du gärna förklara närmare.


-> Jag tror inte att du behöver ladda om XH mer än i initialiseringen,
XH kommer väl aldrig att räknas upp ?

Nepp, men jag satte in den som någon form av desperat åtgärd för att se om det var problemet :oops:


Återigen, Tack för alla svar!
Fortsätt gärna komma med ideér och synpunkter, jag suger i mig kunskaper som en svamp.

Mvh
Nils
Användarvisningsbild
Nisse
Inlägg: 908
Blev medlem: 9 juli 2006, 23:25:46
Ort: Kumla

Inlägg av Nisse »

Andax skrev:Sedan vet jag inte om om count interrupten har lägre prioritet än timer1. Om inte kan cps teoretiskt ändras mellan då du lagrar undan cps i buffern och du addedar cps till sum.
Som jag förstått databladet så stängs alla interupt av medans ett interupt körs. Först när instruktionen RETI utförs aktiveras de igen. Kan någon verifiera att jag fattat rätt?

Det är därför timer1-interuptet skall innhålla så lite kod som möjligt, som sodjan påpekade, för att inte riskera att tappa pulser. Men å andra sidan är det långsamma förlopp och väldigt få pulser, så risken är liten. Och även om det händer tappar man aldrig mer än 1 puls vilket inte syns i den statistiska variationen från strålkällan.

Skulle det bli kritiskt kan jag gå upp till 20 MHz kristall och få lite mer overhead.

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

Inlägg av sodjan »

> Som jag förstått databladet så stängs alla interupt av medans ett interupt körs.

Utan att känna till AVR i detalj, så verkar det rimligt, ja.

> Det är därför timer1-interuptet skall innhålla så lite kod som möjligt

Ja, och att bara plocka bort UART delen gör en *väldig* skillnad.
Du har ju en wait-loop i ready_tx där en massa instruktioner kommer
att "brännas av" i interruptet...

> Och även om det händer tappar man aldrig mer än 1 puls

Eller 2, eller 3. Det kan du inte veta. Men visst, rent statistiskt händer
det inte ofta, så i kurvorna kanske det inte märks.

> -> utan det går att lösa med ett AND #07h

Du gör AND mellan XL och '00000111', så när XL kommer till '00001000'
så blir resultatet '00000000'.
En fördel (kan *ibland* vara viktigt) är att koden *alltid* tar exakt samma
antal instruktioner...
Användarvisningsbild
Nisse
Inlägg: 908
Blev medlem: 9 juli 2006, 23:25:46
Ort: Kumla

Inlägg av Nisse »

Tack Sodjan, nu fattar jag.
Byter jämförandet och vilkorshoppet till: andi XL, buff_length - 1

Så nu är koden riktigt civiliserad :)

Kod: Markera allt

ld		temp,	X
sub		sum,	temp
st		X+,		cps
add		sum,	cps	
andi	XL,		buff_length - 1

mov		avg,	sum
clc
ror		avg
clc
ror		avg
clc
ror		avg
Edit: *Kanske skall ändra så att buff_length blir "rätt", dvs börjar på noll. Slipper ju dra bort 1 vid ANDI då* Nää nu skall jag sova.

Mvh
Nils
Skriv svar