1,基本原理:
1.1图像旋转的基本公式:
在笛卡尔坐标系中,以图像中心原点O,向右为x轴正方向,向上为y轴正方向,图像中任意一点(x0,y0),以图像的中心为圆心顺时针旋转a角后的坐标变换为(x,y),则有:
1.2,HLS的处理过程:
裸核与逻辑之间的AXI4接口,可以实现裸核,逻辑,DDR之间的大量数据交互工作。我们可以规定一个DDR的初始地址(本文使用0x1000000),然后将需要转化的图像,连同操作需要的参数sina、cosa、偏移x、偏移y等一起存入对应的内存中。然后ARM使用GPIO接口PS_LED启动IP核开始计算工作。接下来,等到IP核完成计算,再通过PL_KEY0产生EMIO中断通知ARM可以读取旋转好的图像数据。
系统框图如下:
2,HLS部分的修改总结
打开Vivado HLS 2018.3
->选择OpenExampleProject
->选择axi_master /Next/保存到D:\work\AXI_GaohuiNot1\axi_master
修改C代码如下:
//在头文件中定义
#define pi 3.1405926
#define IMAGE_W 864
#define IMAGE_H 450
#define RImg_W 720
#define RImg_H 320
void example(unsigned char *imgdata)
{
#pragma HLS INTERFACE m_axi port=imgdata depth=864*450*2
int px =imgdata[1]*256+imgdata[0];
int py =imgdata[3]*256+imgdata[2];
int MoneyW =imgdata[5]*256+imgdata[4];
int MoneyH =imgdata[7]*256+imgdata[6];
int sina =imgdata[9]*256+imgdata[8];
int cosa =imgdata[11]*256+imgdata[10];
int k,x,y;
int x0,y0;
int Mx=(px-RImg_W/2);//347,161
int My=(py-RImg_H/2);
for(y=0;y<IMAGE_H;y+=2)
for(x=0;x<IMAGE_W;x+=2)
{
if( ((x-Mx-RImg_W/2 )<MoneyW/2)
&&((RImg_W/2 -(x-Mx) )<MoneyW/2)
&&((y-My-RImg_H/2)<MoneyH/2)
&&((-y+My+RImg_H/2)<MoneyH/2)
)
{
x0=(int)((x-px)*cosa/1000-(y-py)*sina/1000)+px;//
y0 = (x)*sina/1000-(px)*sina/1000;
y0 += py + (y)*cosa/1000-(py)*cosa/1000;
y0 *= IMAGE_W;
y0 += x0;
imgdata[(RImg_W/2)*((y-My)/2)+(x-Mx)/2+IMAGE_H*IMAGE_W]=imgdata[y0];
}
}
}
修改Solution Setting(小齿轮)
Synthesys->时钟150MHz
Part:xc7z010clg400-1
将example.cpp,example_test.cpp改为合适的代码
(怎么改,后边会单独介绍)
->Run C Simulation/(小绿三角)结果正确
->Run C Synthesis/(大绿三角)结果正确
将电脑系统日期改为2000年
->Export RTL/(田字格)结果正确
修改IP名称例如Description:HLS20221218_001
2 Vivado部分
打开Vivado 2018.3
->Creat Project->修改项目名->Part:xc7z010clg400-1
->Creat Block Design->AddIP 选择zynq7
DDR Control Confifuration器件 7010 :MT41J128M16HA-125
Peripheral I/O Pins :UART0 设置14与15脚
MIO :Bank1电压LVCMOS1.8V
PS/PL->AXI Non->GP Master :关闭M AXI GP0
->General->Enable Clk:FCLK_RESET0_N:开(默认就是开)
->HP Slave AXI :S AXI HP0 打开
Clock->PL->FCLK CLK0 : 150 (单位是M, 最大150M,要和IP一致)
添加4个EMIO(1个输出,1个输入)
输出1:HLS开始 裸核输出 D20 (L20为Led)
输入2:HLS结束输入中断 M18 (J18为Key)
原来的{输入1:HLS开始 逻辑输入 [J18]}
原来的{输出2:HLS结束输出 [J20]}
MIO GPIO EMIO_GPIO 打钩
位宽Width2
(引脚make external)
添加IP核
拷贝IP目录到Vivado目录
Setting->IP->Repository(仓库)->选中添加的IP
+->hls IP
->Run Connection 更新
->管脚 Make External
->修改IP的 基地址0x10000000
Data width:32 (其他设置会异常)
逻辑框图如下:
生成顶层模板包装 Creat HDL Wrapper
生成输出 Generate Output Products->2 Out of context per IP
分配管脚
set_property IOSTANDARD LVCMOS33 [get_ports ap_ctrl_0_done]
set_property IOSTANDARD LVCMOS33 [get_ports ap_ctrl_0_idle]
set_property IOSTANDARD LVCMOS33 [get_ports ap_ctrl_0_ready]
set_property IOSTANDARD LVCMOS33 [get_ports ap_ctrl_0_start]
set_property PACKAGE_PIN J20 [get_ports ap_ctrl_0_start]
set_property PACKAGE_PIN M18 [get_ports ap_ctrl_0_done]
set_property PACKAGE_PIN H18 [get_ports ap_ctrl_0_idle]
set_property PACKAGE_PIN H16 [get_ports ap_ctrl_0_ready]
set_property IOSTANDARD LVCMOS33 [get_ports {GPIO_0_0_tri_io[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {GPIO_0_0_tri_io[1]}]
set_property PACKAGE_PIN T14 [get_ports {GPIO_0_0_tri_io[0]}]
set_property PACKAGE_PIN J18 [get_ports {GPIO_0_0_tri_io[1]}]
生成Bit
导出硬件Export Hardware:注意要包含bit
Launch SDK
3, SDK部分
1,使用EMIO按键中断的代码
2,准备数据,输出数据的内容
3,start信号0->1->0;
4,等待中断,当有中断后输出结果
int main()
{
//中断初始化等
...
Xil_DCacheDisable();
//准备数据
//对图像进行旋转
int m_Angle=-15;
double angle=-m_Angle*pi/180;//15度
double cosa=0.990;//0.965;//cos(angle);
double sina=0.139;//0.258;//sin(angle);
int m_CentX =360;//360;
int m_CentY =160;//160;
int m_Width =600;
int m_Height=300;
//=====================
memset(imgdata2,0,12);
imgdata2[0] = m_CentX%256;
imgdata2[1] = m_CentX/256;
imgdata2[2] = m_CentY%256;
imgdata2[3] = m_CentY/256;
imgdata2[4] = m_Width%256;
imgdata2[5] = m_Width/256;
imgdata2[6] = m_Height%256;
imgdata2[7] = m_Height/256;
imgdata2[8] = (int)((int)(sina*1000)%256);
imgdata2[9] = (int)((int)(sina*1000)/256);
imgdata2[10]= (int)((int)(cosa*1000)%256);
imgdata2[11]= (int)((int)(cosa*1000)/256);
for(i=0;i<IMAGE_W*IMAGE_H;i++)
{
Xil_Out8(0x10000000+i, imgdata2[i]);
}
printf("start\r\n");
//等待中断
key_val = 1;
XGpioPs_WritePin(&gpio, LED, key_val);//输出LED灯效果
usleep(20);
key_val = 0;
XGpioPs_WritePin(&gpio, LED, key_val);//输出LED灯效果
//中断触发时,key_press为TURE,延时一段时间后判断按键是否按下,是则反转LED
while (1)
{
if (key_press)
{
usleep(100);
if (XGpioPs_ReadPin(&gpio, KEY) == 0)
{
//打印图片
for(j=0;j<RImg_H/2;j++)//RImg_H/2
{
for(i=0;i<RImg_W/2;i++)
{ printf("%d,",(int)( Xil_In8(0x10000000+IMAGE_W*IMAGE_H+j*RImg_W/2+i)));
}
printf("\r\n");
}
}
key_press = FALSE;
XGpioPs_IntrClearPin(&gpio, KEY); //清除按键KEY中断
XGpioPs_IntrEnablePin(&gpio, KEY); //使能按键KEY中断
}
}
return XST_SUCCESS;
}
时间测试运行时间约100um(还要进一步测试)
运行效果
原图:
输出数据:
结论:
通过HLS制作图像旋转的IP核可以将图像旋转功能实现,同时也可以满足速度要求。
参考书目:
领航者ZYNQ开发板视频盘(D盘) 65_AXI4读写DDR测试实验
领航者ZYNQ开发板视频盘(D盘) 13_GPIO之EMIO按键控制LED实验_程序设计
哔站:Vivado HLS 基本应用与图像处理