I wanted to try the ChibiOS/RT real-time operating system on my micro-controller board that mounts an STM32L152VBT6.
ChibiOS is one of many alternative real-time operating systems such as FreeRTOS, RTEMS, eCos and NuttX. It supports many platforms and it’s actively developed by Giovanni Di Sirio (full disclosure: we both work at STMicroelectronics).
I downloaded the 2.6.3 stable version and started exploring the content of the source code. Basically these are the main parts of ChibiOS code:
- The kernel, containing thread management, synchronization, power modes, etc.
- The Hardware Abstraction Layer (HAL) which exports a generic API for many widespread peripherals and hides the implementation details.
- The startup code/makefiles/linker scripts to make it all work
Then there are demos, test code, external libraries and other stuff such as C++ wrappings. I discovered that my board is not directly supported, but the STM32L1xx is. This means I can use the kernel and startup code but not the HAL until someone (maybe me? I’ll see) writes the code to support it.
The source tree is full of demos that are specific to particular architectures and boards. I got the demo called ARMCM3-GENERIC-KERNEL
and copied it as ARMCM3-STM32L152-OLIMEX
. Then I modified the Makefile because it pointed to STM32F103xB files. I substituted the relavant lines with:
include $(CHIBIOS)/os/ports/GCC/ARMCMx/STM32L1xx/port.mk LDSCRIPT= $(PORTLD)/STM32L152xB.ld
And I ran “make
“. The compilation went well, but the program seems to do nothing externally visible so I didn’t even try to flash it. Now I needed to do something such as LED blinking to see if it works on my board. Here’s the “main.c
” that I quickly wrote based on the blink example of this previous post (most of the information needed to write this code comes from ChibiOS docs, Olimex board datasheet and STM32L152 reference manual).
#include "ch.h" #if !defined(SYSTEM_CLOCK) #define SYSTEM_CLOCK 8000000 #endif #define REG32(addr) (*(volatile uint32_t *)(addr)) #define GPIOE_BASE 0x40021000 #define GPIOE_MODER REG32(GPIOE_BASE + 0x00) #define GPIOE_OTYPER REG32(GPIOE_BASE + 0x04) #define GPIOE_ODR REG32(GPIOE_BASE + 0x14) #define RCC_BASE 0x40023800 #define RCC_AHBENR REG32(RCC_BASE + 0x1C) #define RCC_AHBENR_GPIOEEN 0x10 #define STAT3_PIN 10 #define STAT3_PIN_MASK (1UL<<STAT3_PIN) #define STAT4_PIN 11 #define STAT4_PIN_MASK (1UL<<STAT4_PIN) #define RCC_CR REG32(RCC_BASE + 0x00) #define RCC_CR_HSEON 0x10000 #define RCC_CR_HSERDY 0x20000 #define RCC_CFGR REG32(RCC_BASE + 0x08) #define RCC_CFGR_SW 0x3 #define RCC_CFGR_SW_HSE 0x2 #define RCC_CFGR_SWS 0xC #define RCC_CFGR_SWS_HSE 0x8 static void set_gpioe_moder(int pin, int mode) { uint32_t moder; uint32_t moder_pin_pos; uint32_t moder_pin_mask; moder_pin_pos = pin*2; // 2 bits per pin moder_pin_mask = 0x3UL << moder_pin_pos; moder = GPIOE_MODER; // read from register moder &= ~moder_pin_mask; // clear moder pin field moder |= (mode << moder_pin_pos); // set moder pin field GPIOE_MODER = moder; // write to register } static void hw_init(void) { uint32_t cfgr; // Set HSE as 8MHz system clock RCC_CR |= RCC_CR_HSEON; // Enable High Speed Internal clock while(!(RCC_CR & RCC_CR_HSERDY)) // Wait for HSE ready { continue; } cfgr = RCC_CFGR; cfgr &= ~RCC_CFGR_SW; cfgr |= RCC_CFGR_SW_HSE; // Select HSE as system clock RCC_CFGR = cfgr; while((RCC_CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_HSE) // Wait for HSE as system clock { continue; } // Set SysTick as 1000 system clock cycles STBase->RVR = SYSTEM_CLOCK / CH_FREQUENCY - 1; STBase->CVR = 0; STBase->CSR = CLKSOURCE_CORE_BITS | ENABLE_ON_BITS | TICKINT_ENABLED_BITS; // STAT3 and STAT4 LEDs RCC_AHBENR |= RCC_AHBENR_GPIOEEN; // enable GPIOE clock GPIOE_OTYPER |= STAT3_PIN_MASK; // open-drain set_gpioe_moder(STAT3_PIN, 1); // general purpose output GPIOE_OTYPER |= STAT4_PIN_MASK; // open-drain set_gpioe_moder(STAT4_PIN, 1); // general purpose output } static WORKING_AREA(waThread1, 128); static msg_t Thread1(void *arg) { while (TRUE) { GPIOE_ODR |= STAT3_PIN_MASK; // output pin low -> LED ON chThdSleepMilliseconds(1000); GPIOE_ODR &= ~STAT3_PIN_MASK; // output pin high-z -> LED OFF chThdSleepMilliseconds(1000); } } static WORKING_AREA(waThread2, 128); static msg_t Thread2(void *arg) { while (TRUE) { GPIOE_ODR |= STAT4_PIN_MASK; // output pin low -> LED ON chThdSleepMilliseconds(1100); GPIOE_ODR &= ~STAT4_PIN_MASK; // output pin high-z -> LED OFF chThdSleepMilliseconds(1100); } } int main(void) { hw_init(); chSysInit(); chThdCreateStatic( waThread1, sizeof(waThread1), NORMALPRIO, Thread1, NULL); chThdCreateStatic( waThread2, sizeof(waThread2), NORMALPRIO, Thread2, NULL); while (TRUE) { chThdSleepSeconds(TIME_INFINITE); } }
Basically the program does the following:
- Choose the external oscillator (HSE) as system clock, so that it uses the 8MHz quartz mounted on board.
- Configure the System Tick interrupt that is used by ChibiOS kernel to manage events, threads and synchronization.
- Initialize GPIOs to drive the STAT3/STAT4 LEDs.
- Run two parallel threads, each of them make one LED blink at a certain different frequency.
- The main thread goes to sleep and does nothing forever.
I ran “make
” again, compiling the program, and then I flashed it on board using the same method in the same previous blog post. After the reset, the result is that the red LED STAT3 and the green LED STAT4 start blinking at a speed that, for my naked eye, seem correct (about 1 second to toggle between on and off).
Next possible steps could be to write something for the HAL and use it to drive various things such as the serial port, buttons and ADC.
Posted on 2014/05/02
0