一、前言
在之前的文章中介绍了《板级调试小助手》的系统结构和DDS自定义外设的搭建。这篇文章主要介绍一下如何在PYNQ中驱动平时长剑的OLED12864显示屏,并显示BadApple(毕竟有屏幕的地方就要有BadApple)。效果如下:
BadApple
本项目完全开源,开源地址请查看《板级调试小助手(1)系统结构和原理》文章最后
二、OLED驱动设计
OLED显示屏使用了IIC协议,为了能方便PS端控制,我们需要将其设计成自动读取BRAM中的数据,并显示,OLED驱动的顶层如下所示。
cpp
module oled_top #
(
parameter SLAVE_ADDR = 7'b0111100, //从机地址
parameter CLK_FREQ = 26'd100_000_000, //系统时钟(Hz)
parameter I2C_FREQ = 19'd350_000, //IIC频率
parameter BIT_CTRL = 1'b0 //字地址位控制(16b/8b)
)
(
input sysClk, //系统时钟
input sysRst, //全局复位
//RAM数据接口
output [9:0]rd_addr, //读RAM地址
input [7:0]ram_rd_data, //读RAM数据
//IIC接口
output oled_scl,
inout oled_sda
);
wire i2c_dir_clk ;
wire i2c_exec ;
wire [15:0] i2c_data ;
wire i2c_done ;
i2c_dri #(
.SLAVE_ADDR (SLAVE_ADDR ) ,
.CLK_FREQ (CLK_FREQ ) ,
.I2C_FREQ (I2C_FREQ )
)
i2c_dri_u (
//global clock
.clk (sysClk ),
.rst_n (~sysRst ),
.i2c_exec (i2c_exec ),
.bit_ctrl (BIT_CTRL ),
.i2c_rh_wl (1'b0 ),
.i2c_addr (i2c_data[15:8]),
.i2c_data_w (i2c_data[7:0] ),
.i2c_data_r ( ),
.i2c_done (i2c_done ),
.scl (oled_scl ),
.sda (oled_sda ),
.dri_clk (i2c_dir_clk )
);
oled_ctrl oled_ctrl_u(
.I_sys_clk (i2c_dir_clk),
.I_reset_n (~sysRst ),
.I_i2c_done (i2c_done ),
.O_i2c_data (i2c_data ),
.O_i2c_exec (i2c_exec ),
.O_rd_addr (rd_addr ),
.I_ram_rd_data(ram_rd_data)
);
endmodule
可以看到顶层代码中,除了IIC接口,也具有一个RAM数据读接口。 i2c_dri 模块是OLED驱动模块,oled_ctrl模块用于读取BRAM数据写入OLED的控制模块,具体代码这里就不展示了,需要请移步第一章最后的开源地址。
三、PS端的python代码处理
PL端设计好之后PS端就很好设计的,主要分为一下几个步骤:
1、调用OpenCV库读取.mp4文件,逐帧解析;
2、将解析后的视频帧转换成OLED可以显示的数据;
3、将数据通过AXI总线写入BRAM
PS端代码如下:
python
#显示开机动画
video_path = '123.mp4';
cap = cv2.VideoCapture(video_path); #创建VideoCapture对象
while True:
ret, frame = cap.read() #读一帧数据
# 如果正确读取帧,ret为True
if not ret:
print("Error: No more frames to read.")
break
gray_image = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
_, binary_image = cv2.threshold(gray_image, 170, 255, cv2.THRESH_BINARY)
#变成numpy可以处理的数组
numpy_image = np.array(binary_image)
#获取转换后的数据
re_Addr,re_Data = oled.oled_show(numpy_image)
#写入oled显示缓存
for i in range(0, 256):
bram_ip.write(re_Addr[i],re_Data[i])