目录
引言
接着上次HAL库的初次使用,本次来一个简单的开关灯练习,旨在巩固一下HAL库开发的基本操作步骤。
一、需求分析
实现三个LED依次开灯,然后依次关灯
如:开->LED1->LED2->LED3->关->LED3->LED2->LED1循环往复
二、硬件电路设计
控制LED的引脚:
黄灯 LED-1 -> PA0
蓝灯 LED-2 -> PA1
红灯 LED-3 -> PA8
LED触发条件:
对应引脚输出低电平触发
三、软件设计
本次工程基于HAL库实现,所以工程的创建和配置主要会在STM32CubeMX中进行,少数配置在Keil中进行,最后代码编写将在VSCode中进行。因此进行开始本工程前需要准备好三款软件:STM32CubeMX、Keil、VSCode
1、工程创建及配置
首先,进入STM32CubeMX,点击【ACCESS TO MCU SELECTOR】
等待一会,进入下图页面,然后左键单击左上【实五角星】,双击右下部分中我们使用的【STM32F103ZET6】芯片
进入新工程的配置界面
左键单击【System Core】,展开该菜单,左键点击【SYS】进入配置调试模式,修改Debug一栏为串口单线【serial wire】
修改后如下图所示,不用作系统唤醒(所以不选择);时基源选择默认系统嘀嗒定时器(所以不修改),后面两行不用修改
然后,进入【RCC】配置。分别修改【HSE】和【LSE】旁边一栏,均改为【晶体陶瓷振荡器】
如下图修改
修改这两处后其他不做修改,【RCC】配置后如下图样
然后配置系统时钟72MHz。点击【Clock Configuration】进入,然后修改四处为如下图样子
接着,进入【GPIO】配置。1 左键单击【GPIO】,2 然后收缩GPIO配置部分界面,尽可能留出图形化芯片展示界面,然后在右下角搜索LED对应引脚
1 依次搜索对应引脚,然后对应引脚会开始闪烁,2 接着左键单击闪烁引脚,展示复用功能,3 选择【GPIO Output】即可
选择后会呈现如下界面
然后同理对另两个引脚作同样配置,配置后如下
然后,对引脚工作模式进行配置
1 左键单击引脚栏,然后下面会显示处该引脚对应各个配置项,我们直接进行修改就能配置
本实验本质上还是点灯灭灯,所以配置GPIO工作模式为最高速度的通用推挽输出摸式即可。修改后如下图
其他两个引脚PA1 PA8同理修改,修改后如下图
然后,进入【Project Manager】,默认处于【Project】,配置工程名称及路径
给自己项目名称,然后选择工程存放路径,接着【Application Structers】选择Advanced,【ToolChain Folder Location】选择与工程相同的路径下,【IDE】选择 MDK。大家对于路径和项目名称可根据自身情况修改。修改后如下图
然后进入【Code Generator】,修改两部分勾选,修改如下图样
最后,点击【GENERATE CODE】,生成代码
此时弹出下图界面,点击【Open Project】即可在keil中打开代码
然后在keil中,进入【魔法棒】,然后进入调试【Debug】配置中,点击【settings】,然后点击【Flash Download】,勾选上【Reset and Run】,上电并运行
然后进入【Pack】,取消勾选【enable】,最后不要忘了保存!!!
然后,将之前我们写好的LED驱动代码直接加到该工程中。因为前面流水灯案例关于工程的配置与这里一模一样,只是代码核心逻辑不同。所以对于LED的驱动我们直接拿来用,写起来也简单。
1 LED驱动代码头文件和源文件分别放进工程中【Core】的【Inc】和【Src】中
2 进入keil工程,将LED文件加载到逻辑上的目录中,如下图步骤进行即可
到此,该工程创建配置就完成了!
2、思路简述
这里,我们先简单分析一下本案例实现方法。依次开灯,然后关灯,即我们循环遍历每一个LED,然后依次开灯操作,接着后往前遍历进行关灯操作,中间执行一个开关灯就延时500ms就能实现本案例了。
3、代码编写
编写代码我们在VSCode中进行,所以我们先进入VSCode。然后可以看见我们工程中原有的文件以及我们后面添加的LED驱动文件,即此时我们只需要在main文件中添加核心逻辑即可
那么,现在我们直接进入main文件,开始编写一次开关灯代码。
首先我们要对一组灯进行操作,按照前面我们编写流水灯代码的思路,会借助循环直接去遍历LED,既然遍历那么我们就会采用一个‘盒子’去按照一定顺序吧LED对应引脚给装好,也就是创建LED数组,存放依次存放LED引脚;接着就是在while死循环中编写循环,先进行开灯-延时的操作,然后逆序进行关灯-延时的操作
当然,首先我们在main文件中需要包含LED头文件,这样我们才能在里面使用LED驱动的函数
然后,我们在main函数中创建LED的数组
接着,我们在while死循环中添加核心逻辑
这里要注意的是,我们逆序进行遍历时,我们对循环变量i的类型定义不能简单定义成uint8_t,因为逆序i--到0时,最后一次遍历完他又会i--然后在判断是否≤0,但是由于类型是无符号uint8_t,大家知道无符号的类型数值时0开始的,即uint8_t数值范围0-255,一旦小于0了,就会变成255,此时就大于0了,这样就会导致我们第二个循环跑飞或者现象错误,因此这里我们给i的类型要改成int8_t,这样就能避免此问题的发生。
好的,本案例的代码就这些,实际上案例本身是很简单的,主要是为了巩固一下我们对HAL库开发操作的步骤流程,
这里在展示一下我们编写使用的代码吧
LED驱动代码:
1)led.h
#ifndef __LED_H
#define __LED_H
#include "gpio.h"
#define LED uint16_t
void LED_On(LED led);
void LED_Off(LED led);
void LED_Toggle(LED led);
void LED_AllOn(LED leds[], uint8_t size);
void LED_AllOff(LED leds[], uint8_t size);
#endif
2)led.c
#include "led.h"
void LED_On(LED led)
{
HAL_GPIO_WritePin(GPIOA, led, GPIO_PIN_RESET);
}
void LED_Off(LED led)
{
HAL_GPIO_WritePin(GPIOA, led, GPIO_PIN_SET);
}
void LED_Toggle(LED led)
{
HAL_GPIO_TogglePin(GPIOA, led);
}
void LED_AllOn(LED leds[], uint8_t size)
{
for (uint8_t i = 0; i < size; i++)
{
LED_On(leds[i]);
}
}
void LED_AllOff(LED leds[], uint8_t size)
{
for (uint8_t i = 0; i < size; i++)
{
LED_Off(leds[i]);
}
}
main文件代码:
// 头文件部分添加LED头文件
#include "main.h"
#include "gpio.h"
#include "led.h"
// main函数修改
int main(void)
{
/* USER CODE BEGIN 1 */
LED leds[] = {LED1_Pin, LED2_Pin, LED3_Pin};
/* USER CODE END 1 */
/*
*中间代码不做修改
*/
while (1)
{
/* USER CODE END WHILE */
for (uint8_t i = 0; i < 3; i++)
{
LED_On(leds[i]);
HAL_Delay(500);
}
// 逆序进行关灯
for (int8_t i = 2; i >= 0; i--)
{
LED_Off(leds[i]);
HAL_Delay(500);
}
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
这就是本次练习编写的代码了。对于该案例,我们实际上还有其他的方式去优化代码,比如我们可以思考一下如何只使用一个循环就实现本次一次开关灯的逻辑等,因为比较简单,这里我就不赘述了,如果需要可以在评论区留言哈。
四、效果展示
---------------待编辑--------------------
OK,这次的文章就到这里,在自身的学习过程中,也希望大家在评论区多多指教,谢谢!