ZYNQ学习

GPIO

image-20240425144028448

EMIO

(1)什么是EMIO?

  • PS与PL端之间的接口,EMIO是扩展的MIO,当PS端的引脚不够用的时候,可以通过EMIO进行扩展,使用PL端的引脚

(2)如何使用MIO的输入?

  • XGpioPs_SetDirectionPin(&gpiops_inst, MIO_KEY, 0); 直接设置为0即可

GPIO中断

(1)中断控制器怎么确定是哪个引脚的中断?

  • 判断INT_MASK,如果某个引脚的该寄存器被屏蔽,即该引脚没有产生中断
  • 判断INT_STAT,如果两个引脚的INT_MASK都没有被屏蔽,就判断INT_STAT,某个引脚该寄存器拉高,即该引脚产生中断

(2)中断配置代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
void SetupInterruptSystem(XScuGic *GicInstancePtr, XGpioPs *Gpio, u16 GpioIntrId)
{
// 查找中断控制器的配置信息
IntcConfig = XScuGic_LookupConfig(INTC_DEVICE_ID);
// 初始化中断控制器
XScuGic_CfgInitialize(GicInstancePtr, IntcConfig, IntcConfig->CpuBaseAddress);

// 初始化ARM处理器异常句柄
Xil_ExceptionInit();
// IRQ异常注册处理程序
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT, (Xil_ExceptionHandler)XScuGic_InterruptHandler, GicInstancePtr);
// 为GPIO器件使能中断,打开中断控制器的使能(GIC)
XScuGic_Enable(GicInstancePtr, GpioIntrId);
// 使能处理器的中断
Xil_ExceptionEnableMask();

// 关联中断处理函数
XScuGic_Connect(GicInstancePtr, GpioIntrId, (Xil_ExceptionHandler)IntrHandler, (void *)Gpio);

// 设置中断引脚的中断触发类型为下降沿
XGpioPs_SetIntrTypePin(Gpio, MIO_KEY, XGPIOPS_IRQ_TYPE_EDGE_FALLING);

// 清除上一次中断状态,解决上电时产生的第一次中断信号
XGpioPs_IntrClearPin(Gpio, MIO_KEY);

// 使能具体的中断引脚信号(GPIO)
XGpioPs_IntrEnablePin(Gpio, MIO_KEY);
}

/* 中断服务函数 */
void IntrHandler()
{
printf("IRQ Test!\n\r");
key_press = 1;
XGpioPs_IntrDisablePin(&gpiops_inst, MIO_KEY);
}

AXI_GPIO

(1)AXI_GPIO是一种由FPGA资源搭建的软核,不像PS部分的GPIO有自己的电路,AXI_GPIO没有实际的硬件电路,由用户通过PL端自行配置使用

image-20240425220923941

​ <AXI GPIO 框架>

image-20240426100810297

​ <编程指导>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
/* 实验目的:通过AXI GPIO使用PL端的按键点亮PS端的LED */

main函数:

// 配置及初始化PS端GPIO
gpiops_cfg_ptr = XGpioPs_LookupConfig(GPIOPS_ID);
XGpioPs_CfgInitialize(&gpiops_inst, gpiops_cfg_ptr, gpiops_cfg_ptr->BaseAddr);

// AXI GPIO初始化
XGpio_Initialize(&AXI_Gpio, AXI_GPIO_DEVICE_ID);

// PS端GPIO设置为输出并使能,驱动LED(0:输入/1:输出)
XGpioPs_SetDirectionPin(&gpiops_inst, MIO_LED0, 1);
XGpioPs_SetOutputEnablePin(&gpiops_inst, MIO_LED0, 1);

// AXI GPIO设置为输入并使能中断
XGpio_SetDataDirection(&AXI_Gpio, GPIO_CHANNEL1, 0x00000001); // 硬件中仅使用1bit,这里使用最低位为1

// 设置中断
SetupInterruptSystem(&Intc, &AXI_Gpio, AXI_GPIO_INTERRUPT_ID);

void SetupInterruptSystem(XScuGic *GicInstancePtr, XGpio *AXI_Gpio, u16 AXI_GpioIntrId)
{
// 配置中断控制器GIC
IntcConfig = XScuGic_LookupConfig(INTC_DEVICE_ID); // 查找中断控制器的配置信息
XScuGic_CfgInitialize(GicInstancePtr, IntcConfig, IntcConfig->CpuBaseAddress); // 初始化中断控制器

// 0xA0:中断源优先级,0x1:高电平敏感类型
// 注意:axi_gpio的中断会在任一GPIO接口数值变化时产生一段时间的高电平,无论数值由0变1还是由1变0,因此按钮 按下并弹起实际会产生2次中断的高电平
XScuGic_SetPriorityTriggerType(GicInstancePtr, AXI_GpioIntrId, 0xA0, 0x1);

// 不管使用什么外设,只要用到中断,这几个函数就要有
// 初始化ARM处理器异常句柄
Xil_ExceptionInit();
// IRQ异常注册处理程序
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT, (Xil_ExceptionHandler)XScuGic_InterruptHandler, GicInstancePtr);
// 使能处理器的中断
Xil_ExceptionEnableMask(XIL_EXCEPTION_IRQ);

// 关联中断处理函数
XScuGic_Connect(GicInstancePtr, AXI_GpioIntrId, (Xil_ExceptionHandler)IntrHandler, (void *)AXI_Gpio);

// 为AXI GPIO器件使能中断,打开中断控制器的使能(GIC)
XScuGic_Enable(GicInstancePtr, AXI_GpioIntrId);

// 打开AXI GPIO IP的中断使能
XGpio_InterruptGlobalEnable(AXI_Gpio); // 打开全局中断使能
XGpio_InterruptEnable(AXI_Gpio, 0x00000001); // 打开通道中的中断使能
}

/* 中断服务函数 */
void IntrHandler()
{
printf("IRQ Test!\n\r");
key_press = 1;
XGpio_InterruptDisable(&AXI_Gpio, 0x00000001);
}

自定义IP核

(1)基础知识参考ug1118,实操流程参考ZYNQ小系统板之嵌入式SDK开发指南

(2)自定义IP核,可以实现将模块集成到IP库中。在 Vivado 软件中,通过创建和封装 IP 向导的方式来自定义 IP 核,支持将当前工程、工程中的模块或者指定文件目录封装成 IP 核,当然也可以创建一个带有 AXI4 接口的 IP 核,用于 PS 和 PL 的数据通信。

image-20240428164252896

程序固化实验

image-20240428180244453

(1)ZYNQ包括PL和PS,程序固化时,不能单独固化PL的部分,PL的启动依赖于PS,因此需要固化PS再固化PL程序

image-20240428212243966

(2)程序固化就是将FSBL文件、bit流文件和用户程序合成BOOT.bin文件

(3)BOOT.bin文件拷贝到SD卡,可以直接从SD卡中启动;通过JTAG将BOOT烧录到QSPI FLASH中,可以从QSPI FLASH中启动(该两种方式均不易丢失,掉电后重新上电,仍可以读取程序并运行)

UART串口中断

image-20240429112107583 image-20240429112216131 image-20240429112232833

(1)xintc和xscugic有什么区别?

  • XScuGic 用于 ARM 硬核的内置中断控制器,而 XIntc 用于 AXI 中断控制器,这是 AMD-XILINX 拥有的软 IP。

    XScuGic 只能在 ARM 应用程序中使用。另一方面,XIntc 可用于 ARM/Microblaze/PCIE 等

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
/*
* main.c
*
* Created on: 2024年4月29日
* Author: yxf_f
*/
#include "xparameters.h"
#include "xuartps.h"
#include "xscugic.h"
#include "xil_exception.h"
#include "stdio.h"

#define UART_DEVICE_ID XPAR_XUARTPS_0_DEVICE_ID // 串口器件ID
#define INTC_DEVICE_ID XPAR_SCUGIC_SINGLE_DEVICE_ID // 中断控制器器件ID
#define UART_INT_IRQ_ID XPS_UART0_INT_ID // 串口中断号
#define TRIGGER_LEVEL 1 // 接收FIFO的触发阈值
#define BAUDRATE 115200 // 波特率

XUartPs UartPs; // 实例化UART器件
XScuGic InterruptController; // 实例化中断控制器器件

int UART_init(XUartPs *UartInstPtr);
int INTR_init(XUartPs *UartInstPtr, XScuGic *IntcInstPtr);
void IntrHandler(void *CallbackRef);

int main()
{
// UART初始化
int uart_init_state = UART_init(&UartPs);
if (XST_FAILURE == uart_init_state) {
printf("UART Failed!\n\r");
return XST_FAILURE;
}

// 中断控制器初始化
int intr_init_state = INTR_init(&UartPs, &InterruptController);
if (XST_FAILURE == intr_init_state) {
return XST_FAILURE;
}

while(1){

}

return XST_SUCCESS;
}

/* 串口初始化 */
int UART_init(XUartPs *UartInstPtr)
{
int Status;
XUartPs_Config *Config;

// 查找串口器件
Config = XUartPs_LookupConfig(UART_DEVICE_ID);
if (NULL == Config) {
return XST_FAILURE;
}
// 配置串口
XUartPs_CfgInitialize(UartInstPtr, Config, Config->BaseAddress);

// 硬件自检
Status = XUartPs_SelfTest(UartInstPtr);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}

// 串口模式设置
XUartPs_SetOperMode(UartInstPtr, XUARTPS_OPER_MODE_NORMAL);

// 设置波特率
XUartPs_SetBaudRate(UartInstPtr, BAUDRATE);

// 设置接收FIFO的触发阈值
XUartPs_SetFifoThreshold(UartInstPtr, TRIGGER_LEVEL);

return Status;
}

/* 中断初始化 */
int INTR_init(XUartPs *UartInstPtr, XScuGic *IntcInstPtr)
{
int Status;
XScuGic_Config *IntcConfig;

/* GIC */
// GIC中断控制器配置
IntcConfig = XScuGic_LookupConfig(INTC_DEVICE_ID); // 查找中断控制器的配置信息
Status = XScuGic_CfgInitialize(IntcInstPtr, IntcConfig, IntcConfig->CpuBaseAddress); // 初始化中断控制器
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}

// 不管使用什么外设,只要用到中断,这几个函数就要有
Xil_ExceptionInit();
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT, (Xil_ExceptionHandler)XScuGic_InterruptHandler, IntcInstPtr);
Xil_ExceptionEnable();

// 关联中断处理函数
XScuGic_Connect(IntcInstPtr, UART_INT_IRQ_ID, (Xil_ExceptionHandler)IntrHandler, UartInstPtr);

// 为UART器件使能中断,打开中断控制器的使能(GIC)
XScuGic_Enable(IntcInstPtr, UART_INT_IRQ_ID);

/* UART */
// UART外设中断触发方式(接收阈值触发中断)
XUartPs_SetInterruptMask(UartInstPtr, XUARTPS_IXR_RXOVR);
return Status;
}

void IntrHandler(void *CallbackRef)
{
u8 rev_data;
u32 IsrStatus;
XUartPs *uart_instance_ptr = (XUartPs *)CallbackRef;

// 获取中断状态寄存器的值(读出的值和前面配置的应该一致)
IsrStatus = XUartPs_ReadReg(uart_instance_ptr->Config.BaseAddress,
XUARTPS_IMR_OFFSET);
IsrStatus &= XUartPs_ReadReg(uart_instance_ptr->Config.BaseAddress,
XUARTPS_ISR_OFFSET);
// 根据状态寄存器的值判断接收数据是否达到阈值
if((IsrStatus & (u32)XUARTPS_IXR_RXOVR) != (u32)0)
{
rev_data = XUartPs_RecvByte(uart_instance_ptr->Config.BaseAddress);
// 清除中断标志(往状态寄存器对应的位写1,即可清除)
XUartPs_WriteReg(uart_instance_ptr->Config.BaseAddress, XUARTPS_ISR_OFFSET, IsrStatus);
}

XUartPs_SendByte(uart_instance_ptr->Config.BaseAddress, rev_data);
}

定时器中断

定时器简介

image-20240506110427711

(1)所有的私有定时器和看门狗定时器的时钟是1/2系统时钟,也就是333.333MHz

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
/*
* main.c
*
* Created on: 2024年4月30日
* Author: yxf_f
*/
#include "xparameters.h"
#include "xgpiops.h"
#include "xstatus.h"
#include "xplatform_info.h"
#include <xil_printf.h>
#include "xscutimer.h"
#include "xscugic.h"

#define GPIO_DEVICE_ID XPAR_XGPIOPS_0_DEVICE_ID // GPIO器件ID
#define TIMER_DEVICE_ID XPAR_XSCUTIMER_0_DEVICE_ID // TIMER器件ID
#define INTC_DEVICE_ID XPAR_SCUGIC_SINGLE_DEVICE_ID // 中断器件ID
#define TIMER_INTR_ID XPAR_SCUTIMER_INTR // 私有定时器中断号ID
#define MIOLED0 7 // LED引脚号
#define TIMER_LOAD_VALUE 0x3F94067 // 装载值,200ms / 1 /333.333MHz

XGpioPs Gpio; // GPIO驱动实例
XScuTimer TimerInstance; // TIMER驱动实例
XScuGic IntcInstance; // GIC驱动实例

int MIO_init();
int TIMER_init(XScuGic *IntcInstancePtr, XScuTimer * TimerInstancePtr);
int INTR_init(XScuGic *IntcInstancePtr, XScuTimer * TimerInstancePtr);
void timer_intr_handler(void *CallBackRef);

int main(void)
{
print("Timer Test\r\n");

// MIO初始化配置
MIO_init();

// TIMER初始化配置
TIMER_init(&IntcInstance, &TimerInstance);

// INTR初始化配置
INTR_init(&IntcInstance, &TimerInstance);

// 全部配置完成后,启动定时器
XScuTimer_Start(&TimerInstance);

while (1) {
}

return XST_SUCCESS;
}

/* MIO */
int MIO_init()
{
int Status;
XGpioPs_Config *ConfigPtr;
ConfigPtr = XGpioPs_LookupConfig(GPIO_DEVICE_ID);
Status = XGpioPs_CfgInitialize(&Gpio, ConfigPtr, ConfigPtr->BaseAddr);
if (Status != XST_SUCCESS)
return XST_FAILURE;

XGpioPs_SetDirectionPin(&Gpio, MIOLED0, 1);
XGpioPs_SetOutputEnablePin(&Gpio, MIOLED0, 1);

return XST_SUCCESS;
}

/* TIMER */
int TIMER_init(XScuGic *IntcInstancePtr, XScuTimer * TimerInstancePtr)
{
int Status;
XScuTimer_Config *ConfigPtr;

// TIMER初始化配置
ConfigPtr = XScuTimer_LookupConfig(TIMER_DEVICE_ID);
Status = XScuTimer_CfgInitialize(TimerInstancePtr, ConfigPtr,
ConfigPtr->BaseAddr);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}

// 自检
Status = XScuTimer_SelfTest(TimerInstancePtr);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}

// 设置重装载值
XScuTimer_LoadTimer(TimerInstancePtr, TIMER_LOAD_VALUE);

// 使能自动重装载
XScuTimer_EnableAutoReload(TimerInstancePtr);

return XST_SUCCESS;
}

int INTR_init(XScuGic *IntcInstancePtr, XScuTimer * TimerInstancePtr)
{
int Status;
XScuGic_Config *IntcConfig;

// 中断控制器GIC初始化配置
IntcConfig = XScuGic_LookupConfig(INTC_DEVICE_ID);
if (NULL == IntcConfig) {
return XST_FAILURE;
}
Status = XScuGic_CfgInitialize(IntcInstancePtr, IntcConfig, IntcConfig->CpuBaseAddress);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}

// 中断异常处理,中断必加
Xil_ExceptionInit();
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_IRQ_INT,
(Xil_ExceptionHandler)XScuGic_InterruptHandler,
IntcInstancePtr);
Xil_ExceptionEnable();

// 中断服务程序链接
Status = XScuGic_Connect(IntcInstancePtr, TIMER_INTR_ID,
(Xil_ExceptionHandler)timer_intr_handler,
(void *)TimerInstancePtr);
if (Status != XST_SUCCESS) {
return Status;
}

// 使能GIC中断控制器
XScuGic_Enable(IntcInstancePtr, TIMER_INTR_ID);

// 使能TIMER定时器中断
XScuTimer_EnableInterrupt(TimerInstancePtr);

return XST_SUCCESS;
}

void timer_intr_handler(void *CallBackRef)
{
static int led_state = 0;
XScuTimer *TimerInstancePtr = (XScuTimer *) CallBackRef;

if (XScuTimer_IsExpired(TimerInstancePtr)) {
XScuTimer_ClearInterruptStatus(TimerInstancePtr);
XGpioPs_WritePin(&Gpio, MIOLED0, led_state);
led_state = ~led_state;
}
}

PS XADC接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
/*
* main.c
*
* Created on: 2024年5月6日
* Author: yxf_f
*/
#include "xparameters.h"
#include "xadcps.h"
#include "stdio.h"
#include "sleep.h"

#define XADC_DEVICE_ID XPAR_XADCPS_0_DEVICE_ID // XADC器件ID

static XAdcPs XAdcInst; // XADC实例

int ps_xadc_init(u16 XAdcDeviceId);
void ps_xadc_print(XAdcPs *XAdcInstPtr);
int XAdcFractionToInt(float FloatNum);

int main(void) {
int Status;

Status = ps_xadc_init(XADC_DEVICE_ID);
if (Status != XST_SUCCESS) {
xil_printf("adcps polled printf Example Failed\r\n");
return XST_FAILURE;
}
xil_printf("Successfully ran adcps polled printf Example\r\n");

while(1){
u32 TempRawData = XAdcPs_GetAdcData(&XAdcInst, XADCPS_CH_TEMP);
float TempData = XAdcPs_RawToTemperature(TempRawData);
printf("The Current Temperature is %0d.%03d Centigrades.\r\n",
(int) (TempData), XAdcFractionToInt(TempData));

sleep(1);
}

return XST_SUCCESS;
}

int ps_xadc_init(u16 XAdcDeviceId) {
int Status;
XAdcPs_Config *ConfigPtr;
XAdcPs *XAdcInstPtr = &XAdcInst;

// XADC初始化配置
ConfigPtr = XAdcPs_LookupConfig(XAdcDeviceId);
if (ConfigPtr == NULL) {
return XST_FAILURE;
}
XAdcPs_CfgInitialize(XAdcInstPtr, ConfigPtr, ConfigPtr->BaseAddress);

// 自检
Status = XAdcPs_SelfTest(XAdcInstPtr);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}

// 设置成正常模式/安全模式
XAdcPs_SetSequencerMode(XAdcInstPtr, XADCPS_SEQ_MODE_SAFE);
}

void ps_xadc_print(XAdcPs *XAdcInstPtr) {
printf("start the XAdc Polled Example. \r\n");
// 温度
u32 TempRawData = XAdcPs_GetAdcData(XAdcInstPtr, XADCPS_CH_TEMP);
float TempData = XAdcPs_RawToTemperature(TempRawData);
printf("The Current Temperature is %0d.%03d Centigrades.\r\n",
(int) (TempData), XAdcFractionToInt(TempData));

TempRawData = XAdcPs_GetMinMaxMeasurement(XAdcInstPtr, XADCPS_MAX_TEMP);
float MaxData = XAdcPs_RawToTemperature(TempRawData);
printf("The Maximum Temperature is %0d.%03d Centigrades. \r\n",
(int) (MaxData), XAdcFractionToInt(MaxData));

TempRawData = XAdcPs_GetMinMaxMeasurement(XAdcInstPtr, XADCPS_MIN_TEMP);
float MinData = XAdcPs_RawToTemperature(TempRawData & 0xFFF0);
printf("The Minimum Temperature is %0d.%03d Centigrades. \r\n",
(int) (MinData), XAdcFractionToInt(MinData));

// VccPint 电压
u32 VccPintRawData = XAdcPs_GetAdcData(XAdcInstPtr, XADCPS_CH_VCCPINT);
float VccPintData = XAdcPs_RawToVoltage(VccPintRawData);
printf("The Current VCCPINT is %0d.%03d Volts. \r\n",
(int) (VccPintData), XAdcFractionToInt(VccPintData));

VccPintRawData = XAdcPs_GetMinMaxMeasurement(XAdcInstPtr,
XADCPS_MAX_VCCPINT);
MaxData = XAdcPs_RawToVoltage(VccPintRawData);
printf("The Maximum VCCPINT is %0d.%03d Volts. \r\n", (int) (MaxData),
XAdcFractionToInt(MaxData));

VccPintRawData = XAdcPs_GetMinMaxMeasurement(XAdcInstPtr,
XADCPS_MIN_VCCPINT);
MinData = XAdcPs_RawToVoltage(VccPintRawData);
printf("The Minimum VCCPINT is %0d.%03d Volts. \r\n", (int) (MinData),
XAdcFractionToInt(MinData));

// VccPaux 电压
u32 VccPauxRawData = XAdcPs_GetAdcData(XAdcInstPtr, XADCPS_CH_VCCPAUX);
float VccPauxData = XAdcPs_RawToVoltage(VccPauxRawData);
printf("The Current VCCPAUX is %0d.%03d Volts. \r\n",
(int) (VccPauxData), XAdcFractionToInt(VccPauxData));

VccPauxRawData = XAdcPs_GetMinMaxMeasurement(XAdcInstPtr,
XADCPS_MAX_VCCPAUX);
MaxData = XAdcPs_RawToVoltage(VccPauxRawData);
printf("The Maximum VCCPAUX is %0d.%03d Volts. \r\n", (int) (MaxData),
XAdcFractionToInt(MaxData));

VccPauxRawData = XAdcPs_GetMinMaxMeasurement(XAdcInstPtr,
XADCPS_MIN_VCCPAUX);
MinData = XAdcPs_RawToVoltage(VccPauxRawData);
printf("The Minimum VCCPAUX is %0d.%03d Volts. \r\n", (int) (MinData),
XAdcFractionToInt(MinData));

// VccPdro Votage
u32 VccPdroRawData = XAdcPs_GetAdcData(XAdcInstPtr, XADCPS_CH_VCCPDRO);
VccPintData = XAdcPs_RawToVoltage(VccPdroRawData);
printf("The Current VCCPDDRO is %0d.%03d Volts. \r\n",
(int) (VccPintData), XAdcFractionToInt(VccPintData));

VccPdroRawData = XAdcPs_GetMinMaxMeasurement(XAdcInstPtr,
XADCPS_MAX_VCCPDRO);
MaxData = XAdcPs_RawToVoltage(VccPdroRawData);
printf("The Maximum VCCPDDRO is %0d.%03d Volts. \r\n", (int) (MaxData),
XAdcFractionToInt(MaxData));

VccPdroRawData = XAdcPs_GetMinMaxMeasurement(XAdcInstPtr,
XADCPS_MIN_VCCPDRO);
MinData = XAdcPs_RawToVoltage(VccPdroRawData);
printf("The Minimum VCCPDDRO is %0d.%03d Volts. \r\n", (int) (MinData),
XAdcFractionToInt(MinData));

printf("Exiting the XAdc Polled Example. \r\n");
}

int XAdcFractionToInt(float FloatNum)
{
float Temp;

Temp = FloatNum;
if (FloatNum < 0) {
Temp = -(FloatNum);
}

return( ((int)((Temp -(float)((int)Temp)) * (1000.0f))));
}

FLASH

flash简介

image-20240506164928801

flash分类

image-20240506165231098 image-20240506165456603

QSPI FLASH接口

(1)四根数据线,读写更快

image-20240506165723615

SD卡

SD卡简介

(1)SD 卡共有 9 个引脚线,可工作在 SDIO 模式或者 SPI 模式。在 SDIO 模式下,共用到 CLK、CMD、DAT[3:0]
六根信号线;在 SPI 模式下,共用到 CS(SDIO_DAT[3])、CLK(SDIO_CLK)、MISO(SDIO_DAT[0])、MOSI(SDIO_CMD)四根信号线。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
/*
* main.c
*
* Created on: 2024年5月7日
* Author: yxf_f
*/
#include "xparameters.h"
#include "xil_printf.h"
#include "ff.h"
#include "xdevcfg.h"

#define FILE_NAME "ZDYZ.txt" //定义文件名

const char src_str[30] = "zynq xilink"; //定义文本内容
static FATFS fatfs; //文件系统

//初始化文件系统
int platform_init_fs()
{
FRESULT status;
TCHAR *Path = "0:/";
BYTE work[FF_MAX_SS];

//注册一个工作区(挂载分区文件系统)
//在使用任何其它文件函数之前,必须使用f_mount函数为每个使用卷注册一个工作区
status = f_mount(&fatfs, Path, 1); //挂载SD卡
if (status != FR_OK) {
xil_printf("Volume is not FAT formated; formating FAT\r\n");
//格式化SD卡
status = f_mkfs(Path, FM_FAT32, 0, work, sizeof(work));
if (status != FR_OK) {
xil_printf("Unable to format FATfs\r\n");
return -1;
}
//格式化之后,重新挂载SD卡
status = f_mount(&fatfs, Path, 1);
if (status != FR_OK) {
xil_printf("Unable to mount FATfs\r\n");
return -1;
}
}
return 0;
}

//挂载SD(TF)卡
int sd_mount()
{
FRESULT status;
//初始化文件系统(挂载SD卡,如果挂载不成功,则格式化SD卡)
status = platform_init_fs();
if(status){
xil_printf("ERROR: f_mount returned %d!\n",status);
return XST_FAILURE;
}
return XST_SUCCESS;
}

//SD卡写数据
int sd_write_data(char *file_name,u32 src_addr,u32 byte_len)
{
FIL fil; //文件对象
UINT bw; //f_write函数返回已写入的字节数

//打开一个文件,如果不存在,则创建一个文件
f_open(&fil,file_name,FA_CREATE_ALWAYS | FA_WRITE);
//移动打开的文件对象的文件读/写指针 0:指向文件开头
f_lseek(&fil, 0);
//向文件中写入数据
f_write(&fil,(void*) src_addr,byte_len,&bw);
//关闭文件
f_close(&fil);
return 0;
}

//SD卡读数据
int sd_read_data(char *file_name,u32 src_addr,u32 byte_len)
{
FIL fil; //文件对象
UINT br; //f_read函数返回已读出的字节数

//打开一个只读的文件
f_open(&fil,file_name,FA_READ);
//移动打开的文件对象的文件读/写指针 0:指向文件开头
f_lseek(&fil,0);
//从SD卡中读出数据
f_read(&fil,(void*)src_addr,byte_len,&br);
//关闭文件
f_close(&fil);
return 0;
}

//main函数
int main()
{
int status,len;
char dest_str[30] = "";

status = sd_mount(); //挂载SD卡
if(status != XST_SUCCESS){
xil_printf("Failed to open SD card!\n");
return 0;
}
else
xil_printf("Success to open SD card!\n");

len = strlen(src_str); //计算字符串长度
//SD卡写数据
sd_write_data(FILE_NAME,(u32)src_str,len);
//SD卡读数据
sd_read_data(FILE_NAME,(u32)dest_str,len);

//比较写入的字符串和读出的字符串是否相等
if (strcmp(src_str, dest_str) == 0)
xil_printf("src_str is equal to dest_str,SD card test success!\n");
else
xil_printf("src_str is not equal to dest_str,SD card test failed!\n");

return 0;
}

AXI协议

(参考:AXI4协议学习:架构、信号定义、工作时序和握手机制-CSDN博客

AXI定义

  • AXI,(advanced extensible interface) 高级扩展接口,属于AMBA(advanced microcontroller bus architecture)高级微控制器总线架构。

AXI分类

  • AXI4:高性能存储器映射接口,大量数据通信,用于处理器访问存储器等需要指定地址的高速数据传输场景,支持突发传输模式,一个地址传输256个数据。

  • AXI4-Lite:简化版的AXI4接口,少量数据的存储器映射通信,提供单个数据传输,用于低速外设的访问,不支持突发数据传输模式。

  • AXI-Stream:高速数据流传输接口,非存储映射接口,即不需要地址,用于视频、PCIE、DMA和高速AD等场景,支持不限数据长度的突发传输模式。(通常可以使用AXI Direct Memory Access (DMA) engines将Stream移进或移出内存)

AXI工作方式

(1)AXI规范描述了单个AXI主(master)从(slave)之间的接口。

(2)多个Master和Slave之间的内存映射可以通过Xilinx AXI Interconnect IP 和 AXI SmartConnect IP 连接在一起

(3)数据可以同时在主->从和从->主两个方向传输。数据传输的大小可以变化。AXI4中的限制是最多256个数据传输的突发事务。AXI4-Lite只允许每个事务进行1个数据传输。

(4)AXI4 和 AXI4-Lite有5个独立的数据通道(参考 PDF INI0022dD)

image-20240511121109920

  • 每一个独立的通道都包含一组信息信号(读写地址、数据等信号)、VALID信号和READY信号,用于提供双向握手机制
  • 信息源端使用VALID信号表示当前通道地址、数据和控制信息什么时候有效;信息目的端使用READY信号表示什么时候可以接收信息;读数据通道和写数据通道都包含一个LAST信号,用于表示传输的最后一个数据。
  • 读数据通道和写数据通道都包含各自的地址通道,地址通道携带了请求所需的地址和信息
  • 读数据通道从机——>主机,包含了读数据和读响应的信息,读响应的信号用于表示传输是否完成
  • 写数据通道主机——>从机,包含了写数据,通过WSTRB信号表示当前数据的那个字节有效
  • 写响应通道从机——>主机,包含了写响应信号,用于表示写操作是否完成
  • 所有的五个通道都是通过相同的VALID/READY握手处理来传输地址、数据和控制信息,双向握手的机制意味着主机和从机之间传输数据时,都可以控制传输的速率
  • 只有当VALID和READY信号都为高,传输才会开始

AXI时序示例

  1. 突发读示例

    突发读时序
    • ARVALID和ARREADY都拉高,地址有效,地址传输到从机
      • RVALID和RREADY都拉高,数据传输到从机
      • RLAST与第四个数据D3同步,表示最后一个数据
  2. 重叠突发读示例

    重叠突发读

    • 可以连续发送多个地址,两个突发读也是连续的,但是都有自己的结束RLAST
  3. 突发写时序

    突发写

    • 从机接收到数据后,向主机发送一个写响应,表明写事务已经完成