05 LCD驱动编写
上一篇:04 按键驱动编写
下一篇:06 ADC与I2C应用
作者:桂信科黄鹏老师。note部分是我添加的内容
主要内容
- 如何运用组委会提供的LCD驱动?
- 在此基础上进行界面设计。
- 配合按键驱动应用,实现页面切换、数据更改等功能。
一、在比赛现场使用LCD驱动的叮嘱
1)比赛时组委会提供LCD驱动文件,比赛时驱动文件不用更改,需要学会使用即可。
2)比赛时提供的CubeMX工程文件可直接当做比赛工程的模版。
3)组委会提供的LCD驱动文件,IO口没有定义User Label,因为使用寄存器的方式操作,它不影响驱动的使用。
4)比赛的显示界面只需要显示英文和数值,因此不需要显示中文和图片。
5)若使用组委会提供的CubeMX工程,需要修改时钟配置,由80Mhz改为150Mhz。
若不想使用组委会的模版,自己创建模版也是可行的:需要将组委会的驱动文件拷贝到自己创建的工程中。只需要拷贝3个文件,分别“lcd.c”,”lcd.h“,“fonts.h”。然后将main.c中的LCD初始化代码拷贝到自己的初始化代码中。初始化代码LCD_Init();
思考:真正比赛时以什么步骤写各个模块的代码?
Note
- 搭建工程,拷贝LCD和I2C驱动到工程,下载代码看是否成功。
- 写LED和按键驱动。
- 注册按键回调函数,实现LCD菜单。
- 再去实现ADC、I2C这类的驱动
- 串口和波形输出/捕获
二、引脚配置
参照02 CubeMX配置引脚#三、LCD引脚完成该步骤。
三、代码实现
1. 在官方驱动基础上增加应用函数
题目要求在指定坐标写入字符串,使用组委会的代码不太方便做。我们需要新增加一个函数,功能是在指定的行列坐标写入一个指定的字符串。
//页面中根据坐标显示一行
void Page_Line_Display(uint8_t _byClear,uint8_t _byLine, uint16_t _wColumn,uint8_t* _pbyString)
{
uint8_t i = 0;
if(_byClear == 1)//需要清除该行
{
LCD_DisplayStringLine(Line3, (unsigned char *)" ");
}
_wColumn = 319 - (_wColumn - 1) *16;
_byLine = (_byLine - 1) * 24;
for(i = 0;i < 20;i++)
{
if(*_pbyString == 0)
{
break;
}
LCD_DisplayChar(_byLine, _wColumn, *_pbyString);
_wColumn -= 16;
_pbyString ++;
}
}
2. 页面切换的实现函数
运用了状态机的思想。
定义页面编号:
//定义页面编号枚举
typedef enum
{
PAGE1,
PAGE2,
PAGE3,
PAGE_NUM,
}emPAGEx;
定义页面状态机函数:
//菜单显示
void Menu_Display(void)
{
switch(g_emPage)
{
case PAGE1: //页面1
Page1_Display();
break;
case PAGE2: //页面2
Page2_Display();
break;
case PAGE3: //页面3
Page3_Display();
break;
default:
break;
}
}
该函数在主函数中调用,只需要改变g_emPage页面编号的值就能自动实现页面切换。
3. 页面切换结合按键使用
本章分为两种方法,根据按键驱动方式的不同而不同:
1)使用返回键值的方式,需要在页面显示函数中增加按键的识别操作;
2)使用按键回调函数的,在回到函数中增加按键的应用。
第一种方法实例,传键值给显示函数并调用case,break中的内容:
//菜单显示
//页面1显示
void Page1_Display(emBUTTONx _emButton)
{
uint8_t _bytemp[20] = {0};
static uint8_t _M = 2; //0 高频 1 低频
static uint16_t _P = 32;
static float _V = 68.3;
if(s_byRefresh == 1)
{
Page_Line_Display(0,2,9,(unsigned char*)"DATA");
}
memset(_bytemp,20,0);
if(_M != g_stPage1Par.byMode || s_byRefresh == 1)
{
if(g_stPage1Par.byMode == 0)
{
sprintf((char*)_bytemp,"M=%s","H");
}
else
{
sprintf((char*)_bytemp,"M=%s","L");
}
Page_Line_Display(0,4,6,_bytemp);
}
if(_P != g_stPage1Par.wP_Value || s_byRefresh == 1)
{
memset(_bytemp,20,0);
sprintf((char*)_bytemp,"P=%-2d",g_stPage1Par.wP_Value);
Page_Line_Display(0,5,6,_bytemp);
}
if(_V != g_stPage1Par.fV_Value || s_byRefresh == 1)
{
memset(_bytemp,20,0);
sprintf((char*)_bytemp,"V=%-4.1f",g_stPage1Par.fV_Value);
Page_Line_Display(0,6,6,_bytemp);
}
//LED控制
if(g_stPage1Par.byLock == 1)//锁定
{
LEDx_Contr(LED3,LED_ON);
}
else
{
LEDx_Contr(LED3,LED_OFF);
}
if(g_byPage1B2_Time > 0)
{
if(g_byPage1B2_Time % 5 == 0)
{
LEDx_Toggled(LED2);
}
}
else
{
LEDx_Contr(LED2,LED_OFF);
}
//参数备份
_M = g_stPage1Par.byMode;
_P = g_stPage1Par.wP_Value;
_V = g_stPage1Par.fV_Value;
s_byRefresh = 0;
}
//菜单显示
void Menu_Display(void)
{
static emPAGEx s_emPage = PAGE_NUM;
if(s_emPage != g_emPage)
{
LCD_Clear(Black);
s_byRefresh = 1;
}
s_emPage = g_emPage;
switch(g_emPage)
{
case PAGE1: //页面1
LEDx_Contr(LED1,LED_ON);
Page1_Display();
break;
case PAGE2: //页面2
LEDx_Contr(LED1,LED_OFF);
Page2_Display();
break;
case PAGE3: //页面3
Page3_Display();
break;
default:
break;
}
}
//BUTTON1 回调函数
void Button1_CallBack(emBUTTON_EVENT _event)
{
switch(_event)
{
case BUTTON_CLICK://单击
g_emPage ++;
if(g_emPage == PAGE_NUM)
{
g_emPage = PAGE1;
}
if(g_emPage == PAGE2)
{//进入参数界面,设置临时参数变量
g_stPage2Par.wR_Value_Temp = g_stPage2Par.wR_Value;
g_stPage2Par.wK_Value_Temp = g_stPage2Par.wK_Value;
}
else if(g_emPage == PAGE3)
{//退出参数界面 设置参数生效
g_stPage2Par.wR_Value = g_stPage2Par.wR_Value_Temp;
g_stPage2Par.wK_Value = g_stPage2Par.wK_Value_Temp;
}
break;
case BUTTON_DOUBLE_CLICK://双击
break;
case BUTTON_LONG_CLICK://长按
break ;
default:
break;
}
}
//菜单初始化
void Menu__Init(void)
{
LCD_Clear(Black);
LCD_SetBackColor(Black);
LCD_SetTextColor(White);
//按键回调函数注册
Button_Init(BUTTON1,Button1_CallBack);
Button_Init(BUTTON2,Button2_CallBack);
Button_Init(BUTTON3,Button3_CallBack);
Button_Init(BUTTON4,Button4_CallBack);
}
上一篇:04 按键驱动编写
下一篇:06 ADC与I2C应用