PIC 16F690 LED dimmer

PIC, AVR, Arduino, Raspberry Pi, Basic Stamp, PLC mm.
F00F
Inlägg: 3
Blev medlem: 9 november 2008, 20:19:28
Ort: Karlskrona

PIC 16F690 LED dimmer

Inlägg av F00F »

Hej. Jag är en nybörjare som skaffat mig en pickit2 och provat mig fram med de exempel jag fick med CD skiva från Microchip. Efter alla blinka LED finesser blev jag nyfiken på att dimmra LED med hårdvaru PWM som är inbyggd i 16F690. Tanken är att styra ljusstyrka på en LED med två knappar. Efter en svår vecka, en del av läsande i datasheet och kliande på huvudet fick jag fram ett stycke kod som fungerar, kan man säga. Kan jag få era synpunkter eftersom jag tror att det finns mycket dåliga lösningar i koden. Tack.

Kod: Markera allt

; Styrning av ljusstyrka på en LED med två knappar
; PIC 16F690 @ 4MHz


#include <p16F690.inc>
    __config (_INTRC_OSC_NOCLKOUT & _WDT_OFF & _PWRTE_OFF & _MCLRE_OFF & _CP_OFF & _BOR_OFF & _IESO_OFF & _FCMEN_OFF)

    cblock 0x20
	d1
	d2                  
     endc
      
     org 0

     banksel	ANSEL
	 clrf		ANSEL
	 banksel    TRISC               
	 clrf		TRISC
	 movlw      b'00010000'		; Stäng PWM pin utgång
	 movwf	    TRISC                 
	 bsf       TRISA,TRISA2		; Sätta RA2 som ingång 
								;(RA3 på 16F690 är ingång i default)  
	 
	 banksel	PR2				; PWM period 
	 clrf	    PR2
	 movlw	    b'11111111'
	 movwf		PR2 
	 
	 banksel	CCP1CON	        ; Konfigurera CCP modul för PWM
	 movlw      b'01101100'
	 movwf      CCP1CON
	 
	 banksel	CCPR1L			;PWM duty cycle
	 movlw	    b'00000000'
	 movwf	    CCPR1L
	 
	 banksel	PIR1	   		; Rensa TMR2IF 
	 bcf	    PIR1,TMR2IF		; interrupt flagga i PIR1 reg 
	 
	 banksel	T2CON			; Sätta Timer2 prescaler
	 movlw		b'00000111'
	 movwf	    T2CON		    ; Starta Timer2
	 
Loop:
	 banksel	PIR1				
	 btfss	    PIR1,TMR2IF		; Vänta för Timer2 overflow
	 banksel	TRISC	            
	 clrf	    TRISC			; Starta PWM 
	
     banksel	PORTA	
	 btfss		PORTA,2			; Knapp NER RA2 intryckt?
	 goto		FadeDown		; JA
	 btfsc		PORTA,3			; Knapp UP RA3 intryckt?
	 goto 		Loop			; JA
	 			
FadeUp:	 
	 call       Delay_fade		; Vänta i 0.008 sec 
	 banksel	CCPR1L
	 incfsz		CCPR1L,f		; Höja PWM duty cycle.
	 goto	    Loop			; Om max go till Max 
	 call		Max			

FadeDown:
	 call       Delay_fade			
	 banksel	CCPR1L
	 decfsz		CCPR1L,f		; Sänka PWM duty cycle. 
	 goto 		Loop			; Om noll go till Min
	 call		Min

Max:
     banksel	CCPR1L			; Sätta PWM duty cycle till max
	 movlw	    b'11111111'
	 movwf	    CCPR1L
	 banksel	PORTA				
	 btfsc		PORTA,2			; Lås max läge
	 goto		Max				; och vänta för NER knappen
	 goto 		Loop
	
Min:
     banksel	CCPR1L			; Sätta PWM duty cycle till minimum
	 movlw	    b'00000000'
	 movwf	    CCPR1L
	 banksel	PORTA			; Lås min läge 
	 btfsc		PORTA,3			; och vänta för UP knappen
	 goto		Min	
	 goto		Loop

Delay_fade:						; Mjuk övergång i ljusstyrka
			
	movlw	0x3E
	movwf	d1
	movlw	0x07
	movwf	d2
Delay_fade_0:
	decfsz	d1, f
	goto	$+2
	decfsz	d2, f
	goto	Delay_fade_0
	goto	$+1
	nop
	return

	end
SmourF
Inlägg: 268
Blev medlem: 13 april 2004, 15:33:58
Ort: Jönköping
Kontakt:

Inlägg av SmourF »

hej, kul med byggare! ;)

mitt första blick och "försöka att förstå" av ditt program gick åt skogen :)

kan du inte försöka få ordning på koden, dvs göra en INIT stycke, och lite subrutiner/macron här och där, så att man kan få ett bra sätt att titta på koden,

jag brukar göra så iaf att jag försöker dölja det mesta i MAIN, dvs vad PICen ska göra, i ditt fall de här två knapparna (upp/down) ska finnas i MAIN, sen vad de gör, brukar jag ha som subrutin/macro, ovanför main,

du har ju rätt bra upplägg, det ska du inte tro :), men lite inramringar etc, du kör väldigt "kronologiskt" kan man kanske säga :), så gjorde jag också i början :)

det är iaf så som jag brukar göra när det är assembler :), när man kommer upp i 200rader kod, blir det oläsligt annars tycker jag,

men RA3, kör du inversen på den ?, och du verkar inte kolla ifall knapparna har blivit "släppt" en 0a

jag är dock ingen expert på PWM tyvärr, har inte hunnit leka med det än :)

MVH SmourF
Användarvisningsbild
Swech
EF Sponsor
Inlägg: 4750
Blev medlem: 6 november 2006, 21:43:35
Ort: Munkedal, Sverige (Sweden)
Kontakt:

Inlägg av Swech »

ser ett fel som du inte märkt men som kommer att ge dig
stora problem längre fram

Du använder dig av call men avslutar inte den anropade
rutinen med Return . Istället hoppar du tillbaks till huvudloopen
Detta gör att din stack bryter ihop.

Du MÅSTE avsluta en Call med Return. annars blir det tokigt.


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

Inlägg av vfr »

Snälla Smourf, använd punkt och stor bokstav så blir inlägget mycket lättare att läsa. :wink:

Att använda "call" utan "return" är absolut inget bra för stacken.
sodjan
EF Sponsor
Inlägg: 43251
Blev medlem: 10 maj 2005, 16:29:20
Ort: Söderköping

Inlägg av sodjan »

> fick jag fram ett stycke kod som fungerar, kan man säga.

Vad betyder "kan man säga" ??
Fungerar det eller fungerar det inte ?

> _PWRTE_OFF

Finns väldigt sällan någon anledning att inte ha Power On Timer "på".
Men å andra sidan så gör det ingen större skada att ha den
avslagen heller.

> _MCLRE_OFF

Om du inte har ett absolut behov av att ha MCLR pinnen som en
extra I/O pinne, så är det oftast enklare att använda den som MCLR
pinne som vanligt.

Och självklart ska du avsluta "Min" och "Max" med "return" och inte "goto Loop"...

(Håller även med om att SmourF kan behöva läsa på lite om svenska skrivregler, i alla fall grunderna...)
F00F
Inlägg: 3
Blev medlem: 9 november 2008, 20:19:28
Ort: Karlskrona

Inlägg av F00F »

Hej igen.
Jag har provat att göra koden mer läsbart och strukturerad, men
jag tycker att jag har inte tillräckligt med erfarenhet eller kunskap just nu. Jo sodjan, jag använder MCLRE som ingång för att den är
kopplad till en knapp på den lilla utvecklingskort som medföljde Pickit2. Den har jag flyttat till annan pin, eftersom nu använder jag en sån
"bread board" med hål till kopplingar.
De call som saknade return har jag tagit bort, och använder mig av goto istället.
Som jag uppfattar return hoppar till nästa instruktion
efter call, och jag behöver egentligen hopp till annat ställe i programmet. Är det rätt att använda sig av goto i sådana fall?
Nu har jag problem med skrivning och läsning av eepromet.
För att förenkla lite, ska jag förklara kort vad är menning med detta lilla projektet.
Det handlar om belysning till instrumentpanel i bilen.
Med två knappar kan man höja, respektive sänka ljusstyrkan.
Programmet fungerar som jag vill.
Sen efter kom jag på att det skulle bli jätte bra om den "minns" sista inställningen av ljusstyrkan efter omstart dvs. nästa gång man startar bilen.
Jag tänkte att det skulle kanske gå bra att spara aktuellt värde av CCPR1L i eepromet. Den märkliga är att detta fungerar ibland (känns som slumpmässig), annars är oftast full lyse efter spänningsättning.
Om någon har nån förslag är jag mycket tacksam.
Detta projektet är bara till min utbildningssyfte men vem vet :).
Koden är flyttat till PIC 12F683 eftersom 16F690 känns som overkill.

Kod: Markera allt

#include <p12F683.inc>
	__config (_INTRC_OSC_NOCLKOUT & _WDT_OFF & _PWRTE_ON & _MCLRE_ON & _CP_OFF & _IESO_OFF & _FCMEN_OFF & _BOD_OFF & _CPD_OFF)

	errorlevel -302
	cblock 0x20
	d1
	d2                  
	endc
      
Init:  	
	banksel		CMCON0			
	movlw		b'00000111'	; Stäng av komparatorn
	movwf		CMCON0
	banksel		ANSEL
	clrf		ANSEL		; Alla utgångar digitala
	banksel		TRISIO              
	movlw		b'00010110'	; Stäng av PWM utgång och...
	movwf		TRISIO		; ...sätta ingångar
 				
	banksel		PR2			;PWM period 
	movlw		b'11111111'
	movwf		PR2 
	 
	banksel		CCP1CON		; Konfigurera CCP modul för PWM
	movlw		b'00011100'
	movwf		CCP1CON
	call		Eeprom_read	; Läs värdet till CCPR1L från eeprom
	
	banksel		PIR1		; Rensa TMR2IF interrupt flagga i PIR1 reg 
	bcf			PIR1,TMR2IF
	 
	banksel		T2CON		; Sätt Timer2 prescaler
	movlw		b'00000111'
	movwf		T2CON		; Starta Timer2
	 
Loop:
	banksel		PIR1				
	btfss		PIR1,TMR2IF	; Vänta för Timer2 overflow
	banksel		TRISIO	            
	bcf			TRISIO,2	; Starta PWM
	
	call		Eeprom_write; Skriv	CCPR1L värdet till EEPROM	
	
	banksel		GPIO	
	btfss		GPIO,1		; Knapp Ner RA2 intryckt?
	goto		FadeDown	; Ja.
	btfsc		GPIO,4		; Knapp Upp RA3 intryckt?
	goto 		Loop		; Ja
	 			
FadeUp:	 
	call		Delay_fade	; Vänta i 0.01 sekunder
	banksel		CCPR1L
	incfsz		CCPR1L,f	; Höj PWM duty cycle. Om maximalt call Max
	goto		Loop
	goto		Max			

FadeDown:
	call		Delay_fade		
	banksel		CCPR1L
	decfsz		CCPR1L,f	; Sänk PWM duty cycle. Om noll call Min
	goto		Loop
	goto		Min

Max:
	banksel		CCPR1L		; PWM duty cycle 100%
	movlw		b'11111111'
	movwf		CCPR1L
	banksel		GPIO				
	btfsc		GPIO,1		; Lås max läge och vänta för knapp Ner
	goto		Max
	goto		Loop
	
Min:
	banksel		CCPR1L		; PWM duty cycle 0%
	movlw		b'00000000'
	movwf		CCPR1L
	banksel		GPIO		; Lås min läge och vänta för knapp Up
	btfsc		GPIO,4
	goto		Min	
	goto		Loop

Delay_fade:
	movlw		0x3E
	movwf		d1
	movlw		0x07
	movwf		d2
Delay_fade_0:
	decfsz		d1, f
	goto		$+2
	decfsz		d2, f
	goto		Delay_fade_0
	goto		$+1
	nop
	return
	
Eeprom_read:	 
	banksel		EEADR 				
	movlw		0x00		;Adress i EEPROM som ska läsas
	movwf		EEADR 				
	bsf			EECON1,RD	;Läs EEPROM 
	movf		EEDAT,W 	;Flytta data till W
	banksel		CCPR1L		; Sätta PWM duty cycle läs...
	movwf		CCPR1L		; ...värdet från EEPROM
	return

Eeprom_write:
	banksel		EEADR 
	movlw		0x00		; Adress till EEPROM skrivning
	movwf		EEADR 
	banksel		CCPR1L
	movf		CCPR1L,W	; Flytta värdet CCPR1L till W
	banksel		EEDAT	
	movwf		EEDAT		; Flytta W till EEDAT för skrivning 
	banksel		EECON1 
	bsf			EECON1,WREN
	movlw		0x55		; Lås upp skrivning till EEPROM
	movwf		EECON2 
	movlw		0xAA 
	movwf		EECON2
	bsf			EECON1,WR	; Skriv
	return
end
Senast redigerad av F00F 22 november 2008, 17:19:07, redigerad totalt 1 gång.
Användarvisningsbild
vfr
EF Sponsor
Inlägg: 3515
Blev medlem: 31 mars 2005, 17:55:45
Ort: Kungsbacka

Inlägg av vfr »

Som jag uppfattar return hoppar till nästa instruktion efter call, och jag behöver egentligen hopp till annat ställe i programmet.

Ja, det är ett sätt att se det, men inte helt korrekt. Vid "call" så anropar processorn en subrutin och pushar programräknaren. Vid return så poppas programräknaren och exekveringen fortsätter där den var tidigare. Det är så du ska se på det. Inte bara som hopp hit och dit. Nu ser ju koden lite korrektare ut iallafall med avseende på call och goto.
sodjan
EF Sponsor
Inlägg: 43251
Blev medlem: 10 maj 2005, 16:29:20
Ort: Söderköping

Inlägg av sodjan »

> De call som saknade return har jag tagit bort, och använder mig av goto istället.

Som jag minns det så borde du ha lagt till return och behållit call, skulle
inte koden ha blivit tydligare på så sätt ?

Generellt känns det som om du "hoppar runt" lite planlöst. Ofta är
det bättre och renare att använda call/return och sedan bara ha *en*
väg tillbaka (till loop i ditt fall). Nu har du "goto loop" på fem olika
ställen och jag ser rätt, och det blir onödigt svårt att följa koden.
T.ex så istället för att göra "goto min" och min avslutas med "goto loop"
så är det snyggare med "call min" och min avslutas med "return" och sedan
en "goto loop" direkt efter "call min", om du hänger med.

Generellt bör du lägga ner mycket mer jobb på att strukturera och dela in
koden i logiska block, något i stil med :

Kod: Markera allt

#include <p12F683.inc>
	__config (_INTRC_OSC_NOCLKOUT & _WDT_OFF & _PWRTE_ON & _MCLRE_ON & _CP_OFF & _IESO_OFF & _FCMEN_OFF & _BOD_OFF & _CPD_OFF)

	errorlevel -302
	cblock 0x20
	d1
	d2                  
	endc
      
;****************************************************
; Här hamnar vi vid reset eller power on !
;****************************************************
        call init
	 
;****************************************************
; Main loop !
;****************************************************

Loop:
	banksel		PIR1				
	btfss		PIR1,TMR2IF	; Vänta för Timer2 overflow
	banksel		TRISIO	            
	bcf			TRISIO,2	; Starta PWM
	
	call		Eeprom_write; Skriv	CCPR1L värdet till EEPROM	
	
	banksel		GPIO	
	btfss		GPIO,1		; Knapp Ner RA2 intryckt?
	goto		FadeDown	; Ja.
        
	btfsc		GPIO,4		; Knapp Upp RA3 intryckt?
	goto 		end_of_Loop		; Ja
	 			

FadeUp:	 
	call		Delay_fade	; Vänta i 0.01 sekunder
	banksel		CCPR1L
	incfsz		CCPR1L,f	; Höj PWM duty cycle. Om maximalt call Max
	goto		end_of_Loop
	call		Max			
        goto          end_of_Loop

FadeDown:
	call		Delay_fade		
	banksel		CCPR1L
	decfsz		CCPR1L,f	; Sänk PWM duty cycle. Om noll call Min
	goto		end_of_Loop
        call           Min

end_of_loop:

        goto          Loop

;****************************************************
; Subrutiner
;****************************************************

Max:
	banksel		CCPR1L		; PWM duty cycle 100%
	movlw		b'11111111'
	movwf		CCPR1L
	banksel		GPIO				
	btfsc		GPIO,1		; Lås max läge och vänta för knapp Ner
	goto		Max
        return
	
Min:
	banksel		CCPR1L		; PWM duty cycle 0%
	movlw		b'00000000'
	movwf		CCPR1L
	banksel		GPIO		; Lås min läge och vänta för knapp Up
	btfsc		GPIO,4
	goto		Min	
	return

;****************************************************
; Olika delay rutiner
;****************************************************

Delay_fade:
	movlw		0x3E
	movwf		d1
	movlw		0x07
	movwf		d2
Delay_fade_0:
	decfsz		d1, f
	goto		$+2
	decfsz		d2, f
	goto		Delay_fade_0
	goto		$+1
	nop
	return
	
;****************************************************
; EEPROM rutiner.
;****************************************************

Eeprom_read:	 
	banksel		EEADR 				
	movlw		0x00		;Adress i EEPROM som ska läsas
	movwf		EEADR 				
	bsf			EECON1,RD	;Läs EEPROM 
	movf		EEDAT,W 	;Flytta data till W
	banksel		CCPR1L		; Sätta PWM duty cycle läs...
	movwf		CCPR1L		; ...värdet från EEPROM
	return

Eeprom_write:
	banksel		EEADR 
	movlw		0x00		; Adress till EEPROM skrivning
	movwf		EEADR 
	banksel		CCPR1L
	movf		CCPR1L,W	; Flytta värdet CCPR1L till W
	banksel		EEDAT	
	movwf		EEDAT		; Flytta W till EEDAT för skrivning 
	banksel		EECON1 
	bsf			EECON1,WREN
	movlw		0x55		; Lås upp skrivning till EEPROM
	movwf		EECON2 
	movlw		0xAA 
	movwf		EECON2
	bsf			EECON1,WR	; Skriv
	return

;****************************************************
; Init
;****************************************************

Init:  	
	banksel		CMCON0			
	movlw		b'00000111'	; Stäng av komparatorn
	movwf		CMCON0
	banksel		ANSEL
	clrf		ANSEL		; Alla utgångar digitala
	banksel		TRISIO              
	movlw		b'00010110'	; Stäng av PWM utgång och...
	movwf		TRISIO		; ...sätta ingångar
 				
	banksel		PR2			;PWM period 
	movlw		b'11111111'
	movwf		PR2 
	 
	banksel		CCP1CON		; Konfigurera CCP modul för PWM
	movlw		b'00011100'
	movwf		CCP1CON
	call		Eeprom_read	; Läs värdet till CCPR1L från eeprom
	
	banksel		PIR1		; Rensa TMR2IF interrupt flagga i PIR1 reg 
	bcf			PIR1,TMR2IF
	 
	banksel		T2CON		; Sätt Timer2 prescaler
	movlw		b'00000111'
	movwf		T2CON		; Starta Timer2

        return

;****************************************************

        end
Eller något liknande, du fattar nog...
Notera att de nu bara finns *1* "goto loop" !

Vid "end_of_loop" kan man lägga något som alltid ska göras
innan "goto loop", det är lite svårt om man har spritt ut en massa
"goto loop" över koden...

Sen har du ett par ställen där du "fastnar" och väntar på att en knapp
ska tryckas, t.ex i "Min" och "Max". Inte bra ! Det är bättre att markera
med någon flagga vad som gäller och sedan gå tillbaka till Loop direkt.

Jag ser sedan att du skriver till EEPROM så snart en knapp har tryckts.
Bättra att ha en time-out så att du kan trycka lite fram och tillbaka
och sedan (efter att det har varit stabilt i säg 10 sekunder) spara värdet.
Det blir onödigt mycket skrivningar till EEPROM som det är nu.
F00F
Inlägg: 3
Blev medlem: 9 november 2008, 20:19:28
Ort: Karlskrona

Inlägg av F00F »

Tack jätte mycket för era synpunkter och råd. Nu har jag en bra hemläxa att jobba med. :)
Jag ska inte förtsätta att bygga på, tills jag har fixat koden och lärt mig
ordentlig hur man gör på rätt sett. Sen tänkte jag bygga vidare, t.ex varför
inte ha automatik som känner av hur ljust är det och anpassar lyset. Misstänker att det blir lite analoga insignaler osv. Men tills dess, tack en gång till.
Skriv svar