本文主要描述如何使用STM32最小系统核心板(STM32F103C8T6)+面板板+3只红绿黄LED搭建电路,并通过GPIOA-5、GPIOB-9、GPIOC-14 这3个引脚来控制控制LED灯轮流闪烁。
目录
一、题目要求
假设你手中已有 STM32最小系统核心板(STM32F103C8T6)+面板板+3只红绿蓝LED,并搭建了电路,分别 GPIOA-5、GPIOB-9、GPIOC-14 这3个引脚上控制LED灯(最高时钟 2Mhz ),轮流闪烁,间隔时长1秒 。
1)写出程序设计思路,包括GPIOx端口的各寄存器地址和详细参数;
2)用C语言 寄存器方式 编程实现。
二、设计思路
需要在 STM32F103C8T6 上面通过 初始化GPIO 来实现点亮 LED 灯。
外设实现的功能可能是完全不同的,但是,多数情况下,我们在设计程序的时候不需要考虑外设具体如何怎样实现功能,只需要给外设接在哪里、高电平有效还是低电平有效。因此,完成题目要求的时候,只需要找到 GPIOA-5、GPIOB-9、GPIOC-14 的地址,然后通过 GPIO的初始化,控制寄存器将片外引脚(我们称之为 IO口)拉低拉高, 输出高低电平,以控制LED亮灭。
点灯是所有学单片机的人都应该学会的一项技能。C51单片机和 stm32 点灯类似。
- 51单片机的点灯是,通过控制寄存器将片外引脚(我们称之为IO口)拉低拉高,输出高低电平,以控制LED亮灭。
其过程:单片机给指令->控制寄存器->给IO口电平->控制LED亮灭 - stm32的点灯则是,通过使能外设GPIO时钟,发出指令给外设GPIO,外设GPIO收到指令后,着手配置自己的寄存器,然后给IO口模式,让其实现各种功能。其过程:CPU给指令->GPIO收到指令->配置内部寄存器->配置IO口模式(注意是模式)->控制LED亮灭
STM32开发板中包含较多寄存器,实现流水灯操作,需要对相应的引脚进行操作,对相应的引脚进行时钟配置、输入输出模式设置、最大速率设置。
于是利用 STM32F103C8T6 实现流水灯,要经过以下步骤:
- 时钟配置
- 输入输出模式设置
- 最大速率设置
- 烧录程序
- 运行
三、STM简介
STM32,ST 是意法半导体,M 是 Microelectronics 的缩写,32 表示32 位,合起来理解STM32就是指 ST 公司开发的 32 位微控制器。
STM32 本质上来说是一个微控制器,自带了各种常用通信接口,比如 USART、I2C、SPI 等,可连接非常多的传感器,可以控制很多的设备。现实生活中,我们接触到的很多电器产品都有 STM32 的身影,比如智能手环,微型四轴飞行器,平衡车、移动 POST 机,智能电饭锅,3D 打印机等等。
STM32 有很多系列,可以满足市场的各种需求,从内核上分有 Cortex-M0、M3、M4和 M7 这几种,每个内核又大致分为主流、高性能和低功耗。
单纯从学习的角度出发,可以选择 F1和 F4,F1代表了基础型,基于 Cortex-M3内核,主频为 72MHZ,F4 代表了高性能,基于 Cortex-M4 内核,主频 180M。
STM32F103C8T6芯片介绍
STM32F103C8T6 是一款基于ARM Cortex-M 内核 STM32系列 的 32位 的微控制器,程序存储器容量是 64KB,需要电压 2V~3.6V,工作温度为 -40°C ~ 85°C。具体参数如下:
四、实现过程
1、时钟配置
- 找到时钟使能寄存器映射基地址
- 找到端口偏移地址以及对应端口所在位置
- 外设时钟使能寄存器,偏移量为0x18,起始地址0x4002 1000,该寄存器地址为0x4002 1018
- 使能对应端口时钟
查询数据手册可发现,外设时钟使能寄存器,设偏移量为0x18,起始地址0x4002 1000,该寄存器地址为0x4002 1018
#define RCC_AP2ENR *((unsigned volatile int*)0x40021018) #时钟使能寄存器
手册RCC_APB2ENR,位3是IOPBEN,名字是IO端口B时钟使能,就是我们想要的。把RCC_APB2ENR的位3赋值为1,就是开启GPIOB时钟
RCC->APB2ENR|=1<<2; //APB2-GPIOA外设时钟使能
RCC->APB2ENR|=1<<3; //APB2-GPIOB外设时钟使能
RCC->APB2ENR|=1<<4; //APB2-GPIOC外设时钟使能
//这两行代码可以合为 RCC_APB2ENR|=1<<3|1<<4;
2、输入输出模式设置、最大速率设置
本次实验采用通用推挽输出模式,最高输出时钟频率2Mhz。分别用到GPIOA-5、GPIOB-9、GPIOC-14 这3个引脚上三个引脚。其中A4属于端口配置低寄存器偏移地址为0x00,B9、C14属于端口配置高寄存器偏移地址为0x04。
- 找到GPIOx端口基地址
- 配置对应引脚寄存器,基地址+偏移量
故而配置如下:
//----------------GPIOA配置寄存器 -----------------------
#define GPIOA_CRL *((unsigned volatile int*)0x40010800)
//----------------GPIOB配置寄存器 -----------------------
#define GPIOB_CRH *((unsigned volatile int*)0x40010C04)
//----------------GPIOC配置寄存器 -----------------------
#define GPIOC_CRH *((unsigned volatile int*)0x40011004)
- 设置输出模式为推挽输出,输出速度为2Mhz
例:将GPIOB-9配置成推挽输出模式,且最大速度为2MHz
首先,其为GPIOB9端口,其属于端口配置高寄存器模块,则由上图可知,CNF9和MODE9位为0,其余位为F,即:GPIOB_CRH&=0xFFFFFF0F;又因其为推挽输出模式,且最大速度为2MHz,所以4位寄存器的配置就是CNF9【00】MODE9【10】,0010换成十进制数就是2,即:GPIOB_CRH|=0x00000020
GPIOA_CRL&=0xFFF0FFFF; //设置位清零
GPIOA_CRL|=0X00200000; //PA5推挽输出,把第23、22、21、20位变为0010
GPIOA->ODR|=1<<5; //设置PA5初始灯为灭
GPIOB_CRH&=0xFF0FFFFF; //设置位清零
GPIOB_CRH|=0x00000020; //PB9推挽输出,把第7、6、5、4变为0010
GPIOB->ODR|=0x1<<9; //设置初始灯为灭
GPIOC_CRH&=0xFF0FFFFF; //设置位清零
GPIOC_CRH|=0x02000000; //PC14推挽输出,把第27、26、25、24变为0010
GPIOC->ODR|=0x1<<14; //设置初始灯为灭
3、代码实现
3.1 创建工程文件
-
新建 test2 文件夹 —> 点击 Project 下的 New uVision Project —> 输入文件名 test2
-
因为之后实验采用 STM32F103C8T6 板,因此这里选择 STM32F103C8
-
创建项目出现弹窗,勾选 CORE 项,点击 OK 完成创建
3.2 添加启动代码
要实现 LED流水灯,还需要添加启动代码,以及.c 文件等。
C8T6 使用的启动文件 startup_stm32f10x_md.s,startup_stm32f10x_md.s 是一个启动文件,里面是使用汇编语言写好的基本程序,当 STM32 芯片上电启动的时候,首先会执行这里的汇编程序,从而建立起来C 语言的运行环境,所以我们把这个文件称为启动文件。C8T6 是中容量的arm芯片,启动文件的大致作用如下:
- 初始化堆栈指针 SP
- 初始化程序计数器指针 PC
- 设置堆、栈的大小
- 设置中断向量表的入口地址
- 配置外部 SRAM 作为数据存储器
- 调用 SystemInit() 函数配置 STM32 的系统时钟
- 设置 C 库的分支入口 "__main” (最终用来调用 main 函数)
startup_stm32f10x_hd.s 文件是由ST官方提供的,该文件可以从 KEIL5 安装目录中找到,也可以从 STV3.5 库里面找到,找到该文件后吧启动文件添加到工程里面即可。不同型号的芯片以及不同编译环境使用的汇编文件是不一样的,但功能相同。
在 keilMDK4 中只有 STM32F10x.s 文件。
在《STM32不完全手册里面》,所有的例程都采用了一个叫 STM32F10x.s 的启动文件,里面定义了 STM32 的堆栈大小以及各种中断的名字及入口函数名称,还有启动相关的汇编代码。
STM32F10x.s 是MDK提供的启动代码,从其里面的内容看来,它只定义了3个串口,4个定时器。实际上STM32的系列产品有5个串口的型号,也只有有2个串口的型号,定时器也是,最多的有8个定时器。比如,如果你用的 STM32F103ZET6,而启动文件用的是STM32F10x.s 的话,你可以正常使用串口1 ~ 3的中断,而串口4和5的中断,则无法正常使用。又比如,你TIM1 ~ 4的中断可以正常使用,而5~8的,则无法使用。
所以 STM32F10x.s 并不能适用所有的STM32型号,这样,我们就得对不同型号的STM32,选择不同的启动文件。ST给我们提供了3个启动文件,分别适用于不同容量的STM32芯片。他们是:
startup_stm32f10x_ld.s
startup_stm32f10x_md.s
startup_stm32f10x_hd.s
其中,ld.s适用于小容量 产品;md.s适用于中等容量产品;hd适用于大容量产品;
这里的容量是指FLASH的大小.判断方法如下:
小容量: FLASH≤32K
中容量: 64K≤FLASH≤128K
大容量: 256K≤FLASH
我们开发板使用的是 STM32F103C8T6,FLASH容量为64K,属于中等容量产品,而 STM32F10x.s 刚刚好是满足中等容量型号的启动文件,所以我们使用 STM32F10x.s 是不会有问题的,但是更便于理解,这里还是使用 startup_stm32f10x_md.s。而如果你的是小容量或者大容量的STM32,则相应的选择 startup_stm32f10x_ld.s 或startup_stm32f10x_hd.s 即可。
按照上面的解释,最为普通的工程,STM32F10x.s 文件是够用的,但是在复杂的工程中,还是建议ST库文件的几个启动文件。
这里上传这几个启动文件:
链接:https://pan.baidu.com/s/1tYK8A6og03rPp51ZTrVBBw
提取码:plt3
- 将启动文件拷贝到 test2 工程文件夹下:
- 点击 Target1→Source Group1→双击→设置打开文件类型为 Asm Source file→选择 startup_stm32f10x_md.s→点击 Add,如下图所示:
3.3 代码编写
- 在界面左侧的 Project 栏,打开 Target1,鼠标右键单击 Source Group1, 选择 Add New Item to Group ‘Source Group 1’…
- 添加 test2.c文件
- 编写代码
//--------------APB2使能时钟寄存器------------------------
#define RCC_APB2ENR *((unsigned volatile int*)0x40021018)
//----------------GPIOA配置寄存器 ------------------------
#define GPIOA_CRL *((unsigned volatile int*)0x40010800)
#define GPIOA_ODR *((unsigned volatile int*)0x4001080C)
//----------------GPIOB配置寄存器 ------------------------
#define GPIOB_CRH *((unsigned volatile int*)0x40010C04)
#define GPIOB_ODR *((unsigned volatile int*)0x40010C0C)
//----------------GPIOC配置寄存器 ------------------------
#define GPIOC_CRH *((unsigned volatile int*)0x40011004)
#define GPIOC_ODR *((unsigned volatile int*)0x4001100C)
//函数声明
void Delay_ms(volatile unsigned int);
void A_LED_LIGHT(void);
void B_LED_LIGHT(void);
void C_LED_LIGHT(void);
//时延函数
void Delay_ms( volatile unsigned int t)
{
unsigned int i;
while(t--)
for (i=0;i<10000;i++);
}
//控制灯亮灭
void A_LED_LIGHT()
{
GPIOA_ODR=0x0<<5; //PA5低电平
GPIOB_ODR=0x1<<9; //PB9高电平
GPIOC_ODR=0x1<<14; //PC14高电平
}
void B_LED_LIGHT()
{
GPIOA_ODR=0x1<<5; //PA5高电平
GPIOB_ODR=0x0<<9; //PB9低电平
GPIOC_ODR=0x1<<14; //PC14高电平
}
void C_LED_LIGHT()
{
GPIOA_ODR=0x1<<5; //PA5高电平
GPIOB_ODR=0x1<<9; //PB9高电平
GPIOC_ODR=0x0<<14; //PC14低电平
}
//------------------------主函数--------------------------
int main(){
RCC_APB2ENR|=1<<2; //APB2-GPIOA外设时钟使能
RCC_APB2ENR|=1<<3; //APB2-GPIOB外设时钟使能
RCC_APB2ENR|=1<<4; //APB2-GPIOC外设时钟使能
//也可以写成 RCC_APB2ENR|=1<<3|1<<4;
GPIOA_CRL&=0xFFF0FFFF; //设置位清零
GPIOA_CRL|=0X00200000; //PA5推挽输出,把第23、22、21、20位变为0010
GPIOB_CRH&=0xFF0FFFFF; //设置位清零
GPIOB_CRH|=0x00000020; //PB9推挽输出,把第7、6、5、4变为0010
GPIOC_CRH&=0xFF0FFFFF; //设置位清零
GPIOC_CRH|=0x02000000; //PC14推挽输出,把第27、26、25、24变为0010
GPIOA_ODR |= (1<<5);
GPIOB_ODR |= (1<<9); //设置灯的初始状态为灭
GPIOC_ODR |= (1<<14);
while(1)
{
A_LED_LIGHT();
Delay_ms(60);
B_LED_LIGHT();
Delay_ms(60);
C_LED_LIGHT();
Delay_ms(60);
}
}
3.4 编译调试
-
打开魔术棒,如下图所示勾选 Create HEX File
-
ST-Link/v2 驱动安装
链接:https://pan.baidu.com/s/1oazATTwP9eGdjANB4FmEng
提取码:plt3
点击 .exe 文件进行安装
-
将 STM32F103C8T6 通过ST-Link 连接到电脑 USB接口上
-
ST-Link与 C8T6 之间通过杜邦线按照下图连接:
-
点击 魔术棒,在Debug下勾选 Use:ST-Link Debugger
-
点击 Setting ,若此时 C8T6 通过St-link成功连接到了电脑上,就可以看到下图信息;点击 确定-> OK ,保存设置
-
点击编译
-
将程序烧录到 C8T6 上
此时可能会弹出以下信息:
点击确定就行了
-
烧录成功后红灯长亮
4、硬件连接
使用杜邦线将 A5、B9、C14连接到LED的负极(短脚),将开发板的3.3V输出通过杜邦线连接到LED的正极(长脚)
5、实现效果
建议重新烧录一次,然后按一下开发板上的 reset 键
效果如下:
C8T6实现LED流水灯
五、总结
本次主要使用STM32F103C8T6核心板,通过电路搭建实现LED灯轮流闪烁。通过本次实验进一步加深了我对GPIO寄存器的认识与运用,也让我学会了如何配置GPIO。本次实验不难,但是由于刚刚接触到GPIO的初始化以及配置,导致实验的时间拉的很长,希望通过不断学习可以提高自己的动手能力和解决问题的能力。
参考列表:
1.STM32启动文件:startup_stm32f10x_hd.s等启动文件的简单描述
2.STM32F103C8T6寄存器方式借助面包板点亮LED流水灯详解
3.stm32cubeMX使用HAL库点亮LED流水灯
4.stm32寄存器实现流水灯
5.STM32F103C8T6实现流水灯