基于AT32(STM32)单片机的模块化代码之——Led代码模块化
基于AT32(STM32)单片机的模块化代码之——Led代码模块化上个文章讲了按键的模块化代码,这个文章贴一下我写的led灯部分的模块化代码,其实大同小异,按键和led灯都是gpio口的应用,不同的是用于按键的gpio口方向为输入方向,用于led灯的gpio口的方向为输出方向
1.环境介绍
平台:AT32F415单片机,雅特力公司的AT32系列单片机其实跟STM32系列单片机大同小异,包括库函数等基本都是一样的,所以这款代码无论是AT32还是STM32都是适用的。
开发环境:MDK V5 for arm
.代码模块化思路
2.1在led.h文件中定义枚举类型,枚举应用到的led灯
//枚举led灯
typedef enum
{
LED1_W,
LED1_O,
LED2_W,
LED2_O,
LED3_W,
LED3_O,
LED4_W,
LED4_O,
LED5_W,
LED5_O,
LED6_W,
LED6_O,
LED7_W,
NUM_LED
}ENUM_LED;
将枚举的led与其实际应该进行映射
//将led灯与其标识的功能对应上,电热毯项目
#define LED_POWER LED7_W
//区域1各级led对应
#define LED_Z1L1_W LED1_W
#define LED_Z1L1_O LED1_O
#define LED_Z1L2_W LED2_W
#define LED_Z1L2_O LED2_O
#define LED_Z1L3_W LED3_W
#define LED_Z1L3_O LED3_O
//区域2各级led对应
#define LED_Z2L1_W LED4_W
#define LED_Z2L1_O LED4_O
#define LED_Z2L2_W LED5_W
#define LED_Z2L2_O LED5_O
#define LED_Z2L3_W LED6_W
#define LED_Z2L3_O LED6_O
这样,当在app逻辑层需要对哪个led灯进行操作时,直接用LED_POWER这些实际用途的宏定义操作,如果某些个led灯在后续改版中功能进行了互换,只需要在功能映射宏定义这边做一下修改,其它其它地方都不用动,方便又省事。 在led.c文件中定义数个全局变量,用来作为配置功能,或者存储功能等用途 //根据项目中实际的led灯与引脚的对应关系进行配置
GPIO_Type* gs_LedPort = {GPIOA,GPIOC,GPIOA,GPIOC,GPIOB,GPIOD,
GPIOC,GPIOA,GPIOA,GPIOF,GPIOF,GPIOC};
u16 gu16_LedPins = {GPIO_Pins_8,GPIO_Pins_9,GPIO_Pins_15,GPIO_Pins_12,GPIO_Pins_3,GPIO_Pins_2,
GPIO_Pins_4,GPIO_Pins_7,GPIO_Pins_6,GPIO_Pins_5,GPIO_Pins_4,GPIO_Pins_14
例如我用PC14用做显示电源的指示灯,那么我就将对应的gs_LedPort=GPIOC,
gu16_LedPins=GPIO_Pins_14; 实现几个具体的功能函数
1.点亮单个led灯函数
void LED_ON(ENUM_LED LEDx)
{
GPIO_ResetBits(gs_LedPort,gu16_LedPins); //根据实际电路进行配置
}
关闭单个led灯的函数
void LED_OFF(ENUM_LED LEDx)
{
GPIO_SetBits(gs_LedPort,gu16_LedPins);//根据实际电路进行配置
}
需要根据实际电路设计对对应的gpio口赋低电平或者高电平。例如当对应的gpio为低电平时,led亮,就在LED_ON()函数中调用将gpio设置为低电平的函数。 led模块初始化函数
void LED_Init()
{
ENUM_LED i;
GPIO_InitType GPIOType;
//led灯引脚循环初始化
for (i = 0; i < NUM_LED; i++)
{
GPIO_StructInit(&GPIOType);
GPIOType.GPIO_Pins = gu16_LedPins;
GPIOType.GPIO_Mode = GPIO_Mode_OUT_PP; //模式设置为推挽输出模式
GPIOType.GPIO_MaxSpeed = GPIO_MaxSpeed_50MHz;
GPIO_Init(gs_LedPort,&GPIOType);
LED_OFF(i);//先关闭led灯
}
}
这几个基础函数写好了以后基本就完成了,后面就是根据实际的app部分的逻辑,自行再调用就可以,例如我自己项目中用到的当发生某个系统动作时更新led状态函数:
void LED_UpdatSysLed(u8 act)
{
act = (ENUM_SYSACT)act;
switch (act)
{
case SYS_ACTTURNON: //开机动作时led灯更新
LED_ON(LED_POWER);
LED_UpdatPowerOnLed();
break;
case SYS_ACTTURNOFF: //关机动作时led灯更新
LED_OFF(LED_POWER);
LED_UpdatPowerDownLed();
break;
case SYS_ACTRECOVE:
break;
default:
break;
}
}
其它,分享一个简单的呼吸灯的函数
//亮度变化方向
#define DIR_UP 1
#define DIR_DOWN 2
//灯光亮度调节的最大最低档位,
#define ADJLEV_MAX 200
#define ADJLEV_MIN 20
//led从暗到最亮的一个周期,单位ms
#define LED_BREATHCRCLE3000
void Led_Breath()
{
u32 curTim=Tim_GetT2MSTime();
static u32 lastStepTim=0;
static u8 dir=0;
static u16 curLev=0;
//每次进入呼吸模式都从最暗开始变化,该if语句只有在每次第一次进入呼吸模式时才会执行
if(!gu8_LedBreath**)
{
dir=DIR_UP;
curLev=ADJLEV_MIN;
gu8_LedBreath**=1;
}
//每隔固定时间就更新一次pwm占空比,实现灯光亮度变化
if(curTim-lastStepTim>=(LED_BREATHCRCLE/ADJLEV_MAX))
{
if(dir==DIR_UP)
{
curLev++;
//lev到顶时重置变化方向为向下
if(curLev>=ADJLEV_MAX)
{
dir=DIR_DOWN;
curLev=ADJLEV_MAX;
}
}
else if(curLev==DIR_DOWN)
{
curLev--;
//lev触底时重置变化方向为向上
if(curLev<=ADJLEV_MIN)
{
dir=DIR_UP;
curLev=ADJLEV_MIN;
}
}
PWM_SetDutyCycl(PWM_ZONE2,(curLev/2));//设置pwm占空比的函数,自行实现
lastStepTim=curTim;
}
}
呼吸灯的原理其实很简单,led要想点亮,那么就要led正确的一端是高电平,另一端是低电平,led就会点亮,并且在电压符合led参数的情况下,两端的电压差越高,那么led越亮。所谓的呼吸灯,就是led先从暗到亮,再从亮到暗的一个循环过程。那么这个要怎么实现呢,就需要将单片机控制的led灯的一端设置为pwm引脚,然后通过调节pwm的占空比,实现调节led灯的亮度。这样在一次由亮到暗的过程中,每个一段时间,按照比例去调一下占空比,就能实现灯光越来越暗。 另外还想再提一点的是,我上面写的呼吸灯的代码,每次调节led灯的pwm占空比的时间是相等的,都是
(LED_BREATHCRCLE/ADJLEV_MAX),前阵子做的一个小项目中,因为单片机硬件资源的限制,以及灯光比较亮等原因,呼吸灯过程中,每次在灯关暗区的时候,人眼能够明显察觉到灯关在变暗的过程中会有闪烁情况,后来想了一个办法,那就是根据当前的调节的步骤数,动态变化每步的调节时间,在暗区时就让两步之间的时间间越来越小,在俺去的时候就让两步之间的时间间隔越来越大,因为人眼对暗这个过程敏感,所以这样处理一下,让俺去的时候变得越来越快,这样暗区时间就会缩小很多,人眼就不会觉得暗区灯光变化时会有闪烁情况。
页:
[1]