Nios II驱动LCD12864(一)
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系统,如下图所示。
![](https://img2023.cnblogs.com/blog/2126010/202306/2126010-20230612002152544-264728307.png)
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]); // 写字符数据
}
}
LCD12864 DDRAM的地址映射如下所示,因此第一行首地址地址为80H,首地址加上列数就是要显示字符的DDRAM地址。
实验结果:
![](https://img2023.cnblogs.com/blog/2126010/202306/2126010-20230612002153803-1972514862.jpg)