Links to the compnents used in this post are at the bottom of the page

When I started with the STM32 microcontroller, I did not have easy access to a programmer for it as all those around were in use. I ordered a J-link EDU programmer so that I would no longer have this issue with any chip but in the mean time I tried to use what I had to get some code running.

For this I used a FT232RL USB to serial converter to program the chip with the help of the stm32flash tool as well as System Workbench for STM32 as my IDE. Both tools are available for Windows/MacOS/Linux which is important as I use both MacOS and Windows on a daily basis.

stm32flash is available here
System Workbench for STM32 can be found here

System Workbench requires the creation of an account to download but has worked very well for me and, as a part of the OpenSTM32 project, is completely free.

The wiring setup is as seen below. Unfortunately I do not have a nice diagram for the wiring, but the pin layout is as follows:

  • Serial Ground -> Gnd
  • Serial VCC -> 3.3V (Jumper on serial module set to 3.3V)
  • Serial TX -> A10
  • Serial RX ->A9

IMG_20180405_182011

In my case, the power from the USB plugged into the USB->Serial module was enough to power the board and I didn't have to use the on-board usb but I have heard that this is not always the case. If the red power LED lights up, the power should be fine.

In order to program the board, the boot jumpers have to be set to the configuration seen in the picture above. The jumper on the 'outside' of the board should be set to '1' while the jumper on the inside is set to '0'.

If the jumpers are set as described, the board will enter programming mode after the reset button is pressed and the currently flashed program will not run. If the jumpers are set to '00', the reset button will simply restart the currently flashed program.

Once the board is wired and powered, we can move on to installing the software.

Installing stm32flash and System Workbench on MacOS

The MacOS installation instructions are very much the same as for windows except instead of adding stm32flash to the path, you have to run sudo chmod +x stm32flash from the download directory.

Installing stm32flash and System Workbench on Windows

Installing System Workbench

System Workbench for STM32 can be found here

Of the two programs, we will start with System workbench. From the link above, create an account and validate your email. When your account is active, open the link again.

The install of system workbench is very straightforward as it's made for specifically this application:

  1. Accept all the terms and conditions as you do with everything
  2. Make sure all the boxes are checked
  3. Configure as you prefer
  4. Install
  5. Install the ST-link drivers

We install the ST-link drivers as they are very useful if you ever get the hardware and need to do debugging.

Installing stm32flash

stm32flash is available here

The install of stm32flash on windows is slightly more complex:

  1. Download the stm32flash from the above link
  2. Extract the folder and move it to a safe location (if you clear Downloads regularly)
  3. Copy the file path and search windows for "path" and choosing "Edit the System Environment Variables"
    select "Environment Variables" -> select "path" from "System variables" -> Edit -> New -> paste the path you copied
  4. accept changes and open CMD
  5. In the Command Prompt enter stm32flash and it should output the help page
stm32flash 0.5

http://stm32flash.sourceforge.net/

ERROR: Device not specified
Usage: stm32flash [-bvngfhc] [-[rw] filename] [tty_device | i2c_device]
        -a bus_address  Bus address (e.g. for I2C port)
        -b rate         Baud rate (default 57600)
        -m mode         Serial port mode (default 8e1)
        -r filename     Read flash to file (or - stdout)
        -w filename     Write flash from file (or - stdout)
        -C              Compute CRC of flash content
        -u              Disable the flash write-protection
        -j              Enable the flash read-protection
        -k              Disable the flash read-protection
        -o              Erase only
        -e n            Only erase n pages before writing the flash
        -v              Verify writes
        -n count        Retry failed writes up to count times (default 10)
        -g address      Start execution at specified address (0 = flash start)
        -S address[:length]     Specify start address and optionally length for
                                read/write/erase operations
        -F RX_length[:TX_length]  Specify the max length of RX and TX frame
        -s start_page   Flash at specified page (0 = flash start)
        -f              Force binary parser
        -h              Show this help
        -c              Resume the connection (don't send initial INIT)
                        *Baud rate must be kept the same as the first init*
                        This is useful if the reset fails
        -i GPIO_string  GPIO sequence to enter/exit bootloader mode
                        GPIO_string=[entry_seq][:[exit_seq]]
                        sequence=[-]n[,sequence]
        -R              Reset device at exit.

Examples:
        Get device information:
                stm32flash /dev/ttyS0
          or:
                stm32flash /dev/i2c-0

        Write with verify and then start execution:
                stm32flash -w filename -v -g 0x0 /dev/ttyS0

        Read flash to file:
                stm32flash -r filename /dev/ttyS0

        Read 100 bytes of flash from 0x1000 to stdout:
                stm32flash -r - -S 0x1000:100 /dev/ttyS0

        Start execution:
                stm32flash -g 0x0 /dev/ttyS0

        GPIO sequence:
        - entry sequence: GPIO_3=low, GPIO_2=low, GPIO_2=high
        - exit sequence: GPIO_3=high, GPIO_2=low, GPIO_2=high
                stm32flash -R -i -3,-2,2:3,-2,2 /dev/ttyS0

If the help page displays sucessfully then it is set up properly.

Using stm32flash and System Workbench on Windows

Now we will use these two tools to compile and flash a test program to the board.

Now we can open system workbench and create a new project. System workbench will prompt for a workspace directory which you can set to whatever you want. I have it set to the GitHub directory for my stm32 code.

  1. Create a new project under File -> new -> C project
  2. Name your project something like 'Blink' and select Ac6 STM32 MCU GCC as the project type then click next.
    newproject
  3. Ensure Debug and Release are checked before pressing next
  4. Select the series 'STM32F1' and the board NUCLEO-F1-3RB then press next
    boardsel
  5. Select the Standard Peripheral Library firmware and download it if necessary.
  6. Press finish

The project should now be generated. If the screen stays the same, I have found that I need to sometimes restart Eclipse/System Workbench and redo the project creation.

On the left side of the screen should be the Project explorer with your project open. If you navigate into the 'src' folder you should find your main.c.

We will replace all of the code in this file with:

/**
  ******************************************************************************
 /**
  ******************************************************************************
  * @file    main.c
  * @author  Ac6
  * @version V1.0
  * @date    01-December-2013
  * @brief   Default main function.
  ******************************************************************************
*/


#include "stm32f10x.h"
#include "stm32f1xx_nucleo.h"
			

/*
 * Delay function:
 * Uses the SysTick timer to wait for
 * an arbitrary time in microseconds.
 *
 * The clock source is assumed to be
 * the internal 8MHz RC oscillator
 * divided by 8 (1MHz)
 */
void delay_us(unsigned int time)
{
	/*
	 * Load the delay period in microseconds
	 * assuming a 1MHz source
	 */
	SysTick->LOAD = time;

	/*
	 * Clears the current value and the count flag
	 */
	SysTick->VAL = 0;

	/*
	 * Waits until the count ends
	 */
	while(!(SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk));
}

int main()
{
	/*
	 * Enable all Ports and Alternate Function clocks
	 */
	RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPBEN |
	    RCC_APB2ENR_IOPCEN | RCC_APB2ENR_IOPDEN | RCC_APB2ENR_AFIOEN;

	/*
	 * Disable JTAG and SWO (Free PB3, PB4 and PA15)
	 */
	AFIO->MAPR = AFIO_MAPR_SWJ_CFG_JTAGDISABLE;

	/*
	 * Enable the SysTick Timer with
	 * the CPU clock divided by 8
	 */
	SysTick->CTRL = SysTick_CTRL_ENABLE_Msk;

	/*
	 * Enable the PA1 as a digital output
	 */
	GPIOA->CRL = 0x00000020;

	/*
	 * Infinite loop
	 */
	while(1)
	{
		GPIOA->BSRR = GPIO_BSRR_BS1; //PA1 = 1 (Led OFF)
		delay_us(500000); //500ms delay
		GPIOA->BSRR = GPIO_BSRR_BR1; //PA1 = 0 (Led ON)
		delay_us(500000); //500ms delay
	}
}

Once the code is in, click the hammer in the top left hand corner to build the project. Building the project will generate the bilary file to be flashed to the microcontroller.

This code will alternate the state of GPIO pin A1 so connect an led between pin A1 and ground.

Before going to the next step, open Device Manager and find the COM port that the serial connection is on.

com

In the command prompt, navigate to the project directory using cd. In each project directory is a folder called "Debug" which contains the .bin file. cd in to this folder and run the following command.

stm32flash -w <yourfilename>.bin -g 0x0 <COMX>

The output for me looks like:

C:\Users\Eric\workspace>cd helloworld/Debug

C:\Users\Eric\workspace\helloworld\Debug>stm32flash -w helloworld.bin -g 0x0 COM3

stm32flash 0.5

http://stm32flash.sourceforge.net/

Using Parser : Raw BINARY
Interface serial_w32: 57600 8E1
Version      : 0x22
Option 1     : 0x00
Option 2     : 0x00
Device ID    : 0x0410 (STM32F10xxx Medium-density)
- RAM        : 20KiB  (512b reserved by bootloader)
- Flash      : 128KiB (size first sector: 4x1024)
- Option RAM : 16b
- System RAM : 2KiB
Write to memory
Erasing memory
Wrote address 0x0800092c (100.00%) Done.

Starting execution at address 0x08000000... done.

If you see the same results your LED should be flashing!

A more complicated but more generic listing of code for flashing the onboard LED is at the end of this post.

More Complicated Code but Fewer Wires

This set of code is more complicated to set up but is much nicer in terms of the number of wires and different features usful in the future.

/**
  ******************************************************************************
  * @file    main.c


  * This code is an adaptation made from code posted on github by RoanFourie.
  ******************************************************************************
*/


#include "stm32f10x_conf.h"
#include "rf_stm32f1_led.h"
#include "rf_stm32f1_delay.h"

static void button_setup(void)
{
    GPIO_InitTypeDef  GPIO_InitStructure;

	// Clock Enable
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);

    // Configure as digital input
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
}

int main(void)
{
    // initialize the system frequency
    SystemInit();
    // Delay initialize
    delay_init();
    // all LED initialize
    FM_Led_Init();
    // Initialize the button
    button_setup();

    float duty=0.5;
    float step=0.001;

    while(1)
    {
	  	delay_us(1000*duty);
        FM_Led_Toggle(LED_1);
        delay_us(1000-1000*duty);
        FM_Led_Toggle(LED_1);

        duty=duty-step;

        if (duty<0.01||duty>0.99)
        {
        	step=-step;
        }
    }
}

This program flashes the onboard LED and is adapted from the work of RoanFourie.

Though this code is already pretty long, it uses a few extra files that allow us to sumplify the code in main.c. In the src folder, right click and create 4 new files, 2 .c files and 2 .h files with the names:

  • rf_stm32f1_led.h
  • rf_stm32f1_delay.h
  • rf_stm32f1_led.c
  • rf_stm32f1_delay.c

The code in rf_stm32f1_led.h is:

/***************************************************************
* File     : rf_stm32f1_led.h
***************************************************************/

/***************************************************************
// Header Files Includes
***************************************************************/
#include "stm32f10x.h"
#include "stm32f10x_gpio.h"
#include "stm32f10x_rcc.h"

/***************************************************************
* List of All LEDs
***************************************************************/
typedef enum
{
  LED_1 = 0,
  LED_2 = 1,
  //LED_3 = 2,
  //LED_4 = 3
}LED_NAME;

#define  TOTAL_LED   2 // total LEDs LED_NAME_t

/***************************************************************
* Status of LEDs
***************************************************************/
typedef enum {
  LED_OFF = 0,  // LED OFF
  LED_ON        // LED ON
}LED_STATUS;

/***************************************************************
* Structure of  LED
***************************************************************/
typedef struct {
  LED_NAME LED_NAME;        // Name
  GPIO_TypeDef* LED_PORT;   // Port
  const uint16_t LED_PIN;   // Pin
  const uint32_t LED_CLK;   // Clock
  LED_STATUS LED_INIT;      // status
}LED_struct;

/***************************************************************
* Global Function
***************************************************************/
void FM_Led_Init(void);
void FM_Led_Off(LED_NAME led_name);
void FM_Led_On(LED_NAME led_name);
void FM_Led_Toggle(LED_NAME led_name);

/***************************************************************/

The code in rf_stm32f1_delay.h is:

/***************************************************************
* File     : rf_stm32f1_delay.h
***************************************************************/

/***************************************************************
// Header Files Includes
***************************************************************/
#include "stm32f10x.h"
#include "misc.h"

/***************************************************************
* Global Function
***************************************************************/
void FM_delay_init(void);
void delay_ms(u16 nms);
void delay_us(u32 nus);
void delay_init();

/***************************************************************/

The code in rf_stm32f1_led.c is:

/***************************************************************
* File     : rf_stm32f1_led.c
***************************************************************/

#include "rf_stm32f1_led.h"

/***************************************************************
* All LEDs on Stm32f1 discovery board
***************************************************************/
LED_struct LED[] = {
  //Name, PORT , PIN       , CLOCK            , Init
  {LED_1,GPIOC,GPIO_Pin_13,RCC_APB2Periph_GPIOC,LED_OFF},    // PC13 = On Board User LED
  {LED_2,GPIOB,GPIO_Pin_8,RCC_APB2Periph_GPIOB,LED_OFF},    // PB8 = External configured LED (i.e. on Breadboard
};

/***************************************************************
* Initialize all LEDs
***************************************************************/
void FM_Led_Init(void)
{
  GPIO_InitTypeDef  GPIO_InitStructure;
  LED_NAME led_name;

  for(led_name=0;led_name<TOTAL_LED;led_name++) {
    // Clock Enable
    RCC_APB2PeriphClockCmd(LED[led_name].LED_CLK, ENABLE);

    // Config as digital output
    GPIO_InitStructure.GPIO_Pin = LED[led_name].LED_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(LED[led_name].LED_PORT, &GPIO_InitStructure);
  }
}

/***************************************************************
* LED off
***************************************************************/
void FM_Led_Off(LED_NAME led_name)
{
  GPIO_WriteBit(LED[led_name].LED_PORT,LED[led_name].LED_PIN, Bit_SET);
}

/***************************************************************
* LED on
***************************************************************/
void FM_Led_On(LED_NAME led_name)
{
  GPIO_WriteBit(LED[led_name].LED_PORT,LED[led_name].LED_PIN, Bit_RESET);
}

/***************************************************************
* LED toggle
***************************************************************/
void FM_Led_Toggle(LED_NAME led_name)
{
  LED[led_name].LED_PORT->ODR ^= LED[led_name].LED_PIN;
}

the code in rf_stm32f1_delay.c is:

/***************************************************************
* File     : rf_stm32f1_delay.c
***************************************************************/

#include "rf_stm32f1_delay.h"

static u8  fac_us=0;  // micro second count
static u16 fac_ms=0;  // mili second count

/***************************************************************
* delay Initialize
***************************************************************/
void delay_init()
{
	SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);	//HSI  HCLK/8
    fac_us=SystemCoreClock/8000000;						//SYSCLK/8
	fac_ms=(u16)fac_us*1000;
}

/***************************************************************
* delay in micro second
***************************************************************/
void delay_us(u32 micro_sec)
{
	u32 temp;
	SysTick->LOAD=micro_sec*fac_us; //Load
	SysTick->VAL=0x00;        		//Clear
	SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ;
	do
	{
		temp=SysTick->CTRL;
	}
	while(temp&0x01&&!(temp&(1<<16)));
	SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;
	SysTick->VAL =0X00;
}

/***************************************************************
* Delay in mili second
***************************************************************/
void delay_ms(u16 mili_sec)
{
	u32 temp;
	SysTick->LOAD=(u32)mili_sec*fac_ms;
	SysTick->VAL =0x00;
	SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ;
	do
	{
		temp=SysTick->CTRL;
	}
	while(temp&0x01&&!(temp&(1<<16)));
	SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;
	SysTick->VAL =0X00;
}

From here, follow the same process for building and flashing as above. Enjoy!

Components Featured in this post:
5 pack of STM32F103C8T6
FT232RL USB to Serial Module
Jumper cables