Zynq平台开发基础详解及C/C++实例
1. Zynq平台概述
1.1 Zynq架构简介
Zynq是Xilinx(现已被AMD收购)推出的系统级芯片(SoC)系列,它将ARM处理器核心(处理系统PS)和可编程逻辑单元(PL)集成在同一芯片上。主要系列包括:
- Zynq-7000:集成双核Cortex-A9处理器和Artix/Kintex FPGA
- Zynq UltraScale+ MPSoC:集成四核Cortex-A53(应用处理器)、双核Cortex-R5(实时处理器)、Mali-400 GPU和UltraScale FPGA
- Zynq RFSoC:在UltraScale+ MPSoC基础上增加射频数据转换器
Zynq的主要优势在于将软件的灵活性和硬件的高性能结合在一起,特别适合需要同时进行通用处理和专用硬件加速的应用场景。
1.2 Zynq-7000系列基本组成
以Zynq-7000为例,其基本组成包括:
处理系统(PS)部分:
- 双核ARM Cortex-A9处理器(最高1GHz)
- L1/L2缓存
- 片上存储器(OCM)
- 外部存储器控制器(DDR)
- 丰富的外设(UART、I2C、SPI、USB、Ethernet等)
- 通用I/O接口(GPIO)
可编程逻辑(PL)部分:
- 可编程逻辑单元(LUT)
- 存储资源(BRAM)
- DSP模块
- 可配置I/O
- 时钟管理单元
PS-PL接口:
- 高性能AXI接口(HP0-HP3)
- 通用AXI接口(GP0-GP1)
- 加速一致性端口(ACP)
- EMIO(扩展MIO)接口
2. Zynq开发环境搭建
2.1 开发工具链
开发Zynq平台应用需要的主要工具包括:
- Vivado设计套件:用于硬件设计、综合、实现和比特流生成
- Vitis统一软件平台:包含SDK,用于软件应用程序开发、调试和部署
- PetaLinux工具:用于构建定制的嵌入式Linux系统
- XSCT(Xilinx Software Command-line Tool):命令行开发工具
2.2 硬件设计工作流程
- 在Vivado中创建项目
- 添加处理系统IP(ZYNQ7 Processing System)
- 通过Block Design配置PS设置
- 添加和配置自定义PL IP核
- 连接PS和PL
- 生成HDL封装、综合、实现设计
- 导出硬件定义文件(.xsa)
2.3 软件开发工作流程
- 在Vitis中创建平台项目,导入硬件定义
- 创建应用项目,选择合适的OS(裸机/Linux/FreeRTOS)
- 编写应用程序代码
- 编译、调试应用程序
- 将应用程序部署到Zynq板上运行
3. Zynq PS编程基础
3.1 裸机(Bare-metal)应用程序开发
c
/**
* hello_world.c - Zynq裸机应用程序示例
*
* 这个简单的示例演示了如何在Zynq上进行UART输出
*/
#include <stdio.h>
#include "platform.h"
#include "xil_printf.h"
#include "xparameters.h"
#include "sleep.h"
int main()
{
// 初始化平台
init_platform();
// 打印欢迎信息
print("Hello from Zynq!\n\r");
xil_printf("This is a bare-metal application running on a Zynq device\n\r");
// 打印系统参数
xil_printf("CPU Clock Frequency: %u Hz\n\r", XPAR_CPU_CORTEXA9_CORE_CLOCK_FREQ_HZ);
xil_printf("UART Baud Rate: %u bps\n\r", XPAR_PS7_UART_1_UART_CLK_FREQ_HZ / 16);
// 简单的倒计时循环
for (int i = 10; i > 0; i--) {
xil_printf("Counting down: %d\n\r", i);
sleep(1); // 延时1秒
}
xil_printf("Countdown complete!\n\r");
// 清理平台资源
cleanup_platform();
return 0;
}
3.2 GPIO控制实例
c
/**
* gpio_example.c - Zynq PS GPIO控制示例
*
* 演示如何使用EMIO GPIO控制LED和读取按钮状态
*/
#include <stdio.h>
#include "platform.h"
#include "xgpio.h"
#include "xparameters.h"
#include "sleep.h"
// GPIO设备ID(在硬件设计中定义)
#define GPIO_DEVICE_ID XPAR_PS7_GPIO_0_DEVICE_ID
#define LED_CHANNEL 1 // GPIO通道1用于LED控制
#define BTN_CHANNEL 2 // GPIO通道2用于按钮读取
// LED和按钮位掩码
#define LED_0_MASK 0x01 // LED 0对应的位掩码
#define LED_1_MASK 0x02 // LED 1对应的位掩码
#define BTN_0_MASK 0x01 // 按钮0对应的位掩码
#define BTN_1_MASK 0x02 // 按钮1对应的位掩码
int main()
{
int Status;
XGpio Gpio; // GPIO实例
u32 LedData = 0; // LED状态数据
u32 BtnData; // 按钮状态数据
u32 BtnDataOld; // 前一个按钮状态
init_platform();
xil_printf("Zynq PS GPIO Example\n\r");
// 初始化GPIO驱动
Status = XGpio_Initialize(&Gpio, GPIO_DEVICE_ID);
if (Status != XST_SUCCESS) {
xil_printf("GPIO初始化失败\n\r");
return XST_FAILURE;
}
// 设置LED通道为输出,按钮通道为输入
XGpio_SetDataDirection(&Gpio, LED_CHANNEL, 0x00); // 0表示输出
XGpio_SetDataDirection(&Gpio, BTN_CHANNEL, 0xFF); // 1表示输入
// 读取初始按钮状态
BtnDataOld = XGpio_DiscreteRead(&Gpio, BTN_CHANNEL);
xil_printf("按下按钮0控制LED 0,按下按钮1控制LED 1\n\r");
// 主循环
while (1) {
// 读取当前按钮状态
BtnData = XGpio_DiscreteRead(&Gpio, BTN_CHANNEL);
// 检测按钮变化
if (BtnData != BtnDataOld) {
// 按钮0控制LED 0
if ((BtnData & BTN_0_MASK) && !(BtnDataOld & BTN_0_MASK)) {
// 按钮0按下
LedData ^= LED_0_MASK; // 切换LED 0状态
xil_printf("LED 0切换至: %s\n\r", (LedData & LED_0_MASK) ? "开" : "关");
}
// 按钮1控制LED 1
if ((BtnData & BTN_1_MASK) && !(BtnDataOld & BTN_1_MASK)) {
// 按钮1按下
LedData ^= LED_1_MASK; // 切换LED 1状态
xil_printf("LED 1切换至: %s\n\r", (LedData & LED_1_MASK) ? "开" : "关");
}
// 更新LED状态
XGpio_DiscreteWrite(&Gpio, LED_CHANNEL, LedData);
// 保存当前按钮状态
BtnDataOld = BtnData;
}
// 短暂延时以减少循环速度
usleep(1000);
}
cleanup_platform();
return 0;
}
3.3 中断处理实例
c
/**
* interrupt_example.c - Zynq PS中断处理示例
*
* 演示如何设置和处理GPIO中断
*/
#include <stdio.h>
#include "platform.h"
#include "xgpio.h"
#include "xscugic.h"
#include "xil_exception.h"
#include "xparameters.h"
// 设备ID
#define GPIO_DEVICE_ID XPAR_PS7_GPIO_0_DEVICE_ID
#define INTC_DEVICE_ID XPAR_SCUGIC_SINGLE_DEVICE_ID
#define GPIO_INTERRUPT_ID XPAR_XGPIOPS_0_INTR
// GPIO通道和掩码
#define BTN_CHANNEL 1 // 按钮通道
#define LED_CHANNEL 2 // LED通道
#define BTN_MASK 0x03 // 所有按钮掩码
#define LED_MASK 0x03 // 所有LED掩码
// 全局变量
XGpio Gpio; // GPIO实例
XScuGic IntcInst; // 中断控制器实例
static int LedData = 0; // LED状态
// 中断服务程序
void GpioHandler(void *CallbackRef)
{
XGpio *GpioPtr = (XGpio *)CallbackRef;
u32 Pending;
// 读取中断状态
Pending = XGpio_InterruptGetStatus(GpioPtr);
// 清除中断
XGpio_InterruptClear(GpioPtr, Pending);
// 如果是按钮中断
if (Pending & BTN_MASK) {
// 读取按钮状态
u32 BtnState = XGpio_DiscreteRead(GpioPtr, BTN_CHANNEL);
// 根据按钮状态切换LED
if (BtnState & 0x01) {
// 按钮0按下,切换LED 0
LedData ^= 0x01;
xil_printf("按钮0按下,LED 0切换为: %s\n\r",
(LedData & 0x01) ? "开" : "关");
}
if (BtnState & 0x02) {
// 按钮1按下,切换LED 1
LedData ^= 0x02;
xil_printf("按钮1按下,LED 1切换为: %s\n\r",
(LedData & 0x02) ? "开" : "关");
}
// 更新LED状态
XGpio_DiscreteWrite(GpioPtr, LED_CHANNEL, LedData);
}
}
// 设置GPIO中断
int SetupGpioInterrupt(XScuGic *IntcInstancePtr, XGpio *GpioInstancePtr)
{
int Status;
// 连接中断处理函数
Status = XScuGic_Connect(IntcInstancePtr, GPIO_INTERRUPT_ID,
(Xil_InterruptHandler)GpioHandler,
(void *)GpioInstancePtr);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
// 使能中断控制器上的中断
XScuGic_Enable(IntcInstancePtr, GPIO_INTERRUPT_ID);
// 使能GPIO中断
XGpio_InterruptEnable(GpioInstancePtr, BTN_MASK);
XGpio_InterruptGlobalEnable(GpioInstancePtr);
return XST_SUCCESS;
}
// 设置中断系统
int SetupInterruptSystem(XScuGic *IntcInstancePtr)
{
int Status;
// 初始化中断控制器驱动
XScuGic_Config *IntcConfig;
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_INT,
(Xil_ExceptionHandler)XScuGic_InterruptHandler,
IntcInstancePtr);
// 使能异常
Xil_ExceptionEnable();
return XST_SUCCESS;
}
int main()
{
int Status;
init_platform();
xil_printf("Zynq PS中断处理示例\n\r");
// 初始化GPIO
Status = XGpio_Initialize(&Gpio, GPIO_DEVICE_ID);
if (Status != XST_SUCCESS) {
xil_printf("GPIO初始化失败\n\r");
return XST_FAILURE;
}
// 设置GPIO方向
XGpio_SetDataDirection(&Gpio, BTN_CHANNEL, BTN_MASK); // 按钮设为输入
XGpio_SetDataDirection(&Gpio, LED_CHANNEL, 0); // LED设为输出
// 初始化LED为关闭状态
XGpio_DiscreteWrite(&Gpio, LED_CHANNEL, 0);
// 设置中断系统
Status = SetupInterruptSystem(&IntcInst);
if (Status != XST_SUCCESS) {
xil_printf("中断系统设置失败\n\r");
return XST_FAILURE;
}
// 设置GPIO中断
Status = SetupGpioInterrupt(&IntcInst, &Gpio);
if (Status != XST_SUCCESS) {
xil_printf("GPIO中断设置失败\n\r");
return XST_FAILURE;
}
xil_printf("按下按钮0或按钮1以切换对应的LED\n\r");
// 主循环 - 保持程序运行
while (1) {
// 空循环,中断服务程序处理按钮事件
}
cleanup_platform();
return 0;
}
3.4 定时器应用实例
c
/**
* timer_example.c - Zynq PS定时器应用示例
*
* 演示如何配置和使用Private Timer
*/
#include <stdio.h>
#include "platform.h"
#include "xil_printf.h"
#include "xscutimer.h"
#include "xscugic.h"
#include "xil_exception.h"
#include "xparameters.h"
// 设备ID
#define TIMER_DEVICE_ID XPAR_XSCUTIMER_0_DEVICE_ID
#define INTC_DEVICE_ID XPAR_SCUGIC_SINGLE_DEVICE_ID
#define TIMER_IRPT_INTR XPAR_SCUTIMER_INTR
// 定时器参数
#define TIMER_LOAD_VALUE 333333333 // 约3.33秒(基于333.33MHz时钟)
// 全局变量
XScuTimer TimerInst; // 定时器实例
XScuGic IntcInst; // 中断控制器实例
int TimerExpired; // 定时器到期标志
// 定时器中断处理函数
void TimerIntrHandler(void *CallBackRef)
{
XScuTimer *TimerPtr = (XScuTimer *)CallBackRef;
// 清除中断
XScuTimer_ClearInterruptStatus(TimerPtr);
// 设置标志并打印消息
TimerExpired = 1;
xil_printf("定时器中断触发\n\r");
}
// 设置定时器中断
int SetupTimerInterrupt(XScuGic *IntcInstancePtr, XScuTimer *TimerInstancePtr,
u16 TimerIntrId)
{
int Status;
// 连接中断处理函数
Status = XScuGic_Connect(IntcInstancePtr, TimerIntrId,
(Xil_ExceptionHandler)TimerIntrHandler,
(void *)TimerInstancePtr);
if (Status != XST_SUCCESS) {
return Status;
}
// 使能中断
XScuGic_Enable(IntcInstancePtr, TimerIntrId);
// 使能定时器中断
XScuTimer_EnableInterrupt(TimerInstancePtr);
return XST_SUCCESS;
}
// 设置中断系统
int SetupInterruptSystem(XScuGic *IntcInstancePtr)
{
int Status;
// 初始化中断控制器驱动
XScuGic_Config *IntcConfig;
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_INT,
(Xil_ExceptionHandler)XScuGic_InterruptHandler,
IntcInstancePtr);
// 使能异常
Xil_ExceptionEnable();
return XST_SUCCESS;
}
int main()
{
int Status;
int Count = 0;
init_platform();
xil_printf("Zynq PS定时器应用示例\n\r");
// 初始化定时器
XScuTimer_Config *ConfigPtr;
ConfigPtr = XScuTimer_LookupConfig(TIMER_DEVICE_ID);
if (NULL == ConfigPtr) {
return XST_FAILURE;
}
Status = XScuTimer_CfgInitialize(&TimerInst, ConfigPtr,
ConfigPtr->BaseAddr);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
// 设置中断系统
Status = SetupInterruptSystem(&IntcInst);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
// 设置定时器中断
Status = SetupTimerInterrupt(&IntcInst, &TimerInst, TIMER_IRPT_INTR);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
// 配置定时器参数
XScuTimer_SetPrescaler(&TimerInst, 0); // 设置预分频器为0
XScuTimer_LoadTimer(&TimerInst, TIMER_LOAD_VALUE); // 加载计数值
XScuTimer_EnableAutoReload(&TimerInst); // 启用自动重载
// 启动定时器
XScuTimer_Start(&TimerInst);
xil_printf("定时器已启动,等待中断...\n\r");
// 主循环
while (1) {
// 如果定时器到期
if (TimerExpired) {
// 清除标志
TimerExpired = 0;
// 增加计数并打印信息
Count++;
xil_printf("计数: %d\n\r", Count);
// 如果计数达到5次,退出循环
if (Count >= 5) {
break;
}
}
}
// 停止定时器
XScuTimer_Stop(&TimerInst);
xil_printf("定时器已停止,示例完成\n\r");
cleanup_platform();
return 0;
}
4. PS-PL交互与AXI接口
4.1 AXI接口基本概念
AXI(Advanced eXtensible Interface)是ARM AMBA协议的一部分,用于芯片内部的高性能、高带宽和低延迟的通信。Zynq中主要使用的AXI接口如下:
- AXI-Lite:简化的AXI接口,用于低带宽控制和状态寄存器访问
- AXI4:全功能AXI接口,支持突发传输,用于高带宽数据传输
- AXI-Stream:用于单向流式数据传输,无需地址
在Zynq中,PS和PL之间的通信主要通过以下接口:
- GP AXI接口:通用AXI接口,通常使用AXI-Lite,PS作为主设备
- HP AXI接口:高性能AXI接口,使用AXI4,PL作为主设备
- ACP接口:加速一致性端口,允许PL直接访问PS的缓存
4.2 AXI-Lite外设控制示例
c
/**
* axi_lite_example.c - AXI-Lite接口控制示例
*
* 演示如何通过AXI-Lite接口控制PL中的自定义IP
* 假设自定义IP具有以下寄存器布局:
* - 0x00: 控制寄存器
* - 0x04: 状态寄存器
* - 0x08: 数据寄存器(输入)
* - 0x0C: 结果寄存器(输出)
*/
#include <stdio.h>
#include "platform.h"
#include "xil_printf.h"
#include "xil_io.h"
#include "xparameters.h"
#include "sleep.h"
// 自定义IP的基地址(在Vivado中自动生成的xparameters.h中定义)
#define CUSTOM_IP_BASEADDR XPAR_CUSTOM_IP_0_S00_AXI_BASEADDR
// 寄存器偏移量
#define CONTROL_REG_OFFSET 0x00
#define STATUS_REG_OFFSET 0x04
#define DATA_REG_OFFSET 0x08
#define RESULT_REG_OFFSET 0x0C
// 控制寄存器位定义
#define CTRL_START_BIT 0x01 // 开始运算
#define CTRL_RESET_BIT 0x02 // 复位IP
#define CTRL_MODE_MASK 0x0C // 运算模式(2位)
#define CTRL_MODE_ADD 0x00 // 加法模式
#define CTRL_MODE_SUB 0x04 // 减法模式
#define CTRL_MODE_MUL 0x08 // 乘法模式
#define CTRL_MODE_DIV 0x0C // 除法模式
// 状态寄存器位定义
#define STATUS_DONE_BIT 0x01 // 运算完成
#define STATUS_BUSY_BIT 0x02 // 运算中
#define STATUS_ERROR_BIT 0x04 // 错误标志
// 辅助函数:等待操作完成
static int WaitForCompletion(u32 timeout_ms)
{
u32 status;
int timeout_count = 0;
const int delay_us = 1000; // 1ms检查间隔
const int max_count = timeout_ms;
while (timeout_count < max_count) {
// 读取状态寄存器
status = Xil_In32(CUSTOM_IP_BASEADDR + STATUS_REG_OFFSET);
// 检查完成位
if (status & STATUS_DONE_BIT) {
return XST_SUCCESS;
}
// 检查错误位
if (status & STATUS_ERROR_BIT) {
xil_printf("操作出错,状态寄存器: 0x%08x\n\r", status);
return XST_FAILURE;
}
// 等待1ms
usleep(delay_us);
timeout_count++;
}
xil_printf("操作超时\n\r");
return XST_FAILURE;
}
// 执行数学运算
static int PerformOperation(u32 operand1, u32 operand2, u8 operation, u32 *result)
{
int Status;
u32 control_value = 0;
// 根据操作类型设置控制值
switch (operation) {
case 0: // 加法
control_value = CTRL_MODE_ADD;
break;
case 1: // 减法
control_value = CTRL_MODE_SUB;
break;
case 2: // 乘法
control_value = CTRL_MODE_MUL;
break;
case 3: // 除法
control_value = CTRL_MODE_DIV;
// 检查除数是否为0
if (operand2 == 0) {
xil_printf("错误:除数不能为0\n\r");
return XST_FAILURE;
}
break;
default:
xil_printf("错误:不支持的操作类型\n\r");
return XST_FAILURE;
}
// 先复位IP
Xil_Out32(CUSTOM_IP_BASEADDR + CONTROL_REG_OFFSET, CTRL_RESET_BIT);
usleep(1000); // 等待复位完成
// 写入操作数
Xil_Out32(CUSTOM_IP_BASEADDR + DATA_REG_OFFSET, operand1);
Xil_Out32(CUSTOM_IP_BASEADDR + DATA_REG_OFFSET + 4, operand2); // 假设有两个连续的数据寄存器
// 写入控制寄存器并启动操作
Xil_Out32(CUSTOM_IP_BASEADDR + CONTROL_REG_OFFSET, control_value | CTRL_START_BIT);
// 等待操作完成
Status = WaitForCompletion(1000); // 最多等待1秒
if (Status != XST_SUCCESS) {
return Status;
}
// 读取结果
*result = Xil_In32(CUSTOM_IP_BASEADDR + RESULT_REG_OFFSET);
return XST_SUCCESS;
}
int main()
{
int Status;
u32 result;
init_platform();
xil_printf("AXI-Lite接口控制示例\n\r");
// 测试加法操作
xil_printf("\n测试加法运算:25 + 17\n\r");
Status = PerformOperation(25, 17, 0, &result);
if (Status == XST_SUCCESS) {
xil_printf("结果:%d\n\r", result);
}
// 测试减法操作
xil_printf("\n测试减法运算:100 - 42\n\r");
Status = PerformOperation(100, 42, 1, &result);
if (Status == XST_SUCCESS) {
xil_printf("结果:%d\n\r", result);
}
// 测试乘法操作
xil_printf("\n测试乘法运算:12 * 15\n\r");
Status = PerformOperation(12, 15, 2, &result);
if (Status == XST_SUCCESS) {
xil_printf("结果:%d\n\r", result);
}
// 测试除法操作
xil_printf("\n测试除法运算:144 / 12\n\r");
Status = PerformOperation(144, 12, 3, &result);
if (Status == XST_SUCCESS) {
xil_printf("结果:%d\n\r", result);
}
// 测试除法错误情况
xil_printf("\n测试除法错误:100 / 0\n\r");
Status = PerformOperation(100, 0, 3, &result);
if (Status != XST_SUCCESS) {
xil_printf("除零错误处理成功\n\r");
}
xil_printf("\n示例完成\n\r");
cleanup_platform();
return 0;
}
4.3 AXI-DMA传输示例
c
/**
* axi_dma_example.c - AXI DMA传输示例
*
* 演示如何使用AXI-DMA在PS和PL之间传输数据
* 假设在PL中实现了一个简单的数据处理IP(如滤波器)
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "platform.h"
#include "xil_printf.h"
#include "xil_cache.h"
#include "xaxidma.h"
#include "xparameters.h"
// 设备参数
#define DMA_DEV_ID XPAR_AXIDMA_0_DEVICE_ID
#define DDR_BASE_ADDR XPAR_PS7_DDR_0_S_AXI_BASEADDR
#define MEM_BASE_ADDR (DDR_BASE_ADDR + 0x1000000) // 偏移16MB
// 缓冲区参数
#define TX_BUFFER_BASE MEM_BASE_ADDR
#define RX_BUFFER_BASE (MEM_BASE_ADDR + 0x100000) // 偏移1MB
#define MAX_PKT_LEN 0x100 // 最大传输包长度
// 全局变量
static XAxiDma AxiDma; // AXI DMA实例
// DMA初始化函数
int InitDma()
{
XAxiDma_Config *CfgPtr;
int Status;
// 查找DMA配置
CfgPtr = XAxiDma_LookupConfig(DMA_DEV_ID);
if (!CfgPtr) {
xil_printf("查找DMA配置失败\n\r");
return XST_FAILURE;
}
// 初始化DMA引擎
Status = XAxiDma_CfgInitialize(&AxiDma, CfgPtr);
if (Status != XST_SUCCESS) {
xil_printf("DMA初始化失败,错误码: %d\n\r", Status);
return XST_FAILURE;
}
// 检查是否支持Scatter Gather模式
if (XAxiDma_HasSg(&AxiDma)) {
xil_printf("需要禁用Scatter Gather模式\n\r");
return XST_FAILURE;
}
// 重置DMA引擎
XAxiDma_Reset(&AxiDma);
while (!XAxiDma_ResetIsDone(&AxiDma));
return XST_SUCCESS;
}
// 准备测试数据
void PrepareTestData(u8 *TxBufferPtr, int BufferLength)
{
int Index;
// 生成测试数据(这里使用递增序列)
for (Index = 0; Index < BufferLength; Index++) {
TxBufferPtr[Index] = (u8)Index;
}
// 清空接收缓冲区
memset((void *)RX_BUFFER_BASE, 0, BufferLength);
// 刷新缓存,确保数据实际写入内存
Xil_DCacheFlushRange((UINTPTR)TxBufferPtr, BufferLength);
Xil_DCacheFlushRange((UINTPTR)RX_BUFFER_BASE, BufferLength);
}
// 验证接收到的数据
int CheckData(u8 *TxBufferPtr, u8 *RxBufferPtr, int BufferLength)
{
int Index;
// 先使缓存无效,确保从内存读取最新数据
Xil_DCacheInvalidateRange((UINTPTR)RxBufferPtr, BufferLength);
// 逐字节验证结果
// 注意:在实际应用中,应该根据PL处理逻辑的预期结果来修改此处的验证逻辑
for (Index = 0; Index < BufferLength; Index++) {
// 这里假设PL简单地将每个字节加1
if (RxBufferPtr[Index] != (u8)(TxBufferPtr[Index] + 1)) {
xil_printf("数据不匹配在位置 %d: 期望 %d, 实际 %d\n\r",
Index, (TxBufferPtr[Index] + 1), RxBufferPtr[Index]);
return XST_FAILURE;
}
}
return XST_SUCCESS;
}
// 执行单次传输并验证
int PerformTransfer(int BufferLength)
{
int Status;
u8 *TxBufferPtr = (u8 *)TX_BUFFER_BASE;
u8 *RxBufferPtr = (u8 *)RX_BUFFER_BASE;
xil_printf("开始传输, 数据长度: %d字节...\n\r", BufferLength);
// 准备测试数据
PrepareTestData(TxBufferPtr, BufferLength);
// 启动DMA传输
Status = XAxiDma_SimpleTransfer(&AxiDma, (UINTPTR)RX_BUFFER_BASE,
BufferLength, XAXIDMA_DEVICE_TO_DMA);
if (Status != XST_SUCCESS) {
xil_printf("RX Simple Transfer失败\n\r");
return XST_FAILURE;
}
Status = XAxiDma_SimpleTransfer(&AxiDma, (UINTPTR)TX_BUFFER_BASE,
BufferLength, XAXIDMA_DMA_TO_DEVICE);
if (Status != XST_SUCCESS) {
xil_printf("TX Simple Transfer失败\n\r");
return XST_FAILURE;
}
// 等待传输完成
while (XAxiDma_Busy(&AxiDma, XAXIDMA_DMA_TO_DEVICE));
while (XAxiDma_Busy(&AxiDma, XAXIDMA_DEVICE_TO_DMA));
// 验证结果
Status = CheckData(TxBufferPtr, RxBufferPtr, BufferLength);
if (Status != XST_SUCCESS) {
xil_printf("数据验证失败\n\r");
return XST_FAILURE;
}
xil_printf("传输成功完成!\n\r");
return XST_SUCCESS;
}
int main()
{
int Status;
init_platform();
xil_printf("AXI DMA传输示例\n\r");
// 初始化DMA
Status = InitDma();
if (Status != XST_SUCCESS) {
xil_printf("DMA初始化失败\n\r");
return XST_FAILURE;
}
// 测试不同大小的传输
Status = PerformTransfer(64); // 小数据包
if (Status != XST_SUCCESS) {
goto Done;
}
Status = PerformTransfer(256); // 中等数据包
if (Status != XST_SUCCESS) {
goto Done;
}
Status = PerformTransfer(MAX_PKT_LEN); // 最大数据包
if (Status != XST_SUCCESS) {
goto Done;
}
xil_printf("\n所有测试都通过!\n\r");
Done:
cleanup_platform();
return Status;
}
4.4 基于C++的AXI接口抽象封装
cpp
/**
* axi_wrapper.hpp - AXI接口的C++抽象封装
*
* 提供面向对象的AXI接口访问封装,简化硬件访问
*/
#ifndef AXI_WRAPPER_HPP
#define AXI_WRAPPER_HPP
#include <cstdint>
#include <string>
#include <stdexcept>
#include <functional>
// 导入Xilinx基础库
extern "C" {
#include "xil_io.h"
#include "xil_cache.h"
#include "xstatus.h"
}
// 前向声明
class AXI_Lite_Device;
class AXI_DMA_Device;
/**
* AXI_Exception 类:表示AXI操作异常
*/
class AXI_Exception : public std::runtime_error {
public:
explicit AXI_Exception(const std::string& message)
: std::runtime_error(message) {}
};
/**
* AXI_Lite_Device 类:封装AXI-Lite设备访问
*/
class AXI_Lite_Device {
public:
/**
* 构造函数
*
* @param base_addr 设备基地址
* @param name 设备名称(可选)
*/
AXI_Lite_Device(uint32_t base_addr, const std::string& name = "Unknown")
: base_address_(base_addr), device_name_(name) {}
// 禁用拷贝
AXI_Lite_Device(const AXI_Lite_Device&) = delete;
AXI_Lite_Device& operator=(const AXI_Lite_Device&) = delete;
/**
* 写寄存器
*
* @param offset 寄存器偏移量
* @param value 要写入的值
*/
void writeReg(uint32_t offset, uint32_t value) const {
Xil_Out32(base_address_ + offset, value);
}
/**
* 读寄存器
*
* @param offset 寄存器偏移量
* @return 读取的值
*/
uint32_t readReg(uint32_t offset) const {
return Xil_In32(base_address_ + offset);
}
/**
* 设置寄存器位
*
* @param offset 寄存器偏移量
* @param bit_mask 位掩码
*/
void setBits(uint32_t offset, uint32_t bit_mask) const {
uint32_t value = readReg(offset);
writeReg(offset, value | bit_mask);
}
/**
* 清除寄存器位
*
* @param offset 寄存器偏移量
* @param bit_mask 位掩码
*/
void clearBits(uint32_t offset, uint32_t bit_mask) const {
uint32_t value = readReg(offset);
writeReg(offset, value & ~bit_mask);
}
/**
* 等待寄存器位变为指定值
*
* @param offset 寄存器偏移量
* @param bit_mask 位掩码
* @param expected_value 期望值(0或1)
* @param timeout_ms 超时时间(毫秒)
* @return 是否成功(超时返回false)
*/
bool waitForBits(uint32_t offset, uint32_t bit_mask,
bool expected_value, uint32_t timeout_ms) const {
uint32_t expected = expected_value ? bit_mask : 0;
for (uint32_t i = 0; i < timeout_ms; i++) {
uint32_t value = readReg(offset) & bit_mask;
if (value == expected) {
return true;
}
// 延时1毫秒
usleep(1000);
}
return false;
}
/**
* 获取设备名称
*
* @return 设备名称
*/
const std::string& getName() const {
return device_name_;
}
/**
* 获取设备基地址
*
* @return 基地址
*/
uint32_t getBaseAddress() const {
return base_address_;
}
private:
uint32_t base_address_; // 设备基地址
std::string device_name_; // 设备名称
};
/**
* Memory_Buffer 类:表示DMA可访问的内存缓冲区
*/
class Memory_Buffer {
public:
/**
* 构造函数
*
* @param address 物理地址
* @param size 大小(字节)
*/
Memory_Buffer(uint64_t address, size_t size)
: physical_address_(address), buffer_size_(size) {}
/**
* 获取物理地址
*
* @return 物理地址
*/
uint64_t getPhysicalAddress() const {
return physical_address_;
}
/**
* 获取虚拟地址
*
* @return 虚拟地址
*/
template <typename T = void*>
T getVirtualAddress() const {
return reinterpret_cast<T>(physical_address_);
}
/**
* 获取缓冲区大小
*
* @return 缓冲区大小(字节)
*/
size_t getSize() const {
return buffer_size_;
}
/**
* 清空缓冲区
*/
void clear() const {
std::memset(getVirtualAddress<void*>(), 0, buffer_size_);
flushCache();
}
/**
* 刷新缓存
*/
void flushCache() const {
Xil_DCacheFlushRange(physical_address_, buffer_size_);
}
/**
* 使缓存无效
*/
void invalidateCache() const {
Xil_DCacheInvalidateRange(physical_address_, buffer_size_);
}
private:
uint64_t physical_address_; // 物理地址
size_t buffer_size_; // 缓冲区大小
};
/**
* AXI_DMA_Device 类:封装AXI-DMA设备操作
*/
class AXI_DMA_Device {
public:
// DMA传输方向
enum Direction {
PS_TO_PL = 0, // 内存到流
PL_TO_PS = 1 // 流到内存
};
/**
* 构造函数
*
* @param tx_base_addr MM2S通道基地址
* @param rx_base_addr S2MM通道基地址
* @param name 设备名称(可选)
*/
AXI_DMA_Device(uint32_t tx_base_addr, uint32_t rx_base_addr,
const std::string& name = "DMA")
: tx_controller_(tx_base_addr, name + "_TX"),
rx_controller_(rx_base_addr, name + "_RX"),
device_name_(name) {
// 复位DMA引擎
resetDMA();
}
// 禁用拷贝
AXI_DMA_Device(const AXI_DMA_Device&) = delete;
AXI_DMA_Device& operator=(const AXI_DMA_Device&) = delete;
/**
* 复位DMA引擎
*/
void resetDMA() {
// 复位TX通道
tx_controller_.writeReg(0x00, 0x00000004); // MM2S_DMACR
// 复位RX通道
rx_controller_.writeReg(0x30, 0x00000004); // S2MM_DMACR
// 等待复位完成
bool tx_reset_done = tx_controller_.waitForBits(0x00, 0x00000004, false, 1000);
bool rx_reset_done = rx_controller_.waitForBits(0x30, 0x00000004, false, 1000);
if (!tx_reset_done || !rx_reset_done) {
throw AXI_Exception("DMA引擎复位超时");
}
// 启用DMA
tx_controller_.writeReg(0x00, 0x00000001); // MM2S_DMACR
rx_controller_.writeReg(0x30, 0x00000001); // S2MM_DMACR
}
/**
* 启动DMA传输
*
* @param buffer 内存缓冲区
* @param size 传输大小(字节)
* @param direction 传输方向
*/
void startTransfer(const Memory_Buffer& buffer, size_t size, Direction direction) {
if (direction == PS_TO_PL) {
// 写入MM2S地址
tx_controller_.writeReg(0x18, buffer.getPhysicalAddress() & 0xFFFFFFFF);
tx_controller_.writeReg(0x1C, (buffer.getPhysicalAddress() >> 32) & 0xFFFFFFFF);
// 确保缓存刷新
buffer.flushCache();
// 写入传输长度
tx_controller_.writeReg(0x28, size);
} else {
// 清空接收缓冲区
buffer.clear();
// 写入S2MM地址
rx_controller_.writeReg(0x48, buffer.getPhysicalAddress() & 0xFFFFFFFF);
rx_controller_.writeReg(0x4C, (buffer.getPhysicalAddress() >> 32) & 0xFFFFFFFF);
// 写入传输长度
rx_controller_.writeReg(0x58, size);
}
}
/**
* 等待DMA传输完成
*
* @param direction 传输方向
* @param timeout_ms 超时时间(毫秒)
* @return 传输是否成功完成
*/
bool waitForTransferComplete(Direction direction, uint32_t timeout_ms) {
if (direction == PS_TO_PL) {
// 等待MM2S传输完成
return tx_controller_.waitForBits(0x04, 0x00001000, true, timeout_ms);
} else {
// 等待S2MM传输完成
bool transfer_done = rx_controller_.waitForBits(0x34, 0x00001000, true, timeout_ms);
if (transfer_done) {
// 对于PL到PS的传输,需要使缓存无效
// 这里我们不能使缓存无效,因为没有相应的Buffer实例
// 使用者需要手动调用buffer.invalidateCache()
return true;
}
return false;
}
}
/**
* 检查DMA是否忙
*
* @param direction 传输方向
* @return 通道是否忙
*/
bool isBusy(Direction direction) const {
if (direction == PS_TO_PL) {
return (tx_controller_.readReg(0x04) & 0x00000001) != 0;
} else {
return (rx_controller_.readReg(0x34) & 0x00000001) != 0;
}
}
/**
* 获取传输状态
*
* @param direction 传输方向
* @return 状态寄存器值
*/
uint32_t getStatus(Direction direction) const {
if (direction == PS_TO_PL) {
return tx_controller_.readReg(0x04); // MM2S_DMASR
} else {
return rx_controller_.readReg(0x34); // S2MM_DMASR
}
}
/**
* 执行双向DMA传输
*
* @param tx_buffer 发送缓冲区
* @param rx_buffer 接收缓冲区
* @param size 传输大小(字节)
* @param timeout_ms 超时时间(毫秒)
* @return 传输是否成功
*/
bool performTransfer(const Memory_Buffer& tx_buffer,
const Memory_Buffer& rx_buffer,
size_t size,
uint32_t timeout_ms) {
// 启动接收
startTransfer(rx_buffer, size, PL_TO_PS);
// 启动发送
startTransfer(tx_buffer, size, PS_TO_PL);
// 等待传输完成
bool tx_done = waitForTransferComplete(PS_TO_PL, timeout_ms);
bool rx_done = waitForTransferComplete(PL_TO_PS, timeout_ms);
// 使接收缓存无效(确保从内存读取最新数据)
rx_buffer.invalidateCache();
return tx_done && rx_done;
}
/**
* 获取设备名称
*
* @return 设备名称
*/
const std::string& getName() const {
return device_name_;
}
private:
AXI_Lite_Device tx_controller_; // 发送控制器(MM2S)
AXI_Lite_Device rx_controller_; // 接收控制器(S2MM)
std::string device_name_; // 设备名称
};
#endif // AXI_WRAPPER_HPP
cpp
/**
* axi_cpp_example.cpp - 基于C++的AXI接口使用示例
*
* 演示如何使用C++封装的AXI接口访问硬件
*/
#include <iostream>
#include <iomanip>
#include <string>
#include <cstring>
#include <cstdlib>
#include <ctime>
#include "platform.h"
#include "xil_printf.h"
#include "xparameters.h"
#include "sleep.h"
#include "axi_wrapper.hpp"
// 设备参数
#define CUSTOM_IP_BASEADDR XPAR_CUSTOM_IP_0_S00_AXI_BASEADDR
#define DMA_MM2S_BASEADDR XPAR_AXIDMA_0_BASEADDR
#define DMA_S2MM_BASEADDR (XPAR_AXIDMA_0_BASEADDR + 0x30)
#define DDR_BASE_ADDR XPAR_PS7_DDR_0_S_AXI_BASEADDR
#define MEM_BASE_ADDR (DDR_BASE_ADDR + 0x1000000) // 偏移16MB
#define TX_BUFFER_BASE MEM_BASE_ADDR
#define RX_BUFFER_BASE (MEM_BASE_ADDR + 0x100000) // 偏移1MB
#define MAX_PKT_LEN 0x1000 // 4KB
// 自定义IP寄存器定义
#define CONTROL_REG 0x00
#define STATUS_REG 0x04
#define DATA_IN_REG 0x08
#define DATA_OUT_REG 0x0C
// 控制寄存器位
#define CTRL_START 0x01
#define CTRL_RESET 0x02
#define CTRL_INTR_EN 0x04
// 状态寄存器位
#define STATUS_DONE 0x01
#define STATUS_BUSY 0x02
#define STATUS_ERROR 0x04
/**
* 自定义IP控制器类
*/
class Custom_IP_Controller {
public:
/**
* 构造函数
*
* @param base_addr 设备基地址
*/
Custom_IP_Controller(uint32_t base_addr)
: device_(base_addr, "CustomIP") {
// 复位设备
device_.writeReg(CONTROL_REG, CTRL_RESET);
usleep(1000); // 等待复位完成
device_.writeReg(CONTROL_REG, 0);
}
/**
* 处理数据
*
* @param input_data 输入数据
* @return 处理后的数据
*/
uint32_t processData(uint32_t input_data) {
// 写入输入数据
device_.writeReg(DATA_IN_REG, input_data);
// 启动处理
device_.writeReg(CONTROL_REG, CTRL_START);
// 等待处理完成
if (!device_.waitForBits(STATUS_REG, STATUS_DONE, true, 1000)) {
throw AXI_Exception("处理超时或出错");
}
// 检查错误状态
if (device_.readReg(STATUS_REG) & STATUS_ERROR) {
throw AXI_Exception("处理过程中发生错误");
}
// 读取结果
return device_.readReg(DATA_OUT_REG);
}
/**
* 打印设备状态
*/
void printStatus() const {
uint32_t status = device_.readReg(STATUS_REG);
std::cout << "设备状态: ";
std::cout << "完成=" << ((status & STATUS_DONE) ? "是" : "否") << ", ";
std::cout << "忙=" << ((status & STATUS_BUSY) ? "是" : "否") << ", ";
std::cout << "错误=" << ((status & STATUS_ERROR) ? "是" : "否") << std::endl;
}
private:
AXI_Lite_Device device_;
};
/**
* 准备DMA测试数据
*
* @param buffer 内存缓冲区
* @param pattern 数据模式 (0=递增, 1=随机)
*/
void prepareTestData(const Memory_Buffer& buffer, int pattern) {
uint8_t* data = buffer.getVirtualAddress<uint8_t*>();
if (pattern == 0) {
// 递增模式
for (size_t i = 0; i < buffer.getSize(); i++) {
data[i] = i & 0xFF;
}
} else {
// 随机模式
for (size_t i = 0; i < buffer.getSize(); i++) {
data[i] = rand() & 0xFF;
}
}
buffer.flushCache();
}
/**
* 打印缓冲区内容
*
* @param buffer 内存缓冲区
* @param offset 起始偏移
* @param length 显示长度
*/
void printBuffer(const Memory_Buffer& buffer, size_t offset, size_t length) {
uint8_t* data = buffer.getVirtualAddress<uint8_t*>();
std::cout << "缓冲区内容 [" << std::hex << buffer.getPhysicalAddress()
<< " + " << std::dec << offset << "]:" << std::endl;
// 限制打印长度
size_t max_length = std::min(length, buffer.getSize() - offset);
for (size_t i = 0; i < max_length; i++) {
if (i % 16 == 0) {
std::cout << std::hex << std::setw(4) << std::setfill('0') << i << ": ";
}
std::cout << std::hex << std::setw(2) << std::setfill('0')
<< static_cast<int>(data[offset + i]) << " ";
if ((i + 1) % 16 == 0 || i == max_length - 1) {
std::cout << std::endl;
}
}
std::cout << std::dec; // 恢复十进制输出
}
/**
* 比较缓冲区
*
* @param buffer1 第一个缓冲区
* @param buffer2 第二个缓冲区
* @param length 比较长度
* @return 是否匹配
*/
bool compareBuffers(const Memory_Buffer& buffer1, const Memory_Buffer& buffer2, size_t length) {
uint8_t* data1 = buffer1.getVirtualAddress<uint8_t*>();
uint8_t* data2 = buffer2.getVirtualAddress<uint8_t*>();
size_t max_length = std::min({length, buffer1.getSize(), buffer2.getSize()});
for (size_t i = 0; i < max_length; i++) {
if (data1[i] != data2[i]) {
std::cout << "数据不匹配在位置 " << i << ": "
<< static_cast<int>(data1[i]) << " != "
<< static_cast<int>(data2[i]) << std::endl;
return false;
}
}
return true;
}
/**
* 主函数
*/
int main() {
int status = 0;
init_platform();
try {
std::cout << "C++ AXI接口示例" << std::endl;
std::cout << "==================" << std::endl;
// 初始化随机数生成器
srand(time(NULL));
// 演示AXI-Lite设备访问
std::cout << "\n1. AXI-Lite设备访问示例" << std::endl;
std::cout << "---------------------------" << std::endl;
Custom_IP_Controller ipController(CUSTOM_IP_BASEADDR);
try {
// 处理一些测试数据
uint32_t result1 = ipController.processData(0x12345678);
std::cout << "输入: 0x12345678, 输出: 0x" << std::hex << result1 << std::dec << std::endl;
uint32_t result2 = ipController.processData(0xABCDEF01);
std::cout << "输入: 0xABCDEF01, 输出: 0x" << std::hex << result2 << std::dec << std::endl;
ipController.printStatus();
}
catch (const AXI_Exception& e) {
std::cout << "处理过程中出错: " << e.what() << std::endl;
}
// 演示AXI-DMA传输
std::cout << "\n2. AXI-DMA传输示例" << std::endl;
std::cout << "---------------------------" << std::endl;
// 创建DMA控制器
AXI_DMA_Device dmaController(DMA_MM2S_BASEADDR, DMA_S2MM_BASEADDR, "TestDMA");
// 创建内存缓冲区
Memory_Buffer txBuffer(TX_BUFFER_BASE, MAX_PKT_LEN);
Memory_Buffer rxBuffer(RX_BUFFER_BASE, MAX_PKT_LEN);
// 测试不同大小的传输
size_t test_sizes[] = {64, 256, 1024, 4096};
for (size_t size : test_sizes) {
std::cout << "\n传输大小: " << size << " 字节" << std::endl;
// 准备测试数据
prepareTestData(txBuffer, 0); // 使用递增模式
// 打印发送缓冲区
std::cout << "发送数据(前32字节):" << std::endl;
printBuffer(txBuffer, 0, 32);
// 执行传输
bool success = dmaController.performTransfer(txBuffer, rxBuffer, size, 5000);
if (success) {
std::cout << "传输成功!" << std::endl;
// 打印接收缓冲区
std::cout << "接收数据(前32字节):" << std::endl;
printBuffer(rxBuffer, 0, 32);
// 验证数据 - 根据PL中的实际处理逻辑修改此处的验证逻辑
// 这里假设PL直接传递数据,不做修改
if (compareBuffers(txBuffer, rxBuffer, size)) {
std::cout << "数据验证通过!" << std::endl;
} else {
std::cout << "数据验证失败!" << std::endl;
status = -1;
}
} else {
std::cout << "传输失败! DMA状态:" << std::endl;
std::cout << " TX状态: 0x" << std::hex << dmaController.getStatus(AXI_DMA_Device::PS_TO_PL) << std::endl;
std::cout << " RX状态: 0x" << std::hex << dmaController.getStatus(AXI_DMA_Device::PL_TO_PS) << std::endl;
std::cout << std::dec;
status = -1;
break;
}
}
std::cout << "\n示例完成" << std::endl;
}
catch (const AXI_Exception& e) {
std::cout << "错误: " << e.what() << std::endl;
status = -1;
}
catch (const std::exception& e) {
std::cout << "标准异常: " << e.what() << std::endl;
status = -1;
}
catch (...) {
std::cout << "未知异常!" << std::endl;
status = -1;
}
cleanup_platform();
return status;
}
5. Zynq Linux应用开发
5.1 PetaLinux环境搭建与配置
PetaLinux是Xilinx提供的工具集,用于在Zynq等嵌入式平台上构建和定制Linux系统。以下是基本的步骤概述:
-
安装PetaLinux工具:
bash
# 安装依赖包 sudo apt-get install gcc git make net-tools libncurses5-dev tftpd zlib1g-dev sudo apt-get install libssl-dev flex bison chrpath socat xterm autoconf libtool sudo apt-get install texinfo zlib1g-dev gcc-multilib build-essential # 安装PetaLinux chmod +x petalinux-v2023.1-installer.run ./petalinux-v2023.1-installer.run --dir /opt/petalinux/2023.1
-
设置环境:
bash
source /opt/petalinux/2023.1/settings.sh
-
创建PetaLinux项目:
bash
# 基于模板创建项目 petalinux-create --type project --template zynq --name my_linux_project # 或基于硬件描述文件创建 petalinux-create --type project --template zynq --name my_linux_project cd my_linux_project petalinux-config --get-hw-description=/path/to/hardware/design/
-
配置Linux系统:
bash
# 系统配置 petalinux-config # 内核配置 petalinux-config -c kernel # 根文件系统配置 petalinux-config -c rootfs
-
构建系统:
bash
petalinux-build
-
打包镜像:
bash
petalinux-package --boot --format BIN --fsbl images/linux/zynq_fsbl.elf --fpga images/linux/system.bit --u-boot
5.2 基于Linux的GPIO控制
c
/**
* linux_gpio.c - Linux下GPIO控制示例
*
* 演示如何在Linux中通过sysfs接口控制GPIO
* 编译: gcc -o linux_gpio linux_gpio.c
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <poll.h>
#include <errno.h>
// GPIO管理函数
int gpio_export(unsigned int gpio)
{
int fd, len;
char buf[64];
fd = open("/sys/class/gpio/export", O_WRONLY);
if (fd < 0) {
perror("gpio/export");
return fd;
}
len = snprintf(buf, sizeof(buf), "%d", gpio);
write(fd, buf, len);
close(fd);
return 0;
}
int gpio_unexport(unsigned int gpio)
{
int fd, len;
char buf[64];
fd = open("/sys/class/gpio/unexport", O_WRONLY);
if (fd < 0) {
perror("gpio/unexport");
return fd;
}
len = snprintf(buf, sizeof(buf), "%d", gpio);
write(fd, buf, len);
close(fd);
return 0;
}
int gpio_set_dir(unsigned int gpio, const char* dir)
{
int fd, len;
char buf[64];
len = snprintf(buf, sizeof(buf), "/sys/class/gpio/gpio%d/direction", gpio);
fd = open(buf, O_WRONLY);
if (fd < 0) {
perror("gpio/direction");
return fd;
}
write(fd, dir, strlen(dir));
close(fd);
return 0;
}
int gpio_set_value(unsigned int gpio, unsigned int value)
{
int fd, len;
char buf[64];
len = snprintf(buf, sizeof(buf), "/sys/class/gpio/gpio%d/value", gpio);
fd = open(buf, O_WRONLY);
if (fd < 0) {
perror("gpio/value");
return fd;
}
if (value)
write(fd, "1", 1);
else
write(fd, "0", 1);
close(fd);
return 0;
}
int gpio_get_value(unsigned int gpio)
{
int fd, len, value;
char buf[64];
char ch;
len = snpr