任意2个io直接驱动LCD1602

今天偶然在网上看到有人用2个io口就实现了驱动LCD1602的方法,真是太牛了。很佩服楼主的专研能力,在此也将他的方法给各位介绍下。

实物图:

原理图:



示例程序:

//         Drive a LCD1602 with 2 wire 
//=================================================== 
//ICC-AVR application builder : 2010-10-3 19:30:02 
// Target : M16 
// Crystal: 4.0000Mhz 

#include <iom16v.h> 
#include <macros.h> 

#define Set_E PORTB|=2 
#define Clr_E PORTB&=~2 
#define Set_D PORTB|=1 
#define Clr_D PORTB&=~1 
#define Set_xy(y,x) Send(0,(y<<6)|(x&15)|0x80) 

//=================================================== 
void init_devices(void) 
{ 
  CLI(); //disable all interrupts 
  DDRB  = 0x03; 
  MCUCR = 0x00; 
  GICR  = 0x00; 
  SEI(); //re-enable interrupts 
} 

//=================================================== 
void Delay(unsigned int i) 
{ 
  while(i--); 
} 

//=================================================== 
void Send(unsigned char RS, unsigned char dat) 
{ 
  unsigned char i; 
  for (i = 2; i > 0; i--) 
  { 
    if (dat & 0x80) Set_D; else Clr_D; 
    Delay(10608);//14520us 
    if (RS) Set_E; 
    if (dat & 0x40) Set_D; else Clr_D; 
    Delay(462);  //660us 
    if (dat & 0x20) Set_D; else Clr_D; 
    Delay(18);   //30us 
    Set_E; 
    if (dat & 0x10) Set_D; else Clr_D; 
    _NOP();      //0.5us < t < 1.36us 
    Clr_E; 
    dat <<= 4; 
  } 
} 

//=================================================== 
void init_1602(void) 
{ 
  unsigned char i = 3; 
  Clr_D; 
  Clr_E; 
  Delay(10608); 
  do{ 
    Clr_D; 
    Delay(462); 
    Set_D; 
    Set_E; 
    Delay(18); 
    if (i == 0) Clr_D; 
    _NOP();_NOP();_NOP(); 
    Clr_E; 
    }while(i--); 
  Send(0,0x28); 
  Send(0,0x01); 
  Send(0,0x0f); 
} 

//=================================================== 
void Send_S(unsigned char *p) 
{ 
  while(*p) Send(1,*p++); 
} 

//=================================================== 
void main(void) 
{ 
  unsigned char i; 
  init_devices(); 
  init_1602(); 
   
  Set_xy(0,2); 
  Send_S("Hello world!"); 
  Set_xy(1,3); 
  Send_S("I'm COWBOY."); 
  for (i=0;i<255;i++) Delay(10000); 
   
  Send(0,0x01); 
  Set_xy(0,3); 
  Send_S("Welcome to"); 
  Set_xy(1,1); 
  Send_S("www.ourdev.cn"); 
  while(1); 
}   
原理分析:

以下言论均为引用:

1."靠相差很大的时间常数实现的"

2.上面各位都看出了门道,为了保证数据传输的可靠性,相邻的两bit数据,RC时间常数相差需很大,我这里设定为22倍左右,差距越大,可靠性就越高。事实上,我试了12倍的间隔,仍能正常工作,但考虑到阻容的误差和温漂,以及电磁干扰等因素,选用了22倍间隔。太长的等比间隔,会带来了数据传送速度很慢的问题,如楼主位的RC参数,传送一字节数据约需32ms,正如millwood0所说,连续发送多个字节时,通讯线将会忙不过来,必须等待。为解决此问题,我另外写了个程序,设立发送缓冲区,环状FIFO结构,来暂存待显示的内容,并用定时中断来完成自动发送。IO是省了,却带来几十字节的内存开销,还要占用一个定时器。
    比较实用的方案,正如 zhonghua_li 蓝色天空 所说,多用一个IO,这样每个IO只驱动LCD1602的两个PIN,上面问题就能得到完美解决,包括RC时间常数的选择,也大大放宽了要求,传送速度和普通的驱动方式相当。
    大多数人会有疑问,就那么几个阻容,能可靠工作吗?我也考虑了这问题,在实际的应用中,常常看到数据线上都有小电阻与小电容组成的低通滤波网络来提高数据传输的可靠性。这里的RC用法类似,选用更大的RC,理论上对抵抗外界EMC干扰更有效,设计上只要保证时钟脉冲的下降沿时刻,各数据线的上电平符合LCD1602的要求(VH>4V,VL<1V)。实际测试表现如何?用了简单的方法进行模拟:1.不断发送数据,然后打通手机瞬间,把手机天线紧靠数据线,未发现异常。 2.用工作中的示波器信号线的地线,不断触碰LCD1602相关引脚,也未发现异常。当然这种测试并不规范,所以这种驱动方式玩玩可以,用在产品上还得慎重考虑。

这里是原帖子的链接:http://www.ourdev.cn/bbs/bbs_content_all.jsp?bbs_sn=4301955点击打开链接



评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值