平台:pynq z2
发现没有针对PYNQ的,或者过程并不适合,开个头。
PS端和PL端是通过AXI总线通信。
调用gpio核,实现由arm读取按键的输入,再将按键值送到led
分为几步
思路就是,把arm和fpga的接口通过gpio连起来,形成硬件结构。
step 1:block design调用zynq核
step 2:这里用两个gpio核,分别作为button和led输入输出,设置位宽4
step 3:检查一下地址映射,保证调用软件可以找到gpio的reg
step 4:自动连接,wrap HDL生成顶层的verilog,output hardware product
step 5:xdc约束管脚
step 6:export hardware,并且打开sdk,不用包含bitstream ,到时候再烧进去
step 7:让硬先件生成着bitstream,同步进行sdk设计
思路就是,根据gpio手册,用arm寻址,写数据
step 1 :新建project
step 2 :找到gpio 三态门的tri地址,设置两个gpio的输入、输出,
step 3 :找到gpio数据的地址,设置输入输出数值
step 4 :烧写bitstream,亮灯。
over.
方便起见,不用裁剪,出来啥用啥。
分别作为button和led输入输出,设置位宽4,
ps一共32bit,只用了4个
这里有个坑,默认的tri state value没法修改,输入输出都是0xffff_ffff,是不对的。所以在软件中需要修改。
自动连线后,增加了axi-interconnect 和100Mclk\rst系统
看一眼地址 ,保证调用软件可以找到gpio的reg
只用button led,注意名字用顶层verilog中的输入输出
##LEDs
set_property -dict { PACKAGE_PIN R14 IOSTANDARD LVCMOS33 } [get_ports { led_tri_o[0] }]; #IO_L6N_T0_VREF_34 Sch=led[0]
set_property -dict { PACKAGE_PIN P14 IOSTANDARD LVCMOS33 } [get_ports { led_tri_o[1] }]; #IO_L6P_T0_34 Sch=led[1]
set_property -dict { PACKAGE_PIN N16 IOSTANDARD LVCMOS33 } [get_ports { led_tri_o[2] }]; #IO_L21N_T3_DQS_AD14N_35 Sch=led[2]
set_property -dict { PACKAGE_PIN M14 IOSTANDARD LVCMOS33 } [get_ports { led_tri_o[3] }]; #IO_L23P_T3_35 Sch=led[3]
##Buttons
set_property -dict { PACKAGE_PIN D19 IOSTANDARD LVCMOS33 } [get_ports { sw_tri_i[0] }]; #IO_L4P_T0_35 Sch=btn[0]
set_property -dict { PACKAGE_PIN D20 IOSTANDARD LVCMOS33 } [get_ports { sw_tri_i[1] }]; #IO_L4N_T0_35 Sch=btn[1]
set_property -dict { PACKAGE_PIN L20 IOSTANDARD LVCMOS33 } [get_ports { sw_tri_i[2] }]; #IO_L9N_T1_DQS_AD3N_35 Sch=btn[2]
set_property -dict { PACKAGE_PIN L19 IOSTANDARD LVCMOS33 } [get_ports { sw_tri_i[3] }]; #IO_L9P_T1_DQS_AD3P_35 Sch=btn[3]
不用包含bitstream ,到时候再烧进去
同步进行sdk设计
思路就是,根据gpio手册,用arm寻址,写数据
没用到中断,gpio都是一个channel,不存在gpiox_的x
以上, 手册用完。
可以建pheriperial test或者helloword 用一下他的头文件。
官方api 其实用了很多宏定义,可以参考
https://blog.csdn.net/tangkunjyy/article/details/62038253?depth_1-utm_source=distribute.pc_relevant.none-task&utm_source=distribute.pc_relevant.none-task
官方为了规范,比较啰嗦,层层套娃,做的无非就是以下两步。
根据手册,channel1 ,需要偏移4,base_addr+b’100
#define tri_in *(volatile u32 *) 0x41210100
#define tri_out *(volatile u32 *) 0x41200100
根据手册,就是基地址
#define sw_in *(volatile u32 *) 0x41210000
#define led_out *(volatile u32 *) 0x41200000
#include
#include "xparameters.h"
#include "xgpio.h"
#define tri_in *(volatile u32 *) 0x41210100
#define tri_out *(volatile u32 *) 0x41200100
#define sw_in *(volatile u32 *) 0x41210000
#define led_out *(volatile u32 *) 0x41200000
int main ()
{
tri_in=0xffffffff;
tri_out=0x0;
while(1)
{
led_out=sw_in;
}
return 0;
}
成功!
用两个channel,测试了发现直接赋值法在按键按下的时候,跟官方api函数读出来的不一致,就是读的时候有问题。
而且使用官方api在按下的瞬间由于抖动,也会不一致。
留个坑吧,以后研究
#include
#include "platform.h"
#include "xil_printf.h"
#include "xgpio.h" /* GPIO data struct and APIs */
#include "xstatus.h"
#include "xparameters.h" /* Peripheral parameters */
#include "sleep.h"
#define tri_in *(volatile u32 *) 0x41201100
#define tri_out *(volatile u32 *) 0x41200100
#define sw_in *(volatile u32 *) 0x41201000
#define led_out *(volatile u32 *) 0x41200000
#define GPIO_BITWIDTH 4 /* This is the width of the GPIO */
#define GPIO_DEVICE_ID XPAR_AXI_GPIO_0_DEVICE_ID //device id
#define LED_CHANNEL 1 /* GPIO channel*/
#define SW_CHANNEL 2 /* GPIO channel*/
#define printf xil_printf /* A smaller footprint printf */
XGpio GpioOutput; /* The driver instance for GPIO Device configured as O/P */
XGpio GpioInput; /* The driver instance for GPIO Device configured as I/P */
int main(void)
{//Application start
/* loop forever*/
tri_in=0xffffffff;
tri_out=0x0;
print("Hello World\n\r");
while(1){
u32 Rdata;
u32 data1;
//read channel
XGpio_Initialize(&GpioInput, GPIO_DEVICE_ID);
XGpio_SetDataDirection(&GpioInput, SW_CHANNEL, 0xffffffff);
Rdata=XGpio_DiscreteRead(&GpioInput, SW_CHANNEL);
//out channel
XGpio_Initialize(&GpioOutput, GPIO_DEVICE_ID);
XGpio_SetDataDirection(&GpioOutput, LED_CHANNEL, 0x0);
XGpio_DiscreteWrite(&GpioOutput, LED_CHANNEL, Rdata);
/* equal to
led_out=Rdata;*/
//**********test read
// cant equal??
data1=sw_in;
if (data1 == Rdata)
{
print("SUCESS!.\r\n");
Rdata=data1;
print("change!.\r\n");
}
else print("FAILED!!!!!!!!!!!!!!!!!!!!!.\r\n");
}
return XST_SUCCESS;
}
大致了解,可以不看。
AXI4:(For high-performance memory-mapped requirements)主要面向高性能地址映射通信的需求,是面向地址映射的接口,允许最大256轮的数据突发传输。
AXI4-Lite:(For simple, low-throughput memory-mapped communication)是一个轻量级的地址映射单次传输接口, 占用很少的逻辑单元。
AXI4-Stream:(For high-speed streaming data)面向高速流数据传输,去掉了地址项,允许无限制的数据突发传输。
数据在总线上是遵守协议定的规则来传输的,AXI信号传输先是传地址,然后检测READY+VALID,都为高电平时开始传数据,当主机发送最后一个数据时LAST信号拉高,通知从机传输结束。
握手协议:
READY,VALID握手通信机制,主机产生 VLAID 信号来指明何时数据或控制信息有效。从机产生 READY 信号来指明已经准备好接受数据或控制信息。传输发生在 VALID和 READY 信号同时为高的时候。(还有一个LAST信号表示什么时候传到最后一个数据了)
读时序:地址线上发来地址,地址准备和地址有效都高时,开始发送要读的数据,读准备和读有效都高时数据被读取到,发最后一个数据时读LAST信号拉高。
写时序:地址线上发来地址,地址准备和地址有效都高时,开始发送要写的数据,写准备和写有效都高时数据写入,发最后一个数据时写LAST信号拉高。写数据多了一个反馈信号,反馈给主机,主机接收到这个信号,就知道写成功了。
AXI GPIO软核,在PL端实现,S_AXI端连接在AXI总线上与PS端处理器进行通信,GPIO端连接实际引脚(可任意分配),驱动板载外设。
点击右侧Address Editor即可看到硬件相关信息,在PL端实现的axi_gpio_0 IP核中所包含的寄存器首地址和结束地址,这个信息待会就要导出到硬件设计文件中,通过软件操作相关寄存器。
接下来导出整个设计的硬件设计文件(hdf)供SDK进行软件设计使用,因为设计中包含PL端设计,生成比特流文件,用来配置PL端的设计(AXI GPIO 软ip核)
ref
https://blog.csdn.net/Mculover666/article/details/83051767