Sida 1 av 9
Behöver nybörjarhjälp att programmera i C
Postat: 1 december 2014, 01:21:40
av Magnus_K
Risken finns att det blir en långranding tråd för jag är rätt oduglig när det kommer till att programmera så vi får se hur länge ni står ut.
Det finns exempel på nätet som utför det jag vill utföra men jag måste lära mig någon gång. Har blivit lite less på att kopiera kod och inte förstå vad som händer.
Hårdvara:
- - PIC16F1789 (datablad)
- 4-siffrors, 7-segments LED display (datablad) "Common Anode"
Mjukvara:
Mål:
- - Att ta in ett analogt värde till µC:n och presentera detta korrekt på displayen. Visningen skall kunna flytta "komma" för att visa värde högre än 9,999.
Fråga:
För det första; hur ska jag lägga upp det här?
Jag har börjat att definera vad porten heter som styr var segment för sig. Har också definerat hur många siffror som finns.
Vad bör nästa steg bli? I min skalle så ska jag på något vis få displayen att visa 0.000 default och sedan gå upp till 9.999 innan den slår om till 10.000 osv.
Det jag behöver hjälp med först och främst är hur jag ska tänka för att få lite flöde på det.
Så här långt så har jag avklarat hårdavarubiten med att koppla upp allt och skriva siffra för siffra på displayen. Displayen är multiplexad med gemensam anod så genom PORTA (1:a på respsektive utgång) så styr jag vilken siffra som ska visas och med PORTD vilka segment som ska tändas (genom att sänka respektive segmentsport).
Det är inte heller något problem att ta in det analoga värdet och placera detta i en byte stor variabel.
Den obefintliga koden som jag skrivit än så länge syns nedan. Vidare så har det nog ingen betydelse hur konfigureringen ser ut då jag hantera detta själv. Med andra ord så är det alla definitioner och huruvudprogrammet jag inte kan.
EDIT: Ser att jag skrivit fel antal element i "NUMBER" arrayen men det är fixat.
Kod: Markera allt
void init() {
OSCCON = 0b11111000; //x4 PLL and 32MHz
ANSELA = 0;
ANSELB = 0;
ANSELD = 0;
ANSELE = 0;
CM1CON0 = 0;
CM2CON0 = 0;
CM3CON0 = 0;
CM4CON0 = 0;
OPA1CON = 0;
OPA2CON = 0;
OPA3CON = 0;
TRISD = 0;
LATD = 0;
TRISA = 0;
LATA = 0;
TRISC.B3 = 0;
TRISC.B4 = 1;
}
#define NUMPORT PORTD
#define DIGITPORT PORTA
const int DIGIT[3] = {
0b00000010,
0b00000100,
0b00001000,
0b00010000
};
const int NUMBER[10] = {
0b11000000, //0
0b11111001, //1
0b10100100, //2
0b10110000, //3
0b10011001, //4
0b10010010, //5
0b10000010, //6
0b11111000, //7
0b10000000, //8
0b10010000 //9
};
void main() {
init();
while(1) {
LATA.B1 = 1;
}
}
Re: Behöver nybörjarhjälp att programmera i C
Postat: 1 december 2014, 02:11:37
av adent
Hmmm
Mina 50 öre:
Få snurr på displayen på ren lågnivå, lite som en mini-drivrutin med ett interface, kanske i form av fyra globala variabler (byte:ar) där innehållet i den första ska ut på den första siffran i displayen, innehållet i den andra på den andra siffran o.s.v. Man kan efteråt arbeta om det till att använda en array[4] eller bara två byte:ar (utnyttja att man får plats med 0-9 i en nibble (d.v.s. 1234 lagras som 0x12 och 0x34 t.ex.))
Fokusera sedan på att få ett heltal utskrivet på displayen. Om du har en byte 0-255 så ska du kunna skriva ut den siffran. Det är en god början.
Det jag ser som lite klurigt för en nybörjare är att själva utritningen av siffrorna kräver konstant jobbande av processorn, här kan man t.ex. använda ett timer-interrupt och göra utritningen där.
Sen kan man förstås ge bättre tips om hur man ska strukturera upp det och så men.
MVH: Mikael
Re: Behöver nybörjarhjälp att programmera i C
Postat: 1 december 2014, 02:25:33
av Magnus_K
Ja det där med processorbelastning har jag grunnat på.. Rimligtvis borde det bli så att när väl segmenten är ställda rätt så behöver man bara snurra runt de 4 gemensamma anoderna i ca 60-70 Hz för att det ska se bra ut. Detta med hjälp av en timer och interrupt.
I nuläget spelar det inte så stor roll om det belastar processorn mycket.
Ja du har nog rätt i att jag borde börja lätt och inte göra hela funktionen från början. Ska fokusera på att visa en byte decimalt.
Tack för vägledningen!
EDIT: Sitter och funderar på om jag kan göra följande med 16-bitars variablen (från det analoga värdet):
Om jag förstår operanden "%" (modulus) så är det hur många gånger värdet till höger går på värdet till vänster.
Jag kanske kan ta min variabel och göra först en modulus med 1 och skriva det i högra siffran (samt skala ner värdet till enbart en bit), sen en modulus med 10 på föregående resultat, i andra osv.
Komma-tecknet placerar jag sedan med en if-sats baserat på resulatet av ovan.
Kan undra hur det blir. Upp som en flaggstång eller ner som en pannkaka.
Re: Behöver nybörjarhjälp att programmera i C
Postat: 1 december 2014, 03:41:33
av Magnus_K
Nu har jag det!
Eller ja, i pseudo-kod eller vad man kallar det.
Nu gäller det bara att få det i fungerande kod också...
Tror ni att det i grovt kan fungera?
Kod: Markera allt
Om analogvärde > 9999, visa då 9999 i displayen.
analogvärde % 1000 -> skicka till vänsta siffran.
analogvärde /= 1000
analogvärde % 100 -> skicka till nästa siffra.
analogvärde /= 100
osv
Om analogvärde > 999, sätt komma längst åt vänster.
Om analogvärde > 99 & < 1000, sätt ett steg åt höger
Re: Behöver nybörjarhjälp att programmera i C
Postat: 1 december 2014, 09:13:37
av Icecap
Det finns olika sätt att omvandla ett värde till tecken. Ett sätt är att ta resten av division med 1000, 100 hhv. 10 och placera i rätt "fack".
Ett annat är att börja med sista siffran (ettor).
A: X = 0;
B: Rest = Värde % 10;
C: Siffra[X] = Rest;
D: Värde /= 10;
E: X++;
F: if(X < 4) goto B;
Siffra[0] = ettor, Siffra[3]= tusental.
Re: Behöver nybörjarhjälp att programmera i C
Postat: 1 december 2014, 09:59:53
av sodjan
> Rimligtvis borde det bli så att när väl segmenten är ställda rätt
> så behöver man bara snurra runt de 4 gemensamma anoderna i
> ca 60-70 Hz för att det ska se bra ut.
Planera gärna för ca 100 Hz för att vara på säkra sidan.
Sen så måste så klart segmenten ställas om för varje gång
för varje siffra. Lite oklart hur du menade där egentligen...
Ja, om du inte alltid ska visa samma siffra på alla 4 så klart.
En timer som ger ett interrupt med ca 100 Hz och varje gång:
- Släck nuvarnade anod.
- Lägg ut nästa segment
- Tänd nästa anod.
- Räkna fram nästa siffra.
- return from interrupt.
Detta kan göras med mindre än 1% av processorn.
Koden som skapar rätt segment kombination ligger
någon annanstans, inte i ISR'en.
Re: Behöver nybörjarhjälp att programmera i C
Postat: 1 december 2014, 10:27:28
av Icecap
Jag gjorde just en sådan scanning av ett LED-display i sin tid och jag gjorde så att jag hade 3 bytes (3 siffror) där jag helt enkelt sparade rätt bit-kombination för att tända rätt segment.
Så värden blev omvandlad till siffer-för-siffer och sedan konverterat till bit-mönster innan de läggs i display-buffern.
Timer-ISR'n scannar sedan ut till 7-segment displayen.
Re: Behöver nybörjarhjälp att programmera i C
Postat: 1 december 2014, 12:49:54
av Magnus_K
Ni får ha tack för att tagit er tid.
Svarar er med en del i taget för att försöka göra mina svar lite tydligare.
Icecap skrev:Det finns olika sätt att omvandla ett värde till tecken. Ett sätt är att ta resten av division med 1000, 100 hhv. 10 och placera i rätt "fack".Ett annat är att börja med sista siffran (ettor).
A: X = 0;
B: Rest = Värde % 10;
C: Siffra[X] = Rest;
D: Värde /= 10;
E: X++;
F: if(X < 4) goto B;
Siffra[0] = ettor, Siffra[3]= tusental.
Jag läste jag lite mer om modulus innan jag trynade igår och känner att jag nog kan hantera detta. Den första metoden du nämner är nog lättare, för mig som blåbär, att göra det på.
Är det någon väldig fördel med något av de två sätten du föreslog?
sodjan skrev:> Rimligtvis borde det bli så att när väl segmenten är ställda rätt
> så behöver man bara snurra runt de 4 gemensamma anoderna i
> ca 60-70 Hz för att det ska se bra ut.
Planera gärna för ca 100 Hz för att vara på säkra sidan.
Sen så måste så klart segmenten ställas om för varje gångför varje siffra. Lite oklart hur du menade där egentligen...Ja, om du inte alltid ska visa samma siffra på alla 4 så klart.
Kanon. Då siktar jag på 100 Hz!
Jo det jag tänkte att man kunde göra, för att kanske belasta processorn mindre, var att endast uppdatera den siffran som ändrats, och inte de oförändrade sifforna.
Å andra sidan inser jag ju nu att det var en tankevurpa, så vi glömmer det. Alla siffror måste ju ändå uppdateras hela tiden i 100 Hz och just beräkningen för att få fram vilken siffra som ska skrivas ska ju ändå göras.
sodjan skrev:En timer som ger ett interrupt med ca 100 Hz och varje gång:
- Släck nuvarnade anod.
- Lägg ut nästa segment
- Tänd nästa anod.
- Räkna fram nästa siffra.
- return from interrupt.
Detta kan göras med mindre än 1% av processorn.
Koden som skapar rätt segment kombination liggernågon annanstans, inte i ISR'en.
Ja det här blir bra! Det lilla jag skrivit hittills är att just mappa upp segmentkombinationen så jag borde kunna skriva siffran 1 nu genom att skicka ut NUMBER[1] på tex DIGIT[2].
Får se vart det slutar men nu har jag en god start.
Icecap skrev:Jag gjorde just en sådan scanning av ett LED-display i sin tid och jag gjorde så att jag hade 3 bytes (3 siffror) där jag helt enkelt sparade rätt bit-kombination för att tända rätt segment.
Så värden blev omvandlad till siffer-för-siffer och sedan konverterat till bit-mönster innan de läggs i display-buffern.
Timer-ISR'n scannar sedan ut till 7-segment displayen.
Då ska vi se om jag klarar av ungefär samma sak!
Återkommer med lite mer kod och säkert någon fråga.
Re: Behöver nybörjarhjälp att programmera i C
Postat: 1 december 2014, 13:00:21
av Icecap
Någon speciell fördel vet jag inte om man ska kalla det men det är en skalbar process. Den fungerar till 1 siffer och till 10 siffror, Det enda som ska ändras är antal genomkörningar.
Om man sedan lägger till att den avslutar rutinen när värdet blir noll kan man - vid att fylla display-buffern med släckta segmenter få en LZB (Leading Zero Blanking). Detta betyder att värdet 246 visas som " 246" istället för "0246".
Det går såklart att lösa på många sätt men jag tycker att denna är det enklaste.
Re: Behöver nybörjarhjälp att programmera i C
Postat: 1 december 2014, 13:08:06
av sodjan
> och just beräkningen för att få fram vilken siffra som ska skrivas ska ju ändå göras.
Se till att hålla sär logiken som skapar själva siffrorna från logiken
som scannar dislayerna! Det är två helt olika delar.
Scannern som köra med 100 Hz är ganska "dum i huvudet" och lägger
bara ut färdiga 7-seg mönster på de 4 displayerna hela tiden.
Logiken som önvandlar olika värden till 7-seg mönster ligger
på en annan nivå och vet ingenting om scanningen.
Re: Behöver nybörjarhjälp att programmera i C
Postat: 1 december 2014, 13:28:51
av Icecap
Kod: Markera allt
ISR org 0x004 ; Interrupt vector location
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
movf PCLATH,W
movwf pclath_temp
clrf PCLATH ; Be sure that we're home alone
banksel PIR1
Timer0_Int
btfss INTCON,T0IF ; Is it Timer0?
goto ISR_Exit ; Jump if not
bcf INTCON,T0IF ; Clear interript request flag
clrf PORTB ; Shut off LED's
movf PORTA,W ; Get PORTA into W
andlw b'11111000' ; Mask out Column-bits
movwf PORTA ; Shut off column drivers
incf Scan_Ctr,F
movf Scan_Ctr,W ; Read Scan_Ctr
sublw d'3'-1 ; Is Scan_Ctr >= 3?
btfss STATUS,C ; Skip next if not
clrf Scan_Ctr ; Else clear Scan_Ctr
movf Scan_Ctr,W ; Read Scan_Ctr
addlw ScanDigits ; Offset with ones position
movwf FSR ; Set it in index register
movf INDF,W ; Read the appropriate data
movwf PORTB ; Send out pattern on PORTB
movf Scan_Ctr,W ; Read value
addwf Scan_Ctr,W ; Make it double
addwf PCL,F ; Make an indexed jump
movlw b'00000001' ; Index #0
goto ISR_Scan01
movlw b'00000010' ; Index #1
goto ISR_Scan01
movlw b'00000100' ; Index #2
ISR_Scan01
iorwf PORTA,F
ISR_Exit
movf pclath_temp,W ; Read copy of PCLATH
movwf PCLATH ; Restore to pre-ISR content
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
Detta är (i ASM) scanningsrutinen och det kör på en PIC16F628A.
PORTA.0 slår på 1-positionen.
PORTA.1 slår på 10-positionen.
PORTA.2 slår på 100-positionen.
PORTB är segmenten
PORTB.0 är segment A.
PORTB.1 är segment B.
PORTB.2 är segment C.
PORTB.3 är segment D.
PORTB.4 är segment E.
PORTB.5 är segment F.
PORTB.6 är segment G.
PORTB.7 är DP.
Som du kanske ser gör interrupten inget annat än att:
1: Slå av alla segment.
2: Läsa PORTA för att maska lite bits så att ingen siffra slås på.
3: Skriv värdet till PORTA för att säkra att alla siffror stängs helt av.
4: Räkna upp scannings-räknaren.
5: Nolla scannings-räknaren om den är för hög.
6: Slå i tabellen för vilken siffra som ska visas.
7: Skicka ut segment-data
Klart.
En viktig sak är att steg 2-6 tar lite tid - och det behövs! Har man inte den lilla tid hinner LED'na inte att laddas ut och man får ghosting, alltså att "förra" data "smittar av" på de nuvarande. Speciellt med (långsamma-att-släcka) drivsteg mellan kan detta ge problem.
Re: Behöver nybörjarhjälp att programmera i C
Postat: 1 december 2014, 16:40:48
av Magnus_K
Har tyvärr lite bråttom men tänkte jobba vidare med det här på rasterna i natt.
En snabbfråga bara Icecap. Under punkt 4 och 5 i ditt senaste inlägg så räknar du upp scannings-räknaren eller nollar om den är för hög. Har du lust att förklara?
Min plan var att en timerflagga triggar interruptet och jag nollar Timern och flaggan vid ISR:ens start. Är detta det du menar här?
Tack på förhand och ber om ursäkt för det slarviga inlägget.
Re: Behöver nybörjarhjälp att programmera i C
Postat: 1 december 2014, 17:00:36
av Icecap
Varje interrupt visar nästa siffra i scanningen av displayen. I ditt fall rör det sig om 4 siffror varför det känns ganska dumt att visa fler än 4.
Så når den har räknat 0 - 1 - 2 - 3 och kommer till 4 ska den börja om på 0. Då du har ett binärt antal siffror kan du ganska enkelt nöja dig med att räkna upp den och direkt efter AND'a med 0x03, då får du korrekt räkning.
Re: Behöver nybörjarhjälp att programmera i C
Postat: 1 december 2014, 19:16:29
av sodjan
> och jag nollar Timern
Försök välja pre-scaler o.s.v så att timern kan köra
free-running, då behöver du aldrig bry dig om det.
Om det blir 80, 100 eller 150 Hz spelar ingen roll.
Re: Behöver nybörjarhjälp att programmera i C
Postat: 1 december 2014, 20:17:02
av Icecap
Jepp, timer-delen översåg jag. Som sodjan skriver är det alltid bäst att låta timern köra fritt om man kan hitta en inställning som passar. Och skulle den gå "för fort" har det i grunden mindre betydelse så länge du håller ISR'n tight och inte börjar göra en massa dumheter.
Ena siffran ska släckas, nästa ska väljas och den siffra ska slås på, adjö och tack!