参考博客:基于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)
测试步骤说明:
sudo ./ad9833 1 2000
第一个参数表示选择正弦波,第二个参数表示设置频率。