Sida 1 av 2
Binär klocka med lysdioder
Postat: 15 november 2008, 04:36:20
av rubicus
Hej!
Det här är mitt allra första elektronikprojekt, och jag kanske bör tillägga att jag när jag började inte kunde någonting om elektronik, och definitivt ingenting om mikrokontrollers, men det är "learning by doing" som gäller.
Projektet gick ut på att helt från grunden skapa en klocka som binärt visar tiden med hjälp av lysdioder, och idag står projektet (nästan) klart. Jag använder mig av en PIC16F870 för att kontrollera hur mina LEDs ska lysa, och två knappar för att ställa in tiden (en för timmar och en för minuter/sekunder). Anledningen till att jag skriver nästan klart är att den inte verkar gå helt stabilt, och många omstarter kan behövas innan den börjar gå i början. Men när den väl börjar gå så brukar den gå ganska länge. Som bäst förvrängdes tiden med någon minut eller två på en månad, så även då går den lite ojämnt.
Till bygget:
Jag kom fram till att det gick att driva lysdioderna direkt från utgångarna på min μC, vilket förenklar konstruktionen betydligt, men eftersom pinne RA4 på μCn är open drain fick jag montera lysdioden baklänges där. Jag skapade således ett kopplingsschema i Eagle som kan ses på bilden nedan. Det övre högra hörnet är tillägnat knapparna som ska styra tidsinställningen. Där har jag använt en kondensator tillsammans med ett motstånd för att "avstudsa" knapparna, och sedan anslutit till en NOT-krets för att få schmitt trigger-funktionalitet då detta inte finns i roboten. (Den mystiskt formade saken i väntsra kanten är min egenskapade symbol för batterieliminatorskontakt

)
För att få tiden att hållas så exakt som möjligt har jag använt mig av en 10ppm kristall på 4MHz och ett knep som jag läste här på elektronikforumet. Det handlade om att man varje gång timer 1 utlöses lägger på ett värde, med en massa decmaler, och när heltalsvärdet passerar över en gräns nollställs det. På så vis blir inte alla sekunder exakt lika långa, men man slipper tidsförvrägning och kan justera med så hög upplösning som några sekunder per år genom att ändra värden i programmet. Mer om detta kan man läsa
här.
Genom att placera "bitarna"(lysdioderna) på rätt platser i PICens portar kan jag enkelt överföra det binära värdet till lysdioderna (bara att kopiera värdet från tidsvariabeln till portregistret). Det var en hel del läsande i datablad och andra sidor för att till slut kunna förstå hur assembler fungerar men till slut tror jag att jag fick kläm på det hela! Programkoden lägger jag i ett separat inlägg efter det här då den tar upp mycket plats. (ramen är skapad av MPLAB).
Jag monterade ihop det hela på ett breadboard, och efter en hel del felsökning, med bland annat obunden MCLR etc. fungerade det faktiskt! Den visade tiden. Tyvärr hade jag en del problem med att den i starten inte alltid vill starta programmet från början. Ibland startar den alla röda LED (förutom RA4) tända, ibland med enbart RA4 tänd, ibland utan några, ibland startar den och går rätt ett tag för att sedan stanna upp. När den stannat fungerar det heller inte att trycka på knapparna. Men ibland så fortsätter den som den ska. Är det någon som har någon fundering på varför det blir så? Här är lite bilder från denna fas:
Efter några månaders tester kunde jag konstatera att processorn heller inte verkar gå helt stabilt, den tappar inte lika mycket mellan två tidsperioder, så en justering av programkoden räcker inte där. Vad kan nu detta bero på?
Hur som helst tyckte jag att det var dags att sätta ihop det hela på ett kretskort där det sitter fast. Eftersom jag inte orkar sätta mig in i att etsa egna bestämde jag mig för att experimentkort. Sagt och gjort, så var det hela monterat, men fortfarande finns samma instabilitetsproblem. Jag känner själv att jag har kört in i en återvändsgränd, men hoppas att det finns någon vänlig själ som skulle kunna ge en hint om vad det kan handla om. Här är också några bilder på den färdiga klockan:
Hoppas att det har varit trevlig läsning!
källkod
Postat: 15 november 2008, 04:48:52
av rubicus
För er som är intresserade så postar jag här även källkoden till mitt program. Koden kanske kan tyckas överdokumenterad, men jag tycker att det är viktigt att ha koll på vad man gör, särskilt i assembler
Kod: Markera allt
;**********************************************************************
; *
; If interrupts are not used all code presented between the *
; code section "INT_VECTOR and code section "MAIN" can be removed. *
; In addition the variable assignments for 'w_temp' and *
; 'status_temp' can be removed. *
; *
; If interrupts are used, the 16F870.lkr *
; file will need to be modified as follows: Remove the lines *
; CODEPAGE NAME=vectors START=0x0 END=0x4 PROTECTED *
; and *
; SECTION NAME=STARTUP ROM=vectors *
; and change the start address of the page0 section from 0x5 to 0x0 *
; *
; *
; Refer to the MPASM User's Guide for additional information on *
; features of the assembler and linker (Document DS33014). *
; *
; Refer to the respective PICmicro data sheet for additional *
; information on the instruction set. *
; *
;**********************************************************************
; *
; Filename: klocka.asm *
; Date: 2008-01-05 *
; File Version: 1,4 *
; *
; Author: Oskar Södergren *
; Company: Rosendalsgymnasiet *
; *
; *
;**********************************************************************
; *
; Files required: 16f870.lkr *
; *
; *
; *
;**********************************************************************
; *
; Notes: *
; *
; *
; *
; *
;**********************************************************************
;****************************
; *
; Konfigurationsbitarna *
; *
;****************************
list p=16f870 ; list directive to define processor
#include <p16f870.inc> ; processor specific variable definitions
__CONFIG _CP_OFF & _WDT_OFF & _BODEN_OFF & _PWRTE_ON & _XT_OSC & _WRT_ENABLE_ON & _LVP_OFF & _CPD_OFF
; '__CONFIG' directive is used to embed configuration data within .asm file.
; The labels following the directive are located in the respective .inc file.
; See respective data sheet for additional information on configuration word.
;****************************
; *
; Variabler *
; *
;****************************
cblock 0x20
w_temp ; används för att spara arbetsdata vid ett inerupt
status_temp ; används för att spara arbetsdata vid ett inerupt
pb_temp ; används för att spara läget på PORTB vid interupt
varSek ; används för att spara antal sekunder
varMin ; används för att spara antal minuter
varTim ; används för att spara antal timmar
kHel ;Frekvensen för interupt, heltalet
kUnd1 ;första decimalerna för frekvensen
kUnd2 ;resten av decimalerna för frekvensen
kUnd3 ;resten av decimalerna för frekvensen
raknHel ;räknare för timing
raknUnd1 ;räknare för timing
raknUnd2 ;räknare för timing
raknUnd3 ;räknare för timing
raknSU1 ;startupdelay
raknSU2 ;startupdelay
endc
;****************************
; *
; Reset-vektorn *
; *
;****************************
RESET_VECTOR CODE 0x000 ; processor reset vector
movlw high start ; load upper byte of 'start' label
movwf PCLATH ; initialize PCLATH
goto start ; gå till huvudkoden
;************************************
; *
; Interuptvektorn *
; *
;************************************
INT_VECTOR CODE 0x004 ; interrupt vector location
;******************************************
;Interupt-vektorn, del 1: lagra gamla data*
;******************************************
movwf w_temp ; save off current W register contents
movf STATUS,w ; move status register into W register
movwf status_temp ; save off contents of STATUS register
;**********************************************
;Interupt-vektorn, del 2: leta typ av interupt*
;**********************************************
movf PORTB, w ;Se till att värde inte hinner ändras
movwf pb_temp ;...
btfsc PIR1, TMR1IF ;Timerinterupt?
goto timerIE ;Hantera då det!
btfss INTCON, 0 ;Nej, är det PORTB-interupt då?
goto restore ;Nähä, det var något annat konstigt, gå tillbaka
btfsc pb_temp, 6 ;Ja! Det var port B. Var det RB6?
goto inc_tim ;Svarta knappen, öka timmar
btfsc pb_temp, 7 ;var det RB7 då?
goto pbIE7 ;Röda knappen, öka minuter och nollställ sekunder...
bcf INTCON, 0 ;Nej det var inget som trycktes ned, bara upp,
goto restore ;vi rensar interuptflaggan, först bara innan vi går tillbaka..
;*********************************************
;Interupt-vektorn, del 3: behandla interuptet*
;*********************************************
pbIE7
clrf varSek ;nollställ sekunderna
goto inc_min ;öka minuterna
timerIE ;Hantera timer
decfsz raknHel ;minska heltalet, gå vidare vid 0
goto uppdatera ;om inte 0 återvänd
;Nu har en sekund gått, uppdatera räknaren och öka antal
sekunder
movf kUnd3, w ;Plocka fram minsta undervärdet
addwf raknUnd3, f ;lägg till det på räknaren
movf kUnd2, w ;Plocka fram nästa undervärde...
btfsc STATUS, C ;fick vi overflow på raknUnd3?
incf kUnd2, w ;Ja! öka rakn2 med ytterligare ett steg
addwf raknUnd2, f ;Öka raknUnd2 med rätt antal steg
movf kUnd1, w ;Plocka fram nästa undervärde...
btfsc STATUS, C ;fick vi overflow på raknUnd2?
incf kUnd1, w ;Ja! öka rakn1 med ytterligare ett steg
addwf raknUnd1, f ;Öka raknUnd1 med rätt antal steg
movf kHel, w ;förbered heltalsvärdet
btfsc STATUS, C ;blev det overflow på raknUnd1?
incf kHel, w ;ja! heltalet skall vara ett steg större denna gång
addwf raknHel, f ;öka nu heltalet rätt också...
;gå nu vidare till att öka tiden
;Interupt-vektorn, del 3,5: öka tiden
;************************************
inc_sek
incf varSek ;öka sekundräkningen
movlw .60 ;Testa om vi nátt 60
xorwf varSek, w ;Dá far vi 0 och z-flaggan sätts i STATUS
btfss STATUS, Z ;Har den gjort det?
goto uppdatera ;Naä, i sa fall är vi fardiga med okningen
clrf varSek ;Ja! Nollställ sekunderna...
inc_min
incf varMin ;och öka minuträkningen...
movlw .60 ;Testa om vi nátt 60
xorwf varMin, w ;Dá far vi 0 och z-flaggan sätts i STATUS
btfss STATUS, Z ;Har vi nátt 60?
goto uppdatera ;Nää, nu àr vi klara med förändringar
clrf varMin ;Ja! Vi nollställer minuterna...
inc_tim
incf varTim ;Precis! och dá máste det ha gátt ännu en timme!
movlw .24 ;Har det gátt 24 timmar?
xorwf varTim, w ;...
btfsc STATUS, Z ;Kör då nästa instruktion, annars hoppa över
clrf varTim ;Ja! Nollställ
uppdatera
movf varSek, w ;Nu anvands det antal sek som ligger I varSek
xorlw b'00010000' ;sen spegelvander vi RA4, dá den är open drain
movwf PORTA ;Sen flyttas det modifierade värdet till PORTA
movf varMin, w ;Nu flyttar vi minutvärdet till PORTC
movwf PORTC ;...
movf varTim, w ;Och sá timmarna
movwf PORTB ;Till PORTB
bcf INTCON, 0 ;Rensa interuptflaggan för RBIE
bcf PIR1, TMR1IF ;rensa interuptflaggan för TIMR1
;**********************************************
;Interupt-vektorn, del 4: återställ gamla data*
;**********************************************
restore
movf status_temp,w ; retrieve copy of STATUS register
movwf STATUS ; restore pre-isr STATUS register contents
swapf w_temp,f
swapf w_temp,w ; restore pre-isr W register contents
retfie ; return from interrupt
;************************
; *
; Huvudkoden *
; *
;************************
MAIN CODE
start
;Del 0,5: Låt allting hinna jämna ut sig
;****************************************
delay
decfsz raknSU1
goto delay
decfsz raknSU2
goto delay
;Del 1: Initiering
;********************
bsf STATUS, RP0 ;byt till bank 1
movlw 0xCF ;Ställ in RB-pullup
movwf OPTION_REG ;...
movlw 0x7 ;Sätt alla pinnar digitala
movwf ADCON1 ;...
movlw 0xC8 ;Aktivera interupt, peripheral interupts och RB-interupt
movwf INTCON ;...
bsf PIE1, TMR1IE ;fixa interupt för timer också
clrf TRISA ;Sätt alla portar i PORTA till output
clrf TRISC ;Sätt alla i PORTC till output
movlw 0xC0 ;Sätt 0-5 till output och 6-7 till input på PORTB
movwf TRISB ;...
bcf STATUS, RP0 ;byt tillbaka till bank 0
movlw .15 ;fixa konstanterna för tiden, 15 till
movwf kHel ;hela konstanten
movwf raknHel ;hela räknaren
movlw .66 ;66 till
movwf kUnd1 ;första underkonstanten
movwf raknUnd1 ;första underräknaren
movlw .186 ;186 till
movwf kUnd2 ;andra underkonstanten
movwf raknUnd2 ;andra underräknaren
movlw .165 ;165 till
movwf kUnd3 ;tredje underkonstanten
movwf raknUnd3 ;tredje underräknaren
clrf PORTA ;nollställ utgångar
clrf PORTB ;...
clrf PORTC ;...
clrf varSek ;och tiden
clrf varMin ;...
clrf varTim ;...
movlw 0x1 ;Nu initierar vi slutligen timern
movwf T1CON ;och sätter på den i timerläge..
;Del 2: Huvudkod
;******************
forever
nop
nop
goto forever
END ; directive 'end of program'
Postat: 15 november 2008, 05:29:25
av peter555
Jag tvivlar starkt på att det där är en 10 ppm kristall. snarare 50 ppm om du kör utan trimning. Ska du ha god noggrannhet är det bättre att använda nätfrekvensen som referens.
Postat: 15 november 2008, 08:54:50
av Icecap
Avstutsningen av knappar utför man såklart i mjukvaran, då sparar man ju en krets (i detta fall).
I mjukvaran finns det ett potentiellt problem eller rättare en sak som ville vara bra att göra generellt:
cblock 0x20
w_temp ; används för att spara arbetsdata vid ett inerupt
status_temp ; används för att spara arbetsdata vid ett inerupt
pb_temp ; används för att spara läget på PORTB vid interupt
Hade du istället använd adress 0x70 hade dessa varit tillgängliga oavsett vilken bank som var vald. Det har nog ingen betydelse i just detta projekt men sådär generellt kan det vara en mycket bra idé.
Likaså är det förvisso korrekt men föga intuitivt med att styra bankningsbitten "manuellt", det finns ju bättre sätt:
bsf STATUS, RP0 ;byt till bank 1
kunde likaväl skrivas som:
banksel OPTION_REG
Och sedan är det ju en personlig preferens att undvika svengelska namn men det är ju lite smak & tycke. Vad jag finner direkt dumt är tomma kommentarrader, varför?
Annars ser det faktisk bra ut.
Postat: 15 november 2008, 13:45:30
av JimmyAndersson
Rubicus:
Snyggt! Så ska en projekttråd se ut.

Bra text, man ser allt på bilderna och så är koden väldigt lätt att följa med i.
Det här är mitt allra första elektronikprojekt, och jag kanske bör tillägga att jag när jag började inte kunde någonting om elektronik, och definitivt ingenting om mikrokontrollers, men det är "learning by doing" som gäller.
Du verkar ha väldigt lätt för att lära.

Postat: 15 november 2008, 13:52:51
av rubicus
peter555:
Det ska vara en 10ppm-kristall. Det är den som har prodnr 74-501-90 på elfa. 4,0MHz, stabilitet 10ppm och tolerans 20ppm. Jag måste säga att ja inte har stenkoll på vad siffrorna betyder, men 10ppms stabilitet tolkar jag som att tiden som min klocka visar inte ska variera med mer än 10 miljondelar av den tid som faktiskt gått. Hur hade du änkt dig att man skulle kunna använda sig av nätspänningen? Just nu använder jag en 5V nätadapter så där får jag ju likspänning, och jag gillar inte tanken på att ha 230V på kretskortet då ingen skyddskåpa finns

Jag har tänkt tanken att försöka stoppa in DCF77 i den, men måste erkänna att jag inte har en aning om hur radiovågar tas emot etc. om någon har en fin guide för detta så mottar jag gjärna en länk
Icecap:
Jag vill återigen säga att det är mitt första elektronikprojekt någonsin och att jag tidigare bara lött ihop små färdiga byggsatser från elfa. Anledningen till att jag valde hårdvaruavstudsing är bland annat för att jag var smått osäker på hur bra mjukvaruavstudsningen fungerar när jag använder mig av interupt för att känna av knapptrycken, dessutom kostade kretsen bara ett fåtal kronor. Tack för tipset am att läga variablerna efter 0x70 förresten

.
När du skriver tomma kommentarrader antar jag att du syftar på de rader där jag har skrivit ";...". Det gjorde jag för att markera att det som sker här är en fortsättning på det jag gjorde ovanför; alla har ju sina sätt att skriva. Dessutom så har ju kommentarerna ingen inverkan på programmet eftersom de inte komplieras.
Ingen som har någon idé om varför den ofta hänger sig i uppstart och många omstarter krävs? Är det några register som behöver stå på rätt sätt, någon timer som utlöser fel eller någon pinne som bör knytas till något speciellt?
Tack för all konstruktiv kritik

Postat: 15 november 2008, 14:05:49
av squiz3r
det finns ju många nätadaptrar (såna i plast som man bara pluggar in i väggen) som ger typ 9v växelspänning. Då kan du ju likrikta den också och använda samma som strömkälla till klockan.
Edit: Gjorde det lite tydligare..
Postat: 15 november 2008, 14:46:48
av peter555
+/- 10 ppm får du bara om du trimmar frekvensen och håller den vid 25 grader C. Du bör nog även ha en separat oscillator med egen stabiliserad spänning också för att klara kraven.
Postat: 15 november 2008, 19:05:30
av Icecap
Varför den hänger sig kan jag inte svara på MEN du gör ett "stort" fel:
;Del 0,5: Låt allting hinna jämna ut sig
;****************************************
delay
decfsz raknSU1
goto delay
decfsz raknSU2
goto delay
Gör ALDRIG så!!!!
Detta gör du innan alla portpinnar är ställda, all hårdvara initierat och det är mycket fel!
När PIC'en väl kör programmet ÄR den igång och allt är klart! Vill du ge extern elektronik chans att "falla på plats" kan du göra det EFTER att µC'n är initierat.
När en µC startas upp gäller det att få allting stabilt kring den snabbast möjligt, vill du vänta på att evt. knapptryckningar som kan komma av tidkonstanter... initiera allt och vänta till knapparna "släpps", så enkelt är det.
Annars gillar jag skarpt att du använder interrupt och räknar ner som du gör, det är ett tydligt program med beskrivning av delfunktioner osv.
Postat: 16 november 2008, 15:21:24
av rubicus
Icecap:
Tack för tipset! Jag hade fått för mig att det var en bra grej att göra och tror att jag la till den i samband med att jag glömt att binda MCLR (eller helt enkelt inte visste att man så borde göra). När jag nu tänker efter ska den ju ha en inbyggd startuptimer också...

Jag ska pröva att ta bort den biten, och funderar sedan på att strukturera om initieringsbiten så att den gör saker i en lämpligare ordning (kanske väntar med att aktivera interupt och timers till slutet). Det är inga såna typiska stabilitetsgrejjer i koden som bör ligga där, som jag har missat? Typ att vissa register bör ha vissa värden under uppstarten, och att de i stället får ett slumpmässigt värde i starten. Förhoppningsvis kan jag öka sannolikheten för en lyckad start då iaf.
Är det en bra eller dålig idé att aktivera WDT?
peter555:
25 grader bör den hålla, jag befinner mig inomhus, och jag tror inte den utvecklar mycket värme. Hur menar du att den ska trimmas? Handlar det om att ha ekakt rätt kapacitans parallellkopplad med den, eller är det något man måste göra i en separat aparat? Jag försökte goggla på det men såg bara att det förekommer, kunde ingenstans läsa hur. Jag har tillgång till oscilloskop om det skulle vara så att det behövs. Och är det verkligen stabiliteten man trimmar eller är det hur exakt den går? Att den går exakt 4 MHz är nämligen ganska ointressant för mig, timing kan jag ändra i koden, det som är intressant är att den alltid går lika snabbt. De oscilatorerde har på elfa har i stället en frekvensstabilitet på 100ppm, vilket skulle kunna ge så mycket förvrägning som 50 minuter på ett år. Det finns visserligen jättestabila saker, som skulle ge runt 30s på ett år, men de kostar väldigt mycket också.
Skulle det vara någon mening med att koppla in en zenerdiod till ingången för att få en stabilare spänning? som det ser ut nu så levererar adaptern runt 5.05V och en zenerdiod på 5V borde kunna göra spänningen mycket stabilare? Skulle det i så fall hjälpa till med stabiliteten? Att använda nätspänning verkar fortfarande krångligt eftersom jag inte vill arbeta på 50 Hz, och ska jag försöka göra om den signalen till en bra oscilatorfrekvens känns det mer värt att lägga den energin på att göra radioklocka av den.
Ingen som sitter på en bra länk om var man kan läsa om att ta emot radiosignaler? Fick sitta och lyssna på långvågsradio på den lokala radioklubben häromdagen och är helt såld på det här med radiosignaler just nu, hur de studsar på atmosfären och så vidare. Vore lite kul att se hur det fungerar ihop med elektronik också. På fysiken får man lära sig hur de beter sig och hur man räknar på dem, men aldrig hur de uppkommer (bortsett från inom atomfysik) och hur man tar tillvara på dem. Dessutom skulle det kanske bana vägen för att någon gång kunna bygga en DCF77-mottagare. Problemet just nu är bara att alla texter om det på internet förutsätter att man redan har en färdig mottagare. Jag ser ingen poäng med att göra något om jag inte förstår vad som händer bakom kulisserna.
Återigen: Tack för all hjälp

Postat: 16 november 2008, 15:55:00
av Icecap
WDT behöver du inte! Helt enkelt för att OM den utlöser kommer klockan i alla fall att vara fel tid.
WDT använder man i applikationer som är säkerhetskritiska och en klocka kan knappast vara så.
På den pelletsbrännare jag har gjort mjukvaran till används dock WDT, just för att en hängning i mjukvaran kan leda till omfattande skador (matar in pellets och hänger sig, efter ett tag sker mekanisk stopp pga. fullmatning, glöder finns, gengas bildas och en explosion sker. Inte teoretisk risk men faktisk händelse flera ställen!)
Och en sak: i databladet står det att registerna står i ett visst läge efter reset... men lita ALDRIG på det! Ska ett register stå i ett visst läge SKA man sätta det till det läge oavsett vad "default" är!!! Leta igenom forumet här och du inser varför.
När jag gör kod har jag några rutiner som alltid kommer i den ordning:
Initialize_Hardware(); // Set ports and µC-hardware
Initialize_Variables(); // Secure known values
Enable_Interrupt(); // Enable all interrupts
// Härefter är följden inte kritisk
Initialize_Timers();
Initialize_UART0();
Initialize_UART1();
Initialize_PWM();
....
På detta vis sker det aldrig att mjukvaran går åt skogen.
DCF77: jag har också funderingar åt det hållet men efter mycket kollande har jag kommit fram till att det är enklast att köpa en färdig mottagare, helt enkelt för att storleken blir bäst då. Conrad.de har sånt senast jag kollade. Orsaken är att det är en mycket låg frekvens som "kräver" ferrit-antenn, jag har lyckats linda en spole själv som passar skapligt (verkar ha resonans runt rätt frekvens med lämplig kondensator) men då BL på mitt oscilloskop ligger på ca: 78kHz är det lite svårt att mäta med det

Postat: 16 november 2008, 16:15:09
av tecno
Icecap
ärdet denna DCF modulen du menar eller finns det någon annan som jag inte hittar på Conrad?
http://www.conrad.se/?article=641871
Postat: 16 november 2008, 17:03:06
av peter555
OK, om du justerar i koden så behöver naturligtvis inte absolutfrekvensen vara exakt den nominella. Iofs så kanske det blir svårt att kompensera för udda frekvenser helt ut.
Jag tvivlar dock på att den inbyggda oscillatorn är tillräckligt bra för att klara långtidsstabiliteten. Ska du ha en bra oscillator bör du bygga den separat. Det finns en anledning til att noggranna oscillatorer är dyra.
Postat: 16 november 2008, 18:25:50
av Icecap
tecno: jag skrev conrad.
DE
SKITLÅNG URL
Artikel-Nr.: 641138 - 62
Postat: 16 november 2008, 19:33:23
av Norpan