在调试产品时,我们会用到指示灯来指示产品的当前状态,以便于定位问题点。
本文运用结构体和函数指针的方法,用类似于面向对象编程思维来点亮指示灯。
这种方法优点是:
1、保持模块的独立性,降低模块间的耦合度。
2、方便添加加新成员,想加几个灯就加几个灯。
缺点是:
1、程序用到结构体和函数指针操作,比较复杂。
2、占用内存比较大,小内存单片机不建议使用。
下面是代码,可以实现以下功能:
1、控制指示灯亮灭。
2、控制指示灯闪烁,闪烁初态,闪烁终态,闪烁周期,闪烁次数或持续闪烁。
3、方便添加新指示灯,各个指示灯的状态互不影响。
我不讲实现原理,只讲解如何使用代码,有兴趣的同学可以研究下源码。
建议大家直接拿来用,提高效率。
以下是全部源码
led_drive.h文件
#ifndef LED_DRIVE_H
#define LED_DRIVE_H
#include "gpio.h"
typedef unsigned char u8;
typedef unsigned short int u16;
typedef unsigned int u32;
#define d_LED_num_Max 2
typedef enum LED_MODE
{
NOP =0x00,//初始状态
ON_OFF =0x01,//亮灭模式
BLink =0x02,//闪烁模式
}LED_MODE;
typedef struct LED
{
GPIO_TypeDef *GPIOx;//GPIOx,x=A,B,C,D...
u16 GPIO_Pin; //GPIO_Pinx,x=1,2,3,4...
u8 init_state; //闪烁的初始状态
u8 final_state; //闪烁的最终状态
u8 mode; //亮灭或是闪烁模式
u8 numblinks; //闪烁次数,设置为100次以上,指示灯闪烁不停
u8 tim; //控制闪烁周期的变量,内部使用
u8 period; //闪烁周期
void (*LED_GPIO_Init)(struct LED *pLeds);//指示灯的GPIO初始化
void (*Set_LED_Mode)(struct LED *pLeds,u8 init_state,u8 final_state,LED_MODE mode,u8 numblinks,u8 period);//设置指示灯的参数
void (*LED_ON_OFF)(struct LED *pLeds,u8 state);// 亮灭模式使用,控制指示灯亮灭
}type_LED;
extern u8 B_LED_JC;//放在定时器中,每100ms置位,用于控制指示灯时序
extern void LED_Drive(void);//放在main函数,while(1)执行
extern void LED_Init(void);//初始化所有指示灯的参数
extern type_LED leds[d_LED_num_Max];//指示灯成员,d_LED_num_Max是指示灯数量,修改它可以增加或减少指示灯成员。
#endif
led_drive.c文件
#include "led_drive.h"
type_LED leds[d_LED_num_Max];
void LED_GPIO_Init(type_LED *pLeds);
void LED_ON_OFF(type_LED *pLeds,u8 state);
void Set_LED_Mode(type_LED *pleds,u8 init_state,u8 final_state,LED_MODE mode,u8 numblinks,u8 period);
//本例程只有两个指示灯,LED0和LED1,IO口输出高电平,灯亮
//用户需要多少个指示灯可以改变d_LED_num_Max的值,然后在下面进行IO口初始化。
void LED_Init(void)
{
u8 i;
leds[0].GPIOx=GPIOA;
leds[0].GPIO_Pin=GPIO_PIN_1;
leds[1].GPIOx=GPIOB;
leds[1].GPIO_Pin=GPIO_PIN_2;
//leds[2].GPIOx=GPIOB; 用户添加新成员后,在这里初始化新成员的IO口
//leds[2].GPIO_Pin=GPIO_PIN_5;用户添加新成员后,在这里初始化新成员的IO口
for(i=0;i<d_LED_num_Max;i++)
{
leds[i].init_state=0;
leds[i].final_state=0;
leds[i].mode=ON_OFF;
leds[i].numblinks=0;
leds[i].tim=0;
leds[i].period=0;
leds[i].LED_GPIO_Init=LED_GPIO_Init;
leds[i].Set_LED_Mode=Set_LED_Mode;
leds[i].LED_ON_OFF=LEDx_ON_OFF;
leds[i].LED_GPIO_Init(&leds[i]);
leds[i].LED_ON_OFF(&leds[i],leds[i].init_state);
leds[i].Set_LED_Mode(&leds[i],leds[i].init_state,leds[i].final_state,ON_OFF,leds[i].numblinks,leds[i].period);
}
}
void LED_GPIO_Init(type_LED *pLeds)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = pLeds->GPIO_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}
void LEDx_ON_OFF(type_LED *pLeds,u8 state)
{
switch(state)
{
case 0:HAL_GPIO_WritePin(pLeds->GPIOx, pLeds->GPIO_Pin, GPIO_PIN_RESET);break;
case 1:HAL_GPIO_WritePin(pLeds->GPIOx, pLeds->GPIO_Pin, GPIO_PIN_SET);break;
default:HAL_GPIO_WritePin(pLeds->GPIOx, pLeds->GPIO_Pin, GPIO_PIN_RESET);break;
}
}
void Set_LED_Mode(type_LED *pleds,u8 init_state,u8 final_state,LED_MODE mode,u8 numblinks,u8 period)//numblinks最大值只能设置为100,设置大于100,就是持续闪烁。
{
pleds->init_state=init_state;
pleds->final_state=final_state;
pleds->mode=mode;
pleds->numblinks=(numblinks<<1)-1;
pleds->period=period;
pleds->tim=0;
}
u8 B_LED_JC;//这个标志位放在定时器中断函数里,每100ms置1,用于控制指示灯时序
//如果要求时序非常精确,不用B_LED_JC,直接把LED_Drive()放到定时器中断函数中。
void LED_Drive(void)
{
static u8 led_state[d_LED_num_Max];//闪烁变量
u8 i;
if(!B_LED_JC)
return;
B_LED_JC=0;
for(i=0;i<d_LED_num_Max;i++)
{
if(leds[i].mode==ON_OFF)
{
LED_ON_OFF(&leds[i],leds[i].init_state);
}
if(leds[i].mode==BLink)
{
if(leds[i].numblinks)
{
if(leds[i].tim==0)//指示灯初始状态
{
led_state[i]=leds[i].init_state;
LED_ON_OFF(&leds[i],led_state[i]);
}
if(leds[i].tim<leds[i].period)//闪烁周期控制
leds[i].tim++;
else
{
led_state[i]++;
led_state[i]&=0x01;
LED_ON_OFF(&leds[i],led_state[i]);
leds[i].tim=1;
if(leds[i].numblinks>=199)//闪烁次数设置大于100次,持续闪烁
continue;
leds[i].numblinks--;
}
}
else
LED_ON_OFF(&leds[i],leds[i].final_state);//指示灯最终状态
}
}
}
main.c
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
LED_Init();//初始化所有指示灯的IO和参数
leds[0].Set_LED_Mode(&leds[0],1,1,ON_OFF,0,0);//设置LED0初始状态为亮灯,最终状态为亮灯,模式为亮灭模式,闪烁次数为0次,闪烁周期为0ms,即LED0长亮
leds[1].Set_LED_Mode(&leds[1],1,0,BLink,10,5);//设置LED1初始状态为亮灯,最终状态为灭灯,模式为闪烁模式,闪烁次数为10次,闪烁周期为5*100ms=500ms。设置完之后,LED1每500ms闪烁一次,10次之后灭灯。
while (1)
{
LED_Drive();
}
}
用户使用代码时需要修改3处。
1、#define d_LED_num_Max 2 //修改指示灯数量。
2、LED_Init()中,IO口的初始化。
3、在应用程序中调用leds[0].Set_LED_Mode(&leds[0],1,1,ON_OFF,0,0);修改指示灯的参数。
|