Getting started with ARM Cortex-M3 on GNU/Linux



STM32VLDiscovery is one of the three[2][3] cheap ARM Cortex-M3/M4 boards from ST. It contains an STM32F100RB microcontroller and an on-board ST-Link. Also it is designed to be powered with usb or external 3.3v or 5v. It seems to be a nice board for beginners. We can easily get started with stm32 on linux.

We need to build the toolchain for the bare metal arm ie the binary is to be executed on the arm without any linux/os. Summon-arm-toolchain is a shell script which will download the source of bintutils, gcc/gdb and two or three other packages (newlib, openocd and libopencm3) and compiles them (after applying some patches) to get executables for a "bare-metal" arm toolchain. After cloning the git repo, we need to read the README file, it says we need to install some essential packages before running the shell script. After that we just need to run the sheel "./summon-arm-toolchain" and it will do the remaining job. Finally we will get a new folder ~/sat in which we could see the library files and binary of arm-non-eabi-gcc and and many other required tools.
   Now we need a program to burn the binary to the hardware. For that we can use stlink. A simple make will generate the st-flash program...



 Now we could see some example programs in the libopen-cm3-xxxxxx folder. I just type make and we will get a bin file which could be burned into the chip using st-flash program using the below command.(example)
sudo st-flash write v1 led.bin 0x08000000

Now we can easily write and compile our own program in any path. We just need make a small modification in the makefile.

A simple example of "tossing a coin" using the two led and user push button:
#include <libopencm3/stm32/f1/rcc.h>
#include <libopencm3/stm32/f1/gpio.h>

/* Set STM32 to 24 MHz. */
void clock_setup(void)
{
    rcc_clock_setup_in_hse_8mhz_out_24mhz();
    
    /* Enable GPIOC clock. */
    rcc_peripheral_enable_clock(&RCC_APB2ENR, RCC_APB2ENR_IOPCEN);
}

void gpio_setup(void)
{
    /* Set GPIO 8/9 (in GPIO port C) to 'output push-pull'. */
    gpio_set_mode(GPIOC, GPIO_MODE_OUTPUT_50_MHZ,
    GPIO_CNF_OUTPUT_PUSHPULL, GPIO8);
    gpio_set_mode(GPIOC, GPIO_MODE_OUTPUT_50_MHZ,
    GPIO_CNF_OUTPUT_PUSHPULL, GPIO9);
}

void user_button_enable(void)
{
    /*Enable GPIOA clock */
    rcc_peripheral_enable_clock(&RCC_APB2ENR, RCC_APB2ENR_IOPAEN);
    /*set GPIOA0 as input open-drain */
    gpio_set_mode(GPIOA, GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO0);
}

void delay(volatile u32 t)
{
    t *= 1000;
    while (t--) ;
}

int main(void)
{
    int i;
    
    clock_setup();
    gpio_setup();
    user_button_enable();
    GPIOC_ODR = GPIO9;
    while (1) {
        if (GPIOA_IDR & GPIO0) {
            delay(1);
            while (GPIOA_IDR & GPIO0) ;
            delay(600);
        }
        GPIOC_ODR ^= (GPIO8 | GPIO9);    /* Toggle LEDs. */
    }
    
    return 0;
}

Simply type make to compile it and then type make prog to load the binary to the chip.
Each time we press the blue user button on the led, we will get a single bit random value (0 or 1) which could be observed on the two leds, ie "tossing a coin".
The above example with makefile could be downloaded from below repo.
https://github.com/vinodstanur/STM32VLDISCOVERY

Similarly we can try uart and many more....
Here is the link towards the datasheet and reference manual of STM32F100RB
http://www.st.com/internet/mcu/product/216844.jsp

1 second timer using TIM2
This is another test code which I have done after reading the reference manual. Can be considered as another hello world... Here the two leds will flash for a small duration in each 1 second timer interrupt.
#include <libopencm3/stm32/f1/rcc.h>
#include <libopencm3/stm32/f1/gpio.h>
#include <libopencm3/stm32/timer.h>

/* Set STM32 to 24 MHz. */
void clock_setup(void)
{
    //rcc_clock_setup_in_hse_8mhz_out_24mhz();
    rcc_clock_setup_in_hse_8mhz_out_24mhz();
}

void gpio_setup(void)
{
    /* Enable GPIOC clock. */
    rcc_peripheral_enable_clock(&RCC_APB2ENR, RCC_APB2ENR_IOPCEN);
    /* Set GPIO 8/9 (in GPIO port C) to 'output push-pull'. */
    gpio_set_mode(GPIOC, GPIO_MODE_OUTPUT_50_MHZ,
    GPIO_CNF_OUTPUT_PUSHPULL, GPIO8);
    gpio_set_mode(GPIOC, GPIO_MODE_OUTPUT_50_MHZ,
    GPIO_CNF_OUTPUT_PUSHPULL, GPIO9);
}

void user_button_enable(void)
{
    /*Enable GPIOA clock */
    rcc_peripheral_enable_clock(&RCC_APB2ENR, RCC_APB2ENR_IOPAEN);
    /*set GPIOA0 as input open-drain */
    gpio_set_mode(GPIOA, GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO0);
}

void timer2_init(void)
{
    rcc_peripheral_enable_clock(&RCC_APB1ENR, RCC_APB1ENR_TIM2EN);
    /*we can also use the inbuild fuctions declared in timer.h, but any way
    I am just directly configuring few registers
    */
    //RCC_CFGR |= RCC_CFGR_SW_SYSCLKSEL_PLLCLK|RCC_CFGR_PLLMUL_PLL_CLK_MUL3|RCC_CFGR_PLLSRC_HSE_CLK;
    //RCC_CR |= RCC_CR_HSEON|RCC_CR_PLLON;
    TIM2_CR1 |= TIM_CR1_CEN;
    TIM2_PSC = 1000;    //setting prescaler to 1000
    TIM2_ARR = 24000;    //auto reload value adjusted for 1 second
}

void led_heartbeat(void)
{
    volatile int i;
    GPIOC_ODR = GPIO8;
    i = 200000;
    while (i--) ;
    GPIOC_ODR = GPIO9;
    i = 200000;
    while (i--) ;
    GPIOC_ODR = 0;
}

int main(void)
{
    clock_setup();
    gpio_setup();
    timer2_init();
    while (1) {
        while (!(TIM2_SR & (TIM_SR_UIF))) ;    //polling for update interrupt flag
        TIM2_SR &= ~TIM_SR_UIF;    //clearing update interrupt flag
        led_heartbeat();    //a short duration(<<1s) blink on two leds
    }
    
    return 0;
}
https://github.com/vinodstanur/STM32VLDISCOVERY/tree/master/1second_timer

1 comment :

  1. Hello,

    Very nice post. I wanted to say that I’ve really enjoyed browsing your blog posts. In any case I’ll be subscribing to your feed and I hope you write again soon!


    Thanks,
    ATA Flash Cards

    ReplyDelete