Sida 1 av 2

Dubbel blinka diod med Pic

Postat: 3 september 2006, 00:06:44
av tommy_o
Någon som hjälpa till med kod till att få en diod att blinka med
intervallerna

10ms tänd,
200ms släkt,
10ms tänd,
500ms släkt

och sen börjar den om från början.

Koden är i assembler
kretsen är PIC16F628A

Tack!

Postat: 3 september 2006, 00:27:14
av sodjan
Gå till http://www.piclist.com/techref/piclist/ ... /delay.htm och sakapa
tre subrutiner med 10, 200 resp 500 ms delay.

Sedan blir det bara 4 calls till delay rutinerna mellan tänd/släck av dioden.

Förutsatt att processorn inte ska göra något annat också...

Jag utgår från att du har grundläggande kunskaper om PIC programmering,
annars får du fixa det.

> Koden är i assembler

Finns det redan en kod ?

Postat: 3 september 2006, 00:34:35
av tommy_o
Lust och hjälpa med ett exempel?

har inte så stor koll ännu, men håller på å försöka lära mig
men går lite trögt för tillfället.

Postat: 3 september 2006, 00:53:09
av sodjan
> har inte så stor koll ännu,...

Då kanske du ska börja med att blinka *en* LED.
Varför röra till det i onödan i början ? :-)

Vad är det som är oklart ?

Postat: 3 september 2006, 01:01:28
av tommy_o
det har jag redan gjort, och försöker nu gå vidare med att blinka den lite mer avancerat!


Det som är oklart är att jag inte kommer vidare utan att någon har lust att ge ett exempel så man kan kika i koden och försöka lära sig något.

Postat: 3 september 2006, 01:15:41
av sodjan
(Först, det var ju inte två lysdioder du ville blinka, utan en i två
olika intervall, ursäkta missen...)

OK, så då har du redan en kod för en LED, fint !

Utgå från den och komplettera så att du får de nya intervall
som du vill ha. Exakt *hur* detta ska göras beror naturligtsvis
helt på hur den befintliga koden ser ut !

Om du skulle få en helt ny kod nu, så skulle det antagligen
bara rör till det, det finns 100-tals olika sätt att få en lysdiod
att blinka... :-)

Ett sätt att göra det är att ta din befintliga kod och komplettera
med delay rutiner genererade från länken du fick.

Ett annat sätt är att sätta upp ett timer-interrupt till 10 ms, och sedan
använda det som tidbas för de andra tiderna.

> Det som är oklart är att jag inte kommer vidare

Varför inte ? Vad är problemt ?

Som sagt, så länge det inte finns någon kod att kommentera, så
är det lite svårt att ha några synpunkter...

Postat: 3 september 2006, 01:26:30
av tommy_o
koden jag utgår ifrån är följande:

LIST P=16F628, R=DEC ; Use the PIC16F628 and decimal system

#include "P16F628.INC"

__config _INTRC_OSC_NOCLKOUT & _LVP_OFF & _WDT_OFF & _PWRTE_ON & _BODEN_ON

CBLOCK 0x20 ; Declare variable addresses starting at 0x20
Loop1,Loop2
ENDC


ORG 0x000

CLRF PORTA ; Initialize port A

BSF STATUS,RP0 ; RAM bank 1

CLRF TRISA ; All pins port A output


BCF STATUS,RP0 ; RAM bank 0


MOVLW 7
MOVWF CMCON


Main BSF PORTA,1 ; Turn on LED connected to RA1
CALL delay
BCF PORTA,1 ; Turn off LED connected to RA1
CALL delay
GOTO Main

delay MOVLW 250
MOVWF Loop1
Outer MOVLW 200
MOVWF Loop2
Inner NOP
NOP
DECFSZ Loop2,F
GOTO Inner ; Inner loop = 5 usec.
DECFSZ Loop1,F
GOTO Outer
RETURN

END

Postat: 3 september 2006, 01:41:03
av sodjan
OK.
Fixa ett par till delay rutiner och lägg till anrop till dom
mellan "main" och "goto main". I princip...
delay_10, delay_200 och delay_500 t.ex.
Det är inte den mest kompakta/snyggaste lösningen, men den
som är enklast just nu.

Sen är väl koden "snyggare" formatterad i verkligheten, det blev nog
så här p.g.a att du inte använde code-taggarna...

Se också till att det står 628 *A* överallt...

Postat: 3 september 2006, 02:09:37
av tommy_o
Funkar nästan som jag vill nu!
Men förstår inte sambanden mellan MOVLW tiderna, för tiden som den blinkar de två första blinken är perfekt, men den långa (500ms) släkta är för kort så vill öka denna, till t.ex 1000ms, men hur jag än ändar sifforna så blir det inte som jag vill.

koden nu:

Kod: Markera allt

   
     LIST P=16F628A, R=DEC    ; Use the PIC16F628A and decimal system 

        #include "P16F628A.INC"  ; Include header file 

        __config  _INTRC_OSC_NOCLKOUT & _LVP_OFF & _WDT_OFF & _PWRTE_ON & _BODEN_ON 

        CBLOCK 0x20             ; Declare variable addresses starting at 0x20
          Loop1,Loop2,Loop3,Loop4,Loop5,Loop6 
        ENDC 


        ORG    0x000           
 
        CLRF   PORTA           ; Initialize port A 
 
        BSF    STATUS,RP0      ; RAM bank 1 
 
        CLRF   TRISA           ; All pins port A output 
       

        BCF    STATUS,RP0      ; RAM bank 0 

 
        MOVLW    7 
        MOVWF    CMCON        


Main    BSF     PORTA,1			; Turn on LED connected to RA1 
    	CALL 	delay_10
		BCF     PORTA,1			; Turn off LED connected to RA1
    	CALL	delay_200
		BSF     PORTA,1			; Turn on LED connected to RA1 
    	CALL 	delay_10
		BCF     PORTA,1			; Turn off LED connected to RA1
		CALL 	delay_500            
		GOTO    Main 

delay_200	MOVLW   250		
      	 	MOVWF   Loop1 
Outer   	MOVLW   200
        	MOVWF   Loop2 
Inner   	NOP 
        	NOP 
        	DECFSZ  Loop2,F 
        	GOTO    Inner         
        	DECFSZ  Loop1,F 
        	GOTO    Outer 
        RETURN 

delay_10   	MOVLW   800
        	MOVWF   Loop3 
Outer_10  	MOVLW   600
        	MOVWF   Loop4 
Inner_10   	NOP 
        	NOP 
        	DECFSZ  Loop4,F 
        	GOTO    Inner_10          
        	DECFSZ  Loop3,F 
        	GOTO    Outer_10 
        RETURN 


delay_500   MOVLW   500
        	MOVWF   Loop5 
Outer_500  	MOVLW   200
        	MOVWF   Loop6 
Inner_500  	NOP 
        	NOP 
        	DECFSZ  Loop6,F 
        	GOTO    Inner_10         
        	DECFSZ  Loop5,F 
        	GOTO    Outer_10 
        RETURN 


        END

Postat: 3 september 2006, 03:15:46
av bearing
Det får inte plats större tal än 255 i ett register. Det beror på att det är 8-bitars register i processorn. 8 bitar går att sätta i 256 olika kombinationer vilka representerar talen 0 tom 255.

Kod: Markera allt

255 binärt är	 11111111
256 binärt är	100000000
257 binärt är	100000001
Om du försöker lägga talet 257 i registret får inte den vänstra ettan plats med resultat att 00000001 sparas i registret vilket motsvarar 1 decimalt. På motsvarande sätt sparas 800 som 32 (800 - 3*256 = 32)

Postat: 3 september 2006, 12:50:05
av sodjan
Perfekt !
Nu har vi något att utgå från... :-)

Precis som bearing noterat, så är frågan hur du tänker att
t.ex värdet 500 (dec) ska "få plats" i ett 8-bitars register !?

Om inte de delay-loopar du har nu "räcker till", så får du utöka
dom med en tredje nivå. D.v.s tre st räknare och tre st loopar.

Sedan behöver du inte ha separata räknare (Loop1, Loop2 o.s.v),
delay-rutinerna körs ju aldrig samtidigt och de kan dela räknare.

Kör gärna kodgeneratorn som jag länkade till tidigare så får du
lite förslag på delay-rutiner. Prova med lite olika tider, så ser du att
den kommer att använda 2 eller 3 variabler beroende på hur lång
fördröjningen ska vara. Om vi antar att du kör i 20 Mhz så ser det ut
så här för 10, 200, 500 resp 1000 ms. Notera att 10 ms använder 2
räknare och de längre tiderna använder 3 räknare. Du får komplettera
med en label till varje rutin och en RETURN så att det blir en subrutin...

Kod: Markera allt

;********************************************
; Delay = 0.01 seconds
; Clock frequency = 20 MHz

; Actual delay = 0.01 seconds = 50000 cycles
; Error = 0 %

	cblock
	d1
	d2
	endc

			;49998 cycles
	movlw	0x0F
	movwf	d1
	movlw	0x28
	movwf	d2
Delay_0
	decfsz	d1, f
	goto	$+2
	decfsz	d2, f
	goto	Delay_0

			;2 cycles
	goto	$+1

;********************************************
; Delay = 0.2 seconds
; Clock frequency = 20 MHz

; Actual delay = 0.2 seconds = 1000000 cycles
; Error = 0 %

	cblock
	d1
	d2
	d3
	endc

			;999997 cycles
	movlw	0x08
	movwf	d1
	movlw	0x2F
	movwf	d2
	movlw	0x03
	movwf	d3
Delay_0
	decfsz	d1, f
	goto	$+2
	decfsz	d2, f
	goto	$+2
	decfsz	d3, f
	goto	Delay_0

			;3 cycles
	goto	$+1
	nop

;********************************************
; Delay = 0.5 seconds
; Clock frequency = 20 MHz

; Actual delay = 0.5 seconds = 2500000 cycles
; Error = 0 %

	cblock
	d1
	d2
	d3
	endc

			;2499999 cycles
	movlw	0x16
	movwf	d1
	movlw	0x74
	movwf	d2
	movlw	0x06
	movwf	d3
Delay_0
	decfsz	d1, f
	goto	$+2
	decfsz	d2, f
	goto	$+2
	decfsz	d3, f
	goto	Delay_0

			;1 cycle
	nop

;********************************************
; Delay = 1 seconds
; Clock frequency = 20 MHz

; Actual delay = 1 seconds = 5000000 cycles
; Error = 0 %

	cblock
	d1
	d2
	d3
	endc

			;5000000 cycles
	movlw	0x2D
	movwf	d1
	movlw	0xE7
	movwf	d2
	movlw	0x0B
	movwf	d3
Delay_0
	decfsz	d1, f
	goto	$+2
	decfsz	d2, f
	goto	$+2
	decfsz	d3, f
	goto	Delay_0

Postat: 3 september 2006, 13:25:28
av bengt-re
Man kan också förlänga tiden för inner loopen så att man med ett 8 bitars register kan styra längden på delayen innom de intervall man önskar. Att sätta en label på ett RET ´kommando ger dig längre delay än en NOP för samma mängd kod. Ser stökigt ut, men kan spara några rader kod om man börjar köra slut på minne.

Annars tycker jag att det är dax att börja lära sig att använda timers nu, detta går visserligen att lösa utan timers, men det är bra kunskap och erfarenhet att ha när applikationerna börjar bli mer komplexa.

Postat: 3 september 2006, 14:09:49
av sodjan
> Annars tycker jag att det är dax att börja lära sig att använda timers nu,

Helt rätt !
Bara att plocka fram databladet och läsa på... :-)

Postat: 3 september 2006, 23:09:52
av BMI
Ett fiffigt sätt är som jag lärt mig är att skapa en fast looptid för programmet. Då gäller det att man inte fastnar någonstans för att det skall fungera .dvs programmet måste löpas igenom snabbare än loop tiden

Först måste man naturligtvis bestäma sig för en rimligt looptid för att allt skall funka "hur ofta måste nått hända som snabbast"
men exempelvis en looptid på 1000Hz 1mS
först laddar man prescalern med önskat värde enligt formel EX
8Mhz osc

CLKOUT 2Mhz
------------------------ ------------------ = Loop .31
PRESCALER * LOOP 64 * 1000

Program "loop"

0 Starta klockan,,,, nu går timern ,går alltid mer eller mindre
1 nop
2 kolla om klocka gått ut ,,,ja ,starta om klockan goto 4
3 kolla om klocka gått ut ,,,nej goto 1 forsätt vänta "pause"

Här körs prg var 1ms,, då är det enkelt att hålla kollen på ms
4 inc x
5 inc y
6 if x = 5 .................5ms
7 led1 on ,x=0
8 if y = 10................10ms
9 led1 off, y =0
10 goto 2

Här e pause coden
MOVF RTCC,W ; Hämta pause räknaren
XORLW LOOP ; Pause klar ?
BTFSS STATUS,Z ; Ja, hoppa över !
GOTO PAUSE ; Nej, fortsätt vänta !
CLRF RTCC ; Nolla pause räknaren
CLRWDT ; Watchdog

Kanske inte tillförde något av värde,för den som vet bättre

Postat: 4 september 2006, 08:01:00
av Icecap
När jag kör delay som inte är speciellt tidskritiska använder jag oftast en timer som ger interrupt med 10Hz. På timerinterrupten skrivar jag:
if(Delay_Counter) Delay_Counter--;
(På svenska: om Delay_Counter är större än noll ska den räknas ner med 1)
Som Delay_Counter anger jag då en variabel som klarar av den högsta tiden som jag kan få behov av, oftast en unsigned int, alltså 16 bit.

I main-loopen kan jag då göra lite olika beroende på vad som ska ske men i detta fall ville det bli något liknande:

Kod: Markera allt

Loop:
  Tänd LED;
  Delay_Counter = tänd-tiden;
  while(Delay_Counter); // vänta på att Delay_Counter blir räknat ner till noll
  Släck LED;
  Delay_Counter = Väntatid1;
  while(Delay_Counter); // Samma som förut
  ... // Slå på och av i vilket mönster som helst
  goto Loop;
Oftast gör jag såklart en färdig subrutin som man sedan kallar med ett parameter som klarar resten:

Kod: Markera allt

void Delay(unsigned int Value)
  {
  Delay_Counter = Value;
  while(Delay_Counter);
  }
Och ja, det finns i ASM till PIC också men lite jobb ska väl återstå ;-)