Nios II驱动LCD12864(一)

发布时间 2023-06-12 00:17:15作者: 浅晓寒

​ LCD12864可以显示4行64个英文字符,以及显示4行共32个中文字符(需要中文字库,购买LCD时有些内置中文字库)。LCD12864的资料有很多,这里就不做介绍了。在学习Nios II软核时,需要使用LCD12864显示4行的字符而官方提供的IP核只能驱动LCD1602,因此方法一是利用SOPC技术在Qsys软件中使用PIO组件与LCD12864进行通信,方法二是编写带有Avalon-MM Slave信号接口的Verilog代码,并将其封装为自定义LCD12864组件。

使用PIO

搭建Nios II系统,如下图所示。

LCD的各个引脚及其方向

inout [7:0] lcd_data

output lcd_rs

output lcd_rw

output lcd_en

软件设计部分 C程序如下:

main.c

#include "lcd12864.h"

int main()
{
	Lcd12864_Init();	//初始化
	Lcd12864_WrStr(0, 0, "Hello Nios II");	//在lcd第一行第一列显示
	Lcd12864_WrStr(1, 0, "浅晓寒");	//在lcd第二行第一列显示
	Lcd12864_WrStr(2, 0, "日期:2023.6.11");	//在lcd第三行第一列显示
	return 0;
}

lcd12864.h

#ifndef __LCD12864_H__
#define __LCD12864_H__
 
#include "system.h"
#include "alt_types.h"

//宏定义
#define lcd_data	LCD_DATA_BASE
#define lcd_rs		LCD_RS_BASE
#define lcd_rw		LCD_RW_BASE
#define lcd_en		LCD_EN_BASE

extern void LCD12864_CheckBusy(void);
extern void Lcd12864_WrCmd(alt_u8 cmd);
extern void Lcd12864_WrData(alt_u8 data);
extern void Lcd12864_Init(void);
extern void Lcd12864_WrStr(alt_u8 row, alt_u8 col, alt_u8 *str);
 
#endif

lcd12864.c

#include "lcd12864.h"
#include "altera_avalon_pio_regs.h"
#include <unistd.h>
 
enum /* Write to character RAM */
{
  LCD_CMD_WRITE_DATA    = 0x80
  /* Bits 6:0 hold character RAM address */
};

enum /* Write to character generator RAM */
{
  LCD_CMD_WRITE_CGR     = 0x40
  /* Bits 5:0 hold character generator RAM address */
};

enum /* Function Set command */
{
  LCD_CMD_FUNCTION_SET  = 0x20,
  LCD_CMD_8BIT          = 0x10,
  LCD_CMD_TWO_LINE      = 0x08,
  LCD_CMD_BIGFONT       = 0x04
};

enum /* Shift command */
{
  LCD_CMD_SHIFT         		= 0x10,			//光标左移
  LCD_CMD_SHIFT_DISPLAY 		= 0x08,			//整体左移
  LCD_CMD_SHIFT_RIGHT_DISPLAY   = 0x0C,			//整体右移
  LCD_CMD_SHIFT_RIGHT   		= 0x04			//光标右移
};

enum /* On/Off command */
{
  LCD_CMD_ONOFF         = 0x08,
  LCD_CMD_ENABLE_DISP   = 0x04,
  LCD_CMD_ENABLE_CURSOR = 0x02,
  LCD_CMD_ENABLE_BLINK  = 0x01
};

enum /* Entry Mode command */
{
  LCD_CMD_MODES         = 0x04,
  LCD_CMD_MODE_INC      = 0x02,
  LCD_CMD_MODE_SHIFT    = 0x01
};

enum /* Home command */
{
  LCD_CMD_HOME          = 0x02
};

enum /* Clear command */
{
  LCD_CMD_CLEAR  = 0x01
};

/*
 * 判断LCD是否忙
 * */
void LCD12864_CheckBusy(void)
{
	IOWR_ALTERA_AVALON_PIO_DATA(lcd_rs, 0);	//指令
	IOWR_ALTERA_AVALON_PIO_DATA(lcd_rw, 1);	//读操作
	IOWR_ALTERA_AVALON_PIO_DATA(lcd_en, 1);	//使能LCD
	IOWR_ALTERA_AVALON_PIO_DIRECTION(lcd_data, 0x00);	//设置data引脚方向,0:输入,1:输出
	while((IORD_ALTERA_AVALON_PIO_DATA(lcd_data)&0x80) == 0x80);        // 检测busy flag,1:忙,0:不忙
	IOWR_ALTERA_AVALON_PIO_DATA(lcd_en, 0);	//使能LCD
	usleep(72);                           // 72us
}

//写指令
void Lcd12864_WrCmd(alt_u8 cmd)
{
	LCD12864_CheckBusy();				//检查LCD是否忙
	IOWR_ALTERA_AVALON_PIO_DATA(lcd_rs, 0);	//指令
	IOWR_ALTERA_AVALON_PIO_DATA(lcd_rw, 0);	//写
	IOWR_ALTERA_AVALON_PIO_DATA(lcd_en, 1);	//使能LCD
	IOWR_ALTERA_AVALON_PIO_DIRECTION(lcd_data, 0xff);	//设置data引脚方向,0:输入,1:输出
	IOWR_ALTERA_AVALON_PIO_DATA(lcd_data, cmd);	//写指令
	IOWR_ALTERA_AVALON_PIO_DATA(lcd_en, 0);	//使能LCD
	usleep(72);                           // 72us
}

//写数据
void Lcd12864_WrData(alt_u8 data)
{
	LCD12864_CheckBusy();
	IOWR_ALTERA_AVALON_PIO_DATA(lcd_rs, 1);	//数据
	IOWR_ALTERA_AVALON_PIO_DATA(lcd_rw, 0);	//写
	IOWR_ALTERA_AVALON_PIO_DATA(lcd_en, 1);	//使能LCD
	IOWR_ALTERA_AVALON_PIO_DIRECTION(lcd_data, 0xff);	//设置data引脚方向,0:输入,1:输出
	IOWR_ALTERA_AVALON_PIO_DATA(lcd_data, data);	//写数据
	IOWR_ALTERA_AVALON_PIO_DATA(lcd_en, 0);	//不使能LCD
	usleep(72);                           // 72us
}
 
void Lcd12864_Init(void)
{
    usleep(40*1000);
    Lcd12864_WrCmd(LCD_CMD_FUNCTION_SET | LCD_CMD_8BIT);  // 基础功能8bit接口控制位
    usleep(100);
    Lcd12864_WrCmd(LCD_CMD_FUNCTION_SET | LCD_CMD_8BIT);  // 基础功能8bit接口控制位
    usleep(37);
    Lcd12864_WrCmd(LCD_CMD_ONOFF | LCD_CMD_ENABLE_DISP);  // 整体显示开 游标关 反白关
    usleep(100);
    Lcd12864_WrCmd(LCD_CMD_SHIFT);                 // 光标左移
    usleep(100);
    Lcd12864_WrCmd(LCD_CMD_CLEAR);					//清屏
    usleep(10*1000);
    Lcd12864_WrCmd(LCD_CMD_MODES | LCD_CMD_MODE_INC);  // 进入设定点,游标右移,ddram地址+1
}

/*
 *
 *	函数功能:写英文/中文字符
 * 	行数row:0~3
 * 	列数col:0~7
 * 	*str:要写的英文字符串
 *
 * */
void Lcd12864_WrStr(alt_u8 row, alt_u8 col, alt_u8 *str)
{
    alt_u8 i, addr;
    row &= 0x03;                          // row < 4  0011 & 1001=1
    col &= 0x07;                          // col < 8
    switch(row)
    {
        case 0: addr = 0x80; break;
        case 1: addr = 0x90; break;
        case 2: addr = 0x88; break;
        case 3: addr = 0x98; break;
    }
    addr += col;			//行地址+列地址
    Lcd12864_WrCmd(addr);
    for(i=0; str[i]!='\0'; i++)
    {
        Lcd12864_WrData(str[i]);            // 写字符数据
    }
}

实验结果: