国产FPGA安路 高集成低功耗SF1系列FPSoC新品测评【使用SF1 RISCV IP 核驱动 oled】
[复制链接]
本帖最后由 Grayson__ 于 2023-3-19 11:14 编辑
在本期测评中,我们使用安路 SF1 的 RISCV IP 核控制 oled 的显示功能,参考资料有安路 SF1 的 DS800、SF1 DEMO板的原理图、TN816、TN817、TN818。本次测评分为两个部分:FPGA与MCU,分别使用 TD 与 FD 软件进行编写。
首先是 TD 部分,打开 TD 软件,新建名为 oled 的工程。首先调用 RISCV IP核,具体设置如下图:
调用 PLL IP 核,具体设置如下图,DEMO外部晶振为25MHz,所以PLL输入时钟为25MHz,生成的100MHz用于驱动MCU:
新建 gpio_ctrl.v 文件,用于控制gpio的输入或输出:
module gpio_ctrl(
input gpio_dir, //1'b0:input 1'b1:output
input gpio_out,
inout gpio, //对应fpga端口
output gpio_in
);
assign gpio = gpio_dir ? gpio_out:1'bz;
assign gpio_in = gpio; //当作为输入时,直接将输入值赋值给gpio_in
endmodule
新建 ps_wrapper.v 用于例化 gpio_ctrl 与 MCU 模块:
module ps_wrapper(
input wire clk, //MCU的100MHz时钟
input wire rst,
input wire timer_clk, //fpga的25MHz时钟
input wire jtag_tck,
output wire jtag_tdo,
input wire jtag_tms,
input wire jtag_tdi,
input wire uart_rx,
output wire uart_tx,
inout wire gpio_0,
inout wire gpio_1,
inout wire gpio_2,
inout wire gpio_3,
inout wire gpio_4
);
wire gpio0_out;
wire gpio1_out;
wire gpio2_out;
wire gpio3_out;
wire gpio4_out;
wire gpio0_dir;
wire gpio1_dir;
wire gpio2_dir;
wire gpio3_dir;
wire gpio4_dir;
wire gpio0_in;
wire gpio1_in;
wire gpio2_in;
wire gpio3_in;
wire gpio4_in;
gpio_ctrl u0_gpio_ctrl(
.gpio_dir(gpio0_dir),
.gpio_out(gpio0_out),
.gpio(gpio_0),
.gpio_in(gpio0_in)
);
gpio_ctrl u1_gpio_ctrl(
.gpio_dir(gpio1_dir),
.gpio_out(gpio1_out),
.gpio(gpio_1),
.gpio_in(gpio1_in)
);
gpio_ctrl u2_gpio_ctrl(
.gpio_dir(gpio2_dir),
.gpio_out(gpio2_out),
.gpio(gpio_2),
.gpio_in(gpio2_in)
);
gpio_ctrl u3_gpio_ctrl(
.gpio_dir(gpio3_dir),
.gpio_out(gpio3_out),
.gpio(gpio_3),
.gpio_in(gpio3_in)
);
gpio_ctrl u4_gpio_ctrl(
.gpio_dir(gpio4_dir),
.gpio_out(gpio4_out),
.gpio(gpio_4),
.gpio_in(gpio4_in)
);
MCU u_MCU(
.soft_ip_apbm_en (1'b0),
.qspi0cfg1_mode (1'b1),
.qspi0cfg2_mode (1'b1),
.jtag_tck (jtag_tck),
.jtag_tdo (jtag_tdo),
.jtag_tms (jtag_tms),
.jtag_tdi (jtag_tdi),
.apb_clk ( ),
.apb_paddr ( ),
.apb_pwrite ( ),
.apb_penable ( ),
.apb_pprot ( ),
.apb_pstrobe ( ),
.apb_psel ( ),
.apb_pwdata ( ),
.apb_prdata ( ),
.apb_pready ( ),
.apb_pslverr ( ),
.uart_tx (uart_tx),
.uart_rx (uart_rx),
.gpio0_out (gpio0_out),
.gpio0_dir (gpio0_dir),
.gpio0_in (gpio0_in ),
.gpio1_out (gpio1_out),
.gpio1_dir (gpio1_dir),
.gpio1_in (gpio1_in ),
.gpio2_out (gpio2_out),
.gpio2_dir (gpio2_dir),
.gpio2_in (gpio2_in ),
.gpio3_out (gpio3_out),
.gpio3_dir (gpio3_dir),
.gpio3_in (gpio3_in ),
.gpio4_out (gpio4_out),
.gpio4_dir (gpio4_dir),
.gpio4_in (gpio4_in ),
.core_clk (clk),
.timer_clk (timer_clk),
.core_reset (rst),
.mtip ( ),
.nmi ( ),
.clic_irq ( ),
.sysrstreq ( ),
.apb_clk_down ( ),
.apb_paddr_down ( ),
.apb_penable_down ( ),
.apb_pprot_down ( ),
.apb_prdata_down ( ),
.apb_pready_down ( ),
.apb_pslverr_down ( ),
.apb_pstrobe_down ( ),
.apb_pwdata_down ( ),
.apb_pwrite_down ( ),
.apb_psel0_down ( ),
.apb_psel1_down ( ),
.apb_psel2_down ( )
);
endmodule
新建 top.v ,用于例化 PLL 与 ps_wrapper 模块,需要注意的是,为了简化代码,我们只使用了4个GPIO:
module top(
input clk_25m,
input rst_n,
input jtag_tck,
input jtag_tms,
input jtag_tdi,
output jtag_tdo,
input uart_rx,
output uart_tx,
output oled_scl,
inout oled_sda,
output oled_rst_n,
output led
);
wire clk_100m;
wire rst;
assign rst = ~rst_n;
pll u_pll(
.refclk(clk_25m),
.reset(rst),
.clk0_out(clk_100m)
);
ps_wrapper u_ps_wrapper(
.clk(clk_100m), //MCU的100MHz时钟
.rst(rst),
.timer_clk(clk_25m), //fpga的25MHz时钟
.jtag_tck(jtag_tck),
.jtag_tdo(jtag_tdo),
.jtag_tms(jtag_tms),
.jtag_tdi(jtag_tdi),
.uart_rx(uart_rx),
.uart_tx(uart_tx),
.gpio_0(oled_scl),
.gpio_1(oled_sda),
.gpio_2(oled_rst_n),
.gpio_3(led)
);
endmodule
编译无误后添加管脚约束,之后重新编译综合,无误后 FPGA 部分的设置即完成:
set_pin_assignment { clk_25m } { LOCATION = D7; }
set_pin_assignment { jtag_tck } { LOCATION = C7; }
set_pin_assignment { jtag_tdi } { LOCATION = D5; }
set_pin_assignment { jtag_tdo } { LOCATION = C6; }
set_pin_assignment { jtag_tms } { LOCATION = D6; }
set_pin_assignment { led } { LOCATION = J5; }
set_pin_assignment { oled_rst_n } { LOCATION = H2; }
set_pin_assignment { oled_scl } { LOCATION = G4; }
set_pin_assignment { oled_sda } { LOCATION = J2; }
set_pin_assignment { rst_n } { LOCATION = H3; }
set_pin_assignment { uart_rx } { LOCATION = E4; }
set_pin_assignment { uart_tx } { LOCATION = A4; }
oled 的 scl 与 sda 引脚约束参考TN816:
下面进行 MCU 部分的设置,DEMO板上使用的是0.91寸 128*32 的 oled ,官方资料中没有讲述 oled 的具体型号,不过从官方例程来看,应该与0.91寸的SSD1306相同。程序编写步骤为:
主函数如下,需要注意的是,oled 的复位功能我们使用的是 DEMO上的 KEY1,但是main函数中我们将anlogic_log_display函数放在while循环外面,所以复位功能并未使用到,感兴趣的可以修改程序:
#include "stdio.h"
#include "nuclei_sdk_hal.h"
#include "iic.h"
int main(void)
{
anlogic_log_display();
while(1)
{
LED_High();
display_horizontal_scroll_enable();
delay_1ms(2270);
LED_Low();
display_horizontal_scroll_disable();
delay_1ms(4000);
}
return 0;
}
iic.c如下:
#include "iic.h"
#include "nuclei.h"
#include "gpio_ctrl.h"
//========================IIC协议操作===========================
//拉高SCL,GPIO 0
void SCL_High(void)
{
gpio_wr(0,1);
}
//拉低SCL,GPIO 0
void SCL_Low(void)
{
gpio_wr(0,0);
}
//SDA拉低,GPIO 1
void SDA_Low(void)
{
gpio_wr(1,0);
}
//SDA拉高,GPIO 1
void SDA_High(void)
{
gpio_wr(1,1);
}
//SDA读取数据,GPIO 1
int SDA_read(void)
{
int temp;
temp = gpio_rd(1);
if(temp == 1)
return 1;
else
return 0;
}
//LED输出高,GPIO 3
void LED_High(void)
{
gpio_wr(3,1);
}
//LED输出低,GPIO 3
void LED_Low(void)
{
gpio_wr(3,0);
}
//延时1us
void delay1us(void)
{
delay_1us(1);
}
void IIC_START(void)
{
SCL_Low(); // SCL拉低 防止可能出现的各种误动作
delay1us();
SDA_High(); // SDA拉高
SCL_High(); // SCL拉高 准备发出起始信号
delay1us();
SDA_Low(); // SDA拉低 发出起始信号
SCL_Low(); // SCL拉低 开始传输
}
void IIC_STOP(void)
{
SCL_Low(); // SCL拉低 防止可能出现的各种误动作
SDA_Low(); // SDA拉低
delay1us();
SCL_High(); // SCL拉高 准备发出结束信号
delay1us();
SDA_High(); // SDA拉高 发出结束信号
}
int IIC_WaitACK(void)
{
int s;
SCL_Low(); // 拉低SCL
delay1us();
SDA_High(); // 拉高SDA 主机释放总线
delay1us();
SCL_High(); // 拉高SCL
delay1us();
s = SDA_read(); // 采集SDA信号线状态
delay1us();
SCL_Low(); // 拉低SCL 结束询问ACK
if(s)
return 0; // 无应答(ACK)
else
return 1; // 有应答(ACK)
}
void IIC_Write(int dat)
{
int i;
int temp;
for(i=0; i<8; i++)
{
temp = dat & 0x80;
if(temp == 0x80)
SDA_High();
else
SDA_Low();
dat <<= 1; // 数据格式:高位在前
delay1us();
SCL_High(); // 拉高SCL 发送数据
delay1us();
SCL_Low(); // 拉低SCL 结束发送
}
}
//=============================OLED驱动设置=============================
void oled_display_reset() //显示复位
{
gpio_wr(2,0); //GPIO 2,即 oled_rst_n
delay_1us(10000);
gpio_wr(2,1);
delay_1us(10000);
}
void OLED_Write_cmd(int cmd) //写入命令
{
IIC_START();
IIC_Write(0X78); // 写从机地址'0111 100' 读写符号'0'
IIC_WaitACK();
IIC_Write(0X00); // 写命令
IIC_WaitACK();
IIC_Write(cmd);
IIC_WaitACK();
}
void OLED_Write_dat(int dat) //写数据
{
IIC_START(); // 通信开始
IIC_Write(0X78); // 写从机地址'0111 100' 读写符号'0'
IIC_WaitACK();
IIC_Write(0X40); // 写数据
IIC_WaitACK();
IIC_Write(dat);
IIC_WaitACK();
}
void OLED_Init(void) //OLED初始化
{
oled_display_reset();
OLED_Write_cmd(0XAE); // 关OLED显示
// 基础设置
OLED_Write_cmd(0XA4); // 输出GDDRAM内容
OLED_Write_cmd(0XA6); // 正常显示(1亮0灭)
OLED_Write_cmd(0X81); // 设置对比度
OLED_Write_cmd(0X7F); // 第127级对比度
// COM和SEG输出设置
OLED_Write_cmd(0XD3); // 设置垂直显示偏移(向上)
OLED_Write_cmd(0X00); // 偏移0行
OLED_Write_cmd(0X40); // 设置GDDRAM起始行 0
OLED_Write_cmd(0XA8); // 设置MUX数 (显示行数)
OLED_Write_cmd(0X3F); // MUX=63 (显示63行)
OLED_Write_cmd(0XA1); // 左右反置关(段重映射)
OLED_Write_cmd(0XC8); // 上下反置关(行重映射)
OLED_Write_cmd(0XDA); // 设置COM引脚配置
OLED_Write_cmd(0X02); // 序列COM配置,禁用左右反置
// 时钟设置
OLED_Write_cmd(0XD5); // 设置DCLK分频和OSC频率
OLED_Write_cmd(0X80); // 无分频,第8级OSC频率
// 开OLED
OLED_Write_cmd(0X8D); // 启用电荷泵
OLED_Write_cmd(0X14); // 启用电荷泵
OLED_Write_cmd(0XAF); // 开OLED显示
IIC_STOP();
}
void OLED_Clear(void) //清屏
{
int i,j;
OLED_Write_cmd(0X00); // 水平寻址模式
OLED_Write_cmd(0X21); // 设置列起始和结束地址
OLED_Write_cmd(0X00); // 列起始地址 0
OLED_Write_cmd(0X7F); // 列终止地址 127
OLED_Write_cmd(0X22); // 设置页起始和结束地址
OLED_Write_cmd(0X00); // 页起始地址 0
OLED_Write_cmd(0X07); // 页终止地址 7
for(i=0; i<8; i++) // 写入一帧'0'
for(j=0; j<128; j++)
OLED_Write_dat(0X00);
IIC_STOP();
}
void OLED_Frame(int P[8][128]) //写入图像数据
{
int i,j;
OLED_Write_cmd(0X20); // 设置GDDRAM模式
OLED_Write_cmd(0X00); // 水平寻址模式
OLED_Write_cmd(0X21); // 设置列起始和结束地址
OLED_Write_cmd(0X00); // 列起始地址 0
OLED_Write_cmd(0X7F); // 列终止地址 127
OLED_Write_cmd(0X22); // 设置页起始和结束地址
OLED_Write_cmd(0X00); // 页起始地址 0
OLED_Write_cmd(0X07); // 页终止地址 7
for(i=0; i<8; i++) // 写入一帧数据
{
for(j=0; j<128; j++)
{
OLED_Write_dat(P[i][j]);
IIC_STOP();
}
}
IIC_STOP();
}
void display_horizontal_scroll_enable()
{
OLED_Write_cmd(0x2f);//开启滚动
}
void display_horizontal_scroll_disable()
{
OLED_Write_cmd(0x2e);//关闭滚动
}
void anlogic_log_display(void)
{
int index=0;
int oled_display_data[8][128];
int anlogic_log_data[512] = {0x04, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x07, 0xc0, 0x00, 0x00, 0x07, 0x80, 0x00, 0x00,
0x0f, 0x80, 0x00, 0x00, 0x0f, 0x80, 0x00, 0xf0, 0x0f, 0x80, 0x00, 0xf8, 0x0f, 0x80, 0x00, 0x78,
0x07, 0x80, 0x00, 0x78, 0x07, 0xc0, 0x00, 0x78, 0x07, 0xc0, 0x00, 0x78, 0x07, 0xc0, 0x00, 0x78,
0x03, 0xe0, 0x00, 0x78, 0x03, 0xf0, 0x00, 0xf8, 0x01, 0xf8, 0x00, 0xf8, 0x00, 0xfc, 0x01, 0xf8,
0x00, 0x7f, 0xc7, 0xf0, 0x00, 0x3f, 0xff, 0xf0, 0x00, 0x1f, 0xff, 0xe0, 0x00, 0x0f, 0xff, 0xc0,
0x00, 0x03, 0xff, 0x80, 0x07, 0x00, 0x18, 0x00, 0x07, 0xf0, 0x00, 0x00, 0x07, 0xff, 0x00, 0x00,
0x07, 0xff, 0xf8, 0x00, 0x07, 0xff, 0xff, 0xc0, 0x01, 0xff, 0xff, 0xf8, 0x00, 0x0f, 0xff, 0xf8,
0x00, 0x00, 0x7f, 0xf8, 0x00, 0x00, 0x07, 0xf8, 0x00, 0x00, 0x00, 0x38, 0x07, 0x00, 0x00, 0x00,
0x07, 0xc1, 0xe0, 0x00, 0x07, 0xc0, 0xff, 0x00, 0x07, 0x80, 0xff, 0xf0, 0x07, 0x80, 0xff, 0xf0,
0x07, 0x80, 0x7f, 0xf0, 0x07, 0x80, 0x71, 0xf8, 0x07, 0x80, 0x20, 0xf8, 0x07, 0x80, 0x20, 0x78,
0x07, 0x80, 0x00, 0x78, 0x07, 0xc0, 0x00, 0x78, 0x03, 0xc0, 0x00, 0x78, 0x03, 0xe0, 0x00, 0xf8,
0x03, 0xf0, 0x00, 0xf8, 0x01, 0xf8, 0x00, 0xf8, 0x00, 0xfc, 0x01, 0xf8, 0x00, 0x7f, 0x87, 0xf0,
0x00, 0x3f, 0xff, 0xf0, 0x00, 0x1f, 0xff, 0xe0, 0x00, 0x0f, 0xff, 0xc0, 0x00, 0x03, 0xff, 0x80,
0x00, 0x00, 0x7f, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x7f, 0xe0, 0x00, 0x00, 0xff, 0xf8, 0x00,
0x01, 0xff, 0xfe, 0x00, 0x03, 0xff, 0xff, 0x00, 0x07, 0xff, 0xff, 0xc0, 0x07, 0xf0, 0x3f, 0xe0,
0x0f, 0xc0, 0x0f, 0xe0, 0x0f, 0x80, 0x03, 0xf0, 0x0f, 0x80, 0x01, 0xf0, 0x0f, 0x80, 0x00, 0xf8,
0x0f, 0x80, 0x00, 0x78, 0x07, 0x80, 0x00, 0x78, 0x07, 0x80, 0x00, 0x78, 0x07, 0xc0, 0x00, 0x78,
0x03, 0xe0, 0x00, 0x78, 0x03, 0xf8, 0x00, 0xf8, 0x01, 0xfe, 0x01, 0xf8, 0x00, 0xff, 0x07, 0xf8,
0x00, 0x7f, 0xff, 0xf8, 0x00, 0x3f, 0xff, 0xf0, 0x00, 0x1f, 0xff, 0xe0, 0x00, 0x07, 0xff, 0xc0,
0x00, 0x01, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x78,
0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x78, 0x07, 0x00, 0x00, 0x78,
0x07, 0xf0, 0x00, 0x78, 0x07, 0xff, 0x80, 0x78, 0x07, 0xff, 0xf8, 0x78, 0x07, 0xff, 0xff, 0xf8,
0x07, 0xff, 0xff, 0xf8, 0x00, 0x1f, 0xff, 0xf8, 0x00, 0x01, 0xff, 0xf8, 0x00, 0x00, 0x0f, 0xf8,
0x08, 0x00, 0x00, 0x78, 0x08, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x09, 0x20, 0x00, 0x00,
0x09, 0x24, 0x00, 0x00, 0x09, 0x24, 0x00, 0x00, 0x09, 0x24, 0x80, 0x00, 0x09, 0x24, 0x90, 0x00,
0x09, 0x24, 0x90, 0x00, 0x09, 0x24, 0x94, 0x00, 0x09, 0x24, 0x94, 0x80, 0x09, 0x24, 0x94, 0x90,
0x09, 0x24, 0x94, 0x80, 0x09, 0x24, 0x94, 0x30, 0x09, 0x24, 0x90, 0xf0, 0x09, 0x24, 0x93, 0xf0,
0x09, 0x24, 0x8f, 0xf0, 0x09, 0x24, 0x3f, 0xf0, 0x09, 0x24, 0xff, 0xe0, 0x09, 0x23, 0xff, 0x80,
0x09, 0x0f, 0xfe, 0x00, 0x08, 0x3f, 0xf8, 0x10, 0x08, 0xff, 0xe0, 0x70, 0x03, 0xff, 0x80, 0xf0,
0x07, 0xff, 0x00, 0xf0, 0x03, 0xff, 0xc0, 0xf0, 0x00, 0x7f, 0xf0, 0xf0, 0x00, 0x1f, 0xfc, 0xf0,
0x00, 0x07, 0xff, 0xf0, 0x00, 0x01, 0xff, 0xf0, 0x00, 0x00, 0x7f, 0xf0, 0x00, 0x00, 0x1f, 0xf0,
0x00, 0x00, 0x07, 0xf0, 0x00, 0x00, 0x01, 0xf0, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x10};
//图像数据映射为SSD1306的显示数据
for(int j=0; j<128; j++)
{
for(int i=7; i>=0; i--)
{
if(i > 3)
{
oled_display_data[i][j] = anlogic_log_data[index];
index++;
}
else
oled_display_data[i][j] = 0x00;
}
}
OLED_Init(); //初始化
OLED_Clear(); //清屏
OLED_Write_cmd(0x2e);//关闭滚动
OLED_Frame(oled_display_data); //写入图像
OLED_Write_cmd(0x27); //水平向左或者右滚动 26/27
OLED_Write_cmd(0x00); //虚拟字节
OLED_Write_cmd(0x00); //起始页 0
OLED_Write_cmd(0x07); //滚动时间间隔
OLED_Write_cmd(0x07); //终止页 7
OLED_Write_cmd(0x00); //虚拟字节
OLED_Write_cmd(0xff); //虚拟字节
OLED_Write_cmd(0x2f); //开启滚动
}
需要注意的是,在iic.c文件中使用到了delay_1ms与delay_1us函数,头文件nuclei.h中只存在delay_1ms,我们需进入nuclei_common.c中添加delay_1us函数,并在nuclei.h声明,delay_1us函数参考的是官方例程。
gpio_ctrl.c用于控制4个gpio输入输出:
#include "gpio_ctrl.h"
#include "global.h"
void gpio_wr(int index,int wr_data) //GPIO输出
{
switch(index)
{
case 0: //GPIO 0,即 scl 控制
gpio_in_en = gpio_in_en & 0xfffffffe; //IEN寄存器值共32位,GPIO 0为第0位,输入不使能
if(wr_data == 1)
gpio_out = gpio_out | 0x00000001; //OVAL寄存器值共32位,GPIO 0为第0位,输出1
else
gpio_out = gpio_out & 0xfffffffe; //输出0
gpio_out_en = gpio_out_en | 0x00000001; //OEN寄存器值共32位,GPIO 0为第0位,输出使能
break;
case 1: //GPIO 1,即 sda 控制
gpio_in_en = gpio_in_en & 0xfffffffd; //IEN寄存器值共32位,GPIO 1为第1位,输入不使能
if(wr_data == 1)
gpio_out = gpio_out | 0x00000002; //OVAL寄存器值共32位,GPIO 1为第1位,输出1
else
gpio_out = gpio_out & 0xfffffffd; //输出0
gpio_out_en = gpio_out_en | 0x00000002; //OEN寄存器值共32位,GPIO 1为第1位,输出使能
break;
case 2: //GPIO 2,即 oled_rst_n 控制
gpio_in_en = gpio_in_en & 0xfffffffb;
if(wr_data == 1)
gpio_out = gpio_out | 0x00000004;
else
gpio_out = gpio_out & 0xfffffffb;
gpio_out_en = gpio_out_en | 0x00000004;
break;
case 3: //GPIO 3,即 led 控制
gpio_in_en = gpio_in_en & 0xfffffff7;
if(wr_data == 1)
gpio_out = gpio_out | 0x00000008;
else
gpio_out = gpio_out & 0xfffffff7;
gpio_out_en = gpio_out_en | 0x00000008;
break;
case 4: //GPIO 4,未使用
gpio_in_en = gpio_in_en & 0xffffffef;
if(wr_data == 1)
gpio_out = gpio_out | 0x00000010;
else
gpio_out = gpio_out & 0xffffffef;
gpio_out_en = gpio_out_en | 0x00000010;
break;
case 5: //GPIO 5,未使用
gpio_in_en = gpio_in_en & 0xffffffdf;
if(wr_data == 1)
gpio_out = gpio_out | 0x00000020;
else
gpio_out = gpio_out & 0xffffffdf;
gpio_out_en = gpio_out_en | 0x00000020;
break;
case 6: //GPIO 6,未使用
gpio_in_en = gpio_in_en & 0xffffffbf;
if(wr_data == 1)
gpio_out = gpio_out | 0x00000040;
else
gpio_out = gpio_out & 0xffffffbf;
gpio_out_en = gpio_out_en | 0x00000040;
break;
case 7: //GPIO 7,未使用
gpio_in_en = gpio_in_en & 0xffffff7f;
if(wr_data == 1)
gpio_out = gpio_out | 0x00000080;
else
gpio_out = gpio_out & 0xffffff7f;
gpio_out_en = gpio_out_en | 0x00000080;
break;
default:
break;
}
}
int gpio_rd(int index) //GPIO输入
{
int temp;
switch(index)
{
case 0: //GPIO 0,即 scl 控制
gpio_in_en = gpio_in_en | 0x00000001; //IEN寄存器值共32位,GPIO 0为第0位,输入使能
gpio_out_en = gpio_out_en & 0xfffffffe; //OEN寄存器值共32位,GPIO 0为第0位,输出不使能
temp = gpio_in;
temp = temp & 0x00000001;
if(temp == 0x00000001)
return 1;
else
return 0;
break;
case 1: //GPIO 1,即 sda 控制
gpio_in_en = gpio_in_en | 0x00000002;
gpio_out_en = gpio_out_en & 0xfffffffd;
temp = gpio_in;
temp = temp & 0x00000002;
if(temp == 0x00000002)
return 1;
else
return 0;
break;
case 2: //GPIO 2,即 oled_rst_n 控制
gpio_in_en = gpio_in_en | 0x00000004;
gpio_out_en = gpio_out_en & 0xfffffffb;
temp = gpio_in;
temp = temp & 0x00000004;
if(temp == 0x00000004)
return 1;
else
return 0;
break;
case 3: //GPIO 1,即 led 控制
gpio_in_en = gpio_in_en | 0x00000008;
gpio_out_en = gpio_out_en & 0xfffffff7;
temp = gpio_in;
temp = temp & 0x00000008;
if(temp == 0x00000008)
return 1;
else
return 0;
break;
case 4:
gpio_in_en = gpio_in_en | 0x00000010;
gpio_out_en = gpio_out_en & 0xffffffef;
temp = gpio_in;
temp = temp & 0x00000010;
if(temp == 0x00000010)
return 1;
else
return 0;
break;
case 5:
gpio_in_en = gpio_in_en | 0x00000020;
gpio_out_en = gpio_out_en & 0xffffffdf;
temp = gpio_in;
temp = temp & 0x00000020;
if(temp == 0x00000020)
return 1;
else
return 0;
break;
case 6:
gpio_in_en = gpio_in_en | 0x00000040;
gpio_out_en = gpio_out_en & 0xffffffbf;
temp = gpio_in;
temp = temp & 0x00000040;
if(temp == 0x00000040)
return 1;
else
return 0;
break;
case 7:
gpio_in_en = gpio_in_en | 0x00000080;
gpio_out_en = gpio_out_en & 0xffffff7f;
temp = gpio_in;
temp = temp & 0x00000080;
if(temp == 0x00000080)
return 1;
else
return 0;
break;
}
return 0;
}
其中global.h文件中声明了对 IVAL、 IEN、 OEN、 OVAL 四个寄存器的地址,将其单独拿出来声明是为了防止出现多重定义的警告,GPIO器件地址在官方资料中没有找到,在官方给的例程中可以找到,IVAL、 IEN、 OEN、 OVAL 四个寄存器的地址及使用方法在TN817中有介绍:
于是global.h文件中定义如下:
#ifndef APPLICATION_GLOBAL_H_
#define APPLICATION_GLOBAL_H_
#define GPIO_BASE_ADDRESS 0xE0020000
long unsigned int *GPIO_IVAL = GPIO_BASE_ADDRESS + 0x00000000;
long unsigned int *GPIO_IEN = GPIO_BASE_ADDRESS + 0x00000004;
long unsigned int *GPIO_OEN = GPIO_BASE_ADDRESS + 0x00000008;
long unsigned int *GPIO_OVAL = GPIO_BASE_ADDRESS + 0x0000000c;
#define gpio_in *GPIO_IVAL
#define gpio_in_en *GPIO_IEN
#define gpio_out_en *GPIO_OEN
#define gpio_out *GPIO_OVAL
#endif /* APPLICATION_GLOBAL_H_ */
编译无误后 FD 部分完成,我们打开 TD 下载 bit 与 hex 文件至芯片中,下载方法在前几期中均已讲述,本期测评的下载方式与之前一致:
显示效果是安路logo按一定时间滚动,并且在滚动时LEDB亮,不滚动时LEDB熄灭。
源码在上文中均已给出,相关注释也已加上,需要的可以参考。
对于 oled 驱动不熟悉的可以参考0.91寸的SSD1306 ,需要注意的是 DEMO 板上标示的 oled SDA是J4,而资料中给的是J2,这里是板子上标错了。
与同行测评的一篇 oled 对比来看,本测评中并未添加时钟约束,而 FPGA JTAG 与 MCU JTAG 均能连接,产生区别的原因尚未找到。
|