在ZYNQ-Linux下操作GPIO和VDMA

目录

  • 1. 概述
  • 2. GPIO操作
    • 2.1 确定编号
    • 2.2 初始化
    • 2.3 操作GPIO
  • 3. VDMA操作
    • 3.1 设置VDMA
    • 3.2 VDMA操作代码
  • 4. 结语

1. 概述

此前的文章介绍如何利用petalinux定制ZYNQ-Linux操作系统。当ZYNQ-Linux系统搭建完毕后,需要在这个系统上开发应用程序以完成特定任务,这里面就涉及到如何在ZYNQ-Linux系统上去操作系统硬件资源的问题。目前,网上介绍的比较多的是需要改写Linux操作系统底层的设备树,并编写设备的驱动程序,这样的好处是可以提供一个统一的硬件接口,做到软硬件分离,可以更好地去保护硬件资源,但是实际操作对于初学者来说比较复杂。
本文将介绍一种简单的操作硬件方法,对于一些简单应用,可以直接操作系统的硬件资源,读写寄存器,不需要改写设备树和编写驱动程序。本文将以操作GPIO和操作VDMA两个例子做说明。

2. GPIO操作

本次操作利用GPIO实现LED的点亮及熄灭。硬件如下图所示,LED和ZYNQ的MIO7引脚进行相连。
在ZYNQ-Linux下操作GPIO和VDMA_第1张图片

2.1 确定编号

在目标板的系统中进行入/sys/class/gpio​中查看第一个IO设备的编号,从下图可以看出笔者的目标板第一个IO设备的编号为906
在查找第一个IO设备的编号

2.2 初始化

初始化的代码如下

FILE *fp = NULL;
char path[64];
int led_num = 913;

fp = fopen("/sys/class/gpio/export", "w");
if (fp == NULL)
	perror("export open filed");
else
	fprintf(fp, "%d", led_num); //创建接口
fclose(fp);

memset(path, 0, sizeof path);
snprintf(path, sizeof(path), "/sys/class/gpio/gpio%d/direction", led_num); //将可变参数格式化成字符串存入path[]

fp = fopen(path, "w");
fprintf(fp, "out"); //设置为输出
fclose(fp);

初始化过程完成了两件事,一个是创建了接口,一个是设置了接口的输入和输出特性。

2.3 操作GPIO

操作GPIO,控制LED灯的亮灭。

FILE *fp = NULL;
int led_num = 913;
char path[50] = {"\0"};

sprintf(path, "/sys/class/gpio/gpio%d/value", led_num);
fp = fopen(path, "w");
fprintf(fp, "%d", 1); // LED亮
//fprintf(fp, "%d", 0); // LED灭
fclose(fp);

跟前面设置GPIO特性一样,对于GPIO的操作,同样也是基于一个文件操作。

3. VDMA操作

本次操作利用VDMA将LMH0341接收到视频写入到DDR内存中,再从内存从读取并通过LMH0340进行输出。ZYNQ的模块连接图如下图所示。
在ZYNQ-Linux下操作GPIO和VDMA_第2张图片

3.1 设置VDMA

首先需要确认VDMA地址,可以利用vivado软件查看VDMA,打开vivado软件并选择Address Editor选项卡可以看到VDMA的地址。如下图。
在ZYNQ-Linux下操作GPIO和VDMA_第3张图片
VDMA的设置如下
在ZYNQ-Linux下操作GPIO和VDMA_第4张图片
LMH0341接收的数据是10Bit的宽度,一个像素值的数据长度是20Bit,所以stream data width设置为24,内部memory map data width设置32。帧缓存的深度设置为3帧。

3.2 VDMA操作代码

VDMA实现视频数据搬运的代码如下。

#include 
#include 
#include 
#include 
#include 
#include 

/* Register offsets */
#define OFFSET_PARK_PTR_REG                     0x28
#define OFFSET_VERSION                          0x2c

#define OFFSET_VDMA_MM2S_CONTROL_REGISTER       0x00
#define OFFSET_VDMA_MM2S_STATUS_REGISTER        0x04
#define OFFSET_VDMA_MM2S_VSIZE                  0x50
#define OFFSET_VDMA_MM2S_HSIZE                  0x54
#define OFFSET_VDMA_MM2S_FRMDLY_STRIDE          0x58
#define OFFSET_VDMA_MM2S_FRAMEBUFFER1           0x5c
#define OFFSET_VDMA_MM2S_FRAMEBUFFER2           0x60
#define OFFSET_VDMA_MM2S_FRAMEBUFFER3           0x64
#define OFFSET_VDMA_MM2S_FRAMEBUFFER4           0x68

#define OFFSET_VDMA_S2MM_CONTROL_REGISTER       0x30
#define OFFSET_VDMA_S2MM_STATUS_REGISTER        0x34
#define OFFSET_VDMA_S2MM_IRQ_MASK               0x3c
#define OFFSET_VDMA_S2MM_REG_INDEX              0x44
#define OFFSET_VDMA_S2MM_VSIZE                  0xa0
#define OFFSET_VDMA_S2MM_HSIZE                  0xa4
#define OFFSET_VDMA_S2MM_FRMDLY_STRIDE          0xa8
#define OFFSET_VDMA_S2MM_FRAMEBUFFER1           0xac
#define OFFSET_VDMA_S2MM_FRAMEBUFFER2           0xb0
#define OFFSET_VDMA_S2MM_FRAMEBUFFER3           0xb4
#define OFFSET_VDMA_S2MM_FRAMEBUFFER4           0xb8

/* S2MM and MM2S control register flags */
#define VDMA_CONTROL_REGISTER_START		0x00000001
#define VDMA_CONTROL_REGISTER_CIRCULAR_PARK	0x00000002
#define VDMA_CONTROL_REGISTER_RESET		0x00000004
#define VDMA_CONTROL_REGISTER_GENLOCK_ENABLE	0x00000008
#define VDMA_CONTROL_REGISTER_FrameCntEn	0x00000010
#define VDMA_CONTROL_REGISTER_INTERNAL_GENLOCK	0x00000080
#define VDMA_CONTROL_REGISTER_WrPntr		0x00000f00
#define VDMA_CONTROL_REGISTER_FrmCtn_IrqEn	0x00001000
#define VDMA_CONTROL_REGISTER_DlyCnt_IrqEn	0x00002000
#define VDMA_CONTROL_REGISTER_ERR_IrqEn		0x00004000
#define VDMA_CONTROL_REGISTER_Repeat_En		0x00008000
#define VDMA_CONTROL_REGISTER_InterruptFrameCount	0x00ff0000
#define VDMA_CONTROL_REGISTER_IRQDelayCount	0xff000000

/* S2MM status register */
#define VDMA_STATUS_REGISTER_HALTED		0x00000001  // Read-only
#define VDMA_STATUS_REGISTER_VDMAInternalError	0x00000010  // Read or write-clear
#define VDMA_STATUS_REGISTER_VDMASlaveError	0x00000020  // Read-only
#define VDMA_STATUS_REGISTER_VDMADecodeError	0x00000040  // Read-only
#define VDMA_STATUS_REGISTER_StartOfFrameEarlyError	0x00000080  // Read-only
#define VDMA_STATUS_REGISTER_EndOfLineEarlyError	0x00000100  // Read-only
#define VDMA_STATUS_REGISTER_StartOfFrameLateError	0x00000800  // Read-only
#define VDMA_STATUS_REGISTER_FrameCountInterrupt	0x00001000  // Read-only
#define VDMA_STATUS_REGISTER_DelayCountInterrupt	0x00002000  // Read-only
#define VDMA_STATUS_REGISTER_ErrorInterrupt	0x00004000  // Read-only
#define VDMA_STATUS_REGISTER_EndOfLineLateError	0x00008000  // Read-only
#define VDMA_STATUS_REGISTER_FrameCount		0x00ff0000  // Read-only
#define VDMA_STATUS_REGISTER_DelayCount		0xff000000  // Read-only


typedef struct {
	unsigned int baseAddr;
	int vdmaHandler;
	int width;
	int height;
	int pixelLength;
	int fbLength;
	unsigned int* vdmaVirtualAddress;
	unsigned char* fb1VirtualAddress;
	unsigned int fb1PhysicalAddress;
	unsigned char* fb2VirtualAddress;
	unsigned int fb2PhysicalAddress;
	unsigned char* fb3VirtualAddress;
	unsigned int fb3PhysicalAddress;

	pthread_mutex_t lock;
} vdma_handle;


int vdma_setup(vdma_handle *handle, unsigned int baseAddr, int width, int height, int pixelLength, unsigned int fb1Addr, unsigned int fb2Addr, unsigned int fb3Addr)
{
	handle->baseAddr	= baseAddr;
	handle->width		= width;
	handle->height		= height;
	handle->pixelLength	= pixelLength;
	handle->fbLength	= pixelLength * width * height;
	handle->vdmaHandler	= open("/dev/mem", O_RDWR | O_SYNC);

	handle->vdmaVirtualAddress = (unsigned int*)mmap(NULL, 65535, PROT_READ | PROT_WRITE, MAP_SHARED, handle->vdmaHandler, (off_t)handle->baseAddr);
	if(handle->vdmaVirtualAddress == MAP_FAILED) {
		perror("vdmaVirtualAddress mapping for absolute memory access failed.\n");
		return -1;
	}

	handle->fb1PhysicalAddress	= fb1Addr;
	handle->fb1VirtualAddress	= (unsigned char*)mmap(NULL, handle->fbLength, PROT_READ | PROT_WRITE, MAP_SHARED, handle->vdmaHandler, (off_t)fb1Addr);
	if(handle->fb1VirtualAddress == MAP_FAILED) {
		perror("fb1VirtualAddress mapping for absolute memory access failed.\n");
		return -2;
	}

	handle->fb2PhysicalAddress	= fb2Addr;
	handle->fb2VirtualAddress	= (unsigned char*)mmap(NULL, handle->fbLength, PROT_READ | PROT_WRITE, MAP_SHARED, handle->vdmaHandler, (off_t)fb2Addr);
	if(handle->fb2VirtualAddress == MAP_FAILED) {
		perror("fb2VirtualAddress mapping for absolute memory access failed.\n");
		return -3;
	}

	handle->fb3PhysicalAddress	= fb3Addr;
	handle->fb3VirtualAddress	= (unsigned char*)mmap(NULL, handle->fbLength, PROT_READ | PROT_WRITE, MAP_SHARED, handle->vdmaHandler, (off_t)fb3Addr);
	if(handle->fb3VirtualAddress == MAP_FAILED) {
		perror("fb3VirtualAddress mapping for absolute memory access failed.\n");
		return -3;
	}

	memset(handle->fb1VirtualAddress, 255, handle->width * handle->height * handle->pixelLength);
	memset(handle->fb2VirtualAddress, 255, handle->width * handle->height * handle->pixelLength);
	memset(handle->fb3VirtualAddress, 255, handle->width * handle->height * handle->pixelLength);

	return 0;
}


unsigned int vdma_get(vdma_handle *handle, int num)
{
	return handle->vdmaVirtualAddress[num >> 2];
}


void vdma_set(vdma_handle *handle, int num, unsigned int val)
{
	handle->vdmaVirtualAddress[num >> 2] = val;
}


void vdma_halt(vdma_handle *handle)
{
	vdma_set(handle, OFFSET_VDMA_S2MM_CONTROL_REGISTER, VDMA_CONTROL_REGISTER_RESET);
	vdma_set(handle, OFFSET_VDMA_MM2S_CONTROL_REGISTER, VDMA_CONTROL_REGISTER_RESET);

	munmap((void *)handle->vdmaVirtualAddress, 65535);
	munmap((void *)handle->fb1VirtualAddress, handle->fbLength);
	munmap((void *)handle->fb2VirtualAddress, handle->fbLength);
	munmap((void *)handle->fb3VirtualAddress, handle->fbLength);

	close(handle->vdmaHandler);
}


void vdma_start_triple_buffering(vdma_handle *handle)
{
	vdma_set(handle, OFFSET_VDMA_S2MM_CONTROL_REGISTER, 0x0000108B);
	vdma_set(handle, OFFSET_VDMA_S2MM_FRAMEBUFFER1, handle->fb1PhysicalAddress);
	vdma_set(handle, OFFSET_VDMA_S2MM_FRAMEBUFFER2, handle->fb2PhysicalAddress);
	vdma_set(handle, OFFSET_VDMA_S2MM_FRAMEBUFFER3, handle->fb3PhysicalAddress);
	vdma_set(handle, OFFSET_VDMA_S2MM_FRMDLY_STRIDE, handle->width * 3);	// A8
	vdma_set(handle, OFFSET_VDMA_S2MM_HSIZE, handle->width * 3);		// A4
	vdma_set(handle, OFFSET_VDMA_S2MM_VSIZE, handle->height);		// A0

	vdma_set(handle, OFFSET_VDMA_MM2S_CONTROL_REGISTER, 0x0000008B);
	vdma_set(handle, OFFSET_VDMA_MM2S_FRAMEBUFFER1, handle->fb1PhysicalAddress);
	vdma_set(handle, OFFSET_VDMA_MM2S_FRAMEBUFFER2, handle->fb2PhysicalAddress);
	vdma_set(handle, OFFSET_VDMA_MM2S_FRAMEBUFFER3, handle->fb3PhysicalAddress);
	vdma_set(handle, OFFSET_VDMA_MM2S_FRMDLY_STRIDE, handle->width * 3);	// 58
	vdma_set(handle, OFFSET_VDMA_MM2S_HSIZE, handle->width * 3);		// 54
	vdma_set(handle, OFFSET_VDMA_MM2S_VSIZE, handle->height);		// 50

}

int main(int argc, char *argv[])
{
	//int i, j;
	vdma_handle handle;
	QCoreApplication a(argc, argv);

	// Setup VDMA handle and memory-mapped ranges
	vdma_setup(&handle, 0x43000000, 1920, 1080, 3, 0x01000000, 0x02000000, 0x03000000);

	// Start triple buffering
	vdma_start_triple_buffering(&handle);

	sleep(60);

	// Halt VDMA and unmap memory ranges
	vdma_halt(&handle);
	return a.exec();
}

vdma_setup​是初始化VDMA的函数 ,从函数的实现来看,在ZYNQ-Linux下实现VDMA操作的方法也是通过文件操作的方式,handle->vdmaHandler = open("/dev/mem", O_RDWR | O_SYNC);​由于帧缓存的深度为3,初始化的主要任务是建立3个缓存物理地址和VDMA虚拟地址的映射关系。

vdma_start_triple_buffering​中实现了S2MM​和MM2S​的寄存器设置,相应寄存器的位定义可以参考Xilinx官方文档pg020。

4. 结语

本文主要参考了以下两篇博客,并在前人的基础上做了一些修正和补充。如有错误,欢迎指正。
https://blog.csdn.net/qq_37280428/article/details/123630452
https://blog.csdn.net/luotong86/article/details/52486365

你可能感兴趣的:(fpga开发,linux)