使用STM32点亮LED
多学多练
一、题目描述
假设你手中已有 STM32最小系统核心板(STM32F103C8T6)+面板板+3只红绿蓝LED,并搭建了电路,分别GPIOA-5、GPIOB-9、GPIOC-14 这3个引脚上控制LED灯(最高时钟2Mhz),轮流闪烁,间隔时长1秒。
1)写出程序设计思路,包括GPIOx端口的各寄存器地址和详细参数;
2)用C语言 寄存器方式编程实现。
3)安装 stm32CubeMX,用cubemx完成初始化过程,采用HAL库编程实现。
4)在Keil下用软件仿真运行上面代码,并用虚拟逻辑分析仪观察 对应管脚上的输出波形(高低电平转换),看是否是1秒的周期。
二、设计思路
外设的功能都是完全不同的,但初始化都是大同小异的。
点灯是所有学单片机的人都应该学会的第一项技能,这样子才算入门。
51单片机的点灯是,通过控制寄存器将片外引脚(我们称之为IO口)拉低拉高,输出高低电平,以控制LED亮灭。
其过程:单片机给指令->控制寄存器->给IO口电平->控制LED亮灭
stm32的点灯则是,通过使能外设GPIO时钟,发出指令给外设GPIO,外设GPIO收到指令后,着手配置自己的寄存器,然后给IO口模式,让其实现各种功能。
其过程:CPU给指令->GPIO收到指令->配置内部寄存器->配置IO口模式(注意是模式)->控制LED亮灭
三、寄存器实现
01 时钟配置
使用PA6的端口,在面包板相应端口上接上LED灯
为什么配置时钟?为了省电,默认的时钟都是关闭的。配置STM32的任何资源前,都必须首先使能时钟。
时钟的信息在参考手册里边,参考手册十分巨大,不用通读,就像一个字典,需要什么查什么。
时钟控制名字叫做RCC,属于AHB总线。GPIOA属于APB2。
我们已经知道,GPIO端口A的地址从0x4001 0800开始。接下来只寻找时钟使能寄存器的地址:
复位和时钟控制RCC的地址从0x4002 1000开始;
可以在6.3.7小节找到APB2外设时钟使能寄存器(RCC_APB2ENR),偏移地址是0x18,所以APB2的地址就是0x4002 1018。
看手册RCC_APB2ENR,位2是IOPAEN,名字是IO端口A时钟使能,就是我们想要的。
把RCC_APB2ENR的位2赋值为1,就是开启GPIOA时钟。
02 模式设置
既然叫做IO,那么肯定就是可以输入,可以输出,到底是输入还是输出呢?
控制LED需要输出高电平或是低电平,所以需要配置为输出。
由于STM32的每个IO都需要4个位来配置,所以一个32位的寄存器最大只能配置8个IO(32位的单片机的寄存器就是32位的)。STM32中,用端口配置低寄存器(GPIOx_CRL)来配置引脚Px0-Px7, 用端口配置高寄存器(GPIOx_CRH)来配置引脚Px8-Px15。
配置引脚PA6,使用的寄存器是GPIOA_CRH。下面我们来寻找这个寄存器的地址。
在单片机的编程中,要想做某件事,必须寻找相应的寄存器。在8.2.4小节,可以找到端口输出数据寄存器,就是我们需要的。我们需要输出1(主要看面包板上LED灯的正负极怎么接)其他两个寄存器的寻找方法与之类似。
03 代码编写
led.c
#include "led.h"
//初始化PA5 PB9 PC14为输出口.并使能这三个口的时钟
//LED IO 初始化
void LED_Init(void)
{
RCC->APB2ENR|=1<<2; //使能 IOPA 时钟
RCC->APB2ENR|=1<<3; //使能 IOPB 时钟
RCC->APB2ENR|=1<<4; //使能 IOPC 时钟
// RCC->APB2ENR|=1<<5; //使能 IOPD 时钟
GPIOA->CRL&=0XFF0FFFFF;
GPIOA->CRL|=0X00300000;//PA.5 推挽输出
GPIOA->ODR|=1<<5; //PA.5 输出高
GPIOB->CRH&=0XFFFFFF0F;
GPIOB->CRH|=0X00000030;//PB.9 推挽输出 9属于端口配置高位寄存器
GPIOB->ODR|=1<<9; //PB.9 输出高
GPIOC->CRH&=0XF0FFFFFF;
GPIOC->CRH|=0X03000000;//PC.14 推挽输出
GPIOC->ODR|=1<<14; //PC.14 输出高
}
led.h
#ifndef __LED_H
#define __LED_H
#include "sys.h"
//LED 端口定义
#define LED0 PAout(5) // DS0
#define LED1 PBout(9) // DS1
#define LED2 PCout(14) // DS2
void LED_Init(void); //初始化
#endif
test.c
#include "sys.h"
#include "delay.h"
#include "led.h"
int main(void)
{
Stm32_Clock_Init(9); //系统时钟设置
delay_init(72); //延时初始化
LED_Init(); //初始化与 LED 连接的硬件接口
while(1)
{
LED0=0;
LED1=1;
LED2=1;
delay_ms(1000);
LED0=1;
LED1=0;
LED2=1;
delay_ms(1000);
LED0=1;
LED1=1;
LED2=0;
delay_ms(1000);
}
}
04 创建工程文件
新建工程,选择F103C8
基本配置
加入启动配置文件
新建以下文件并将启动文件加入目录,这时候我们发现这里报错,没有"sys.h"系统文件
添加系统配置文件“Manage Project Items…”
添加两个组:
点击魔术棒,生成HEX文件
加入sys、delay、usart的include路径:
在文件夹中添加system_stm32f10x.c
可以解决..\OBJ\test.axf: Error: L6218E: Undefined symbol SystemInit (referred from startup_stm32f10x_hd.o).
的错误。所以说大家有问题可以多百度解决一哈。
在创建工程这里需要耗费很多精力,所以还是建议大家可以找一个靠谱的模板文件,每次都复制模板文件夹,不用重复新建这些文件。
四、HAL库实现
01 安装 STM32CubeMX
笔者在假期观看B站完成安装,有很多依附环境,一点点复杂。但祝愿大家都可以成功安装
02 安装HAL库
- 打开安装好的STMCubeMX,点击
HELP
->Manage embedded software packages
:
- 会跳出来一个选择型号界面 勾选上你要安装的HAL库, 点击“Install Now” 直到安装成功。 如下图(笔者已经安装过一种,图片仅做演示)
03 新建工程
- 回到STMCubeMX的主界面,创建新项目:
- 在part name里选择自己的芯片,点击信息栏中的具体芯片信息选中,点击start project。(包装方式TR:带卷;无特性:盘装)
- 点击system core,进入SYS,在debug下选择serial wire:
- 配置时钟,进入上面的rcc,有两个时钟,一个是hse和lse,我们要用是GPIO接口,而这些接口都在APB2里:
接下来观察时钟架构,APB2总线的时钟由hse控制,同时在这个界面得把PLLCLK右边选上:
- 将hse那里设为Crystal/Ceramic Resonator:
- 接下来就是点击相应的引脚设置输出寄存器了,就是output那一项,一共选了三个,是PA5,PB9,PC14:
- 点击project manager,配置好自己的路径和项目名,然后IDE那项改为MDK-ARM:
- 进入 code generate界面,选择生成初始化.c/.h文件,后面点击generate code,选择open project,然后就到KEIL5了
04 Keil仿真调试
打开Keil文件,在main函数中找到while循环:
在循环中写入以下代码:
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */ //用户代码开始部分
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_5,GPIO_PIN_RESET);//PA5亮灯
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_9,GPIO_PIN_SET);//PB9熄灯
HAL_GPIO_WritePin(GPIOC,GPIO_PIN_14,GPIO_PIN_SET);//PC14熄灯
HAL_Delay(1000);//延时1s
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_5,GPIO_PIN_SET);//PA5熄灯
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_9,GPIO_PIN_RESET);//PB9亮灯
HAL_GPIO_WritePin(GPIOC,GPIO_PIN_14,GPIO_PIN_SET);//PC14熄灯
HAL_Delay(1000);//延时1s
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_5,GPIO_PIN_SET);//PA5熄灯
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_9,GPIO_PIN_SET);//PB9熄灯
HAL_GPIO_WritePin(GPIOC,GPIO_PIN_14,GPIO_PIN_RESET);//PC14亮灯
HAL_Delay(1000);//延时1s
}
/* USER CODE END 3 *///用户代码结束部分
五、硬件连接
wait
六、逻辑仿真分析
- Target界面中,选择跟正确的晶振大小,我使用的是8MHz的外部晶振。这个选项在软件仿真中起到很重要的作用,如果选择错误,那么波形一定是错误的,因为时间不准确。
- Debug页的设置:
- 点击Debug,进入调试界面, 选择逻辑分析仪
- 选择要观察的引脚:点击Setup Logic Analyzer
- 增加要观察的引脚
-
相关设置
-
运行程序
-
观察波形
可以看到三个引脚交替101、011、110
七、总结
寄存器和HAL库相比之下,还是HAL库更加方便。寄存器查找位置,翻阅文件十分复杂。通过这次学习能够更深刻学到STM32编程的意义。也希望早日上实物操作,锻炼动手能力。
参考资料
1、STM32F103C8T6寄存器方式借助面包板点亮LED流水灯详解
2、stm32cubeMX使用HAL库点亮LED流水灯