Electronics project notes/Arduino and AVR notes
See also Electronics project notes/Microcontroller and computer platforms
Environment/compilation; differences between basic AVR and Arduino coding
avr-libc is a stripped-down implementation of libc. Not everything you might expect is in avr-libc (or Arduino's code). For example, (s)printf won't handle %f.
Both avr-libc and Arduino's additions have a bunch of code that makes things work consistently on different AVRs.
C or C++?
Coding based on just avr-libc is often done in C, and compiled with avr-gcc.
Arduino's additions ((the stuff mentioned here)) are C++, and the Arduino IDE compiles with avr-g++/avr-c++.
Arduino uses C++ for things like inheriting code from Stream / Print. (I'm not sure why exactly they chose it. It can, under certain conditions, compile to more code than really necessary)
This means that you can't always easily copy Arduino code into your own C projects - it's not hard to use C code in C++ compilation, but you simply cannot use C++ code in C.
Yet since the form of C++ you can use is limited, it tends to be nothing fancier than bookkeeping in objects, so often enough you can strip out the useful code and rewrite things as a bunch of C functions.
Another option is to switch your project to C++. There's a avr-c++/avr-g++ and no reason it couldn't work (see [1]. Arduino does it, and so have other people, though it may practice it may require a bunch of fiddling and web searching about lower level details before it works nicely.
- Telling it about your hardware
Most hardware features are only available after you include avr/io.h, which will only work if you have defined a variable stating which specific AVR variant you are compiling for (e.g. __AVR_ATmega328P__).
Code may requires things like F_CPU to be set, to the clock rate you are running at in Hz.
In the IDE, this is effectively done via the "Tools/Board" menu, so this only really comes up when not using the Arduino IDE.
- On time
Timekeeping on AVR is usually based on using one of its hardware timers to generate interrupts, and having an interrupt handler that increments a variable (with knowledge of the CPU clock rate).
For example, millis() reports a counter that is updated from an interrupt hooked to Timer0(verify).
You can roll your own.
You'll need to learn how to use the timers, or copy non-buggy code from somewhere.
http://www.bildr.org/forum/viewtopic.php?f=17&t=386
See also:
Arduino hardware notes
Note: things related primarily to AVRs and barely to Arduinos should be moved to #AVR notes below.
Note that much of the below is biased to be about the Duemilanove as it was written when that the most common board.
The older Diecimila is hard to find now.
The newer Uno are similar in most ways.
Arduino IDE[2] (itself derived from Processing[3]) makes it easier to write and compile code, program the microcontroller (using avrdude), and monitor the serial port when you want to.
Intro - the more official Arduinos
Arduino refers both to the board designs and to the programming environment that is very commonly used to work with these boards.
The core of most Arduinos is an Atmel AVR-series microcontroller.
That AVR does most of the work, and the board around it mostly represents convenience -
easily pluggable pins,
USB/serial interface (for programming as well as communication),
regulated power, (automatic switching to) power plug,
crystal clock (not strictly necessary; see notes below),
and such.
Many of the common Arduino boards currently use the ATmega328 AVR, older boards used the ATmega168 (and even older ones the ATmega8).
Boards like the Mega choose more complex variants like the 1280 and similar.
The faster Due and Zero and use ARMs.
There are about a dozen distinct Arduino boards with names.
These may have different amounts of flash and RAM, IO pins, and a number are specialized - for example stripped down and/or small, are geared to be LiPo-powered, to do Bluetooth, or such. See http://www.arduino.cc/en/Main/Hardware for a list and details of each.
(There are also a few revisions of a number of these boards, occasionally just with slightly different component specs (sometimes a slightly newer AVR), and in one or two cases with mildly different pinouts. (see e.g. the difference between the Nano 2.x versus Nano 3.x))
The basic-size arduinos have actually seen a lot of revisions. Right now you'll probably mostly see (from latest to older):
- There's also Arduino Diecimila (literally '10000', referring to the amount of arduinos shipped at the time of design), not seen anymore, and the Arduino NG (Nuova Generazione, 'New Generation'), not produced anymore. Since these are not made anymore, and they used earlier AVR chips, you can ignore these.
- Duemilanove[4] (lit. '2009', introduced in 2009)
- USB socket: handled by FTDI FT232RL
- Uno[5] (introduced late 2010) is basically a sightly updated duemilanove:
- The power header adds IOREF (5V or 3.3V depending on what you run the board at, and an unused pin
- different reset circuit - seems to solve an issue in this and previous boards where the auto-reset pulse (from the USB-to-serial) occasionally had other side effects[6](verify)
- The pin-8-to-13 header with SCL, SDA - which are the same as A4 and A5 on the other side (seems done to ease shield designs that also fit e.g. the Leonardo; its uC puts I2C on different physical pins)
- USB socket handled Atmega8U2 (a simple USB-capable uC) instead
- relevant revision differences:
- R1 and R2 use Atmega8U2, R3 uses ATMEGA16U2
- R2 and R3 add solder pads exposing the U2's PB4, PB5, PB6, PB7 (apparently to give you more communication options. These are also interrupt/timer pins, which is sometimes pretty useful)
- R2 and R3 pull the U2's HWB line to ground, making its DFU mode simpler to use
- R3 extended pin headers
- relevant revision differences:
- http://arduino.cc/en/Main/ArduinoBoardUno
- http://www.ladyada.net/library/arduino/unofaq.html
- Leonardo[7]
- slightly cheaper than Uno
- Uses ATmega32U4 as the main uC (which speaks USB directly, and this board existed before the Uno so it was a selling point)
- sometimes easier to deal with than doing USB on an Uno (verify)
- otherwise most comparable with the Uno
- differences with similar-sized boards:
- more pins usable as analog in
- one more PWM out
- using I2C doesn't take away A4 and A5 (but D2 and D3 and their interrupt use instead)
- two UARTs, Serial being the USB-serial link, Serial1 the exposed pins (verify)
More PWM pins
More digital pins full stop
A second hardware serial port
"I need more pins" boards:
- Mega
- Uses ATmega1280 (128KB flash, 8KB SRAM, 4KB EEPROM)
- http://arduino.cc/en/Main/ArduinoBoardMega
- Mega 2560[8] (introduced late 2010)
- Mostly like Mega, but with 256KB flash instead of 128KB
- Like Uno, USB socket handled via an Atmega8U2
- http://arduino.cc/en/Main/ArduinoBoardMega2560
- and e.g. the Due (also faster, see below)
Some more-specific-function boards:
- BT[9]
- Has a bluetooth module (and no USB, or physical serial port)
- Otherwise much like the NG (/ Diecimila)
- (Uses an ATmega168)
- Can be programmed over bluetooth
- http://arduino.cc/en/Main/ArduinoBoardBluetooth
- Fio[10]
- Set up for XBee, and LiPo charging
- http://arduino.cc/en/Main/ArduinoBoardFio
- Various Lilypad boards
- often Atmega328 or Atmega168; ATmega32U4 for Lilypad USB
- http://arduino.cc/en/Main/ArduinoBoardLilyPad
Smaller boards:
- Pro[11]
- somewhat like a SMD variant of the Duemilenove-style board (seems to come with the 168 as well as 328, in 3.3V and 5V variants, at 8MHz and 16MHz)
- flatter than, still mostly the same area as, a classical arduino
- http://arduino.cc/en/Main/ArduinoBoardPro
- Nano[12]
- Nano 2.x uses ATmega168, Nano3.x uses ATmega328
- basically a Duemilanove crammed onto a smaller size with the same components (non-replacable SMD AVR, mini-B USB connector, no DC socket (but still has the regulator, so this is just a wiring thing))
- pin headers on the bottom, easily plugged into a solderless breadboard.
- Not easy to use with shields designed for the Duemilinove/Mega
- http://arduino.cc/en/Main/ArduinoBoardNano
- Mini[13]
- ATmega168 / ATmega328
- also a crammed board - smaller than the nano because it omits some components you can handle them yourself when you know what you're doing - no power regulator, no FTDI (so no USB, and no 3.3V), and apparently no ICSP.
- Doesn't have board pins for all analog channels. You can solder the rest, but it makes it harder to stick the mini onto a breadboard
- Not easy to use with shields designed for the Duemilinove/Mega
- There is an official USB adapter board for the Mini
- http://arduino.cc/en/Main/ArduinoBoardMini
- Pro Mini[14]
- ATmega168 / ATmega328P
- physically like the Mini (e.g. no usb)
- can also be run at 3.3V (at 8MHz max), needs to be 5V at 16 MHz (verify)
- http://arduino.cc/en/Main/ArduinoBoardProMini
- Micro / Pro Micro[15]
- ATmega32U4 (which speaks USB directly)
- https://store.arduino.cc/arduino-micro
- 5V run at 16kHz, 3.3V version run at 8kHz
Side note: In terms of small form factor, you may also care about the teensy 2 series
Faster boards
- Zero[16]
- ARM Cortex-M0+ at 48 MHz (ATSAMD21G18)
- 32KB SRAM
- 256KB flash
- CPU is 3.3V, and its GPIO isn't 5V tolerant(verify)
- ADC is 12-bit (earlier were 10-bit)
- 1-channel 10-bit DAC
- faster PWM (verify)
- board form factor like earlier arduinos
- Due[17] (note: nothing to do with duemilenove)
- ARM Cortex-M3 at 84MHz (AT91SAM3X8E)
- 64+32KB SRAM
- 512 KB flash
- CPU is 3.3V, GPIO isn't 5V tolerant.
- 12-bit ADC (12 wired on the board)
- 2-channel 12-bit DAC
- faster PWM, and on more pins
- board form factor like earlier mega
- Note: You may also care about the teensy 3 series
- MKR
- itself refers to a form factor (size/shape), with a few distinct boards
- all with the ARM M0+ (so 3.3V, and not 5V tolerant)
- each with a specialisation, e.g. MKR WiFi, MKR Zero aimed somewhat at audio, MKR WAN that speaks LoRa, MKR Vidor does video processing
- and a bunch of existing shields (communication, sensors, etc)
Since Arduino hardware design is open source, there are other manufacturers of Arduino boards, and a number of derivatives/variations of the official boards. By now, there are many. See for example freeduino, roboduino, DFRduino, iDuino, sanguino, Seeeduino, Really Bare Bones Board (RBBB), Imaguino, some single-sided versions (so you can etch your own), cheaper chinese-made variants, etc.
-->
Notes that apply to various boards
Clock speed
Most AVRs seem to be rated to work fine up to 20MHz. Most arduino boards run them at 16MHz.
You can run them faster or slower, which some people do when that makes specific timing simpler (e.g. for TV signal generation), you just have to pay attention to a few extra details, e.g. library assumptions about clock speed..
Coding
Intro
On libraries and separating into files
You usually want to use the IDE - it hides a bunch of hidden compilation trickery that makes life simpler.
People interested in doing it themselves are often the ones with the background to manage anyway, but will be interested in http://www.arduino.cc/en/Hacking/BuildProcess
Power (classic boards)
When you want to fiddle with the way you supply power, keep in mind that most basic boards do something like:
Powerjack -- polarity protection -- Vin -- 5V regulator -+- AVR diode | +- 5V pin
(See also duemilanove board schematic - and check when you use a less usual Arduino)
Power-related pins:
- Gnd (in various places, for convenience)
- RESET: Functionally equivalent to the reset button. Occasionally used to expose this on shields
- 5V: post-regulator power from jack/Vin, or taken from USB (which is already regulated)
- Vin: in the path metioned below, before the regulator (called RAW on some(verify)).
- E.g. a battery or adapter without the power-jack plug could be put here. You can also effectively leech from a power-jack-plugged adapter here.
- 3V3: 3.3V
- on pre-Uno USB boards this comes from the FTDI chip which can supply at most 50mA. It seems that boards with the FTDI always power that FTDI so 3.3V is present whenever there's power on Jack/Vin-powered, not only when USB-powered(verify) (For the Nano, the hardware description mentions the FTDI is only on when USB-powered)
- on boards with the 8U2 (Uno and later), 3.3V comes from a regulator, which is specced for 150mA
The Duemilenove and newer boards can automatically choose between the jack and USB power (preferring the jack/Vin).
Older versions had a jumper to select one of the two.
On external power, and drawing current from it
- The Arduino's regulator is a simple linear regulator (may be one of various, it's not strictly specced - I've seen people mention 400mA and 800mA, so don't count on more than 400mA).
- it prefers 7V to perhaps 12V
- it will work with as little as 6V but may then output less than 5V, which can mean not all components are stable enough
- it can deal with up to perhaps 20V, but will likely heat up and die faster from the heat (regulator's heat genration being proportional to current times voltage drop. It'll already get warm dropping from 12V when drawing ~100mA, so if you want adde components to draw that or more, try to get a better-matched voltage, like 7.5V -- or make the draw path external to that regulator via a transistor or relay or such)
- The polarity protection diode seems specced at 1A
- You should probably assume the traces cannot take more than 1A anyway.
- There is a polyfuse limiting power from USB to ~500mA.
Powering the board can be done e.g. with:
- USB power
- hardware (e.g. a non-powered hub) may give no more than 100mA
- 100mA is by USB specs, more is possible but has to be negotiated -- in theory. You can often draw up to the USB maximum (500mA, or 900mA for USB3) without it actively complaining or cutting you off.
- Protected on the Arduino side with a 500mA polyfuse (Computer USB ports should have polyfuses too; by-specs USB can deal with short circuits because of them)
- A DC adapter on the power jack (through the regulator, as does the polarity protection diode)
- Wallwarts supplying something between 7.5 and 9V are probably handiest (if you may draw more than a little current, then preferably a regulated one so you know voltage won't drop too much); see also notes below
- 12V is doable, but the regulator starts heating up at a lower current (over ~200mA already means it's pretty hot)
- You can steal power via Vin - you bypass and don't tax the regulator, but it's still after the polarity protection diode so keep it below 1A
- Well-regulated 5V can be plugged into 5V (bypassing both diode and regulator).
- Perhaps only useful if you have a more efficient regulator; if you do this from a DC adapter, all you save is a few dozen milli-amps, which in most situations is peanuts.
- ...and it's easier to fry stuff if you make the mistake of flipping the polarity
- Batteries on the 5V line are a bad idea, mainly because it's hard to get the voltage right. voltage. Four 1.2V AA rechargables is okay, four 1.5V AA alkalines is too much. One Lithium cell not enough, two too much.
- different story when you configure the AVR for 3.3V operation(verify)
- A battery plugged onto Vin (board regulator applies), but needs to be >6V.
- A 9V battery is easiest to deal with (easier than five penlites)
Other current limits:
- ~40mA from any one IO pin (if you want more, you want a transistor, darlington/fetlington, or such, and collect power from 5V (few hundred mA max), Vin (~1A max), or something external).
- the 5V pin is limited by coming from the regulator - so can source a few hundred mA in practice, less if it has to drop a bunch of voltage.
- the 3V3 pin can supply 50mA if it comes from the FTDI, 150mA from the Uno's regulator. Pretty little.
Expectable current drawn in practice
The AVR and Arduino board around it seems to draw a little under 30mA when not driving anything (and clocked at the usual 16MHz). Running it slower than that will make it draw a little less (~20mA?(verify)).
This may come for a decent part from the regulator - unless specifically low-loss, it may have ~10mA quiescent current (low-loss variants may be <1mA).
Some shields are power hogs in comparison, easily drawing 100-200mA.
Arduino - IO, communication, chip programming
Other special, pin-tied functionality
Boards like the duemilanove and uno have something like
- 20 GPIO pins
- 6 are selectable by the ADC (single muxer; see notes below) - referred to as A0..A5
- the others are referred to as D0..D13
- PWM on some of them (clock-based, 8-bit). (three or six. Some other boards have more)
- Supports I2C / TWI on pins A4 and A5
- Supports SPI on pins D10..D13 (note: overlap with ICSP pins)
- hardware interrupts can be tied to pins D2 and D3; see AttachInterrupt
- Pins D0 and D1 go to the USB-to-serial chip, and to the AVR's UART. This is RS232-style serial, but at 5V.
- (The non-soldered four-pin header labeled X3 are the FTDI's CTS, DSR, DCD, and RI)
- Pin D13 has a resistor+led on board. Can be useful to signal roughly what your code is up to.
- supports ICSP (on the 3x2 header)
The larger boards can have more interrupts, more UARTs, more PWM, more analog pins, and such. There are a whole bunch of specialized boards, particularly if you count all the arduino derivatives.
See also:
- Bootloader and serial
The ATmega comes preloaded with a bootloader (uses a small portion of Flash space, e.g. 2KB of 32KB), which means it speaks Atmel's STK500 protocol (on TX, RX), which is why and how you can reprogram the rest of Flash memory over serial, and don't need a programmer.
Most boards not additionally tie the (usb-to-serial's) DTR to the AVR's reset line, so that you don't have to press reset at the right time, like you did on the first boards.
You could also use this to reset the AVR remotely via serial.
This DTR line seems to also be the reason that in some cases, the AVR reset when you connect USB.
If you really want to use the Flash that the bootloader uses, you could go completely without a bootloader (but you'd need the programmer each time) you'll need a programmer.
They are actually fairly simple to buy/build, because ISP is also mostly just a protocol and doesn't need weird voltages - you can use one arduino to program AVRs.
See e.g.
IO: All digital, some analog
Board labeling split pins into analog and digital pins, but actually all of them are GPIO (general-purpose IO) pins.
For example, on the smallish boards with 6 analog pins and 14 digital pins, this can be better described as 20 GPIO pins with 6 routable to the ADC, and
- A0 is also D14
- A1 is also D15
- A2 is also D16
- A3 is also D17
- A4 is also D18
- A5 is also D19
EEPROM
ADC
See also 'AVR120: Characterization and Calibration of the ADC on an AVR'
Many AVRs have one ADC, which in most cases has a multiplexer in front of it so it can easily switch between sampling from a number of its physical pins.
In most variants the ADC has up to 10-bit resolution, and is usually run in single-ended conversion mode, in which it returns values in the 0..1023 range for 0V to Vref (truncated, so 0 means ≤0V, 1023 means ≥Vref).
(Some AVRs in some packages can also be run in differential mode, but don't count on this)(verify)
Vref can be one of three references:
- analogReference(DEFAULT) - 5V (probably Vcc?(verify))
- analogReference(INTERNAL) - an internal reference (1.1V on ATmega168, 328 and many others; 2.56V on ATmega8)
- analogReference(EXTERNAL) - The voltage on the Aref pin (possibly a minimum voltage of about 1V?(verify))
On speed
The ADC takes 13 of its own clocks for conversion, and is run at a division of the input clock. This makes the maximum sample rate (avr_clock/(adc_divisor*13)), which is (16MHz/(adc_divisor*13)) on most arduinos.
The speed is set by tweaking three bits in ADCSRA. When dividing a 16MHz clock, speeds are:
Resulting clocked samples theoretical max ADPS2 ADPS1 ADPS0 Divisor at per sec accuracy note 0 0 0 2 8MHz ~615k (inaccurate) 0 0 1 2 8MHz ~615k (inaccurate) 0 1 0 4 4MHz ~307k (not so accurate) 0 1 1 8 2MHz ~153k (not so accurate) 1 0 0 16 1MHz ~76k (lessened accuracy becomes noticable) 1 0 1 32 500kHz ~38k 1 1 0 64 250kHz ~19k 1 1 1 128 125kHz ~9.6k (the most accurate, the arduino default)
Arduino boards defaults to the 125kHz/~9.6k setting. And is slightly slower than that in use because of a little extra code around the actual sampling, some of which you can do a little faster with specific-case hardcoding rather than analogRead calls. I've gotten ~8.9ksamples/sec out of this, so good enough.
The AVR specs mention that the ADC will only really give ~10 bit resolution when run slower than 200kHz, but also that resolution is still acceptable up to 1MHz, so it seems feasible enough to use 16MHz divided by 64, 32, or 16.
You may also find use for /8 for 2Mhz, /4 for 4Mhz or even /2 for 8MHz, though aside from the lower accuracy, you may also have trouble using the data (or moving it off) fast enough. It could still make sense for some fast triggering logic.
If you want more channels or more accuracy, look at external ADCs, e.g. http://www.arduino.cc/playground/Main/InterfacingWithHardware#adcdac
On accuracy
There are a few different influences on overall accuracy
- the sampling speed (see above)
- output impedance of what you're sampling
- if it's on the order of 10kOhm or higher, the ADC's sample-and-hold capacitor is basically a part of things
- resulting in crossbleed to the next conversion. You may not notice on a singly slow moving signal, but you will if it's a different pin and/or fast signal
- switching noise of the processor itself
- which is why there's a "sample while sleeping" mode
- the rest of the pin's input block, e.g. the digital input on the same pin, is still connected (verify)
- and if that digital in part of the port is floating, that will be some extra noise, and present some capacitance, which can be some issue sensing high-impedance things
- Look in the datasheet, around DIDR0
Manual ADC stuff
If you decide to do the ADC stuff at lower level, there are a few useful snippets of code, and a few details you may want to know about.
Details vary slightly between boards, so if you're stripping code, steal it from the Arduino library.
Not muxing between reads
The implementation of analogRead() chooses to always set channel via ADMUX, so one reason to use more manual code is if you want repeated reads from one channel to be a little faster.
A variant without that ADMUX set would be something like:
inline int read_adc() {
// Does an ADC sample with the current ADMUX settings
byte low,high;
ADCSRA |= _BV(ADSC); // start the conversion. Also seen as sbi(ADCSRA, ADSC);
while (bit_is_set(ADCSRA, ADSC))
; // ADSC is cleared when the conversion finishes
low=ADCL; // reading ADCL locks both of these
high=ADCH; // until reading ADCH; seems to ensure the value comes from the same conversion.
return (high<<8) | low;
}
Older code had delay(1) between ADMUX change and the conversion.
This seems motivated by two potential problems:
- For high-impedance reason (see above)
- order of 100us to 1ms is usually enough, depending on the impedance
- When you use ADMUX, you may see the ADCH and ADCL from different channels/conversions. Ways to work around this:
- throw away the first value you read out, use the next (verify)
- delay 1ms (less is probably enough) to make sure the ADC has set both from the new conversion. Probably slower than...
- Read ADCL before ADCH: reading ADCL locks both until ADCH is read and ensures(verify) both come from the same conversion.
- poll for complete conversion (ADSC bit in ADCSR register; something like while ((ADCSRA & (1<<ADSC)) != 0) ; or while (bit_is_set(ADCSRA, ADSC)) ;
Note that in certain conditions, the delay is not necessary at all.
Faster-returning versions; interrupt stuff
Each conversion takes 13 ADC clocks, and the ADC is run at a division of the main oscillator. At Arduino's default rate, each conversion takes around 100 us.
So whenever you care about spending the most possible time on other code, and it's fine if a sample is slightly late, then consider doing what e.g. Mozzi's mozziAnalogRead() does:
It returns the most recently read value from the channel, and places a request for the pin to be read (which will be picked up in the background via interrupt handler) Since that function never does a conversion itself, it's guaranteed to return in much less time (~16us).
There are some footnotes, like there's no point to calling it faster than the ADC conversion speed, and new values are roughly one conversion's worth of time late.
Also, Free-running mode means the ADC continuously does conversions.
This is meant to be used with an interrupt handler called at the end of every conversion, as the main reason to do this is conversion at a very regular interval.
It also works out as a faster readout, though now there's more focus on you using the values soon enough.
Sampling while the AVR sleeps
There's an AVR sleep mode that allows ADC sampling during it, which makes the sampling slightly less noisy, because it avoids some switching noise next to it.
It's probably only interesting when you want to squeeze out a little more accuracy (at the cost of some AVR time/speed, and don't mind that this interferes with varied timing-sensitive things.
Code I've seen for this tends to resemble: (verify)
inline int read_adc_lownoise() {
ADCSRA |= _BV( ADIE ); // tell ADC to interrupt when finished
set_sleep_mode(SLEEP_MODE_ADC); // tell AVR the sleep mode to use
sleep_enable(); // allow sleep (doesn't trigger it yet)
do { // for robustness, loop until finished (because _any_ interrupt might wake the CPU, not just ADC completed)
sei(); // make sure interrupts are on. Are enabled to start with, but will be disabled two lines below
sleep_cpu(); // sleep the AVR (also triggers the ADC(?))
cli(); // avoids race condition in 'conversion finished' check (apparently; not sure what that note meant)
} while( ( (ADCSRA & (1<<ADSC)) != 0 ) ); // keep looping until the ADC tells us it is finished.
//done, so return to normal operation
sleep_disable(); // disallow sleep
sei(); // make sure interrupts are on
ADCSRA &= ~_BV( ADIE ); //turn the ADC interrupt off again - some code might not expect this interrupt
low=ADCL;
high=ADCH;
return (high<<8) | low;
}
See also:
Internal temperature
You can roughly measure the temperature of the AVR chip, for ...P variants of the AVR, such as the 328P (and quite possibly others(verify)), in that you can switch their ADC muxer to an internal temperature reference (against the internal 1.1V voltage reference).
This is fairly low resolution
- it varies ~138mV in a 130-degree-C range, which means it'll never be better than ~1°C, and that much only with some added cleverness
- you're measuring a diode, which is pretty linear (yay)
- it's not calibrated, so easily a few degrees off
- AVR temperature is a poor measure of the air around it - particularly since the AVR itself produces heat
See also:
- Your AVR's general reference PDF
- AVR122: Calibration of the AVR's internal temperature reference
- For code, see something like ChipTemp
Serial
On most AVRs except the Mega there is one serial port, on the Mega series there are four. The first of them (pin 0+1) is typically wired to the board's serial connector or USB via serial-to-USB.
Technical details
Serial-to-USB:
- Before the Uno this was an FTDI chip, typically the FT232R or the FT232RL (the SSOP variant).
- Since the Uno this is an Atmel chip, the Atmel 8U2. By default this fills much the same purpose as the FTDI (needs its own driver), but can be reprogrammed to do more than that.
You may be interested in datasheets:
- http://www.atmel.com/dyn/products/datasheets.asp?family_id=607 (find the applicable AVR in the list)
- http://www.google.com/search?q=FT232R+USB+UART+IC+filetype%3Apdf (should give the FT232R datasheet as first hit)
- http://www.google.com/search?q=Atmel%208U2
AVR and usb-to-serial chips seem do only 0..5V-level serial (and seem to work with 3.3V), so don't connect them to a classic -15..+15V serial port directly.
People seem to commonly use the AVR at 9600 baud (the FTDI auto-bauds to what the AVR uses, so you don't have to worry about it).
Some computer-side programs may only understand one of the more basic baud rates, one of 300, 1200, 2400, 4800, 9600, 14400, 19200, 28800, 38400, 57600, or 115200.
The AVR (and FTDI) can be run at arbitrary baud rates - useful e.g. for MIDI (at 31250Hz), and at up to two megabaud, but timing can be finicky, and it's not easy to usefully use that speed (when actually doing something useful on the AVR as well). There are Arduino oscilloscope projects that use the faster speeds (along with fast ADC sampling), to move off something like 360000 values per second.
Notes on the PC side
In linux the FTDI is most likely to go on /dev/ttyUSB0. If you use more than one, or plug out and in very fast, you might also want to try /dev/ttyUSB1 (and probably higher).
On windows, the FTDI driver seems to start putting ports at COM5 and higher, so the highest-numbered port is often the one you want.
If you have multiple Arduinos hooked up, you may want them to identify themselves (probably with some hardcoded identifier) if you want a program to use them consistenty.
If you want your code to take a best guess at returning the port that's probably the arduino, you might want something like the following (here for example for python and pyserial)
There is no cleverness to this, other than assuming that (linux) they're on /dev/ttyUSB*, and (windows) they're on the highest-numbered available port.
import serial
def best_guess_arduino_port( port_speed=9600, try_ports=None , **kwargs):
""" Returns a pyserial object for the first port that seemed usable,
or None if there are no ports that can be used (within the specified set).
try_ports - a sequence, of port paths and numbers.
Use of None (the default) means it defaults to looking relatively exhaustively.
In most cases you can safely give your smaller ranges, e.g.:
('/dev/ttyUSB0','/dev/ttyUSB1',11,10,9,8,7,6,5,4,3,2,1,0)
...meaning COM12..COM1 under windows, /dev/ttyS11../dev/ttyS0 under linux)
The idea behind the descending order is that a plug-in device tends to be assigned
a port above physical serial ports, so you usually want the highest-numbered port.
Any other (keyword) arguments are passed through to the Serial constructor.
"""
if try_ports==None:
try_ports = ['/dev/ttyUSB0','/dev/ttyUSB1']
try_ports.extend( range(31,-1,-1) # 31..0
ret=None
for try_port in try_ports:
try:
ret = serial.Serial(try_port, port_speed, timeout=0.15, **kwargs)
break # if the above succeeds, we're done
except serial.serialutil.SerialException: # probably the complaint that it doesn't exist
pass
return ret
Notes on the arduino serial code
With one-serial-port boards you have a predefined object called Serial. With mega variants, the second, third and fourth ports are Serial1, Serial2, and Serial3.
Hardware serial implementation comes from HardwareSerial.h/HardwareSerial.cpp, which uses Print.h/print.cpp and its behaviour (as do SoftwareSerial and some other things).
Writing through Serial.write() seems to be a single-byte write, in that there is no transmit buffer (probably a design decision to keep things simple, as you would have to serve that transmit buffer through interrupts, and in most situations it would not help enough to be worth it)(verify). Serial.print and Serial.println will mostly just Serial.write the the given data per character (and use the Print class to avoid duplicate code).
The Serial code stores received data into a 128-byte circular buffer in RAM. Serial.available() reports whether there is data in that buffer, Serial.read() returns data from that buffer.
This buffer also means you don't need flow control for incoming data if you can guarantee that buffer never fills up. Which depends both on data size and speed of polling, but can often be done.
If the AVR has to receive more than 128 bytes in the interval between you checking and emptying that buffer (available(), read()), you may wish to plan for loss, or to use flow control (when you can).
You can change the size of this buffer if you need to (in HardwareSerial.cpp), but realize that if you set it higher, you're trimming your already scarce SRAM. (Note: If you set it to ≥256 you may need to look for (u)int8-based things that now need to be uint16-based, which as of this writing means changing the declaration of available()).
When reading large chunks of data, you probably don't want to rely purely on available() - you want to know how much data you're waiting for, or have some delimiter in the data (e.g. a newline).
(People have reported that available() can report 0 immediately after reading data, even when you know there is data (for example when you read only part of the data reported to be available()). I don't immediately see a cause for this in the current code, so this may have been fixed, I don't know. If true, it means that code that does something like while (Serial.available() > 0) will be flaky too.)
See also (serial)
- Messenger library
TWI (I2C)
On basic Arduino boards: Analog 4 (SDA) and analog 5 (SCL).
On Mega boards, this is pin 20 and 21.
The AVR seems to be capable of speeds up to 400kHz. The Wire library uses 100kHz by default
If you run the AVR slower, the max speed lowers, e.g. 100kHz is probably sustainable on an AVR clocked at 2MHz, 400kHz down to perhaps 8MHz.
See also:
- http://www.avrbeginners.net/architecture/twi/twi.html
- IO_and_wired_communication#I2C and TWI for some general notes
Libraries
The Arduino IDE provides the Wire library (referring to TWI).
Notes:
- Wire has a 32-byte buffer it uses for queueing bytes to be sent, and for received data
- Wire.begin() enables the internal pullups on SDA and SCL.
- They are higher-resistance than you might wish for (AVR's datasheet mentions as 20..50kOhm, which is pretty high). This may work, but you may wish to disable them after adding your own pullups on the bus.
- Sending (in master mode): beginTransmission/endTransmission is for sending data. The former mostly sets some library stuff, the latter actually starts communication (sends the contents of the buffer that you fill through calling Wire.send())
- Wire's communiations does blocking reads/writes, which may potentially hang your programming (given specific wiring trouble)
- requestFrom() does a blocking read (available() and read() just report on the already-filled buffer)
See also:
Other libraries
E.g. the one here, which adds timeout. Master-only (right now).
SPI
If you want to talk to SPI devices, you can choose to use the AVR's ability to speak SPI, making it handle the actual on-the-wire communication, or you can choose to bit-bang it, which isn't as fast but can be done on any pins.
The AVR's hardware SPI is wired to:
- arduino pin 11 for MOSI
- arduino pin 12 for MISO
- arduino pin 13 for SCLK
Pin 10 is only special when using the AVR as an SPI slave. When the Arduino is a master you can use any pin(s) to select slave(s).
Note that MOSI+MISO+SCLK are also exposed in the 2x3 ICSP header (the other three there being Vcc, reset, and ground). Some shields use SPI via those pins, particularly when they want to be compatible with the Mega boards as well(verify).
It is not very hard to access the SPI hardware directly yourself (select, write byte to AVR register, wait for transfer to complete, read from AVR register and unselect), but there is an SPI library that makes your life a little simpler.
Speed can matter, as some devices do not do high-speed communication (and some are faster than the AVR). The speed division (and remember that SPI2X does not apply in all AVRs):
SPI2X SPR1 SPR0 Divisor Speed on 16MHz arduinos 0 0 0 4 4MHz 0 0 1 16 1MHz 0 1 0 64 250kHz 0 1 1 128 125kHz 1 0 0 2 8MHz 1 0 1 8 2MHz 1 1 0 32 500kHz 1 1 1 64 250kHz
See also:
Some usage examples:
- http://www.arduino.cc/en/Tutorial/SPIDigitalPot
- http://www.arduino.cc/playground/Main/EthernetShieldSDHardwareSPIMod
- http://klk64.com/arduino-spi/
- http://softsolder.wordpress.com/2009/07/18/arduino-hardware-assisted-spi-synchronous-serial-data-io/
- http://www.arduino.cc/playground/Main/SpiRAM
Direct port access (digital pins)
The GPIO pins on a ATmega328 and similar are on ports B, C, and D.
digitalRead and digitalWrite access these ports, but also do a bunch of work around it:
- figures out hardware port register for the pin
- figures out bit in that for the pin
- disables PWM timer if it was on
- for read: reads port, masks the bit corresponding to the pin, returns HIGH/LOW accordingly
- for write: reads port, changes the corresponding bit in the pin values, writes the result to the port
Direct port access refers to reading and writing out the registers directly.
This is faster than digitalRead/digitalWrite if you don't care about all of the extra work it does, and certainly when you want to change multiple than one pin at once.
You do have to assume PWM is off, and you are hardcoding for a specific AVR/board.
For each port there is
- a direction register (usually altered by pinMode())
- a register that corresponds to the value to write for each pin when it's in output mode
- a register that corresponds to the value read for each the pin
For a regular-sized board (Duemilinove and such):
Port D is digital pins 0 to 7
Port B is digital pins 8 through 13 (The highest two bits in the are not usable; connected to the crystal)
Port C are the pins mentioned as analog input pins 0 to 5 (6 and 7 only accessible on the Arduino Mini). These are general-purpose IO pins like the digital pins (hence they have the same-style access ports), meaning they can be used as digital pins whenever they're not used by the ADC:
- DDRC - The Port C Data Direction Register - read/write
- Write to PORTC (read-write), read from PINC (read only)
These pins can additionally be used by the 6-channel ADC (...In the PDIP package mostly seen in arduinos, there are variations), which can sample one pin at a time; the implementation of analogRead() is roughly:
- sets that channel, and the analog reference
- starts the conversion
- waits on the ADC status for that conversion to finish
- Reads ADCL, ADCH, and returns (high<<8)|low
Interrupts
AVRs have a bunch of interrupts, most of them internal (timer, wdt, spi, i2c, usart, adc), and a few external.
External interrupts, i.e. trigger based on change on GPIO pins, is registered like:
- interrupt number
- ATmega8, 168, 328 family has two:
- interrupt 0 means arduino digital pin 2
- interrupt 1 means arduino digital pin 3
- interrupt 4, 5, and 6 are "something happened on this port"
- Atmega1280 and friends have:
- interrupt 0 means arduino digital pin 21
- interrupt 1 means arduino digital pin 20
- interrupt 2 means arduino digital pin 19
- interrupt 3 means arduino digital pin 18
- interrupt 4 means arduino digital pin 2
- interrupt 5 means arduino digital pin 3
- TODO: complete this list
- ATmega8, 168, 328 family has two:
You can configure this with attachInterrupt(interrupt, function, mode)[18], which assigns a handler for an interrupt (...number).
where
- interrupt - the number above
- function - what to call call
- mode:
- RISING: low-to-high edge
- FALLING: high-to-low edge
- CHANGE: any edge
- LOW: trigger whenever low (continuously?(verify))
It seems you can actually use any GPIO pin for on-change (-only) interrupts. While there is an interrupt vector for every pin, all pins on a port share the same interrupt, you'll need some code checking which pin generated each interrupt.(verify)
Notes and caveats:
- You want to avoid noise triggering interrupts.
- particularly from floating; a pull-up or pull-down is typically enough here.
- Any globals you alter from an interrupt function and also access from your regular code should be declared volatile[19].
- interrupts are disabled while in an interrupt handler (by default)
- This means everything that relies on interrupts:
- PWM pauses
- millis()
- delay()
- serial receiving will be postponed - and may lose bytes if you postpone too long
- any other communication will probably make a timing mistakes
- so keep your interrupt handler as fast as possible. Consider any work that isn't time-critical to your main loop
- you can enable interrupts while in an interrupt handler , but this allows nested interrupts, which should be used with care
- delayMicroseconds() turns off interrupts while it's running
- an interrupt takes at least 3 clock cycles (verify) to get started, plus the time it takes to save and restore registers.
- If you want to be sure some code is not interrupted (may be necessary when you're thinking about signal generation with microsecond-scale accuracy), you can temporarily disable interrupts
- Note this means you cannot rely on timekeeping anymore, and while you can often fix that for your code, this may make timing/interrupt behaviour worse for something else - including anything served via interrupts.
Toggling interrupts:
- disable globally with cli() or noInterrupts() (difference?(verify)
- re-enable globally with sei() or interrupts() (difference?(verify)
You can also more selectively mask out interrupts, but this tends to mean lower-level register tweaking. If you're comfortable with that you probably already read up and know what you're doing anyway.
See also:
Timers
The AVR used in the basic arduinos have three (more, on some boards) timers/counters (and a watchdog timer).
The Arduino code uses one of these for its timing and delay functions (interrupt handler does simple counting), and all three for PWM.
All can be used more manually and flexibly, but you'll want to know how that affects timekeeping and PWM.
ATmega8, 168 and 328:
Timer0
- 8-Bit
- Can do PWM (Phase Correct, and a simpler variant)
- Can trigger interrupts
- can be prescaled (divided) by 1, 8, 64, 256, or 1024
- Arduino uses it for PWM (pins 5 and 6), millis(), micros(), delay()
Timer1
- 16 bit
- Can do PWM (Phase Correct, and a simpler variant)
- Can trigger interrupts
- can be prescaled (divided) by 1, 8, 64, 256, or 1024
- Arduino uses it for PWM (pins 9,10)
Timer2
- 8-Bit
- Can trigger interrupts
- can be prescaled (divided) by 1, 8, 64, 256, or 1024
- Arduino uses it for PWM (pins 3 and 11)
ATmega32u4 has much the same features in a timer0, timer1, (no timer 2), timer3, and adds a timer4
ATmega1280 and friends:
See also:
PWM
ATmega8, 168 and 328 have two 8-bit timers and one 16-bit timer (by default used as 8-bit too), which enables six PWM-capable pins.
ATmega1280 and friends have a bunch more timers (6?(verify)) and PWM pins (15?(verify))
ATmega32U4 has one 8-bit, two 16-bit, and one more specialized high speed 10-bit (up to 7 PWM pins combined(verify))
This refers to hardware PWM based on internal timers/counters, comparators, and the interrupts they can trigger -- you can bit-bang PWM on more pins if you can spare the CPU and are fine with the lower speed).
AVR PWM is often a little more capable than arduino exposes; the API exposes what is uniform behaviour for more AVRs and boards, and only partially exposes the further capabilities of some. (also, for some variants you would need to alter the timer Arduino reserves for its timekeeping -- you can do that, but it's more advanced tweakery)
Arduino libraries default to 8-bit PWM, a higher prescaler (so slower speed), and phase-correct on most pins, making for max ~490Hz for many boards (~980 Hz on fast-mode pins, and some boards deviate[20]).
You can get up to a few kHz 8-bit PWM on many AVRs, and get 16-bit on some, but you have to configure it knowing the AVR and the side effects. (analogWriteResolution() just for Due, Zero, MKR, i.e. the ARM-based boards), on most other boards you have to do it more directly.
See also:
- PWM
- http://www.arcfn.com/2009/07/secrets-of-arduino-pwm.html
- https://arduino.stackexchange.com/questions/12718/increase-pwm-bit-resolution
- ATMega328 pdf page 94ish
- https://playground.arduino.cc/Main/TimerPWMCheatsheet/
More and other IO
If you want to control more separate outputs, options include:
- IO expander ICs
- often something like 4, 8, 16, 24 digital ports
- connected via something like I2C, SPI or such
- can sometimes be used for well-timed IO independent of the uC
- can sometimes also be a handy way to connect different voltage levels (and add 5V tolerance to a 3.3V device)
- some are PWM-capable
- state at startup may be more defined than a uC's GPIO
- Another AVR (there are simpler/cheaper variants, such as the ATtiny series)
- useful if you want to PWM or do something else complex, otherwise can be a little overkill (then again, it's not horribly expensive)
- ...although the most stripped-down AVR+oscillator setup should cost you no more than some other solutions
More inputs:
- IO expanders
- ADCs
Other IO:
- MIDI is a serial protocol at 31250 bits per second. You can make the hardware serial do that, so you mostly just need the right DIN socket/plug. [21]
- obdev's v-usb-stack is a code-only implementation of a (low-speed) USB device. (see e.g. [22] for some projects that use it)
Bit-banging serial
Serial is switching things on and off at a relatively slow rate, so if you can make it regular enough (it's asynchronous signaling, meaning no separate clock), you can speak serial on any GPIO pins.
It's less efficient than hardware serial, and easier to have specific feature limitations and runtime issues with.
For example, even the libraries that can receive on multiple pins can only do so on one at a time.
Can still be very useful for informed use,
for example send-only,
or question-response so that you know when you have to receive, and can ignore the pin most of the time)
Or temporary use, e.g. spitting out some debug on software serial, when you need the hardware serial for the real design.
...though keep in mind that at lower baud rates you'll probably need to use for bit banging to be stable, things take more than a few milliseconds to send out or receive.
Arduino's own
(SoftwareSerial.h)
Can't go faster than 9600.
Baud rates of 1200 and lower noticeably affect millis() timing. People seem to report that 2400 is a reliable choice.
The main loop has to pay attention to incoming data,
meaning data sent to use while there is no read() listening is lost,
there is no receive buffer (there's little point),
available() doesn't work,
and read() blocks until data arrives(verify).
Also, it may itself disturb (other) time-critical code you may have, because it uses delayMicroseconds() for timing, which implies that it disables interrupts frequently (which also affects millis()), even though it's for short amounts of time.
AFSoftSerial and NewSoftSerial
(AFSoftSerial.h, NewSoftSerial.h)
AFSoftSerial was a solid improvement over older SoftwareSerial, which was then further adapted into NewSoftSerial.
Since Arduino 1.0 (~2012), NewSoftSerial was adopted as the implementation of SoftwareSerial, so you should no longer have to care about this.
Uses pin-change interrupts to receive data, and a (64-byte) receive buffer.
Spends more time in its interrupts, meaning less time for your program but also that it it's harder to mess up.
Supports more baud rates than SoftwareSerial.h code does - mostly higher ones.
http://arduiniana.org/libraries/NewSoftSerial/
If you get garbage characters
- Both sides should share ground. If not, you may see slight to total mangling of your data.
- Overly high (or low) baud rates may be more susceptible to certain types of trouble (may also depend on the rest of your code). Check documentation for support, and forums for reports about what works better than others.
- Anything that uses interrupts may interrupt in the middle of reads and writes.
If the interrupt fires rarely, you'll probably see mostly good transmission and occasional garbage. If you have a common and unavoidable interrupt (e.g. external interrupt), this can make software serial effectively useless.
Semi-sorted notes
ATmega8U2 / ATmega16U2 / ATmega32U2 notes
Boards up to the Duemilenove used the FTDI FT232(RL) USB-to-serial chip (or ones with similar function, like CH340 on cheaper chinese copies).
Recent Arduino boards like the Uno and Mega 2560 tend to instead use separate Atmega8U2 or Atmega16U2. (Arduinos with a U2 will often have a separate 3.3V regulator (~150mA), because its ~50mA of 3.3V previously came from the FT232)
The Leonardo and Micro are a single ATmega32U4.
These U2s and U4s are programmable AVR themselves.
Leonardo and Micro
The main microcrontroller is a single ATmega32U4 (note: U4), doing both the main work and talking USB.
These will easily present as a composite USB device, so you can e.g. have it be a serial port and HID device with minimal bother. (In Arduino naming, its Serial is the usb-serial interface, Serial1 the board-exposed hardware serial pins)
When a separate chip
On the Uno and such, there is a main AVR, and a U2 with default firmware that makes it a USB-to-serial device - but you can make it act like vairous other USB devices (like MIDI; HID like keyboard, mouse, joystick, MIDI; disk). (These 8U2 and 16U2 have most pins unconnected, at least on Arduino boards) with more work.
On U2 programming
DFU (Device Firmware Update) refers to the protocol used to update USB device firmware - a wider USB standard, used on a whole bunch of devices. In our case it would update the ATmega8U2 / ATmega16U2 / ATmega32U2's flash, i.e. firmware.
It's useful to us because it's a separate protocol over the same wire, making it easy for us to distinguish between updating the U2, and the main AVR over serial.
(some different details on a Leonardo, given what it is)
Note that once you reprogram the U2 to be/default to something other than USB-serial device,
the one-click IDE of the main uC upload probably won't work (unless you considered it).
So you may wish to see if your chosen firmware can e.g. do usb-serial as well, or do it based on a jumper.
There are more options to you - e.g you can also use ISP for either, as the ICSP headers for both are exposed. Pick the one that makes the most sense to you.
https://www.arduino.cc/en/Hacking/DFUProgramming8U2
https://code.google.com/archive/p/unojoy/wikis/GettingStarted.wiki
http://ww1.microchip.com/downloads/en/DeviceDoc/doc7799.pdf
On U2 firmware
Most people seem to use firmware based on LUFA (apparently arduino itself uses its usb-serial(verify)).
http://www.fourwalledcubicle.com/LUFA.php
http://blog.asdfa.net/foot-controller-v2/
https://github.com/harlequin-tech/arduino-usb/tree/master/firmwares
MIDI:
- Moco[23] is the idea that you can have the uC speak regular serial-MIDI (which you can also put on a plug) and have the U2 translate that to usb-midi.
- which technically imposes a 31kbaud bandwidth limit where you can avoid it, but unless you're doing things like MPE you're probably fine
http://morecatlab.akiba.coocan.jp/lab/index.php/aruino/midi-firmware-for-arduino-uno-moco/?lang=en
Controllino
(...though I stubbornly type Controlluino)
Controlluino is three arduino derived series (MINI, MAXI, MEGA)
(Apparently based on the Arduino MEGA ADK )
- DIN rail mountable
- power can be USB, 12V or 24V, and inputs seem to be robust to that (but the X1..X4 things are 5V)
- has a bunch of relays
- thermal overload detection (relays and board)
- RTC that should keep state for around 2 weeks unpowered
Analog input is voltage-divided to be scaled to the input voltage.
Digital inputs seem to be robust to the supply voltage
- with 12V supply, <3.6V is low, >9V is high
- with 24V supply, <7.2V is low, >18V is high
(suggesting CMOS-level level shifter buffer?)
Digital outputs are either High-Side (mini, maxi). The maxi also has Half-Bridge.
They seem to be buffered in that you can pull 0.5A from each.
The relays can be used for DC, 230V, or SELV.
There are separate blocks because safety regulations say you may not mix these.
Ethernet, where present, seems to be classic WIZnet W5100 style, connected via SPI.
SPI is shared with the RTC, so you have to chip-select each. If you install the Controlluino Ethernet library, this will be handled for you.
See also:
Data types
- char: 8-bit, signed
- byte: 8-bit, unsigned
- int: 16-bit (signed or unsigned)
- word: same as (unsigned) int
- long: 32-bit (signed or unsigned)
- float: 32-bit, apparently standard IEEE754
- double: the same as float (at least currently)
You can define (only 8-bit) literals in a bit-wise way, like B10011001
Print, String, and such
String types
You're coding C, so the most basic string type is C's null-terminated array-of-char.
Arduino provides a String class (formerly TextString) which is mostly convenience wrapping around what internally is still a C string
- adding conversions and operators and such, e.g.
- converting from other types (e.g. String(13, HEX);)
- convenience functions for
- comparisons (compareTo (basically strcmp), equals, equalsIgnoreCase)
- basic testing and searching (e.g. startsWith, endsWith, indexOf, lastIndexOf)
- a few alterations (e.g. concat, replace, trim, toUpperCase, toLowerCase)
- some others (substring (copy), toFloat because sprintf doesn't handle it(verify))
- knows how to copy data in from PROGMEM strings (eases progmem use a little)
- c_str (reference to the actual c string, use with care), getBytes (basically strncpy)
PROGMEM strings aren't strings directly; they are offsets into flash rather than pointers into RAM, so everything that consumes these must know that and interpret them that way.
- ...which includes String, and Print (see below), which are decent reasons to use them over bare C strings.
- some libraries make a point of supporting them, but how exactly has varied over time, so pay some attention (in part due to the way it's typed)
Printing to devices (and memory)
The Print class (from Print.h/Print.cpp) is also the base of hardware Serial. Other libraries may also choose to do extend Print (e.g. SoftwareSerial and some LCD libraries), so you can often .print() and/or .println() to those, and that base code will handle the built-in types.
This is useful in that Print deals with char arrays, String, and PROGMEM strings.
The Print class's print() and println() calls are heavily overloaded, to take
- char[]
- String
- signed/unsigned char/int/long
- for integer data it's a little ambiguous what you might want, so it takes extra parameters. Roughly:
- DEC, HEX, OCT, and BIN ask to print text assuming those respective bases (seems to cast the value via a long)
- BYTE casts to a char and prints out the value as a byte. Meant to write bytes out as their binary selves and not represented as text.
- for integer data it's a little ambiguous what you might want, so it takes extra parameters. Roughly:
- float
- progmem strings
- Printable things - an interface that lets you extend your own structs to hand them into print(). In the Arduino libraries, e.g. IPAddress uses it.
so they will generally pick up the type.
Writing to strings in memory
To keep RAM use low, it is often useful to write out string fragments in parts, as soon as you can. In a bunch of cases you can rely on Print behaviour (HardwareSerial, NewSoftSerial, LiquidCrystal, others), but there are various other cases where you need to compose complete strings before using them.
Options you have
- rely on the behaviour around String (see below)
- limits: you should probably know a little about how this works, and some cases it can break
- combine things via sprintf()
- limits:
- seems to pulls in up to 2k of code (if it wasn't already) - sometimes formatting libraries may be preferable(verify)
- won't do floats without some hackery (e.g. copy out Print's printFloat code into your sketch).
- Consider PString, which is a Print style interface to writing to a (preallocated fixed-length) char array, and adds a little string safety.
- A library. Apparently adds 100 to 600 bytes of code.
- String's + operator is is overloaded to concatenate thing
- limits: doing multiple will create unnecessary things along the way, so this is convenient but not RAM-efficient.
- String with reserve() and += seems to avoid that (verify)
More readable string code
Note that whether it's C string functions or Print-derives things, this this often leads to doing things in many lines, like
Serial.print("The button was pressed "); Serial.print(counter); Serial.println(" times");
...which may be efficient, but can get tedious.
You might care about things like Streaming, which is syntax-fu that allows
Serial << "The button was pressed " << counter << " times";
(Streaming could also be combined with PString or such to print to memory strings(verify)).
Bootloader stuff
The bootloader is convenient, but not required.
Without the bootloader, you get to use the flash space it would use, and save a little time at bootup that they wait for new uploads.
With a bootloader, you can use the plain serial port (rather than ISP or JTAG, which is more wires) to upload new sketches.
The bootloader describes the first 512 bytes to 2KByte of code in an Arduino's AVR's flash.
The bootloader runs when the Arduino is powered up, or is reset. It does a few basic bits of setup most wouldn't care about, but more importantly:
It listening on the serial port for a second for something that sounds like Atmel's STK500 protocol. If valid, this is an upload of code that needs to go to the rest of flash.
If not runs the last-stored program.
What the (USB) Arduino boards added to this is tying the FTDI chip's DTR line to the AVR's reset, so that the serial port can do a timed reset yourself as you did on older boards.
There are also some variations on the bootloader, and modifications you can do. For example,
- the no-wait bootloader only looks for programing after reset, not at powerup (makes sense on USB arduinos)
- optiboot is mostly smaller (512 bytes), and can be made faster (the basic bootloader runs at speeds like 19200)
Programming the AVR without a bootloader is also not very complex.
The cheapest, if you have another arduino, is to program it as an ISP programmer, see [25].
Or even using ParallelProgrammer via a standard parallel port -- except having those on PCs (and particularly laptops, has gone out of style so you'ld probably need a USB-parallel adapter, so it's not the handiest.
If you do this a lot, then you may like something dedicated (and probably with a plug that fits on the ICSP header, it's easier). There's a bunch of options, including
- AVRISP mkII: USD ~35.00
- USBtinyISP: USD ~25.00
- an STK500 board
See also:
- http://www.dl1dow.de/inhalt/arduino/bootloader/e_arduino_bootloader.htm
- http://wiki.ullihome.de/index.php/Hauptseite
- http://www.ladyada.net/library/arduino/bootloader.html
- http://arduino.cc/en/Hacking/Programmer
- http://jtxp.org/tech/tinysafeboot_en.htm
Timing and timekeeping
millis():
- milliseconds since program start
- returns an unsigned long (not an int)
- overflows every 49 days (232 milliseconds)
micros():
- milliseconds since program start
- returns an unsigned long (not an int)
- overflows every 71 minutes (232 microseconds)
- resolution of four microseconds (increments with that much)
More on millis
When doing intervals, it's generally best to use subtraction, like:
if ((millis() - last_trigger_time) >= duration ) { action; last_trigger_time=millis(); }
This will work around integer overflow, roughly because subtraction overflows in the same way. (This assumes the wraparound is the integer overflowing, and not coded to wrap earlier, as it was in Arduino 0011 and earlier. It also assumes your duration is shorter than 2**32 milliseconds, i.e. 49.71 days).
There are slightly more explicit ways of dealing with wrapover issues. TODO: work out in more detail
Notes:
- The above also is a way to avoid doing intervals via blocking delay(), which in some situations you will need to get away from.
- Interrupt handlers generally pause timekeeping, which makes the time counters run slower
- Keep in mind that millis() returns an unsigned long (uint32_t), and storing that in a signed long (or in an int) will lead to weirdness.
- the code above will actually end up having an interval slower than duration, because it compares against the end of the last action.
- you may not care about that drift until it's large and/or variable
- you can also do last_trigger_time+=duration and/or set last_trigger_time before that code
- ...in either those cases, consider what happens when your code takes a full interval of time (trigger continuously and potentially not allow other code), or a signification portion of it, and what you want to happen
When you want to do things at regular intervals, you could write code like:
wait_until=now + interval; while (millis() < wait_until) { // wait }
however, this will glitch around wrapover time (it triggers early(verify), TODO: explain why).
more on micros
More on timekeeping
The timing for millis() and micros() is not accurate enough for long-term timekeeping.
This isn't specific to the functions, or to arduinos, but to oscillators in general.
Don't count on any basic oscillator doing much better than on the order of 50 ppm or so {{{1}}}, which means it'll be off by up to ~4 seconds per day. Some are better (and pricier), some is constant and can be calibrated away once you measure it, some of it depends on measurable things like temperature, some of it just is variable.
Getting accurate times
- You could buy a receiver for radio-frequency time
- This tends to mostly be a ~EUR10 antenna, you still have to do a bunch of receiving.
- that said, this is arguably the easiest way to not drift over the long term
- if you have something that has a GPS receiver, use that - GPS is one of the best time sources available in general
- however, while the module itself has very accurate time, not all make it easy to get it out - read up on 1PPS outputs
- also, GPS is a relatively expensive component to get just for this
- if you have Ethernet/Wifi access anyway, you can interface with NTP servers
- if not, it's a complex and pricy way to go
If instead of regular corrections you want to set time once, then keep it accurate on its own for a long time,
there are (tiny-battery-backed) clock modules, e.g. TWI / I2C ones. You'll still have to set them,
- they may still be no better than 10ppm (approx a second per day)
- some of them perhaps 2ppm (1 second per ~5 days, approx. 1 minute per year)
A few modules you interface with have built-in clocks for some reason, and you may be able to use that
Arduino Time library is a convenience library that lets you set a time (from an external source) and the continues counting using the AVR timers. If using GPS or radio time, and syncing regularly, this tends to be more than enough.
There is some code to interface it with an RTC module, with NTP (using ethernet shield), and from GPS.
Power saving
See also: http://www.arduino.cc/playground/Learning/ArduinoSleepCode
Arduino itself can power-save by sleeping. A sleeping AVR can only be woken up by interrupts (or a reset).
If you do this to save power, note that some of the basic power draw comes from the regulator (say, a 78xx series always uses ≥ ~10mA)(verify), so the AVR isn't the only thing you should be looking at.
The arduino has five sleep modes:
- SLEEP_MODE_IDLE
- stops main CPU, IO still on (least savings)
- useful when you want to do work only in reaction to incoming IO (...interrupts)
- SLEEP_MODE_ADC - 'ADC Noise Reduction Mode'
- stops CPU and IO
- ...to minimize switching noise during ADC sampling
- AVR wakes up again when the ADC interrupts to signal 'conversion complete'
- SLEEP_MODE_PWR_SAVE (verify)
- SLEEP_MODE_STANDBY
- everything disabled except internal oscillator (when you use it in the first place)
- useful when you want faster startup than regular (verify)
- SLEEP_MODE_PWR_DOWN (lowest-power mode)
- saves registers, freezes internal(?) oscillator (verify)
See also
Relatively board-specific notes
Seeduino
Seeeduino mega
ATmega 1280-based board.
Basically a cheaper and smaller variant of the the Arduino Mega.
Seeeduino stalker
Board with
- solar charging a lipo battery,
- microSD card,
- Bee series socket
- grove connector
- RTC
A bunch of revisions since 2009. 3.1 is currently the latest
While some summaries mention the v1 uses the 168 and not the 328, both variants have been available.
Differences with arduino:
- The CR2032 battery holder is to keep the RTC holding onto the time while the board is not powered
- The 5-pin serial interface under the XBee socket is connected to (from top to bottom):
- DTR - you'll want this when programming the AVR (it does automatic reset), though with some timely pressing of the reset button you can do without it
- Gnd
- Bee socket's pin 3 (RX) and arduino pin 1 (UART's TX)
- Bee socket's pin 2 (TX) and arduino pin 0 (UART's RX)
- 5V (post protection)
- LEDs (top to bottom):
- D5: 'user define' - like the basic arduino pin 13 LED
- D3: XBee pin 15 (association / IO 5)
- D1 (green) & D2 (red): Power, reset
- Jumper near the center: Wire the Bee socket's pin 15 (XBee: association/IO 5) to arduino digital pin 2 (to let you attach an interrupt handler)
- 4x2 block, top right: I2C connectors, 3.3V and 5V
- SD socket is I2C, selector is on pin 10
I'm not sure what the default AVR code does, but I needed to program Blink to clear the serial line and talk to an XBee in the socket.
Arduino IDE and environment notes
Randomness
In general, the C rand() call uses the libc random() function(verify), which can be one of a few PRNGs [26].
There are others you could use, which can be useful e.g. if you want one of a known faster speed[http://engineeringnotes.blogspot.com/2015/07/a-fast-random-function-for-arduinoc.html ].
You should assume that that always generates the same sequence.
When getting a sequence that looks arbitrary enough, changing moderately quickly, that doesn't necessarily matter even if it is always the same.
Getting something random enough to probably be different each time is enough, is a little harder, but doable.
One way is to sample floating pins (ADC if you have it), checking that it's not always the same, and then using some average.
Getting something that will be different each time, and hard to influence externally, which you may need for secure solutions, is harder.
The ADC solution may be somewhat predictable (it may e.g. be you're using one of a few thousand seeds), and it may be influencable (with an electric field, or just grounding), so this is not ideal solution to e.g. generate tokens that should be entirely random, certificates, etc.
You now run into the question of good entropy sources, testing them, and the fact that that is slow. If you need this, read up.
Editor
If you don't like the editor much, take a look at PlatformIO, an extension to VSCode
Compilation environment
Different tabs
data types
AVR notes
Anything above that has no Arduino-specific details may be moved here
Some AVRs
To get an indication when it's useful to get specific variants: (Note that there are many AVR variants, and a single digit or letter difference can matter. The below is mostly a list of things you're relatively likely to see)
Some of the common AVRs, memorywise:
- ATmega328 (common on Arduino Duemilanove boards and comparable)
- ATmega168 (but now you'll usually see a 328 instead)
- ATmega8 (not seen much anymore)
- ATmega1280 (used on Arduino Mega (before late 2010, when Mega2560 and Uno were released))
- has 128KB Flash (8 KB used by bootloader)
- has 8KB (S)RAM
- Has 4KB EEPROM
- 4 USARTs intead of 1
- 16 ADC pins
- 14 PWM pins (verify) (six timers)
- 86 IO pins (though Arduino boards don't expose them all)
- ATmega2560 (used on Arduino Mega2560)
- has 256KB Flash (8 KB used by bootloader)
- has 8KB (S)RAM
- Has 4KB EEPROM
- 4 USARTs intead of 1
- 16 ADC pins
- 14 PWM pins (verify) (six timers)
- 86 IO pins (though Arduino boards don't expose them all)
If you're going for small and/or single-purpose, you can look at the ATTiny series. There are a lot of variations, a sliding scale of price versus features. Some of the ones I've looked at at:
- ATTiny84
- 8KB Flash
- 512B RAM
- 512B EEPROM
- DIP version is 14-pin
- ATTiny85
- 8KB Flash
- 512B RAM
- 512B EEPROM
- DIP version is 8-pin
- ATTiny45
- 4KB Flash
- 256B RAM
- 256B EEPROM
- DIP version is 8-pin
- ATTiny2313
- 2KB Flash
- 128B RAM
- 128B EEPROM
- DIP version is 20-pin
- ATTiny13
- 1Kb Flash
- 64B SRAM
- 64B EEPROM
- has only one timer
- DIP version is 8-pin
Keep in mind
- you'll probably want to program these via ISP (rather than the one click serial STK bootloader way), so you'll probably want to use one one Arduino as an AVR ISP (or buy/build your own ISP).
- the 8-pin versions can be very cramped. Even if the feature's there, it may be on the same pin as another you want.
Programming the hardware
All(verify) the real-world options build on one of the two basic ways to program an AVR:
- via a previously burned bootloader set up to program the rest of flash
- via ISP
Both are built on hardware-level AVR features.
- Programming via bootloader
The bootloader is an optional step in the AVR boot process. When used, it means a small portion of flash is run first, and allowed to write to the rest of flash.
The AVRs on arduinos have a bootloader that, after reset and before starting the program, talks stk500 (An Atmel protocol) via the serial port. This is how arduinos are updateable with a single mouse click (give or take - recent arduinos use the usb-serial to trigger a reset. Older arduinos required well-timed manual resetting).
Since the bootloader cannot overwrite itself, or set the AVR configuration (the fuses), this also means it's hard to get the AVR into an unresponsive state than with ISP.
The cost for this convenience is (usually) 1KB or 2KB of flash space.
- In-system programming
The lower-level way is to use in-system programming (ISP), which lets write write flash, change/add/remove the bootloader, and set the fuses (Atmel's name for AVR low-level configuration bits, see below).
The pins used for this are in the 3x2 ICSP header (alongside power, and ground, and reset). (These pins are shared with the SPI lines, which sounds similar but has little relation)
See AVR910 on more details on how AVRs do ISP.
Because ISP is simple, and its timing fairly strict, you need something more or less dedicated to programming, so your setup will often look something like:
PC <----- serial -----> ISP programmer hardware <----- SPI lines------> Target AVR (talking ISP)
These programmers tend to speak AVRISP protocol (see AVR069) and stk500 as part of that (verify).
The simplest way to get an ISP programmer is probably to use an extra Arduino with a sketch that makes it an ISP programmer.
You can get dedicated ISP programmer hardware for a little less than what an Arduino costs.
It is possible to bit-bang ISP, for example via a PC parallel or serial port (or e.g. an Arduino board you have lying around).
This lets you program AVRs with little more than a plug and some resistors - but it's very very slow, and sometimes fragile, but can be handy if you need to do just once or twice. (For example, the avrusb500 ISP programmer is designed to be bit-banged via its own FT232 - which means you don't need a programmer to program this programmer, you don't need a bootloader to be present - any old AVR chip will do)
In terms of hardware options and cost: (the cheaper of prices I've found for each, no shipping included, etc.)
- Bit banging
- Slow
- sometimes fragile
- you can buy a serial or parallel bit banger, often for less than EUR15. You can build your own for less
- bit-banging an arduino's AVR via the Arduino's FT232 can be done for almost free. You mostly need to wire some FT232 lines to SPI lines. Slow.
- It can be hard hard to find (friendly) software that does the actual bit-banging. In particular USB-to-serial-FT232 is hard to find. Doing so on a generic serial port is a little easier to find.
- ISP programmers:
- Arduino board, with an ISP-programming sketch[[27]]. If you have one around anyway, this is basically free. Arguably the easiest way to get a programmer, and to reprogram another Arduino.
- Sparkfun's Pocket AVR Programmer (~EUR10) - seems targeted primarily at 168 and 328, may not work for all others(verify)
- AVRISP mkII and clones (~EUR25) - sort of the classic
- USBtinyISP (~EUR15)
- avrusb500 (~EUR20)
- STK500 development board (~EUR60?) - makes it easy enough to program all of the AVR series (DIP variants) without having to think about it. But relatively pricy.
- AVR dragon development board (~EUR40?) - can be used as an ISP programmer, and as an AVR debugger itself
See also:
- Technical reference:
- AVR061 - stk500 protocol
- AVR068 - stk500 protocol, v2
- AVR069 - AVRISP mkII Communication Protocol
- AVR910 - In-System Programming
Fuses
AVRs have some low-level configuration bits, which Atmel calls 'fuses'.
I heave no idea why they're called that - they have nothing to do with fuses as the protective circuit element.
They can't be set via code, only by an ISP programmer.
There are two or three bytes worth of them.
Their bits's meaning can vary somewhat, mostly between AVR series. You'll probably want to use a fuse calculator to be sure you're using the right values for your AVR.
The sort of things you can set:
- Select the clock source - external, or internal, and the speed of the internal clock source
- Whether to use boot code, and how large it is (and implicitly what the boot vector is)
- Brown-out detection - basically the choice to stop working when the voltage dips, to avoid potentially working unpredictably.
- Can usually be set to 4.0 (for 5.0V operation), 2.7V (for 3.3V operation), and in some cases 1.8V.
- allow use of watchdog timer?
- Control whether the reset pin is treated as that, or as an I/O pin.
- Whether to keep or erase EEPROM memory when the flash is erased
- Enable serial programming / data downloading? (basically a read-only toggle that you'ld need to change before doing the actual programming)
Minimal AVR hardware
You can strip down the AVR to minimal requirements, for example for price in larger projects, or to use simple slave AVRs dedicated to specific time-critical tasks (consider e.g. that an Atmega8 costs ~EUR2).
You can use an AVR with little more than a power line (strictly not even an external clock source) and communication with your main AVR.
If you want to build an AVR setup from scratch (which you can't really call Arduino - Arduino refers mostly to the whole boards around the AVR uC) you'll want to know a few things, like:
For power: If you have a regulated 5V adapter, you can hook it up directly.
However, if you want to be robust to unregulated or higher-voltage adapters, add a 5V regulator such as a 7805 - and know that their minimum input is a little higher.
If you want to be robust to plug where you can make polarity mistakes (e.g. DC barrel plugs), add a polarity protection diode.
For the clock source:
- the no-component solution is to use the AVR's internal oscillator, which can be configured at a few different frequencies.
- However, even at its fastest this is significantly slower than the AVR's capability, and also somewhat more jittery than a crystal (to the point where it can affect certain types and speeds of communication)
- you'll need an ISP programmer to tell the AVR to use that oscillator (you may need one anyway, since AVRs generally don't come with Arduino-like bootloaders).
- A crystal plus two load capacitors
- Can be bought for at most EUR 3 (combined)
- Useful when you want higher speed than the internal oscillator, want a specific speed (e.g. for dedicated video signal generation), or need lower jitter than the internal oscillator or a resonator gives you
- A resonator
- Should cost around EUR 1
- more jittery than a crystal, so comparable to the internal oscillator, but if you want it faster than the internal one and don't need time-critical stuff, it's cheaper than a crystal.
See also:
- http://www.ladyada.net/learn/avrdevtut/fuses.html
- http://www.google.com/search?q=AVR54%3A+Run-time+calibration+of+the+internal+RC+oscillator
- http://www.scienceprog.com/programming-avr-fuse-bits-oscillator-settings/
- http://www.scienceprog.com/avr-internal-oscillator-jitter-research/
See also:
(If you're doing this for price, note you can get some cheaper versions of an arduino, e.g. boarduino))
JY-MCU
Dealextreme sells a 10-EUR AVR board, with little more than an AVR, a crystal, a 3.3V regulator, and pin headers - and adds a few buttons and LEDs. The USB port is just for power.
Watchdog timer (and software reset)
What and why
A watchdog is generally a "if I don't check in every X often, do Y" thing.
The AVR watchdog timer consists of
- its own clock source (~128kHz)
- a counter
- checking in ('feeding the dog') amounts to resetting it to 0 (with a dedicated opcode)
- ...and which, when at a configurable threshold...
- does one of (configurable)
- AVR reset, or
- interrupt, or
- nothing (verify)
Using it for reset is often a "my code can sometimes get stuck, and I want things to restart completely rather than risk being hung forever"
For this, configure it for reset, and have all parts of regular operation main loop (and everything that takes some time) check in.
Configuring for interrupt is often used either
- in combination with sleep (e.g. letting you wake the AVR every few seconds, to save power).
- for the interrupt handler you then also define (but note the general timers are often more flexible for this)
The watchdog can be used/enabled
- via registers (see wdt_enable, or look for WDTCR)
- via fuses (can't be disabled with code)
Reset
While the AVR can use the watchdog for an interrupt, avr/wdt.h is targeted only at reset(verify).
It defines:
- wdt_enable(timeout)
- which you also handle one of WDTO_15MS, WDTO_30MS, WDTO_60MS, WDTO_120MS, WDTO_250MS, WDTO_500MS, WDTO_1S, WDTO_2S, defined in the same file
- Some devices also have a WDTO_4S and WDTO_8S
- wdt_disable()
- wdt_reset() - feeds the dog. (actually a single inlined assembly instruction)
Notes:
- the times seem to be slower at 3V than at 5V
- Don't count on watchdog times being precise anyway.
- on many devices, the watchdog stays enabled after reset
- so if your setup() may take long, you may create an eternally resetting AVR. Consider feeding or temporarily disabling the watchdog during setup
- If you need a "do software reset" function, the easiest is probably to set a short timeout, and then just not check in. (if you think your sketch might have watchdog checkins anywhere, the easiest way to ensure it doesn't is to then do an infinite loop).
- consider the previous point
- Code varies a little for some AVRs.
- The Due has entirely different watchdog functionality (verify)
- Consider that long waits in your setup() may lead to perpetual resets.
- In some cases you want it to fail early and fail hard (e.g. don't get DHCP lease? keep resetting to make network state simpler to think about), but often enough you want to enable the watchdog after all your init code.
Interrupt
On memory
See #Some AVRs for the memory various AVRs have.
Note there is no protection between the uses of RAM - the stack will happily dip into the heap and overwrite heap data, .bss, and .data (see also the next section).
You probably want to make sure that the worst-case stack size will not clobber any of your data. So avoid (variable/unknown depths) recursion, and other things that could use a lot of stack.
Probably also avoid any (unbounded/unpredictable-sized) dynamic allocation, where you can.
Libraries will often pull in variables and take a few bytes of SRAM or more.
- Serial is one of the largest examples, with its 128-byte ringbuffer.
Note that literal strings will sit in RAM, so are often large users of RAM too.
If you use a lot of strings, one at a time, look to #On storing data in Flash to alleviate that.
When you use Serial, then easily ~220 bytes will be used before you start doing much. It's easy to have that be at ~400 bytes once you have a bunch of code.
You could argue that whatever you can do in static, pre-allocated variables (in .bss/.data) is less of an unknown than when you do it with malloc/free (on the heap), and that dynamic memory allocation on an uC is rarely necessary anyway.
Free memory
Basically, see http://www.arduino.cc/playground/Code/AvailableMemory
It lists the two basic common types of implementations:
- "figure out the difference between the bottom of the stack and the top of the heap"
Fast, because it's trivial math on some existing global variables.
It works because the default memory layout is:
.data variables .bss variables the heap (growing up) the stack (growing down from the end of memory)
That layout means there is a single chunk of memory that can be used by the stack and heap, and it makes sense to report that as free memory.
There are minor variations of the implementation. They look something like:
inline int available_memory() {
extern int __heap_start, *__brkval;
int v;
return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval);
}
</code>
or (arduino-specific?)
<syntaxhighlight lang="c">
inline int available_memory() {
if((int)__brkval == 0)
return ((int)&free_memory) - ((int)&__bss_end);
else
return ((int)&free_memory) - ((int)__brkval);
}
Downsides:
- assumes that memory layout. (Still, you'ld probably know about it if you use a different one)
- implementations are specific to compilation environments
- since memory will fragment if you malloc/free in a mixed way, this code will underestimate (but arguably less than the try-to-allocate method)
See also http://www.nongnu.org/avr-libc/user-manual/malloc.html
- "try to allocate the largest block you can (and free it immediately)"
int available_memory(int size=2048) { // start size
byte *buf;
while ((buf = (byte *)malloc(--size)) == NULL) ;
free(buf);
return size;
}
Setting the start size no larger than possibly for a given AVR makes the function finish faster. For ATmega328, 2K is a reasonable default. On some 1K is enough, on some you'll need 4K or 8K. (in practice, you could start 100-200 bytes lower, because the variables, stack, library use tend to sum to at least that much. But this will vary with your exact code, so the above is a decent default)
Intuitively this implementation feels iffy, particularly to programmers used to garbage collectors or relying on fancy low-fragmenting malloc implementations. Yet the malloc implementation here is a simple linked list thing(verify) and you immediately free after the first successful allocation, it just leaves things as they were before the call.
(some people report crashes?)
Downsides:
- it finds the largest contiguous free block, not the total; if you have previously mixed malloc/free sizes, it will underestimate (though perfectly estimate the largest single allocation you can do)
- It's slow, because it tries every size. You could make it a little coarser and faster by doing size-=2 or so.
External storage
EEPROM
For "keep things around" needs, note you can often get I2C EEPROMs for a few bucks per MBit (for example, the 24LC256 (32KByte) for under $2).
PSRAM
For more scratch-space needs, you can get SPI (/QPI) PSRAM for rather less than that.
See for example what the Teensy 4.1 does [28]
Flash, probably SD cards
When you want a lot of storage, SD cards tend to be a price-efficient option.
SD by specs must speak SPI so are easy enough to communicate with.
That's not their fastest interface, the library may not be optimizing for speed, and the nature of flash means that flushing small chunks is inefficient.
Even for good cases, don't count on more than 1..2mbit/s (particularly since you don't have DMA). Though you probably won't need more.
While you can use it as arbitrary blocks of storage, the arduino SD library speaks FAT16 and FAT32, and this lets you plug it into a PC and copy data off as files.
You can buy SD card sockets, or just solder the SD card to some wires (finicky for microSD).
SD card shields exist, for example this SD data logger shield, microSD shield (USD/EUR ~15).
You'll also find SD sockets on some shields with some other primary purpose (data transfer for the audio shield, a seemingly non-functional SD slot on the older wiznet ethernet shield, and apparently a functional microSD slot on the new ethernet shield)
And some smaller boards, sometimes just breakouts, sometimes with level shifters and regulators so that it's usable from 3-5V.
See also:
- http://blog.makezine.com/archive/2008/06/how_to_sd_card_readwrite.html
- http://www.youritronics.com/interfacing-sd-card-with-arduino/
- http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1206874649/8
On storing data in Flash (PROGMEM)
By default, variables are placed in RAM, because that's where all memory access looks.
When you have pieces of data you consider read-only, you can place it in flash instead,
and read it out from there whenever you need it.
This is partially assisted by the compiler / toolchain, in that there is some syntax that means "place pieces of data into flash, not RAM". You still have to explicitly fetch it from there when you need it, though.
Flash acts as slower, read-only memory, but there is more of it (e.g. 2KB SRAM and ~32KB flash for the ATmega328),
this can be useful when
you have data you can't all fit in RAM (or don't want to), and
you only ever need a smaller part of that data in RAM at a time.
Caveats:
- Read-only, so only for truly static data
- Flash is slower than RAM
- (also, the extra step of fetching it from flash to RAM before you can access it would be a little slower even if it were RAM speed)
- you must know you never need more of that data in RAM than you have free RAM
- in many cases that's trivial to oversee. In some cases this enables some really poor resource planning on your part.
- it sometimes won't reduce RAM use by much for practical reasons. You're basically limited by the largest thing you fetch from PROGMEM
- still useful for things like wavetables (you should only need one part at a time), exhaustive amounts of error messages (you only need one error at a time)
- unused RAM is useless. If you had the RAM anyway and didn't need it for other things, you made things slower for no gain.
- the placeholder that points to flash also takes space in RAM. Not much (it's a pointer-like thing) but this it makes little to no sense for primitive types
- ...but it's defined for all primitives in case you e.g. manually deal with arrays of them
- everything that uses it must be aware of progmem
- some libraries are, some are not.
Under the covers, it works by using storing pointers that just happen to be offsets within Flash. You can't dereference these as pointers, because that would look them up in the wrong place (RAM).
There are around two dozen provided functions to help take string/memory copying and allow "take actually read out the data from flash.
There are a bunch of helper functions of the "arguments can/must be PROGMEM" sort that let you port existing mem/string code (they include memchr_P, memcmp_P, memcpy_P, memmem_P, memrchr_P, strcat_P, strchr_P, strchrnul_P, strcmp_P, strcpy_P, strcasecmp_P, strcasestr_P, strcspn_P, strlcat_P, strlcpy_P, strlen_P, strnlen_P, strncmp_P, strncasecmp_P, strncat_P, strncpy_P, strpbrk_P, strrchr_P, strsep_P, strspn_P, strstr_P)
(Regular functions that take pointers will happily accept PROGMEM pointers and do completely the wrong thing with them.
For example, Serial.println( PSTR("Hello World One!\n") ); will compile and run, but will certainly not do what you want. Print is actually PROGMEM-aware, but it needs to be told to do so and that particular syntax doesn't do that.)
Note that PROGMEM is an AVR-gcc thing. Other compilers may implement the same idea, but differently.
See also:
- http://www.arduino.cc/en/Reference/PROGMEM
- http://www.nongnu.org/avr-libc/user-manual/group__avr__pgmspace.html
Some example code
64K border
If you have more than 64KB of flash and the (bootloader plus) pgm data is larger than 64KB, things will break unless you know how to work with it.