Behöver nybörjarhjälp att programmera i C

C, C++, Pascal, Assembly, Raspberry, Java, Matlab, Python, BASIC, SQL, PHP, etc.
Användarvisningsbild
Magnus_K
EF Sponsor
Inlägg: 5854
Blev medlem: 4 januari 2010, 17:53:25
Ort: Skogen mellan Uppsala-Gävle

Behöver nybörjarhjälp att programmera i C

Inlägg 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:
  • - MicroC Pro for PIC
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;

     }

     
}
Användarvisningsbild
adent
Inlägg: 4103
Blev medlem: 27 november 2008, 22:56:23
Ort: Utanför Jönköping
Kontakt:

Re: Behöver nybörjarhjälp att programmera i C

Inlägg 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
Användarvisningsbild
Magnus_K
EF Sponsor
Inlägg: 5854
Blev medlem: 4 januari 2010, 17:53:25
Ort: Skogen mellan Uppsala-Gävle

Re: Behöver nybörjarhjälp att programmera i C

Inlägg 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.
Användarvisningsbild
Magnus_K
EF Sponsor
Inlägg: 5854
Blev medlem: 4 januari 2010, 17:53:25
Ort: Skogen mellan Uppsala-Gävle

Re: Behöver nybörjarhjälp att programmera i C

Inlägg 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
Användarvisningsbild
Icecap
Inlägg: 26148
Blev medlem: 10 januari 2005, 14:52:15
Ort: Aabenraa, Danmark

Re: Behöver nybörjarhjälp att programmera i C

Inlägg 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.
Användarvisningsbild
sodjan
EF Sponsor
Inlägg: 43178
Blev medlem: 10 maj 2005, 16:29:20
Ort: Söderköping
Kontakt:

Re: Behöver nybörjarhjälp att programmera i C

Inlägg 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.
Användarvisningsbild
Icecap
Inlägg: 26148
Blev medlem: 10 januari 2005, 14:52:15
Ort: Aabenraa, Danmark

Re: Behöver nybörjarhjälp att programmera i C

Inlägg 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.
Användarvisningsbild
Magnus_K
EF Sponsor
Inlägg: 5854
Blev medlem: 4 januari 2010, 17:53:25
Ort: Skogen mellan Uppsala-Gävle

Re: Behöver nybörjarhjälp att programmera i C

Inlägg 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.
Användarvisningsbild
Icecap
Inlägg: 26148
Blev medlem: 10 januari 2005, 14:52:15
Ort: Aabenraa, Danmark

Re: Behöver nybörjarhjälp att programmera i C

Inlägg 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.
Användarvisningsbild
sodjan
EF Sponsor
Inlägg: 43178
Blev medlem: 10 maj 2005, 16:29:20
Ort: Söderköping
Kontakt:

Re: Behöver nybörjarhjälp att programmera i C

Inlägg 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.
Användarvisningsbild
Icecap
Inlägg: 26148
Blev medlem: 10 januari 2005, 14:52:15
Ort: Aabenraa, Danmark

Re: Behöver nybörjarhjälp att programmera i C

Inlägg 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.
Användarvisningsbild
Magnus_K
EF Sponsor
Inlägg: 5854
Blev medlem: 4 januari 2010, 17:53:25
Ort: Skogen mellan Uppsala-Gävle

Re: Behöver nybörjarhjälp att programmera i C

Inlägg 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.
Användarvisningsbild
Icecap
Inlägg: 26148
Blev medlem: 10 januari 2005, 14:52:15
Ort: Aabenraa, Danmark

Re: Behöver nybörjarhjälp att programmera i C

Inlägg 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.
Användarvisningsbild
sodjan
EF Sponsor
Inlägg: 43178
Blev medlem: 10 maj 2005, 16:29:20
Ort: Söderköping
Kontakt:

Re: Behöver nybörjarhjälp att programmera i C

Inlägg 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.
Användarvisningsbild
Icecap
Inlägg: 26148
Blev medlem: 10 januari 2005, 14:52:15
Ort: Aabenraa, Danmark

Re: Behöver nybörjarhjälp att programmera i C

Inlägg 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!
Skriv svar