Hjälp mig felsöka 1-wire/PWM-kod [LÖST!]

PIC, AVR, Arduino, Raspberry Pi, Basic Stamp, PLC mm.
Tottish
Inlägg: 847
Blev medlem: 30 juni 2007, 19:11:37
Ort: Oslo, Norge

Hjälp mig felsöka 1-wire/PWM-kod [LÖST!]

Inlägg av Tottish »

OK, nu har jag suttit själv med detta problem i ca 20 timmar och tycker fanimej inte att jag kommer närmare en lösning.
Programmet är skrivet i assembler för en PIC16F886 (8Mhz intosc) och ska hantera en PWM (med CCP2) på 5kHz och skall parrallellt med det göra temperaur-avläsningar från en DS18b20 över 1-wire.
Koden är upp byggd på det här viset:
Varje gång som en stigande flank detekters på INT-pinnen (som är knuten till PWM-pinnen) så genereras ett interrupt. I interrupt-rutinen (ISR) så läses ADC-värdet från en pot in och den bestämmer DutyCycle på PWMen. Innan den lämnar ISR så sätter den en flagga för att indikera att ett interrupt har skett.
Denna flagga används av main-koden för det finns bara tid att skicka en bit över one-wirebusen per PWM/ISR-cykel. Man vill ju inte att mjukvaru-protokollet ska haverera pga att ett interrupt inträffar mitt i en bit-sändning så därför löste jag det så att main-koden helt enkelt sänder/tar emot en bit och går sedan in i en loop där den kollar statusen på ISR-flaggan och så fort den är satt till 1 så sätter loopen den tillbaks till noll och går vidare för att sända bit nummer två osv...

Problemet är att jag vill ha en min/maxvärdes limitering i ISRen. När jag implementerat detta så börjar koden bete sig väldigt underligt. Jag upptäckte det dock väldigt sent och kan inte garantera att det är det som är felet för programmet beter sig så (för mig) irrationellt så jag vet varken ut eller in men jag har i alla fall isolerat ett exempel som jag kan visa för er.
Jag har i exempelt implementerat en liten bit kod som kontrollerar om värdet från ADCn är högre än en viss nivå. I exemplet så är värdet 80 vilket motsvarar ungefär 80% DutyCycle.
Om värdet är under 80 så kör den en goto till där DutyCycle bestämms men om det är över 80 så hoppar den över goto:n och går in i en likadan goto raden efter.
Helt värdelös funktion men den utlöser det fel som jag brottas med.
I slutet på main loopen så säger jag åt PICen att skicka ut de två byte som den fått av DS18b20 innehållandes temperaturen seriellt ut på UARTen. Dessa är korrekta så länge jag håller ADC-värdet under den givna gränsen (80i det här exemplet) men går jag över den så blir det två stycken h'FF' ut till oscilloskopet.
Hårdkodar jag två värden precis innan det är dags att sända ut dem på UARTen så blir de precis som jag vill ha dem så jag kan bara dra den slutsatsen att det borde vara i inläsningen som det blir fel.
Jag kan dock inte för mitt liv begripa hur/var/varför det blir fel. Det ser helt perfekt ut på oscilloskopet och jag ser att DS18b20n sänder ut rätt data hela tiden och PICen samplar på exakt rätt ställen men ändå blir det bara ettor i registret. Men alltså bara när ADC-värdet är högre än 80, vilket naturligtvis inte ska ha med saken att göra.
Jag vill också tillägga att ingen tempavläsning görs i den här exempelkoden utan för debugning så använder jag bara de kända default-värdet i temp-registret i DS18b20.

Det är en ganska mastig kod så jag börjar med att posta mainloopen, ISRen och ReceiveByte-Loopen/funktionen eftersom det andra verkar funka som det ska. Vill ni ha mer så ska jag städa upp koden och lägga ut men det känns som att jag måste ha missat något elementärt som uppkallar det här beteendet och jag är fast besluten om att ta reda på VAD det är som är fel. Har slösat bort alldeles för mycket tid med den här koden för att jag inte ska få lära mig felet jag gjort och kunna se till att aldrig aldrig ALDRIG upprepas igen! :shock:

Kod: Markera allt

main	
		call	NextStepWaitLoop

	call	Delay				;Delays for about 100ms. Gives the DS18b20 time to convert temp

	call	NextStepWaitLoop	;For correct timing of the resetPulse
	call	ResetPulse
	
	movlw	h'CC'				;The SkipROM-command
	movwf	OWbyte1
	call	SendByte

	movlw	h'BE'				;The ReadScratchpad-command
	movwf	OWbyte1
	call	SendByte		

		banksel	PORTA
		bsf		PORTA, 0			
	call	ReceiveByte
	movfw	OWbyte1				;Copy LSB just received to OWbyte2
	movwf	OWbyte2
	call	ReceiveByte			;Gets the MSB and puts it in OWbyte1
		banksel	PORTA
		bcf		PORTA, 0


	banksel	TXREG
	movfw	OWbyte2
	movwf	TXREG

	banksel	PIR1	
	btfss	PIR1, TXIF	
	goto 	$-2

	banksel	TXREG
	movfw	OWbyte1
	movwf	TXREG

	goto	main	

;RECEIVE-BYTE****************************************************************
;This routine Recieves a byte from the OW-bus and stores it in OWByte1.

ReceiveByte
	clrf	OWByte1		;Clear it out
	movlw	d'8'		;Eight bits in the byte
	movwf	OWcounter	
	bcf		STATUS, C
	
ReceiveLoop
	call	NextStepWaitLoop
	rrf		OWbyte1, 1	;Rotate byte for next bit, store result in OWbyte1
	call	LoZ		;bring OWpin low for 6µs
	nop
	nop
	nop
	nop			
	call	HiZ		;bring it high and wait 4µs before sampling
	nop
	nop
	nop
	nop
	nop
	nop
	nop

	banksel	PORTC
			bcf		PORTA, 0			
	btfsc	PORTC, 0	;Sample bus, If its high, write bit=1
	bsf		OWbyte1, 7	
			bsf		PORTA, 0	
	decfsz	OWcounter
	goto	ReceiveLoop

	return				;When counter hits zero and all(8) bits has been received

;Så här ser loopen ut som väntar på att flaggan ska sättas i ISRen.
NextStepWaitLoop
	btfss	NextStep
	goto	NextStepWaitLoop
	bcf		NextStep
	return

;;;;;;;;;;;;;;;;;;;;;;;;;;ISR;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;***************************************************************

Interrupt
		call	DebugLEDon	

	banksel	ADRESH
	movfw	ADRESH

	movwf	DutyCycle		 

HighLimitTest			;if RAMx < K
	movfw	HighLimit
	subwf	DutyCycle, 0	;result in W 
	skpc
	goto	AssignDutyCycle ;LowLimitTest

	goto	AssignDutyCycle

AssignDutyCycle
	movfw	DutyCycle

	banksel	CCPR2L			;Put the 8 MSb in the Duty-cycle
	movwf	CCPR2L
	
	banksel	ADCON0			;Order new A/D aquisition
	bsf		ADCON0, 1

	banksel INTCON
	bcf		INTCON, INTF	;Clear the INT-pin Interrupt-flag

	bsf		NextStep 		;(Tells the OW-guys that a new cycle has been initiated.

		call	DebugLEDoff
	
	retfie

Funderade på om det var något med timingen som blev fel när den är tvungen att skriva till ProgramCounter men laborerande med nop visar att så inte är fallet. Jag kan lägga till både en, två och tre nop i ISRen utan att det påverkar utgånegn av laborationen.

Jag är oändligt tacksam för all hjälp jag kan få här. Har kört fast som aldrig förr. Tyckte precis att jag hade fått så pass bra kläm på det här med Assembler att jag kan "klara mig själv" men det högmodet bestraffades å det grövsta.

Tack för ordet,
MVH
/En väldigt matt Tottish
Senast redigerad av Tottish 16 december 2008, 14:21:25, redigerad totalt 1 gång.
bearing
Inlägg: 11677
Blev medlem: 2 mars 2006, 01:01:45
Ort: Ängelholm

Inlägg av bearing »

För att slippa risken att få ett interrupt mitt i one-wire rutinen kan du helt enkelt stänga av interrupt innan den anropas.

Jag tycker det verkar krångligt att använda INT-interrupt kopplat till CCP2-pinnen istället för att använda ett timerinterrupt. Behöver verkligen potentiometern avläsas så ofta? jag tror det går bra med ett timerinterrupt t.ex. 10 gånger per sekund. Har du räknat om ADC:en hinner sampla i 5kHz?

Jag förstår inte testvillkåret i interruptet. Vad är det meningen ska hända?

Med koden som är nu är det (väl?) ingen skillnad mellan >80 och <=80, förutom att det kanske blir något fel på grund av att variabeln slår runt.

Är serieporten initierad att skicka 8 bitar?
Om den skickar 7 är det ju naturligt att >=0x80 blir fel.
Tottish
Inlägg: 847
Blev medlem: 30 juni 2007, 19:11:37
Ort: Oslo, Norge

Inlägg av Tottish »

Potentiumetern behöver inte avläsas så ofta men tanken är att detta skall styras av en dator sedan och mycket snabba ändringar är önskvärda i slutapplikationen. Jag känner inte till impendansen i styrsignalen i fråga ännu så jag kan inte avgöra om 5kHz funkar men jag vill ha det på det viset i den här koden så kan jag säga åt den att bara sampla om varannat varv istället om det blir nödvändigt.

Testvillkåret är som sagt värdelöst. Det kan dock enkelt modifieras om så att om ADC-output > 80 så sätter vi DutyCycle till 80. Ville dock bryta ner problemet så mycket som möjligt och upptäckte att jag inte behöver skriva till/från variabler för att felet skulle uppstå. Utfallet är dock detsamma om jag använder den som HighLimit-test.

"Med koden som är nu är det (väl?) ingen skillnad mellan >80 och <=80, förutom att det kanske blir något fel på grund av att variabeln slår runt. "
Förstår inte riktigt vad du menar med att variabeln 'slår runt'. Vilken variabel menar du? DutyCycle? Den förblir väl oförändrad oavsätt relationen till 80?

"Är serieporten initierad att skicka 8 bitar?
Om den skickar 7 är det ju naturligt att >=0x80 blir fel."
Som sagt: Skickar jag hårdkodade värden så går det galant. Tror du har missförstått en sak här, dock; Det är alltså inte DutyCycle som ska skickas ut på UART utan den inlästa tempraturen.

MVH
/Tottish
bearing
Inlägg: 11677
Blev medlem: 2 mars 2006, 01:01:45
Ort: Ängelholm

Inlägg av bearing »

Jag såg inte att dutycycle lades tillbaka i W.

Förstår inte så mycket av det du skrivit, måste jag säga. Det är rörigt.

Menar du att värdena som skickas över serieporten påverkas av att ADC-gränsen läggs in i interruptet?
Tottish
Inlägg: 847
Blev medlem: 30 juni 2007, 19:11:37
Ort: Oslo, Norge

Inlägg av Tottish »

"I slutet på main loopen så säger jag åt PICen att skicka ut de två byte som den fått av DS18b20 innehållandes temperaturen seriellt ut på UARTen. Dessa är korrekta så länge jag håller ADC-värdet under den givna gränsen (80 i det här exemplet) men går jag över den så blir det två stycken h'FF' ut till oscilloskopet."

Vet inte om jag kan beskriva det mycket närmare... Om jag ställer poten så att det kommer ett värde från ADCn som är högre än 80 så skickar UARTen ut två byte innehållande h'FF' istället för de värden som kommer från DS18b20 (h'05' och h'50').
v-g
EF Sponsor
Inlägg: 7875
Blev medlem: 25 november 2005, 23:47:53
Ort: Kramforce

Inlägg av v-g »

Dels bör man väl spara undan alla värden på status wreg osv i tempregister medan man meckar i interuptrutinen (du kanske gjort det men utelämnat koden i tråden)

Carryflaggan är lömsk när man subtraherar använd ADDWF istället. Testa med olika värden i simulatorn. Kontrollera genom att hårdkoda dessa värden i koden om den gör som du vill.
Tottish
Inlägg: 847
Blev medlem: 30 juni 2007, 19:11:37
Ort: Oslo, Norge

Inlägg av Tottish »

Puuuh, tror jag har hittat felet nu, åt minstone ett alvarligt fel och det var tämligen elemnetärt.
I receiveloop så roterar jag ju in vilket värde som än finns i STATUS, C i den byte jag tar emot! :shock:

Hur jag lyckades undvika att problemet utlöste fattar jag fortfarande inte. Hade suttit och programerat i tre dagar och allt flöt på väldigt fint. Det var först när jag trodde att jag var klar som jag upptäckte felet. Var väl inte på poten och skruvade så ofta kanske men ändå....

Tack för hjälpen i alla fall! Skriver vidare om det inte löser problemet helt.

MVH
/Tottish
bearing
Inlägg: 11677
Blev medlem: 2 mars 2006, 01:01:45
Ort: Ängelholm

Inlägg av bearing »

Fixade du så att W och STATUS sparas och återställs i interruptet?
Tottish
Inlägg: 847
Blev medlem: 30 juni 2007, 19:11:37
Ort: Oslo, Norge

Inlägg av Tottish »

Nej, jag löste det på ett enklare sätt för just den här applikationen. En BCF STATUS, C innan RRF-kommandot fixade biffen. Skulle inte tjäna mycket till spara undan C-värdet här då det är 'okänt' redan från början. Det är alltså inte säkert att det skulle funka även om den inte var interrupt-styrd och om den skulle göra det så skulle rutinen vara väldigt känslig för ändringar i koden som berör C-flaggan.
Att spara undan resten av STATUS och Wreg känns inte nödvändigt då koden är skriven så att programmet alltid ska vara "idle-state" när ett interrupt inträffar, alltså bara stå och vänta på just detta så att det kan komma åt att sända nästa bit.
Undantaget är Delay-loopen som ger DS18b20 tid att konvertera tempraturen till ett digitalt värde (ca 100ms långt). Här har du en poäng bearing, när jag skriver countdown-värdet till subloopen här inne så kan man gott tänka sig att ett interrupt kommer och stör ibland, har inte visat sig vara ett problem men det ärnog bäst att jag ändrar det ändå.

Tack för de interrupt-rellaterade tipsen bearing och v-g! Det här är det första program jag skriver som använder interrupts och är timing-kritiskt. Har en känsla att det här kommer gå smidigare i framtiden nu när man trampat i några fallgropar och blivit påvidsad andra.

MVH
/Tottish

EDIT: Äsch, rätt ska väl vara rätt antar jag. Lika bra att spara undan värdena, sagt och gjort. :)
sodjan
EF Sponsor
Inlägg: 43251
Blev medlem: 10 maj 2005, 16:29:20
Ort: Söderköping

Inlägg av sodjan »

Eller stänga av interrupt under delay loopen, om det är OK...
bearing
Inlägg: 11677
Blev medlem: 2 mars 2006, 01:01:45
Ort: Ängelholm

Inlägg av bearing »

Jag tror definitivt att det är nödvändigt att spara undan registrena. Om ett interrupt sker mellan dessa instruktioner t.ex. kommer fel värde att skickas till datorn.

Kod: Markera allt

	movfw	OWbyte2
	movwf	TXREG
Lägg också in BCF/BSF INTCON, GIE innan/efter one-wire rutinen, annars kommer ju timingen bli fel om ett interrupt sker där.
Tottish
Inlägg: 847
Blev medlem: 30 juni 2007, 19:11:37
Ort: Oslo, Norge

Inlägg av Tottish »

sodjan:
Nej, jag vill ha kontinuerlig uppdatering av DutyCyclen och ett uppbehåll på 100ms är inte acceptabelt. (DutyCycle uppdateras i interrupt-koden)

bearing:
Jag tror inte du förstår vad jag menar. Alltså, koden är skriven så att ett interrupt *bara* kan inträffa när koden står och väntar på att just det ska hända (undantag: delayloopen som redan avhandlats). Eftersom jag vet att ett interrupt kommer att inträffa var 200:e µs, (periodtid för 5kHz) varken oftare eller mer sällan, så har jag alltså gjort kodsnuttarna som exekveras för varje enskild cykel kortare än de ca 160µs jag har på mig mellan interrupten (själva interruptkoden tar upp till 40µs att exekvera). Det var detta jag försökte uttrycka i mitt förra inlägg.

Därför är det i det kodexempel som du tagit upp ingen risk att ett interrupt sker så länge CCP-modulen beter sig som den ska.
Av samma anledning så ska jag inte slå av interrupts i 1-wire rutinen eftersom den då skulle sända en bit för att sedan stå och vänta på att ett interrupt skulle inträffa så att den, efter det, skulle kunna sända nästa bit. Som jag skrev i första inlägget så finns bara tid att sända en bit ber cykel.

Förstår du hur jag har kodat? Eller är det jag som missförstått dig?

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

Inlägg av sodjan »

> Här har du en poäng bearing, när jag skriver countdown-värdet till
> subloopen här inne så kan man gott tänka sig att ett interrupt
> kommer och stör ibland,

Notera att, att stänga av GIE en kort stund (t.ex runt två MOVLW, MOVFW)
inte *förhindrar* ett interrupt att inträffa, det bara fördröjer det ett par instruktionscykler.
Interruptet kommer alltså så snart GIE sätts på igen...

Ibland kan det vara bättre att göra så än att lägga till de extra cykler
som går åt för att spara register i själva interruptkoden.
Tottish
Inlägg: 847
Blev medlem: 30 juni 2007, 19:11:37
Ort: Oslo, Norge

Inlägg av Tottish »

Det har du förstås rätt i sodjan... För att citera Peter Dalle: "Tänkte inte på det..."

Skulle kunna ändra det i koden men jag har så pass bra med tid och allt verkar fungera felfritt nu så jag tror jag kör på så här. Ska dock se till att ha det i bakhuvudet till nästa gång.

MVH
/Tottish
bearing
Inlägg: 11677
Blev medlem: 2 mars 2006, 01:01:45
Ort: Ängelholm

Inlägg av bearing »

Då är det jag som inte förstått. Jag ber om ursäkt.

Du har alltså gjort så att PWM-modulens pulser är "klockan" i systemet. Jag förstår, och ser vissa fördelar med det.

PWM-modulen kan ge ett internt interrupt, om jag minns rätt. Det skulle spara dig en pinne, samt minska risken att det externa interruptet inte uppfattar pulsen, när t.ex. duty är 0.


Har du räknat på hur lång tid det här tar?
Jag minns inte hur det var med bufferstorleken på TXREG, men om det är så att väntandet på TXIF tar 10/9600 sekunder hinner det ske flera interrupt under den tiden.

Kod: Markera allt

	banksel	TXREG
	movfw	OWbyte2
	movwf	TXREG

	banksel	PIR1	
	btfss	PIR1, TXIF	
	goto 	$-2

	banksel	TXREG
	movfw	OWbyte1
	movwf	TXREG
Skriv svar