Archiv für den Monat: August 2015

Minimal-Arduino selbst bauen: Bootloader auf ATMEGA328P-PU

Zunächst einmal braucht man für die Programmierung der ATMEGA328P-PU einen „normalen“ Arduino. Ich habe mir bei ebay für wenig Geld aus Hong Kong einen Arduino-Klon besorgt. Es handelte sich dabei um einen „Pro.mini.328P“ („New Pro Mini atmega328 Board 5V 16M Arduino Compatible Nano NEW“). Das Gerät war ohne USB-Header ausgestattet, also musste ich zunächst noch einen USB-Serial-Wandler hierfür nutzen. Diesen hatte ich schon, er stammte ebenfalls aus Hong Kong.

Nachdem RX vom USB-Wandler zum TX vom mini Pro verbunden wurden, musste noch TX<->RX, 5V<->VCC und GND<->GND verbunden werden. Die „Blink“-Demo sollte laufen.

Danach die Blink-Demo in der Arduino-Programmierumgebung laden und testweise auf den Ardunio laden. Blinkt die Diode nun im (neu) vorgegebenen Takt, hat alles geklappt. Die Voraussetzung für die eigentliche Programmierung ist jetzt gegeben.

Nun kann man sich grob weiter an dem Artikel der make-Redaktion entlanghangeln: http://www.heise.de/make/artikel/Arduino-Uno-als-In-System-Programmer-2769246.html

Übrigens: Um das AVRDUDE unter Windows zu nutzen, lädt man sich das Open Source-Programm von hier: http://download.savannah.gnu.org/releases/avrdude/avrdude-6.1-mingw32.zip

Als nächsten Schritt muss der Pro.mini.328P zu einem ISP-Programmer gemacht werden. Hierzu lädt man in der Arduino-IDE unter „Beispiele“ den Sketch „Ardunio ISP“ aus und lädt diesen hoch.

Die Verkabelung muss danach wie folgt aussehen (den 10kOhm-Widerstand am Pin 1 kann man auch weglassen).

BreadboardAVR

Der Quarz muss wirklich gut verbunden sein, sonst erhält man fortlaufend nur Fehlermeldungen der Art:

avrdude: stk500_recv(): programmer is not responding
avrdude: stk500_getsync() attempt 1 of 10: not in sync: resp=0x36
avrdude: stk500_recv(): programmer is not responding
avrdude: stk500_getsync() attempt 2 of 10: not in sync: resp=0x36
avrdude: stk500_recv(): programmer is not responding
avrdude: stk500_getsync() attempt 3 of 10: not in sync: resp=0x36
avrdude: stk500_recv(): programmer is not responding
avrdude: stk500_getsync() attempt 4 of 10: not in sync: resp=0x36
avrdude: stk500_recv(): programmer is not responding
avrdude: stk500_getsync() attempt 5 of 10: not in sync: resp=0x36
avrdude: stk500_recv(): programmer is not responding
avrdude: stk500_getsync() attempt 6 of 10: not in sync: resp=0x36
avrdude: stk500_recv(): programmer is not responding
avrdude: stk500_getsync() attempt 7 of 10: not in sync: resp=0x36
avrdude: stk500_recv(): programmer is not responding
avrdude: stk500_getsync() attempt 8 of 10: not in sync: resp=0x36
avrdude: stk500_recv(): programmer is not responding
avrdude: stk500_getsync() attempt 9 of 10: not in sync: resp=0x36
avrdude: stk500_recv(): programmer is not responding
avrdude: stk500_getsync() attempt 10 of 10: not in sync: resp=0x36

Und man sollte beim als ISP verwendeten Arduino nicht vergessen, zwischen GND und RESET einen 10 Micro-Farad-Kondensator einzusetzen, da dieser sonst immer wieder zurückgesetzt wird.

In der Arduino-IDE den ZIEL-Arduino auswählen (Hier: ATMEGA328P-PU = Arduino Uno) und als Programmer auf „ArduinoISP“ umstellen. Dann kann man mit „Bootloader brennen“ hoffentlich das blinken des Arduinos sehen und nach wenigen Sekunden ist der Bootloader fertig gebrannt. Da ich hiernach eine Menge unnötiger Zeit mit einer Fehlersuche vertan hatte, hier noch der wichtige Tipp, dass man den Quell-Arduino (den man als ISP einsetzt) am Ende, also nach fertigem Brennen noch einmal in den RESET schickt. Aus irgendeinem Grund hatte bei mir der ATMEGA328P sonst in einer Breadboard-Anordnung keinerlei Anstalten gemacht, das Bootloader-Blinken zu Beginn von sich zu geben, noch irgendein Programm via RX/TX anzunehmen.

Noch ungelöstes Problem: Nur nach einem manuellen (!) Reset akzeptiert der ATMEGA328P ein neues Programm aus der Arduino-IDE via RX/TX.

Low Power Arduino ATMEGA328P

Nachdem ich den Minimal-Arduino auf dem Breadboard aufgebaut hatte, wollte ich diesen gerne über eine CR2032-Batterie mit Strom versorgen, und das möglichst lange.

Zum Glück gibt es schon einige Artikel im Internet, die sich mit dem Stromverbrauch des ATMEGA328P auseinandergesetzt haben. Allerdings erwarten hier viele einen Interrupt von Extern, was in meinem Anwendungsfall (halbwegs regelmässig einen Sensor auslesen und die Information abspeichern oder verschicken) nicht ganz praktisch war. Vor allem, weil ich den passenden Uhrenquarz nicht verfügbar habe…

Hier also (nachvollziehbar) die einzelnen Schritte, die ich bei meinem ATMEGA328P unternommen habe, um ihn den hohen Stromverbrauch streitig zu machen.

Step 1: Default blink program with 5s delay, 16 MHz ext. Osz., 3V CR2032-battery

void setup() {
  // initialize digital pin 13 as an output.
  pinMode(13, OUTPUT);
}
 
void loop() {
  digitalWrite(13, HIGH);   // turn the LED on (HIGH is the voltage level)
  delay(5000);              // wait for a second
  digitalWrite(13, LOW);    // turn the LED off by making the voltage LOW
  delay(5000);              // wait for a second
}

Result:

  • LED On: 6.8mA
  • LED Off: 5.8mA

Step 2: All other pins as input

void setup() {
  //To reduce power, setup all pins as inputs with no pullups
  for(int x = 1 ; x < 18 ; x++){
    pinMode(x, INPUT);
    digitalWrite(x, LOW);
  }
  // initialize digital pin 13 as an output.
  pinMode(13, OUTPUT);
}
 
void loop() {
  digitalWrite(13, HIGH);   // turn the LED on (HIGH is the voltage level)
  delay(5000);              // wait for a second
  digitalWrite(13, LOW);    // turn the LED off by making the voltage LOW
  delay(5000);              // wait for a second
}

Result:

  • LED On: 6.45mA
  • LED Off: 5.65mA

Step 3: Power Down during sleep

I’ll admit that this next step is a bit confusing, as a lot of things are introduced.

#include <avr/sleep.h> //Needed for sleep_mode
#include <avr/wdt.h>
 
volatile boolean f_wdt=1;
 
void setup() {
  //To reduce power, setup all pins as inputs with no pullups
  for(int x = 1 ; x < 18 ; x++){
    pinMode(x, INPUT);
    digitalWrite(x, LOW);
  }
  // initialize digital pin 13 as an output.
  pinMode(13, OUTPUT);
  setup_watchdog(8);
}
 
byte state=0;
 
void loop() {
    if (f_wdt==1) {  // wait for timed out watchdog / flag is set when a watchdog timeout occurs
    f_wdt=0;       // reset flag
    switch (state){
    case 0:
      digitalWrite(13, HIGH);   // turn the LED on (HIGH is the voltage level)
      state = 1;
      setup_watchdog(0);
      break;
    case 1:
      digitalWrite(13, LOW);    // turn the LED off by making the voltage LOW
      state = 0;
      setup_watchdog(8);
      break;
    }
    system_sleep();
  }
}
 
//****************************************************************  
// set system into the sleep state 
// system wakes up when watchdog is timed out
void system_sleep() {
  // power_adc_disable()
  set_sleep_mode(SLEEP_MODE_PWR_DOWN); // sleep mode is set here
  sleep_enable();
  sleep_mode();                        // System sleeps here
  sleep_disable();                     // System continues execution here when watchdog timed out 
  // power_adc_enable()
}
 
//****************************************************************
// 0=16ms, 1=32ms,2=64ms,3=128ms,4=250ms,5=500ms
// 6=1 sec,7=2 sec, 8=4 sec, 9= 8sec
void setup_watchdog(int ii) {
 
  byte bb;
  if (ii > 9 ) ii=9;
  bb=ii & 7;
  if (ii > 7) bb|= (1<<5);
  bb|= (1<<WDCE);
 
  MCUSR &= ~(1<<WDRF);
  // start timed sequence
  WDTCSR |= (1<<WDCE) | (1<<WDE);
  // set new watchdog timeout value
  WDTCSR = bb;
  WDTCSR |= _BV(WDIE);
}
//****************************************************************  
// Watchdog Interrupt Service / is executed when  watchdog timed out
ISR(WDT_vect) {
  f_wdt=1;  // set global flag
}

Result:

  • LED On: 1.07mA
  • LED Off: 0.02mA (206uA)

As long as we depend on the internal oscillator, we won’t get any further then 206uA, as the watchdog is still enabled and drawing current. Time to get one of those nice 32kHz-oscillators.

Here are some more links related to this topic, some of those helped me in my process (some in german):

  • http://www.mikrocontroller.net/articles/Sleep_Mode
  • http://interface.khm.de/index.php/lab/interfaces-advanced/sleep_watchdog_battery/