目录
1、前言
本篇文章主要讲解在Xilinx ZedBoard上开发OLED显示功能,从OLED硬件平台设计、IP核设计、SDK驱动设计到应用VxWorks6.9开发OLED显示功能,力求讲述清楚开发流程,并配套完整的演示软件和相关代码进行验证。下面将从以下几个方面进行讲解。
- OLED设计概述
- Xilinx Vivado OLED IP核设计与实现
- OLED驱动设计与实现
- VxWorks6.9开发OLED驱动和应用。
开发工具说明:
- Xilinx Vivado2018.3
- Xilinx SDK2018.3
- WorkBench3.3
2、OLED设计概述
图1 OLED设计流程图
Zedboard使用Inteltronic/Wisechip公司的OLED显示模组UG-2832HSWEG04,分辨率为128×32,是一款单色被动式显示屏幕,驱动电路采用SSD1306芯片,Zedboard具体的OLED接口电路如图2所示, UG-2832HSWEG04对应的引脚定义如表1所描述。
图2 OLED接口原理图
UG-2832HSWEG04引脚定义表1
引脚定义 | 描述 |
OLED_VBAT | 电源控制引脚,低电平有效。(注意,不直接对应OLED的VBAT引脚,在原理图上作为控制脚,为低的时候将VCC3V3送入OLED。) |
OLED_VDD | 同OLED_VBAT,低电平有效 |
OLED_RES | 复位脚,上电的时候拉低,复位一段时间再拉高 |
OLED_DC | 数据/命令选择引脚 |
OLED_SCLK | 串行时钟引脚 |
OLED_SDIN | 串行数据引脚 |
2.1 Vivado OLED IP核设计与实现
让OLED正确的显示数据,需要Znyq7000与其通信,参考SSD1306的控制时序,通信协议采用四线SPI协议,支持写模式,数据和命令均通过该协议传输,其时序如图3所示,SPI通信协议在时钟的下降沿改变数据,上升沿采样有效数据。因此,需要通过Vivado设计OLED的IP核,使其满足OLED对应的驱动时序。
图3 OLED SPI控制时序图
图4为系统设计框图,标黄色部分为OLED IP核模块,其引脚OLED[5:0]分别对应图2原理图中的6个控制管脚。具体对应关系如下。
OLED[0]-OLED_DC OLED[1]-OLED_RES OLED[2]-OLED_SCK OLED[3]-OLED_SDIN OLED[4]-OLED_VBAT OLED[5]-OLED_VDD |
图4 系统设计框图
OLED对应的IP核PL侧AXI地址空间分配如图5所示。访问OLED的地址为0x43C00000,分配的空间大小为64KB。
图5 OLED分配的PL侧地址空间
2.2 SDK OLED驱动设计与实现
首先分析OLED UG-2832HSWEG04屏幕驱动芯片SSD1306工作原理。如图6所示,该OLED一共有四页,Page0-Page3,每页大小为128×8,该图没有重映射,为复位后默认映射。其初始化命令可以参考SSD1306的资料,为了快速开发OLED的驱动,可以基于Xilinx提供的SDK工具和BSP包快速进行开发。
图6 OLED显示方式
本节主要在Vivado完成硬件平台PL设计之后,基于PS部分的驱动软件设计,实现对OLED的控制。
由于ZedBoard开发板的OLED只支持写,不支持读,因此控制OLED就是在SCLK的时钟下,通过SDIN进行命令和数据的传输。OLED的初始化通过命令进行操作实现。
对于SPI通信,需要通过PS操作AXI地址控制PL输出对应管脚的高低电平。本示例共有6个引脚,因此需要设置6个引脚的高低电平。代码如下:
#define OLED_BASE_ADDR 0X43c00000 //这里根据vivado中分配的地址来修改
#define OLED_DC 0 #define OLED_RES 1 #define OLED_SCLK 2 #define OLED_SDIN 3 #define OLED_VBAT 4 #define OLED_VDD 5
// DC #define Set_OLED_DC (Xil_Out32(OLED_BASE_ADDR,Xil_In32(OLED_BASE_ADDR)|(1<<OLED_DC))) #define Clr_OLED_DC (Xil_Out32(OLED_BASE_ADDR,Xil_In32(OLED_BASE_ADDR)&(~(1<<OLED_DC)))) // RES #define Set_OLED_RES (Xil_Out32(OLED_BASE_ADDR,Xil_In32(OLED_BASE_ADDR)|(1<<OLED_RES))) #define Clr_OLED_RES (Xil_Out32(OLED_BASE_ADDR,Xil_In32(OLED_BASE_ADDR)&(~(1<<OLED_RES)))) // SCLK #define Set_OLED_SCLK (Xil_Out32(OLED_BASE_ADDR,Xil_In32(OLED_BASE_ADDR)|(1<<OLED_SCLK))) #define Clr_OLED_SCLK (Xil_Out32(OLED_BASE_ADDR,Xil_In32(OLED_BASE_ADDR)&(~(1<<OLED_SCLK)))) // SDIN #define Set_OLED_SDIN (Xil_Out32(OLED_BASE_ADDR,Xil_In32(OLED_BASE_ADDR)|(1<<OLED_SDIN))) #define Clr_OLED_SDIN (Xil_Out32(OLED_BASE_ADDR,Xil_In32(OLED_BASE_ADDR)&(~(1<<OLED_SDIN)))) // OLED_VBAT #define Set_OLED_VBAT (Xil_Out32(OLED_BASE_ADDR,Xil_In32(OLED_BASE_ADDR)|(1<<OLED_VBAT))) #define Clr_OLED_VBAT (Xil_Out32(OLED_BASE_ADDR,Xil_In32(OLED_BASE_ADDR)&(~(1<<OLED_VBAT)))) // OLED_VDD #define Set_OLED_VDD (Xil_Out32(OLED_BASE_ADDR,Xil_In32(OLED_BASE_ADDR)|(1<<OLED_VDD))) #define Clr_OLED_VDD (Xil_Out32(OLED_BASE_ADDR,Xil_In32(OLED_BASE_ADDR)&(~(1<<OLED_VDD))))
//OLED控制用函数 void write_data(u8 data); void write_cmd(u8 data); void OLED_Display_On(void); void OLED_Display_Off(void); void OLED_Refresh_Gram(void); void OLED_Init(void); void OLED_Clear(void); void OLED_DrawPoint(u8 x,u8 y,u8 t); void OLED_Fill(u8 x1,u8 y1,u8 x2,u8 y2,u8 dot); void OLED_ShowChar(u8 x,u8 y,u8 chr,u8 size,u8 mode); void OLED_ShowNum(u8 x,u8 y,u32 num,u8 len,u8 size); void OLED_ShowString(u8 x,u8 y,const u8 *p); |
2.3 OLED初始化
OLED初始化代码一般参考屏幕厂家推荐的配置,满足需求即可。根据SSD1306数据手册的初始化说明,其初始化流程如图7所示。
图7 SSD1306初始化流程
SSD1306初始化,首先需要实现最基本的SPI写命令和写数据函数。
//向SSD1306写入一个字节的命令。 void write_cmd(u8 data) { u8 i; Clr_OLED_DC;
for(i=0;i<8;i++) { Clr_OLED_SCLK;
if(data&0x80) Set_OLED_SDIN; else Clr_OLED_SDIN; Set_OLED_SCLK; data<<=1; } }
//向SSD1306写入一个字节的数据。 void write_data(u8 data) { u8 i; Set_OLED_DC;
for(i=0;i<8;i++) { Clr_OLED_SCLK;
if(data&0x80) Set_OLED_SDIN; else Clr_OLED_SDIN; Set_OLED_SCLK; data<<=1; } } |
利用写数据和写命令的函数来实现SSD1306的初始化,初始化前需要对OLED屏幕进行复位。具体实现代码如下:
//初始化SSD1306 void OLED_Init(void) { Clr_OLED_VDD; usleep(300); // 延时300us Clr_OLED_RES; usleep(30000); // 延时30ms Set_OLED_RES; usleep(30000); // 延时30ms Clr_OLED_VBAT;
write_cmd(0xA8); write_cmd(0x3F); write_cmd(0xD3); write_cmd(0x00); write_cmd(0x40); write_cmd(0xA0); write_cmd(0xC8); write_cmd(0xDA); write_cmd(0x02); write_cmd(0x81); write_cmd(0x7F); write_cmd(0xA4); write_cmd(0xA6); write_cmd(0xD5); write_cmd(0x80); write_cmd(0x8D); write_cmd(0x14); write_cmd(0xAF); } |
2.4 OLED更新显存
显存数据写入SSD1306存储器,通过PS侧创建GRAM内存(共128个字节),在每次修改的时候,只修改PS的GRAM(映射OLED的SRAM),修改完后一次性把PS上的GRAM写入OLED的GRAM。
//OLED的显存 //存放格式如下. //[0]0 1 2 3 ... 127 //[1]0 1 2 3 ... 127 //[2]0 1 2 3 ... 127 //[3]0 1 2 3 ... 127 //[4]0 1 2 3 ... 127 //[5]0 1 2 3 ... 127 //[6]0 1 2 3 ... 127 //[7]0 1 2 3 ... 127 u8 OLED_GRAM[128][8]; |
更新显存到OLED具体代码如下:
//更新显存到LCD void OLED_Refresh_Gram(void) { u8 i,n; for(i=0;i<8;i++) { write_cmd (0xb0+i); //设置页地址(0~7) write_cmd (0x02); //设置显示位置—列低地址,偏移了2列 write_cmd (0x10); //设置显示位置—列高地址 for(n=0;n<128;n++)write_data(OLED_GRAM[n][i]); } } |
3、VxWorks6.9开发驱动与应用
3.1 OLED驱动
VxWorks6.9开发OLED驱动可以参考Xilinx SDK OLED的驱动代码,快速移植到VxWorks6.9中。
1、首先在sysLib.c文件中映射OLED AXI基地址空间为VxWorks的虚拟地址空间。
{ ZYNQ7K_OLED_BASE, /* OLED */ ZYNQ7K_OLED_BASE, SZ_64K, MMU_ATTR_VALID_MSK | MMU_ATTR_PROT_MSK | MMU_ATTR_DEVICE_SHARED_MSK, MMU_ATTR_VALID | MMU_ATTR_SUP_RWX | MMU_ATTR_DEVICE_SHARED },
|
2、在xlnx_zyn7k的BSP包中增加oled的目录,新建oled.h和oled.c文件,导入font.h字库文件,如图8所示。
图8 OLED BSP包目录
3、oled.c文件主要把之前Xilinx SDK访问OLED AXI地址宏定义进行VxWorks接口的适配即可。
图9 适配VxWorks6.9接口
4、把oled.c文件include加入到sysLib.c,只有这样编译器才会关联编译oled.c文件,否则不会对oled.c文件进行编译。
注意:如果出现WorkBench3.3采用“//”注释编译不能通过,可以通过配置编译器编译选项解决该问题。如图10所示,增加-std=gnu99选项或者-std=c99选项。
图10 编译器配置项
3.2 OLED应用
在userAppInit.c文件中应用上面开发的BSP驱动接口。将在ZedBoard板卡上显示“Hi VxWorks6.9”字样。
void usrAppInit (void) { #ifdef USER_APPL_INIT USER_APPL_INIT; /* for backwards compatibility */ #endif
/* add application specific code here */ dosFsVolFormat("/ram0", 0, 0);
#ifdef FLASH_TFFS_FORMAT /* 仅针对第一次格式化tffs文件系统生效 */ tffsDrv(); sysTffsFormat(); usrTffsConfig(0, 0, "/tffs0"); dosFsVolFormat("/tffs0", 0, 0); #endif
ifconfig("gem0 172.17.70.230 up");
OLED_Init(); OLED_Clear(); OLED_ShowString(0,0,"Hi VxWorks6.9"); OLED_Refresh_Gram(); } |
3.3 OLED显示
本示例提供oled.bit的FPGA文件和vxWorks烧写文件,为了调试方便,提供了支持网络启动VxWorks的BOOT.bin引导文件方便验证。本示例所有的软件均在Avnet发布的ZedBoard上验证通过。
操作命令如下:
1.加载FPGA bit文件 tftpboot 0x10000000 oled.bit fpga loadb 0 0x10000000 ${filesize}
2.加载和运行vxWorks文件 tftpboot 0x10000000 vxWorks bootelf 0x10000000 |
VxWorks6.9运行结果如图11所示,OLED显示效果如图12所示。
图11 VxWorks6.9运行图
图12 OLED显示图