STM32HAL库 -- 4.LED流水灯

目录

1.简介

2.LED模块简介

3.硬件连接

4.编写代码

4.1led.h编写

4.2LED灯初始化

4.3点亮、关闭LED灯

4.4切换LED灯状态

4.5查询LED灯状态

5.LED代码

5.1led.h

5.2led.c

5.3流水灯 -- main.c

1.简介

        通过上一个博客介绍的HAL库操作GPIO的函数,来实现LED流水的的实验。笔者使用的开发板是STM32F103ZET6为MCU的正点原子精英板,读者可以使用自己的开发板来复现实验。开发板不是重点,重点是学习HAL库的使用。如果读者没学习过GPIO以及HAL库操作GPIO,可以通过下面的链接进行简单的学习一下:

STM32HAL库 -- 1.手把手创建HAL库工程模板

STM32HAL库 -- 2.介绍外设GPIO

STM32HAL库 -- 3.介绍HAL库中操作GPIO的函数

2.LED模块简介

        LED(Light Emitting Diode),即发光二极管,是一种能够将电能转化为可见光的半导体器件。LED的核心部分是由P型半导体和N型半导体组成的晶片,在P型半导体和N型半导体之间有一个过渡层,称为PN结。当电流通过导线作用于这个晶片时,电子和空穴在PN结复合,能量以光子的形式释放出来,从而发出光芒。不同材料的半导体可以发出不同颜色的光,导通压降也不同。

3.硬件连接

        笔者的STM32开发板上有两个LED灯,分别连接在了PB5和PE5引脚上,如下图所示:

        PB5连接的是红灯,LED0;PE5是绿灯,LED1。另外的一个LED灯用作电源指示灯。

        首先,两个灯的正极分别连接了一个电阻,用于限流,防止电流过大烧毁,然后并联到3.3V的VCC电源上。阴极分别连接STM32的PB5和PE5上。(这两个灯的导通压降我忘记了。。)

4.编写代码

4.1led.h编写

        首先复制一份模板工程,在/Drivers/BSP目录下创建一个文件夹,起名为LED,用于存放驱动LED灯的代码,并在该文件夹中创建led.c和led.h文件,如下图所示:

        之后打开工程编写led.h的代码。

        首先编写预编译处理的语句如下图所示:

        #ifndef _LED_H_:这是一个预处理指令,用于检查是否已经定义了 _LED_H_ 这个宏。如果这个宏没有被定义,那么编译器会执行接下来的代码。

        #define _LED_H_:如果 _LED_H_ 没有被定义,那么这个指令会定义 _LED_H_ 宏。这通常用于确保头文件只被包含一次,避免重复定义的问题。

        #endif:这是预处理指令的结束标志,表示如果 _LED_H_ 宏没有被定义,那么执行到这里就结束了。如果 _LED_H_ 已经被定义,那么整个代码块都会被忽略。

        这种结构通常用于C语言的头文件中,以确保头文件只被包含一次,避免重复定义的问题。

        使用宏定义先定义出LED灯使用的GPIO端口和引脚,如下图所示:

        之后定义了枚举值,枚举出灯的状态,灯的号码,等只有亮灭两个状态,所以代码如下图所示:

        之后,思考需要封装哪些函数,LED灯初始化,点亮某个LED灯,关闭某个LED灯,全部打开,全部关闭,流水灯,某个灯闪烁,全部闪烁。

4.2LED灯初始化

        相当于初始化GPIO引脚首先要打开所用外设的时钟,之后配置成推挽输出模式,输出速度慢速,引脚和上下拉电阻,最后写入初始值。代码如下:

/**
 * @brief       LED初始化函数
 * @param       无
 * @retval      无
 */
void LED_Init(void)
{
	/*	1.打开时钟	*/
	__HAL_RCC_GPIOB_CLK_ENABLE();
	__HAL_RCC_GPIOE_CLK_ENABLE();
	
	/*	2.配置GPIO	*/
	GPIO_InitTypeDef gpio_initure = {0};
	gpio_initure.Pin = LED0_PIN;				//LED0引脚
	gpio_initure.Mode = GPIO_MODE_OUTPUT_PP;	//推挽输出
	gpio_initure.Speed = GPIO_SPEED_FREQ_LOW;	//慢速
	gpio_initure.Pull = GPIO_NOPULL;			//不使用上下拉电阻,推挽输出模式下,上下拉电阻通常无实际作用
	HAL_GPIO_Init(LED0_PORT, &gpio_initure);	//调用GPIO初始化函数
	
	gpio_initure.Pin = LED1_PIN;				//LED0引脚
	HAL_GPIO_Init(LED1_PORT, &gpio_initure);	//调用GPIO初始化函数
	
	/*	3.写入初始值,默认关灯	*/
	HAL_GPIO_WritePin(LED0_PORT, LED0_PIN, GPIO_PIN_RESET);
	HAL_GPIO_WritePin(LED1_PORT, LED1_PIN, GPIO_PIN_RESET);
}

        注意:

        1.推挽输出模式下,通常不需要配置上拉或下拉电阻,因为GPIO输出驱动器已直接控制电平。当输出高电平时,引脚由内部电路驱动到VCC,当输出低电平时,引脚由内部电路驱动到GND。

         2.开漏输出模式下,GPIO只能拉低电平或处于高阻态。必须外接上拉电阻,否则无法输出高电平。典型应用:I²C总线、多设备共享线路(避免电平冲突)。

4.3点亮、关闭LED灯

       要使LED灯被点亮,需要LED两端的电压差大于导通条件。查看上面原理图,正极接的3.3V,负极接在GPIO引脚,要使得LED灯被点亮,需要做的是GPIO输出低电平,这样LED灯的两端电压差就大于导通压降了。同理,熄灭LED灯就输出高电平。

       封装几个函数,想要什么操作就调用什么函数即可。将打开、关闭、全部打开、全部关闭分别封装成函数,代码如下:

/**
 * @brief       打开LEDx灯
 * @param       无
 * @retval      无
 */
void LED_TurnOn(LED_Num ledx)
{
	if(ledx == LED0)
		HAL_GPIO_WritePin(LED0_PORT, LED0_PIN, GPIO_PIN_RESET);
	else if(ledx == LED1)
		HAL_GPIO_WritePin(LED1_PORT, LED1_PIN, GPIO_PIN_RESET);
}


/**
 * @brief       打开全部LED灯
 * @param       无
 * @retval      无
 */
void LED_TurnOnAll(void)
{
	HAL_GPIO_WritePin(LED0_PORT, LED0_PIN, GPIO_PIN_RESET);
	HAL_GPIO_WritePin(LED1_PORT, LED1_PIN, GPIO_PIN_RESET);
}


/**
 * @brief       关闭LEDx灯
 * @param       无
 * @retval      无
 */
void LED_TurnOff(LED_Num ledx)
{
	if(ledx == LED0)
		HAL_GPIO_WritePin(LED0_PORT, LED0_PIN, GPIO_PIN_SET);
	else if(ledx == LED1)
		HAL_GPIO_WritePin(LED1_PORT, LED1_PIN, GPIO_PIN_SET);
}


/**
 * @brief       关闭全部LED灯
 * @param       无
 * @retval      无
 */
void LED_TurnOffAll(void)
{
	HAL_GPIO_WritePin(LED0_PORT, LED0_PIN, GPIO_PIN_SET);
	HAL_GPIO_WritePin(LED1_PORT, LED1_PIN, GPIO_PIN_SET);
}

4.4切换LED灯状态

        直接使用HAL_GPIO_TogglePin函数来完成操作。代码如下:

/**
 * @brief       切换LEDx灯状态
 * @param       无
 * @retval      无
 */
void LED_Toggle(LED_Num ledx)
{
	if(ledx == LED0)
		HAL_GPIO_TogglePin(LED0_PORT, LED0_PIN);
	else if(ledx == LED1)
		HAL_GPIO_TogglePin(LED1_PORT, LED1_PIN);
}


/**
 * @brief       切换全部的LED灯状态
 * @param       无
 * @retval      无
 */
void LED_ToggleAll(void)
{
	HAL_GPIO_TogglePin(LED0_PORT, LED0_PIN);
	HAL_GPIO_TogglePin(LED1_PORT, LED1_PIN);
}

4.5查询LED灯状态

        再封装一个函数,将某个LED灯的枚举值作为参数,函数的返回值是灯的状态。

        这样就使用查询的方式来改变LED灯的状态,如果查询的关闭状态,就打开;如果的打开状态,就关闭。

        代码如下:

/**
 * @brief       查询LEDx灯状态
 * @param       无
 * @retval      LED_State,LED_OFF为关闭状态,LED_ON为开启状态
 */
LED_State LED_CheckStatus(LED_Num ledx)
{
	GPIO_PinState bitstatus = (GPIO_PinState)0;
	LED_State status = LED_OFF;
	
	if(ledx == LED0)
		bitstatus = HAL_GPIO_ReadPin(LED0_PORT, LED0_PIN);
	else if(ledx == LED1)
		bitstatus = HAL_GPIO_ReadPin(LED1_PORT, LED1_PIN);
	
	if(bitstatus == GPIO_PIN_SET)
		status = LED_OFF;
	else if(bitstatus == GPIO_PIN_RESET)
		status = LED_ON;
	
	return status;
}

5.全部代码

5.1led.h

        我把led.h的代码放在这里,供大家参考。

#ifndef __LED_H__
#define __LED_H__


/*****************************************************
                头文件
******************************************************/
#include "sys.h"


/*****************************************************
                宏定义
******************************************************/
#define     LED0_PORT		 GPIOB
#define     LED0_PIN         GPIO_PIN_5

#define     LED1_PORT		 GPIOE
#define     LED1_PIN         GPIO_PIN_5


/*****************************************************
                数据类型定义
******************************************************/
typedef enum
{
  LED_OFF = 0u,
  LED_ON
}LED_State;

typedef enum
{
	LED0 = 0u,
	LED1
}LED_Num;
	


/*****************************************************
                函数声明
******************************************************/
void LED_Init(void);
void LED_TurnOn(LED_Num ledx);
void LED_TurnOnAll(void);
void LED_TurnOff(LED_Num ledx);
void LED_TurnOffAll(void);
void LED_Toggle(LED_Num ledx);
void LED_ToggleAll(void);
LED_State LED_CheckStatus(LED_Num ledx);


#endif

5.2led.c

        封装的函数:

#include "led.h"


/**
 * @brief       LED初始化函数
 * @param       无
 * @retval      无
 */
void LED_Init(void)
{
	/*	1.打开时钟	*/
	__HAL_RCC_GPIOB_CLK_ENABLE();
	__HAL_RCC_GPIOE_CLK_ENABLE();
	
	/*	2.配置GPIO	*/
	GPIO_InitTypeDef gpio_initure = {0};
	gpio_initure.Pin = LED0_PIN;				//LED0引脚
	gpio_initure.Mode = GPIO_MODE_OUTPUT_PP;	//推挽输出
	gpio_initure.Speed = GPIO_SPEED_FREQ_LOW;	//慢速
	gpio_initure.Pull = GPIO_NOPULL;			//不使用上下拉电阻,推挽输出模式下,上下拉电阻通常无实际作用
	HAL_GPIO_Init(LED0_PORT, &gpio_initure);	//调用GPIO初始化函数
	
	gpio_initure.Pin = LED1_PIN;				//LED0引脚
	HAL_GPIO_Init(LED1_PORT, &gpio_initure);	//调用GPIO初始化函数
	
	/*	3.写入初始值,默认关灯	*/
	HAL_GPIO_WritePin(LED0_PORT, LED0_PIN, GPIO_PIN_SET);
	HAL_GPIO_WritePin(LED1_PORT, LED1_PIN, GPIO_PIN_SET);
}


/**
 * @brief       打开LEDx灯
 * @param       无
 * @retval      无
 */
void LED_TurnOn(LED_Num ledx)
{
	if(ledx == LED0)
		HAL_GPIO_WritePin(LED0_PORT, LED0_PIN, GPIO_PIN_RESET);
	else if(ledx == LED1)
		HAL_GPIO_WritePin(LED1_PORT, LED1_PIN, GPIO_PIN_RESET);
}


/**
 * @brief       打开全部LED灯
 * @param       无
 * @retval      无
 */
void LED_TurnOnAll(void)
{
	HAL_GPIO_WritePin(LED0_PORT, LED0_PIN, GPIO_PIN_RESET);
	HAL_GPIO_WritePin(LED1_PORT, LED1_PIN, GPIO_PIN_RESET);
}


/**
 * @brief       关闭LEDx灯
 * @param       无
 * @retval      无
 */
void LED_TurnOff(LED_Num ledx)
{
	if(ledx == LED0)
		HAL_GPIO_WritePin(LED0_PORT, LED0_PIN, GPIO_PIN_SET);
	else if(ledx == LED1)
		HAL_GPIO_WritePin(LED1_PORT, LED1_PIN, GPIO_PIN_SET);
}


/**
 * @brief       关闭全部LED灯
 * @param       无
 * @retval      无
 */
void LED_TurnOffAll(void)
{
	HAL_GPIO_WritePin(LED0_PORT, LED0_PIN, GPIO_PIN_SET);
	HAL_GPIO_WritePin(LED1_PORT, LED1_PIN, GPIO_PIN_SET);
}


/**
 * @brief       切换LEDx灯状态
 * @param       无
 * @retval      无
 */
void LED_Toggle(LED_Num ledx)
{
	if(ledx == LED0)
		HAL_GPIO_TogglePin(LED0_PORT, LED0_PIN);
	else if(ledx == LED1)
		HAL_GPIO_TogglePin(LED1_PORT, LED1_PIN);
}


/**
 * @brief       切换全部的LED灯状态
 * @param       无
 * @retval      无
 */
void LED_ToggleAll(void)
{
	HAL_GPIO_TogglePin(LED0_PORT, LED0_PIN);
	HAL_GPIO_TogglePin(LED1_PORT, LED1_PIN);
}

/**
 * @brief       查询LEDx灯状态
 * @param       无
 * @retval      LED_State,LED_OFF为关闭状态,LED_ON为开启状态
 */
LED_State LED_CheckStatus(LED_Num ledx)
{
	GPIO_PinState bitstatus = (GPIO_PinState)0;
	LED_State status = LED_OFF;
	
	if(ledx == LED0)
		bitstatus = HAL_GPIO_ReadPin(LED0_PORT, LED0_PIN);
	else if(ledx == LED1)
		bitstatus = HAL_GPIO_ReadPin(LED1_PORT, LED1_PIN);
	
	if(bitstatus == GPIO_PIN_SET)
		status = LED_OFF;
	else if(bitstatus == GPIO_PIN_RESET)
		status = LED_ON;
	
	return status;
}

5.3流水灯 -- main.c

在这里实现流水灯,另外,main函数还有测试其他函数的代码,被我用#if 0 ...... #endif注释了,这里面的代码不会被编译执行。

代码如下:

#include "sys.h"
#include "uart1.h"
#include "delay.h"

#include "led.h"


int main(void)
{
    HAL_Init();                         /* 初始化HAL库 */
	stm32_clock_init(RCC_PLL_MUL9); 	/* 设置时钟, 72Mhz */
    uart1_init(115200);
	LED_Init();
	
	while(1)
	{
		//流水灯
		LED_TurnOn(LED0);
		LED_TurnOff(LED1);
		delay_ms(500);
		LED_TurnOn(LED1);
		LED_TurnOff(LED0);
		delay_ms(500);
		
#if 0
		LED_TurnOn(LED0);
		delay_ms(500);
		LED_TurnOff(LED0);
		delay_ms(500);
		
		LED_TurnOn(LED1);
		delay_ms(500);
		LED_TurnOff(LED1);
		delay_ms(500);
		
		LED_TurnOnAll();
		delay_ms(500);	
		LED_TurnOffAll();
		delay_ms(500);	
		
		LED_Toggle(LED1);
		delay_ms(500);	
		LED_Toggle(LED0);
		delay_ms(500);	
		LED_ToggleAll();
		delay_ms(500);


		if(LED_CheckStatus(LED0) == LED_ON)
		{
			LED_Toggle(LED0);
			delay_ms(500);
		}
		else if(LED_CheckStatus(LED0) == LED_OFF)
		{
			LED_Toggle(LED0);
			delay_ms(500);
		}
#endif		
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值