ZYNQ linux block device driver development (ADC as an example)

Posted by bran on Wed, 02 Feb 2022 01:18:53 +0100

1, vivado hardware environment construction:

1. Modify the clock configuration of CPU and set FCLK_CLK2 is modified to 65MHz, and the clock is led out in two ways to be used by two AD9226 chip clocks:

2. Connect other signals, save them, click the Address Editor to view the address configuration. If some modules do not have an address configured,
Click Auto Assign Address. Then Generate Output Products and Create HDL Wrapper, bind AD9226 pin in XDC, and then generate bit file

3. The whole control process:

2, AD9226 data acquisition:

1. Because the data collected by ADC needs to be transferred to ZYNQ through DMA, and the interface with DMA is AXIS stream interface, so
ADC data needs to be converted into AXIS stream data, and the clock frequency of ADC is different from that of AXIS. Therefore, FIFO needs to be added for cross clock domain data processing. At the same time, the AXIS Master function needs to be realized. The workflow is:
1) ARM configures start register and acquisition length register.
2) Since bit0 of AD9226 is MSB, the sequence is adjusted and the symbol bit is extended to 16bit for easy operation. Then, the collected data of ADC is stored in FIFO.
3) DMA uses the AXIS interface to read the data in FIFO until the configured amount of data is read.

2. verilog collects analog signals

module ad9226_data_resize(
	input               adc_clk,
	input               adc_otr,
	output reg[63:0]    adc_data,
	output reg          adc_data_valid,         
	input [11:0] 	    ad9226_data
	);
reg[11:0] ad9226_data_d0;
reg[11:0] ad9226_data_d1;
reg[11:0] ad9226_data_d2;
reg[11:0] ad9226_data_d3;
reg[1:0] cnt;
wire ad_data_wr;
wire empty;

always@(posedge adc_clk)
begin
	ad9226_data_d0 <= {ad9226_data[0],ad9226_data[1],ad9226_data[2],ad9226_data[3],
					   ad9226_data[4],ad9226_data[5],ad9226_data[6],ad9226_data[7],
					   ad9226_data[8],ad9226_data[9],ad9226_data[10],ad9226_data[11]};
	ad9226_data_d1 <= ad9226_data_d0;
	ad9226_data_d2 <= ad9226_data_d1;
	ad9226_data_d3 <= ad9226_data_d2;
end
always@(posedge adc_clk)
begin
	cnt <= cnt + 2'd1;
end
always@(posedge adc_clk)
begin
	adc_data <= { {4{ad9226_data_d0[11]}},ad9226_data_d0,{4{ad9226_data_d1[11]}},ad9226_data_d1,{4{ad9226_data_d2[11]}},ad9226_data_d2,{4{ad9226_data_d3[11]}},ad9226_data_d3};
	adc_data_valid <= (cnt == 2'd3) ? 1'b1 : 1'b0;
end
endmodule

3, Hardware driver analysis of AD block device

1. The software in kernel space is zimage (including seven subsystems, such as process management, memory management, file system, etc.), in which memory management finally corresponds to the underlying block device management, device control finally corresponds to the underlying character device driver, and the network finally corresponds to the underlying network device driver. When the kernel space software is running, the CPU is in SVC management mode, and the kernel space software can not directly access the physical address of hardware peripherals. If you want to access, you must map the physical address of the peripheral to the virtual address of the kernel space. If the software in the kernel space illegally accesses the memory, it will directly cause the collapse of the operating system. The driver belongs to the kernel space, and the virtual address range of the kernel space is 3G ~ 4G (0xc0000000 ~ 0xFFFFFF)

2. Block device access process

3. linux kernel memory allocation related functions are written to the driver Functions in c

(1) kmalloc, function prototype: void * kmalloc (size_t size, gfp_t flags)

Function function: allocate memory from the direct memory mapping area, and the allocated memory access efficiency is the highest

Function features: the allocated physical memory and virtual memory are continuous; The minimum allocated memory size is 32 bytes and the maximum is 4MB(128KB for the old version kernel)

Function parameters:

Size: Specifies the size of the allocated memory, in bytes

flags: Specifies the behavior of allocating memory. Generally, the macro GFP is used_ Kernel: allocating memory is. If you use this macro, you tell the kernel to try to allocate memory this time. If the memory is insufficient, it will sleep until there is free memory. Therefore, this macro cannot be used to interrupt the context! GFP_ATOMIC: allocating memory means that if the memory is insufficient, it will not cause sleep operation, but will return immediately, so it can be used to interrupt the context

Return value: returns the first address of the allocated kernel virtual memory

(2) Memory is no longer used, remember to release: kfree. Function prototype: void kfree(void *addr)

Examples of memory freeing kfree:

char *addr;
addr = kmalloc(0x100000, GFP_KERNEL);
memcpy(addr, "hello,world", 12);
kfree(addr);       

(3), __ get_free_pages  /  free_pages -- one page of linux system = 4KB

Function prototype: unsigned long__ get_free_pages  (  gfp_t flags,  unsigned order )

Function function: allocate memory from the direct memory mapping area, with high memory access efficiency

Function features: since the memory is allocated from the direct memory mapping area, it is physically and virtually continuous; The memory size is 4MB, and the maximum allocation is 4MB

Function parameters:

flgas: Specifies that memory allocation is the behavior of. Generally, the macro GFP is used_ Kernel: allocating memory is. If you use this macro, you tell the kernel to try to allocate memory this time. If the memory is insufficient, it will sleep until there is free memory. Therefore, this macro cannot be used to interrupt the context! GFP_ATOMIC: allocating memory means that if the memory is insufficient, it will not cause sleep operation, but will return immediately, so it can be used to interrupt the context

order: order = 0, allocate 1 page

order = 1, 2 pages allocated

order = 2, 4 pages allocated

order = 3, 8 pages allocated

Return value: when saving the first address of the applied kernel virtual memory, pay attention to the conversion of data types. When the memory is no longer used, remember to release the memory:

   void free_pages(unsigned long addr, int order);

Example:

   unsigned long addr;

   addr = __get_free_pages(GFP_KERNEL, 2);

   memcpy( (char *)addr, "hello,world", 12 );

    free_pages(addr, 2);

  (4). Vmalloc / vfree} function prototype: void * vmalloc (int size)

Function function: allocate memory from dynamic memory mapping area

Function features:

A. the allocated kernel virtual address is continuous, but the corresponding physical address is not necessarily continuous, so the memory access efficiency is not high

B. the theoretical value of the maximum allocated memory is 120MB

C, it allocates memory. If the memory is insufficient, it will also lead to sleep, so it cannot be used to interrupt the context

Size: Specifies the size of the allocated memory

Return value: returns the first address of the allocated kernel virtual memory

Free memory: void vfree (void * addr); Example:

   void * addr;
   addr = vmalloc(0x200000);
   memcpy(addr, "hello,world", 12);
   vfree(addr);

(5) add vmalloc = size (for example, vmalloc=250M) to the kernel startup parameters, indicating that the size of the dynamic memory mapping area will be expanded from 120M to 250M when the kernel is started

Example: restart the development board, enter the command line of uboot, and set:

setenv bootargs root=/dev/nfs nfsroot=192.168.1.8:/opt/rootfs ip=192.168.1.110:192.168.1.8:192.168.1.1:255.255.255.0::eth0:on init=/linuxrc console=ttySAC0,115200 vmalloc=250M

boot //Start the linux system, observe the kernel print information, and check whether the size of vmalloc area has changed

N. Driver code

#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/types.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/device.h>
#include <linux/cdev.h>
#include <linux/of.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
#include <linux/pm.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/gfp.h>
#include <linux/mm.h>
#include <linux/dma-buf.h>
#include <linux/string.h>
#include <linux/uaccess.h>
#include <linux/dmaengine.h>
#include <linux/completion.h>
#include <linux/wait.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/pagemap.h>
#include <linux/errno.h>
#include <linux/clk.h>
#include <linux/interrupt.h>
#include <linux/vmalloc.h>
#include <linux/moduleparam.h>
#include <linux/miscdevice.h>
#include <linux/ioport.h>
#include <linux/notifier.h>
#include <linux/init.h>
#include <linux/pci.h>

#define DEVICE_NUM        16
#define ADC_MEM_PAGE_NUM	128
#define ADC_BUFF_SIZE		16384            
#define ADC_MEM_BUFF_SIZE (ADC_BUFF_SIZE * ADC_MEM_PAGE_NUM)     


#define AX_ADC_MAGIC  	' x '/ / define magic number
#define AX_ADC_MAX_NR  	  2 / / define the maximum ordinal number of the command,

#define COMM_GET_CURPAGENUM 	_IO(AX_ADC_MAGIC, 1)
#define COMM_SET_TEST		_IO(AX_ADC_MAGIC, 0)

#define AX_MAJOR 181
#define AX_DEVICES 3
struct ax_adc 
{
	struct class 	*cls;
	int 		major;
	char            devname[16];
	u8		*mem_vraddr1;
	u32 		mem_phyaddr1;
	u8		*mem_vraddr2;
	u32 		mem_phyaddr2;
	int          	index;
	struct mutex	mutex;
	int             mijor;
	int             ax_bufState;
	u8              *ax_mem_msg_buf;
	int             ax_mem_curpage_num;
	int             irq;
	void __iomem	*base_address;	
	struct device	*dev;
	resource_size_t  remap_size;             

};
static struct ax_adc ax_dev[DEVICE_NUM];
static int  ax_device_num = 0;
static unsigned char * malloc_reserved_mem(long size)
{
	unsigned char *p = kmalloc(size, GFP_KERNEL);
	unsigned char *tmp = p;
	unsigned int i, n;
	if(NULL == p)
	{
		printk("Error : malloc_reserved_mem kmalloc failed!\n");
		return NULL;
	}
	n = size/(ADC_BUFF_SIZE) + 1;
	if(0 == size % (ADC_BUFF_SIZE))
        {
		-- n;
	}

	for(i =0; i < n; ++i)
	{
		SetPageReserved(virt_to_page(tmp));
		tmp += ADC_BUFF_SIZE;
	}
	return p;
}


static int ax_adc_mmap(struct file *file, struct vm_area_struct *vma)
{
	struct inode *inode = file_inode(file);
	int imi = iminor(inode);
	int ima = imajor(inode);
	int i = 0;
	for(i=0;i<ax_device_num;i++)
	{
		if((ax_dev[i].major == ima) && (ax_dev[i].mijor == imi))
			break;
		if(i>=ax_device_num-1)
			return 0;			
	}

	unsigned long offset 	= vma->vm_pgoff << PAGE_SHIFT;
	unsigned long physics 	= ((unsigned long)ax_dev[i].ax_mem_msg_buf)- PAGE_OFFSET;
	unsigned long mypfn 	= physics >> PAGE_SHIFT;
	unsigned long vmsize	= vma->vm_end-vma->vm_start;
	unsigned long psize 	= ADC_MEM_BUFF_SIZE - offset; 
	if(vmsize > psize)
		return -ENXIO;
	if(remap_pfn_range(vma,vma->vm_start,mypfn,vmsize,vma->vm_page_prot))
		return -EAGAIN;

	return 0;
}

static long ax_adc_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
	int ret = 0;
	if(_IOC_TYPE(cmd) != AX_ADC_MAGIC) return - EINVAL;
	if(_IOC_NR(cmd) > AX_ADC_MAX_NR) return - EINVAL;
	switch(cmd)
       {
		case COMM_GET_CURPAGENUM:
			if(arg>=0 && arg <ax_device_num)
			return ax_dev[arg].ax_mem_curpage_num;
		default :
			ret = - EINVAL;
	}
	return ret;
}


static struct file_operations ax_adc_fops = 
{	
	.owner  	= THIS_MODULE,
	.unlocked_ioctl = ax_adc_ioctl,
	.mmap 		= ax_adc_mmap,
};

static irqreturn_t ax_adc_interrupt(int irq, void *dev_id)
{
	int i=0;
	int index = 0;
	for(i =0;i<ax_device_num;i++)
	{
		if(ax_dev[i].irq == irq)
		{
			index = i;
			break;
		}
		if(i>=ax_device_num-1)
		{
			printk(KERN_ALERT "ax_adc_interrupt irq error\n");				
			return IRQ_HANDLED;
		}
	}
	
	int count = 0;
	mutex_lock(&ax_dev[index].mutex);		
	int state = readl(ax_dev[index].base_address  + 0x3 * 4);
	ax_dev[index].ax_bufState = (state>>3) & 0x7;
	count = readl(ax_dev[index].base_address + 0x5 * 4);
	if(ax_dev[index].ax_mem_msg_buf==NULL)
	{
		printk(KERN_ALERT "ax_adc_interrupt ax_mem_msg_buf=NULL error\n");	
	}
        else
        {
		if(ax_dev[index].ax_mem_curpage_num<0)
		{
			printk(KERN_ALERT "ax_adc curpage error\n");	
		}
                else
		if(ax_dev[index].ax_mem_curpage_num< ADC_MEM_PAGE_NUM )   //Copy physical memory data to shared memory area
		{

			if(ax_dev[index].ax_bufState)
			{
memcpy(ax_dev[index].ax_mem_msg_buf+ax_dev[index].ax_mem_curpage_num*ADC_BUFF_SIZE,ax_dev[index].mem_vraddr2,ADC_BUFF_SIZE);
			}
                        else
                        {		memcpy(ax_dev[index].ax_mem_msg_buf+ax_dev[index].ax_mem_curpage_num*ADC_BUFF_SIZE,ax_dev[index].mem_vraddr1,ADC_BUFF_SIZE);
			}
			ax_dev[index].ax_mem_curpage_num++;
			if(ax_dev[index].ax_mem_curpage_num>= ADC_MEM_PAGE_NUM)
				ax_dev[index].ax_mem_curpage_num = 0;
		}
	
	}
	writel(1, ax_dev[index].base_address  + 0x4 * 4);
	udelay(1);
	writel(0, ax_dev[index].base_address  + 0x4 * 4);
	mutex_unlock(&ax_dev[index].mutex);
	return IRQ_HANDLED;
}

static int ax_adc_probe(struct platform_device *pdev)
{
	struct ax_adc   ax;
	struct resource *res;
	int		err, i;
	struct device *dev;
	dev_t devt;
	for(i=0;i<DEVICE_NUM;i++)
	{

		memset(ax.devname,0,16);
		snprintf(ax.devname, sizeof(ax.devname)-1, "ax_adc%d", i+1);
		ax.major = register_chrdev(0, ax.devname, &ax_adc_fops);
		ax.cls = class_create(THIS_MODULE, ax.devname);
		ax.mijor = i;
		ax.index = i;
		dev = device_create(ax.cls, &pdev->dev, MKDEV(ax.major, ax.mijor), NULL, ax.devname);
		if (IS_ERR(dev)) 
                {
			class_destroy(ax.cls);
			unregister_chrdev(ax.major, ax.devname);
			if(i>=DEVICE_NUM-1)
			{
				return 0;
			}
		
		}
                else
		{
			ax_device_num++;			
			break;
		}
	}
	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	if(res == NULL)
	{
		printk(KERN_ALERT "ax_adc_probe res error!\n");
		return -ENOENT;
	}
	ax.base_address = devm_ioremap_resource(&pdev->dev, res);
	if (IS_ERR(ax.base_address))
        {
		return PTR_ERR(ax.base_address);
        }
	ax.remap_size = resource_size(res);
	ax.irq = platform_get_irq(pdev,0);
	if (ax.irq <= 0)
        {
		return -ENXIO;
        }
	mutex_init(&ax.mutex);
	ax.dev = &pdev->dev;
	ax.ax_bufState			= 0;
	ax.ax_mem_msg_buf 		= NULL;
	ax.ax_mem_curpage_num 	= 0;
	ax.mem_vraddr1 = dma_alloc_writecombine(ax.dev, ADC_BUFF_SIZE, &ax.mem_phyaddr1, GFP_KERNEL);
	if (!ax.mem_vraddr1)
        {
		goto fail1;
        }
	ax.mem_vraddr2 = dma_alloc_writecombine(ax.dev, ADC_BUFF_SIZE, &ax.mem_phyaddr2, GFP_KERNEL);
	if (!ax.mem_vraddr2)
		goto fail2;	
	writel(ax.mem_phyaddr1, ax.base_address + 0x13 * 4);
	writel(ax.mem_phyaddr2, ax.base_address + 0x14 * 4);	
	writel(ADC_BUFF_SIZE, ax.base_address + 0x11 * 4);
	writel(0, ax.base_address + 0x12 * 4);
	writel(1, ax.base_address + 0x12 * 4);
	ax.ax_mem_msg_buf = malloc_reserved_mem(ADC_MEM_BUFF_SIZE);
	if(ax.ax_mem_msg_buf==NULL)
	{
		printk(KERN_ALERT "ax_adc_probe malloc error\n");
		goto fail3;
	}
	err = request_threaded_irq(ax.irq, NULL,
				ax_adc_interrupt,
				IRQF_TRIGGER_RISING | IRQF_ONESHOT,
				ax.devname, &ax);				   
	if (err) 
        {
		printk(KERN_ALERT "ax_adc_probe irq	error=%d\n", err);
		goto fail3;
	}
	memcpy(&ax_dev[i], &ax, sizeof(struct ax_adc));
	platform_set_drvdata(pdev, &ax_dev[i]);	
	return 0;
fail3:
	if(ax.ax_mem_msg_buf!=NULL)
	{
		kfree(ax.ax_mem_msg_buf);
		ax.ax_mem_msg_buf = NULL;
	}	
	free_irq(ax.irq, &ax);
	dma_free_writecombine(ax.dev, ADC_BUFF_SIZE, ax.mem_vraddr2, ax.mem_phyaddr2);
fail2:
	dma_free_writecombine(ax.dev, ADC_BUFF_SIZE, ax.mem_vraddr1, ax.mem_phyaddr1);
fail1:
	device_destroy(ax.cls, MKDEV(ax.major, ax.mijor));	
	class_destroy(ax.cls);
	unregister_chrdev(ax.major, ax.devname);
	return -ENOMEM;

}

static int ax_adc_remove(struct platform_device *pdev)
{
	int i = 0;
	for(i=0;i<ax_device_num;i++)
	{
		device_destroy(ax_dev[i].cls, MKDEV(ax_dev[i].major, ax_dev[i].mijor));	
		class_destroy(ax_dev[i].cls);
		unregister_chrdev(ax_dev[i].major, ax_dev[i].devname);
		if(ax_dev[i].ax_mem_msg_buf!=NULL)
		{
			kfree(ax_dev[i].ax_mem_msg_buf);
			ax_dev[i].ax_mem_msg_buf = NULL;
		}
		free_irq(ax_dev[i].irq, &ax_dev[i]);
		dma_free_writecombine(ax_dev[i].dev, ADC_BUFF_SIZE, ax_dev[i].mem_vraddr1, ax_dev[i].mem_phyaddr1);
		dma_free_writecombine(ax_dev[i].dev, ADC_BUFF_SIZE, ax_dev[i].mem_vraddr2, ax_dev[i].mem_phyaddr2);
	}
	return 0;
}
static int ax_adc_suspend(struct device *dev)
{

	return 0;
}
static int ax_adc_resume(struct device *dev)
{
 
	return 0;
}
static const struct dev_pm_ops ax_adc_pm_ops = 
{
	.suspend = ax_adc_suspend,
	.resume  = ax_adc_resume,
};
MODULE_DEVICE_TABLE(platform, ax_adc_driver_ids);
static const struct of_device_id ax_adc_of_match[] = 
{
	{.compatible = "alinx,ax-adc" },
	{ }
};
MODULE_DEVICE_TABLE(of, ax_adc_of_match);
static struct platform_driver ax_adc_driver = 
{
	.probe = ax_adc_probe,
	.remove	= ax_adc_remove,
	.driver = 
        {
		.owner   		= THIS_MODULE,
		.name	 		= "ax-adc",
		.pm    			= &ax_adc_pm_ops,
		.of_match_table	= ax_adc_of_match,		
	},
};
module_platform_driver(ax_adc_driver);
MODULE_AUTHOR("Guowc");
MODULE_DESCRIPTION("Alinx ADC");
MODULE_LICENSE("GPL v2");

Sector is the basic unit for hardware to transmit data. The hardware transmits the data of one sector to the memory at a time. However, unlike sectors, blocks are the basic unit of data transmitted by virtual file systems. In Linux, the size of a block must be a power of 2, but cannot exceed the size of a page (4k). (on the X86 platform, the size of a page is 4094 bytes, so the block size can be 512102420484096).

4, Today is being fully updated

/*
 *  ad9226 application
 *
 *
 */

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include<QFile>
#include<QMessageBox>
#include<stdio.h>
#include<stdlib.h>
#include<fcntl.h>
#include<unistd.h>
#include<math.h>
#include <QTextStream>
#include <iostream>
#include <cstring>
#include <string.h>
#include <semaphore.h>
#include<sys/ioctl.h>
#include <sys/mman.h>

#define AX_ADC_MAGIC  	' x '/ / define magic number
#define AX_ADC_MAX_NR  	  2 / / define the maximum ordinal number of the command,
#define COMM_ALLOC_MEM 	  	_IO(AX_ADC_MAGIC, 0)
#define COMM_GET_CURPAGENUM 	_IO(AX_ADC_MAGIC, 1)
#define COMM_TEST			 	_IO(AX_ADC_MAGIC, 0)

#define DISPLAY
#define DISPLAYPIXELS      512


MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

//    setGeometry(800, 480, 800, 480);

    InitShow();

    InitData();

}

void MainWindow::InitData()
{

    recvDevFd1 =open("/dev/ax_adc1",O_RDWR);

    recvDevFd2 =open("/dev/ax_adc2",O_RDWR);

    if ((0 >= recvDevFd1) && (0 >= recvDevFd2))
    {
        QMessageBox::warning(this,"open device",QString::fromLocal8Bit("open device failed !"));
        return;
    }

    m_bepagenum1  = 0;
    m_curpagenum1 = 0;
    m_pagecount1  = 0;
    m_bepagenum2  = 0;
    m_curpagenum2 = 0;
    m_pagecount2  = 0;

    if(recvDevFd1>0)
    {
        m_pShareMem1 = mmap(NULL, ADC_MEM_BUFF_SIZE,PROT_READ,MAP_SHARED, recvDevFd1,0);
        if( m_pShareMem1 == MAP_FAILED ) {
            printf("Error : getMemMsgBuf() fail mmap!\n");
        }


    }

    if(recvDevFd2>0)
    {
       m_pShareMem2 = mmap(NULL, ADC_MEM_BUFF_SIZE,PROT_READ,MAP_SHARED, recvDevFd2,0 );
       if( m_pShareMem2 == MAP_FAILED ) {
           printf("Error : getMemMsgBuf() fail mmap!\n");
       }

    }

    recvFileTimer1 = new QTimer(this);
    connect( recvFileTimer1, SIGNAL(timeout()), this, SLOT(Timer_RecvfromDev1()));
    recvFileTimer1->start(50);

}

void MainWindow::InitShow()
{
    ui->customPlot->setBackground(QBrush(Qt::black));
    ui->customPlot->yAxis->setLabelColor(Qt::white);
    ui->customPlot->xAxis->setLabelColor(Qt::white);
    ui->customPlot->xAxis->setBasePen(QPen(Qt::white, 1));
    ui->customPlot->yAxis->setBasePen(QPen(Qt::white, 1));
    ui->customPlot->xAxis->setTickLabelColor(Qt::white);
    ui->customPlot->yAxis->setTickLabelColor(Qt::white);
    ui->customPlot->xAxis->setTickPen(QPen(Qt::white, 1));
    ui->customPlot->yAxis->setTickPen(QPen(Qt::white, 1));
    ui->customPlot->xAxis->setSubTickPen(QPen(Qt::white, 1));
    ui->customPlot->yAxis->setSubTickPen(QPen(Qt::white, 1));
    ui->customPlot->yAxis->setTicks(true);
    ui->customPlot->yAxis->setSubTicks(true);
    ui->customPlot->yAxis->setPadding(20);
//    ui->customPlot->yAxis->setTickStep(0.1);
//    ui->customPlot->yAxis2->setTickLabels(true);

    ui->customPlot->yAxis->grid()->setSubGridVisible(true);
    ui->customPlot->xAxis->grid()->setSubGridVisible(true);

    QSharedPointer<QCPAxisTickerFixed> intTicker(new QCPAxisTickerFixed);
    intTicker->setTickStep(0.001);
    intTicker->setScaleStrategy(QCPAxisTickerFixed::ssMultiples);
    ui->customPlot->yAxis->setTicker(intTicker);

//    ui->customPlot->rescaleAxes();
//    ui->customPlot->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom | QCP::iSelectAxes );
    ui->customPlot->setInteractions( QCP::iSelectAxes );
    ui->customPlot->xAxis->setRange(0, 512);
    ui->customPlot->yAxis->setRange(-5, 5);
    ui->customPlot->axisRect()->setupFullAxesBox();

    ui->customPlot->xAxis->setLabel("t");
    ui->customPlot->yAxis->setLabel("V");
    ui->customPlot->legend->setVisible(false);

    connect(ui->customPlot, SIGNAL(axisDoubleClick(QCPAxis*,QCPAxis::SelectablePart,QMouseEvent*)), this, SLOT(axisLabelDoubleClick(QCPAxis*,QCPAxis::SelectablePart)));

    ui->customPlot->setContextMenuPolicy(Qt::CustomContextMenu);
    connect(ui->customPlot, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(contextMenuRequest(QPoint)));

    for(int i=0;i<CHAN_NUM+1;i++)
        bchanshow[i] = true;

    recvStop = false;

    axisup = 0.0f;
    axiszoom = 1.0f;
    axisupnum = 0;
    axiszoomnum = 0;

    ui->chan1->setVisible(true);
    ui->chan2->setVisible(true);

    ui->chan1->setText("C1");
    ui->chan2->setText("C2");

    ui->chan1->setStyleSheet("background-color: rgb(250,0,0);font-size:14px;color:black");
    ui->chan2->setStyleSheet("background-color: rgb(0,250,0);font-size:14px;color:black");

    ui->customPlot->addGraph();
    ui->customPlot->graph(0)->setLineStyle((QCPGraph::LineStyle)(1));
    ui->customPlot->graph(0)->setPen(QPen(Qt::red));
    ui->customPlot->graph(0)->setName("chan1");

    ui->customPlot->addGraph();
    ui->customPlot->graph(1)->setLineStyle((QCPGraph::LineStyle)(1));
    ui->customPlot->graph(1)->setPen(QPen(Qt::green));
    ui->customPlot->graph(1)->setName("chan2");


}

MainWindow::~MainWindow()
{
    free(recvBuff1);
    recvBuff1 = NULL;

    free(recvBuff2);
    recvBuff2 = NULL;

    CloseDev(recvDevFd1);
    recvDevFd1 = 0;
    CloseDev(recvDevFd2);
    recvDevFd2 = 0;

    delete ui;
}

void MainWindow::axisLabelDoubleClick(QCPAxis *axis, QCPAxis::SelectablePart part)
{
  if (part == QCPAxis::spAxisLabel)
  {
    bool ok;
    QString newLabel = QInputDialog::getText(this, "osci", "New axis label:", QLineEdit::Normal, axis->label(), &ok);
    if (ok)
    {
      axis->setLabel(newLabel);
      ui->customPlot->replot();

    }
  }

}

void MainWindow::Timer_RecvfromDev1()
{
    m_pagecount1 = 0;
    m_curpagenum1 = ioctl(recvDevFd1, COMM_GET_CURPAGENUM,0);


    if(m_curpagenum1>0)
        m_curpagenum1 --;

#ifdef DISPLAY
    //DISPLAYPIXELS 512
    if(m_bepagenum1>m_curpagenum1)
    {
        memcpy(paintBuff1, m_pShareMem1 + (m_bepagenum1+1)*ADC_BUFF_SIZE, DISPLAYPIXELS * 2);
    }else if(m_bepagenum1 < m_curpagenum1)
    {
        memcpy(paintBuff1, m_pShareMem1 + (m_bepagenum1+1)*ADC_BUFF_SIZE, DISPLAYPIXELS * 2);
    }else{
        memcpy(paintBuff1, m_pShareMem1 + (m_curpagenum1+1)*ADC_BUFF_SIZE, DISPLAYPIXELS * 2);
    }

    m_bepagenum1 = m_curpagenum1;

    paintCnt1 = DISPLAYPIXELS;

#else
    //all data
    if(m_bepagenum1>m_curpagenum1)
    {
        memcpy(memBuff1, m_pShareMem1 + (m_bepagenum1+1)*ADC_BUFF_SIZE, (ADC_MEM_PAGE_NUM-m_bepagenum1-1)*ADC_BUFF_SIZE);
        m_pagecount1 = ADC_MEM_PAGE_NUM-m_bepagenum1-1;
        memcpy(memBuff1+m_pagecount1*ADC_BUFF_SIZE, m_pShareMem1, (m_curpagenum1+1)*ADC_BUFF_SIZE);
        m_pagecount1 = m_pagecount1 + m_curpagenum1 + 1;

    }else if(m_bepagenum1 < m_curpagenum1)
    {
        memcpy(memBuff1, m_pShareMem1 + (m_bepagenum1+1)*ADC_BUFF_SIZE, (m_curpagenum1-m_bepagenum1)*ADC_BUFF_SIZE);
        m_pagecount1 = m_curpagenum1-m_bepagenum1;
    }else{
        memcpy(memBuff1, m_pShareMem1 + (m_curpagenum1+1)*ADC_BUFF_SIZE, ADC_BUFF_SIZE);
        m_pagecount1 = 1;

    }

    m_bepagenum1 = m_curpagenum1;

/*    memset(paintBuff1, 0, QT_MAX_PKT_LEN*2);


    long size= (m_pagecount1*ADC_BUFF_SIZE)<(QT_MAX_PKT_LEN*2)?(m_pagecount1*ADC_BUFF_SIZE):(QT_MAX_PKT_LEN*2);
    if(size>0)
    {

        memcpy(paintBuff1, memBuff1, size);
        paintCnt1 = size/2;

   }*/
#endif


/
#ifdef DISPLAY
    //DISPLAYPIXELS 512
    m_pagecount2 = 0;
    m_curpagenum2 = ioctl(recvDevFd2, COMM_GET_CURPAGENUM,1);

    if(m_curpagenum2>0)
        m_curpagenum2 --;


    if(m_bepagenum2>m_curpagenum2)
    {
        memcpy(paintBuff2, m_pShareMem2 + (m_bepagenum2+1)*ADC_BUFF_SIZE, DISPLAYPIXELS * 2);
    }else if(m_bepagenum2 < m_curpagenum2)
    {
        memcpy(paintBuff2, m_pShareMem2 + (m_bepagenum2+1)*ADC_BUFF_SIZE, DISPLAYPIXELS * 2);
    }else{
        memcpy(paintBuff2, m_pShareMem2 + (m_curpagenum2+1)*ADC_BUFF_SIZE, DISPLAYPIXELS * 2);
    }

    m_bepagenum2 = m_curpagenum2;

    paintCnt2 = DISPLAYPIXELS;

#else
    m_pagecount2 = 0;
    m_curpagenum2 = ioctl(recvDevFd2, COMM_GET_CURPAGENUM,1);

    if(m_curpagenum2>0)
        m_curpagenum2 --;


    if(m_bepagenum2>m_curpagenum2)
    {
        memcpy(memBuff2, m_pShareMem2 + (m_bepagenum2+1)*ADC_BUFF_SIZE, (ADC_MEM_PAGE_NUM-m_bepagenum2-1)*ADC_BUFF_SIZE);

        m_pagecount2 = m_pagecount2 + ADC_MEM_PAGE_NUM-m_bepagenum2-1;

        memcpy(memBuff2+m_pagecount2*ADC_BUFF_SIZE, m_pShareMem2, (m_curpagenum2+1)*ADC_BUFF_SIZE);

        m_pagecount2 = m_pagecount2 + m_curpagenum2 + 1;


    }else if(m_bepagenum2 < m_curpagenum2)
    {
        memcpy(memBuff2, m_pShareMem2 + (m_bepagenum2+1)*ADC_BUFF_SIZE, (m_curpagenum2-m_bepagenum2)*ADC_BUFF_SIZE);

        m_pagecount2 = m_pagecount2 + m_curpagenum2-m_bepagenum2;


    }else{
        memcpy(memBuff2, m_pShareMem2 + (m_curpagenum2+1)*ADC_BUFF_SIZE, ADC_BUFF_SIZE);
        m_pagecount2 = 1;
    }


    m_bepagenum2 = m_curpagenum2;

    memset(paintBuff2, 0, QT_MAX_PKT_LEN*2);

    size= (m_pagecount2*ADC_BUFF_SIZE)<(QT_MAX_PKT_LEN*2)?(m_pagecount2*ADC_BUFF_SIZE):(QT_MAX_PKT_LEN*2);
    if(size>0)
    {

        memcpy(paintBuff2, memBuff2, size);
        paintCnt2 = size/2;

   }

#endif

 //  if(paintCnt2>0)
 //       DrawGraph(2);
//    paintCnt1 = paintCnt1>paintCnt2?paintCnt1:paintCnt2;

   if(paintCnt1>0)
        DrawGraph();
}

void MainWindow::removeAllGraphs()
{
  ui->customPlot->clearGraphs();
  ui->customPlot->replot();
}

void MainWindow::CloseDev(int fd)
{
    if (0 >= fd)
    {
        return;
    }

    ::close(fd);
}

void MainWindow::DrawGraph()
{
    int size = 0;
    size = paintCnt1<512?paintCnt1:512;
    QVector<double> y0(size), y1(size);
    QVector<double> x0(size);
    for(int i=0;i<size;i++)
    {
        y0[i] = paintBuff1[i]*10.0f/4096;
        y1[i] = paintBuff2[i]*10.0f/4096;
        x0[i] = i;
    }

    if(bchanshow[0])
    {
        ui->customPlot->graph(0)->setData(x0, y0);
        ui->customPlot->graph(0)->setVisible(true);
    }else{
        ui->customPlot->graph(0)->setVisible(false);
    }

    if(bchanshow[1])
    {
        ui->customPlot->graph(1)->setData(x0, y1);
        ui->customPlot->graph(1)->setVisible(true);
    }else{
        ui->customPlot->graph(1)->setVisible(false);
    }

    ui->customPlot->replot();
}

void MainWindow::on_chan1_clicked()
{
    bchanshow[0] = !bchanshow[0];
    if(bchanshow[0])
        ui->chan1->setText("C1");
    else
        ui->chan1->setText("c1");
}

void MainWindow::on_chan2_clicked()
{
    bchanshow[1] = !bchanshow[1];
    if(bchanshow[1])
        ui->chan2->setText("C2");
    else
        ui->chan2->setText("c2");

}


void MainWindow::on_stop_clicked()
{

    recvStop = !recvStop;
    if(recvStop)
    {
        if(recvFileTimer1!=NULL)
            recvFileTimer1->stop();
    }else
    {
        if(recvFileTimer1!=NULL)
            recvFileTimer1->start(0);
    }
}

void MainWindow::on_zoomin_clicked()
{

/*    QWheelEvent wheelEvent(QPointF(5,5), 120, Qt::MidButton, Qt::NoModifier);
    QCoreApplication::sendEvent(ui->customPlot, &wheelEvent);*/

    if(axiszoomnum<9)
    {
        ui->customPlot->yAxis->scaleRange(0.8f);//Scale by scale factor
        axiszoom = axiszoom*0.8f;
        ui->customPlot->replot();
        axiszoomnum++;
    }
}

void MainWindow::on_zoomout_clicked()
{
//    QWheelEvent wheelEvent(QPointF(5,5), -120, Qt::MidButton, Qt::NoModifier);
//    QCoreApplication::sendEvent(ui->customPlot, &wheelEvent);
    if(axiszoomnum>-6)
    {
        ui->customPlot->yAxis->scaleRange(1.25f);//Scale by scale factor
        axiszoom = axiszoom*1.25f;
        ui->customPlot->replot();
        axiszoomnum--;
    }
}

void MainWindow::on_up_clicked()
{
/*    QPoint pt=cursor().pos();//Get current mouse position

    QMouseEvent mousePressEvent(QEvent::MouseButtonPress, QPointF(5,5), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);
    QCoreApplication::sendEvent(ui->customPlot, &mousePressEvent);

    QMouseEvent mouseMoveEvent(QEvent::MouseMove, QPointF(5,7), Qt::NoButton, Qt::NoButton, Qt::NoModifier);
    QCoreApplication::sendEvent(ui->customPlot, &mouseMoveEvent);

    QMouseEvent mouseReleaseEvent(QEvent::MouseButtonRelease, QPointF(5,7), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);
    QCoreApplication::sendEvent(ui->customPlot, &mouseReleaseEvent);

    QMouseEvent mouseMoveEvent1(QEvent::MouseMove, QPointF(5,5), Qt::NoButton, Qt::NoButton, Qt::NoModifier);
    QCoreApplication::sendEvent(ui->up, &mouseMoveEvent1);*/


    ui->customPlot->yAxis->moveRange(0.5f);//Move axis
    axisup = axisup + 0.5f;
    ui->customPlot->replot();
}

void MainWindow::on_down_clicked()
{
/*
    QMouseEvent mousePressEvent(QEvent::MouseButtonPress, QPointF(5,7), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);
    QCoreApplication::sendEvent(ui->customPlot, &mousePressEvent);

    QMouseEvent mouseMoveEvent(QEvent::MouseMove, QPointF(5,5), Qt::NoButton, Qt::NoButton, Qt::NoModifier);
    QCoreApplication::sendEvent(ui->customPlot, &mouseMoveEvent);

    QMouseEvent mouseReleaseEvent(QEvent::MouseButtonRelease, QPointF(5,5), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);
    QCoreApplication::sendEvent(ui->customPlot, &mouseReleaseEvent);

    QMouseEvent mouseMoveEvent1(QEvent::MouseMove, QPointF(5,5), Qt::NoButton, Qt::NoButton, Qt::NoModifier);
    QCoreApplication::sendEvent(ui->up, &mouseMoveEvent1);*/
    ui->customPlot->yAxis->moveRange(-0.5f);//Move axis
    axisup = axisup - 0.5f;
    ui->customPlot->replot();
}
void MainWindow::on_back_clicked()
{
    ui->customPlot->yAxis->moveRange(-axisup);//Move axis
    ui->customPlot->yAxis->scaleRange(1/axiszoom);//Scale by scale factor
    axisup=0.0f;
    axiszoom = 1.0f;
    axiszoomnum = 0;
    ui->customPlot->replot();

}