Programming STM32 Based Boards with the Arduino IDE

Since it’s inception the Arduino IDE has demonstrated the desire to support all kind of platforms, from Arduino clones and variations of different manufacturers to third party boards like the ESP32 and ESp8266. As more people get familiar with the IDE, they are beginning to support more boards that are not based on ATMEL chips and for today’s tutorial we will look on one of such boards. We will examine how to program the STM32 based, STM32F103C8T6 development board with the Arduino IDE.

Blue Pill Board

 

The STM32 board to be used for this tutorial is none other than the STM32F103C8T6 chip based STM32F1 development board commonly referred to as “Blue Pill” in line with the blue color of its PCB. Blue Pill is powered by the powerful 32-bit STM32F103C8T6 ARM processor, clocked at 72MHz. The board operates on 3.3v logic levels but its GPIO pins have been tested to be 5v tolerant. While it does not come with WiFi or Bluetooth like the ESP32 and Arduino variants, it offers 20KB of RAM and 64KB of flash memory which makes it adequate for large projects. It also possesses 37 GPIO pins, 10 of which can be used for Analog sensors since they have ADC enabled, along with others which are enabled for SPI, I2C, CAN, UART, and DMA. For a board which costs around $3, you will agree with me that these are impressive specs. A summarized version of these specifications compared with that of an Arduino Uno is shown in the image below.

Comparing the Blue Pill with Arduino Uno

Based on the specs above, the frequency at which Blue pill operates is about 4.5 times higher than an Arduino UNO, for today’s tutorial, as an example on how to use the STM32F1 board, we will connect it to a 1.44″ TFT display and program it to calculate the “Pi” constant. We will note how long it took the board to obtain the value an compare it with the time it takes an Arduino Uno to perform the same task.

Required Components

The following components are required to build this project;

  1. STM32 Board
  2. FTDI Programmer
  3. Color TFT
  4. Push Button
  5. Small Breadboard
  6. Wires
  7. Power Bank
  8. USB to Serial Converter

As usual, all the components used for this tutorial can be bought from the attached links. The power bank is however only needed if you want to deploy the project in a stand-alone mode.

Schematics

As mentioned earlier, we will connect the STM32F1 board to the 1.8″ ST7735 based colored TFT Display along with a push button. The push button will be used to instruct the board to start the calculation.

Connect the components as shown in the Schematics below.



To make the connections easy to replicate, the pin to pin connections between the STM32 and the display is described below.

STM32 – ST7735

5V - VCC
GND - GND
PA2 - CS
PA3 - DC
PA4 - RST
PA5 - SCK
PA7 - SDA
3.3V - LED

Go over the connections once again to be sure everything is as it should be as it tends to get a little bit tricky. With this done, we proceed to set up the STM32 board to be programmed with the Arduino IDE.

Setting up the Arduino IDE for STM32

As with most boards not made by Arduino, a bit of setup needs to be done before the board can be used with the Arduino IDE.  This involves installing the board file either via the Arduino Board manager or downloading from the internet and copy the files into the hardware folder. The Board Manager route is the less tedious one and since the STM32F1 is among the listed boards, we will go that route.

Start by adding the link for the STM32 board to the Arduino preference lists. Go to File -> Preferences, then enter this URL ( http://dan.drown.org/stm32duino/package_STM32duino_index.json ) in the box as indicated below and click ok.

Now go to Tools -> Board -> Board manager, it will open a dialogue box with a search bar. Search for STM32F1 and install the corresponding package.

The installation procedure will take a few seconds. After that, the board should now be available for selection under the Arduino IDE board list.

Code

The code will be written the same way we’d write any other sketch for an Arduino project, with the only difference being the way the pins are referenced.

To be able to easily develop the code for this project, we will use two libraries which are both modifications of standard Arduino Libraries to make them compatible for the STM32. We will use the modified version of the Adafruit GFX  and the Adafruit ST7735 libraries. Both libraries can be downloaded via the links attached to them.

As usual, I will be doing a short breakdown of the code. We start the code by importing the two libraries that we will use.

#include <Adafruit_GFX_AS.h>  //https://github.com/rogerclarkmelbourne/Arduino_STM32/tree/master/STM32F1/libraries/Adafruit_GFX_AS
#include "Adafruit_ST7735.h" // https://github.com/KenjutsuGH/Adafruit-ST7735-Library

Next, we define the pins of the STM32 to which the CS, RST, and DC pins of the LCD are connected.

#define cs     PA2
#define rst    PA4 
#define dc     PA3

Next, we create some color definitions to make it easy to use colors by their names in the code later instead of by their hex values.

// Color definitions
#define BLACK    0x0000
#define BLUE     0x001F
#define RED      0xF800
#define GREEN    0x07E0
#define CYAN     0x07FF
#define MAGENTA  0xF81F
#define YELLOW   0xFFE0 
#define WHITE    0xFFFF

Next, we set the number of iterations we want the board to go through along with the refresh duration for the progress bar to be used.

#define ITERATIONS 500000L    // number of iterations
#define REFRESH_TFT 7500      // refresh bar every 7500 iterations
#define ACTIVATED LOW

With this done, we create an object of the ST7735 library which will be used to reference the display all through the entire project. We also indicate the pin of the STM32 to which the pushbutton is connected and create a variable to hold its state.

Adafruit_ST7735 tft = Adafruit_ST7735(cs, dc, rst);

int percent = 0;
int progress = 1;
const int buttonPin = PB9;     
int buttonState = 0;

With this done, we move to the void setup() function.

We start by setting the pinMode() of the pin to which the pushbutton is connected, activating an internal pull-up resistor on the pin since the pushbutton connects to ground when pressed.

void setup(void) {

  pinMode(buttonPin, INPUT_PULLUP);

Next, we initialize serial communication and the screen, setting the background of the display to black and calling the printUI() function to display the interface.

  Serial.begin(9600);
  
  tft.initR(INITR_BLACKTAB);   // initialize a ST7735S chip, black tab

  tft.fillScreen(ST7735_BLACK);

  printUI();
 
}

Next is the void loop() function.

The void loop function is quite simple and short, thanks to the use of libraries/functions.

We start by reading the state of the push button. If the button has been pressed, we remove the current message on the screen using the removePressKeyText() and draw the changing progress bar using the drawBar() function. We then call the start calculation function to obtain and display the value of Pi along with the time it took to calculate it.

void loop() {
  
  buttonState = digitalRead(buttonPin);
  if (buttonState == ACTIVATED) {   
    removePressKeyText();
    drawBar();
    startCalculation();
  }

If the pushbutton is not pressed, the device stays in Idle mode with the screen demanding that a key be pressed to interact with it.

else {
  }

Finally, a delay is inserted at the end of the loop to give a bit of time before sketch “loops”.

delay(10);
}

The remaining part of the code are the functions called to achieve the tasks from drawing the bar to calculating the Pi. Most of these functions have been covered in several other tutorials that involve the use of the ST7735 display.

void fillBar(int percent)
{
    int counter =60;
    percent = map(percent,0,100,5,121);     
    for(counter = 60; counter<75;counter++)
    {
     tft.drawFastHLine(5, counter, percent, YELLOW );
    }
}

void printUI()
{
 tft.setCursor(30,5);
 tft.setTextColor(RED);
 tft.setTextSize(1);
 tft.print("PI BENCHMARK");

 tft.setCursor(30,60);
 tft.setTextColor(WHITE);
 tft.setTextSize(1);
 tft.print("PRESS KEY");
 
 tft.setCursor(5,120);
 tft.setTextColor(RED);
 tft.setTextSize(1);
 tft.print("PI:");

 tft.setCursor(5,140);
 tft.setTextColor(RED);
 tft.setTextSize(1);
 tft.print("TIME:");
}

void removePressKeyText()
{
 tft.setCursor(30,60);
 tft.setTextColor(BLACK);
 tft.setTextSize(1);
 tft.print("PRESS KEY");
}

void drawBar()
{
  tft.drawRect(5,60,120,15, YELLOW);
}

void startCalculation()
{
 unsigned long start, time;
 unsigned long niter=ITERATIONS;
 int LEDcounter = 0;
 unsigned long i;
 float x = 1.0;
 float pi=1.0;

 Serial.begin(9600);
 Serial.print("Beginning ");
 Serial.print(niter);
 Serial.println(" iterations...");
 Serial.println();

 start = millis();  
 for ( i = 2; i < niter; i++) {
   x *= -1.0;
   pi += x / (2.0f*(float)i-1.0f);   
    if (LEDcounter++ > REFRESH_TFT) {
     LEDcounter = 0;
     progress++;
     percent = progress*100/(ITERATIONS/ REFRESH_TFT);
     fillBar(percent);
    }
 }
 time = millis() - start;
 
 pi = pi * 4.0;

 Serial.print("# of trials = ");
 Serial.println(niter);
 Serial.print("Estimate of pi = ");
 String piString = String(pi,7);
 String timeString = String(time)+"ms";
 Serial.println(piString);
 
 Serial.print("Time: "); Serial.print(time); Serial.println(" ms");
 
 tft.setCursor(40,120);
 tft.setTextColor(GREEN);
 tft.setTextSize(1);
 tft.print(piString);

 tft.setCursor(40,140);
 tft.setTextColor(GREEN);
 tft.setTextSize(1);
 tft.print(timeString);
}

The complete code for the project is available below and is attached under the download section.

#include <Adafruit_GFX_AS.h>  //https://github.com/rogerclarkmelbourne/Arduino_STM32/tree/master/STM32F1/libraries/Adafruit_GFX_AS
#include "Adafruit_ST7735.h" // https://github.com/KenjutsuGH/Adafruit-ST7735-Library

//Please note that you need to delete the Adafruit ST7735 library for Arduino if you have it installed, else the
//sketch won't compile.

#define cs     PA2
#define rst    PA4 
#define dc     PA3

// Color definitions
#define BLACK    0x0000
#define BLUE     0x001F
#define RED      0xF800
#define GREEN    0x07E0
#define CYAN     0x07FF
#define MAGENTA  0xF81F
#define YELLOW   0xFFE0 
#define WHITE    0xFFFF

#define ITERATIONS 500000L    // number of iterations
#define REFRESH_TFT 7500      // refresh bar every 7500 iterations
#define ACTIVATED LOW 

#if defined(__SAM3X8E__)
    #undef __FlashStringHelper::F(string_literal)
    #define F(string_literal) string_literal
#endif

Adafruit_ST7735 tft = Adafruit_ST7735(cs, dc, rst);

int percent = 0;
int progress = 1;
const int buttonPin = PB9;     
int buttonState = 0;    

void setup(void) {

  pinMode(buttonPin, INPUT_PULLUP);  
  
  Serial.begin(9600);
  
  tft.initR(INITR_BLACKTAB);   // initialize a ST7735S chip, black tab

  tft.fillScreen(ST7735_BLACK);

  printUI();
 
}

void loop() {
  
  buttonState = digitalRead(buttonPin);
  if (buttonState == ACTIVATED) {   
    removePressKeyText();
    drawBar();
    startCalculation();
  }
  else {
  }

delay(10);
}

void fillBar(int percent)
{
    int counter =60;
    percent = map(percent,0,100,5,121);     
    for(counter = 60; counter<75;counter++)
    {
     tft.drawFastHLine(5, counter, percent, YELLOW );
    }
}

void printUI()
{
 tft.setCursor(30,5);
 tft.setTextColor(RED);
 tft.setTextSize(1);
 tft.print("PI BENCHMARK");

 tft.setCursor(30,60);
 tft.setTextColor(WHITE);
 tft.setTextSize(1);
 tft.print("PRESS KEY");
 
 tft.setCursor(5,120);
 tft.setTextColor(RED);
 tft.setTextSize(1);
 tft.print("PI:");

 tft.setCursor(5,140);
 tft.setTextColor(RED);
 tft.setTextSize(1);
 tft.print("TIME:");
}

void removePressKeyText()
{
 tft.setCursor(30,60);
 tft.setTextColor(BLACK);
 tft.setTextSize(1);
 tft.print("PRESS KEY");
}

void drawBar()
{
  tft.drawRect(5,60,120,15, YELLOW);
}

void startCalculation()
{
 unsigned long start, time;
 unsigned long niter=ITERATIONS;
 int LEDcounter = 0;
 unsigned long i;
 float x = 1.0;
 float pi=1.0;

 Serial.begin(9600);
 Serial.print("Beginning ");
 Serial.print(niter);
 Serial.println(" iterations...");
 Serial.println();

 start = millis();  
 for ( i = 2; i < niter; i++) {
   x *= -1.0;
   pi += x / (2.0f*(float)i-1.0f);   
    if (LEDcounter++ > REFRESH_TFT) {
     LEDcounter = 0;
     progress++;
     percent = progress*100/(ITERATIONS/ REFRESH_TFT);
     fillBar(percent);
    }
 }
 time = millis() - start;
 
 pi = pi * 4.0;

 Serial.print("# of trials = ");
 Serial.println(niter);
 Serial.print("Estimate of pi = ");
 String piString = String(pi,7);
 String timeString = String(time)+"ms";
 Serial.println(piString);
 
 Serial.print("Time: "); Serial.print(time); Serial.println(" ms");
 
 tft.setCursor(40,120);
 tft.setTextColor(GREEN);
 tft.setTextSize(1);
 tft.print(piString);

 tft.setCursor(40,140);
 tft.setTextColor(GREEN);
 tft.setTextSize(1);
 tft.print(timeString);
}

Uploading Code to the STM32

Uploading sketches to the STM32f1 is a little bit complex compared to standard Arduino compatible boards. To upload code to the board, we need an FTDI based, USB to Serial converter.

Connect the USB to serial converter to the STM32 as shown in the schematics below.

Connect FTDI to STM32

Here is a pin to pin map of the connection

FTDI  – STM32

VCC - 5V
GND - GND
Rx - A9
Tx - A10

With this done, we then change the position of the board’s state jumper to position one (as shown in the gif below), so as to put the board in programming mode. Press the reset button on the board once after this and we are ready to upload the code.

On the computer, ensure you select “Generic STM32F103C board” and select serial for the upload method after which you can hit the upload button.

Select the board and set upload method to serial

Once the Upload is complete, change the state jumper to position “0” this will put the board in “run” mode and it should now start running based on the code uploaded. At this point, you can disconnect the FTDI and power the board over its USB. In case the code does not run after powering, ensure you have restored the jumper properly and recycle power to the board.

Demo

With the code complete, follow the upload process described above to upload the code to your setup. You should see the display come up as shown in the Image below.

Press the pushbutton to start the calculation. You should see the progress bar slide gradually until the end. At the end of the process, the value of Pi is displayed along with the time which the calculation took.

The same code is implemented on an Arduino Uno. The result is shown in the image below.

Arduino Speed Test

Comparing these two values, we see that “Blue Pill” is over 7 times faster than the Arduino Uno. This makes it ideal for projects which involves heavy processing and time constraints. The small size of the Blue pill also serves as an advantage here as it is only a bit bigger than the Arduino nano and it can be used in place where the Nano won’t be fast enough.

That’s it for today’s tutorial guys. What will you be building with the Blue Pill? feel free to share along with any questions you might have, under the comment section.

The video version of this tutorial can be watched on youtube.

Leave a Reply

RELATED PROJECTS

By continuing to use the site, you agree to the use of cookies. more info

The cookie settings on this website are set to "allow cookies" to give you the best browsing experience possible. If you continue to use this website without changing your cookie settings or you click "Accept" below then you are consenting to this.

Close