Tania ATmega8 z Chin: podróbka czy okazja?
MilisecondsTimer.cpp
#include <avr/io.h> #include <avr/interrupt.h> #include "MillisecondTimer.h" volatile uint32_t MillisecondTimer::_counter=0; volatile uint8_t MillisecondTimer::_subCounter=0; /* * Timer 0 interrupt handler */ ISR(TIMER0_OVF_vect) { MillisecondTimer::_subCounter++; if((MillisecondTimer::_subCounter & 0x3)==0) MillisecondTimer::_counter++; TCNT0+=6; }
Teraz możemy odmierzać czas. Potrzebuję prostego sposobu na adresowanie pinów GPIO. Oto efektywne rozwiązanie: szablony i definicje typów pozwalają uzyskać czytelny kod, a implementacja w czystym asemblerze zapewnia, że rozwiązanie jest maksymalnie wydajne.
GpioPin.h
#pragma once /* * GPIO ports */ struct GPIOB { enum { Port = 0x18, Dir = 0x17, Pin = 0x16 }; }; struct GPIOC { enum { Port = 0x15, Dir = 0x14, Pin = 0x13 }; }; struct GPIOD { enum { Port = 0x12, Dir = 0x11, Pin = 0x10 }; }; /* * Base template for all GPIO pins. Provide support for set/reset */ template<uint8_t TPort,uint8_t TPin> struct GpioPin { static void set() { asm volatile( " sbi %[port],%[pin] \n\t" :: [port] "I" (TPort), [pin] "I" (TPin) ); } static void reset() { asm volatile( " cbi %[port],%[pin] \n\t" :: [port] "I" (TPort), [pin] "I" (TPin) ); } static void changeTo(bool state) { if(state) set(); else reset(); } }; /* * GPIO output, provide support for init */ template<class TPort,uint8_t TPin> struct GpioOutputPin : GpioPin<TPort::Port,TPin> { static void setup() { asm volatile( " sbi %[port],%[pin] \n\t" :: [port] "I" (TPort::Dir), [pin] "I" (TPin) ); } }; /* * GPIO input, provide support for init, read */ template<class TPort,uint8_t TPin> struct GpioInputPin : GpioPin<TPort::Port,TPin> { static void setup() { asm volatile( " cbi %[port],%[pin] \n\t" :: [port] "I" (TPort::Dir), [pin] "I" (TPin) ); } static bool read() { uint8_t r; asm volatile( " clr %[result] \n\t" // result = 0 " sbic %[port],%[pin] \n\t" // skip next if port bit is clear " inc %[result] \n\t" // result = 1 : [result] "=r" (r) : [port] "I" (TPort::Pin), [pin] "I" (TPin) ); return r; } }; /* * All pins used in this project */ typedef GpioOutputPin<GPIOD,5> GpioLed;
Po tych przygotowaniach napisanie funkcji main, która obsługuje miganie, jest trywialne.
Blink.cpp
#include <avr/io.h> #include <avr/interrupt.h> #include "GpioPin.h" #include "MillisecondTimer.h" int main() { MillisecondTimer::setup(); GpioLed::setup(); // PD5 (pin 11) sei(); for(;;) { GpioLed::set(); MillisecondTimer::delay(1000); GpioLed::reset(); MillisecondTimer::delay(1000); } // not reached return 0; }
Kod jest już napisany i musimy go skompilować. Wykorzystuję narzędzie scons, ponieważ jest oparte na Pythonie – języku użytecznym też w innych sytuacjach.
Oto mój plik SConstruct. Wykorzystujemy program avrdude do wgrania pliku binarnego do pamięci mikrokontrolera za pomocą programatora USBAPA, a polecenie fuse ustawia zworki na wykorzystanie zewnętrznego oscylatora.
SConstruct
""" Usage: scons [upload] [upload] specify this option to automatically upload to using avrdude to a USBASP connected MCU. To do a 'clean' use the -c flag To do a parallel build use the -jN flag """ import os # source the environment env=Environment(ENV=os.environ) # compiler is avr-gcc env.Replace(CC="avr-gcc") env.Replace(CXX="avr-g++") env.Replace(PROGSUFFIX=".elf") # set up our options env.Replace(CXXFLAGS=["-mmcu=atmega8", "-Os", "-g", "-DF_CPU=8000000", "-std=c++11", "-Wall", "-Werror", "-Wextra", "-pedantic-errors", "-fno-rtti", "-ffunction-sections", "-fdata-sections", "-fno-exceptions"]) env.Replace(LINKFLAGS=["-Wl,-Map,blink.map", "-mrelax", "-g", "-Wl,--gc-sections", "-mmcu=atmega8"]) # compile source code to .elf binary elf=env.Program("blink",[Glob("*.cpp")]) Decider('timestamp-newer') # convert elf to hex flashable image env.Command("blink.hex",elf,"avr-objcopy -j .text -j .data -O ihex $SOURCE $TARGET") # calculate size and generate assembly source env.Command("blink.siz",elf,"avr-size $SOURCE | tee $TARGET") env.Command("blink.lst",elf,"avr-objdump -S $SOURCE > $TARGET") # upload target uses avrdude flash_cmd="avrdude -c usbasp -p m8 -e -U flash:w:blink.hex" # internal oscillator #fuse_cmd="avrdude -c usbasp -p m8 -e -U lfuse:w:0xe4:m -U hfuse:w:0xd9:m" # external crystal fuse_cmd="avrdude -c usbasp -p m8 -e -U lfuse:w:0xff:m -U hfuse:w:0xd9:m" upload=env.Alias("upload","blink.hex",flash_cmd) fuse=env.Alias("fuse","blink.hex",fuse_cmd) AlwaysBuild([upload,fuse])