Posts Tagged blinky

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

Blinky on the STM32L Discovery

My program seems to have locked-in syndrome, so now I’ll see if I can get it to flash an LED.

A good start would be to check the schematic for where the LED is connected.  There’s one connected to PB6 and PB7, and they’re actually marked with this on the PCB, next to the two push buttons.

Now how to interface them?  The programming manual has a whole section on GPIOs.  It mentions that there are registers for selecting the alternate function (which is how you activate SPI, the USARTs etc), selecting whether the pin is an output or input, whether there are pull-up or pull-down resistors activated, among other things.  One thing worth noting is section 6.3.1, which says “During and just after reset, the alternate functions are not active and the I/O ports are configured in input floating mode.”  What it doesn’t say though is how the registers map to the I/O pins.

The first page of the reference manual mentions one document I haven’t looked at yet: the datasheet.  And sure enough, the memory map in section 5 says that port B is at memory location 0x40020400.  There’s still no mention of how these map to the I/O registers, or how to access the registers from C code.

Figure 1 of the reference manual suggests the GPIO access is via the “AHB system bus”.  A search of the CPU reference manual says that AHB is the “Advanced High-performance Bus”, which doesn’t really mean anything for this.

Another look at the memory map shows that port B goes from 0x40020400 to 0x400207FF.  That’s 1kB of address space, so maybe all of the port registers live here?  If I assume that, I need to set a few bits in GPIOA_MODER at 0x40020400, and turn on the output pin in GPIOA_ODR at 0x40020414 (the reference manual shows the offset of this register as 0x14).  Like this:

    *((int*) 0x40020400) = 0x00005000;
    *((int*) 0x40020414) = 0x00000080;

No that doesn’t work… time to cheat.  I’ll look at “blinky.c”, which is included with the Keil IDE.  It mentions a GPIO clock, maybe I need to enable that.  This idea is a bit unusual to me, since AVRs don’t have a clock for the output pins, but maybe in an ARM you need one so DMA works or something.  Figure 12 contains a rather elaborate map of how the clocks work, but the important bit is on the right: HCLK goes to the AHB bus (which I saw earlier and dismissed!)  This is fed through a prescaler from SYSCLK.  Section 5.3.8 discusses the AHB peripheral clock enable register (RCC_AHBENR) which has a “GPIOB EN” bit at bit 1.  RCC is at 0x40023800, AHBENR is at offset 1C, so this register is at 0x4002381C.

So this gets the LED to light:

  *((uint32_t*) 0x4002381C) = 0x00000002; /* Enable GPIO clock */
  *((uint32_t*) 0x40020400) = 0x00005000; /* Output mode */
  *((uint32_t*) 0x40020408) = 0x00005000; /* 2MHz clock speed */
  *((uint32_t*) 0x40020418) = 0x00000080; /* LED on */

That’s pretty ugly and you wouldn’t want to write too much code like that, so I’ll look at libraries that contain these numbers instead.

Leave a Comment