Use EMIO and MIO to control Pmod GPIOS on zedboard by vivado and SDK (Zedboard)
Use EMIO and MIO directly to control ZedBoard's GPIOs is significantly more efficient than AXI-based approaches; there's no requirement for additional IP cores. A notable drawback of this method is its limitation when employing the programmable system (PS) for direct control; signals must be sent pin-by-pin, resulting in minor delays between individual pins. In contrast, AXI-based approaches allow simultaneous handling of all read/write operations. Furthermore, while EMIO and MIO enable bidirectional communication on controlled pins, pre-configured AXI-based systems maintain fixed directions upon creation; this makes them equally convenient for use in specific scenarios.
本章将介绍如何通过EMIO和MIO直接控制PMD。与之前采用AXI GPIO的方式不同,在使用PS直接控制GPIO时,在每个设备上都需要单独进行read或write操作。相邻设备之间还存在延时。相比之下,在创建的时候就已经设置了方向属性,并不是像AXI GPIO那样提供统一的方向设置功能,在创建的时候就已经设置了方向属性,并不是像AXI(GPIO)那样提供统一的方向设置功能,在创建的时候就已经设置了方向属性,并不是像AXI(GPIO)那样提供统一的方向设置功能
Conception:
There three ways to drive GPIOs by PS on FPGA
If you wish to utilize the PL part for driving the GPIO, you can avoid the PS part and make use of the bit-file.
1. PS ->MIO -> GPIOs (no need for constraints, pin_number[53:0] driven by PS)
2. PS ->EMIO -> PL->GPIOs (need constraints, pin_number[117: 54] driven by PL)
3. PS ->EMIO ->AXI Bus -> PL-> GPIOs (need constraints, pin_number[117: 54] driven by PL)

(This is the guide picture for WLAN installing)
Design:
1. Adding block design in vivado:

Remember to check "EMIO" in MIO configuration

Configure the constraints for EMIO-controlled pins. It's unnecessary to configure constraints for MIO-controlled GPIOs, as their I/O settings are fixed.

Generate block wrapper -> generate bitstream.
2. Driving GPIOs in SDK
I have developed a template specifically for block design projects, enabling you to access and configure various pin settings by un commenting different port configurations, which allows you to manage whether data is written or read from specific pins.
包含外部文件<标准输入输出函数库>
引入平台相关的头文件'平台函数'
包含内部库文件'XIL printf接口'
引入外部接口文件'XGPI OPS操作集'
加载参数处理相关的头文件'XPARA-METERS'
定义宏变量DELAYTIME等于1千万
定义宏常量TEST CYCLE等于8
int main()
{ XGpioPs gpioStruct;
XGpioPs_Config *gpioConfig;
s32 xStatus;
int i=0, count=0;
init_platform();
int pinNum = 54; //this is the number of first pin that controlled by EMIO !!!
u32 pin_out = 1; //1 output signal
u32 pin_in = 0; //0 input signal
// int read1[TEST_CYCLE];
int read2[TEST_CYCLE];
// int read3[TEST_CYCLE];
// int read4[TEST_CYCLE];
// int read5[TEST_CYCLE];
// int read6[TEST_CYCLE];
// int read7[TEST_CYCLE];
// int read8[TEST_CYCLE];
// int read9[TEST_CYCLE];
// int read_10[TEST_CYCLE];
// int read_11[TEST_CYCLE];
// int read_12[TEST_CYCLE];
// int read_13[TEST_CYCLE];
// int read_14[TEST_CYCLE];
// int read_15[TEST_CYCLE];
// int read_16[TEST_CYCLE];
// int read_17[TEST_CYCLE];
// int read_18[TEST_CYCLE];
// int read_19[TEST_CYCLE];
// int read_20[TEST_CYCLE];
初始化int类型的数组input{n}由交替排列的0和1组成。
其中n取值范围为从输入索引到输入索引结束。
例如:初始化int类型的数组input{2}由交替排列的值{[具体数值]}组成。
同样地:
初始化int类型的数组input{4}由交替排列的值{[具体数值]}组成。
...
同样地:
初始化int类型的数组input{39}由交替排列的值{[具体数值]}组成。
print("------------------------Start testing-------------------------- \n\r");
gpioConfig = XGpioPs_LookupConfig(XPAR_PS7_GPIO_0_DEVICE_ID);
if(gpioConfig == NULL) //如果gpioConfig为空,则无法找到对应的设备
{
print("错误提示:无法查找gpioConfig。\n");
return XST_FAILURE;
}
// 初始GPIO设置是至关重要的步骤
xStatus = XGpioPs_CfgInitialize(&gpioStruct, gpioConfig, gpioConfig->BaseAddr);
if(xStatus != XST_SUCCESS)
{
print("PS MIO GPIO初始化出现错误。\n");
}
else
{
print("PS MIO GPIO初始化顺利完成。\n");
}
// 配置gpios的方向受EMIO控制
XGpioPs.ConfigureTheDirectionPin(&gpioStruct, pinNum, pin_out); //向gpios发送信号
// XGpioPs.ConfigureTheDirectionPin(&gpioStruct, pinNum, pin_in);
XGpioPsEnableOutputFunctionality(&gpioStruct, pinNum);
// 配置GPIO结构的输出方向设置到指定通道
.ConfigureXGpioPsSetDirectionPin(&gpioStruct, pinNum + 1L, PIN_OUTPUT_DIRECTION);
// 配置GPIO结构的输入方向设置到指定通道
.ConfigureXGpioPsSetDirectionPin(&gpioStruct, pinNum + 1L, PIN_INPUT_DIRECTION);
// 读取GPIO通道上的信号
// 配置GPIO结构的输出启用状态设置到指定通道
.ConfigureXGpioPsSetOutputEnablePin(&gpioStruct, pinNum + 1L);
XGpioPsConfigureDirection(\&gpioStruct, pinNum+2, OUTPUT);
XGpioPsConfigureDirection(\&gpioStruct, pinNum+2, INPUT);
XGpioPsEnableOutput(\&gpioStruct, pinNum+2);
// ConfigureGPIOsDirPin(&gpioStruct, pinNum+3, out_direction);
// ConfigureGPIOsDirPin(&gpioStruct, pinNum+3, in_direction);
ConfigureGPIOsOutEnable(&gpioStruct, pinNum+3);
// 调用XGpioPs_SetDirectionPin配置指定通道的方向为输出;
// 调用XGpioPs_SetDirectionPin配置指定通道的方向为输入;
设置指定通道的输出状态为启用;
// SetDirectionPin(&gpioStruct, pinNum + 5U, output_pin);
// SetDirectionPin(&gpioStruct, pinNum + 5U, input_pin);
SetOutputEnablePin(&gpioStruct, pinNum + 5U, 1);
// 配置第6个GPIO模块的第2个引脚为输出高电平状态
// 配置第6个GPIO模块的第3个引脚为输入低电平状态
// 启用第6个GPIO模块的第2个引脚以输出高电平
// 使用XGpioPs_SetDirectionPin函数设置方向输出至指定地址pinNum+7引脚。
// 使用XGpioPs_SetDirectionPin函数设置方向输入至指定地址pinNum+7引脚。
调用XGpioPs_SetOutputEnablePin函数置位引脚输出端口至指定地址pinNum+7引脚并设为启用状态。
通过设置方向输出引脚配置模块的GPIO管脚配置为输出状态。
通过设置方向输入引脚配置模块的GPIO管脚配置为输入状态。
通过启用高电平信号使指定引脚配置模块的GPIO管脚处于活动高电平状态。
// XGpioPs_SetDirection(&gpioStruct, pinNum+9) = OUT;
// XGpioPs_SetDirection(&gpioStruct, pinNum+9) = IN;
XGpioPs_SetOutputEnable(&gpioStruct, pinNum+9) = true;
XGpioPs_ConfigureDirectionPin(&gpioStruct, pinNum+10, OUTPUT_DIR);
XGpioPs_ConfigureDirectionPin(&gpioStruct, pinNum+10, INPUT_DIR);
XGpioPs_ENABLEOutputPin(&gpioStruct, pinNum+10);
该函数用于设置GPIO方向引脚的输出状态。
同样地,在此位置上设置GPIO方向引脚的输入状态。
通过指定位掩码的方式激活GPIO输出。
// Set the output direction of the GPIO PIN at position pin_num + 0x0C to Output.
// Set the output direction of the GPIO PIN at position pin_num + 0x0C to Input.
The Output Enable bit for the GPIO Pin at index pin_num + 0x0C is being set to Active High.
// XGpioPs_SetDirectionPin(&gpioStruct, pinNum+13, pin_out);
// XGpioPs_SetDirectionPin(&gpioStruct, pinNum+13, pin_in);
XGpioPs_SetOutputEnablePin(&gpioStruct, pinNum+13, high);
XGpioPsConfigureDirectionPin(&gpioStruct, pinNum+14, out状态);
XGpioPsConfigureDirectionPin(&gpioStruct, pinNum+14, in状态);
XGpioPsConfigureOutputEnablePin(&gpioStruct, pinNum+14, high\ level\ enable);
// 由指定地址的位置XGPIOPs_输出/输入端口模块...的设置为输出/输入状态;
// 由指定地址的位置XGPIOPs_输出/输入端口模块...的设置为输入状态;
$XGPIOPs_OUTPUT_ENABLE = 1;
// 配置GPIO引脚Pins为out方向;
// 配置GPIO引脚Pins为in方向;
// 配置GPIO引脚Pins的输出状态设为启用状态;
// XGpioPs_ConfigureDirectionChannel(&gpioStruct, channelNum+17, PIN_OUTPUT);
// XGpioPs_ConfigureDirectionChannel(&gpioStruct, channelNum+17, PIN_INPUT);
XGpioPs_EnableOutputChannel(&gpioStruct, channelNum+17, 1);
// XGpioPs_SetDirectionPin((uintptr_t)&gpioStruct,(uint32_t)(uintptr_t)pinNum+0x24U,( summerint32_t ) summerint32_t(pin_out));
// XGpioPs_SetDirectionPin((uintptr_t)&gpioStruct,(unit32_t)(uintptr_t)pinNum+0x24U,( unit32_t ) unit32t(pin_in));
XGpioPs_SetOutputEnablePin((uintptr_t)&gpioStruct,(unit32t)(uintptr_t)pinNum+0x24U,(unit8t)) 0x0000_ 9999_ 9999);
XGpioPs\_SetDirectionPin(gpioparam, pinNum+19, pin_out);
XGpioPs\_SetDirectionPin(gpioparam, pinNum+19, pin_in);
XGpioPs\_SetOutputEnablePin(gpioparam, pinNum+19, 1);
其中前20号端口可同时配置为输入与输出
自定义函数SetDirectionPin(&gpioStruct, gpioNumber + 21, outPort);
自定义函数SetOutputEnablePin(&gpioStruct, gpioNumber + 21, 1);
XGpioPs_SetOutputConfig(&gpioStruct, pinNum+22, pin_out);
XGpioPsConfigureOutputDirection(gpioStruct, pinNum+23, pin_out);
XGpioPsConfigureDigitalPinDirection(gpioStruct, gpioNumber + 24, output_pin);
XGpioPsConfigureDigitalPinOutputEnable(gpioStruct, gpioNumber + 24, OE);
设置GPIO方向引脚为输出型,并将地址设为pin_num + 25;
设置GPIO的输出启用引脚,并将地址设为pin_num + 25且置高电平;
XGpioPs_SetDirectionPort(&gpioStruct, pinNum+26, pin_out);
XGpioPs_SetOutputEnablePort(&gpioStruct, pinNum+26, 1);
设置方向引脚至指定地址(即第27个引脚号)为out端口。
将输出功能在指定地址(即第27个引脚号)设为高电平以确保输出被启用。
设置方向引脚的位置函数调用将被传递给XGpioPs_SetDirectionPin函数。\n\n配置输出启用引脚的位置函数调用将被传递给XGpioPs_SetOutputEnablePin函数。\n
XGpioPs\_SetDirectionPin\$(&\text{ gpioStruct}, \text{ pinNum}+29, \text{ pin_out});
XGpioPs\_SetOutputEnablePin\$(&\text{ gpioStruct}, \text{ pinNum}+29, 1);
XGpioPs.ConfigureDirectionPin(&gpioStruct, gpioNumber + 30, output_pin);
XGpioPs@EnableOutputPin(&gpioStruct, gpioNumber + 30, true);
XGpioPs_SetDirectionBit(&gpioStruct, pinNum+31, pin_out);
XGpioPs_SetOutputConfig(&gpioStruct, pinNum+31, true);
// 配置GPIO引脚为MIO类型
XPIO_SetDirection(&gpioStruct, 13, OUT);
XPIO_SetOutputEnable(13, true); // 或者#define BIT_OUTPUT
XGpioPs\_SetDirectionPin\left(&gpioStruct,\, 10,\, pin\_out\right);
XGpioPs\_SetOutputEnablePin\left(&gpioStruct,\, 10,\, 1\right);
XGpioPs\_ConfigureDirectionPin(\&gpioparam, 11, pin\_out);
XGpioPs\_EnableOutputPin(\&gpioparam, 11);
调用XGpioPs_SetDirectionPin函数将第12个GPIO引脚的方向设置为输出方向;调用XGpioPs_SetOutputEnablePin函数将第12个GPIO引脚的输出启用状态设为高电平。
设置方向引脚gpioStruct的输出状态为pin_out;
设置输出启用引脚gpioStruct为高电平;
设置方向管脚,并通过指针传递给结构体 ninth 引脚。
设置输出启用管脚,并通过指针传递给结构体 ninth 引脚的启用状态标志位为 1。
XGpioPs_设置方向并置引脚(&gpioStruct, 14, pin_out);
XGpioPs_启使输出端口引脚(&gpioStruct, 14, 1);
XGpioPs ConfigureDirectionPin(&gpioStruct, 15, pin_out);
XGpioPs SetOutputState(&gpioStruct, 15, HIGH);
Xgpiops_setDirectionPin(\&gpiostruct,\ 15,\ pin\_out); Xgpiops_setOutputEnablePin(&gpiostruct,\ 15,\ 1);$
/READ/WRIET_TO_GPIO/
while(count<TEST_CYCLE){
//output signals to EMIO
XGpioPs_WritePin(&gpioStruct, pinNum, input0[count]);
// XGpioPs_WritePin(&gpioStruct, pinNum+1, input1[count]);
// XGpioPs_WritePin(&gpioStruct, pinNum+2, input2[count]);
// XGpioPs_WritePin(&gpioStruct, pinNum+3, input3[count]);
// XGpioPs_WritePin(&gpioStruct, pinNum+4, input4[count]);
// XGpioPs_WritePin(&gpioStruct, pinNum+5, input5[count]);
// XGpioPs_WritePin(&gpioStruct, pinNum+6, input6[count]);
// XGpioPs_WritePin(&gpioStruct, pinNum+7, input7[count]);
// XGpioPs_WritePin(&gpioStruct, pinNum+8, input8[count]);
// XGpioPs_WritePin(&gpioStruct, pinNum+9, input9[count]);
// XGpioPs_WritePin(&gpioStruct, pinNum+10, input_10[count]);
// XGpioPs_WritePin(&gpioStruct, pinNum+11, input_11[count]);
// XGpioPs_WritePin(&gpioStruct, pinNum+12, input_12[count]);
// XGpioPs_WritePin(&gpioStruct, pinNum+13, input_13[count]);
// XGpioPs_WritePin(&gpioStruct, pinNum+14, input_14[count]);
// XGpioPs_WritePin(&gpioStruct, pinNum+15, input_15[count]);
// XGpioPs_WritePin(&gpioStruct, pinNum+16, input_16[count]);
// XGpioPs_WritePin(&gpioStruct, pinNum+17, input_17[count]);
// XGpioPs_WritePin(&gpioStruct, pinNum+18, input_18[count]);
// XGpioPs_WritePin(&gpioStruct, pinNum+19, input_19[count]);
// XGpioPs_WritePin(&gpioStruct, pinNum+20, input_20[count]);
// XGpioPs_WritePin(&gpioStruct, pinNum+21, input_21[count]);
// XGpioPs_WritePin(&gpioStruct, pinNum+22, input_22[count]);
// XGpioPs_WritePin(&gpioStruct, pinNum+23, input_23[count]);
// XGpioPs_WritePin(&gpioStruct, pinNum+24, input_24[count]);
// XGpioPs_WritePin(&gpioStruct, pinNum+25, input_25[count]);
// XGpioPs_WritePin(&gpioStruct, pinNum+26, input_26[count]);
// XGpioPs_WritePin(&gpioStruct, pinNum+27, input_27[count]);
// XGpioPs_WritePin(&gpioStruct, pinNum+28, input_28[count]);
// XGpioPs_WritePin(&gpioStruct, pinNum+29, input_29[count]);
// XGpioPs_WritePin(&gpioStruct, pinNum+30, input_30[count]);
// XGpioPs_WritePin(&gpioStruct, pinNum+31, input_31[count]);
输出信号发送至MIO
XGpioPs_WritePin(&gpioStruct, 13, input\_32[count]); 这是由第一个JTAG外设(JE1)控制的第一个GPIO端口。
XGpioPs_WritePin(&gpioStruct, 10, input\_33[count]); 这是第二个GPIO端口(由第一个JTAG外设 JE1 控制)。
XGpioPs_WritePin(&gpioStruct, 11, input\_34[count]); 第三个 GPIO 端口(由第一个 JTAG 外设 JE1 控制)。
XGpioPs_WritePin(&gpioStruct, 12, input\_35[count]); 第四个 GPIO 端口(由第一个 JTAG 外设 JE1 控制)。
XGpioPs_WritePin(&gpioStruct, 0, input\_36[count]); 第五个 GPIO 端口(由第一个 JTAG 外设 JE1 控制)。
XGpioPs_WritePin(&gpioStruct, 9, input\_37[count]); 第六个 GPIO 端口 (由第一个 JTAG 外设 JE2 控制)。
XGpioPs_WritePin(&gpioStruct, 14, input\_38[count]); 第七个 GPIO 端口 (由第一个 JTAG 外设 JE2 控制)。
XGpioPs_WritePin(&gpioStruct, 15, input\_39[count]); 第八个 GPIO 端口 (由第一个 JTAG 外设 JE2 控制).
该循环机制通过添加延迟配置系统时钟,并在每次迭代中增加DELAY / 100ns的时间量;其运行频率设定为100MHz。
// 从EMIO读取信号
// 将第1个读数将被存储到数组read1[count]中
int read1[count]=XGpioPs_ReadPin(&gpioStruct, pinNum);
// 将第2个读数将被存储到数组read2[count]中
int read2[count]=XGpioPs_ReadPin(&gpioStruct, pinNum+1);
// 将第3个读数将被存储到数组read3[count]中
int read3[count]=XGpioPs_ReadPin(&gpioStruct, pinNum+2);
// 将第4个读数将被存储到数组read4[count]中
int read4[count]=XGpioPs_ReadPin(&gpioStruct, pinNum+3);
// 将第5个读数将被存储到数组read5[count]中
int read5[count]=XGpioPs_ReadPin(&gpioStruct, pinNum+4);
// 将第6个读数将被存储到数组read6[count]中
int read6[count]=XGpioPs_ReadPin(&gpioStruct, pinNum+5);
// 将第7个读数将被存储到数组read7[count]中
int read7[count]=XGpioPs_ReadPin(&gpioStruct, pinNum+6);
// 将第8个读数将被存储到数组read8[count]中
int read8[count]=XGpioPs_ReadPin(&gpioStruct, pinNum+7);
// 将第9个读数将被存储到数组read9[count]中
int read9[count]=XGpioPs_ReadPin(&gpioStruct, pinNum+8);
// 第10个读数将被存储到数组read_10(count) 中
int read_10[cnt]=XGpioPs_ReadPin(&gpioStruct, pinNum+9);
注意:这里已经更改了索引计数方式和变量命名策略
继续按照此模式执行后续操作
生成内容
return 0;
}
Reference:
