赞
踩
参考博客:基于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 <linux/module.h> /* Every Linux kernel module must include this head */
- #include <linux/init.h> /* Every Linux kernel module must include this head */
- #include <linux/kernel.h> /* printk() */
- #include <linux/fs.h> /* struct fops */
- #include <linux/errno.h> /* error codes */
- #include <linux/cdev.h> /* cdev_alloc() */
- #include <linux/ioport.h> /* request_mem_region() */
- #include <linux/delay.h>
- #include <linux/moduleparam.h>
- #include <linux/types.h>
- #include <linux/gpio.h>
- #include <linux/device.h>
- #include <linux/types.h>
-
- #include <linux/miscdevice.h> //必须有
-
- #include <asm/uaccess.h>
- #include <asm/div64.h>
-
- #include <asm/atomic.h>
- #include <asm/unistd.h>
- #include <asm/io.h>
-
-
- #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 <stdio.h>
- #include <stdlib.h>
- #include <unistd.h>
- #include <sys/types.h>
- #include <sys/ioctl.h>
- #include <sys/stat.h>
- #include <fcntl.h>
- #include <string.h>
-
- #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 <linux/miscdevice.h>
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
第一个参数表示选择正弦波,第二个参数表示设置频率。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。