使用Vivado初探ZedBoard的OLED驱动

Posted 愣娃娃

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用Vivado初探ZedBoard的OLED驱动相关的知识,希望对你有一定的参考价值。

一、原理简介

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]}]
View Code

 也就是说,我的引脚对应关系为:

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_ */
View Code

 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!=\'\\0\')
    {
        if(x>MAX_CHAR_POSX){x=0;y+=16;}
        if(y>MAX_CHAR_POSY){y=x=0;OLED_Clear();}
        OLED_ShowChar(x,y,*p,16,1);
        x+=8;
        p++;
    }
}

//初始化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);
}
View Code

 4.新建font.h头文件,代码如下:

* font.h

#ifndef FONT_H_
#define FONT_H_

//常用ASCII表
//偏移量32
//ASCII字符集
//偏移量32
//大小:12*6
const unsigned char asc2_1206[95][12]={
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*" ",0*/
{0x00,0x00,0x00,0x00,0x3F,0x40,0x00,0x00,0x00,0x00,0x00,0x00},/*"!",1*/
{0x00,0x00,0x30,0x00,0x40,0x00,0x30,0x00,0x40,0x00,0x00,0x00},/*""",2*/
{0x09,0x00,0x0B,0xC0,0x3D,0x00,0x0B,0xC0,0x3D,0x00,0x09,0x00},/*"#",3*/
{0x18,0xC0,0x24,0x40,0x7F,0xE0,0x22,0x40,0x31,0x80,0x00,0x00},/*"$",4*/
{0x18,0x00,0x24,0xC0,0x1B,0x00,0x0D,0x80,0x32,0x40,0x01,0x80},/*"%",5*/
{0x03,0x80,0x1C,0x40,0x27,0x40,0x1C,0x80,0x07,0x40,0x00,0x40},/*"&",6*/
{0x10,0x00,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"\'",7*/
{0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0x80,0x20,0x40,0x40,0x20},/*"(",8*/
{0x00,0x00,0x40,0x20,0x20,0x40,0x1F,0x80,0x00,0x00,0x00,0x00},/*")",9*/
{0x09,0x00,0x06,0x00,0x1F,0x80,0x06,0x00,0x09,0x00,0x00,0x00},/*"*",10*/
{0x04,0x00,0x04,0x00,0x3F,0x80,0x04,0x00,0x04,0x00,0x00,0x00},/*"+",11*/
{0x00,0x10,0x00,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*",",12*/
{0x04,0x00,0x04,0x00,0x04,0x00,0x04,0x00,0x04,0x00,0x00,0x00},/*"-",13*/
{0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*".",14*/
{0x00,0x20,0x01,0xC0,0x06,0x00,0x38,0x00,0x40,0x00,0x00,0x00},/*"/",15*/
{0x1F,0x80,0x20,0x40,0x20,0x40,0x20,0x40,0x1F,0x80,0x00,0x00},/*"0",16*/
{0x00,0x00,0x10,0x40,0x3F,0xC0,0x00,0x40,0x00,0x00,0x00,0x00},/*"1",17*/
{0x18,0xC0,0x21,0x40,0x22,0x40,0x24,0x40,0x18,0x40,0x00,0x00},/*"2",18*/
{0x10,0x80,0x20,0x40,0x24,0x40,0x24,0x40,0x1B,0x80,0x00,0x00},/*"3",19*/
{0x02,0x00,0x0D,0x00,0x11,0x00,0x3F,0xC0,0x01,0x40,0x00,0x00},/*"4",20*/
{0x3C,0x80,0x24,0x40,0x24,0x40,0x24,0x40,0x23,0x80,0x00,0x00},/*"5",21*/
{0x1F,0x80,0x24,0x40,0x24,0x40,0x34,0x40,0x03,0x80,0x00,0x00},/*"6",22*/
{0x30,0x00,0x20,0x00,0x27,0xC0,0x38,0x00,0x20,0x00,0x00,0x00},/*"7",23*/
{0x1B,0x80,0x24,0x40,0x24,0x40,0x24,0x40,0x1B,0x80,0x00,0x00},/*"8",24*/
{0x1C,0x00,0x22,0xC0,0x22,0x40,0x22,0x40,0x1F,0x80,0x00,0x00},/*"9",25*/
{0x00,0x00,0x00,0x00,0x08,0x40,0x00,0x00,0x00,0x00,0x00,0x00},/*":",26*/
{0x00,0x00,0x00,0x00,0x04,0x60,0x00,0x00,0x00,0x00,0x00,0x00},/*";",27*/
{0x00,0x00,0x04,0x00,0x0A,0x00,0x11,0x00,0x20,0x80,0x40,0x40},/*"<",28*/
{0x09,0x00,0x09,0x00,0x09,0x00,0x09,0x00,0x09,0x00,0x00,0x00},/*"=",29*/
{0x00,0x00,0x40,0x40,0x20,0x80,0x11,0x00,0x0A,0x00,0x04,0x00},以上是关于使用Vivado初探ZedBoard的OLED驱动的主要内容,如果未能解决你的问题,请参考以下文章

zedboard中OLED源码

zedboard在PL端实现流水灯

vivado 创建FPGA工程

Vivado常见问题集锦

petalinux&zedboard(自定义IP学习笔记)

在zedboard上跑不带桌面系统的linux