AD7705驱动代码 -- Linux SPI设备驱动
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 EXPERIMENTALhelp 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), //8bitsAD7705_REG_SETUP = (1 << 4), //8bitsAD7705_REG_CLOCK = (2 << 4), //8bitsAD7705_REG_DATA = (3 << 4), //16bitsAD7705_REG_TEST = (4 << 4), //8bitsAD7705_REG_OFFSET = (6 << 4), //24bitsAD7705_REG_GAIN = (7 << 4), //24bitsAD7705_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,单极性工作,允许输入缓冲,滤波器同步设置为0printk(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,单极性工作,允许输入缓冲,滤波器同步设置为0spi_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,单极性工作,允许输入缓冲,滤波器同步设置为0spi_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); //内部自校准通道channelreturn ;
} /*
*********************************************************************************************************
* 函 数 名: 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设备固定主设备号为153AD7705 -> 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); //初始化AD7705return 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.csdn.net/caogos/article/details/53034196
AD7705驱动代码 -- Linux SPI设备驱动相关推荐
- STM32MP157驱动开发——Linux块设备驱动
STM32MP157驱动开发--Linux块设备驱动 一.简介 二.驱动开发 1.使用请求队列的方式 2.测试① 3.不使用请求队列的方式 4.测试② 参考文章:[正点原子]I.MX6U嵌入式Linu ...
- Linux SPI设备驱动
实现了SPI OLED外设驱动,OLED型号为SH1106. 1.主机驱动与外设驱动分离 Linux中的I2C.SPI.USB等总线驱动,都采用了主机(控制器)驱动与外设(设备)驱动分离的思想.主机端 ...
- linux spi屏驱动程序,65 linux spi设备驱动之spi LCD屏驱动
SPI的控制器驱动由平台设备与平台驱动来实现. 驱动后用spi_master对象来描述.在设备驱动中就可以通过函数spi_write, spi_read, spi_w8r16, spi_w8r8等函数 ...
- Linux spi驱动分析(四)----SPI设备驱动(W25Q32BV)
一.W25Q32BV芯片简介 W25X是一系列SPI接口Flash芯片的简称,它采用SPI接口和CPU通信,本文使用的W25Q32BV容量为32M,具体特性如下: 1.1.基本特性 该芯片最大支持10 ...
- LINUX SPI设备驱动模型分析之二 SPI总线模块分析
上一篇文章我们简要介绍了SPI驱动模块,本章我们详细说明一下spi总线.设备.驱动模块的注册.注销以及这几个模块之间的关联. SPI总线的注册 spi模块也是基于LINUX设备-总线-驱动模型进行开发 ...
- linux probe函数调用,linux spi设备驱动中probe函数何时被调用
这两天被设备文件快搞疯了,也怪自己学东西一知半解吧,弄了几天总算能把设备注册理清楚一点点了.就以spi子设备的注册为例总结一下,免得自己忘记. 首先以注册一个spidev的设备为例: static s ...
- linux usb驱动 probe,linux USB设备驱动之2:usb设备的probe全过程
本文将详细讲述2.6.22下的一个USB设备插上linux系统的PC后是如何一步一步调到我们的usb设备驱动的probe函数的,我们知道我们的USB驱动的probe函数中的一个参数是interface ...
- linux驱动内核,Linux内核设备驱动之Linux内核基础笔记整理
1. Linux内核驱动模块机制 静态加载, 把驱动模块编进内核, 在内核启动时加载 动态加载, 把驱动模块编为ko, 在内核启动后,需要用时加载 2. 编写内核驱动 #include #includ ...
- linux 编译字符设备驱动错误,linux字符设备驱动框架及编写流程
流程: init { } exit { } 申请设备号 (动态注册/静态注册) 创建一个字符设备 cdev_alloc 初始化字符设备 cdev_init 设备号和字符设备关联 cdev_add 销毁 ...
- Linux内核驱动调试,Linux内核设备驱动之内核的调试技术笔记整理
/****************** * 内核的调试技术 ******************/ (1)内核源代码中的一些与调试相关的配置选项 内核的配置选项中包含了一些与内核调试相关的选项,都集中 ...
最新文章
- 街篮最新服务器,《街篮》全新两组服务器开启 与你一起迎新年!
- 职场女人的心理问题提醒
- [译文]Domain Driven Design Reference(三)—— 模型驱动设计的构建模块
- 堆化 二叉堆一般用数组来表示。typedef struct _minHeapNodetypedef struct _otherInfo-icoding-C-数据结构
- 光端机怎样使用?光端机怎么和交换机连接?
- SPI和RAM IP核
- [css] 固定的外框尺寸,里面的图片尺寸不固定,如何让图像自适应外框呢?
- c语言蓝牙接收6,终于搞定了通过两路蓝牙接收数据
- DQL、DML、DDL、DCL的概念与区别
- oracle13001,安装oracle11g 遇到INS-13001环境不知足最低要求 解决方法
- mysql单表大小限制
- Servlet和JSP的区别与MVC模式
- 如何利用FL Studio进行听湿录干的声音录制
- 图论复习(二)-——哈密顿图及其应用
- Linux-DHCP服务器
- max30102c语言程序,STM32驱动MAX30102源码
- 梦幻西游跑商脚本教程
- 【16.8】苹果四代蓝牙耳机+吉萌兔保温杯+小红杯拿铁黑咖啡+公务员教材真题
- 解决Android模拟器不显示问题
- APP热更新方案(转)
热门文章
- dpdk LRO功能总结
- 如何解决CF安全数据上报异常 16-2
- Cmd命令检测电脑配置:
- DVWA-SQL Injection
- pandas——数据透视表
- Android Wi-Fi源码分析之wpa_supplicant初始化(三):wpa_supplicant_add_iface函数分析
- 计算机打印怎么取消,打印机如何取消打印作业?如何删除打印作业?
- android系统无法识别u盘,OTG无法识别U盘怎么办 OTG无法识别解决方法
- 华为无线网卡无服务器,联通华为无线上网卡连接时连接被终止解决方法 - 小众知识...
- 打孔式计算机,矩阵式打孔计算机3005-D型