加我模型论坛

 找回密码
 注册
搜索
热搜: 活动 交友 discuz
查看: 5583|回复: 3
打印 上一主题 下一主题

我的第五个单片机开源项目——PIC单片机LCD1602液晶屏显示程序

[复制链接]
跳转到指定楼层
1#
发表于 2012-7-10 12:51:51 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 joyrus 于 2012-7-11 20:31 编辑

项目设计目的:通过PIC单片机的PORTD和PORTB端口配合控制LCD1602的数据显示。

本项目使用C语言编写使用PICC(Hitech C)

通过事先的学习与研究,了解了各种实验板、DEMO板、开发板提供的LCDemo演示程序。包括C8051系列提供的LCDemo程序发现几乎所有的程序都是雷同的,使用起来也并不是很方便。所以我以这些Demo程序作为蓝本,写了一套自己的LCD控制函数,相比较那些Demo程序,大量减少了函数的数量(主要使用的只有4个),使用起来简单了很多。并利用宏定义来衍生出需要的各种控制指令,用户也可以利用这4个函数自行预定义各种需要的LCD控制指令,灵活应用这套简单的LCD1602液晶屏控制函数。

更重要的是在这个项目中我遇到了一些棘手的问题,最终攻克了这些难关,待我稍后整理后和大家分享。

程序已经完成有需要的朋友可以直接使用这些函数,请往下看!
2#
 楼主| 发表于 2012-7-11 19:33:30 | 只看该作者
本帖最后由 joyrus 于 2012-7-11 19:57 编辑

在各位查看程序之前,我先说说本次程序编写中遇到的一些意外情况,以避免没有经验的朋友重蹈覆辙!

由于也是缺乏经验,所以还是以惯常的方式先写一些尝试性的代码来测试自己使用的方法是否可行。本来这种习惯还是可以给我带来不少的便利,一般都会很快通过测试,然后进入正式的功能模块设计。但是这次情况却截然不同!

由于事先也了解了LCD是一个速度很慢的外设,当然是针对单片机的处理速度来讲的。所以根据LCD的技术资料一开始就在需要延时的地方在程序中加入了远大于要求的延时,但是无论如何都无法使LCD有正确的显示。最终我放弃了自己的测试代码,而选择使用Demo提供的代码来确定LCD是否工作正常,很显然Demo代码证明LCD完全正常!根据Demo的思路我重新进行了代码的编写,很遗憾我自己的代码如何都通不过测试。问题很明显在我自己的身上,带着遗憾的心情我再一次用Demo程序来测试,哈哈!我的运气真好,Demo竟然也罢工了!LCD出现了和我自己代码一样的问题!

这给了我灵感!原来问题很简单LCD是一个很慢的设备,而且也是一个对时序要求很严格的外设!!!时序!!!虽然PICKit3可以全速调试程序,但却无法时LCD正常工作!没有别的办法,只能通过直接烧写程序看效果。也就是因为我在PICKit3调试模式下运行Demo所以也失败了!有了这个教训我自己的代码很快就完成了,但是在Demo的代码中有一段让我匪夷所思。

Demo代码在LCD的初始化过程中连续使用了4次初始化指令,而且前三次是不查询LCD忙标志的!当时我很疑惑,由于有了之前的教训我就暂时沿用了Demo的这个疑惑。之后我在一个C8051的Demo程序中看到了几乎完全相同的代码,包括了这个疑云!使我感到高兴的是这段代码含有一个简单的注释,// Set display mode 3 times, no detect Busy,这很明显告诉我初始化设置3次,不检测BF忙标志。详细的代码请看这里c8051的LCD1602液晶驱动程序范例很有参考价值。

为了验证这确实不是废代码,我将这3次初始化屏蔽掉了。戏剧化的一幕出现了,程序运行一切正常,预设的字符在向我眨着眼,有点嘲笑我的意思,哈哈哈~~~~慢着!既然在所有,毫不夸张的说,Demo中都是4次初始化LCD指令,那么我为什么要反其道而行呢!何况初始化一般在程序中只出现一次,无伤大雅。很有可能在一个完整的系统程序中这样做是必须的。所以我还是保留了这些代码。
3#
 楼主| 发表于 2012-7-11 20:00:46 | 只看该作者
本帖最后由 joyrus 于 2012-7-11 20:10 编辑

下面就是主程序和函数库了。

主程序如下:

/******************************************************
* 本函数库为LCD1602驱动函数
* 本函数库在HITECH C环境下可以直接使用
* 如果在其他C编译器环境下使用需要根据具体的编译器做调整
* 本人联系方法:沈毅;joyrus@163.netwww.joyrus.com,QQ:15953321
*
*******************************************************/
//#include <pic.h>
#include "MYLCD1602.h"

typedef unsigned char uchar;
typedef unsigned int uint;

volatile uchar my1string[]="    I am Ray!   ";
volatile uchar my2string[]=" Hi, I am Tony! ";

void main(viod)
{
TRISB=0;   //设LCD控制端口线为输出
     
init_lcd(); //初始化LCD
set_lcd_off; //LCD显示关闭

put_lcd_string(0, 0, my1string);   //LCD写入字符串
put_lcd_string(0, 1, my2string);

for(uint i=50000; i>0; i--);

while(1)
{
  set_lcd_on;   //LCD显示开
  for(uint i=50000; i>0; i--);
  set_lcd_off;
  for(uint i=50000; i>0; i--);
}
//end while(1)
}//end main
4#
 楼主| 发表于 2012-7-11 20:02:32 | 只看该作者
本帖最后由 joyrus 于 2012-7-11 20:56 编辑

函数库如下:

/******************************************************
* 本函数库为LCD1602驱动函数
* 本函数库在HITECH C环境下可以直接使用
* 如果在其他C编译器环境下使用需要根据具体的编译器做调整
* 本人联系方法:沈毅;joyrus@163.netwww.joyrus.com,QQ:15953321
*
*******************************************************/
#include <pic.h>
#include <htc.h>

#ifndef _XTAL_FREQ
//本函数库使用了HITECH C提供的标准函数__delay_us/ms需要设定下面的晶振选项
// Unless specified elsewhere, 4MHz system frequency is assumed
#define _XTAL_FREQ 4000000
#endif

#define LCDCON_EN RB5     //PORTB5为LCD使能控制
#define LCDCON_RW RB4     //PORTB4为LCD读写控制
#define LCDCON_RS RB3     //PORTB3为LCD指令/数据选择控制
#define LCDPORT PORTD     //PORTD为LCD的8位总线LCDPORT
#define LCDPORT_READ TRISD=0xFF //TRISD为LCD的8位总线方向控制LCDTRIS
#define LCDPORT_WRITE TRISD=0x00
#define LCDCONPORT_TRIS TRISB  //TRISB为LCD的3位控制线方向控制LCDCONTRIS
//#define LCDP0 RD0
//#define LCDP1 RD1
//#define LCDP2 RD2
//#define LCDP3 RD3
//#define LCDP4 RD4
//#define LCDP5 RD5
//#define LCDP6 RD6
//#define LCDP7 RD7

#define WBF 1         //定义符号1
#define LCD_READ 1
#define LCD_DATA 1

#define NBF 0         //定义符号0
#define LCD_WRITE 0
#define LCD_COMD 0
   
//本函数库使用了HITECH C提供的标准函数__delay_us/ms
//也可以使用下面自定义的延时函数
void us_delay(unsigned char time);  //微秒延时
void ms_delay(unsigned char time);  //毫秒延时

//读LCD数据,返回BF标志,和内存指针
unsigned char get_lcd(void);   
         
//LCD初始化
void init_lcd(void);            

//发送数据d/地址a/指令c到LCD
//DorC选择数据或者指令
//BF是否检测LCD忙
//不检测LCD忙仅在初始化LCD时使用!
//本函数一次写入一个字符!
void put_lcd(unsigned char ADC, unsigned char DorC, unsigned char BF);  

//向LCD发送字符串,真正要显示的数据
//x列,y行字符坐标,*strpoint字符串数组地址
//本函数一次写入一串字符!
void put_lcd_string(unsigned char x, unsigned char y, unsigned char *strpoint);

#define delay_8us us_delay(1)            //延时8us
#define delay_60us us_delay(8)           //延时8us
#define delay_2ms ms_delay(2)            //延时2ms
#define lcd_busy (get_lcd() & 0x80)      //读并传递LCD忙BF标志

//利用宏和本函数库定义需要的各种LCD指令
#define set_lcd_init (put_lcd(0x38, LCD_COMD, WBF))         //lCD外部使用初始化
#define set_lcd_off (put_lcd(0x08, LCD_COMD, WBF))          //LCD显示关闭
#define set_lcd_clear (put_lcd(0x01, LCD_COMD, WBF))        //LCD清屏
#define set_lcd_cursor (put_lcd(0x06, LCD_COMD, WBF))       //LCD光标显示设定
#define set_lcd_on (put_lcd(0x0C, LCD_COMD, WBF))           //LCD显示打开
#define set_lcd_position (put_lcd(0x80, LCD_COMD, WBF))     //LCD首字符定位
//#define lcd_init ((put_lcd(0x38, LCD_COMD, NBF))          //LCD初始化函数内部使用
//#define set_lcd_curmove                                   //LCD光标画面位移方式设定     

///////////////函数///////////////////
void put_lcd_string(unsigned char x, unsigned char y, unsigned char *strpoint)
{
unsigned char position;
if(y)
  position=0xC0+x;    //第二行
else
  position=0x80+x;    //第一行
put_lcd(position, LCD_COMD, WBF);     //写入首字符定位指令
//逐个写字符串
while (*strpoint)
{
  put_lcd(*strpoint, LCD_DATA, WBF);
  strpoint++;                       //指向字符串中的下一字符
}
}
unsigned char get_lcd(void)
{
LCDCON_RS=LCD_COMD;
LCDCON_RW=LCD_READ;
LCDCON_EN=LCD_WRITE;
__delay_us(8);            //产生写到读的脉冲
//delay_8us;
LCDCON_EN=LCD_READ;
LCDPORT_READ;
return LCDPORT;
}
void put_lcd(unsigned char ADC, unsigned char DorC, unsigned char BF)
{
if(BF){while(lcd_busy);}   //等待LCD空闲
LCDPORT_WRITE;
LCDPORT=ADC;
LCDCON_RS=DorC;
LCDCON_RW=LCD_WRITE;
LCDCON_EN=LCD_READ;        //产生从读到写的脉冲
__delay_us(60);
//delay_60us;
LCDCON_EN=LCD_WRITE;
}
void init_lcd(void)
{
LCDPORT_WRITE;
LCDPORT=0x00;
//lcd_init;
put_lcd(0x38, LCD_COMD, NBF);   //初始化指令3次,不检测BF忙标志
__delay_ms(2);
//delay_2ms;
//lcd_init;
put_lcd(0x38, LCD_COMD, NBF);
__delay_ms(2);
//delay_2ms;
//lcd_init;
put_lcd(0x38, LCD_COMD, NBF);
__delay_ms(2);
//delay_2ms;
set_lcd_init;
set_lcd_off;
set_lcd_clear;
set_lcd_cursor;
set_lcd_on;
set_lcd_position;
}
void us_delay(unsigned char time)
{
while(time)
{
  time--;
}
}
void ms_delay(unsigned char time)
{
unsigned char i=100;
unsigned char j=200;
while(time)
{
  time--;
  while(i)
  {
   i--;
   while(j){j--;}   
  }
}
}
您需要登录后才可以回帖 登录 | 注册

本版积分规则

小黑屋|手机版|Archiver|JOYRUS.com

GMT+8, 2024-12-22 20:26 , Processed in 0.078728 second(s), 16 queries .

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表