使用工具:
- stm32f103C8T6
- USB转串口
- 面包板
- 导线若干
- LED3个
一、STM32初识
STM32,从字面上来理解,ST 是意法半导体,M 是 Microelectronics 的缩写,32 表示32 位,合起来理解,STM32 就是指 ST 公司开发的 32 位微控制器。在如今的 32 位控制器当中,STM32 可以说是最璀璨的新星,它受宠若娇,大受工程师和市场的青睐,无芯能出其右。
STM32 属于一个微控制器,自带了各种常用通信接口,比如 USART、I2C、SPI 等,可接非常多的传感器,可以控制很多的设备。现实生活中,我们接触到的很多电器产品都有 STM32 的身影,比如智能手环,微型四轴飞行器,平衡车、移动 POST 机,智能电饭锅,3D 打印机等等。
STM32 有很多系列,可以满足市场的各种需求,从内核上分有 Cortex-M0、M3、M4和 M7 这几种,每个内核又大概分为主流、高性能和低功耗。
单纯从学习的角度出发,可以选择 F1和 F4,F1代表了基础型,基于 Cortex-M3内核,主频为 72MHZ,F4 代表了高性能,基于 Cortex-M4 内核,主频 180M。本文则选择的F1下的stm32f103c8t6 。
二、点灯
点亮LED灯,需要用到GPIO 端口。
为了点亮LED灯,需要三个步骤:
- 打开GPIO口的时钟
- 初始化GPIO口(选择推挽输出)
- 设置低电平
1. 打开时钟
-
GPIO的地址: -
时钟的地址:
即0x40021018 ,则打开三个IO口的时钟需要将三个位都置1:
#define RCC_APB2ENR (*(unsigned int *)0x40021018)
RCC_APB2ENR |= (1<<3);
RCC_APB2ENR |= (1<<4);
RCC_APB2ENR |= (1<<2);
2. 初始化
GPIO口有八种模式:
- 输入浮空
- 输入上拉
- 输入下拉
- 模拟输入
- 开漏输出
- 推挽式输出
- 推挽式复用功能
- 开漏复用功能
这里使用推挽输出
端口1-7为低,端口8-15为高。每个引脚由四个位控制。 以GPIOB 和0号引脚(B0)为例,将其设置为推挽输出 ,并设置最大速度为10MHz ,则将控制B0 的四个位设置为0001 :
#define GPIOB_CRL (*(unsigned int *)0x40010c00)
GPIOB_CRL |= (1<<0);
GPIOB_CRL &= ~(0xE<<0);
对于GPIOB 的B0 、GPIOC 的C15 、GPIOA 的A0 ,设置如下:
#define GPIOB_CRL (*(unsigned int *)0x40010C00)
#define GPIOC_CRH (*(unsigned int *)0x40011004)
#define GPIOA_CRL (*(unsigned int *)0x40010800)
GPIOB_CRL |= (1<<0);
GPIOB_CRL &= ~(0xE<<0);
GPIOC_CRH |= (1<<28);
GPIOC_CRH &= ~(0xE0000000);
GPIOA_CRL |= (1<<0);
GPIOA_CRL &= ~(0xE<<0);
3. 设置低电平
输出高电平则为1,低电平则为0
以GPIOB 和0号引脚(B0)为例,将其设置为低电平:
#define GPIOB_ODR (*(unsigned int *)0x40010C0C)
GPIOB_ODR &= ~(1<<0);
对于GPIOB 的B0 、GPIOC 的C15 、GPIOA 的A0 ,设置如下:
#define GPIOB_ODR (*(unsigned int *)0x40010C0C)
#define GPIOC_ODR (*(unsigned int *)0x4001100C)
#define GPIOA_ODR (*(unsigned int *)0x4001080C)
GPIOB_ODR &= ~(1<<0);
GPIOC_ODR &= ~(1<<15);
GPIOA_ODR &= ~(1<<0);
三、创建项目
1. 新建项目
点击Project 下的New uVision Project :
选择项目路径,填写文件名:
选择芯片:
右击文件夹,添加新文件:
选择.c ,文件名为main :
关闭:
将所需要的启动文件复制到项目目录下(f103c8t6启动文件为startup_stm32f10x_md.s :
右击文件夹,选择Add Existing Files to Group Source Group 1 (或双击文件夹):
选择All FIles ,选择刚刚添加的启动文件,Add ,Add 之后Close :
打开魔术棒,如下图所示勾选Create HEX File :
在main.c 中写入空的main函数:
int main(){
}
编译,发现报错:
原因:没有SystemInit 函数 添加SystemInit 函数:
void SystemInit(void);
int main(){
}
void SystemInit(){
}
此时编译,不报错:
2. 编写代码
在上一节分析的基础上,加上循环,那么需要延时函数:
void delay(unsigned long);
void delay(unsigned long count)
{
while(count--);
}
则总代码为:
#define GPIOB_BASE 0x40010C00
#define GPIOC_BASE 0x40011000
#define GPIOA_BASE 0x40010800
#define RCC_APB2ENR (*(unsigned int *)0x40021018)
#define GPIOB_CRL (*(unsigned int *)0x40010C00)
#define GPIOC_CRH (*(unsigned int *)0x40011004)
#define GPIOA_CRL (*(unsigned int *)0x40010800)
#define GPIOB_ODR (*(unsigned int *)0x40010C0C)
#define GPIOC_ODR (*(unsigned int *)0x4001100C)
#define GPIOA_ODR (*(unsigned int *)0x4001080C)
void SystemInit(void);
void Delay_ms(volatile unsigned int);
void Delay_ms( volatile unsigned int t)
{
unsigned int i;
while(t--)
for (i=0;i<800;i++);
}
int main(){
RCC_APB2ENR |= (1<<3);
RCC_APB2ENR |= (1<<4);
RCC_APB2ENR |= (1<<2);
GPIOB_CRL |= (1<<0);
GPIOB_CRL &= ~(0xE);
GPIOC_CRH |= (1<<28);
GPIOC_CRH &= ~(0xE0000000);
GPIOA_CRL |= (1<<0);
GPIOA_CRL &= ~(0xE);
GPIOB_ODR |= (1<<0);
GPIOC_ODR |= (1<<15);
GPIOA_ODR |= (1<<0);
while(1){
GPIOB_ODR &= ~(1<<0);
Delay_ms(1000000);
GPIOB_ODR |= (1<<0);
Delay_ms(1000000);
GPIOC_ODR &= ~(1<<15);
Delay_ms(1000000);
GPIOC_ODR |= (1<<15);
Delay_ms(1000000);
GPIOA_ODR &= ~(1<<0);
Delay_ms(1000000);
GPIOA_ODR |= (1<<0);
Delay_ms(1000000);
}
}
void SystemInit(){
}
四、连接电路
对于USB转TTL模块 和stm32f103c8t6 连接
GND — GND 3v3 — 3v3 TXD — A10 RXD — A9
总电路:
编译:
连接到电脑,打开mcuisp ,上传HEX文件 到stm32f103c8t6 上:
开始编程:
成功实现流水灯:
五、汇编实现
RCC_APB2ENR EQU 0x40021018;配置RCC寄存器,时钟,0x40021018为时钟地址
GPIOB_BASE EQU 0x40010C00
GPIOC_BASE EQU 0x40011000
GPIOA_BASE EQU 0x40010800
GPIOB_CRL EQU 0x40010C00
GPIOC_CRH EQU 0x40011004
GPIOA_CRL EQU 0x40010800
GPIOB_ODR EQU 0x40010C0C
GPIOC_ODR EQU 0x4001100C
GPIOA_ODR EQU 0x4001080C
Stack_Size EQU 0x00000400;栈的大小
AREA STACK, NOINIT, READWRITE, ALIGN=3 ;NOINIT: = NO Init,不初始化。READWRITE : 可读,可写。ALIGN =3 : 2^3 对齐,即8字节对齐。
Stack_Mem SPACE Stack_Size
__initial_sp
AREA RESET, DATA, READONLY
__Vectors DCD __initial_sp ; Top of Stack
DCD Reset_Handler ; Reset Handler
AREA |.text|, CODE, READONLY
THUMB
REQUIRE8
PRESERVE8
ENTRY
Reset_Handler
bl LED_Init;bl:带链接的跳转指令。当使用该指令跳转时,当前地址(PC)会自动送入LR寄存器
MainLoop BL LED_ON_C
BL Delay
BL LED_OFF_C
BL Delay
BL LED_ON_A
BL Delay
BL LED_OFF_A
BL Delay
BL LED_ON_B
BL Delay
BL LED_OFF_B
BL Delay
B MainLoop;B:无条件跳转。
LED_Init;LED初始化
PUSH {R0,R1, LR};R0,R1,LR中的值放入堆栈
;控制时钟
LDR R0,=RCC_APB2ENR;LDR是把地址装载到寄存器中(比如R0)。
ORR R0,R0,
LDR R1,=RCC_APB2ENR
STR R0,[R1]
;初始化GPIOA_CRL
LDR R0,=GPIOA_CRL
BIC R0,R0,
LDR R1,=GPIOA_CRL
STR R0,[R1]
LDR R0,=GPIOA_CRL
ORR R0,
LDR R1,=GPIOA_CRL
STR R0,[R1]
;将PA0置1
MOV R0,
LDR R1,=GPIOA_ORD
STR R0,[R1]
;初始化GPIOB_CRL
LDR R0,=GPIOB_CRL
BIC R0,R0,
LDR R1,=GPIOB_CRL
STR R0,[R1]
LDR R0,=GPIOB_CRL
ORR R0,
LDR R1,=GPIOB_CRL
STR R0,[R1]
;将PB0置1
MOV R0,
LDR R1,=GPIOA_ORD
STR R0,[R1]
;初始化GPIOC
LDR R0,=GPIOC_CRH
BIC R0,R0,
LDR R1,=GPIOC_CRH
STR R0,[R1]
LDR R0,=GPIOC_CRH
ORR R0,
LDR R1,=GPIOC_CRH
STR R0,[R1]
;将PC15置1
MOV R0,
LDR R1,=GPIOC_ORD
STR R0,[R1]
POP {R0,R1,PC};将栈中之前存的R0,R1,LR的值返还给R0,R1,PC
LED_ON_A
PUSH {R0,R1, LR}
MOV R0,
LDR R1,=GPIOA_ORD
STR R0,[R1]
POP {R0,R1,PC}
LED_OFF_A
PUSH {R0,R1, LR}
MOV R0,
LDR R1,=GPIOA_ORD
STR R0,[R1]
POP {R0,R1,PC}
LED_ON_B;亮灯
PUSH {R0,R1, LR}
MOV R0,
LDR R1,=GPIOB_ORD
STR R0,[R1]
POP {R0,R1,PC}
LED_OFF_B;熄灯
PUSH {R0,R1, LR}
MOV R0,
LDR R1,=GPIOB_ORD
STR R0,[R1]
POP {R0,R1,PC}
LED_ON_C;亮灯
PUSH {R0,R1, LR}
MOV R0,
LDR R1,=GPIOC_ORD
STR R0,[R1]
POP {R0,R1,PC}
LED_OFF_C;熄灯
PUSH {R0,R1, LR}
MOV R0,
LDR R1,=GPIOC_ORD
STR R0,[R1]
POP {R0,R1,PC}
Delay
PUSH {R0,R1, LR}
MOVS R0,
MOVS R1,
MOVS R2,
DelayLoop0
ADDS R0,R0,
CMP R0,
BCC DelayLoop0
MOVS R0,
ADDS R1,R1,
CMP R1,
BCC DelayLoop0
MOVS R0,
MOVS R1,
ADDS R2,R2,
CMP R2,
BCC DelayLoop0
POP {R0,R1,PC}
NOP
END
五、总结
总的来说,刚开始学习stm32 还是有点困难,需要多加练习。
参考
【单片机】野火STM32F103教学视频
STM32入门教程
STM32F103C8T6实现流水灯(c语言和汇编两个版本)
STM32串口下载程序
|