蓝桥杯嵌入式CT117E硬件开发平台经验分享04 | LED显示

蓝桥杯嵌入式CT117E硬件开发平台 | LED显示驱动设计

关于LED驱动部分,首先注意的则是LED接口问题,由于LED采用了PC8 - PC15 和LCD的数据线共用了这一部分GPIO口,所以设计时候要考虑到此处问题。设计流程如下:

  1. LED初始化设计,常规的IO口初始化操作,不再多介绍。根据开发板原理图可知,LED灯采用了74HC573锁存器驱动,因此也需要配置74HC573的使能端对应的GPIO口:PD2,如下代码:
/**
  * @说明     LED 相关GPIO引脚工作模式及时钟源配置
  * @参数     None 
  * @返回值   None
***/
void LED_Init(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD,ENABLE);
	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_11 | GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15; //LED灯占用的GPIO
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;  //推挽输出模式
	GPIO_Init(GPIOC, &GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;  //74HC573锁存器使能端对应的GPIO
	GPIO_Init(GPIOD, &GPIO_InitStructure);
}
  1. LED宏定义和结构体设计:为了方便对LED精准操作又不需要记具体数值,故而为LED做了对应的宏名。同时设计了LED的结构体,目的在于根据结构体的标志FLAG,对LED灯需要什么状态做出判断。
#define LED1    8
#define LED2    9
#define LED3    10
#define LED4    11
#define LED5    12
#define LED6    13
#define LED7    14
#define LED8    15
#define LEDALL	0xff
/**
  * @说明      LED_TypeDef结构体用于对LED控制的标志位元素进行定义
  * typedef enum{		
	OFF = 0,
	ON = 1,
	REVERSE = 2
    }STATE_ENUM;
  * @包含出处  JUDGE_TypeDef位于#include "Def_config.h"
***/
typedef struct{	
	JUDGE_ENUM IS_LED_1_OUT;
	JUDGE_ENUM IS_LED_2_OUT;
	JUDGE_ENUM IS_LED_3_OUT;
	JUDGE_ENUM IS_LED_4_OUT;  
}LED_TypeDef;
extern LED_TypeDef LED_Structure;
  1. LED驱动设计方法:由于LED和LCD公用GPIO口的关系,所以两者同时使用时会出现很多冲突,导致LED突亮等不稳定操作,虽然LED有573锁存器锁存状态,但是对于LED读取当前IO状态并进行反转操作时就会有影响,因为此时LED对应的GPIO状态很可能不是之前LED设置的状态,而是LCD数据操作时遗留的状态。解决方法:尽量不采用GPIO位读取,而是将LED的状态赋值给一个变量,只将最终设计的状态赋值给LED对应的GPIO,这样就尽量的少操作了GPIO,而大多数的判断等都是对自己赋值的一个变量而设计,非常稳定,不会因为GPIO状态而影响。实现方法如下(***第一版设计,有一定的缺陷:LED控制函数无法和库函数一样同时进行多个LED的赋值操作,除了全部赋值外,此问题会在下面的二版解决。***):
**
  * @说明     控制LED打开或关闭
  * @参数     Led: LED编号,GPIO_Pin_8到GPIO_Pin_15
  * @参数     Ledstatus: 0:关闭LED* @参数     1:打开LED    
  * @参数 	  2:reverse:反转LED状态
  * @返回值   None
  * @注意     GPIOC_Buff为重要的缓存参数,不得随意更改,LED的状态靠此参数配置
  * @注意     LED_Control函数为了减小工作量,没有循环检测处理,故而
  * @注意     每次只能处理一个LED或者全部的LED,不能进行 或操作 并行处理多个LED状态
  * @注意     如:LED_Control(LED1,ON);LED_Control(LED2,ON);LED_Control(LEDALL,ON);
  * @注意     千万不要:LED_Control(LED1 | LED2,ON);!!!此方法无法使用
  */
__IO uint16_t GPIOC_Buff=0xffff; //__IO 为 volatile:不允许编译器优化
void LED_Control(uint8_t LED,STATE_ENUM LED_Status)
{
	uint16_t LED_Buff=0xffff;
	if(LED != 0xff)  
	{
		if(LED_Status == OFF){
			GPIOC_Buff |= (1 << LED);
		}        
		else if(LED_Status == ON)
		{
			GPIOC_Buff &=~ (1 << LED);
		}
		else
		{
			LED_Buff = GPIOC_Buff;  
			if(LED_Buff & (1 << LED))  //LED 口为1,表示LED关着, 反转则需要开启
			{
				GPIOC_Buff &=~ (1 << LED);  
			}
			else{
				GPIOC_Buff |= (1 << LED);
			} 
		}	
	}
	else  //ledall control
	{
		if(LED_Status == OFF){
			GPIOC_Buff |= 0xff00;
		}        
		else if(LED_Status == ON)
		{
			GPIOC_Buff &= 0x00ff;
		}
		else
		{
			LED_Buff = GPIOC_Buff;  
			if(LED_Buff & 0xff00)   //为真,高8位不为0
			{
				GPIOC_Buff &= 0x00ff;
			}
			else{
				GPIOC_Buff |= 0xff00;        
			} 
		}
	}
	GPIOC->ODR |= 0Xff00;  //清空LCD写入的缓存问题!!!	
	GPIOC->ODR &= GPIOC_Buff;	    //只有开的LED才有资格被写入ODR(GPIO输出数据寄存器)
	GPIO_SetBits(GPIOD,GPIO_Pin_2);  //573锁存器使能,存入LED状态
	GPIO_ResetBits(GPIOD,GPIO_Pin_2);  //状态锁存 ,573不使能,之后GPIO的状态不会影响LED
}
  1. LED第二版驱动:虽然LED第一版驱动无法同时对多个LED操作,但是也有一个好处:可以进行累加LED选择,因为宏定义对LED都是具体数值8-15,可以动态选择操作的LED,在有一年的嵌入式省赛试题中操作LED会非常方便,这个之后再谈。第二版则是仿照GPIO库操作而设计,能够使用或语句同时操作多个LED的状态:开,关,反转。方法如下:
//对LED宏定义做了修改,方便识别操作的LED是哪一位,与一版LED驱动最大的不同
#define LED1    ((uint16_t)0x0100)
#define LED2    ((uint16_t)0x0200)
#define LED3    ((uint16_t)0x0400)
#define LED4    ((uint16_t)0x0800)
#define LED5    ((uint16_t)0x1000)
#define LED6    ((uint16_t)0x2000)
#define LED7    ((uint16_t)0x4000)
#define LED8    ((uint16_t)0x8000)
#define LEDALL	((uint16_t)0xFF00)

//LED.c中的驱动,依据GPIO驱动而设计
__IO uint16_t GPIOC_Buff=0xffff; //__IO 为 volatile:不允许编译器优化
void LED_Control(uint16_t LED,STATE_ENUM LED_Status)
{
	uint16_t LED_Buff=0xffff;
	uint16_t pinpos = 0x0000,pos = 0x0000,currentpin = 0x0000;
	for(pinpos = 0x00;pinpos < 0x08;pinpos ++)
	{
		pos = ((uint32_t)0x01) << (pinpos + 0x08);
		currentpin = (LED) & pos;
		if(currentpin == pos)
		{
			if(LED_Status == OFF){
			GPIOC_Buff |= (((uint32_t)0x0001) << (pinpos + 0x08));
			}        
			else if(LED_Status == ON)
			{
				GPIOC_Buff &=~ (((uint32_t)0x0001) << (pinpos + 0x08));
			}
			else
			{
				LED_Buff = GPIOC_Buff;  
				if(LED_Buff & (((uint32_t)0x0001) << (pinpos + 0x08)))  //LED 口为1,表示LED关着, 反转则需要开启
				{
					GPIOC_Buff &=~ (((uint32_t)0x0001) << (pinpos + 0x08));
				}
				else{
					GPIOC_Buff |= (((uint32_t)0x0001) << (pinpos + 0x08)); 
				} 
			}
		}			
	}
	GPIOC->ODR |= 0Xff00;  //清空LCD写入的缓存问题!!!	
	GPIOC->ODR &= GPIOC_Buff;	    //只有开的LED才有资格被写入ODR
	GPIO_SetBits(GPIOD,GPIO_Pin_2);  //高电平允许
	GPIO_ResetBits(GPIOD,GPIO_Pin_2);  //低电平状态锁存
}

你可能感兴趣的:(单片机嵌入式,嵌入式,单片机,stm32,编程语言)