AD7705设备驱动代码,开发板:iTOP4412精英版,内核版本:Linux3.0.15,结果会比实际值偏移一位,正在查找原因,随时更新,欢迎讨论!
(1)在内核平台文件中spi2_board_info[]添加设备信息如下:
#ifdef CONFIG_AD7705_CTL //direct use spi2, or use spi-gpio
{
.modalias = "AD7705",
.platform_data = NULL,
.max_speed_hz = 1*1000*1000,
.bus_num = 2, //spi2总线
.chip_select =0,
.mode = SPI_MODE_3, //采用SPI mode3工作模式
.controller_data = &spi2_csi[0],
},
#endif
(2)在内核源码中drives/spi目录下配置Kconfig:
config AD7705_CTL
tristate "AD7705 Module driver support"
depends on EXPERIMENTAL
help
This supports AD7705 Module drivers.
(3)并在对应Makefile中添加编译选项:
obj-$(CONFIG_AD7705_CTL) += ad7705.o
(4)驱动源码:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/err.h>
#include <linux/miscdevice.h>
#include <linux/gpio.h>
#include <linux/delay.h>
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/fs.h>
#include <linux/time.h>
#include <linux/errno.h>
#include <linux/clk.h>
#include <linux/compat.h>
#include <asm/uaccess.h>
#include <linux/mutex.h>
#include <linux/kfifo.h>
#include <linux/spi/spi.h>
#include <linux/spi/spidev.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/cdev.h>
#include <linux/kdev_t.h>
#include <plat/gpio-cfg.h>
#include <mach/gpio-exynos4.h>
/*
*********************************************************************************************************
* 所有的寄存器(包括通信寄存器本身和输出数据寄存器)进行读操作之前,
* 必须先写通信寄存器,然后才能读选定的寄存器。
*********************************************************************************************************
*/
enum
{
AD7705_REG_COMM = (0 << 4), //8bits
AD7705_REG_SETUP = (1 << 4), //8bits
AD7705_REG_CLOCK = (2 << 4), //8bits
AD7705_REG_DATA = (3 << 4), //16bits
AD7705_REG_TEST = (4 << 4), //8bits
AD7705_REG_OFFSET = (6 << 4), //24bits
AD7705_REG_GAIN = (7 << 4), //24bits
AD7705_WRITE = (0 << 3),
AD7705_READ = (1 << 3),
AD7705_CH_1 = 0, // AIN1+ AIN1-
AD7705_CH_2 = 1, // AIN2+ AIN2-
AD7705_CH_3 = 2, // AIN1- AIN1-
AD7705_CH_4 = 3 // AIN1- AIN2-
};
enum
{
AD7705_MD_NORMAL = (0 << 6), //正常模式
AD7705_MD_CAL_SELF = (1 << 6), //自校准
AD7705_MD_CAL_ZERO = (2 << 6), //零标度系统校准
AD7705_MD_CAL_FULL = (3 << 6), //满标度系统校准
AD7705_GAIN_1 = (0 << 3),
AD7705_GAIN_2 = (1 << 3),
AD7705_GAIN_4 = (2 << 3),
AD7705_GAIN_8 = (3 << 3),
AD7705_GAIN_16 = (4 << 3),
AD7705_GAIN_32 = (5 << 3),
AD7705_GAIN_64 = (6 << 3),
AD7705_GAIN_128 = (7 << 3),
AD7705_BIPOLAR = (0 << 2), //双极性工作
AD7705_UNIPOLAR = (1 << 2), //单极性工作
AD7705_BUF_NO = (0 << 1), //缓冲器控制
AD7705_BUF_EN = (1 << 1),
AD7705_FSYNC_0 = 0, //滤波器同步控制
AD7705_FSYNC_1 = 1
};
enum
{
AD7705_CLKDIS_0 = (0 << 4), //主时钟禁止位
AD7705_CLKDIS_1 = (1 << 4),
AD7705_CLKDIV_0 = (0 << 3), //2.4576MHz (CLKDIV=0 )
AD7705_CLKDIV_1 = (1 << 3), //4.9152MHz (CLKDIV=1 )
AD7705_CLK_0 = (0 << 2), //时钟位,主时钟频率为2.4576MHz(CLKDIV=0)或为4.9152MHz(CLKDIV=1),CLK应置“0”,主时钟频率为1MHz(CLKDIV=0)或2MHz(CLKDIV=1),CLK应置“1”
AD7705_CLK_1 = (1 << 2),
AD7705_UPDATE_20 = (0), //输出更新速度,不同于AD采样速率
AD7705_UPDATE_25 = (1),
AD7705_UPDATE_100 = (2),
AD7705_UPDATE_200 = (3),
AD7705_UPDATE_50 = (0),
AD7705_UPDATE_60 = (1),
AD7705_UPDATE_250 = (2),
AD7705_UPDATE_500 = (3)
};
#define SPIDEV_MAJOR 153 // 主设备号
#define N_SPI_MINORS 123 // 次设备号
#define AD7705_CHANNEL_NUM (2)
#define AD7705_DRDY_PIN (EXYNOS4_GPA1(4)) //BUF_GPS_TXD AD7705的引脚DRDY ,配置为输入引脚
#define AD7705_RESET_PIN (EXYNOS4_GPA1(5)) //BUF_GPS_RXD AD7705的引脚RESET ,配置为输出引脚
#define AD7705_MISO_PIN (EXYNOS4_GPC1(3))
#define AD7705_MOSI_PIN (EXYNOS4_GPC1(4))
#define AD7705_SCLK_PIN (EXYNOS4_GPC1(1))
#define AD7705_NSS_PIN (EXYNOS4_GPC1(2))
#define AD7705_BUFFER 2048
static struct class *AD7705_class = NULL;
struct AD7705_spidata {
struct spi_device *spi_dev;
dev_t major_num; //主设备号
dev_t minor_num; //次设备号
dev_t cdev_num; //设备号
u16 val;
struct cdev *AD7705_cdev; //字符设备
unsigned char *buffer;
};
static struct AD7705_spidata *AD7705 = NULL; //私有数据
/*
*********************************************************************************************************
* 函 数 名: AD7705_request_gpio
* 功能说明: 申请GPIO引脚,并配置引脚功能
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
static void AD7705_request_gpio(void)
{
printk(KERN_EMERG"[%s][%d]\r\n", __FUNCTION__, __LINE__);
if (gpio_request(AD7705_MISO_PIN, "MISO"))
{
printk(KERN_EMERG"[%s][%d]: gpio_request(AD7705_MISO_PIN) fail.\n", __FUNCTION__, __LINE__);
}
if (gpio_request(AD7705_MOSI_PIN, "MOSI"))
{
printk(KERN_EMERG"[%s][%d]: gpio_request(AD7705_MOSI_PIN) fail.\n", __FUNCTION__, __LINE__);
}
if (gpio_request(AD7705_SCLK_PIN, "SCLK"))
{
printk(KERN_EMERG"[%s][%d]: gpio_request(AD7705_SCLK_PIN) fail.\n", __FUNCTION__, __LINE__);
}
if((s3c_gpio_cfgpin(AD7705_MISO_PIN, S3C_GPIO_SFN(0x5))) < 0
|| (s3c_gpio_cfgpin(AD7705_MOSI_PIN, S3C_GPIO_SFN(0x5))) < 0
|| (s3c_gpio_cfgpin(AD7705_SCLK_PIN, S3C_GPIO_SFN(0x5))) < 0
|| (s3c_gpio_cfgpin(AD7705_NSS_PIN, S3C_GPIO_SFN(0x5))) < 0)
{
printk(KERN_DEBUG"[%s][%d]: s3c_gpio_cfgpin faild", __FUNCTION__, __LINE__);
}
if(gpio_request(AD7705_DRDY_PIN, "AD7705 DRDY"))
{
gpio_free(AD7705_DRDY_PIN);
printk(KERN_EMERG"gpio request drdy pin faild!\n");
}
if(s3c_gpio_cfgpin(AD7705_DRDY_PIN, S3C_GPIO_INPUT) < 0)
{
printk(KERN_EMERG"AD7705_DRDY_PIN error!\n");
}
if(gpio_request(AD7705_RESET_PIN, "AD7705 RESET"))
{
gpio_free(AD7705_RESET_PIN);
printk(KERN_EMERG"gpio request reset pin faild!\n");
}
if(s3c_gpio_cfgpin(AD7705_RESET_PIN, S3C_GPIO_OUTPUT) < 0)
{
printk(KERN_EMERG"AD7705_RESET_PIN error!\n");
}
s3c_gpio_setpull(AD7705_RESET_PIN, S3C_GPIO_PULL_UP);
gpio_set_value(AD7705_RESET_PIN, 1);
return ;
}
/*
*********************************************************************************************************
* 函 数 名: AD7705_free_gpio
* 功能说明: 释放申请的GPIO
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
static void AD7705_free_gpio(void)
{
printk(KERN_EMERG"[%s][%d]\r\n", __FUNCTION__, __LINE__);
gpio_free(AD7705_MISO_PIN);
gpio_free(AD7705_MOSI_PIN);
gpio_free(AD7705_SCLK_PIN);
gpio_free(AD7705_NSS_PIN);
gpio_free(AD7705_RESET_PIN);
gpio_free(AD7705_DRDY_PIN);
return ;
}
/*
*********************************************************************************************************
* 函 数 名: AD7705_reset
* 功能说明: 复位 AD7705芯片
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
static void AD7705_reset(void)
{
printk(KERN_EMERG"[%s][%d]\r\n", __FUNCTION__, __LINE__);
gpio_set_value(AD7705_RESET_PIN, 1);
msleep(1);
gpio_set_value(AD7705_RESET_PIN, 0);
msleep(5);
gpio_set_value(AD7705_RESET_PIN, 1);
msleep(1);
return ;
}
/*
*********************************************************************************************************
* 函 数 名: AD7705_sync_spi
* 功能说明: 同步AD7705芯片SPI接口时序
* 形 参:
* 返 回 值: 无
*********************************************************************************************************
*/
static void AD7705_sync_spi(struct spi_device *spi_dev)
{
u8 tx_buf[4] = {0xFF,0xFF,0xFF,0xFF};
printk(KERN_EMERG"[%s][%d]\r\n", __FUNCTION__, __LINE__);
// 至少32个串行时钟内向AD7705的DIN线写入逻辑"1"
spi_write(spi_dev, tx_buf, sizeof(tx_buf));
}
/*
*********************************************************************************************************
* 函 数 名: AD7705_wait_DRDY
* 功能说明: 等待内部操作完成。 自校准时间较长,需要等待。
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
static int AD7705_wait_DRDY(void)
{
int i = 0;
uint32_t time_cnt = 500; //超时1s
// printk(KERN_EMERG"[%s][%d]\r\n", __FUNCTION__, __LINE__);
for (i=0; i<time_cnt; i++)
{
if (0 == gpio_get_value(AD7705_DRDY_PIN))
{
break;
}
msleep(1);
}
if (i >= time_cnt)
{
printk(KERN_EMERG"[%s][%d]: AD7705_wait_DRDY Time Out ...\r\n", __FUNCTION__, __LINE__);
return -1;
}
return 0;
}
/*
*********************************************************************************************************
* 函 数 名: AD7705_calibZero_self
* 功能说明: 启动自校准. 内部自动短接AIN+ AIN-校准0位,内部短接到Vref 校准满位。此函数执行过程较长
* 实测约 180ms
* 形 参: channel : ADC通道,1或2
* 返 回 值: 无
*********************************************************************************************************
*/
static void AD7705_systemcalib_self(struct spi_device *spi_dev, u8 channel)
{
u8 tx_buf[2] = {0};
u8 rx_buf[1] = {0};
printk(KERN_EMERG"[%s][%d]\r\n", __FUNCTION__, __LINE__);
tx_buf[0] = AD7705_REG_SETUP | AD7705_WRITE | channel; // 写设置寄存器
tx_buf[1] = AD7705_MD_CAL_SELF | AD7705_GAIN_1 | AD7705_UNIPOLAR | AD7705_BUF_EN | AD7705_FSYNC_0;
//配置通道channel(0x46):激活自校准,增益为1,单极性工作,允许输入缓冲,滤波器同步设置为0
printk(KERN_EMERG"[%s][%d]: Write SETUP Register: 0x%x\r\n", __FUNCTION__, __LINE__, tx_buf[1]);
spi_write(spi_dev, tx_buf, sizeof(tx_buf));
msleep(250); /* 等待内部操作完成 --- 时间较长,约180ms*/
/**********************************读寄存器写入的数据,用作调试****************************************/
AD7705_wait_DRDY();
memset(rx_buf, 0, sizeof(rx_buf));
tx_buf[0] = AD7705_REG_SETUP | AD7705_READ | channel; //(0x46) 读设置寄存器 , 用作调试
rx_buf[0] = spi_w8r8(spi_dev, tx_buf[0]);
if(rx_buf[0] < 0)
{
printk(KERN_EMERG"[%s][%d]: Read SETUP Register faild.\r\n", __FUNCTION__, __LINE__);
}else{
printk(KERN_EMERG"[%s][%d]: Read SETUP Register: 0x%x\r\n", __FUNCTION__, __LINE__, rx_buf[0]);
}
AD7705_wait_DRDY();
memset(rx_buf, 0, sizeof(rx_buf));
tx_buf[0] = AD7705_REG_CLOCK | AD7705_READ | channel; //(0x8) 读时钟寄存器 , 用作调试
rx_buf[0] = spi_w8r8(spi_dev, tx_buf[0]);
if(rx_buf[0] < 0)
{
printk(KERN_EMERG"[%s][%d]: Read CLOCK Register faild.\r\n", __FUNCTION__, __LINE__);
}else{
printk(KERN_EMERG"[%s][%d]: Read CLOCK Register: 0x%x\r\n", __FUNCTION__, __LINE__, rx_buf[0]);
}
/***********************************************************************************************************/
return ;
}
/*
*********************************************************************************************************
* 函 数 名: AD7705_calibZero_self
* 功能说明: 启动系统校准零位. 请将AIN+ AIN-短接后,执行该函数。校准应该由主程序控制并保存校准参数。
* 执行完毕后。可以通过 AD7705_ReadReg(REG_ZERO_CH1) 和 AD7705_ReadReg(REG_ZERO_CH2) 读取校准参数。
* 形 参: channel : ADC通道,1或2
* 返 回 值: 无
*********************************************************************************************************
*/
static void AD7705_calibZero_self(struct spi_device *spi_dev, u8 channel)
{
u8 tx_buf[2] = {0};
printk(KERN_EMERG"[%s][%d]\r\n", __FUNCTION__, __LINE__);
tx_buf[0] = AD7705_REG_SETUP | AD7705_WRITE | channel; // 写设置寄存器
tx_buf[1] = AD7705_MD_CAL_ZERO | AD7705_GAIN_1 | AD7705_UNIPOLAR | AD7705_BUF_EN | AD7705_FSYNC_0; //配置通道channel:激活零标度系统校准,增益为1,单极性工作,允许输入缓冲,滤波器同步设置为0
spi_write(spi_dev, tx_buf, sizeof(tx_buf));
msleep(200);
return ;
}
/*
*********************************************************************************************************
* 函 数 名: AD7705_calibFull_self
* 功能说明: 启动系统校准满位. 请将AIN+ AIN-接最大输入电压源,执行该函数。校准应该由主程序控制并保存校准参数。
* 执行完毕后。可以通过 AD7705_ReadReg(REG_FULL_CH1) 和 AD7705_ReadReg(REG_FULL_CH2) 读取校准参数。
* 形 参: channel : ADC通道,1或2
* 返 回 值: 无
*********************************************************************************************************
*/
static void AD7705_calibFull_self(struct spi_device *spi_dev, u8 channel)
{
u8 tx_buf[2] = {0};
printk(KERN_EMERG"[%s][%d]\r\n", __FUNCTION__, __LINE__);
tx_buf[0] = AD7705_REG_SETUP | AD7705_WRITE | channel; // 写设置寄存器
tx_buf[1] = AD7705_MD_CAL_FULL | AD7705_GAIN_1 | AD7705_UNIPOLAR | AD7705_BUF_EN | AD7705_FSYNC_0; //配置通道channel(0x46):激活满标度系统校准,增益为1,单极性工作,允许输入缓冲,滤波器同步设置为0
spi_write(spi_dev, tx_buf, sizeof(tx_buf));
msleep(200);
return ;
}
/*
*********************************************************************************************************
* 函 数 名: AD7705_config_channel
* 功能说明: 配置AD7705的指定通道
* 形 参: channel : ADC通道,1或2
* 返 回 值: 无
*********************************************************************************************************
*/
static void AD7705_config_channel(struct spi_device *spi_dev, u8 channel)
{
u8 tx_buf[2] = {0};
// printk(KERN_EMERG"[%s][%d]\r\n", __FUNCTION__, __LINE__);
tx_buf[0] = AD7705_REG_CLOCK | AD7705_WRITE | channel; // 写时钟寄存器
tx_buf[1] = AD7705_CLKDIS_0 | AD7705_CLKDIV_1 | AD7705_CLK_0 | AD7705_UPDATE_50;
//配置通道channel(0x8):允许主时钟输出,时钟分频,CLK为4.9152MHz,输出更新速率为50Hz
printk(KERN_EMERG"[%s][%d]: Write CLOCK Register: 0x%x\r\n", __FUNCTION__, __LINE__, tx_buf[1]);
spi_write(spi_dev, tx_buf, sizeof(tx_buf));
AD7705_systemcalib_self(spi_dev, channel); //内部自校准通道channel
return ;
}
/*
*********************************************************************************************************
* 函 数 名: AD7705_config_channel
* 功能说明: 初始化AD7705, 复位AD7705并重新配置
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
static void AD7705_reset_and_reconfig(struct spi_device *spi_dev)
{
// printk(KERN_EMERG"[%s][%d]\r\n", __FUNCTION__, __LINE__);
AD7705_reset(); //复位AD7705
msleep(1);
AD7705_sync_spi(spi_dev); //同步SPI接口时序, AD7705串行接口失步后将其复位。复位后要延时500us再访问
msleep(1);
AD7705_config_channel(spi_dev, AD7705_CH_1);
return ;
}
/*
*********************************************************************************************************
* 函 数 名: AD7705_read_channel
* 功能说明: 读AD7705
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
static int AD7705_read_channel(struct spi_device *spi_dev, u8 channel, u16 *ad)
{
int ret = -1;
u16 value = 0xffff;
u8 tx_buf[1] = {0};
u8 rx_buf[2] = {0};
// printk(KERN_EMERG"[%s][%d]\r\n", __FUNCTION__, __LINE__);
AD7705_wait_DRDY(); // 等待转换完成
tx_buf[0] = AD7705_REG_DATA | AD7705_READ | channel; //(0x38)下一步对数据寄存器进行读操作
ret = spi_write_then_read(spi_dev, tx_buf, sizeof(tx_buf), rx_buf, sizeof(rx_buf)); //每次读的都是上一次转换结果
value = (rx_buf[0]<<8) | rx_buf[1];
//value = value << 1; //读出数据会比真实数据向右偏移一位,比如实际数据0xc,可能读出0x8006,且最高位总为1,正在查找原因 !!!!!!!!!!!!!!!
if (ret < 0)
{
printk(KERN_EMERG "[%s][%d]: AD7705_read_byte() faild. ret=%d \n", __FUNCTION__, __LINE__, ret);
goto fail;
}
if (0xffff == value) //接口迷失, // AD7705上电一段时间后,可能会出现读到的值一直是0xfff的情况
{
printk(KERN_EMERG "Error: [%s][%d]: value = 0xffff \n", __FUNCTION__, __LINE__);
ret = -1;
goto fail;
}
*ad = value;
fail:
return ret;
}
/*
*********************************************************************************************************
* 函 数 名: AD7705_get_value
* 功能说明: 获取AD7705的读数值
* 形 参: channel : ADC通道,1或2; val: AD7705转换结果,传入参数地址
* 返 回 值: 无
*********************************************************************************************************
*/
static ssize_t AD7705_get_value(struct spi_device *spi_dev, u8 channel, u16 *val)
{
int ret = 0;
int i=0;
// printk(KERN_EMERG"[%s][%d]\r\n", __FUNCTION__, __LINE__);
/*
* 为了避免通道切换造成读数失效,读2次
* 实际上每次读到的是上一次采集的结果(可以两个通道交替采集就能看到效果)
*/
for(i=0; i<2; i++)
{
ret = AD7705_read_channel(spi_dev, channel, val);
if (ret < 0)
{
/*失败,重启AD7705并重新配置*/
AD7705_reset_and_reconfig(spi_dev);
printk(KERN_EMERG "Error: [%s][%d]: AD7705_reset_and_reconfig...\n", __FUNCTION__, __LINE__);
return ret;
}
msleep(5); //防止读数频率过快
}
return ret;
}
static ssize_t AD7705_read(struct file *filp, char __user *buf, size_t len, loff_t *lft)
{
struct AD7705_spidata *adc;
unsigned long missing;
unsigned char *tmp = NULL; //分配4个字节缓冲区
// printk(KERN_EMERG"[%s][%d]\r\n", __FUNCTION__, __LINE__);
if((!(tmp = kmalloc(4, GFP_KERNEL))) || (!(adc = kmalloc(sizeof(struct AD7705_spidata), GFP_KERNEL))))
{
printk(KERN_EMERG"[%s][%d]: kmalloc error, Kernel read faild!\n", __FUNCTION__, __LINE__);
return -1;
}
memset(adc, 0, sizeof(struct AD7705_spidata));
adc = (struct AD7705_spidata *) filp->private_data;
adc -> val = 0x0;
AD7705_get_value(adc->spi_dev, AD7705_CH_1, &adc->val);
printk(KERN_EMERG"[%s][%d]: AD7705 value = 0x%x\r\n", __FUNCTION__, __LINE__, adc->val);
sprintf(tmp, " %u" , adc->val); // 将ad值传递给用户程序
missing = copy_to_user(buf, tmp, strlen(tmp));
if(missing)
{
printk(KERN_EMERG"missing data %ld Bytes.\n", missing);
}
kfree(tmp);
return 0;
}
static int AD7705_open(struct inode *node, struct file *filp)
{
printk(KERN_EMERG"[%s][%d]\r\n", __FUNCTION__, __LINE__);
if(!(AD7705 -> buffer = (unsigned char *) kmalloc(AD7705_BUFFER, GFP_KERNEL)))
{
printk(KERN_EMERG"AD7705 -> buffer error!\n");
}
filp -> private_data = AD7705; //获得私有数据结构
return 0;
}
static int AD7705_close(struct inode *inode, struct file *filp)
{
printk(KERN_EMERG"[%s][%d]\r\n", __FUNCTION__, __LINE__);
return 0;
}
static ssize_t AD7705_write(struct file *filp, const char __user *buf, size_t len, loff_t *lft)
{
// printk(KERN_EMERG"[%s][%d]\r\n", __FUNCTION__, __LINE__);
return 0;
}
static long AD7705_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
// printk(KERN_EMERG"[%s][%d]\r\n", __FUNCTION__, __LINE__);
return 0;
}
static struct file_operations AD7705_fops = {
.owner = THIS_MODULE,
.open = AD7705_open,
.release = AD7705_close,
.read = AD7705_read,
.write = AD7705_write,
.unlocked_ioctl = AD7705_ioctl,
};
static int __devinit AD7705_probe(struct spi_device *spi_dev)
{
int status = -1;
printk(KERN_EMERG"[%s][%d]\r\n", __FUNCTION__, __LINE__);
if(!(AD7705 = kmalloc(sizeof(struct AD7705_spidata), GFP_KERNEL)))
{
printk(KERN_EMERG"kmalloc faild!\n");
}
memset(AD7705, 0, sizeof(struct AD7705_spidata));
BUILD_BUG_ON(N_SPI_MINORS > 256);
AD7705 -> spi_dev = spi_dev;
AD7705 -> major_num = SPIDEV_MAJOR; //申请设备号,SPI设备固定主设备号为153
AD7705 -> minor_num = N_SPI_MINORS;
if(AD7705 -> major_num || AD7705 -> minor_num)
{
AD7705 -> cdev_num = MKDEV(AD7705 -> major_num, AD7705 -> minor_num);
status = register_chrdev_region(AD7705 -> cdev_num, 1, "AD7705"); //申请设备号
if(status < 0)
{
printk(KERN_EMERG"[%s][%d]: register_chrdev_region faild ! Now alloc_chrdev_region\r\n", __FUNCTION__, __LINE__);
alloc_chrdev_region(&AD7705 -> cdev_num, AD7705 -> minor_num, 1, "AD7705");
}
}else{
alloc_chrdev_region(&AD7705 -> cdev_num, AD7705 -> minor_num, 1, "AD7705");
}
AD7705 -> major_num = MAJOR(AD7705 -> cdev_num);
AD7705 -> minor_num = MINOR(AD7705 -> cdev_num);
printk(KERN_EMERG"[%s][%d]: major_num is %d, minor_num is %d\n", __FUNCTION__, __LINE__, AD7705 -> major_num, AD7705 -> minor_num);
if(!(AD7705 -> AD7705_cdev = kmalloc(sizeof(struct cdev), GFP_KERNEL)))
{
printk(KERN_EMERG"kmalloc faild!\n");
goto faild;
}
memset(AD7705->AD7705_cdev, 0, sizeof(struct cdev));
AD7705->AD7705_cdev -> owner = THIS_MODULE;
AD7705->AD7705_cdev -> ops = &AD7705_fops;
cdev_init(AD7705->AD7705_cdev, &AD7705_fops);
if(cdev_add(AD7705 -> AD7705_cdev, AD7705 -> cdev_num, 1) < 0)
{
printk(KERN_EMERG"[%s][%d]: cdev_add faild!\n", __FUNCTION__, __LINE__);
goto faild;
}
if(!(AD7705_class = class_create(THIS_MODULE, "AD7705_Class")))
{
printk(KERN_EMERG"[%s][%d]: class_create faild!\n", __FUNCTION__, __LINE__);
goto faild;
}
device_create(AD7705_class, NULL, AD7705 -> cdev_num, NULL, "AD7705"); //创建设备节点
AD7705_request_gpio();
AD7705_reset_and_reconfig(AD7705 -> spi_dev); //初始化AD7705
return 0;
faild:
printk(KERN_EMERG"AD7705 module init faild!\n");
unregister_chrdev_region(AD7705 -> cdev_num, 1);
kfree(AD7705 -> AD7705_cdev);
return -1;
}
static int __devexit AD7705_remove(struct spi_device *spi_dev)
{
printk(KERN_EMERG"[%s][%d]\r\n", __FUNCTION__, __LINE__);
AD7705_free_gpio();
device_destroy(AD7705_class, AD7705 -> cdev_num);
class_destroy(AD7705_class);
cdev_del(AD7705 -> AD7705_cdev);
kfree(AD7705);
unregister_chrdev_region(AD7705 -> cdev_num, 1);
return 0;
}
static struct spi_driver AD7705_driver = {
.driver = {
.name = "AD7705", //驱动名称,需要与设备名称匹配
.owner = THIS_MODULE,
},
.probe = AD7705_probe,
.remove = __devexit_p(AD7705_remove),
};
static int __init init_AD7705(void)
{
if(spi_register_driver(&AD7705_driver) < 0)
{
printk(KERN_EMERG"[%s][%d]: spi_register_driver faild!\n", __FUNCTION__, __LINE__);
spi_unregister_driver(&AD7705_driver);
return -1;
}
printk(KERN_EMERG"[%s][%d]: AD7705 module init success!\n", __FUNCTION__, __LINE__);
return 0;
}
static void __exit exit_AD7705(void)
{
spi_unregister_driver(&AD7705_driver);
printk(KERN_EMERG"[%s][%d]: AD7705 module exit success!\n", __FUNCTION__, __LINE__);
}
module_init(init_AD7705);
module_exit(exit_AD7705);
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("YANG");
MODULE_DESCRIPTION("AD7705 Linux driver");
(5)应用测试源码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
void read_channel(char *dev_file_path)
{
int fd = 0;
int ret = 0;
unsigned int buff[128] = {0};
fd = open(dev_file_path, O_RDONLY);
if (-1 == fd)
{
printf("[%s] open device file fail.\n", __FUNCTION__);
return ;
}
memset(buff, 0, 128);
ret = read(fd, buff, 128);
if (0 > ret)
{
printf("[%s] not read data. ret=%d\n", __FUNCTION__, ret);
}
printf("[%s] buff=%s\n\n", __FUNCTION__, buff);
close(fd);
return ;
}
int main(void)
{
char dev_path[] = {"/dev/AD7705"};
while(1)
{
read_channel(dev_path);
sleep(1);
}
}
参考博客:https://blog.youkuaiyun.com/caogos/article/details/53034196