打印
[匠人手记]

《两轮小车DIY》之《LCD模块》(动态更新)

[复制链接]
8296|46
手机看帖
扫描二维码
随时随地手机跟帖
沙发
程序匠人|  楼主 | 2009-3-16 22:59 | 只看该作者

LCD显示终于调通了,上图。欧耶!

使用特权

评论回复
板凳
程序匠人|  楼主 | 2009-3-16 23:00 | 只看该作者

特写镜头

使用特权

评论回复
地板
hotpower| | 2009-3-16 23:01 | 只看该作者

哈哈~~~搞个汉字屏多欧耶~~~雷翻了~~~

使用特权

评论回复
5
程序匠人|  楼主 | 2009-3-16 23:05 | 只看该作者

头文件 LCD1602.h

//--------------------------------------------------------
// 项目:通用
// 模块:LCD1602显示驱动
// 说明:
// 设计:程序匠人(版权所有,引用者请保留原作者姓名)
//--------------------------------------------------------
/*
版本说明:
LCD1602.h   2009-3-9 23:18:18


*/
//--------------------------------------------------------


//--------------------------------------------------------
//LCD 端口定义
//--------------------------------------------------------
//V0为液晶显示器对比度调整端.接正电源时对比度最弱,接地电源时对比度最高.
//对比度过高时会产生“鬼影”,使用时可以通过一个10K的电位器调整对比度

#define LCD_PORT GPIOB        //LCD控制口

#define LCD_E GPIO_Pin_5      //使能信号(下降沿有效)
#define LCD_RW  GPIO_Pin_6    //读写信号(1=读,0=写)
#define LCD_RS  GPIO_Pin_7    //寄存器选择信号(1=数据寄存器,0=指令寄存器)
                              //当RS=0,RW=0时,写入指令或者显示地址
                              //当RS=0,RW=1时,读忙信号
                              //当RS=1,RW=0时,写入数据

#define LCD_DB0 GPIO_Pin_8    //8位双向数据线
#define LCD_DB1 GPIO_Pin_9    //8位双向数据线
#define LCD_DB2 GPIO_Pin_10   //8位双向数据线
#define LCD_DB3 GPIO_Pin_11   //8位双向数据线
#define LCD_DB4 GPIO_Pin_12   //8位双向数据线
#define LCD_DB5 GPIO_Pin_13   //8位双向数据线
#define LCD_DB6 GPIO_Pin_14   //8位双向数据线
#define LCD_DB7 GPIO_Pin_15   //8位双向数据线

#define LCD_BUSY LCD_DB7      //忙标志位(1=忙,0=不忙)

//--------------------------------------------------------
//LCD 控制定义
//--------------------------------------------------------
#define LCD_E_L GPIO_ResetBits(LCD_PORT, LCD_E);  //使能信号=0
#define LCD_E_H GPIO_SetBits(LCD_PORT, LCD_E);    //使能信号=1

#define LCD_RW_L GPIO_ResetBits(LCD_PORT, LCD_RW);//读写信号=0
#define LCD_RW_H GPIO_SetBits(LCD_PORT, LCD_RW);  //读写信号=1

#define LCD_RS_L GPIO_ResetBits(LCD_PORT, LCD_RS);//寄存器选择信号=0
#define LCD_RS_H GPIO_SetBits(LCD_PORT, LCD_RS);  //寄存器选择信号=1

//--------------------------------------------------------
//函数声明
//--------------------------------------------------------
//内部函数,不建议外部调用
extern void GPIO_Config_LCD_OUT(void) ;           //LCD IO口 配置 (控制口=输出,数据口=输出)
extern void GPIO_Config_LCD_IN(void) ;            //LCD IO口 配置 (控制口=输出,数据口=输入)

extern void CheckBusy_LCD(void) ;                 //检测LCD忙信号函数
extern void WriteCommand_LCD(u8 wclcd,u8 busyc) ; //向LCD发送指令函数
extern void WriteData_LCD(u8 wdlcd) ;             //向LCD发送数据函数

//外部函数,允许外部调用
extern void Init_LCD(void) ;                        //LCD初始化函数
extern void DisplayOneChar_LCD(u8 x,u8 y,u8 ddata); //显示指定坐标的字符函数(入口:x=字符坐标(0~15),y=行坐标(0~1),ddata=待显示字符)
extern void DisplayString_LCD(u8 x,u8 y,u8 *ddata); //显示指定坐标的字符串函数(入口:x=字符坐标(0~15),y=行坐标(0~1),*ddata=待显示字符串的指针)

//--------------------------------------------------------
//
//  THE END
//
//  版权所有:程序匠人(引用者请保留原作者姓名)
//
//--------------------------------------------------------

使用特权

评论回复
6
hotpower| | 2009-3-16 23:05 | 只看该作者

我记得1602不需要延时那么许多时间~~~

使用特权

评论回复
7
程序匠人|  楼主 | 2009-3-16 23:09 | 只看该作者

回hot

根据1602的资料,在初始化时,需要ms级的延时
另外。为了通用性。匠人做了一个独立的延时模块(基于SysTick中断),不但可以供显示模块调用,其他模块也可以调用。

//--------------------------------------------------------
//LCD初始化函数
//--------------------------------------------------------
void Init_LCD(void)
{
  GPIO_Config_LCD_OUT();      //LCD IO口 配置 (控制口=输出,数据口=输出)
  
  SysTick_Delay(15);          //延时15ms
  WriteCommand_LCD(0x38,0);   //三次显示模式设置,不检测忙信号
  SysTick_Delay(5);           //延时5ms
  WriteCommand_LCD(0x38,0);
  SysTick_Delay(5);           //延时5ms
  WriteCommand_LCD(0x38,0);
  SysTick_Delay(5);           //延时5ms

  WriteCommand_LCD(0x38,1);   //设置显示模式(8位数据口,2行显示,5*8DOTS),开始要求每次检测忙信号
  WriteCommand_LCD(0x08,1);   //关显示
  WriteCommand_LCD(0x01,1);   //清屏
  WriteCommand_LCD(0x06,1);   //显示光标右移设置(光标位置自加,显示移动)
  WriteCommand_LCD(0x0c,1);   //开显示,光标不显示,不闪烁
}

使用特权

评论回复
8
hotpower| | 2009-3-16 23:09 | 只看该作者

哈哈~~~H文件书写要“风度”~~~不要忘了“C++”人民

#ifndef __LCD1602_H__
#define __LCD1602_H__
#ifdef __cplusplus
extern "C"
{
#endif


#ifdef __cplusplus
}
#endif
#endif//__LCD1602_H__

使用特权

评论回复
9
香水城| | 2009-3-16 23:15 | 只看该作者

建议匠人,可以把I/O口的驱动速度根据需要设置为10MHz或2MHz

现在你设置为50MHz。

设置为10MHz或2MHz,可以比50MHz时省电且产生的噪声较小。

使用特权

评论回复
10
hotpower| | 2009-3-16 23:18 | 只看该作者

俺同意教主之论点~~~

使用特权

评论回复
11
程序匠人|  楼主 | 2009-3-16 23:20 | 只看该作者

I/O口的驱动速度,容俺在研究研究。。。。

呵呵,现在是抄一块是一块。还顾不上那么多细节啊。。。。。。

不过,还是要谢谢啦。记下了。。。。

使用特权

评论回复
12
computer00| | 2009-3-16 23:22 | 只看该作者

我晕...匠人这么个程序也能发这么多个贴...放一个贴里不就

使用特权

评论回复
13
hotpower| | 2009-3-16 23:23 | 只看该作者

哈哈~~~他想让俺陪他玩~~~

使用特权

评论回复
14
程序匠人|  楼主 | 2009-3-17 22:37 | 只看该作者

1602的“检测LCD忙信号”的改进程序


1602的程序网上随便一搜,就是一箩筐。匠人也看过不下10个,其中也包括圈圈写的。说实话,没有一个能看中的。

就拿这个忙信号来说事吧。大多数人都是死等的方式,即:如果LCD_BUSY=1,等待;否则退出。就像匠人昨天给出的程序一样:

//--------------------------------------------------------
//检测LCD忙信号函数
//说明: 如果LCD_BUSY=1,等待;否则退出
//--------------------------------------------------------
void CheckBusy_LCD(void)
{
  GPIO_Config_LCD_IN();   //LCD IO口 配置 (控制口=输出,数据口=输入)
  while(1)
  {
    LCD_E_L;
    LCD_RS_L;
    LCD_RW_H;
    LCD_E_H;
    if(GPIO_ReadInputDataBit(LCD_PORT,LCD_BUSY)==0) break;  //当LCD_BUSY=0时跳出循环
  }
  LCD_E_L;
}

读忙程序中有一个死等待循环。
那么问题来了。
如果LCD接触不良或者其它故障,一直读不到空闲信号,会导致系统的挂起(或看门狗复位)。

当然,网上也有一些人可能是遇到或意识到了这个问题,他们采取了延时等待的方法。比如,用延时3~5ms的方法代替“读忙”。
这样也是一个权益之计。只不过,每次等待3~5ms(实际上可能用不了那么多时间),似乎也很痴呆。

匠人的改进方法是:读忙+限时,双管齐下兼顾可靠性和效率性。改进后的程序如下:

//--------------------------------------------------------
//检测LCD忙信号函数
//说明: 如果LCD_BUSY=1(忙),等待;如果LCD_BUSY=0(闲)或者等待超时,退出
//--------------------------------------------------------
void CheckBusy_LCD(void)
{
  GPIO_Config_LCD_IN();                                                                       //LCD IO口 配置 (控制口=输出,数据口=输入)
  SysTick_Delay_Start(5);                                                                        //启动5ms延时
  
  while(1)
  {
    LCD_E_L;
    LCD_RS_L;
    LCD_RW_H;
    LCD_E_H;
    if(GPIO_ReadInputDataBit(LCD_PORT,LCD_BUSY)==0) break;  //当LCD_BUSY=0(闲),跳出循环
    if(SysTick_Delay_Check()) break;                                                     //当等待超时,跳出循环
  }
  LCD_E_L;
}


举一反三,在对其它外部器件“读忙操作”时也可以借鉴这个方法。
 

使用特权

评论回复
15
hotpower| | 2009-3-17 22:41 | 只看该作者

感觉应该用do{}while();~~~

使用特权

评论回复
16
程序匠人|  楼主 | 2009-3-17 22:49 | 只看该作者

同意 hotpower

用do{}while();可能更严谨一些。但不知编译出来是否会更有效率。没有试过。

这不是重点。

重点是程序中不能有死循环(或潜在的死循环。)

使用特权

评论回复
17
hotpower| | 2009-3-17 22:54 | 只看该作者

GPIO_ReadInputDataBit有可能死循环~~~

使用特权

评论回复
18
程序匠人|  楼主 | 2009-3-17 23:00 | 只看该作者

GPIO_ReadInputDataBit ? 哪句会陷入死循环?愿闻其详!

/*******************************************************************************
* Function Name  : GPIO_ReadInputDataBit
* Description    : Reads the specified input port pin.
* Input          : - GPIOx: where x can be (A..E) to select the GPIO peripheral.
*                : - GPIO_Pin:  specifies the port bit to read.
*                    This parameter can be GPIO_Pin_x where x can be (0..15).
* Output         : None
* Return         : The input port pin value.
*******************************************************************************/
u8 GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, u16 GPIO_Pin)
{
  u8 bitstatus = 0x00;
  
  /* Check the parameters */
  assert_param(IS_GET_GPIO_PIN(GPIO_Pin)); 
  
  if ((GPIOx->IDR & GPIO_Pin) != (u32)Bit_RESET)
  {
    bitstatus = (u8)Bit_SET;
  }
  else
  {
    bitstatus = (u8)Bit_RESET;
  }
  return bitstatus;
}

使用特权

评论回复
19
hotpower| | 2009-3-17 23:15 | 只看该作者

哈哈~~俺这个没版权~~~

使用特权

评论回复
20
xwj| | 2009-3-18 00:26 | 只看该作者

呵呵,匠人咋还玩1602啊?

现在都是用手机屏啊,又轻又便宜,最主要的是能轻易的显示中文啊

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

734

主题

11156

帖子

678

粉丝