Getting started with an STM32L Discovery with Linux and GCC

I’ve got the “hello world” working on my STM32L Discovery board that I got about 8 months ago.  It’s not even the canonical blinking light, but it counts up and you only know that it works by using a debugger!  Another site gave me the basic idea, but I needed a few changes to get it working.

    1. Download the Linaro bare metal ARM toolchain (it’s near the bottom of the page).  Extract it somewhere (I put it in /opt).
    2. Download and build OpenOCD.  I’m using version 0.6.0.  I used Checkinstallso I had a managed package:
      tar -zxvf openocd-0.6.0.tar.gz
      cd openocd-0.6.0.tar.gz
      ./bootstrap
      ./configure --prefix=/usr --enable-jlink --enable-amtjtagaccel --enable-ft2232_libftdi
      make
      sudo checkinstall make install
    3. Now something to compile.  I used this:
      // By Wolfgang Wieser, heavily based on:
      // http://fun-tech.se/stm32/OlimexBlinky/mini.php
      
      #define STACK_TOP 0x20000800   // just a tiny stack for demo
      
      static void nmi_handler(void);
      static void hardfault_handler(void);
      int main(void);
      
      // Define the vector table
      unsigned int *myvectors[4]
      __attribute__ ((section("vectors"))) = {
          (unsigned int *) STACK_TOP,         // stack pointer
          (unsigned int *) main,              // code entry point
          (unsigned int *) nmi_handler,       // NMI handler (not really)
          (unsigned int *) hardfault_handler  // hard fault handler
      };
      
      int main(void)
      {
          int i=0;
      
          for(;;)
          {
              i++;
          }
      }
      
      void nmi_handler(void)
      {
          for(;;);
      }
      
      void hardfault_handler(void)
      {
          for(;;);
      }
    4. Build it:
      arm-none-eabi-gcc -I. -fno-common -O0 -g -mcpu=cortex-m0 -mthumb -c -o main.o main.c

      I believe the -O0 is to stop the compiler optimizing out the counting loop.

    5. Now for linking. The script on the other site didn’t seem to work for me – when I started the debugger, it looks like it was trying to run code from memory address 0. From what I’ve seen, the flash actually lives at 0x02000000, which might explain the problem. I found another script at ChibiOSwhich seemed to work better. Download the script, then run the linker:
      arm-none-eabi-ld -v -TSTM32L152xB.ld -nostartfiles -o demo.elf main.o
    6. Now extract the binary image from the .elf:
      arm-none-eabi-objcopy -Obinary demo.elf demo.bin

      My binary is a whopping 52 bytes!

    7. Before uploading the binary, the permissions on the Discovery board need changing, because only root can access it at the moment. Put this in /etc/udev/rules.d/90-stm32ldiscovery.rules:
      ATTRS{idVendor}=="0483", ATTRS{idProduct}=="3748", MODE="0666"

      This will give everyone write access to the Discovery. To apply the rules, run:

      sudo service udev restart
    8. Now to start OpenOCD, and upload the binary:
      $ openocd -f /usr/share/openocd/scripts/board/stm32ldiscovery.cfg
      Open On-Chip Debugger 0.6.0 (2012-09-15-16:06)
      Licensed under GNU GPL v2
      For bug reports, read
      	http://openocd.sourceforge.net/doc/doxygen/bugs.html
      adapter speed: 1000 kHz
      srst_only separate srst_nogate srst_open_drain
      Info : clock speed 1000 kHz
      Info : stm32lx.cpu: hardware has 6 breakpoints, 4 watchpoints

      In another terminal:

      $ telnet localhost 4444
      Trying 127.0.0.1...
      Connected to localhost.
      Escape character is '^]'.
      Open On-Chip Debugger
      > poll
      background polling: on
      TAP: stm32lx.cpu (enabled)
      target state: halted
      target halted due to breakpoint, current mode: Thread 
      xPSR: 0x01000000 pc: 0x0800001a msp: 0x200007f0
      target state: halted
      target halted due to breakpoint, current mode: Thread 
      xPSR: 0x01000000 pc: 0x0800001a msp: 0x200007f0
      > reset halt
      target state: halted
      target halted due to debug-request, current mode: Thread 
      xPSR: 0x01000000 pc: 0x08000010 msp: 0x20000800
      > flash probe 0
      flash size = 128kbytes
      flash size = 128kbytes
      flash 'stm32lx' found at 0x08000000
      > flash write_image erase demo.bin 0x08000000
      auto erase enabled
      target state: halted
      target halted due to breakpoint, current mode: Thread 
      xPSR: 0x61000000 pc: 0x20000012 msp: 0x20000800
      wrote 4096 bytes from file demo.bin in 0.325034s (12.306 KiB/s)
      > reset
      target state: halted
      target halted due to breakpoint, current mode: Thread 
      xPSR: 0x01000000 pc: 0x08000010 msp: 0x20000800
      > exit
      Connection closed by foreign host.

      I don’t know what all of those commands do though!

    9. Now to see whether the code is actually running:
      $ arm-none-eabi-gdb demo.elf
      GNU gdb (GNU Tools for ARM Embedded Processors) 7.3.1.20120613-cvs
      Copyright (C) 2011 Free Software Foundation, Inc.
      License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
      This is free software: you are free to change and redistribute it.
      There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
      and "show warranty" for details.
      This GDB was configured as "--host=i686-linux-gnu --target=arm-none-eabi".
      For bug reporting instructions, please see:
      <http://www.gnu.org/software/gdb/bugs/>...
      Reading symbols from /home/damien/projects/stm32l-demo/demo.elf...done.
      (gdb) target remote :3333
      Remote debugging using :3333
      main () at main.c:21
      21	{
      (gdb) cont
      Continuing.
      ^C
      Program received signal SIGINT, Interrupt.
      main () at main.c:26
      26	        i++;
      (gdb) print i
      $3 = 496378
      (gdb) cont
      Continuing.
      ^C
      Program received signal SIGINT, Interrupt.
      main () at main.c:26
      26	        i++;
      (gdb) print i
      $4 = 903650
      (gdb) quit
      A debugging session is active.
      
      	Inferior 1 [Remote target] will be detached.
      
      Quit anyway? (y or n) y
      Ending remote debugging.

      Yay, it looks like it’s running!

A program isn’t of much use if it can’t communicate outside of the chip, so driving I/O will be next. There looks like three options:

  1. Write to the hardware directly.  This involves looking through the CPU’s user manual, and working out how to access the I/Os.
  2. Use another library to access the hardware.  This is much like how you write AVR code – you access all of the I/Os through C library calls.  ST supplies a library, while it doesn’t have a particularly nice license it’s probably a good starting point.
  3. Use a operating system like ChibiOS, which has support for this board.  Having developed stuff for the AVR, I think it would be nice to have the resources of a real operating system – I wouldn’t have to worry about implementing scheduling and interrupts myself.

Hopefully one day I’ll try these out and get around to writing about the results!

My next post describes a what the code in this example does.

Comments (2)

Whiteboard bot’s first drawing

Here’s the first drawing from my whiteboard robot! It doesn’t draw anything by itself because I must have messed up the maths, but I was able to operate it manually and draw some circles. The lines are wonky because the wall the board was attached to was shaking. I didn’t get the pen to move in and out, to draw separate lines.

The robot suspends a whiteboard marker from two strings.  The strings are wrapped around pulleys.  The strings are wound onto spools, driven by motors connected via a serial link to a PC.  The PC interprets an SVG image, and calculates how much each motor needs to turn to draw each line segment.

It’s currently being driven with a Pololu Baby Oranguatan and some DC motors  that were lent to me, and various 3D printed parts to hold the pen and wind the string.

I have a few complaints about the Baby Orangutan.  First is their choice to use two wires to connect the H-bridge driver.  This makes the motors slightly easier to move, but means that to drive two motors, you need to use both 8-bit timers on the AVR.  With a 3-wire connection (two for direction, one for PWM), I would only have needed to use one timer, leaving the other one for Arduino compatibility or to drive the servo which moves the pen in and out.  My other issue is the placement of the status LED: I would have thought it goes on port B5 – like the Arduino – but they put it on one of the serial port pins, making it useless if you’re using the serial port.

I had the circuit powered at Science Alive for a few hours, but the manual movement function stopped working.  I connected the circuit on perfboard using very thin enamelled wire.  I doubled the wires between the H-bridge chip and the motors, but wasn’t smart enough to do the same for the power supply, so there might have been some brownouts when the motors started.  I should add some capacitors too.  The motors were less powerful than I expected, so my 10cm reels put a bit too much strain on the motors.  I’ll have to get some smaller reels printed.

I think I’ll get the simulator running again – hopefully that will help me get the calculations for the reel movement correct.

Leave a Comment

Whiteboard robot number crunching

I’m thinking of building a robot that suspends a whiteboard marker from two strings, and draws on a whiteboard. There would be two motors that can change the length of the strings.

I was playing with some calculations for this robot, literally on the back of an envelope. I need to calculate how long each string will be for a particular coordinate.

Pythagoras’ theorem gives:

l_1^2=x^2+y^2

and

l_1^2=r^2+s^2

If we smash them together, we get:

r^2 + s_1^2 = x^2 + y^2

s_1 = \sqrt{x^2+y^2-r^2}

and of course

s_2 = \sqrt{(w-x)^2+y^2-r^2}

That was easier than I expected – the maths wasn’t too hard! This should be enough to draw short segments with linear interpolation.

But, I’d like to know how the string length changes as I draw a straight line. Imagine a line being drawn perpendicular to the string; when the length of the string gets very long, it should lengthen at the same rate as the line. This suggests that the string length and the line length forms a hyperbola.

Consider a horizontal line beneath one of the wheels. Let x be the distance from the closest point on the line to the wheel. s is the string length. If the line went through the wheel, the graph

\frac{x}{s}=1

would give the line where the x position is the same as the string length.

If the line does not pass through the wheel, this equation from earlier where r is the wheel radius and y is the shortest distance from the wheel to the line:

s^2 = x^2 + y^2 - r^2

can become

1 = \frac{x^2 + y^2 - r^2}{s^2}

which should produce a hyperbola. When the string is long enough, y²-r² becomes negligible.

Previously I’ve implemented Bresenham’s algorithm to interpolate values on an AVR; it would be nice to do the same thing to calculate this parabola while drawing a straight line segment. It turns out someone has worked out how to use calculations like this for hyperbolae.

Leave a Comment

Fixing the foam pads on a trackball

Problem: the trackball scratches my graphics tablet!

Solution: foam crafty adhesive shapes.

Fixed! It’s a bit slippery, but I’ll get used to it.

Leave a Comment

Drive an Arduino from a mobile phone wirelessly

A while ago I had the idea of using cheap bluetooth modules to operate an Arduino from a mobile phone, thus saving the expense of an IOIO board (or similar).  I purchased the module, but it didn’t arrive during a period of unemployment and I never got around to doing anything with it.

Someone beat me to it.

I’ll have to give it a go one day.  This will save me the trouble of doing it myself!

I wasn’t sure whether a slave device would be fine, or whether I’d have to get the bluetooth module running in master mode.  Someone has found out how to upload different firmware versions, to allow  master mode in these devices.

Leave a Comment

A very cheap AVR dev board

I bought one of these boards for about $4 on eBay.  It’s based on the USB ASP – a cheap AVR programming board.  I remember seeing a post on Hack A Day a while ago about using them as a general purpose dev board, with 5 lines broken out to the black pin header.  By removing a link on the bottom of the board, you can get 3.3V from a regulator.

First I wanted to see what the stock firmware was.  I shorted J2, which connects the reset pin to the header pins, allowing the chip to be programmed.  I dumped the data, and saw this:

00000000  00 00 01 01 02 02 03 03  04 04 05 05 06 06 07 07  |................|
00000010  08 08 09 09 0a 0a 0b 0b  0c 0c 0d 0d 0e 0e 0f 0f  |................|
00000020  10 10 11 11 12 12 13 13  14 14 15 15 16 16 17 17  |................|
00000030  18 18 19 19 1a 1a 1b 1b  1c 1c 1d 1d 1e 1e 1f 1f  |................|

It sounds like that’s what you get when the chip is locked.  I used avrdude to erase the chip, and I was able to program it again using the original firmware from the USB ASP site.  I had a quick look at the fuse bits, and they seem to be correct.  I haven’t actually tried this firmware, so no doubt some of the pins are in different places.

The pinout appears to be the same as the original. The pins themselves are different because of the chip packaging:

D+ 13, 32 PB1, PD2 (INT0)
D- 12 PB0
MOSI 15 PB3 (MOSI)
RST 14 PB2
J2 29 RESET
SCK 17 PB5 (SCK)
MISO 16 PB4 (MISO)

The price is amazing since the AVR stick costs about $10, and contains fewer parts.  Perhaps I should retry my V-USB experiment with it.

A while ago I found some V-USB tutorials, but I haven’t gone through them yet.

Comments (1)

Add a caption to a JPEG image without reencoding

I was looking for a way to add captions to JPEG images without re-encoding them.  It turns out there’s a patch to jpegtran that overlay one JPEG image over another one, without re-encoding either.  Here’s how I built it:

  1. Download the sources of the IJG version of jpegtran (I used version 8c), and decompress it somewhere
  2. Download the “drop” patch, and copy the source files into the source extracted in the previous step
  3. configure, make (as usual)

I made a script that automatically adds a label using Imagemagick to create the label, and this version of jpegtran to put them together:

#!/bin/sh

JPEGTRAN="/opt/jpeg-8c/bin/jpegtran"

if [ $# -lt 3 ] ; then
echo "Usage: jpeglabel [label] [in] [out]" >2
exit 2
fi

LABELFILE=`mktemp`
convert -type truecolor -size 256x16 "label:$1" jpg:"$LABELFILE"
"$JPEGTRAN" -drop +16-16 "$LABELFILE" -outfile "$3" "$2"

rm "$LABELFILE"

I tried making some examples to show, but it turns out the (experimental) “drop” patch is very fussy about the files you give it.  I got it to work on 320×240 pictures from my camera, but not on some other images I tried.  It requires the “sampling ratio” to be the same on both images, which is found in the advanced JPEG settings in Gimp.

Leave a Comment

Run a V-USB demo on an AVR stick (Easylogger)

This article describes how to run one of the V-USB demos on an AVR stick (which is an Easylogger clone).

V-USB is a firmware-only USB implementation for AVR 8-bit microcontrollers.  This means that it’s possible for most AVR chips to communicate via USB, even though they have no hardware support for that.

The AVR stick

The AVR stick is a tiny circuit board that contains the minimum components to get a somewhat useful USB device, based on an ATmega85.  It costs about $10, and is available in Australia from Little Bird Electronics and also from Sparkfun.

I’ll use the USB HID class, because it doesn’t require any fancy drivers to get running.

First, grab the V-USB code, (I’m using version 20100715), and extract the /examples/hid-custom-rq directory somewhere.

Let’s get the firmware working first.  The AVR stick works a bit differently to the circuit the demo is for, so we need a few changes.

The ATmega85 only has one port: port B.  The sample uses port D.  Edit these lines in /firmware/usbconfig.h:

#define USB_CFG_IOPORTNAME      B
#define USB_CFG_DMINUS_BIT      0

The LED is on PORTB1, not PORTB0, so change /firmware/main.c:

#define LED_BIT             1

Finally, we need to change the Makefile to match the ATmega85:

DEVICE  = attiny85
F_CPU   = 16500000

Where does 16500000 come from? The AVR stick uses the internal RC oscillator for its clock, which runs at about 8MHz.  The problem is, V-USB needs a clock speed of at least 12MHz to run.  ATmega5s have a fuse setting which doubles its clock speed (they call it the PLL clock or something like that).

While the internal RC clock is fairly stable, it’s not very accurate.  When V-USB starts, it uses the USB signal to calibrate the clock so it runs at the same speed as the USB bus.  Using this calibration, it’s possible to run the internal clock at 8.25MHz, which is doubled to 16.5MHz.  This is the speed the AVR stick must run at.

Since I was using the Bus Pirate to program the AVR stick, I had to change the AVRDUDE= line also.

That was it for the firmware – hopefully it compiles and uploads to the AVR stick.

Now for the software that runs on the computer.  When I compiled and ran ./set-led on, I got this message:

Could not find USB device "LEDCtlHID" with vid=0x16c0 pid=0x5df

but when I ran lsusb, I saw this line:

Bus 002 Device 023: ID 16c0:05df VOTI

“VOTI” doesn’t match “LEDCtlHID”, no wonder it couldn’t find it!  But when I ran sudo lsusb -v, I saw this instead:

iProduct                2 LEDCtlHID

OK, so “VOTI” is simply the string form of the USB vendor ID 0x16c0, and the name “LEDCtlHID” is correct after all.  What’s going on then?

I noticed that the usbOpenDevice function takes a few file descriptors, of which the final one is called warningsFp.  This is called from set-led.c; perhaps I’ll change this last parameter from NULL to stderr.

When I ran ./set-led on again, I got this:

Warning: cannot query manufacturer for VID=0x16c0 PID=0x05df: error sending control message: Operation not permitted

That’s a slightly more useful error message!  So the USB library doesn’t seem to be able to use the device.  I read that some USB devices appear under /dev/usb, so let’s have a look with ls -l /dev/usb:

crw------- 1 root root 180, 96 2011-12-14 22:50 hiddev0
crw------- 1 root root 180, 97 2011-12-16 21:33 hiddev1

Maybe that’s the problem – there’s no write permissions on the device.  To fix this, I’ll make this device writable by everyone.  Create the file /etc/udev/rules.d/90-avrstick.rules, and put this in it:

SUBSYSTEM=="usb", DEVTYPE="usb_device", SYSFS{idVendor}=="16c0", SYSFS{idProduct}=="05df", MODE="0666"

then sudo reload udev.  Remove the stick then put it back in, then look at /dev/usb again:

crw------- 1 root root 180, 96 2011-12-14 22:50 hiddev0
crw-rw-rw- 1 root root 180, 97 2011-12-16 21:42 hiddev1

That’s a bit better.  Now run ./set-led on – the white LED on the AVR stick should come on!

There’s one improvement I can think of – the C code is pretty ugly, so I had a go in Python.  I copied the USB calls from the C code.  The Python code follows:

import sys
import usb
import itertools

vid=0x16C0; pid=0x05DF;

devs = itertools.ifilter(
    lambda dev: dev.idVendor==vid and dev.idProduct==pid,
    (dev for bus in usb.busses() for dev in bus.devices)
)         

if len(sys.argv) == 1:
    print 'Specify "on", "off" or "status"'
    sys.exit(1)
op = sys.argv[1]

for dev in devs:
    #print "Handling device %04x:%04x" % (dev.idVendor, dev.idProduct)
    handle = dev.open()
    if op == "on" or op == "off":
        handle.controlMsg(
            usb.TYPE_VENDOR | usb.RECIP_DEVICE | usb.ENDPOINT_OUT,
            1, # = CUSTOM_RQ_SET_STATUS
            [], # Empty payload - the data goes in the "wValue" field
            value = (op == "on" and 1 or 0 )
        )
    else:
        data = handle.controlMsg(
            usb.TYPE_VENDOR | usb.RECIP_DEVICE | usb.ENDPOINT_IN,
            2, # = CUSTOM_RQ_GET_STATUS
            1, # How many bytes to read
        )
        print "LED is " + (data[0] and "on" or "off")

That’s much shorter than the C code, and does the same thing (admittedly there’s much less error handling here).  I used the pyusb library version 0.4.2.  The documentation for this library is a bit thin, but I found the Python command “help(usb)” of some use, as well as the source code for the library.

Talking to the USB device is all using Control signals, which I think you need to do because it’s pretending to be an HID device.  I found it interesting that they never send data to the AVR stick – they send the desired LED status in the “wValue” field, which sounds like a 16-bit field that’s in every control message.  I don’t know much about USB, but conveniently copying the C code worked fine.

Leave a Comment

How an electric kettle knows when the water is boiling

My cheap kettle leaked.  You couldn’t press the lever down when it wasn’t on the base, so I hoped to find an electromagnet which held the switch on when it had power.  I didn’t find one (I only managed to salvage two neon lights from it), but I found out how kettles switch off when they’re boiling.

The steam is collected by this hood, which sends it down the tube into the base.

The steam then enters the base of the kettle through the hole at the bottom of this picture, where it’s deflected onto this disk.

The disk flexed when I pressed in the middle.  The part that sticks out pressed against the lever that switches the kettle on and off.  I guess that as the disk heats and expands, it flexes in the same way.

The lever that operates the kettle is like a large microswitch, in that it always snaps to one side or the other, and takes little force to change.  The disk flexes and presses lightly on the lever, but it’s enough force to trip the switch so the kettle turns off.

I didn’t find the electromagnet I was looking for.  Instead, whether the lever stays down depends on whether the kettle is sitting on its base.  If not, a pin with a strong spring on it protrudes from the bottom of the kettle.  The spring prevents the lever from staying down.

Leave a Comment

Laser printer mirror hacking

I pulled the mirror out of a laster pinter, and I had a go making it work.

I’d seen an article before that does this, but I had a datasheet for the driver chip.  Conveniently all of the pins I needed had test points on the board – that saves me having to attach something to the original plug.

It was pretty easy to drive – apply 12V, put a square wave on CLK and pull SS low.  The datasheet says that 5V is fine for the clock signal.  Apparently LD changes state once the mirror is up to speed.  The laster started spinning at 50Hz, but ran a bit rough below 500Hz.  It didn’t get any faster once the signal was at 5kHz.

To test out the relationship between the mirror and the clock signal, I made an Arduino flash a light 6 times for each clock pulse.  I saw 6 reflections in the mirror, so I guess the mirror spins 1/6 of a resolution for each clock pulse.

I tried running it at 5V, but I had no luck there.

Comments (1)

« Newer Posts · Older Posts »