Dubbel blinka diod med Pic
Dubbel blinka diod med Pic
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!
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!
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 ?
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 ?
(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...
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...
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
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
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...
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...
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:
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
Senast redigerad av tommy_o 3 september 2006, 11:36:22, redigerad totalt 1 gång.
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.
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)
Kod: Markera allt
255 binärt är 11111111
256 binärt är 100000000
257 binärt är 100000001
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...
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
- bengt-re
- EF Sponsor
- Inlägg: 4829
- Blev medlem: 4 april 2005, 16:18:59
- Skype: bengt-re
- Ort: Söder om söder
- Kontakt:
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.
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.
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
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
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:
Oftast gör jag såklart en färdig subrutin som man sedan kallar med ett parameter som klarar resten:
Och ja, det finns i ASM till PIC också men lite jobb ska väl återstå 
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;
Kod: Markera allt
void Delay(unsigned int Value)
{
Delay_Counter = Value;
while(Delay_Counter);
}
