Posts Tagged tutorial

ChibiOS on the STM32L Discovery

I previously used the libraries provided by ST to do something on the STM32L Discovery, now I’ve made a short demo of using ChibiOS.  Hopefully by using an OS to write code, I don’t have to mess around with timers myself to process events periodically, which I always found time consuming on the AVR.  Here’s what I did:

  1. Download and extract ChibiOS.  I used version 2.4.2.  I extracted the archive to /opt.
  2. Make a copy of this directory: ChibiOS_2.4.2/demos/ARMCM3-STM32L152-DISCOVERY.  This is what I edited for my demo.
  3. Delete the keil and iar directories; they’re for different IDEs and we don’t need them.
  4. Replace main.c with this:
    #include <ch.h>
    #include <hal.h>
    
    int main(void) {
      halInit();
      chSysInit();
    
      palSetPadMode(GPIOB, 7, PAL_MODE_OUTPUT_PUSHPULL);
      while (1) {
        palSetPad(GPIOB, 7);
        chThdSleepMilliseconds(500);
        palClearPad(GPIOB, 7);
        chThdSleepMilliseconds(500);
      }
    }

    I adapted this from another blog.

  5. In the Makefile, change the CHIBIOS variable to point to where you extracted ChibiOS.
  6. Run make, then upload the binary to the Discovery.  The correct compiler should be invoked if the Linaro bare-metal compiler is on your PATH, like I did for my first coding attempt.

It’s as simple as that – a light that flashes once a second!

So what does this code do?

halInit() and chSysInit() seem to go at the start of any ChibiOS program. The HAL is what tries to abstract out the peripherals on each chip. The document about the architecture explains this some more.

pal means “Port Abstraction Layer”, and functions relating to this start with pal. Functions starting with ch relate to the kernel. There are two reference manuals in ChibiOS: one for the kernel (the cross platform stuff, although there’s a separate manual for each compiler), and one for the peripherals (the more chip-specific stuff).

It looks like once you call chSysInit, that function continues to run as a thread.  I would hope because we’re calling a sleep function, the CPU is actually sleeping and not busy-waiting.

I’m surprised how easy this was to get running.  The Makefile is very good – you don’t need to keep a copy of the entire operating system in your project directory; it will find it and compile the relevant parts.  There’s no code directly accessing the hardware either, so the I/O code is no more complicated than Arduino code.  Of course I’m expecting to have to learn more about ChibiOS and the STM32L as I make more complicated things.

I’d like to use ChibiOS to write firmware to drive a television from the Discovery, so I can learn about RTOS scheduling and DMA, which I plan to use to copy the framebuffer to the output.  I imagine I’ll need to learn more about how clocks work to control the speed the data is written to the TV.

Advertisements

Leave a Comment

How the STM32L Discovery demo works

In my previous post, I got a basic program running on a STM32L Discovery board.  Now I hope to work out what the program works.

The program contained this data structure:

// 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
};

This is a structure with four pointers.  It also has this in front: __attribute__ ((section("vectors"))).  The linker script contains a section with a similar name, and while I don’t know anything about linker scripts, it looks like it goes right at the start of the flash memory. In other words, these four pointers look like the first 32 bytes of any program.

Is there any documentation that describes this? After suffering through ST’s “product selector”, I found the page for the CPU, where I found the reference manual. This is a bit like a AVR datasheet; it tells you all about the interfaces the chip has. Since my program doesn’t talk to the outside world yet, this document isn’t terribly helpful; but it does point to a document from ARM about the CPU core.

After searching for various terms in this document, I eventually found out that this table is called the “vector table” and is described in section 5.9.1. Although the table in the code is self-explanatory, it’s nice to find the reference to what exactly it does. The document also says there’s other vectors that may appear after these, so that may be useful to know one day.

Now that I’ve started to find my way around the documentation, maybe I can go on to making the chip actually do something!

Leave a Comment

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)