Introduktion til C (Læst 5850x)

Offline pacman

  • Højpas filter
  • *****
  • Indlæg: 311
  • Antal brugbare Indlæg: 8
  • Jens Bauer (Forsøgs-person)
    • Vis profil
Introduktion til C
« Dato: August 17, 2011, 16:45:37 »
Ethvert C-program starter med funktionen 'main'. På en microcontroller som AVR, vil det typisk se sådan ud for et 'tomt' program, der gør 'ingenting':

Kode:
int main()
{
    /* call program-initialization from here */

    while(1)    /* do this forever */
    {
        /* this is your main-loop; you might want to put some code here */
    }
    return(0);  /* (never reached) */
}

første ting du ser, er 'int'. Dette er en variabel-type. Vi har en funktion vi kalder 'main', denne vil returnere en værdi. Derfor er vi nødt til at fortælle hvilken type værdi den skal returnere.
En int er lidt et vidt begreb. På en microcontroller som AVR vil den nok typisk være 16 bit bred (2 bytes).
Inde i vores funktion har vi et til keyword (nøgleord), der hedder 'while'.
While er en C-kommando og tager et enkelt udtryk. Så længe dette udtryk er sandt, vil de efterfølgende kommandoer (dem der således er inde i næste krølle-parantes blok) blive udført.
Sidst ses keyword'et 'return'. Dette er også en C-kommando. 'return' tager også et enkelt udtryk som argument, og dette udtryk bliver returneret (givet til) den kode som har kaldt rutinen (i dette tilfælde 'main').

Vi kan prøve at gøre en smule mere...
Kode:
#include <avr/io.h>
#include <util/delay.h>

void initLED()
{
    DDRA = 0xff;    /* set all pins to become output pins */
    PORTA = 0x00;   /* turn off all pins (eg. set all pins low) */
}

int main()
{
    initLED();

    while(1)    /* do this forever */
    {
        PORTA ^= (1 << PA0);
        _delay_ms(250);
        _delay_ms(250);
    }
    return(0);  /* (never reached) */
}

Vi har nu en hel masse mere.
#include indlæser en 'header-fil', dvs. en fil, der indeholder definitioner og forskellige værdier, program-stumper, funktions-prototyper, mm.
Det er ganske rart at man har include-kommandoen. Dette er ikke direkte en C-kommando, men det er noget pre-processoren håndterer. En C-compiler består nemlig af flere dele. En pre-processor køres først, og gør et stykke arbejde på din sourcekode, og denne kode som pre-processoren så har lavet, videregives til den rigtige C-compiler. Når C-compileren har lavet sin binære fil (en 'objekt-fil'), skal linkeren overtage. Linkeren kan klistre flere binære filer sammen. Dette er praktisk, for så kan man have en masse klargjorte binære filer, så man ikke skal compile en hel masse hver eneste gang.

#include kan bruges på to forskellige slags filer.
1: #include <systemheader.h>
2: #include "userheader.h"

En 'system-header' er en headerfil, som er 'installeret' sammen med din compiler.
En 'userheader' er en headerfil, som du selv har lavet, og den ligger sammen med din source-kode (i samme directory/mappe).

OK, næste ting, er at vi definerer vores egen rutine, den kalder vi 'initLED'.
Da den ikke returnerer nogen værdi, skriver vi keywordet 'void' foran. Void betyder stort set 'ingenting', man kan også sige 'smid væk'.
I vores initLED rutine sætter vi først DDRA til at have alle bits sat. DDRA er defineret i den include-fil der hedder <avr/io.h>.
DDRA er en forkortelse, og står for 'Data Direction Register A'.
PORTA er din microcontroller's første port (hvis den altså er implementeret; for enkelte microcontrollere har ingen PORTA, men måske en PORTB eller PORTC).
PORTA bruges på 2 måder...
Hvis du har sat et port-ben til at være input, kan du sætte bitten i PORTA til 1, for at koble pull-up modstanden til. Sættes bitten til 0, kobles pull-up modstanden fra.
Hvis du har sat portbenet til at være output, og sætter portbenets bit til 1, vil dette portben sende fx. 5V ud, hvis din forsyning til microcontrolleren er 5V.
Er portbenets bit sat til 0, vil portbenet's output være 0V (GND).

Nu kommer vi ned til main...
der kalder vi rutinen 'initLED'. Da denne rutine ingen parametre (argumenter) tager, lader vi parantesen med argumenter være tom.
    initLED();
-Linien skal afsluttes med semikolon. Dette er C's måde at opdele kommandoer/instruktioner.
Du kan således godt have flere kommandoer på samme linie, når de bare opdeles med semikolon.
Mellemrum, tabuleringer, Return og Linefeed karakterer behandles helt ens; dvs. alle behandles på samme måde som mellemrum.

Hvis nu initLED havde returneret en værdi, kunne vi have skrevet...

  int8_t myvar;

  myvar = initLED();
...så kunne vi lave udregninger på denne værdi som initLED returnerede. Men da vi har valgt at initLED ikke skal returnere en værdi, kan vi kun kalde rutinen på den måde, som koden ovenover viser.

Inde i while-løkken har vi følgende linie:

PORTA ^= (1 << PA0);

Den kan også skrives således:

PORTA = PORTA ^ (1 << PA0);

Dette kræver lidt forklaring...
Vi sætter PORTA lig med den gamle værdi af PORTA XOR (1 bitskiftet til venstre PA0 gange).
^ (hat) betyder XOR, eller Exclusive OR.
XOR ligner PLUS lidt i funktion, men XOR er en direkte binær funktion.
Et binært tal består af 0'er og 1'er, fx.
%11001001

XOR'er man 2 binære tal sammen, er resultatet af de bits der er 1 i begge tal = 0.
Er den ene bit 1 og den anden bit 0, bliver resultatet = 1.
Eks:
Kode:
    %1100
XOR %1001
    -----
    %0101

OK, nu er vi ovre XOR-delen. Vi har stadig bitskiftning.
PA0 er egentlig et konstant tal, som er defineret i avr/io.h
-Værdien for PA0 er sådan set 0, så vores linie vil se sådan ud inde bag ved:

PORTA = PORTA ^ (1 << 0);
Dette bliver regnet ud af compileren og forkortet til:
PORTA = PORTA ^ 1;

-Så alt i alt, hvad der sker, er at den laveste bit i PORTA, dvs. bitten for PA0, bliver ændret fra 0 til 1, hvis den i forvejen er 0, eller fra 1 til 0, hvis den i forvejen er 1.

Næste 2 linier ser således ud:
_delay_ms(250);

_delay_ms er en funktion der er defineret i filen util/delay.h
Den har brug for at man har defineret F_CPU til den clock-frekvens som microcontrolleren kører. Ofte vil denne clock-frekvens være 8MHz, hvis man kører med den interne oscillator. (Personligt kører jeg næsten altid med 20MHz). Indstillingen af F_CPU foregår i den fil, der hedder Makefile (hvis du bruger WinAVR eller anden  compiler-distribution med gcc).

 

Offline Danni-Hansen

  • µProcessoren
  • *
  • Indlæg: 544
  • Antal brugbare Indlæg: 17
    • Vis profil
Sv: Introduktion til C
« Svar #1 Dato: September 03, 2011, 21:16:50 »
Hej Pacman.

Hvordan gør man det her på linux mon? Jeg bruger ubuntu, og ville compile c filen med gcc -o test test.c i min terminal.

Men, den skal bruge de der header-filer du snakker om.

What to do?

Tak
Mvh. Danni Hansen.

 

Offline pacman

  • Højpas filter
  • *****
  • Indlæg: 311
  • Antal brugbare Indlæg: 8
  • Jens Bauer (Forsøgs-person)
    • Vis profil
Sv: Introduktion til C
« Svar #2 Dato: September 03, 2011, 23:47:15 »
Hvordan gør man det her på linux mon? Jeg bruger ubuntu, og ville compile c filen med gcc -o test test.c i min terminal.

Linux ? -Så er du heldig. Jeg sidder selv med Mac OS X, som er UNIX, så det ligner hinanden vældig godt på dette punkt. :)

Citér
Men, den skal bruge de der header-filer du snakker om.

What to do?

Jo, hvis du bare skal lave en 'hånd-kompilering', så kan du bruge noget a'la...

Kode:
avr-gcc -Wall -Os -DF_CPU=1000000 -mmcu=attiny44  -o test.elf test.o

Skal du have flere C-filer compilet, så kan du lægge navnene på .o filerne bagefter test.o:

Kode:
avr-gcc -Wall -Os -DF_CPU=1000000 -mmcu=attiny44  -o test.elf test.o sound.o potmeter.o

-Men hvis du vil have noget mere 'holdbart' i længden, bør du nok have en Makefile, som holder styr på diverse filer/dependencies. Til at starte med vil jeg dog anbefale at prøve om ovenstående virker, da en Makefile kan være lidt overvældende at lave selv. (Personligt snyder jeg; jeg har en Makefile template, som jeg kopierer til alle mine projekter, og derefter ændrer jeg den hist og her).

 

Offline Danni-Hansen

  • µProcessoren
  • *
  • Indlæg: 544
  • Antal brugbare Indlæg: 17
    • Vis profil
Sv: Introduktion til C
« Svar #3 Dato: September 04, 2011, 12:37:43 »
hmmm, nu vil den så bare ikke "afspille"/køre filen... den siger flg:
-bash: ./ardotest: cannot execute binary file

efter jeg compilede den med:
avr-gcc -Wall -Os -DF_CPU=1000000 -mmcu=attiny44  -o ardotest ardotest.c
Mvh. Danni Hansen.

 

Offline pacman

  • Højpas filter
  • *****
  • Indlæg: 311
  • Antal brugbare Indlæg: 8
  • Jens Bauer (Forsøgs-person)
    • Vis profil
Sv: Introduktion til C
« Svar #4 Dato: September 04, 2011, 12:59:31 »
hmmm, nu vil den så bare ikke "afspille"/køre filen... den siger flg:
-bash: ./ardotest: cannot execute binary file

efter jeg compilede den med:
avr-gcc -Wall -Os -DF_CPU=1000000 -mmcu=attiny44  -o ardotest ardotest.c

Nixen. For den skal køres på en microcontroller. :)
-Desuden bør den hedde .elf til efternavn...

Når den er compilet, bør den lige konverteres til noget som avrdude kan bruge...
Kode:
avr-objcopy -j .text -j .data -O ihex ardotest.elf ardotest.hex

For at uploade den til din microcontroller, skal du nok bruge avrdude.
Kode:
avrdude -c avrispmkii -P usb -p attiny44 -U flash:w:ardotest.hex:i

-Alt ovenstående går ud fra at du bruger en attiny44.
Hvis ikke dette er tilfældet, kan du skifte 'attiny44' ud med fx. 'atmega16' eller hvilken type AVR du nu bruger.

Hvorfor kan filen ikke køres på linux?
1: (Det simple svar) Linux har ingen LED.
2: Linux kender ikke til AVR header-filerne
3: Din Linux kører på en 32-bit eller 64-bit x86 arkitektur, programmet er designet til en 8-bit AVR arkitektur.

Dvs. når programmet oversættes til binære koder, har disse binære koder vidt forskellig betydning alt efter hvilken CPU de kører på.
Det er som at sætte en italiener der ikke kan dansk, til at læse Gyldendals Store Kogebog. Han kender bogstaverne, men kører fast efter at have prøvet at stave sig igennem de første få ord.

Det, der sker når man 'bygger' et program, er følgende:
1: Program-koden (kilde-teksten/source code) skrives og gemmes i en fil
2: Program-koden kompileres (af compileren) til en binær fil, der kaldes en 'objekt-fil'
3: Objekt-filen klistres sammen med andre objekt-filer til et helt program, dette klarer linkeren.
4: Er programmet compilet til fx. Linux på en Linux-kommando-linie kan det nu køres, ellers fortsæt til punkt 5...
5: Det binære program (her avr-objcopy) konverteres/omdannes eventuelt til et midlertidigt format, som passer til det værktøj der skal bruges efterfølgende...
6: Et værktøj (her avrdude) sender filen til den CPU/microcontroller, som skal udføre/køre koden.
7: (Når avrdude er færdig med at sende programmet, startes programmet straks, og kører indtil der ikke er strøm til microcontrolleren længere. Når der sættes strøm på igen, vil programmet startes forfra, altså som ved RESET.)

 

Offline Danni-Hansen

  • µProcessoren
  • *
  • Indlæg: 544
  • Antal brugbare Indlæg: 17
    • Vis profil
Sv: Introduktion til C
« Svar #5 Dato: September 06, 2011, 21:38:02 »
Hej igen Pacman.

Hmm, okey, jeg havde åbenbart misforstået pointen med denne type programmering, da jeg troede koden skulle køres på linux, også sende/modtage signalerne fra chippen... :-/

Nå, men, nu har jeg da noget at lege med alligevel.

Tak endnu engang ;)
Mvh. Danni Hansen.

 

Offline pacman

  • Højpas filter
  • *****
  • Indlæg: 311
  • Antal brugbare Indlæg: 8
  • Jens Bauer (Forsøgs-person)
    • Vis profil
Sv: Introduktion til C
« Svar #6 Dato: September 06, 2011, 22:31:05 »
Hmm, okey, jeg havde åbenbart misforstået pointen med denne type programmering, da jeg troede koden skulle køres på linux, også sende/modtage signalerne fra chippen... :-/

Chippen skal programmeres af dig, så du får ingen signaler, med mindre du selv har lavet programmet der skal generere signalerne. :)

Jeg mener obdev.at har noget kode der kommunikerer med en AVR over USB-porten.
Jeg tror nok eksemplet ligger i PowerSwitch på deres V-USB side.

(Bemærk: Du kan ikke lave en USB-programmerings-enhed uden at have en programmerings-enhed i forvejen, da du skal have en programmerings-enhed for at programmere chippen i gør-det-selv programmerings-enheden). -Nu er det ord så også brugt op. ;)

 

Offline Danni-Hansen

  • µProcessoren
  • *
  • Indlæg: 544
  • Antal brugbare Indlæg: 17
    • Vis profil
Sv: Introduktion til C
« Svar #7 Dato: September 08, 2011, 01:27:46 »
Hej Pacman.

Hehe, den forstod jeg altså ikke meget af. Desværre.
Mvh. Danni Hansen.