文章目录
前言
本工程案例为记录开发过程以及一些问题的解决
一、工程介绍:
本工程是在KELI环境中,直接引用官方的函数库进行开发。引脚的定义以及引用为本人通过多次测试后进行使用。
二、开发步骤
1.引入库(打开GPIO官方例子进行了修改)
2.GPIO引脚初始化
- 宏定义:
#define CON_PORT GpioPortB
#define DAT_PORT GpioPortA
#define CS1 GpioPin0
#define CS2 GpioPin1
#define RS GpioPin3
#define RW GpioPin4
#define EN GpioPin5
#define LIGHT GpioPin6
#define Max_Column 128
#define Max_Line 8
#define OUT 0
#define IN 1
- 控制脚:
static void App_LcdConInit(void)
{
stc_gpio_cfg_t stcGpioCfg;
Sysctrl_SetPeripheralGate(SysctrlPeripheralGpio, TRUE); //打开GPIO外设时钟
stcGpioCfg.enDir = GpioDirOut; //端口方向配置->输出
stcGpioCfg.enDrv = GpioDrvH; //驱动能力配置->高驱动能力
stcGpioCfg.enPu = GpioPuDisable; //端口上下拉配置->无
stcGpioCfg.enPd = GpioPdDisable;
stcGpioCfg.enOD = GpioOdDisable; //开漏输出配置->开漏输出关闭
stcGpioCfg.enCtrlMode = GpioAHB; //总线控制模式配置->AHB
Gpio_Init(CON_PORT, GpioPin0, &stcGpioCfg); //< LCD控制引脚初始化
Gpio_Init(CON_PORT, GpioPin1, &stcGpioCfg);
Gpio_Init(CON_PORT, GpioPin3, &stcGpioCfg);
Gpio_Init(CON_PORT, GpioPin4, &stcGpioCfg);
Gpio_Init(CON_PORT, GpioPin5, &stcGpioCfg);
Gpio_Init(CON_PORT, GpioPin6, &stcGpioCfg);
Gpio_SetClrPort(CON_PORT,0x0000007a); //< LCD控制引脚关闭
}
- 数据脚:
static void App_LcdDataInit(void)
{
stc_gpio_cfg_t stcGpioCfg;
Sysctrl_SetPeripheralGate(SysctrlPeripheralGpio, TRUE); //打开GPIO外设时钟
stcGpioCfg.enDir = GpioDirOut; //端口方向配置->输出
stcGpioCfg.enDrv = GpioDrvH; //驱动能力配置->低驱动能力
stcGpioCfg.enPu = GpioPdDisable; //端口上下拉配置->无
stcGpioCfg.enPd = GpioPdDisable;
stcGpioCfg.enOD = GpioPdDisable; //开漏输出配置->开漏输出关闭
stcGpioCfg.enCtrlMode = GpioAHB; //总线控制模式配置->AHB
Gpio_Init(DAT_PORT, GpioPin0, &stcGpioCfg); // LCD数据引脚初始化
Gpio_Init(DAT_PORT, GpioPin1, &stcGpioCfg);
Gpio_Init(DAT_PORT, GpioPin2, &stcGpioCfg);
Gpio_Init(DAT_PORT, GpioPin3, &stcGpioCfg);
Gpio_Init(DAT_PORT, GpioPin4, &stcGpioCfg);
Gpio_Init(DAT_PORT, GpioPin5, &stcGpioCfg);
Gpio_Init(DAT_PORT, GpioPin6, &stcGpioCfg);
Gpio_Init(DAT_PORT, GpioPin7, &stcGpioCfg);
Gpio_SetClrPort(DAT_PORT,0x000000ff); //< LCD数据引脚关闭
}
3.主时钟初始化以及延时函数封装
- 由于官方使用滴答时钟最小延时为10US,而屏幕的驱动需要的延时很低,故封装了一个简单的延时函数供工程使用:
/**
* \brief delay1us
* delay approximately 1us.
* \param [in] u32Cnt
* \retval void
*/
void delay_1us(uint32_t u32Cnt)
{
do
{
__NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP();
__NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP();
__NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP();__NOP();__NOP();
}
while(--u32Cnt);
}
4.驱动LCD函数(该方案比较稳当,但是驱动速度还可以提高,希望大神们多多指教):
void sendData(u8 dataPort,u8 data) //引用到该函数是为发送数据,可以使用其他函数替代。【如"Gpio_SetPort"或者“Gpio_SetClrPort”】
{
if((data & 0x01) == 0x01)
Gpio_SetIO(dataPort,GpioPin0);
else
Gpio_ClrIO(dataPort,GpioPin0);
if((data & 0x02) == 0x02)
Gpio_SetIO(dataPort,GpioPin1);
else
Gpio_ClrIO(dataPort,GpioPin1);
if((data & 0x04) == 0x04)
Gpio_SetIO(dataPort,GpioPin2);
else
Gpio_ClrIO(dataPort,GpioPin2);
if((data & 0x08) == 0x08)
Gpio_SetIO(dataPort,GpioPin3);
else
Gpio_ClrIO(dataPort,GpioPin3);
if((data & 0x10) == 0x10)
Gpio_SetIO(dataPort,GpioPin4);
else
Gpio_ClrIO(dataPort,GpioPin4);
if((data & 0x20) == 0x20)
Gpio_SetIO(dataPort,GpioPin5);
else
Gpio_ClrIO(dataPort,GpioPin5);
if((data & 0x40) == 0x40)
Gpio_SetIO(dataPort,GpioPin6);
else
Gpio_ClrIO(dataPort,GpioPin6);
if((data & 0x80) == 0x80)
Gpio_SetIO(dataPort,GpioPin7);
else
Gpio_ClrIO(dataPort,GpioPin7);
}
void dataPortReInit(u8 mode)//刚调试程序时,没有加入这一段函数,驱动屏幕有输出不拉低现象,使能LCD后数据BUSY依旧保持高电平
{
stc_gpio_cfg_t stcGpioCfg;
Sysctrl_SetPeripheralGate(SysctrlPeripheralGpio, TRUE); //打开GPIO外设时钟
if(mode == IN)//输入
{
stcGpioCfg.enDir = GpioDirIn; //端口方向配置->输出
stcGpioCfg.enDrv = GpioDrvH; //驱动能力配置->低驱动能力
stcGpioCfg.enPu = GpioPdDisable; //端口上下拉配置->无
stcGpioCfg.enPd = GpioPdDisable;
stcGpioCfg.enOD = GpioPdDisable; //开漏输出配置->开漏输出关闭
stcGpioCfg.enCtrlMode = GpioAHB; //总线控制模式配置->AHB
Gpio_Init(DAT_PORT, GpioPin0, &stcGpioCfg); // LCD数据引脚初始化
Gpio_Init(DAT_PORT, GpioPin1, &stcGpioCfg);
Gpio_Init(DAT_PORT, GpioPin2, &stcGpioCfg);
Gpio_Init(DAT_PORT, GpioPin3, &stcGpioCfg);
Gpio_Init(DAT_PORT, GpioPin4, &stcGpioCfg);
Gpio_Init(DAT_PORT, GpioPin5, &stcGpioCfg);
Gpio_Init(DAT_PORT, GpioPin6, &stcGpioCfg);
Gpio_Init(DAT_PORT, GpioPin7, &stcGpioCfg);
}
else //输出
{
stcGpioCfg.enDir = GpioDirOut; //端口方向配置->输出
stcGpioCfg.enDrv = GpioDrvH; //驱动能力配置->低驱动能力
stcGpioCfg.enPu = GpioPdDisable; //端口上下拉配置->无
stcGpioCfg.enPd = GpioPdDisable;
stcGpioCfg.enOD = GpioPdDisable; //开漏输出配置->开漏输出关闭
stcGpioCfg.enCtrlMode = GpioAHB; //总线控制模式配置->AHB
Gpio_Init(DAT_PORT, GpioPin0, &stcGpioCfg); // LCD数据引脚初始化
Gpio_Init(DAT_PORT, GpioPin1, &stcGpioCfg);
Gpio_Init(DAT_PORT, GpioPin2, &stcGpioCfg);
Gpio_Init(DAT_PORT, GpioPin3, &stcGpioCfg);
Gpio_Init(DAT_PORT, GpioPin4, &stcGpioCfg);
Gpio_Init(DAT_PORT, GpioPin5, &stcGpioCfg);
Gpio_Init(DAT_PORT, GpioPin6, &stcGpioCfg);
Gpio_Init(DAT_PORT, GpioPin7, &stcGpioCfg);
}
}
void CheckBusy(void)
{
u8 status;
Gpio_ClrIO(CON_PORT,RS);
delay_1us(1);
Gpio_SetIO(CON_PORT,RW);
delay_1us(1);
sendData(DAT_PORT,0x00FF);
delay_1us(1);
dataPortReInit(IN);
do
{
Gpio_SetIO(CON_PORT,EN); //开使能
delay_1us(1);
status = Gpio_GetInputIO(DAT_PORT,GpioPin7);//接收BF位,判断是否忙
} while(status & 0X01);
Gpio_ClrIO(CON_PORT,EN);
dataPortReInit(OUT);
}
static void writecom(u8 l_or_r,u8 com) //写指令 0是左边 1是右边 2是两边
{
if(l_or_r >= 2)
{
CheckBusy();
delay_1us(1);
Gpio_SetIO(CON_PORT,CS1); //选中左片选
delay_1us(1);
Gpio_ClrIO(CON_PORT,CS2);
delay_1us(1);
Gpio_ClrIO(CON_PORT,RS); //写指令
delay_1us(1);
Gpio_ClrIO(CON_PORT,RW); //读
delay_1us(1);
sendData(DAT_PORT,com);
delay_1us(1);
Gpio_SetIO(CON_PORT,EN); //开使能
delay_1us(10); //10us应该能发送完了
Gpio_ClrIO(CON_PORT,EN);
delay_1us(10);
CheckBusy();
delay_1us(1);
Gpio_ClrIO(CON_PORT,CS1); //选中左片选
delay_1us(1);
Gpio_SetIO(CON_PORT,CS2);
delay_1us(1);
Gpio_ClrIO(CON_PORT,RS); //写指令
delay_1us(1);
Gpio_ClrIO(CON_PORT,RW); //读
delay_1us(1);
sendData(DAT_PORT,com);
delay_1us(1);
Gpio_SetIO(CON_PORT,EN); //开使能
delay_1us(10);
Gpio_ClrIO(CON_PORT,EN);
}
else
{
if(l_or_r == 1)
{
CheckBusy();
delay_1us(1);
Gpio_ClrIO(CON_PORT,CS1); //选中左片选
delay_1us(1);
Gpio_SetIO(CON_PORT,CS2);
delay_1us(1);
Gpio_ClrIO(CON_PORT,RS); //写指令
delay_1us(1);
Gpio_ClrIO(CON_PORT,RW); //读
delay_1us(1);
sendData(DAT_PORT,com);
delay_1us(1);
Gpio_SetIO(CON_PORT,EN); //开使能
delay_1us(10);
Gpio_ClrIO(CON_PORT,EN);
}
else
{
CheckBusy();
delay_1us(1);
Gpio_SetIO(CON_PORT,CS1); //选中左片选
delay_1us(1);
Gpio_ClrIO(CON_PORT,CS2);
delay_1us(1);
Gpio_ClrIO(CON_PORT,RS); //写指令
delay_1us(1);
Gpio_ClrIO(CON_PORT,RW); //读
delay_1us(1);
sendData(DAT_PORT,com);
delay_1us(1);
Gpio_SetIO(CON_PORT,EN); //开使能
delay_1us(10);
Gpio_ClrIO(CON_PORT,EN);
}
}
}
static void writedat(u8 l_or_r,u8 dat) //写数据
{
if(l_or_r >= 2)
{
CheckBusy();
delay_1us(1);
Gpio_SetIO(CON_PORT,CS1); //选中左片选
delay_1us(1);
Gpio_ClrIO(CON_PORT,CS2);
delay_1us(1);
Gpio_SetIO(CON_PORT,RS); //写指令
delay_1us(1);
Gpio_ClrIO(CON_PORT,RW); //读
delay_1us(1);
sendData(DAT_PORT,dat);
delay_1us(1);
Gpio_SetIO(CON_PORT,EN); //开使能
delay_1us(10);
Gpio_ClrIO(CON_PORT,EN);
delay_1us(10);
CheckBusy();
delay_1us(1);
Gpio_ClrIO(CON_PORT,CS1); //选中左片选
delay_1us(1);
Gpio_SetIO(CON_PORT,CS2);
delay_1us(1);
Gpio_SetIO(CON_PORT,RS); //写指令
delay_1us(1);
Gpio_ClrIO(CON_PORT,RW); //读
delay_1us(1);
sendData(DAT_PORT,dat);
delay_1us(1);
Gpio_SetIO(CON_PORT,EN); //开使能
delay_1us(10);
Gpio_ClrIO(CON_PORT,EN);
}
else
{
if(l_or_r == 1)
{
CheckBusy();
delay_1us(1);
Gpio_ClrIO(CON_PORT,CS1); //选中左片选
delay_1us(1);
Gpio_SetIO(CON_PORT,CS2);
delay_1us(1);
Gpio_SetIO(CON_PORT,RS); //写指令
delay_1us(1);
Gpio_ClrIO(CON_PORT,RW); //读
delay_1us(1);
sendData(DAT_PORT,dat);
delay_1us(1);
Gpio_SetIO(CON_PORT,EN); //开使能
delay_1us(10);
Gpio_ClrIO(CON_PORT,EN);
}
else
{
CheckBusy();
delay_1us(1);
Gpio_SetIO(CON_PORT,CS1); //选中左片选
delay_1us(1);
Gpio_ClrIO(CON_PORT,CS2);
delay_1us(1);
Gpio_SetIO(CON_PORT,RS); //写指令
delay_1us(1);
Gpio_ClrIO(CON_PORT,RW); //读
delay_1us(1);
sendData(DAT_PORT,dat);
delay_1us(1);
Gpio_SetIO(CON_PORT,EN); //开使能
delay_1us(10);
Gpio_ClrIO(CON_PORT,EN);
}
}
}
5.驱动LCD函数:
void clear_lcd(void)
{
u8 x,y;
for(x=0xb8; x<=0xbf; x++)
{
writecom(0,x);
for(y=0x40; y<=0x80; y++)
{
writecom(0,y);
writedat(0,0X00); //0X00
}
}
for(x=0xb8; x<=0xbf; x++)
{
writecom(1,x);
for(y=0x40; y<=0x80; y++)
{
writecom(1,y);
writedat(1,0X00);
}
}
}
void lcd12864_init(void)
{
writecom(2,0x3e);//全屏关
writecom(2,0xc0);//从c0行开始
writecom(2,0x3f);//全屏开
writecom(2,0xb8);//从第一页开始显示
writecom(2,0x40);//从第一列开始显示
writecom(2,0xc0);//从c0行开始
}
//x:0~127
//y:0~7
void LCD_ShowF6x8Char(u8 x, u8 y, u8 chr)
{
u8 c=0,i=0;
u8 xCoo;
u8 xReal=0;
c=chr-' ';//得到偏移后的值
if(((x+6)>Max_Column) || (y>Max_Line))
{
}
else
{
for(i=0; i<6; i++)
{
if(x < 64)
{
xCoo = 0;
xReal = x;
}
else
{
xCoo = 1;
xReal = x - 64;
}
writecom(xCoo,(y+0xb8));
writecom(xCoo,(xReal+0x40));
writedat(xCoo,F6x8[c][i]);
x++;
}
}
}
//x:0~127
//y:0~7
void LCD_ShowContinueEngString(u8 x,u8 y,u8 *chr)
{
u8 j=0;
u8 i=0;
u8 chrNum=0;
chrNum = strlen(chr);
while(chr[j] != '\0')
// for(i=0; i<chrNum; i++)
{
LCD_ShowF6x8Char(x,y,chr[j]);
x += 6;
j++;
if(x >= Max_Column)
{
break;
}
}
}
- 主函数:
const u8 messageBuf[5]= {"12345"};
******************************************************************************/
int32_t main(void)
{
App_LedInit(); ///< LED端口初始化
delay1ms(20);
App_PortCfg(); //按键端口配置
delay1ms(10);
App_ClkInit(); //时钟初始化配置
delay1ms(10);
App_Xth32MHzToPll48MHz(); //设置系统时钟为48MHZ
delay1ms(10);
App_LcdInit(); //LED控制引脚初始化
delay1ms(50);
App_LcdDataInit();//LED数据引脚初始化
delay1ms(50);
lcd12864_init(); 屏幕引脚初始化
while(1)
{
Gpio_SetIO(CON_PORT,LIGHT);
clear_lcd();
delay1ms(500);
LCD_ShowContinueEngString(96,0,&messageBuf);
}
}
总结
以上为这次工程的主要内容,一些其他的细节就不一一细讲了。本次分享为LCD的驱动以及简单的显示函数介绍,所以在以上基础上需要增加字库和一些基本的函数方可进行运行。