Zynq ADC数据采集和UDP传输测试
摘要
运行于Zybo开发板petalinux的应用程序,参考了黑金的教程。实现了从AD9238读出数据,再通过UDP传输至服务器,主要进行了传输速度测试。
main.c
#include <stdio.h>
#include <fcntl.h>
#include <linux/fb.h>
#include <sys/mman.h>
#include <stdlib.h>
#include "adc_capture.h"
//#include "frame_buffer.h"
//#include "wave.h"
#define ADC_CAPTURELEN 1920 /* ADC capture length */
#define ADC_COE 16 /* ADC coefficient */
#define ADC_BYTE 2 /* ADC data byte number */
#define ADC_BITS 12
/*
*Wave defines
*/
#define CANVAS_LEN 1920*1080*3 /* Canvas total length in byte */
#define WAVE_START_ROW 150 /* Grid and Wave start row in frame */
#define WAVE_START_COLUMN 0 /* Grid and Wave start column in frame */
#define WAVE_HEIGHT 256 /* Grid and Wave height */
#include <time.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define SERVER_PORT 8080
#define BUFF_LEN 512
#define SERVER_IP "192.168.1.30"
int client_fd;
struct sockaddr_in ser_addr;
int init_udp()
{
client_fd = socket(AF_INET, SOCK_DGRAM, 0);
if(client_fd < 0)
{
printf("create socket fail!\n");
return -1;
}
memset(&ser_addr, 0, sizeof(ser_addr));
ser_addr.sin_family = AF_INET;
ser_addr.sin_addr.s_addr = inet_addr(SERVER_IP);
// ser_addr.sin_addr.s_addr = htonl(INADDR_ANY); //注意网络序转换
ser_addr.sin_port = htons(SERVER_PORT); //注意网络序转换
return 0;
}
int main(int argc, char *argv[])
{
st_fb_info fb_info;
u8 CanvasBuffer[CANVAS_LEN];
int fd0,fd1;
int wave_width;
short CH0DmaRxBuffer[ADC_CAPTURELEN];
short CH1DmaRxBuffer[ADC_CAPTURELEN];
int dma_flag0=0;
int dma_flag1=0;
struct timeval tv;
struct timeval tv0;
struct timeval tv1;
struct timezone tz;
float time_use=0;
float rate=0;
int pkg0_cnt=0;
int pkg1_cnt=0;
/* Open video memory */
if (fb_init(&fb_info)) {
printf("fb inint error \n");
exit(1);
}
adc_capture_init(&fd0,"/dev/adc0",ADC_CAPTURELEN,ADC_CAPTURELEN*2);
adc_capture_init(&fd1,"/dev/adc1",ADC_CAPTURELEN,ADC_CAPTURELEN*2);
wave_width = fb_info.width;
init_udp();
gettimeofday(&tv0, NULL);
while(1){
if(adc_capture(fd0,ADC_CAPTURELEN,(unsigned char *)CH0DmaRxBuffer,&dma_flag0))
{
exit(1);
}
if(adc_capture(fd1,ADC_CAPTURELEN,(unsigned char *)CH1DmaRxBuffer,&dma_flag1))
{
exit(1);
}
if(dma_flag0==1)
{
//use UDP send read data to PC
sendto(client_fd, (void *)CH0DmaRxBuffer, ADC_CAPTURELEN, 0, (struct sockaddr*)&ser_addr, sizeof(ser_addr));
pkg0_cnt++;
if((pkg0_cnt%10000) == 0) {
gettimeofday(&tv, NULL);
time_use = (tv.tv_sec-tv0.tv_sec)*1000000+(tv.tv_usec-tv0.tv_usec);
rate = ADC_CAPTURELEN*10000/time_use;
// printf("adc0_capture[%d]:%ld.%ld\n", pkg0_cnt, tv.tv_sec, tv.tv_usec);
printf("adc0 data rate: %.2fMbps \n", rate);
tv0=tv;
}
}
if(dma_flag1==1)
{
if((pkg1_cnt%10000) == 0) {
gettimeofday(&tv, &tz);
printf("adc1_capture[%d]:%ld.%ld\n", pkg1_cnt, tv.tv_sec, tv.tv_usec);
}
//use UDP send read data to PC
sendto(client_fd, (void *)CH1DmaRxBuffer, ADC_CAPTURELEN, 0, (struct sockaddr*)&ser_addr, sizeof(ser_addr));
pkg1_cnt++;
}
dma_flag0=0;
dma_flag1=0;
// /* Grid Overlay */
// draw_grid(wave_width, WAVE_HEIGHT,CanvasBuffer);
// /* channel 0 Overlay */
// draw_wave(wave_width, WAVE_HEIGHT, CH0DmaRxBuffer, CanvasBuffer, UNSIGNEDSHORT, ADC_BITS, YELLOW, ADC_COE);
// /* channel 1 Overlay */
// draw_wave(wave_width, WAVE_HEIGHT, CH1DmaRxBuffer, CanvasBuffer, UNSIGNEDSHORT, ADC_BITS, RED, ADC_COE) ;
// /* Copy Canvas to frame buffer */
// frame_copy(wave_width, WAVE_HEIGHT, WAVE_START_COLUMN, WAVE_START_ROW, &fb_info, CanvasBuffer);
// usleep(500*1000);
}
}
adc_capture.h
#ifndef ADC_CAPTURE_H
#define ADC_CAPTURE_H
/* IOCTL defines */
/* IOCTL defines */
#define AXI_ADC_IOCTL_BASE 'W'
#define AXI_ADC_SET_SAMPLE_NUM _IO(AXI_ADC_IOCTL_BASE, 0)
#define AXI_ADC_SET_DMA_LEN_BYTES _IO(AXI_ADC_IOCTL_BASE, 1)
#define AXI_ADC_DMA_INIT _IO(AXI_ADC_IOCTL_BASE, 2)
#define AXI_ADC_DMA_START _IO(AXI_ADC_IOCTL_BASE, 3)
#define AXI_ADC_DMA_DEINIT _IO(AXI_ADC_IOCTL_BASE, 4)
int adc_capture_init(int *fd,char *adc_dev,int adc_sample_num,int dma_len_bytes);
int adc_capture(int fd, int samples_num, unsigned char *buf, int *dma_flag);
void convert_to_u8(int samples_num, short *src_buf,unsigned char *dst_buf);
#endif
adc_capture.c
#include <termios.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "adc_capture.h"
/* Thread capture ADC samples and put it to Circular buffer */
int adc_capture_init(int *fd,char *adc_dev,int adc_sample_num,int dma_len_bytes) {
//Open ADC dev node
*fd = open(adc_dev, O_RDONLY);
if (*fd < 0) {
return -1;
}
if (ioctl(*fd, AXI_ADC_SET_SAMPLE_NUM, adc_sample_num)) {
printf("ADC DMA AXI_ADC_SET_SAMPLE_NUM failed: %s\n", strerror(errno));
return -2;
}
if (ioctl(*fd, AXI_ADC_SET_DMA_LEN_BYTES, dma_len_bytes)) {
printf("ADC DMA AXI_ADC_SET_DMA_LEN_BYTES failed: %s\n", strerror(errno));
return -2;
}
if (ioctl(*fd, AXI_ADC_DMA_INIT)) {
printf("ADC DMA INIT failed: %s\n", strerror(errno));
return -2;
}
return 0;
}
int adc_capture(int fd, int samples_num, unsigned char *buf, int *dma_flag) {
if (ioctl(fd, AXI_ADC_DMA_START) == 0) {
read(fd, buf, samples_num * 2);
*dma_flag = 1;
} else {
printf("XADC DMA START failed: %s\n", strerror(errno));
return -1;
}
return 0;
}
makefile
CC = arm-linux-gnueabihf-gcc
LIBS := -lm
main:main.o adc_capture.o frame_buffer.o wave.o
$(CC) main.o adc_capture.o frame_buffer.o wave.o -o main -lm -L /usr/lib -L /lib
main.o:main.c
$(CC) -c main.c -o main.o
adc_capture.o:adc_capture.c
$(CC) -c adc_capture.c -o adc_capture.o
frame_buffer.o:frame_buffer.c
$(CC) -c frame_buffer.c -o frame_buffer.o
wave.o:wave.c
$(CC) -c wave.c -o wave.o
clean:
rm -rf *.o
测试结果
因为在main.c文件的主循环中没有开启HDMI显示,所以能测试出数据采集和传输的最大速度。
实际测试的速度达到每通道7.75MB/Sec,两通道加起来都不到USB 2.0最大速度的一半。初步推测是因为DMA应用不合理导致速度上不去,解决的办法是改写FPGA和Linux相关代码。