Kom i gang med C for AVR

Etter å ha prokrastinert altfor lenge, var det idag endelig på tide å komme i gang med Embedded C for ATmega328p. Dette er valgt databrikke på Arduino UNO og derfor et trygt valg når man skal lære seg mikrokontrollerprogrammering.

ATmega328p er del av AVR-familien av mikrokontrollere som Atmel (kjøpt opp av Microchip i 2016) begynte å utvikle i 1996. Hvem som helst kan skaffe seg den. Enten ved å kjøpe løst eller i stedet skaffe seg f.eks. Arduino-startpakke. Jeg valgte Arduino-startpakke siden jeg også trengte brødbrett, testledninger, enkle komponenter, osv.

Arduino-brettet med mikrokontroller er enkelt å programmere via Arduino IDE. Dette er et neddummet programmeringsverktøy som gjør programmeringen lett. Og siden det finnes mange dokumenterte Arduino-prosjekter, med ulike vanskelighetsgrader, kan hvem som helst prøve seg. Derfor er det også et smart valg når man skal lære seg mikrokontrollerprogrammering.

Den reelle utfordringen blir å skrive ren embedded C-kode som kan erstatte jallakode skrevet i Arduino IDE. Dette krever forståelse av C, registermanipulering, osv.

Første prosjekt: Lysdiode + bryter

Som første prosjekt valgte jeg å kombinere Arduino-prosjektene Blink og How to Wire and Program a Button. Forklaringen er åpenbar; å kontrollere innsignal og utsignal på mikrokontrolleren er første viktige skritt.

Pinnene PB0 og PB1 (markert med rød understreking) er de jeg valgte å bruke. Jeg kunne også brukt mange av de andre, så lenge de støtter ønsket funksjonalitet.

Her må man lese databladet for å være sikker ….

Kretsen

Først måtte bryter og lysdiode kobles riktig på brødbrettet (easy peasy):

Når bryter ikke er innkoblet skal innsignalet (via hvit ledning) være lavt, dvs. 0V. Når bryteres trykkes ned, vil derimot signalet endre seg til 5V.

Utsignalet (rød ledning) er også 5V, men dette er for høyt for vanlige lysdioder, derav resistoren. Etter spenningsfallet over resistoren, blir gjenværende spenning over lysdioden mer passe.

Dette er samme konfigurasjon som i Arduino-prosjektene lenket til over. Forskjellen er at pinne 8 og 9 ble brukt i stedet.

Arduino-kode

Deretter gjenstod det kun litt enkel kodeskriving i Arduino IDE:

const int PIN_FOR_BUTTON = 8;
const int PIN_FOR_LED = 9;
const int LONG_DELAY = 5000;
const int SHORT_DELAY = 10;

void setup() {
   pinMode(PIN_FOR_BUTTON, INPUT);
   pinMode(PIN_FOR_LED, OUTPUT);
}

void loop() {
   if (digitalRead(PIN_FOR_BUTTON) == HIGH) {
      digitalWrite(PIN_FOR_LED, HIGH);
      delay(LONG_DELAY);
   } else {
      digitalWrite(PIN_FOR_LED, LOW);
      delay(SHORT_DELAY);
   }
}

Valgte pinner deklareres øverst, sammen med ventetidene. I setup() settes inn- og utmodus. Og i loop() sjekker man innsignalet og agerer når dette endres. Ganske selvforklarende.

Testing

Etter programmet var ferdig og lastet opp, ble det testet. Og det fungerte fint.

Ved å trykke på knappen tennes dioda. Også slukkes den 5 sekunder (5000 ms) etter knappen ble sluppet igjen:

Lyset kan virke skarpt, men resistoren var på 220\Omega, slik som i det lenkede Arduino-prosjektet. Neste gang vil jeg muligens øke resistansen enda mer.

Embedded C

Kodeskrivingen over kan knapt kalles utfordrende, da den ferdige koden virket omtrent på første forsøk. Embedded C ble derimot litt mer krevende. Først og fremst pga. registermanipuleringen som tar litt tid å bli vant til.

Men etter litt knoting, lesing og testing ble det en løsning:

#undef F_CPU
#define F_CPU 16000000 // CPU frequency to be considered by the delay macros

#include <avr/io.h>
#include <util/delay.h>

int main (void)
{
   /*
      ATmega328P on Arduino board
      1x switch on pin 0 on port B
      1x LED on pin 1 on port B
   */

   // Data direction register for port B, input = 0 and output = 1
   DDRB &= ~(1<<PB0); // Set pin 0 to input on port B
   DDRB |= 1<<PB1; // Set pin 1 to output on port B

   // Delay in ms
   const int LONG_DELAY = 5000;
   const int SHORT_DELAY = 10;

   while (1) {

      // Button down (active) = high
      if (PINB & 1<<PB0) { // If pin 0 set high on port B
         PORTB |= 1<<PB1; // Set pin 1 to high on port B
         _delay_ms(LONG_DELAY);

      // Button up (inactive) = low
      } else {
         PORTB &= ~(1<<PB1); // Set pin 1 to low on port B
         _delay_ms(SHORT_DELAY);
      }
   }
}

Her brukes makroer (DDRB, PB0, osv.), dette forenkler arbeidet og gjør koden lesbar.

Samme kode ville riktignok også fungert i Arduino IDE, men å bli vant til å kun bruke denne IDE-en er ikke særlig smart. Den er altfor simpel. Det er sunt å fikle med andre IDE-er, sette opp avrdude manuelt, osv. for å bli mest mulig stødig.

Min valgte IDE for Embedded C er inntil videre Eclipse Classic 3.7.0, med AVR-plugin installert. Dette er da på Linux-distro Zorin OS. For å laste opp til mikrokontrolleren benyttes avrdude, men dette gjøres sømløst fra Eclipse. Det er ikke knoting i terminal.

Tanken var å gå over til MPLAB X IDE fra Microchip, men førsteinntrykket skuffet.

Legg igjen en kommentar

Din e-postadresse vil ikke bli publisert. Obligatoriske felt er merket med *