Thursday, November 4, 2010

USB Doodad 4: Blinkenleds

Progress on the USB Doodad has been going well with several successes.
After soldering on the processor, I added the reset pullup resistor, and rigged up a 6-pin programming header on the end of the board.

(Note this is a prototype at a later stage)

There's no permanent position for a programming header on the board.  This is because once the board has a bootloader, loading software on the Doodad will be done over USB.  Our plan is to use pogo pins to make a "bed of nails" that we can temporarily sit the Doodad board on to get the bootloader into it.

I plugged the Doodad into my programmer, and plugged the programmer into my PC.  There was no smoke (!), and the avrdude program could recognise the '328!  That's always a good sign.

[root@onza mjd]# /usr/bin/avrdude -c avrisp2 -P usb -p m328p -q

avrdude: AVR device initialized and ready to accept instructions
avrdude: Device signature = 0x1e950f

avrdude: safemode: Fuses OK

avrdude done.  Thank you.

[root@onza mjd]#

I then soldered a LED and a dropper resistor onto the pin for bit 5 of port C, searched for a simple "blinkenled" program on the 'net, and compiled it for the '328:

[mjd@onza 328hello]$ cat hello.c
#include <avr/io.h>
#include <util/delay.h>

int main()
{
    // Set bit 5 of port C to be an output
    DDRC |= _BV(PC5);

    while (1)
    {
        // Invert the value of pin 5 on port C
        PORTC ^= _BV(PC5);
        _delay_ms(500);
    }
}

[mjd@onza 328hello]$ avr-gcc -Os -DF_CPU=8000000 -mmcu=atmega328p -o hello.o -c hello.c
[mjd@onza 328hello]$ avr-gcc -Os -DF_CPU=8000000 -mmcu=atmega328p -o hello.elf hello.o
[mjd@onza 328hello]$ avr-objcopy -O ihex hello.elf hello.hex
[mjd@onza 328hello]$ ls -l hello.hex
-rw-rw-r--. 1 mjd mjd 582 2010-11-21 14:52 hello.hex
[mjd@onza 328hello]$

I then programmed it into the '328:

[root@onza 328hello]# /usr/bin/avrdude -c avrisp2 -P usb -p m328p -U flash:w:hello.hex
avrdude: AVR device initialized and ready to accept instructions

Reading | ################################################## | 100% 0.01s

avrdude: Device signature = 0x1e950f
avrdude: NOTE: FLASH memory has been specified, an erase cycle will be performed
         To disable this feature, specify the -D option.
avrdude: erasing chip
avrdude: reading input file "hello.hex"
avrdude: input file hello.hex auto detected as Intel Hex
avrdude: writing flash (200 bytes):

Writing | ################################################## | 100% 0.08s

avrdude: 200 bytes of flash written
avrdude: verifying flash memory against hello.hex:
avrdude: load data flash data from input file hello.hex:
avrdude: input file hello.hex auto detected as Intel Hex
avrdude: input file hello.hex contains 200 bytes
avrdude: reading on-chip flash data:

Reading | ################################################## | 100% 0.06s

avrdude: verifying ...
avrdude: 200 bytes of flash verified

avrdude: safemode: Fuses OK

avrdude done.  Thank you.

[root@onza 328hello]#

Yay, the LED blinks!  But there's a slight problem: The LED should blink once a second, but I found it was actually blinking once every eight seconds.  This is because by default, there's a fuse in the '328 which divides the clock by 8.

AVR chips have a few bytes of persistent memory called "fuses".  The bits in these fuses control things such as what source the chip uses for clock, and what areas of memory are protected.  The name for the fuse that's dividing the clock by 8 is called CKDIV8.

Here's how I retrieved the fuse values:

[root@onza 328hello]# /usr/bin/avrdude -c avrisp2 -P usb -p m328p -t

avrdude: AVR device initialized and ready to accept instructions

Reading | ################################################## | 100% 0.01s

avrdude: Device signature = 0x1e950f
avrdude> dump lfuse
0000  62                                               |.               |

avrdude> dump hfuse
0000  d9                                                |.               |

avrdude> dump efuse
0000  07                                                |.               |

avrdude> quit

avrdude: safemode: Fuses OK

avrdude done.  Thank you.

[root@onza 328hello]#

I then looked up an AVR fuse calculator:

  http://frank.circleofcurrent.com/fusecalc/

Turning off the CKDIV8 fuse means the "low fuse" value changes from 0x62 to 0xE2.  The fuse calculator also helpfully shows the avrdude parameters to make this change:

[root@onza 328hello]# /usr/bin/avrdude -c avrisp2 -P usb -p m328p -U lfuse:w:0xE2:m

avrdude: AVR device initialized and ready to accept instructions

Reading | ################################################## | 100% 0.01s

avrdude: Device signature = 0x1e950f
avrdude: reading input file "0xE2"
avrdude: writing lfuse (1 bytes):

Writing | ################################################## | 100% 0.02s

avrdude: 1 bytes of lfuse written
avrdude: verifying lfuse memory against 0xE2:
avrdude: load data lfuse data from input file 0xE2:
avrdude: input file 0xE2 contains 1 bytes
avrdude: reading on-chip lfuse data:

Reading | ################################################## | 100% 0.00s

avrdude: verifying ...
avrdude: 1 bytes of lfuse verified

avrdude: safemode: Fuses OK

avrdude done.  Thank you.

I can then check that the fuse programming has worked:

[root@onza 328hello]# /usr/bin/avrdude -c avrisp2 -P usb -p m328p -t

avrdude: AVR device initialized and ready to accept instructions

Reading | ################################################## | 100% 0.01s

avrdude: Device signature = 0x1e950f
avrdude> dump lfuse
0000  e2                                                |.               |

avrdude> quit
avrdude: safemode: Fuses OK

avrdude done.  Thank you.

[root@onza 328hello]#

After doing this, the LED flashes once per second.

At this point, the '328's clock is being provided from an internal oscillator.  The internals oscillator is convenient if you want to keep the number of parts on the board to a minimum, but the frequency can drift with temperature, so it's not a great choice for a USB device.  So my next step was to solder on a quartz crystal and the two associated capacitors.

Back in the fuse calculator, I used the pull-down menu in the "low fuse presets" to choose "Ext. Crystal Osc.; Frequency 8.0- MHz; Start-up time PWRDWN/RESET: 16K CK/14 CK + 65 ms; [CKSEL=1111 SUT=11]".  That makes the low fuse value 0xFF.  So I programmed that in using avrdude as above:

[root@onza 328hello]# /usr/bin/avrdude -c avrisp2 -P usb -p m328p -U lfuse:w:0xff:m





avrdude done.  Thank you.

The LED is now flashing, but it's twice as fast as it should be.  This is because when I compiled the code, I said the clock frequency was 8MHz, to match the internal oscillator.  That's not going to work very well with a 16MHz external crystal.  So I recompiled the code:

[mjd@onza 328hello]$ avr-gcc -Os -DF_CPU=16000000 -mmcu=atmega328p -o hello.o -c hello.c

I won't show the linking and .hex file creation steps as they're the same as above, but after reprogramming the '328, the LED now blinks at 1Hz.

So, I have a board on which I can run software, and the means to program it.  Winnage!

1 comment:

  1. Hi there. Nice blog. You have shared useful information. Keep up the good work! This blog is really interesting and gives good details. aluminium soldering flux, Water soluble flux.

    ReplyDelete