请求提供AVNET Zynq7000通过DMA控制用户LED的示例
Hey there! I’ve worked on exactly this kind of project with the MicroZed (Zynq-7000) before, so let’s walk through how to toggle the user LED using DMA under Linux. This approach lets you offload the GPIO write operations from the CPU, which is useful for low-latency or high-throughput scenarios.
First, let’s cover the basics:
- Your MicroZed should be running a Linux image (preferably built with Petalinux) with DMA engine support enabled, and the GPIO driver loaded.
- The default user LED on most MicroZed boards is connected to MIO7 (double-check your board schematic if this doesn’t match).
- We’ll use the Zynq PS’s built-in GDMA (General Purpose DMA) controller to write directly to the GPIO data register—no PL logic required here.
You’ll need to ensure your device tree correctly enables both the GPIO controller and DMA engine. Here’s a snippet to add/modify in your device tree (typically system-top.dts in Petalinux):
/* Enable GPIO controller and configure MIO7 as output */ &gpio { status = "okay"; user-led { gpios = <&gpio 7 GPIO_ACTIVE_HIGH>; output-high; /* Optional: Start with LED on */ }; }; /* Enable Zynq PS GDMA controller */ &dma { status = "okay"; dma-channel@0 { xlnx,device-id = <0>; xlnx,memcpy = <1>; /* Enable memcpy mode for memory-to-memory/memory-peripheral transfers */ }; };
After updating the device tree, rebuild your Petalinux image and flash it to the board.
The most straightforward way to test this is by directly mapping the GPIO and DMA registers into user space via /dev/mem. Here’s a complete C example that toggles the LED using DMA:
#include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <sys/mman.h> #include <unistd.h> #include <errno.h> #include <string.h> /* Zynq-7000 PS Register Base Addresses */ #define GPIO_BASE 0xE000A000 #define GDMA_BASE 0xE0000000 /* GPIO Register Offsets */ #define GPIO_DIR 0x04 /* Direction register */ #define GPIO_DATA 0x00 /* Data register */ /* GDMA Register Offsets (Channel 0) */ #define GDMA_SRC_ADDR 0x10 /* Source address */ #define GDMA_DST_ADDR 0x18 /* Destination address */ #define GDMA_CTRL 0x00 /* Control register */ #define GDMA_LEN 0x28 /* Transfer length */ #define GDMA_STATUS 0x04 /* Status register (for completion check) */ #define PAGE_SIZE 4096 #define LED_MASK (1 << 7) /* MIO7 bit mask */ int main() { int mem_fd; void *gpio_map, *dma_map; volatile unsigned int *gpio_dir_reg, *gpio_data_reg; volatile unsigned int *dma_src, *dma_dst, *dma_ctrl, *dma_len, *dma_status; unsigned int led_on = LED_MASK; unsigned int led_off = 0; // Open /dev/mem to access physical memory mem_fd = open("/dev/mem", O_RDWR | O_SYNC); if (mem_fd < 0) { fprintf(stderr, "Failed to open /dev/mem: %s\n", strerror(errno)); return EXIT_FAILURE; } // Map GPIO registers into user space gpio_map = mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, mem_fd, GPIO_BASE); if (gpio_map == MAP_FAILED) { fprintf(stderr, "GPIO mmap failed: %s\n", strerror(errno)); close(mem_fd); return EXIT_FAILURE; } gpio_dir_reg = (volatile unsigned int *)(gpio_map + GPIO_DIR); gpio_data_reg = (volatile unsigned int *)(gpio_map + GPIO_DATA); // Set MIO7 as output *gpio_dir_reg |= LED_MASK; // Map GDMA registers into user space dma_map = mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, mem_fd, GDMA_BASE); if (dma_map == MAP_FAILED) { fprintf(stderr, "DMA mmap failed: %s\n", strerror(errno)); munmap(gpio_map, PAGE_SIZE); close(mem_fd); return EXIT_FAILURE; } dma_src = (volatile unsigned int *)(dma_map + GDMA_SRC_ADDR); dma_dst = (volatile unsigned int *)(dma_map + GDMA_DST_ADDR); dma_ctrl = (volatile unsigned int *)(dma_map + GDMA_CTRL); dma_len = (volatile unsigned int *)(dma_map + GDMA_LEN); dma_status = (volatile unsigned int *)(dma_map + GDMA_STATUS); printf("DMA-controlled LED toggle started. Press Ctrl+C to stop.\n"); // Loop to toggle LED via DMA while (1) { // Configure DMA to write LED_ON to GPIO_DATA *dma_src = (unsigned int)&led_on; *dma_dst = GPIO_BASE + GPIO_DATA; *dma_len = 4; // Transfer 32-bit word (4 bytes) *dma_ctrl = 0x1; // Start DMA transfer // Wait for transfer completion (check done bit) while (!(*dma_status & 0x2)); sleep(1); // Configure DMA to write LED_OFF to GPIO_DATA *dma_src = (unsigned int)&led_off; *dma_dst = GPIO_BASE + GPIO_DATA; *dma_len = 4; *dma_ctrl = 0x1; while (!(*dma_status & 0x2)); sleep(1); } // Cleanup (unreachable unless we add a signal handler) munmap(dma_map, PAGE_SIZE); munmap(gpio_map, PAGE_SIZE); close(mem_fd); return EXIT_SUCCESS; }
Compile & Run
- Transfer the code to your MicroZed (via SCP, USB, or direct editing).
- Compile it with GCC:
gcc dma_led_toggle.c -o dma_led_toggle - Run with root privileges (required for
/dev/memaccess):sudo ./dma_led_toggle
- Root Access: You must run the program as root—regular users don’t have permission to access
/dev/mem. - GPIO Pin Check: If the LED doesn’t toggle, verify your board’s schematic for the correct MIO pin (some MicroZed variants use MIO6 instead of MIO7).
- DMA Driver Loading: Check if the DMA driver is loaded with
dmesg | grep dma. You should see messages about the Xilinx DMA driver initializing. - Petalinux Configuration: If you’re building your own image, ensure DMA support is enabled in the kernel config (
petalinux-config -c kernel→ Device Drivers → DMA Engine Support → enable Xilinx DMA drivers). - PL-Based AXI DMA: If you’re using an AXI DMA core in the PL instead of the PS GDMA, adjust the DMA base address to match your PL design’s address assignment (from Vivado).
内容的提问来源于stack exchange,提问作者tannoy connect

