zoukankan      html  css  js  c++  java
  • LCD1602液晶显示模块的单片机驱动深入详解之软件篇(AVR)

    LCD1602液晶显示模块的驱动虽然比七段数码管之类的显示要复杂一些,但实际上也并不是很难,最主要的还是初始化,为什么这么说呢?我们在调试一块新液晶屏的时候,都会先初始化看看有没有光标在闪,没有光标前是一番努力(PROTEUS上也是这么做的),光标出来之后就相对很容易了,因为光标出来了,至少说明硬件连接是没有问题的,模块也已经成功接收到了指令,后面就是啃数据手册、改程序、烧录程序观察显示的循环了。

    那初始化的流程是怎么样的呢?我们还是看看HD44780的数据手册吧,如下图所示:

    可以看到,初始化的主要步骤如下:

    (1)上电:这特么也算是一个步骤么?是的!如果你用的是其它液晶模块,比如LCD12864,会发现有一个复位引脚,LCD1602是没有复位引脚的,HD44780本身已经有内部复位逻辑。上电后复位其实会做很多事情的,主要如下:

        a. 清除屏幕数据

        b.设置8位并行通讯方式,1行显示,5X8点阵显示

        c.显示关,关标关,光标闪烁关

        d.地址自动加1,没有屏移

    从复位所做的事情来看,初始化该做的都已经做了,要看到光标只需要两条指令:打开显示,再开启光标显示即可。但是为了充分保证初始化的成功率(有可能LCD1602由于外部电气特性条件没达到而没有成功初始化),一般还是会用指令显式重新进行初始化(况且有些时候默认的初始化可能还达不到我们的要求),后面的几条语句其实就是重复上电后做的那些事,我们看看

    (2)功能设置(Function Set ):其实就是b点

    (3)显示打开/关闭(Display On/Off Control):其实就是c.1(打开显示)

    (4)进入模式设置(Entry Mode set):其实就是d

    (5)前面就是初始化,后面就是写显示数据了

    ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    下面是Atmega AVR驱动LCD1602液晶显示模块代码,第一行显示“LCD1602 DEMO”,第二行显示“--CNBLOGS--”,这里没有读状态指令的使用,实际使用时,在写指令或数据时加一些延时即可

    注意:分了三个文件,main.c是入口,lcd1602.h与lcd1602.c是具体的一些读写函数

    /*******************************************************************************
    * 文件名: main.c
    * 功能描述: LCD1602显示屏驱动
    *******************************************************************************/

    #include <iom128v.h>
    #include <macros.h>
    #include "lcd1602.h"
    /*******************************************************************************
    * 函数名称: void port_init(void)
    * 功能描述: LCD1602显示屏的IO口类型设置
    *******************************************************************************/
    void port_init(void)
    {
        //以下为LCD1602相关接口引脚,客户可根据需要进行引脚更改

        DDRA |= BIT(6)|BIT(7); //PA6->RS,PA7->RW

        PORTA |= (BIT(6));
        PORTA |=(BIT(7));
        DDRC =0XFF; //PC->(D0-D7)
        PORTC=0X00;
        DDRG |= BIT(2); //PG2->E
        PORTG |= BIT(2);
        //以下与LCD接口无关,不同的平台可无需要关注
        DDRB |= BIT(1);
        PORTB |= BIT(0);
        DDRD =0X00;
        PORTD=0X00;
        DDRE =0X00;
        PORTE=0X00;
        DDRF =0X00;
        PORTF=0X00;
    }
    /*******************************************************************************
    * 函数名称: void init_devices(void)
    * 功能描述: 单片机系统初始化设置(不同的平台有不同的设置,此函数可不关注)

    *******************************************************************************/
    void init_devices(void)
    {
        //stop errant interrupts until set up
        CLI(); //disable all interrupts
        XDIV = 0x00; //xtal divider
        XMCRA = 0x00; //external memory
        port_init();

        MCUCR = 0x00;
        EICRA = 0x00; //extended ext ints
        EICRB = 0x00; //extended ext ints
        EIMSK = 0x00;
        TIMSK = 0x00; //timer interrupt sources
        ETIMSK = 0x00; //extended timer interrupt sources
        SEI(); //re-enable interrupts
        //all peripherals are now initialized
    }

    //这里是入口
    void main(void)
    {
        int i=0;
        init_devices();//初始化系统
        LCD_init(); //LCD初始化
        LCD_write_str(0, 0, "--Lcd1602--Demo-");
        LCD_write_str(0, 1, "-----CNBLOGS-----");
        while(1){}
    }

    ///////////////////////以下为lcd1602.c/////////////////////////////////

    #include "lcd1602.h"

    //us延时函数
    void delay_us(uint n) //8*0.125=1us
    {
        int i,j;
        for(j=0;j<8;j++)  {
            for (i=0;i<n;i++)  {NOP();}
        }
    }

    //ms延时函数
    void delay_ms(uint i)
    {
        while(i--) {
            uint j;
            for(j=1;j<=1332;j++) {}
        }
    }
    //EN引脚产生一个高电平脉冲,控制LCD写时序
    void LCD_en_write(void)
    {
        E_ON;//EN_SET;
        delay_us(3);
        E_OFF;//EN_CLR
        delay_us(3);//
    }
    //写指令函数
    void Write_Instruction(uchar command)
    {
        RS_OFF;//RS_CLR;
        RW_OFF;//RW_CLR
        E_ON;//EN_SET;
        PORTC=command;
        LCD_en_write();//写入指令数据
    }

    //写数据函数
    void Write_Data(uchar Wdata)
    {
        RS_ON;//RS_SET;
        RW_OFF;//RW_CLR;
        E_ON;//EN_SET;
        PORTC=Wdata;
        LCD_en_write();//写入数据
    }
    //清屏函数
    void LCD_clear(void)
    {
        Write_Instruction(0x01);
        delay_ms(20);
    }

    //字符显示初始地址设置
    void LCD_SET_XY(uchar X,uchar Y)
    {
        uchar address;
        if(Y==0) {
            address=0x80+X;//Y=0,表示在第一行显示,地址基数为0x80
        } else {
            address=0xc0+X;//Y非0时,表时在第二行显示,地址基数为0XC0
        }
        Write_Instruction(address);//写指令,设置显示初始地址
    }

    //在第X行Y列开始显示,指针*S所指向的字符串
    void LCD_write_str(uchar X,uchar Y,uchar *s)
    {
        LCD_SET_XY(X,Y);//设置初始字符显示地址
        while(*s) {//逐次写入显示字符,直到最后一个字符"/0"    
           Write_Data(*s);//写入当前字符并显示
            s++;//地址指针加1,指向下一个待写字符
        }
    }

    //在第X行Y列开始显示Wdata所对应的单个字符
    void LCD_write_char(uchar X,uchar Y,uchar Wdata)
    {
        LCD_SET_XY(X,Y);//写地址
        Write_Data(Wdata);//写入当前字符并显示
    }
    //显示屏初始化函数
    void LCD_init(void)
    { //注意:可以与上面的步骤对应起来
        delay_ms(5);//上电延时一段时间,使供电稳定
        Write_Instruction(0x3c); //Function Set
        Write_Instruction(0x01); //清屏,后面的指令无严格顺序
        Write_Instruction(0x06); //写字符,整屏显示不移动
        Write_Instruction(0x0f); //开显示,开光标, 开闪烁
    }

     ////////////////////////////以下为lcd1602.h//////////////////////////////////////////////////////////

    #ifndef _LCD1602_H
    #define _LCD1602_H

    #include "iom128v.h"
    #include "macros.h"

    #define uchar unsigned char
    #define uint unsigned int

    #define RS_ON PORTA |= BIT(6);//RS置1
    #define RS_OFF PORTA &= ~BIT(6);//RS置0

    #define RW_ON PORTA |= BIT(7);//RW置1
    #define RW_OFF PORTA &= ~BIT(7);//RW置0

    #define E_ON PORTG |= BIT(2);//E置1
    #define E_OFF PORTG &= ~BIT(2);//E置0

    void delay_us(uint n);
    void delay_ms(uint i);
    void Port_init(void);
    void LCD_init(void);
    void LCD_en_write(void);
    void LCD_clear(void);
    void Write_Instruction(uchar command);
    void Write_Data(uchar Wdata);
    void LCD_SET_XY(uchar X,uchar Y);
    void LCD_write_str(uchar X,uchar Y,uchar *s);
    void LCD_write_char(uchar X,uchar Y,uchar Wdata);

    #endif

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    写博文时我手头上暂时没有LCD1602,所以用单片机驱动手头上的虚拟器件模块,在手机上看到的效果如下图所示,与实际LCD1602液晶显示屏效果是一样的:

    关注《电子制作站》微信公众号dzzzzcn,更多精彩技术文章与视频等着你~~
  • 相关阅读:
    虚拟机更新为阿里数据源
    linux设置开机同步时间
    linux免密码登录
    Google 开源的 Python 命令行库:深入 fire(一)
    统计各个分类和标签下的文章数
    缩写名词解释
    Google 开源的 Python 命令行库:初探 fire
    稳定易用的 Django 分页库,完善分页功能
    Python 命令行之旅:使用 click 实现 git 命令
    Python 库打包分发、setup.py 编写、混合 C 扩展打包的简易指南(转载)
  • 原文地址:https://www.cnblogs.com/sunshine-jackie/p/6953960.html
Copyright © 2011-2022 走看看