Programming AVRs - Part 2

I used to use the FT245R chip to program my AVRs but I was looking for something more comfortable. Like the Bus Pirate. Works great. No special configuration needed for avrdude. Simply say it’s using now a Bus Pirate and which USB port it shows at.

 

Update: It’s too slow, so I flashed the STK500v2 firmware, which means I tell avrdude I got an STK500v2. While it loses all the features of the real Bus Pirate, it’s magnitudes faster (and I have the AVR extended patches and updated the Bus Pirate firmware to 6.1, but it was still too slow for larger programs).

 
More 16 Segment LED Fun

I continued to look for a 16 Segment Font, alas, there seems to be none. The best I found are two videos on YouTube. So I finally made my own one, and for the benefit of the world, here the code for all printable 96 ASCII characters:

#include <avr/pgmspace.h>

// Segment bit order is (MSB) A1 A2 B C D1 D2 E F G1 G2 H I J K L M (LSB)
const uint16_t uiCharacterMap[96] PROGMEM =
{
 // SPACE ! " # $ % & '
 0x0000, 0x0300, 0x0110, 0x0fd2, 0xddd2, 0x95db, 0x8eb4, 0x0010,
 // ( ) * + , - . /
 0x000c, 0x0021, 0x00ff, 0x00d2, 0x0001, 0x00c0, 0x0004, 0x0009,
 // 0 1 2 3 4 5 6 7
 0xff09, 0x3008, 0xec41, 0xdc48, 0x01d2, 0xcd84, 0x1fc0, 0xc00a,
 // 8 9 : ; < = > ?
 0xffc0, 0xf1c0, 0x8080, 0x8081, 0x0c09, 0xc0c0, 0x0c24, 0xe142,
 // @ A B C D E F G
 0xfe83, 0x3049, 0xfc52, 0xcf00, 0xfc12, 0xcfc0, 0xc3c0, 0xdf40,
 // H I J K L M N O
 0x33c0, 0xcc12, 0x3e00, 0x038c, 0x0f00, 0x3328, 0x3324, 0xff00,
 // P Q R S T U V W
 0xe3c0, 0xff04, 0xe3c4, 0xddc0, 0xc012, 0x3f00, 0x0309, 0x3305,
 // X Y Z [ | ] ^ _
 0x002d, 0x002a, 0xcc09, 0x8b00, 0x0024, 0x7400, 0x0120, 0x0c00,
 // ` a b c d e f g
 0x0020, 0x8e92, 0x0b82, 0x0a80, 0x0e92, 0x0a81, 0x40d2, 0x8992,
 // h i j k l m n o
 0x0382, 0x0a00, 0x0812, 0x001e, 0x0412, 0x12c2, 0x0282, 0x0a82,
 // p q r s t u v w
 0x8390, 0x8192, 0x0280, 0x0444, 0x04d2, 0x0e02, 0x0201, 0x1e02,
 // x y z { | } ~ DEL
 0x002d, 0x3450, 0x0881, 0x4492, 0x0012, 0x8852, 0x0128, 0x00ff
 };

Note the PROGMEM extra attribute which is helpful on a Harvard CPU like the ATmega are. Without it, the table will be copied and used in RAM. 192 Byte in total does not sound too bad, unless you use an ATmega168 which has only 1 kByte. To access this type of special memory, you need the include file and to access the array, a special function is needed:

a=pgm_read_word_near(&(uiCharacterMap[i]));

It’s all documented here. On a normal CPU (more RAM, no Harvard architecture) you would not bother.

I thought about adding “spinning things”, but those are trivial to make and I thus leave those as an exercise to the reader.

 
EAGLE - First Impression

EAGLE is probably the most common software used for PCB creation. Many years ago I saw a professional using it on DOS. Looked good and not simple at all. Back then creating PCBs was quite expensive too, so I never thought about making them myself. For most of my purposes a breadboard works well enough. For a bit more permanent things I have those which is just the same layout and connections, but for soldering.

For a bit more complex things though the problems of breadboards become an issue:

Distressing as it may sound, solderless breadboards can be very flakey, especially as they age. If you’re having problems with your circuit, it could be that the little metal clips on the inside aren’t working well. Try poking it with your finger, or moving it to a different section.

It’s still great for small tests, but for permanent things, expect a  low reliability.

Anyway, for a large clock using those quite large 16 segment LEDs, breadboards don’t work well: a single digit is too large and needs 2 breadboards, with some gap between. The next digit will have a lot of gap between the previous digit. All in all, not optimal.

In the end, and because that clock will be permanent, some soldering is needed. Soldering without PCB is not fun: lots of burned fingers, a messy layout and errors are my experiences. A PCB would be sooo much nicer.

Enter the world of low-cost PCB manufacturing. The low price is done by merging small PCBs onto one larger one, which splits the setup costs by many users. In the end, a 5cm by 5cm board with 2 layers of copper, solder stop mask, silk screen is amazingly US$10. Larger ones get a bit more expensive, but it’s still cheap. And I get 10 boards. There is some extra delay of course as all those small boards needs to get pooled to one large PCB.

What does it have to do with EAGLE? EAGLE is the program which is recommended to create the PCBs from. Other programs work too (e.g. KiCAD), but most tutorials are for EAGLE and most services can handle EAGLE files, either directly or indirectly. EAGLE also runs on Linux.

So I downloaded EAGLE v5.1 as

aptitude install eagle

did not work.

And then the steep learning curve of EAGLE started. At first I could not do anything at all; this is an old program and it shows: hardly anything works as expected. This tutorial helped me to get started. It stops where the layout starts though, so here my additions:

  • If you want to fill a plane (e.g. with GND), make a polygon on the board layout of where you want it. RMB (right mouse button) will show the properties of it. Add Isolate to have a distance from signals. 0 is a stupid default value.
  • Use the name command (RMB on the polygon) to merge it with the GND signal (of whatever signal you want to have connected).
  • To start the autorouter, click on the autorouter button (yeah, it sounds very straightforward). Any signals which could not be connected will be air-wires (thin yellow lines).
  • To rip up one trace, use RMB and delete.
  • To rip up all traces, click on the ripup button, then on the go icon (the traffic light next to the STOP icon).
  • Click on ratsnest to see the polygon being poured.
  • It’s fun to watch the autorouter on slightly complex layouts. If your design is complex or space is too limited, you might end up with some air wires. Try to relocate some components.
  • The rules file document dictate what the manufacturer can do. E.g. available drill sizes or capabilities like thinnest possible traces etc. The default values are quite conservative. To load, click on Edit/Design rules and load the rules for your service.
  • The CAM file creates the layers (copper per layer, silk screen, solder stop masks). It creates all the needed Gerber files.
  • gerbv is a nice utility to display those created Gerber files layer for layer. Use it recommended. While you’ll unlikely stop small errors (e.g. a single traces missing), you’ll find layers completely missing.
  • Never only open schematics or board view. Always have both open, as otherwise you create inconsistencies which are not fun to manually fix. As long as both are open, any modification on one window will update the other one.
  • The free version of EAGLE is limited to 8cm by 10cm and 2 layers. Good for small stuff. For hobby use (non-commercial) you can get the standard edition (10cm by 16cm, 6 layers) for modest US$125. I doubt I’ll need those capabilities, but if I do, I know there’s a cheap upgrade path.
TODO on my side:
  • Understand the bus feature. That will clean up the schematics a lot.
  • Name signals
  • Create library items (e.g. for the MCP23017 I found no usable library so I had to use a generic 28 pin DIL socket with no proper names and no knowledge of what is input or output or GND or Vcc.
 
16 Segment LED

16 Segment LEDs are (obviously) somewhere between 7 segment LED and graphical displays. Quite micro-controller friendly as they do not need a lot of memory or CPU performance or external hardware, but they look better and can display the full range of alphanumerical characters.

For those using these type of displays, a lot of time is spent trying to find “bitmap” data for characters. Some special ICs contain those already, but that I have none of those. After searching for font data and coming up empty-handed, I created my own:

// Segment bit order is A1 A2 B C D1 D2 E F G1 G2 H I J K L M

const unsigned int uiCharacterMap[0x40] =
	{
//	0	1	2	3	4	5	6	7   8   9
	0xff09, 0x3008, 0xec41, 0xdc48, 0x01d2, 0xcd84, 0x1fc0, 0xc00a,	0xffc0, 0xf1c0,
	0x7712, 0x3300, 0x6742, 0x7740, 0x3350, 0x5750, 0x1752, 0x7300, 0x7752, 0x7350
};

Those are digits 0 to 19 (yes, not just 0 to 9). Unfortunately 20 does not work, and neither do 22 or 23. Here a video of the 20 numbers: http://www.youtube.com/watch?v=pI8WTVAadiA

 
I2C fun

I realized quickly that the MultiAVR library is only the core Arduino library. What is missing are the non-core ones, like the I²C library (AKA TWI or “Wire”). Expecting a serious re-write, I was pleasantly surprised to see that I had to make exactly one change:

*** /usr/share/arduino/libraries/Wire/utility/twi.c     2011-01-10 14:28:07.000000000 +0900
--- /home/harald/workspace/BlinkSanguino/utility/twi.c  2011-09-24 22:38:53.283676108 +0900
***************
*** 69,74 ****
--- 69,77 ----
      // as per note from atmega8 manual pg167
      sbi(PORTC, 4);
      sbi(PORTC, 5);
+   #elif defined(__AVR_ATmega644P__) || defined(__AVR_ATmega1284P__)
+     sbi(PORTC, 0);
+     sbi(PORTC, 1);
    #else
      // activate internal pull-ups for twi
      // as per note from atmega128 manual pg204

and that was it.

In one day I was able to talk to:

  1. the LCD (16×2 characters)
  2. an PCF8574A (8 bit I/O)
  3. an PCF8583 (RTC)
  4. an MCP23017 (16 bit I/O)
The last one has a strange behaviour: Port A and B seem to be swapped.

 

 
Programming AVRs

Programming an AVR microcontroller can be done by many ways. If you have an Arduino bootloader like this, it can be done via serial interface. If you use the one and only serial interface though, that’s not the best option.

One method is to use an existing Arduino, which is neat if you have one or want to use one dedicated as programmer.  Or you could get a dedicated programmer or something more generic. Or an FT245R. Which I happen to have flying around.

The instructions are simple to follow and worked out-of-the-box as long as the tested version of AVRDUDE and libftdi is used.

I installed this special version into /usr/local. Here some example invocations:

  • Setting fuses (default frequency of an 644P is 1MHz, so you must use -B 1 to slow the programming down):
    sudo avrdude-ftdi -v -p m644p -c ft245r -P ft0 -B 1 -U lfuse:w:0xE2:m -U hfuse:w:0×99:m -U efuse:w:0xFD:m
  • Reading all 64KB FLASH from the 644P (takes 13s):
    sudo avrdude-ftdi -v -p m644p -c ft245r -P ft0 -U flash:r:/tmp/644flash.hex

Small update: /etc/avrdude.conf must contain this:

programmer
  id    = "ftdi";
  desc  = "SparkFun FTDI Basic Breakout";
  type  = ft245r;
  miso  = 1;  # CB0
  sck   = 0;  # CB1
  mosi  = 2;  # CB2
  reset = 4;  # CB4
;

 

 
AVR minus Arduino

Arduino’s hardware is great and plentiful available, but for bare bones, the single microcontroller is all you need.

So I got myself some ATmega328P chips (250 Yen a piece) to set up a minimal Arduino like described here. All I need is an Arduino to program the blank chip. Got that, so I was hopeful.

First step is to burn a bootloader via the ISP interface of the 328P. Worked flawlessly. Infact, I tried all variations of bootloaders: with external clock and without, and the normal bootloader as well as OptiBoot. All of them looked ok: after reset an LED connected to digital pin 13 blinks with the usual “there is no program” sequence. But all attempts to program something using the serial connection failed with

avrdude: stk500_recv(): programmer is not responding

errors. I tried all possible board types and I tried to use avrdude using all possible baud rates. Nothing worked.

After spending countless hours trying to find a solution I gave up and skipped the Arduino interface part and its abstraction. The benefit is that I know exactly what is happening and the serial port now is free and available for other uses beside programming.

I found a good (and simple) example here and here I found how to use avrdude correctly:

avrdude -v -p m328p -c avrisp -P /dev/ttyUSB0 -b 19200 -e -U flash:w:demo.hex

This is using an Arduino as programmer acting as an avrisp programmer to program the ATmega328P. Worked on the first try.

Some more examples:

  • And to read the config and fuse bits:
avrdude -v -p m328p -c avrisp -P /dev/ttyUSB0 -b 19200
  • To program the fuse bits (fuse bit values taken from here) and erase the flash memory (-e):
avrdude -v -p m328p -c avrisp -P /dev/ttyUSB0 -b 19200 -e -U lfuse:w:0xE2:m -U hfuse:w:0xDA:m -U efuse:w:0xFD:m

On an unrelated note: During the next days, so time permits, I’ll check out this and this. This looks helpful too. Then I can get rid of the Arduino IDE, but keep the libraries.

 
Arduino and Temperature

I got some sensors from my favorite local electronics shop and one of them was a temperature sensor. Analog output, so I can use it for an XBee too.

I connected it to my FunnelIO board I bought some time ago, and about 10min later I had the room temperature printed on my screen. I really like Arduino boards:  while the CPU is very limited (1 KByte RAM, 14 kByte FLASH for user program), it gets simple things like this quickly done.

Here the loop of the test program:

  // read the value from the sensor:
  sensorValue = analogRead(sensorPin);
  Serial.print("A0=");
  Serial.print(sensorValue);
  Serial.print(" = ");
  //temperature=((sensorValue*3300.0/1024.0)-600.0)/10.0;
  //Serial.print(temperature);
  temperature=(sensorValue*322)-60000;
  Serial.print(temperature/1000);
  Serial.println(" Celsius");

The LM61 outputs 600mV at 0°C and 10mV for each degree more (or less) from -25°C up to +85°C.

 

 
LCD and Arduino

Driving a LCD from an Arduino is no problem at all. There are nice libraries doing this. I bought a slightly different display which uses an I2C interface instead of the usual 4 or 8 bit parallel interface. Since I connected recently the Wii Nunchuck which uses I2C too to my small Arduino board, the next logical step was to add the display too. After all, I2C is all about adding devices to a 2 wire bus.

There was only one small problem: I2C LCD is not supported natively by the Arduino library system. But someone (and I really do not know who as the amount of comments was limited) created a library for Arduino here. All I can say about the author is that he (or she) is using a Mac and probably Japanese since I found a Japanese comment. This library was a great starting point, and although it was not complete and quite some functions were commented out with a “Does not work”, they worked out fine after I replaced them with what I thought should be here according to the data sheet of the used controller (ST7032i).

Now I have connected to my Arduino:

  1. Wii Nunchuck (2 button, 3 axis accelerometer, analog joystick)
  2. LCD (2 lines with 16 characters each)
  3. Servos (like those)
  4. A 12V fan which can be controlled via PWM to change its rpm
  5. LEDs of course
  6. 12V relays

If anyone wonders what the goal here is, for those I can only say: “Der Weg ist das Ziel” by Confucius.

Next on my list:

  1. RTC via I2C so the Arduino knows what time it is
  2. A piezo buzzer
  3. More sensors, like temperature, humidity, light etc.
  4. Another uController via I2C

With the first two I can make an alarm clock. Not that I would need one…

 

 
XBee - Again

xbee-python is out in version 2.0.0 which officially supports the ZigBee (Series 2) version of the Digi XBee modules. Thus time to update my test programs to this new library.

Here the result:

#!/usr/bin/python

# This is a simple demo to remotely turn a LED on and off
# 2011-02-11 Harald Kubota

import serial
from xbee import ZigBee
import time

PORT='/dev/ttyUSB0'
BAUD_RATE=9600
ser = serial.Serial(PORT, BAUD_RATE)

# ZB XBee here. If you have Series 1 XBee, try XBee(ser) instead
xbee=ZigBee(ser)

#MAC, number written on the back of the XBee module
# CO3 = my coordinator
# EP1 = my endpoint with the LED on pin 11
device={
        "CO3":'\x00\x13\xa2\x00\x40\x52\x8d\x8a',
        "EP1":'\x00\x13\xa2\x00\x40\x4a\x61\x84'
}
#64 bit address
led=False

#change remote device function
xbee.remote_at(dest_addr_long=device["EP1"],command='D2',parameter='\x02')
xbee.remote_at(dest_addr_long=device["EP1"],command='D1',parameter='\x03')
xbee.remote_at(dest_addr_long=device["EP1"],command='IR',parameter='\x04\x00')
xbee.remote_at(dest_addr_long=device["EP1"],command='IC',parameter='\x02')

while 1:
        #set led status
        led=not led
        if led:
                xbee.remote_at(dest_addr_long=device["EP1"],command='D4',parameter='\x04')
        else:
                xbee.remote_at(dest_addr_long=device["EP1"],command='D4',parameter='\x05')
        # wait 1 second
        time.sleep(1)

ser.close()

Time to learn Python. I was mostly guessing my way through the Python code of this library. Luckily it’s far easier to read and understand than some other languages. Maybe I am just not a friend of putting $ signs in front of variables.

© 2011 Harald's Random Stuff Suffusion theme by Sayontan Sinha