Beagleboneblack 中DDS模块驱动源码(AD9833)

参考博客:基于OMAPL138的Linux字符驱动_GPIO驱动AD9833(二)之cdev与read、write

https://blog.csdn.net/u014281970/article/details/82145664

/*ad9833.c*/
/*
 * AD9833 of ADI driver code for Beagleboneblack  debian9.5  kernel 4.14.79
 *
 * Copyright (C) 2018 Wei Haochen	2019/1/21 modify by wangsong
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * MULTIBEANS, NPU Youyi West Ave, Beilin District, Xi'an, China.
 */

#include    /* Every Linux kernel module must include this head */
#include      /* Every Linux kernel module must include this head */
#include    /* printk() */
#include        /* struct fops */
#include     /* error codes */
#include      /* cdev_alloc()  */
#include    /* request_mem_region() */
#include 
#include 
#include 
#include 
#include 
#include 

#include 	//必须有

#include 
#include 

#include 
#include 
#include 


#define				DRV_AUTHOR					"Wei haochen Wangsong"
#define				DRV_DESC					"AD9833 on AM335X beaglebone"
#define				DRV_NAME					"AD9833-ADI"	//设备名称


#define				AD9833_SIZE					0x1000
#define				MEM_CLEAR					0x1
#define				AD9833_MAJOR				230			//主设备号
#define           	AD9833_REG_RESET            0x0100		//根据AD9833寄存器设定的重启
#define           	AD9833_FREQ0_REG            0
#define             AD9833_FREQ1_REG            1

//AD9833_MAGIC ioctl命令需要的参数,描述了ioctl命令的类型,8位。每种设备或系统都可以指定自己的一个类型号
//ioctl用这个类型来表示ioctl命令所属的设备或驱动,一般用ASCII码字符来表示,如‘a’/.
#define				AD9833_MAGIC				'k'
#define				CMD_PARA_FREQ				0x10
#define				CMD_PARA_PHASE				0x11
#define				CMD_PARA_TYPE				0x12

//_IO(type,nr):无数据传输。幻数(type)\序号(nr)
#define				CMD_TYPE_SIN				_IO( AD9833_MAGIC, 0)	//命令选择正弦波
#define				CMD_TYPE_TRI				_IO( AD9833_MAGIC, 1)	//命令选择三角波
#define				CMD_TYPE_SQE				_IO( AD9833_MAGIC, 2)	//命令选择方波

#define				CMD_FREQ_SET(X)				_IO( CMD_PARA_FREQ, X)
#define				CMD_PHASE_SET(X)			_IO( CMD_PARA_PHASE, X )
#define				CMD_TYPE_SET(X)				_IO( CMD_PARA_TYPE,X )

#define				IO_HIGH						1
#define				IO_LOW						0

//对应的beaglebone这个板子的GPIO
#define				AD9833_FSY_IO				30   //P9header 11
#define				AD9833_CLK_IO				48   //P9header 15
#define				AD9833_DAT_IO				49	 //P9header 23

//下面三个函数都仅仅在模拟SPI时序时候使用
#define				io_clk(x)					gpio_set_value( AD9833_CLK_IO,x )
#define				io_fsy(x)					gpio_set_value( AD9833_FSY_IO,x )
#define				io_dat(x)					gpio_set_value( AD9833_DAT_IO,x )


typedef	struct ad9833_t AD9833;

enum ad9833_wavetype_t{
	SIN,SQU,TRI
};

struct ad9833_hw_t {

	unsigned int clk;
	unsigned int sdi;
	unsigned int fsy;
};

struct ad9833_t {

	struct ad9833_hw_t hw;
	struct ad9833_t *self;
	enum ad9833_wavetype_t wave_type;

	struct	cdev	cdev;
	unsigned char	mem[ AD9833_SIZE ];

	unsigned int delay;

	void (*write_reg)	( AD9833 *self, unsigned int reg_value);
	void (*init_device)	( AD9833 *self );
	void (*set_wave_freq)( AD9833 *self , unsigned long freqs_data);
	void (*set_wave_type)( AD9833 *self, enum ad9833_wavetype_t wave_type );
	void (*set_wave_phase)( AD9833 *self, unsigned int phase );
	void (*set_wave_para)( AD9833 *self, unsigned long freqs_data, unsigned int phase, enum ad9833_wavetype_t wave_type );
};

static void ad9833_set_wave_type( AD9833 *dev, enum ad9833_wavetype_t wave_type );
static void ad9833_set_phase( AD9833 *dev, unsigned int phase_value );
static void ad9833_set_freq( AD9833 *dev, unsigned long freq );
static void ad9833_set_para( AD9833 *dev, unsigned long freqs_value, unsigned int phase_value, enum ad9833_wavetype_t wave_type );
static void ad9833_init_device( AD9833 *dev ) ;
static void ad9833_write_reg( AD9833 *dev, unsigned int reg_value );
static long ad9833_ioctl(struct file  *file, unsigned int cmd, unsigned long arg );

AD9833 *ad9833;
static int	ad9833_major	=	AD9833_MAJOR;	//主设备号

module_param( ad9833_major, int, S_IRUGO );

static const short	ad9833_gpios[] = {
		AD9833_FSY_IO,
		AD9833_CLK_IO,
		AD9833_DAT_IO,
};

AD9833 *ad9833_dev_new(void)
{
	AD9833 *dev = (AD9833*)kcalloc(1, sizeof(AD9833), GFP_ATOMIC);

	dev->hw.fsy			  =	  AD9833_FSY_IO;
	dev->hw.sdi			  =   AD9833_DAT_IO;
	dev->hw.clk			  =	  AD9833_CLK_IO;

	dev->set_wave_para    =   &ad9833_set_para;
	dev->init_device      =   &ad9833_init_device;
	dev->write_reg        =   &ad9833_write_reg;
	dev->set_wave_freq    =   &ad9833_set_freq;
	dev->set_wave_phase	  =   &ad9833_set_phase;
	dev->set_wave_type    =   &ad9833_set_wave_type;
	dev->init_device( dev );

	return dev;
}

static long ad9833_ioctl(struct file  *file, unsigned int cmd, unsigned long arg)
{

	printk(DRV_NAME "\tRecv cmd: %u\n", cmd);
	printk(DRV_NAME "\tRecv arg: %lu\n", arg);
	switch( cmd ) {
	case CMD_TYPE_SIN:
		ad9833->set_wave_freq(ad9833, 1500);
		ad9833->set_wave_type(ad9833, SIN);
		printk( DRV_NAME " set wave is sine wave! arg = %lu\n" , arg );

		break;

	case CMD_TYPE_TRI:
		ad9833->set_wave_freq(ad9833, 1500);
		ad9833->set_wave_type(ad9833, TRI);
		printk( DRV_NAME " set wave is tri wave! arg = %lu\n" , arg );
		break;

	case CMD_TYPE_SQE:
		ad9833->set_wave_freq(ad9833, 1500);
		ad9833->set_wave_type(ad9833, SQU);
		printk( DRV_NAME " set wave is sw wave! arg = %lu\n" , arg );
		break;

	}
	return	0;
}

//写入数据,模拟的3线SPI写入时序
static void ad9833_write_reg( AD9833 *dev, unsigned int reg_value )
{
	unsigned short i;
	io_clk(IO_HIGH);	//需要修改为为beaglebone的设置方式
	io_fsy(IO_HIGH);
	ndelay(10);
	io_fsy(IO_LOW);

	for ( i = 0; i < 16; i++ ) {

		if ( reg_value & 0x8000 )
			io_dat(IO_HIGH);
		else
			io_dat(IO_LOW);
		ndelay(10);
		io_clk(IO_LOW);
		ndelay(10);
		reg_value = reg_value << 1;
		ndelay(10);
		io_clk(IO_HIGH);
	}
	io_fsy(IO_HIGH);
	io_dat(IO_HIGH);
}
 
//ad9833初始化
static void ad9833_init_device( AD9833 *dev )
{
	dev->write_reg( dev, AD9833_REG_RESET );
	dev->set_wave_para( dev,1500, 0 ,SIN );
}

//设置相位
static void ad9833_set_para( AD9833 *dev, unsigned long freqs_value, unsigned int phase_value, enum ad9833_wavetype_t wave_type )
{
	unsigned long dds_frequence_data;
	unsigned int dds_frequence_low;
	unsigned int dds_frequence_high;
	unsigned int phase_data;
	phase_data      =   phase_value | 0xC000;

	dds_frequence_data      =   freqs_value * 10;
	dds_frequence_low       =   dds_frequence_data & 0x3FFF;
	dds_frequence_low       |=  0x4000;
	dds_frequence_data      =   dds_frequence_data >> 14;
	dds_frequence_high      =   dds_frequence_data & 0x3FFF;
	dds_frequence_high      |=  0x4000;
	// 	reset device
	dev->write_reg( dev, 0x0110 );
	dev->write_reg( dev, 0x2100 );

	dev->write_reg( dev,dds_frequence_low );
	dev->write_reg( dev,dds_frequence_high );
	dev->write_reg( dev, phase_data );

	if( wave_type == TRI ) {
		dev->write_reg( dev, 0x2002 );
	}else if( wave_type == SQU ) {
		dev->write_reg(  dev, 0x2028);
	}else {
		dev->write_reg( dev, 0x2000 );
	}
}

//设置频率,寄存器28位,分两次写入
static void ad9833_set_freq( AD9833 *dev, unsigned long freq )
{
	unsigned long dds_frequence_data;
	unsigned long dds_frequence_low;
	unsigned long dds_frequence_high;

	dds_frequence_data      =   freq;
	dds_frequence_low       =   dds_frequence_data & 0x3FFF;
	dds_frequence_low       |=  0x4000;
	dds_frequence_data      =   dds_frequence_data >> 14;
	dds_frequence_high      =   dds_frequence_data & 0x3FFF;
	dds_frequence_high      |=  0x4000;

	dev->write_reg( dev, dds_frequence_low );
	dev->write_reg( dev, dds_frequence_high );

}

static void
ad9833_set_phase( AD9833 *dev, unsigned int phase_value )
{
	unsigned int phase_temp;
	phase_temp = phase_value | 0xC000;
	dev->write_reg( dev, phase_temp );
}


//选择产生波的类型:TRI三角波;SQU方波;SIN正弦波
static void ad9833_set_wave_type( AD9833 *dev, enum ad9833_wavetype_t wave_type )
{
	if( wave_type == TRI ) {
		dev->write_reg( dev, 0x2002 );
	}else if( wave_type == SQU ) {
		dev->write_reg(  dev, 0x2028);
	}else {
		dev->write_reg( dev, 0x2000 );
	}
}

static ssize_t ad9833_driver_read( struct file *filp, char __user *buffer, size_t size, loff_t *f_pos )
{
	unsigned long 	p		=	*f_pos;
	unsigned int 	count	=	size;
	int 			ret		=	0;

	if ( p >= AD9833_SIZE )		//p为读的位置相对稳健开头的偏移
		return 0;
	if ( count > AD9833_SIZE - p )
		count = AD9833_SIZE - p;
	if ( raw_copy_to_user( buffer, ad9833->mem + p, count) ) { 
	//从内核空间拷贝数据到用户目录,buffer:目标地址: ad9833->mem + p;count:拷贝的字节数
	//copy_to_user()成功返回0;失败返回1.
		ret	=	-EFAULT;
	}else {
		*f_pos += count;
		ret = 	count;
		printk( DRV_NAME "\tread %u bytes from %lu\n", count, p );
	}
	return ret;
}


//从用户目录往内核空间写入数据
static ssize_t ad9833_driver_write( struct file *filp, const char __user *buffer, size_t size, loff_t *f_pos )
{
	unsigned long 	p		=	*f_pos;
	unsigned int 	count	=	size;
	int 			ret		=	0;
	char * endp="str";	

	if ( p >= AD9833_SIZE )
		return 0;
	if ( count > AD9833_SIZE - p )
		count = AD9833_SIZE - p;

	memset( ad9833->mem,0, AD9833_SIZE ); //用于对内存空间的初始化
	
	if ( raw_copy_from_user( ad9833->mem + p, buffer, count) ) {
		ret	=	-EFAULT;
	}else {
		*f_pos += count;
		ret = 	count;
		printk( DRV_NAME "\twrite %u bytes from %lu\n", count, p );
		printk( DRV_NAME "\tRecv: %s \n", ad9833->mem + p );
		printk( DRV_NAME "\tSet freq is: %ld \n", simple_strtol(ad9833->mem + p,&endp,0));
		//将一个字符串转换成unsigend long long型数据
		ad9833->set_wave_freq(ad9833, simple_strtol(ad9833->mem + p,&endp,0) );
	}
	return ret;
}
//用户进程利用在对设备文件进行诸如read/write操作的时候,系统调用通过设备文件的主设备号找
//到相应的设备驱动程序,然后读取这个数据结构相应的函数指针,接着把控制权交给该函数,这是
//Linux的设备驱动程序工作的基本原理。
static struct file_operations ad9833_fops = {

		.owner				=	THIS_MODULE,
		.read				=  	ad9833_driver_read,
		.write				=	ad9833_driver_write,
		.unlocked_ioctl  	=  	ad9833_ioctl,
};

static struct miscdevice ad9833_miscdev  = {

		.name				=	DRV_NAME,
		.fops				=	&ad9833_fops,
};

dev_t	devno;
static int __init ad9833_dev_init( void )	//入口函数
{
	int  i,ret;
	int  index_minor = 0;		//次设备号
	int mk_major;

	/*
	 * cdev alloc and release device code.
	 * */
	 //返回值:成功执行返回dev_t类型的设备编号
	 //ad9833_major:主设备号
	devno = MKDEV( ad9833_major, index_minor );		//获取设备在设备表中的位置
	
	mk_major	=	MKDEV(ad9833_major, 0);
	if( ad9833_major ) {	
	//主设备:devno:要分配的设备编号范围的初始值(次设备号常设为0); 
	//1:连续编号范围. 
	//DRV_NAME:编号相关联的设备名称. 
		ret = register_chrdev_region( devno, 1, DRV_NAME );  //注册模块
	}else {		//如果没有设备号,则动态申请一个设备号
	//alloc_chrdev_region() 函数用于动态申请设备编号范围
		ret = alloc_chrdev_region( &devno, 0, 1, DRV_NAME );
		ad9833_major	=	MAJOR(devno);
	}
	if( ret < 0 ) {
		printk(DRV_NAME "\t cdev alloc space failed.\n");
		return ret;
	}
	/*
	 * AD9833 new device
	 * */
	printk( DRV_NAME "\tApply memory for AD9833.\n" );
	ad9833 = ad9833_dev_new();
	if( !ad9833 ) {
		ret = -ENOMEM;
		printk(DRV_NAME "\tad9833 new device failed!\n" );
		goto fail_malloc;
	}

	/*
	 * AD9833 init gpios.
	 * */
	printk( DRV_NAME "\tInititial GPIO\n" );

	for ( i = 0; i < 3; i ++ ) {
		//ad9833_gpios[i]:则为你要申请的哪一个管脚;"AD9833 GPIO":为其取一个名字
		ret	=	gpio_request( ad9833_gpios[i], "AD9833 GPIO" );	//GPIO申请注册
		//返回值为0表示申请成功
		if( ret ) {
			printk("\t%s: request gpio %d for AD9833 failed, ret = %d\n", DRV_NAME,ad9833_gpios[i],ret);
			return ret;
		}else {
			printk("\t%s: request gpio %d for AD9833 set succussful, ret = %d\n", DRV_NAME,ad9833_gpios[i],ret);
		}
		gpio_direction_output( ad9833_gpios[i],1 );
		gpio_set_value( ad9833_gpios[i],0 );
	}

	/*
	 * cdev init.
	 * */
	cdev_init( &ad9833->cdev, &ad9833_fops );//静态内存定义初始化
	ad9833->cdev.owner	=	THIS_MODULE;
	ret = cdev_add( &ad9833->cdev, mk_major,1 );
	if( ret ) {
		printk( KERN_NOTICE "Error %d adding ad9833 %d", ret, 1 );
		return ret;
	}

	ret = misc_register( &ad9833_miscdev );
	printk( DRV_NAME "\tinitialized\n" );
	return 0;

	fail_malloc:
	unregister_chrdev_region( mk_major,1 );
	return ret;

}


static void __exit ad9833_dev_exit( void )		//出口函数
{
	int i;
	for( i = 0; i < 3; i++) {
		gpio_free( ad9833_gpios[i] );
	}
	misc_deregister( &ad9833_miscdev );
	unregister_chrdev_region( devno,1 );

}

module_init( ad9833_dev_init );
module_exit( ad9833_dev_exit );


MODULE_AUTHOR( DRV_AUTHOR );
MODULE_DESCRIPTION(DRV_DESC);
MODULE_LICENSE("GPL");


编译模块的Makefile:

#Makefile for ad9833.c
ARCH=arm
CROSS_COMPILE=arm-linux-gnueabihf-
ifneq ($(KERNELRELEASE),)
	obj-m := ad9833.o
else 
	KERNELDIR ?= /home/ws/4.14/linux-4.14/
	PWD := $(shell pwd)
default:
	make -C $(KERNELDIR) M=$(PWD) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) modules

clean:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) clean
	rm -rf modules.order

endif

测试用的源码:

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

#define				AD9833_MAGIC				'k'
#define				CMD_TYPE_SIN				_IO( AD9833_MAGIC, 0)
#define				CMD_TYPE_TRI				_IO( AD9833_MAGIC, 1)
#define				CMD_TYPE_SQE				_IO( AD9833_MAGIC, 2)


const char dev_path[]="/dev/AD9833-ADI";

int main(int argc , char *argv[])
{

    int fd = -1, i = 0;
    
    printf("ad9833 test program run....\n");


    fd = open(dev_path, O_RDWR|O_NDELAY);  // 打开设备

    if (fd < 0) {
        printf("Can't open /dev/AD9833-ADI\n");
        return -1;
    }

    printf("open device.\n");

    if( strcmp(argv[1],"1") == 0 ) {
	ioctl(fd, CMD_TYPE_SIN, 5);
	printf("argc = %d,sine wave = %s \n", CMD_TYPE_SIN, argv[1]);
    }else if(  strcmp(argv[1],"2") == 0 ) {
	ioctl(fd, CMD_TYPE_TRI, 1);
	printf("argc = %d,tri wave = %s \n", CMD_TYPE_TRI,argv[1]);
    }else{
 	ioctl(fd, CMD_TYPE_SQE, 1);
	printf("argc = %d,sqe wave = %s \n", CMD_TYPE_SQE, argv[1]);
    }
    write(fd, argv[2], strlen(argv[2]));
    
    printf("argc = %d\n", argc);
    close(fd);

    return 0;


}

编译测试代码的Makefile:

CROSS=arm-linux-gnueabihf-
all: ad9833_test 
ad9833_test: ad9833_test.c
	$(CROSS)gcc -o ad9833_test.o ad9833_test.c -static
clean:
	@rm -rf ad9833_test *.o

在调试原作者源码时遇到的问题:

1、 error: implicit declaration of function ‘copy_to_user’; did you mean‘raw_copy_to_user’? [-Werror=implicit-function-declaration]
  if ( copy_to_user( buffer, ad9833->mem + p, count) ) {
       ^~~~~~~~~~~~
       raw_copy_to_user
原因:linux4.14的内核取消了copy_to_user,改为了raw_copy_to_user

2、/home/ws/beaglebone_ad9833/driver/ad9833.c:149:9: error: function declaration isn’t a prototype [-Werror=strict-prototypes]
 AD9833 *ad9833_dev_new()
         ^~~~~~~~~~~~~~
原因:函数中传参为空的时候使用void,修改源码*ad9833_dev_new(void)


3、/home/ws/beaglebone_ad9833/driver/ad9833.c:310:25: warning: passing argument 1 of ‘raw_copy_to_user’ discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]
  if ( raw_copy_to_user( buffer, ad9833->mem + p, count) ) {

原因:传参的第一个函数将不是const定义
修改:
static ssize_t ad9833_driver_read( struct file *filp, const char __user *buffer, size_t size, loff_t *f_pos )

static ssize_t ad9833_driver_read( struct file *filp, char __user *buffer, size_t size, loff_t *f_pos )

4、/home/ws/beaglebone_ad9833/driver/ad9833.c:345:74: error: passing argument 2 of ‘simple_strtol’ from incompatible pointer type [-Werror=incompatible-pointer-types]
   printk( DRV_NAME "\tSet freq is: %d \n", simple_strtol(ad9833->mem + p,"str",0));
In file included from ./include/linux/list.h:9:0,
                 from ./include/linux/module.h:9,
                 from /home/ws/beaglebone_ad9833/driver/ad9833.c:21:
./include/linux/kernel.h:438:13: note: expected ‘char **’ but argument is of type ‘char *’
 extern long simple_strtol(const char *,char **,unsigned int);
原因:
        printk( DRV_NAME "\tSet freq is: %d \n", simple_strtol(ad9833->mem + p,"str",0));
        ad9833->set_wave_freq(ad9833, simple_strtol(ad9833->mem + p,"str",0) );

修改为:
        char * endp="str";

        printk( DRV_NAME "\tSet freq is: %d \n", simple_strtol(ad9833->mem + p,&endp,0));
        ad9833->set_wave_freq(ad9833, simple_strtol(ad9833->mem + p,&endp,0) );


5、/home/ws/beaglebone_ad9833/driver/ad9833.c:363:15: error: variable ‘ad9833_miscdev’ has initializer but incomplete type
 static struct miscdevice ad9833_miscdev  = {
原因:缺少头文件

#include


6、/home/ws/beaglebone_ad9833/driver/ad9833.c:362:25: error: initialization from incompatible pointer type [-Werror=incompatible-pointer-types]
   .unlocked_ioctl   =   ad9833_ioctl,
                         ^~~~~~~~~~~~
原因:内核版本变迁,现在ioctl函数的返回值应该定义为long型。
修改为:
static long ad9833_ioctl(struct file  *file, unsigned int cmd, unsigned long arg)

测试步骤说明:

  1. 将ad9833.c的源码和Makefile拷贝到一个文件夹下,在PC端的终端进入文件夹下,执行make命令,则可以编译成模块,其中Makefile中需要修改依赖的linux编译过得内核路径,也许交叉编译链用的也不一样,也需要修改。
  2. 将编译过的整个文件夹通过TFTP传输到与其相连的Beaglebone中。
  3. 登录到beaglebone上,使用insmod命令挂载模块,lsmod查看挂载状态,rmmod卸载模块。
  4. 接着回到PC端复制测试文件的源码和Makefile,放在一个文件夹下,将文件传输到Beaglebone中,通过串口(minicom)登录到Beaglebone上。在Beaglebone的该文件夹下执行make命令,生成可执行文件。
  5. root权限执行命令。
    sudo ./ad9833 1 2000

    第一个参数表示选择正弦波,第二个参数表示设置频率。

 

你可能感兴趣的:(BeagleBone)