100% found this document useful (1 vote)
325 views33 pages

Lab 1 Introduction To Stm32F103 and Ide: St-Link V2 and Keil Uvision5

Uploaded by

Kiều Nguyễn
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
100% found this document useful (1 vote)
325 views33 pages

Lab 1 Introduction To Stm32F103 and Ide: St-Link V2 and Keil Uvision5

Uploaded by

Kiều Nguyễn
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 33

LAB 1

INTRODUCTION TO STM32F103 AND IDE


STM32F103 is an STMicroelectronics’s 32-bit microcontroller based on the ARM Cortex-M3
processor. The processor clock speed is up to 72MHz. It has 64 Kbytes of flash memory and 20
Kbytes of SRAM. There are several development boards for this microcontroller available on the
market. In this tutorial, I will use this development board. It has 40 pins that fit into breadboard. It has
2× 12-bit ADCs, 7× timers, and Up to 9 communication interfaces (3× USART, 2× I2C, 2× SPI, 1×
CAN, 1× USB).

ST-Link v2 and Keil uVision5


ST-Link v2 is an in-circuit debugger and programmer for the STM8 and STM32 microcontroller families.
There are 2 types of ST-Link, the original version and the clone version. For the IDE, I will use Keil uVision5.
The evaluation version of Keil uVision5 has 32 Kbytes code limitation, but for learning purpose it is enough.
You can download Keil uVision5 for MDK ARM from here. After that, you have to download MDK5
Software Packs (libraries) for this microcontroller from here.
Create a New Project

Before you can create a new project in Keil, you must install the Software Packs. You can install it by
clicking Pack Installer icon on toolbar. In the Pack Installer dialog you can select
menu File → Import, and then select the Software Pack (the .pack file).

To create a new project, go to Project → New uVision Project, give a name for that project, and
then select your microcontroller (STM32F103C8). After that, you can select which peripheral library
that you want to use, then click the Resolve button.
Build the Project

In the project window, you can rename the Target 1 and Source Group 1 folder
to STM32F103C8 and Source respectively. Right click on Source folder then click Add New Item
to Group menu. Select the .c file and give a name for that file (for example main.c). You can write
your code in main.c file. You can build the project by clicking Build icon from toolbar or press F7.
Your Built Output should contain zero error.
Download the Code

After you build the project successfully, then you can download the code into the microcontroller. To
do that, you need to configure the ST-Link settings by clicking Options for Target icon. In
the Debug tab, select ST-Link Debugger and click Settings button. Set the properties in Cortex-M
Target Driver Setup dialog like this:
Check the Reset and Run option, so the microcontroller will run the program after the download
process finished. If you not check this option, then you have to press the reset button to run the
program.

After the settings for ST-Link have been configured properly, you can start flash the microcontroller,
by pressing Download icon from toolbar. You can also debug the program from menu Debug →
Start/Stop Debug Session.
LAB 2
STM32F103 GPIO
STM32F103 GPIO are generic pins that can be configured as input or output. In this tutorial, I will
explain how to use STM32F103 GPIO for controlling an LED on/off. STM32F103 GPIO can be
configured in 4 different modes (input mode, output mode, analog input mode, and alternate function
mode). For controlling an LED on/off, we need to configure a GPIO pin in output mode.

GPIO Output Mode

There are 2 output modes for GPIO, output open drain and output push-pull. The logic voltage of
STM32F103 GPIO is 3.3V, so the pin output voltage is 3.3V. This is the characteristic of a GPIO pin
when it is configured in output mode:
 The output driver is enabled.
 In open drain mode, a “0” in the output data register activates the N-MOS while a “1” in the output
data register leaves the port in Hi-Z. (the P-MOS is never activated in open drain mode).
 In push-pull mode, a “0” in the output data register activates the N-MOS while a “1” in the output data
register activates the P-MOS.
 The schmitt trigger input is activated.
 The weak pull-up and pull-down resistors are disabled.
 The data present on the GPIO pin is sampled into the input data register every APB2 clock cycle.
 A read access to the input data register gets the GPIO state in open drain mode.
 A read access to the output data register gets the last written value in push-pull mode.

LED Circuit

This is the LED circuit that I use in this tutorial. The LED configuration is active low. When the PA0 is
low or “0”, the LED will turn on. When the PA0 is high or “1”, the LED will turn off. The output mode
that I use is output push-pull mode so, when I write “0” to the output data register, the N-MOS will
turn on, therefore the LED will turn on (current can flow from Vdd to Vss through LED and resistor).
When I write “1” to the output data register, the P-MOS will turn on, therefore the LED will turn off
(current can‟t flow from Vdd to Vdd).
Example Code

To configure PA0 in output push-pull mode, we must do this steps:

Enable the peripheral clock for GPIOA. GPIOA is connected to APB2 bus.

Initialize GPIOA by using GPIO_InitTypeDef struct.

This the code to make an LED blinking, for the project file you can get it from here.

#include "stm32f10x.h"
#include "stm32f10x_rcc.h"
#include "stm32f10x_gpio.h"

void delay(unsigned int nCount);


GPIO_InitTypeDef GPIO_InitStruct;

int main (void)


{
// Enable clock for GPIOA
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

// Configure PA0 as push-pull output


GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOA, &GPIO_InitStruct);

while (1)
{
/* Toggle LED on PA0 */
// Reset bit will turn on LED (because the logic is interved)
GPIO_ResetBits(GPIOA, GPIO_Pin_0);
delay(1000);
// Set bit will turn off LED (because the logic is interved)
GPIO_SetBits(GPIOA, GPIO_Pin_0);
delay(1000);
}
}
// Delay function
void delay(unsigned int nCount)
{
unsigned int i, j;

for (i = 0; i < nCount; i++)


for (j = 0; j < 0x2AFF; j++);
}

It is also possible to use output open drain mode to control the LED. The LED circuit is the same.
When I write “0” to output data register, the N-MOS will turn on, therefore the LED will turn on
(current can flow from Vdd to Vss through LED and resistor). When I write “1” to output data register,
PA0 is in Hi-Z (current can‟t flow because PA0 is Hi-Z/floating), therefore the LED will turn off.

GPIO Read Button

STM32F103 GPIO
In this tutorial, I will explain how to use STM32F103 GPIO for reading a push button. STM32F103
GPIO can be configured in 4 different modes (input mode, output mode, analog input mode, and
alternate function mode). For reading a button, we need to configure a GPIO pin in digital input
mode.

GPIO Input Mode


STM32F103 GPIO has 3 digital input mode: input with internal pull-up, input with internal pull-down,
and input floating. The logic voltage of STM32 is 3.3V, so the logic voltage for GPIO input pins are
also 3.3V, but there are several pins that 5V tolerant. We can use 5V logic level to this 5V tolerant
input pins. The table below shows I/O voltage level on STM32F103 input pins. The characteristic of
a GPIO pin when it is configured in input mode is shown in figure below.
 The output driver is disabled.
 The schmitt trigger input is activated.
 The weak pull-up and pull-down resistors are activated or not depending on the input configuration
(pull-up, pull-down, or floating).
 The data present on the GPIO pin is sampled into the input data register every APB2 clock cycle.
 A read access to the input data register obtains the GPIO pin state.
Button Circuit

This is the button circuit for this tutorial. I also add an LED in this circuit just for showing the state of
the button (is pressed or not). For the GPIO input mode, I use the input mode with internal pull-up,
therefore the button circuit is active low (when the button is pressed, the logic in input data register is
“0”). The GPIO output mode for the LED is open-drain mode and also active low.

Example Code
This is the example code. The purpose of this code is to turn on the LED when the button is pressed
and turn off the LED when it is not pressed

#include "stm32f10x.h"
#include "stm32f10x_rcc.h"
#include "stm32f10x_gpio.h"

GPIO_InitTypeDef GPIO_InitStruct;

int main(void)
{
// Enable clock for GPIOA
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

// Cofigure PA0 as open-drain output


GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_OD;
GPIO_Init(GPIOA, &GPIO_InitStruct);

// Cofigure PA1 as input with internal pull-up resistor


GPIO_InitStruct.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(GPIOA, &GPIO_InitStruct);

while (1)
{
// If button on PA1 is pressed (button circuit is active low)
if (!(GPIO_ReadInputData(GPIOA) & GPIO_Pin_1))
{
// Turn on LED on PA0 (LED circuit is active low)
GPIO_ResetBits(GPIOA, GPIO_Pin_0);
}
else
{
// Turn off LED on PA0
GPIO_SetBits(GPIOA, GPIO_Pin_0);
}
}
}

You can use other GPIO input modes, which are input with internal pull-down or input floating. For
input floating, you must add an external pull-up or pull-down resistor. This is the example circuit for
input with internal pull-down and input floating with external pull-up. When you use a pull-down
resistor, the logic is active high (when the button is pressed, the logic in input data register is “1”).
LAB 3
STM32F103 System Timer
Create a Delay Function with System Timer

STM32F103 System Timer or SysTick is a timer inside the CPU. SysTick is a basic countdown timer.
SysTick can be polled by software or can be configured to generate an interrupt. To use SysTick, we
must load a value to the reload value register. The width of reload value register is 24-bit, so it can
counts from 0x00FFFFFF to 0. In this tutorial, I will explain how to use SysTick for creating a delay
function. SysTick can be configured through the registers below.

To easily configure SysTick, we can use SysTick_Config() function. This function is defined in
core_cm3.h. This function will initialize SysTick and its interrupt, then start the SysTick. The counter
is in free running mode to generate periodic interrupts. The input parameter of this function is the
number of ticks between two interrupts.

Create A Delay Function

In this tutorial, I will create 2 delay functions (DelayUs() and DelayMs()). To create DelayUs()
function, we should configure the SysTick interrupt to be triggered every 1 us by using
SysTick_Config() function. There is a variable called usTicks that holds the value of ticks in us.
Every time we call DelayUs() function, we should load this variable with the delay value in us and
then we poll this variable until reach 0. This variable will be decremented by 1 every 1 us by
SysTick_Handler() ISR. To create DelayMs() function, we should load the delay value in ms, then it
will be decremented by 1 every 1000 us using DelayUs() function. This is the code for delay library.
To use the delay functions, you should call the DelayInit() function first.

---------Include file delay.h…………….


#ifndef __DELAY_H
#define __DELAY_H

#ifdef __cplusplus
extern "C" {
#endif

#include "stm32f10x.h"

void DelayInit(void);
void DelayUs(uint32_t us);
void DelayMs(uint32_t ms);

#ifdef __cplusplus
}
#endif

#endif

-------delay.c-----------

#include "delay.h"

// For store tick counts in us


static __IO uint32_t usTicks;

// SysTick_Handler function will be called every 1 us


void SysTick_Handler()
{
if (usTicks != 0)
{
usTicks--;
}
}

void DelayInit()
{
// Update SystemCoreClock value
SystemCoreClockUpdate();
// Configure the SysTick timer to overflow every 1 us
SysTick_Config(SystemCoreClock / 1000000);
}

void DelayUs(uint32_t us)


{
// Reload us value
usTicks = us;
// Wait until usTick reach zero
while (usTicks);
}

void DelayMs(uint32_t ms)


{
// Wait until ms reach zero
while (ms--)
{
// Delay 1ms
DelayUs(1000);
}
}
LAB 4
STM32F103 INTERFACE - SPI
STM32F103 SPI (Serial Peripheral Interface) is a synchronous serial communication protocol. In this
interface, in addition to transmit and receive lines, there is a third line that is used for clock line. Each
slave device also has a chip select (enable) pin, that is used for activating the device. So to use SPI,
we need 2 wires for data lines (MOSI, MISO), 1 wire for clock line, and 1 wire per device for chip
select line. MOSI (Master Out Slave In) is used for data transfer from master device to slave device.
MISO (Master In Slave Out) is used for data transfer from slave device to master device.

SPI Protocol

SPI communication is different from other serial communication especially on data transfer. There is
no concept like transmit and receive data, but there is a data trading concept. When data trading
occurs, the data bits in master register is traded with the data bits in slave register on every clock
from master (one data bit per clock tick). You can think SPI is like shift registers. There are 2 shift
registers, one in master device and another in slave device. Each input of shift register is connected
to the output of the other through MOSI and MISO lines, so that they form a ring.
The figure above illustrates the bit trading from master to slave. Master register contain data 0xFF
and slave register contain data 0x00. After one clock tick, the master is left with seven of its original
bits and the first one that is come in from the slave, and vice versa. After a total of eight clock ticks,
all eight bits of each byte have traded place. Sometimes not all data byte come from slave or sent to
slave is meaningful. This happen because probably slave device has not received any command
yet, so the data in slave register is not meaningful. Another case, if we just want to take data from
slave, but don‟t want to send any command to slave, we can place dummy byte on master register
and then give eight clock ticks for trading with data bits in slave register.
The figure above is the timing diagram of SPI protocol. We know that to communicate to the slave
device, the slave select pin should be activated (active low). The data is sampled every rising edge
of clock. We can also sample data on falling edge of clock, this setting can be configured depending
on the feature of the hardware SPI that you use. In this example, master is send data byte 0x53 to
slave and then slave send data byte 0x46 to master. The order of the data is LSB first, but it can also
MSB first depending on the configuration.

Example Code

In this tutorial, I will explain how to use SPI in STM32F103 as a master, and for the slave I will use
Arduino. We can send data char „1‟ from SPI master to turn on LED blinking on Arduino. To turn off
LED blinking, we can send „0‟ from SPI master. Master can also read LED blinking status (off/on)
from Arduino by sending „?‟ first, then read the LED blinking status which will return 0 or 1. This is
the Arduino code for SPI slave device.

1 #define LED_PIN 9
2
3 volatile uint8_t led_blink = 0;
4
5 ISR(SPI_STC_vect)
6 {
7 uint8_t data_byte = SPDR;
8
9 switch (data_byte)
10 {
11 case '0':
12 led_blink = 0;
13 SPDR = 0;
14 break;
15 case '1':
16 led_blink = 1;
17 SPDR = 0;
18 break;
19 case '?':
20 // Place LED blinking status in SPDR register for next transfer
21 SPDR = led_blink;
22 break;
23 }
24 }
25
26 void setup()
27 {
28 pinMode(LED_PIN, OUTPUT);
29
30 // Set MISO pin as output
31 pinMode(MISO, OUTPUT);
32 // Turn on SPI in slave mode
33 SPCR |= (1 << SPE);
34 // Turn on interrupt
35 SPCR |= (1 << SPIE);
36 }
37
38 void loop()
39 {
40 // If LED blink status is on, then blink LED for 250ms
41 if (led_blink == 1)
42 {
43 digitalWrite(LED_PIN, HIGH);
44 delay(250);
45 digitalWrite(LED_PIN, LOW);
46 delay(250);
47 }
48 else if (led_blink == 0)
49 {
50 digitalWrite(LED_PIN, LOW);
51 }
52 }

For the SPI on STM32F103, I create several functions such as for initialize SPI, SPI data transfer,
and enable/disable slave device. In main function, I write codes to turn on and off the LED blinking
on Arduino, then ask the current LED blinking status every 2500 ms. The LED blinking status will be
displayed on LCD.

#include "stm32f10x.h"
#include "stm32f10x_rcc.h"
#include "stm32f10x_gpio.h"
#include "stm32f10x_spi.h"
#include "delay.h"
#include "lcd16x2.h"

#define SPIx_RCC RCC_APB2Periph_SPI1


#define SPIx SPI1
#define SPI_GPIO_RCC RCC_APB2Periph_GPIOA
#define SPI_GPIO GPIOA
#define SPI_PIN_MOSI GPIO_Pin_7
#define SPI_PIN_MISO GPIO_Pin_6
#define SPI_PIN_SCK GPIO_Pin_5
#define SPI_PIN_SS GPIO_Pin_4

void SPIx_Init(void);
uint8_t SPIx_Transfer(uint8_t data);
void SPIx_EnableSlave(void);
void SPIx_DisableSlave(void);

uint8_t receivedByte;

int main(void)
{
DelayInit();
lcd16x2_init(LCD16X2_DISPLAY_ON_CURSOR_OFF_BLINK_OFF);

SPIx_Init();

while (1)
{
// Enable slave
SPIx_EnableSlave();
// Write command to slave to turn on LED blinking
SPIx_Transfer((uint8_t) '1');
DelayUs(10);
// Write command to slave for asking LED blinking status
SPIx_Transfer((uint8_t) '?');
DelayUs(10);
// Read LED blinking status (off/on) from slave by transmitting
// dummy byte
receivedByte = SPIx_Transfer(0);
// Disable slave
SPIx_DisableSlave();
// Display LED blinking status
lcd16x2_clrscr();
if (receivedByte == 0)
{
lcd16x2_puts("LED Blinking Off");
}
else if (receivedByte == 1)
{
lcd16x2_puts("LED Blinking On");
}
DelayMs(2500);

// Enable slave
SPIx_EnableSlave();
// Write command to slave to turn off LED blinking
SPIx_Transfer((uint8_t) '0');
DelayUs(10);
// Write command to slave for asking LED blinking status
SPIx_Transfer((uint8_t) '?');
DelayUs(10);
// Read LED blinking status (off/on) from slave by transmitting
// dummy byte
receivedByte = SPIx_Transfer(0);
// Disable slave
SPIx_DisableSlave();
// Display LED blinking status
lcd16x2_clrscr();
if (receivedByte == 0)
{
lcd16x2_puts("LED Blinking Off");
}
else if (receivedByte == 1)
{
lcd16x2_puts("LED Blinking On");
}
DelayMs(2500);
}
}

void SPIx_Init()
{
// Initialization struct
SPI_InitTypeDef SPI_InitStruct;
GPIO_InitTypeDef GPIO_InitStruct;

// Step 1: Initialize SPI


RCC_APB2PeriphClockCmd(SPIx_RCC, ENABLE);
SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_128;
SPI_InitStruct.SPI_CPHA = SPI_CPHA_1Edge;
SPI_InitStruct.SPI_CPOL = SPI_CPOL_Low;
SPI_InitStruct.SPI_DataSize = SPI_DataSize_8b;
SPI_InitStruct.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
SPI_InitStruct.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_InitStruct.SPI_Mode = SPI_Mode_Master;
SPI_InitStruct.SPI_NSS = SPI_NSS_Soft | SPI_NSSInternalSoft_Set;
SPI_Init(SPIx, &SPI_InitStruct);
SPI_Cmd(SPIx, ENABLE);

// Step 2: Initialize GPIO


RCC_APB2PeriphClockCmd(SPI_GPIO_RCC, ENABLE);
// GPIO pins for MOSI, MISO, and SCK
GPIO_InitStruct.GPIO_Pin = SPI_PIN_MOSI | SPI_PIN_MISO | SPI_PIN_SCK;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(SPI_GPIO, &GPIO_InitStruct);
// GPIO pin for SS
GPIO_InitStruct.GPIO_Pin = SPI_PIN_SS;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(SPI_GPIO, &GPIO_InitStruct);

// Disable SPI slave device


SPIx_DisableSlave();
}

uint8_t SPIx_Transfer(uint8_t data)


{
// Write data to be transmitted to the SPI data register
SPIx->DR = data;
// Wait until transmit complete
while (!(SPIx->SR & (SPI_I2S_FLAG_TXE)));
// Wait until receive complete
while (!(SPIx->SR & (SPI_I2S_FLAG_RXNE)));
// Wait until SPI is not busy anymore
while (SPIx->SR & (SPI_I2S_FLAG_BSY));
// Return received data from SPI data register
return SPIx->DR;
}

void SPIx_EnableSlave()
{
// Set slave SS pin low
SPI_GPIO->BRR = SPI_PIN_SS;
}

void SPIx_DisableSlave()
{
// Set slave SS pin high
SPI_GPIO->BSRR = SPI_PIN_SS;
}
LAB 5
STM32F103 SPL - Interfacing LCD16x2

LCD16x2
LCD16x2 is a popular display module and commonly used in various devices. In this tutorial, I will
explain about how to use LCD16x2 with STM32F103 microcontroller. If you want to know the detail
how LCD16x2 works, you can follow this tutorial. To interface the LCD with STM32F103, you need
the library files (lcd16x2.h and lcd16x2.c). You can get the library files and from here.

LCD16x2 Library

In this library, we can configure the GPIO for LCD in lcd16x2.h file. This definitions (in source code
below) in lcd16x2.h contain the GPIO configuration for the LCD. In this definitions, you can configure
the GPIO for LCD control lines (RS, RW, EN) and LCD data lines (D4-D7). This library only support
4-bit data mode.

1 // LCD control lines (must be on the same port)


2 #define LCD16X2_RCC_GPIO_CONTROL RCC_APB2Periph_GPIOB
3 #define LCD16X2_GPIO_CONTROL GPIOB
4 #define LCD16X2_GPIO_RS GPIOB
5 #define LCD16X2_GPIO_RW GPIOB
6 #define LCD16X2_GPIO_EN GPIOB
7 // LCD data lines (must be on the same port)
8 #define LCD16X2_RCC_GPIO_DATA RCC_APB2Periph_GPIOA
9 #define LCD16X2_GPIO_DATA GPIOA
10 #define LCD16X2_GPIO_D4 GPIOA
11 #define LCD16X2_GPIO_D5 GPIOA
12 #define LCD16X2_GPIO_D6 GPIOA
13 #define LCD16X2_GPIO_D7 GPIOA
14 // Pin definition
15 #define LCD16X2_PIN_RS GPIO_Pin_12
16 #define LCD16X2_PIN_RW GPIO_Pin_13
17 #define LCD16X2_PIN_EN GPIO_Pin_14
18 #define LCD16X2_PIN_D4 GPIO_Pin_8 // 4-bit mode LSB
19 #define LCD16X2_PIN_D5 GPIO_Pin_9
20 #define LCD16X2_PIN_D6 GPIO_Pin_10
21 #define LCD16X2_PIN_D7 GPIO_Pin_11 // 4-bit mode MSB

Example Code

To use this library, in the main.c file, you must call lcd16x2_init() function. There are 5 parameters
for this function that can be used, depending on what type of cursor you want to use:

 LCD16X2_DISPLAY_OFF_CURSOR_OFF_BLINK_OFF
 LCD16X2_DISPLAY_ON_CURSOR_OFF_BLINK_OFF
 LCD16X2_DISPLAY_ON_CURSOR_OFF_BLINK_ON
 LCD16X2_DISPLAY_ON_CURSOR_ON_BLINK_OFF
 LCD16X2_DISPLAY_ON_CURSOR_ON_BLINK_ON

This library is also support custom character display. This code below is an example how to use the
library for displaying a string and also a custom character. The custom character is created by
defining the pattern. In this example, I use the pattern for battery indicator.

LCD16x2

1 #include "stm32f10x.h"
2 #include "stm32f10x_rcc.h"
3 #include "stm32f10x_gpio.h"
4 #include "delay.h"
5 #include "lcd16x2.h"
6
7 // Custom char data (battery symbol)
8 uint8_t custom_char[] = { 0x0E, 0x1B, 0x11, 0x11, 0x11, 0x11, 0x1F, 0x1F };
9
10 int main(void)
11 {
12 // Delay initialization
13 DelayInit();
14
15 // LCD initialization
16 lcd16x2_init(LCD16X2_DISPLAY_ON_CURSOR_OFF_BLINK_OFF);
17
18 // Create custom char
19 lcd16x2_create_custom_char(0, custom_char);
20
21 while (1)
22 {
23 // Display custom char
24 lcd16x2_put_custom_char(0, 0, 0);
25 lcd16x2_puts(" Battery Low");
26 DelayMs(500);
27 // Clear display
28 lcd16x2_clrscr();
29 DelayMs(500);
30 }
31 }
LAB 6
STM32F103 SPL– Interfacing Unipolar
Stepper Motor
Stepper Motor
Stepper motor is an electromechanical device that converts electrical pulses into discrete
mechanical movements. In this tutorial, I will explain how to control a unipolar stepper motor using
STM32F103 microcontroller. If you don‟t know the basic of the stepper motor, I suggest you to read
this post. To interface a stepper motor from a microcontroller, we can‟t directly drive it with GPIO
pins because GPIO pins have maximum current that can sink or source from it. To overcome this
problem, we can use driver circuit. The driver circuit for unipolar stepper motor can be built by using
4 transistors to drive large current to the 4 wires of a stepper motor. It also can be built with
ULN2003 IC. This is the circuit for driving a unipolar stepper motor from STM32F103 by using
ULN2003 IC.

In this tutorial, I will use the 28BYJ-48 stepper motor. This motor is very cheap and it also comes
with driver module based on ULN2003 IC. This motor runs with 5V supply and has gear inside. The
gear reduction ratio is approximately 64:1. If you search from the internet, other people say that the
gear reduction ratio is actually 63.68395:1.
There are 2 common modes that can be used for controlling stepper motor, full step and half step.
Actually there is 1 more mode that can be used for controlling stepper motor in more advanced
ways. The mode is called micro step, but in this tutorial, I will explain only the full step and half step.

Full Step

The stepper motor is controlled by giving a sequence of electrical pulses. That electrical pulses are
applied to the 4 wires of stepper motor. Each electrical pulse is consist of 4-bits. We can applied
these pulses to the 4 wires by using 4 GPIO pins. The full step mode has a sequence of electrical
pulses that consist of 4 different pulses. Every 1 sequence is equivalent to 4 steps of movement, so
1 pulse is equivalent to 1 step movement. The following table is the sequence of full step mode.

One step movement of this motor in full step mode is 11.25°. By applying these sequence once, the
internal shaft of stepper motor will move 4 x 11.25° = 45°. To rotate the internal shaft of stepper
motor 1 revolution, we need 360° / 45° = 8 sequences (32 steps). The actual shaft of this motor is
geared with 64:1 gear ratio. That means every actual shaft rotates 1 revolution, the internal shaft
must rotate 64 revolutions, so to rotate the actual shaft 1 revolution, we need 32 x 64 = 2048 steps.
To move stepper motor 2048 steps, we can applied the sequence 512 times (because 1 sequence is
4 step). The following code is for rotating the actual shaft of the stepper motor 1 clockwise
revolution.

1 // One revolution CW using full step mode


2 for (cycle = 0; cycle < 512; cycle++)
3 {
4 GPIO_Write(GPIOB, 0x1000);
5 delay(5);
6 GPIO_Write(GPIOB, 0x2000);
7 delay(5);
8 GPIO_Write(GPIOB, 0x4000);
9 delay(5);
10 GPIO_Write(GPIOB, 0x8000);
11 delay(5);
12 }

Between each pulse in a sequence, we need to add delay, because the stepper motor works much
slower compared to the execution time of a microcontroller to execute 1 line of code. This delay
allows stepper to move 1 step before we apply next pulse. This delay is directly related to rotation
speed of the shaft. The smaller delay value will give faster rotation speed. We can reverse the
direction of the shaft rotation by reversing the applied sequence.

Full step counterclockwise


1 // One revolution CCW using full step mode
2 for (cycle = 0; cycle < 512; cycle++)
3 {
4 GPIO_Write(GPIOB, 0x8000);
5 delay(5);
6 GPIO_Write(GPIOB, 0x4000);
7 delay(5);
8 GPIO_Write(GPIOB, 0x2000);
9 delay(5);
10 GPIO_Write(GPIOB, 0x1000);
11 delay(5);
12 }

This is the full code for rotating stepper motor one revolution clockwise and one revolution counter-
clockwise.
#include "stm32f10x.h"
#include "stm32f10x_rcc.h"
#include "stm32f10x_gpio.h"

void delay(unsigned int nCount);


GPIO_InitTypeDef GPIO_InitStruct;
int cycle = 0;

int main(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);

GPIO_InitStruct.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_13 |


GPIO_Pin_14 | GPIO_Pin_15;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOB, &GPIO_InitStruct);
// One revolution CW using full step mode
for (cycle = 0; cycle < 512; cycle++)
{
GPIO_Write(GPIOB, 0x1000);
delay(5);
GPIO_Write(GPIOB, 0x2000);
delay(5);
GPIO_Write(GPIOB, 0x4000);
delay(5);
GPIO_Write(GPIOB, 0x8000);
delay(5);
}

delay(1000);

// One revolution CCW using full step mode


for (cycle = 0; cycle < 512; cycle++)
{
GPIO_Write(GPIOB, 0x8000);
delay(5);
GPIO_Write(GPIOB, 0x4000);
delay(5);
GPIO_Write(GPIOB, 0x2000);
delay(5);
GPIO_Write(GPIOB, 0x1000);
delay(5);
}

while (1)
{
}
}

void delay(unsigned int nCount)


{
unsigned int i, j;

for (i = 0; i < nCount; i++)


for (j = 0; j < 0x2AFF; j++);
}
Half Step

Half step mode consists of 8 electrical pulse for every 1 sequence. The sequence for half step mode
is shown in the following table. The different between full step and half step is the step resolution.
When you use half step, you will get more smaller degree per step movement. In this motor, if we
use half step, you will get 5.625° per 1 step, but the total number of degree per 1 sequence is the
same (5.625° x 8 = 45°). The effect is the shaft rotates more smooth.

This is the code for rotating stepper motor one revolution clockwise and the one revolution counter-
clockwise using half step mode.

// One revolution CW using half step mode


2 for (cycle = 0; cycle < 512; cycle++)
3 {
4 GPIO_Write(GPIOB, 0x9000);
5 delay(5);
6 GPIO_Write(GPIOB, 0x1000);
7 delay(5);
8 GPIO_Write(GPIOB, 0x3000);
9 delay(5);
10 GPIO_Write(GPIOB, 0x2000);
11 delay(5);
12 GPIO_Write(GPIOB, 0x6000);
13 delay(5);
14 GPIO_Write(GPIOB, 0x4000);
15 delay(5);
16 GPIO_Write(GPIOB, 0xC000);
17 delay(5);
18 GPIO_Write(GPIOB, 0x8000);
19 delay(5);
20 }
21
22 delay(1000);
23
24 // One revolution CCW using half step mode
25 for (cycle = 0; cycle < 512; cycle++)
26 {
27 GPIO_Write(GPIOB, 0x8000);
28 delay(5);
29 GPIO_Write(GPIOB, 0xC000);
30 delay(5);
31 GPIO_Write(GPIOB, 0x4000);
32 delay(5);
33 GPIO_Write(GPIOB, 0x6000);
34 delay(5);
35 GPIO_Write(GPIOB, 0x2000);
36 delay(5);
37 GPIO_Write(GPIOB, 0x3000);
38 delay(5);
39 GPIO_Write(GPIOB, 0x1000);
40 delay(5);
41 GPIO_Write(GPIOB, 0x9000);
42 delay(5);
43 }

You might also like