一、原理简介
Vivado版本:2016.2
OLED型号:128*32的UG-2832HSWEG04
ZedBoard的OLED部分电路原理图如下:(需要我们关心的是我用红色椭圆标注出来的3处,一共6个信号)
zedboard控制OLED的主要方法是:自己设计一个IP核,把OLED对应的6个控制引脚进行逻辑设计和约束,IP核通过AXI总线,把OLED对应的6个控制引脚和PS联系起来。通过PS编写相应的驱动程序,即可实现对OLED的控制。方案如下:
二、在Vivado中进行硬件设计
1.创建工程 (板子型号选zedboard)>> Tools >> Create and Package IP
2.选择Create AXI4 Peripheral
3.更改ip名
4.一路默认,在最后一页选择Edit IP(最小得选4个寄存器实际我们只用到了一个32位寄存器中的低6位)
5.此时会建立一个临时工程,在我们修改完成后会自行删除
6.给顶层文件添加输出端口OLED,即添加如下代码:output wire [5:0] OLED
给子模块添加.OLED(OLED),
7.同样,给子模块添加输出端口OLED,即添加如下代码: output reg [5:0] OLED,
然后添加如下代码,将slv_reg0寄存器的低6位赋给OLED端口
8.保存,并在如下页面的Customization GUI中确认,所添加的输出端口是否显示出来,如果没有,在此页面顶部有会提示,点击更新一下即可
9.点击下图底部的Re-Package IP,此时软件自动关闭。可在IP catalog的下属文件夹中找到了定义的IP。(以上为创建AXI IP的基本步骤)
10.创建Block Design,名字默认。添加ZYNQ,以及自定义的myOLED_ip。点击Run Block Automation。
11.修改ZYNQ,只保留一个uart1和默认的GPIO。点击Run Connection Automation,并Regenerate Layout,最终的block设计图如下:
分配的地址如下:
12.validate design >> generate output products >> create HDL wrapper.
13.查看zedboard中OLED硬件连接原理图(我们需要用到的就是我红色椭圆标注出来的)
14.综合后,打开综合设计,在I/O Planning 视图中按原理图添加OLED端口约束管脚,这里的顺序可以随意,但一定要和后续的驱动程序相对应。
这里给出我的约束文件代码:
set_property IOSTANDARD LVCMOS33 [get_ports {OLED[5]}] set_property IOSTANDARD LVCMOS33 [get_ports {OLED[4]}] set_property IOSTANDARD LVCMOS33 [get_ports {OLED[3]}] set_property IOSTANDARD LVCMOS33 [get_ports {OLED[2]}] set_property IOSTANDARD LVCMOS33 [get_ports {OLED[1]}] set_property IOSTANDARD LVCMOS33 [get_ports {OLED[0]}] set_property PACKAGE_PIN U12 [get_ports {OLED[5]}] set_property PACKAGE_PIN U11 [get_ports {OLED[4]}] set_property PACKAGE_PIN AA12 [get_ports {OLED[3]}] set_property PACKAGE_PIN U10 [get_ports {OLED[0]}] set_property PACKAGE_PIN U9 [get_ports {OLED[1]}] set_property PACKAGE_PIN AB12 [get_ports {OLED[2]}]
也就是说,我的引脚对应关系为:
15.生成比特流 >> export to hardware >> lanch sdk 。至此,vivado中的硬件设计就结束了。
三、在SDK中进行软件设计
1.新建工程application project >> 选择hello world模板(这里有一些自动添加进来的文件可能会用到,比如:platform.h和platform.c 如果选择空模板的话,这些文件不会被自动添加进来,目前我也不知道怎么手动添加它们)
2.新建oled.h头文件,代码如下:
/* * oled.h */ #ifndef OLED_H_ #define OLED_H_ #include "xil_types.h" // 数据类型定义 #include "xil_io.h" extern int usleep(unsigned int useconds); #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 //Xil_Out32(OLED_BASE_ADDR,1<<i); // 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); #endif /* OLED_H_ */
3.新建oled.c源文件,代码如下:
* oled.c #include "oled.h" #include "stdlib.h" #include "font.h" //SSD1306 OLED 驱动IC驱动代码 //4线串口 //版本:V1.1 //cuter //2012-12-16 //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]; //更新显存到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]); } } //向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; } } //开启OLED显示 void OLED_Display_On(void) { write_cmd(0X8D); //SET DCDC命令 write_cmd(0X14); //DCDC ON write_cmd(0XAF); //DISPLAY ON } //关闭OLED显示 void OLED_Display_Off(void) { write_cmd(0X8D); //SET DCDC命令 write_cmd(0X10); //DCDC OFF write_cmd(0XAE); //DISPLAY OFF Set_OLED_VDD; Set_OLED_VBAT; } //清屏函数,清完屏,整个屏幕是黑色的!和没点亮一样!!! void OLED_Clear(void) { u8 i,n; for(i=0;i<8;i++) for(n=0;n<128;n++) OLED_GRAM[n][i]=0X00; OLED_Refresh_Gram();//更新显示 } //画点 //x:0~127 //y:0~63 //t:1 填充 0,清空 void OLED_DrawPoint(u8 x,u8 y,u8 t) { u8 pos,bx,temp=0; if(x>127||y>63) return;//超出范围了. pos=7-y/8; bx=y%8; temp=1<<(7-bx); if(t) OLED_GRAM[x][pos]|=temp; else OLED_GRAM[x][pos]&=~temp; } //x1,y1,x2,y2 填充区域的对角坐标 //确保x1<=x2;y1<=y2 0<=x1<=127 0<=y1<=63 //dot:0,清空;1,填充 void OLED_Fill(u8 x1,u8 y1,u8 x2,u8 y2,u8 dot) { u8 x,y; for(x=x1;x<=x2;x++) { for(y=y1;y<=y2;y++) OLED_DrawPoint(x,y,dot); } OLED_Refresh_Gram();//更新显示 } //在指定位置显示一个字符,包括部分字符 //x:0~127 //y:0~63 //mode:0,反白显示;1,正常显示 //size:选择字体 16/12 void OLED_ShowChar(u8 x,u8 y,u8 chr,u8 size,u8 mode) { u8 temp,t,t1; u8 y0=y; chr=chr-' ';//得到偏移后的值 for(t=0;t<size;t++) { if(size==12)temp=asc2_1206[chr][t]; //调用1206字体 else temp=asc2_1608[chr][t]; //调用1608字体 for(t1=0;t1<8;t1++) { if(temp&0x80)OLED_DrawPoint(x,y,mode); else OLED_DrawPoint(x,y,!mode); temp<<=1; y++; if((y-y0)==size) { y=y0; x++; break; } } } } //m^n函数 u32 mypow(u8 m,u8 n) { u32 result=1; while(n--)result*=m; return result; } //显示2个数字 //x,y :起点坐标 //len :数字的位数 //size:字体大小 //mode:模式 0,填充模式;1,叠加模式 //num:数值(0~4294967295); void OLED_ShowNum(u8 x,u8 y,u32 num,u8 len,u8 size) { u8 t,temp; u8 enshow=0; for(t=0;t<len;t++) { temp=(num/mypow(10,len-t-1))%10; if(enshow==0&&t<(len-1)) { if(temp==0) { OLED_ShowChar(x+(size/2)*t,y,' ',size,1); continue; }else enshow=1; } OLED_ShowChar(x+(size/2)*t,y,temp+'0',size,1); } } //显示字符串 //x,y:起点坐标 //*p:字符串起始地址 //用16字体 void OLED_ShowString(u8 x,u8 y,const u8 *p) { #define MAX_CHAR_POSX 122 #define MAX_CHAR_POSY 58 while(*p!='