Set up a Barebones STM32F4 development environment (Windows & Mac)

Every time I come back to developing for the STM32F4 discovery board, I end up searching for several hours for a basic barebones template and guide for setting everything up. I decided to make this post as a refresher for myself, and I am sure it will help many others out there. I have made a repository on my github page, which contains templates for Mac OSX and Windows (using .bat and Makefiles). The templates incorporate the ST Standard Peripheral Driver library as well as sample assembly code.

I prefer using a barebones setup (no IDE) when developing for the STM32F4. However, it is extremely easy to start development with an IDE. If you want to try out an IDE, I highly recommend CooCox CoIDE. The following steps are provided for those using either a Windows or Mac OSX operating system. If you are using Linux, then I recommend going to EliasElectronics; I have used this on my own system.

0. Install git

I highly recommend that you install git on your machine, regardless if you are using OSX or Windows.
There are tons of great examples on how to install git on Windows and OSX, so just do a google search.

Once you have git installed, clone the repo I have made which contains all of the Barebones templates.

Windows Toolchain Setup:

1. Install arm-none-eabi

We first need to download the GNU Toolchain for ARM embedded systems. Go to the following URL, and download the windows installer (as of now: 4.8 2014q3).

Go to wherever you downloaded the installer and run the executable to start the installation process. When you get to the step to choose a destination location, browse to a path without spaces, as seen below.

add gcc to PATH photo add_gcc_to_windows_path.png

Continue with the installation until you reach the final screen. Make sure you click “Add path to environment variable” as seen below.

installation Path arm-none-eabi photo ARM1.jpg

Now open up the command prompt and type:

arm-none-eabi-gcc --version

You should see the following results if the ARM toolchain was setup correctly.

 photo armnoneeabiCMD.jpg

At this point, you can start developing for the STM32F4. The only caveat is that you must use windows batch files in order to build your projects. This limits the cross compatibility of your code, as you will most likely be using some sort of makefile on OSX and Linux. However, this option is favorable as you do not need to download any other software to build your projects. Therefore, I have created a template for you to use, along with the batch scripts. You can find the template, Barebones_Windows_Batch, on my github.

If you choose to go this route you will still need the ST-Link Utility software in order to flash the .hex file to the ST board. Skip to step 3 for the instructions to install.

2. Install MinGW and MSYS (optional if using .bat files)

We will now install MinGW and MSYS in order to get a nice terminal emulator and many unix capabilites including make.
Download the latest mingw-get-setup.ext
Select mingw32-base, mingw32-gcc-g++, msys-base under ‘Basic Setup’
Installation Manager photo mingw_installation_manager.png
Select msys-minty and msys-wget under ‘All Packages’
Install Mintty photo install_mintty.png
Select Installation/Apply Changes to install the software selected
 photo mingw_installation_successful.png

Add the following to the your PATH variable:


 photo add_msys_to_the_path.png
Create a desktop shortcut to mintty terminal which is located here:C:\MinGW\msys\1.0\bin
Right click on the shortcut, click on properties, and change the target to:

C:\MinGW\msys\1.0\bin\mintty.exe /bin/bash -l

You now have all the command line utilities necessary to build the STM templates using the make command.

3. Install ST-Link Utility

In order to flash the STM32F4 board with our compiled project, we will use the ST-Link Utility.
Go to the link above and download the ST-Link Utility. Follow the steps as it will install all of the necessary drivers for connecting to the STM32 board.

4. Flash The STM32F4

After you build your project, either through make.bat, or the Makefile, you must then flash the .hex file to the ST board.
A. Connect your STM32F4-Discovery board to your PC
B. Open up the ST-Link Utility
C. Select File/Open File/ and navigate to the build/ directory and open the ***.hex file
D. Select Target/Program and Verify/Start to flash the board
 photo STLinkUtility.jpg

Mac OSX Toolchain Setup

1. Install arm-none-eabi

You can either download the toolchain as we did above and then edit your path variables, or we can use the homebrew package manager for osx, which is the way I will describe below.
A. Install Homebrew
Homebrew is a very nice package manager for OSX, much like apt-get on Ubuntu. There is a lot of information on their website, so feel free to take a look.
Otherwise, simply open the terminal, enter the following command, and follow all the steps to install.

ruby -e "$(curl -fsSL"

Now that we have homebrew installed, it is very easy to install the ARM toolchain. Simply enter the following commands:

brew tap PX4/homebrew-px4
brew update
brew install gcc-arm-none-eabi-48

This will install the latest (as of Oct. 5, 2014) ARM toolchain (4.8 2014q3)
To test that the installation works, enter:

arm-none-eabi-gcc --version

and you should get the following output
 photo ScreenShot2014-10-05at94513PM.png

2. Install ST-Link

In order to flash our programs onto the STM32F4 we will use a software called st-link. We will need to get and compile from source, the st-link software. Run the following commands to build st-link. *This assumes you are at your home (~) directory.

mkdir src && cd src
git clone git://
cd stlink

Now we will install the st-link tools we will use and make them available via the PATH variable. *Again, this assumes you are in the ~/src/stlink directory from above.

sudo mkdir /usr/local/opt/stlink
sudo cp st-flash /usr/local/opt/stlink/.
sudo cp st-util /usr/local/opt/stlink/.

Now we must add the st-link to our path (be cautious with this)

sudo bash -c "echo /usr/local/opt/stlink >> /etc/paths"
---> OR, edit the file yourself and append "/usr/local/opt/stlink"
sudo emacs /etc/paths

To test the st-link is working, restart the terminal, plug in your STM32F4-Discovery board, and enter st-util into the terminal. You should get the following output.

 photo ScreenShot2014-10-05at94836PM.png

Have fun developing

Now everything should be setup and you can start developing on either Windows or OSX. I hope this helped some of you and saved many hours hunting. Let me know if you have any questions, and feel free to checkout my youtube page,, for tutorials on the STM32F4-Discovery Board. Thanks!!

Telemetry: 3.2″ Touchscreen LCD

 photo 3d05e3b8-6223-44d5-9986-d808b3d58494.jpg

LCD_Back photo LCD_Back.jpg

I bought this 3.2″ TFT LCD screen, shipped from China for only $16.×240-Touch-LCD-A.htm

The main reason I ordered this LCD screen was because I wanted something large enough to be able to see on the dashboard, but I didn’t want something too large. I have also been looking into a graphics package, uGFX, which works with the ChibiOS RTOS. The LCD and Touch controllers on this LCD screen are supported by the graphics package I mentioned, so there is no need to write the drivers for them to work. I will be connecting the screen temporarily by wires, but I will eventually design a PCB shield to connect the LCD to the STM32F4 board. I will post the wiring connections soon, so stay tuned.

Telemetry: Bluetooth Testing Code

The following code is what I used to test the Bluetooth communications. You can modify this code to send only a few commands and see the response back, but otherwise this code will continually send the AT command for the vehicle speed and the Hex returned value will be output on the computer terminal program.

#include <stm32f4xx.h>
#include <misc.h>
#include <stm32f4xx_usart.h>
#include <stm32f4xx_gpio.h>
#include <stm32f4xx_rcc.h>

#define BAUD_RATE 38400
#define PROMPT '>'			//End of ELM response character
#define DONE 1				//Tells status of ELM output
#define WAITING 0			//Tells status of ELM output
#define MAX_STRLEN 50 		// this is the maximum string length of our string in characters
volatile char received_string1[MAX_STRLEN+1]; // this will hold the recieved string from USART1
volatile char received_string2[MAX_STRLEN+1]; // this will hold the recieved string from USART2
volatile uint32_t msTicks;  //counts 1ms timeTicks
int ELM_reading = WAITING;	//Elm Starts out in waiting mode, not finished sending data or waiting

#define ELM_RESET 	"ATZ\r"
#define ADD_LINES 	"ATL1\r"
#define ENGINE_RPM 	"010C\r"
#define VEHICLE_SPEED 	"010D\r"
#define MAF_RATE 	"0110\r"

  delays number of tick Systicks (happens every 1 ms)
void Delay (uint32_t dlyTicks) {
  uint32_t curTicks;

  curTicks = msTicks;
  while ((msTicks - curTicks) < dlyTicks);
void SysTick_Handler(void) {

void init_USART1(uint32_t baudrate){

	GPIO_InitTypeDef GPIO_InitStruct; 	 // this is for the GPIO pins used as TX and RX
	USART_InitTypeDef USART_InitStruct;  // this is for the USART1 initialization
	NVIC_InitTypeDef NVIC_InitStructure; // this is used to configure the NVIC (nested vector interrupt controller)

	/* enable APB2 peripheral clock for USART1
	 * note that only USART1 and USART6 are connected to APB2
	 * the other USARTs are connected to APB1
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);

	/* enable the peripheral clock for the pins used by
	 * USART1, PB6 for TX and PB7 for RX
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);

	/* This sequence sets up the TX and RX pins
	 * so they work correctly with the USART1 peripheral
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7; // Pins 6 (TX) and 7 (RX) are used
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF; 			// the pins are configured as alternate function so the USART peripheral has access to them
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;		// this defines the IO speed and has nothing to do with the baudrate!
	GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;			// this defines the output type as push pull mode (as opposed to open drain)
	GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;			// this activates the pullup resistors on the IO pins
	GPIO_Init(GPIOB, &GPIO_InitStruct);					// now all the values are passed to the GPIO_Init() function which sets the GPIO registers

	/* The RX and TX pins are now connected to their AF
	 * so that the USART1 can take over control of the
	 * pins
	GPIO_PinAFConfig(GPIOB, GPIO_PinSource6, GPIO_AF_USART1); //

	/* Now the USART_InitStruct is used to define the
	 * properties of USART1
	USART_InitStruct.USART_BaudRate = baudrate;				// the baudrate is set to the value we passed into this init function
	USART_InitStruct.USART_WordLength = USART_WordLength_8b;// we want the data frame size to be 8 bits (standard)
	USART_InitStruct.USART_StopBits = USART_StopBits_1;		// we want 1 stop bit (standard)
	USART_InitStruct.USART_Parity = USART_Parity_No;		// we don't want a parity bit (standard)
	USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None; // we don't want flow control (standard)
	USART_InitStruct.USART_Mode = USART_Mode_Tx | USART_Mode_Rx; // we want to enable the transmitter and the receiver
	USART_Init(USART1, &USART_InitStruct);					// again all the properties are passed to the USART_Init function which takes care of all the bit setting

	/* Here the USART1 receive interrupt is enabled
	 * and the interrupt controller is configured
	 * to jump to the USART1_IRQHandler() function
	 * if the USART1 receive interrupt occurs
	USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); // enable the USART1 receive interrupt

	NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;		 // we want to configure the USART1 interrupts
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;// this sets the priority group of the USART1 interrupts
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;		 // this sets the subpriority inside the group
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;			 // the USART1 interrupts are globally enabled
	NVIC_Init(&NVIC_InitStructure);							 // the properties are passed to the NVIC_Init function which takes care of the low level stuff

	// finally this enables the complete USART1 peripheral

void init_USART2(uint32_t baudrate){
	GPIO_InitTypeDef GPIO_InitStruct; 	 // this is for the GPIO pins used as TX and RX
	USART_InitTypeDef USART_InitStruct;  // this is for the USART2 initialization
	NVIC_InitTypeDef NVIC_InitStructure; // this is used to configure the NVIC (nested vector interrupt controller)

	// enable Clocks for APB1 and GPIOA
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);

	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_3; // Pins 2 (TX) and 3 (RX) are used
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF; 			
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;		
	GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;			
	GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;			
	GPIO_Init(GPIOA, &GPIO_InitStruct);					

	//Connect to AF
	GPIO_PinAFConfig(GPIOA, GPIO_PinSource2, GPIO_AF_USART2); //

	USART_InitStruct.USART_BaudRate = baudrate;				
	USART_InitStruct.USART_WordLength = USART_WordLength_8b;
	USART_InitStruct.USART_StopBits = USART_StopBits_1;		
	USART_InitStruct.USART_Parity = USART_Parity_No;		
	USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None; 
	USART_InitStruct.USART_Mode = USART_Mode_Tx | USART_Mode_Rx; 
	USART_Init(USART2, &USART_InitStruct);					

	USART_ITConfig(USART2, USART_IT_RXNE, ENABLE); // enable the USART2 receive interrupt

	NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;		 
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;		 
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;			 


void USART_puts(USART_TypeDef* USARTx, volatile char *s){

		// wait until data register is empty
		while( !(USARTx->SR & 0x00000040) );
		USART_SendData(USARTx, *s);

void send_command(volatile char *command){
	while(ELM_reading==0){			//Stay here if ELM is still responding (i.e. ELM_reading==0)
	ELM_reading=WAITING;			//Set value back to waiting
	USART_puts(USART1, command);	//Send command passed in to the OBDII

int main(void){	
	volatile char RPM_value[5], MAF[5], Vss[3];
	int i=0;

	  SystemCoreClockUpdate();                      /* Get Core Clock Frequency   */
	  if (SysTick_Config(SystemCoreClock / 1000)) { /* SysTick 1 msec interrupts  */
	    while (1);                                  /* Capture error              */

	  init_USART1(BAUD_RATE); 	//init BT_USART to 38400 baud
	  init_USART2(BAUD_RATE); 	//init COMP_USART to 38400 baud

	  Delay(5000);		//!!Delay 5 sec for testing, let BT connect to each other
	  send_command(ELM_RESET);	//Reset ELM
	  Delay(5000);		//!!Delay 5 sec
	  send_command(ENGINE_RPM);	//Get rid of buffer from start "SEARCHING..."
	  //Infinite loop sending/receiving vehicle speed
		  send_command(VEHICLE_SPEED);	//Then get real RPM values
			  if(received_string1[i]=='4' && received_string1[i+1]=='1'){
				  /*Output Vehicle speed in Hex*/
				  USART_puts(USART2, "\r\n\nVehicle Speed:");	//in km/hr
				  USART_puts(USART2, Vss);
				  USART_puts(USART2, "\r\n\n");	
	  while (1);	//Stay in program

// this is the interrupt request handler (IRQ) for ALL USART1 interrupts
void USART1_IRQHandler(void){
	// check if the USART1 receive interrupt flag was set

		static uint8_t cnt = 0; // this counter is used to determine the string length
		char t = USART1->DR; // the character from the USART1 data register is saved in t

		//Check for PROMPT, which signifies the end of ELM response
		if((t!=PROMPT) && (cnt < MAX_STRLEN) ){		//PROMPT == '>'
			received_string1[cnt] = t;
		else{		//We reached the end of prompt OR too many characters
			received_string1[cnt] = t;
			cnt = 0;
			USART_puts(USART2, received_string1);	//!!Send response to USART2
			ELM_reading = DONE;		//Done == 1, set back to zero right away (Poll this in code until == 1)

// this is the interrupt request handler (IRQ) for ALL USART2 interrupts
void USART2_IRQHandler(void){

	// check if the USART2 receive interrupt flag was set

		static uint8_t cnt = 0; // this counter is used to determine the string length
		char t = USART2->DR; // the character from the USART2 data register is saved in t

		/* check if the received character is not the LF character (used to determine end of string)
		 * or the if the maximum string length has been been reached
		if( (t!=0xA && t!=0xD) && (cnt < MAX_STRLEN) ){	//!!0xA == '\n' and 0xD == \r Carriage Return
			received_string2[cnt] = t;
		else{ // otherwise reset the character counter and print the received string
			cnt = 0;
			USART_puts(USART1, received_string2);	//!!Send response to USART1


Telemetry: Bluetooth

Now that I know how to communicate with the on board diagnostics through USART, I decided to incorporate Bluetooth. The Bluetooth modules that I decided to go with are the HC-05 modules. I chose these because you can adjust the mode for each individual module to slave or master and they are extremely easy to implement. The master module will connect to the ELM327 scanner and the slave will connect to the Discovery board. You could buy an ELM327 scanner that already has Bluetooth, and then you would only need the slave module, but I already had the wired scanner.

Before connecting these modules to the scanner and stm32, you have to set the modes, one to master, and the other to slave. There is already a great blog that describes these steps, which I used to set my modules.
Getting-Bluetooth-modules-talking-to-each-other, by Zak Kemble

Now that the modules are setup, you should be able to power them on and they should automatically connect to each other (if you followed the steps from the site posted). Now it is time to wire them to the scanner and discovery board.

Master Module:
ELM327        BT
5V       —>    5V
GND    —>   GND
Tx        —>   Rx
Rx        —>   Tx

Slave Module:
BT               Discovery
5V       —>    5V
GND   —>    GND
Tx       —>    Rx (PA3 – Any USARTx Rx Line)
Rx       —>    Tx (PA2 – Any USARTx Tx Line)

For the connections to the Discovery board, you can choose any USART lines. For the code that I wrote I chose to use USART2, PA2 and PA3.

To test out the Bluetooth, I wrote a code in Keil that sends anything received on USART2 (Rx from the BT) to USART1. I then connected the USB to USART module to USART1 and my laptop. Anything that is sent from the ELM scanner will be sent through both BT, then to USART1 which is connected to my laptop, this way I can monitor the activity using the terminal. I also connected the Tx lines from USART1 to USART2 so I could also send commands from the terminal. This is pretty much the same setup I used for development, until I got my LCD screen.

Another cool note. You can connect the slave module to the ELM scanner, and then connect to this scanner from your phone (Only Android, not Iphone). You can download a Bluetooth terminal app and send commands to the OBDII. This is really handy when you want to test the output of a particular command quickly. *NOTE: You cannot connect to the master module from your phone, only other slave modules, this is because a master cannot connect to a master and a slave cannot connect to a slave.

Telemetry: Hacking the ELM327 Scanner

The first thing that I decided to do for this project was to hack the ELM327 scanner to get serial transmission directly from the board and not the USB that was attached to the scanner. I know I wanted to eventually use the HC-05 Bluetooth modules which talk over UART, so I needed a way to access the data signals from the ELM327. I ended up opening the ELM327 scanner by removing the 4 screws that are WELL hidden underneath the main label where the LEDs are located.
 photo OpeningElm327_1.jpg

I forgot to take a picture at this point right after opening the scanner, so I borrowed these pictures from google. After opening the ELM327 you will see a chip layout similar to below. My ELM scanner is actually a clone and uses the PIC18f258 chip as you can see in the middle.
 photo OpeningElm327_3-2.jpg

To access the data lines from the ELM327, I looked at the schematic for the ELM327 chip and got the following (color references are from the picture above)
Pin 17 (Purple) —> RS232 Tx
Pin 18 (Yellow) —> RS232 Rx
Pin 19 (Black) —> GND
Pin 20 (Red) —> Vcc (5v)
 photo ELM_CHIP.jpg

I soldered 4 wires to the 4 pins above and cut off the USB cable. I am going to add header pins to the location where the USB cables were connected, that way I can always reconnect the USB if need be. I then connected the Tx and Rx lines to the Rx and Tx respectively of the USB to UART module to test communications.
 photo ConnectTxRx.jpg

I went out to the car, plugged the OBDII into the car, and connected the USB to UART into my laptop. I connected to the serial COM port on my laptop and was able to send AT commands to the OBDII interface through the serial lines I soldered. This was a huge step because now I know that you can communicate through these lines. I can then focus on getting a Bluetooth Connection working. You could possibly buy an ELM scanner that has Bluetooth with it, but I wanted to pair two Bluetooth modules instead of hoping that I could pair to the given module on the ELM327. This is why I did not buy another scanner and decided to hack mine. It did not cost me anything to add the two wires to the scanner and enable serial transmission. There is a product out there that they already soldered the wires for you, but I think it is around $40. Considering the scanner is about $10 from ebay, I think it’s worthwhile to add the wires yourself. I learned a lot about the ELM MCU in the datasheet, and it seems like it will be invaluable when I go to program how to communicate with the OBDII later on.

STM32F4 Car Telemetry System

I will be tracking my progress on here as I develop a car telemetry system using the STM32F4-Discovery board.

The goal of this project is to be able to monitor and display data from the OBDII port in the car onto a display connected to the ST board. The main features at the beginning will be bluetooth communication, touchscreen LCD display, and just some basic readings (such as Engine Temp, RPM, Current gas mileage, etc).

I am going to try to be as clear and concise as possible, so someone else can replicate what I have done.


STM32F4-Discovery Board
 photo 847f6f6b-f252-4cb7-a738-f2b02312edac.jpg

(2) HC05 Bluetooth modules (w/ Breakout board)
 photo HC05_BT_Front.jpg
 photo HC05_BT_Back.jpg

ELM327 OBDII Scanner
 photo ELM327Scanner.jpg

CP2102 USB to UART module (Used for testing)
 photo CP2102_USBtoUART.jpg

LCD Screen (TBD)
Connecting Wires
Software to develop code for the Cortex M4, I am going to be using CooCox CoIDE, however the code should be easily portable