引言
随着信息化的发展,基于Nios的嵌入式系统开发成为国内当前研究的热点,它具有成本低廉、设计灵活、可裁减、可扩充以及软硬件在系统可编程的特点。通常,Nios只带有Character LCD(16×2,optrex 16207)的驱动,如果要换成带汉字显示的图形LCD,则要开发相应的组件和驱动[1]。当然,对Nios来说这是很容易实现的,但同时也是十分重要的。本文以SMG240128A(简称为“240128A”)为例介绍Nios驱动LCD的方法。
用Nios控制240128A有两种方式:一是通过自定义硬件模块方式将Nios的Avalon从端口与240128A连接,Nios可通过该模块直接与240128A通信;二是利用Nios自带的输入输出PIO核控制240128A。
1 自定义方式
SOPC(片上可编程系统)技术具有很高的灵活性,充分体现在SOPC Builder不但自带了一些标准的组件(如并行输入/输出PIO核)供用户使用;而且同时也提供了一个组件编辑器,用来创建和编辑用户自己的SOPC Builder组件,灵活地实现与外设通信。一个典型的SOPC Builder组件应包含以下几部分:
① 硬件文件,描述组件硬件的HDL模块。
② 软件文件,一个定义组件寄存器映像的C语言头文件,软件控制组件所需的驱动程序。
③ 组件描述文件(class.ptf),定义组件的结构,为SOPC Builder提供将组件集成到系统的信息。组件编辑器自动创建这个文件,它是基于所提供的硬件、软件及在编辑器GUI中指明的参数而创建的[2]。
本文的自定义方式就是利用HDL语言描述一个自定义的硬件模块,然后通过组件编辑器将其封装成一个SOPC Builder元件,再编写C语言的驱动程序,在Nios II IDE环境下运行便可实现LCD正常显示。
自定义硬件模块的内部结构定义如下:将读请求信号read_n、写请求信号write_n、1位地址信号address分别与R_D、W_R、C_D直连;writedata为Avalon从端口写入的8位数据端口。readdata为Avalon从端口读出的8位数据端口。ZDB为双向三态数据端口,Nios与LCD之间的双向数据通信可用Verilog语言描述如下:
assign dbz=write_n? 8'bz : writedata;
//write_n为0时dbz=writedata,否则dbz输出呈高阻态
assign readdata=dbz;
//dbz输出呈高阻态时可将数据输入到readdata端口
当写请求信号write_n(低电平)有效时,writedata数据可以输出到dbz;当读请求信号read_n(低电平)有效时,写请求信号write_n是无效的,dbz输出呈高阻态,此时数据可以从dbz输入到readdata,即可完成数据的双向通信。
Nios处理器与240128A之间的具体通信过程如下:当Nios要向LCD写数据时,Nios发出的写请求信号通过Avalon从端口传到模块的write_n端口,再从模块的W_R端口传到LCD的写请求(WR)信号端口,同时8位数据写到模块的writedata端口,再从模块的ZDB端口输出到LCD的数据端口;当Nios要从LCD读状态时,Nios发出的读请求信号通过Avalon从端口传到模块的read_n端口,再从模块的R_D端口传到LCD的读请求(RD)信号端口,于是LCD将8位的状态信息通过数据端口传到模块的ZDB端口,再从模块的readdata端口输出到Avalon从端口。以上过程中1位地址信号address与C_D直连(内部连接,图1中未表示),用来控制LCD的C/D端口。当address为0时,对数据操作;为1时,对指令操作[1,3]。具体原理框图如图1所示。
图1 自定义方式Nios与240128A连接框图
当上述定义的硬件模块添加进Nios系统后,再编写C语言驱动程序,便可实现对LCD的基本操作了。对240128A的基本操作有:读状态、写命令、写数据。读状态只需1条命令就可以了:state=IORD (SMG_LCD_BASE,1)。state为读入的状态值,SMG_LCD_BASE为LCD的基地址。1为偏移地址,表示读入的为指令。同理,向LCD 写入命令可表示为IOWR(SMG_LCD_BASE,1,command),写入数据表示为IOWR(SMG_LCD_BASE,0,dat)。由于LCD速度比Nios(此处为50 MHz)的速度慢几个时钟周期,所以在写数据/指令后,要有延时,等LCD完成操作后再进行下一步操作,通常设计延时1 μs以上就够了[3]。每次向LCD操作时都要判断其状态是否准备好,写完数据后要把对应的指令功能代码也写进去,LCD才能正确识别。写1字节数据流程如图2所示。
240128A初始化流程如图3所示,由于显示文字是调用字库,所以初始化时设为纯图形模式。我们注意到,240128A为240×128点阵的黑白显示器,可以用1个字节表示8个点,对应位1表示黑点,0表示白点,定义一个数组disp_data[128][30](每个元素为8位无符号字符型变量),就可对应1帧显示屏上所有点,向数组写入数据就可以实现图形显示。譬如当要显示某点时,根据该点在240128A中x和y坐标值可对应到数组disp_data具体元素的对应位,写入要显示的值,再送到240128A相应地址中即可显示。对于文字的显示,可先将字库文件下载到Flash存储器中。譬如,英文8×16点阵文件ASC16和汉字16×16点阵文件HZK16;当需要显示文字时,直接调字库显示即可。
2 利用PIO核控制方式
并行输入/输出PIO核提供Avalon从端口和通用I/O端口之间的寄存器映射接口。I/O端口既可与片内用户逻辑连接,又可与FPGA的外围器件连接。每个PIO可提供32个I/O端口,4个32位存储器映像的寄存器。Nios处理器可以通过这4个寄存器控制PIO并与PIO通信。它们分别是:
数据寄存器读取数据寄存器将返回输入端口值,将数据写至数据寄存器时,其结果是将数据送往输出端口;
方向寄存器控制每个双向PIO口的实际方向,只有当PIO被配置成双向模式时,此寄存器才存在;
中断屏蔽寄存器使能或禁止相应PIO输入端口中断,只有当硬件配置成可产生IRQ中断时,此寄存器才存在;
边沿捕获寄存器记录是否捕获到边沿信息,只有当硬件配置为边沿捕获模式时,此寄存器才存在[1]。
此处由于要实现双向数据通信,所以将PIO核硬件配置为三态双向模式,而中断屏蔽寄存器和边沿捕获寄存器可以不用设置,也就不存在了。
要利用PIO核直接控制,需要在SOPC Builder中将一个PIO核添加进Nios系统,并将该核设置为11位三态双向模式,其高3位用来作为240128A的控制位,低8位作为数据位。240128A与Nios连接框图如图4所示。
图2 向240128A写1字节数据 图3 240128A初始化流程
图4 SMG240128A与Nios连接框图
由于状态位和数据位都在同一个PIO的端口中(即对应数据寄存器的低11位),所以操作时应分别对高3位和低8位操作,才能实现对LCD的读写控制。读状态流程如图5所示。
向LCD写命令的流程如图6所示,写数据同样可以通过上述方法实现。需要注意的是:当向LCD写命令/数据时,应将数据/命令与相应的状态位合在一起通过11位的PIO口写入LCD,操作结束后应使读、写无效,以免LCD写入了错误的数据/命令。还有一种方法是利用两个PIO核实现对LCD的控制,即一个核用来控制状态,一个核用来传输数据。后面显示的方法与自定义方式是一样的,这里不再重复。
图5 读LCD状态流程 图6 向LCD写命令流程
以上两种方法都能很好地实现LCD的显示,但在实现LCD控制上有比较大的不同:PIO方式是靠改写PIO口数据高3位来实现LCD控制的,Nios与PIO核之间的通信是隐藏了;而自定义方式中,读/写LCD是利用Avalon总线周期读/写有效时产生的低电平来实现的。两种方法的优缺点是:PIO方式是利用Nios自带PIO核实现的,无需增加硬件模块,但每次对LCD读/写操作都要修改PIO方向寄存器,而且数据与指令要分别操作,因此C语言编写的驱动程序要复杂些;自定义方式需自己添加硬件模块,但对LCD读/写操作只需一条指令就可以了,因此C语言编写的驱动程序要简单些。
结语
通过使用两种方法实现Nios对液晶显示器的控制可以看出: Nios是一个性价比较高的微处理器软核,既可以利用其自带的软核,又可以采用用户自定义的方式完成与外部设备通信,与其他处理器或控制电路相比,具有设计方便、修改简单且具有在线编程和灵活性高等优点。一般的MCU,如51单片机或ARM处理器是硬核,其功能不能根据应用的需要来更改;而Nios处理器是软核,可以根据不同的应用场合定制功能最合适的处理器,而且还可以在一块FPGA中同时定制多个Nios处理器,以提高效率。我们相信,基于Nios的SOPC技术将拥有广阔的发展空间,其应用会越来越广泛。