1.简介

上几周硬件找了一个ADC芯片的替代料,需要我这边写一个驱动做测试。以前都是配置与修改设备树,最多查看或小改一下驱动,没有写过驱动,所有记录一下思路。大致思路就是先看原理图,再看芯片手册,知道要写驱动的芯片张怎么样,怎么工作的。

2.查看原理图

3.查看芯片手册

3.1 引脚定义

参考输入等相关引脚

ADC通道相关引脚

SPI相关引脚

GPIO相关功能引脚

电源等相关引脚

3.2 内部架构与相关原理


3.3 芯片寄存器的操作(重点)

我们操作一个设备一般就是操作其设备相关的寄存器

3.3.1 手动模式



SPI一次传输16bit的数据,如果设置成手动模式,相关设置如下

0001 (选择手动模式) 1(开启低字段编码) 0001(选择的通道) 1(开启2Vref量程) 0(关闭休眠模式) 0(关闭GPIO功能) 0000(GPIO0)

则驱动用SPI往此ADC芯片传0001 1000 1100 0000,就设置相关寄存器工作

选择的通道数据,需要延迟3个数据

3.3.2 Auto1模式



设置通道寄存器

1000 0000 0000 0000 进入auto1 program sequence,下一帧编程通道
1010 1010 0101 0101 扫描 0 2 4 6 9 11 13 15 通道

3.3.3 Auto2模式



设置通道寄存器

3.3.4选择模式下继续操作


还有GPIO和告警的设置,但因为项目没有用到,驱动没有去编写,所有不做分析

4.开始编写驱动

#include <linux/module.h>
#include <linux/err.h>
#include <linux/platform_device.h>
#include <linux/types.h>
#include <linux/string.h>
#include <linux/spi/spi.h>
#include <linux/gpio.h>
#include <linux/device.h>
#include <linux/mutex.h>
#include <linux/delay.h>
#include <linux/kthread.h>#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/of_gpio.h>
#include <linux/of_platform.h>
#include <linux/fs.h>
#include <linux/input.h>#define SPIADC_DEBUG (1)
#ifdef SPIADC_DEBUG#define spiadc_debug(fmt...) printk(KERN_INFO "spiadc: "fmt)
#else#define spiadc_debug(fmt...)   do{}while(0)
#endif#define FSU_SPI_DRIVER_VERSION          ("1.01.001")/** * 复位所有寄存器*/
#define TPC5120_RESET_CHIP          (uint16_t)0x4200/** * 模式控制寄存器*/
#define TPC5120_MANNUAL_MODE_REG    (uint16_t)0x0001
#define TPC5120_AUTO1_MODE_REG      (uint16_t)0x0002
#define TPC5120_AUTO2_MODE_REG      (uint16_t)0x0003/** * 自动模式程序寄存器*/
#define TPC5120_AUTO1_PROG_REG      (uint16_t)0x0008
#define TPC5120_AUTO2_PROG_REG      (uint16_t)0x0009/** *  MANNUAL模式寄存器*/
#define ManuLSBEN                   ((uint16_t)0x0800)
#define ManuCHnl                    ((uint16_t)0x0080)
#define ManuRangSel                 ((uint16_t)0x0040)
#define ManuPD                      ((uint16_t)0x0020)
#define ManuGPIOEn                  ((uint16_t)0x0010)
#define ManuGPIOData                ((uint16_t)0x0001)/** * AUTO1模式寄存器 */
#define Auto1LSBEN                  ((uint16_t)0x0800)
#define Auto1CHnlRst                ((uint16_t)0x0400)
#define Auto1RangSel                ((uint16_t)0x0040)
#define Auto1PD                     ((uint16_t)0x0020)
#define Auto1GPIOEn                 ((uint16_t)0x0010)
#define Auto1GPIOData               ((uint16_t)0x0001)/** * AUTO2模式寄存器 */
#define Auto2LSBEN                  ((uint16_t)0x0800)
#define Auto2CHnlRst                ((uint16_t)0x0400)
#define Auto2RangSel                ((uint16_t)0x0040)
#define Auto2PD                     ((uint16_t)0x0020)
#define Auto2GPIOEn                 ((uint16_t)0x0010)
#define Auto2GPIOData               ((uint16_t)0x0001)typedef enum
{Manual,Auto1,Auto2,
}TPC5120_mode_type;typedef enum
{lsd_code_disable,lsd_code_enabled,
}TPC5120_lsd_code;typedef enum
{power_down_disable,power_down_enabled,
}TPC5120_power_down;typedef enum
{chan_no_reset,chan_reset,
}TPC5120_chan_type;typedef enum
{range_v,range_2v,
}TPC5120_range_type;typedef enum
{gpio_disable,gpio_enabled,
}TPC5120_GPIO_type;typedef enum
{gpio_0 = 0x0,gpio_1 = 0x2,gpio_2 = 0x4,gpio_3 = 0x8,
}TPC5120_GPIO_number;typedef struct
{u8 value_valid;//数据更新标志u8 chan;u16 reg_value; //寄存器读取的数值u32 adc_value; //转换处理出来的数值}fsu_spi_adc;typedef struct
{u8 adc_valid;      //adc修改标志u8 adc_mode;       //adc工作模式u16 adc_ch_scan;   //adc扫描通道u32 task_sleep_time;//线程休眠时间int vref;int max;struct device *dev;struct spi_device *spi;struct kobject *kobj_spi_adcs;struct task_struct *spi_adc_task;//线程fsu_spi_adc f_spi_adc_data[16]; //各通道数据
}fsu_spi_dev;/*
* 函数名: show_fsu_spi_data
* 功能:显示adc数据
*/
static ssize_t show_fsu_spi_data(struct device *dev, struct device_attribute *attr, char *buf)
{int i,len = 0;fsu_spi_dev *fsd = dev_get_drvdata(dev);if (NULL == fsd){printk(KERN_ERR ": fsu spi device empty!!!\n");return 0;}len = sprintf(buf, "ChNo        RegisterValue        Value        Status \n");for(i = 0; i < 16; i++){len += sprintf(buf + len, "%-16d    %-8d     %-4d          %-8s\n", \fsd->f_spi_adc_data[i].chan, fsd->f_spi_adc_data[i].reg_value, \fsd->f_spi_adc_data[i].adc_value, fsd->f_spi_adc_data[i].value_valid ? "normal" : "fault");}return len;
}/*
* 函数名: show_spi_driver_ver
* 功能:显示adc驱动版本
*/
static ssize_t show_spi_driver_ver(struct device *dev, struct device_attribute *attr, char *buf)
{fsu_spi_dev *fsd = dev_get_drvdata(dev);if (NULL == fsd){printk(KERN_ERR ": fsu spi device empty!!!\n");return 0;}return ( sprintf(buf, "%s\n", FSU_SPI_DRIVER_VERSION) );
}/*
* 函数名: show_spi_adc_chan
* 功能:显示adc 通道号
*/
static ssize_t show_spi_adc_chan(struct device *dev, struct device_attribute *attr, char *buf)
{fsu_spi_dev *fsd = dev_get_drvdata(dev);if (NULL == fsd){printk(KERN_ERR ": fsu spi device empty!!!\n");return 0;}spiadc_debug("spi adc chan:%x\n", fsd->adc_ch_scan);return (sprintf(buf, "spi adc scan channl = %x \n", fsd->adc_ch_scan));
}/*
* 函数名: set_spi_adc_chan
* 功能:设置adc扫描通道
*/
static ssize_t set_spi_adc_chan(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{u32 u32Data;fsu_spi_dev *fsd = dev_get_drvdata(dev);if (NULL == fsd){printk(KERN_ERR ": fsu spi device empty!!!\n");return 0;}spiadc_debug("set buf:%s\n", buf);if (kstrtou32(buf, 10, &u32Data)){printk(KERN_ERR "spi adc scan channl para error!\n");return -EINVAL;}spiadc_debug("set Data:%d\n", u32Data);if(u32Data >= 0){fsd->adc_ch_scan = u32Data & 0x0000ffff;fsd->adc_valid = 1;}else{spiadc_debug("spi adc scan channl input error!\n");fsd->adc_valid = 0;}return count;
}/*
* 函数名: show_spi_adc_mode
* 功能:显示adc工作模式
*/
static ssize_t show_spi_adc_mode(struct device *dev, struct device_attribute *attr, char *buf)
{fsu_spi_dev *fsd = dev_get_drvdata(dev);if (NULL == fsd){printk(KERN_ERR ": fsu spi device empty!!!\n");return 0;}spiadc_debug("spi sdc mode:%x\n", fsd->adc_mode);return (sprintf(buf, "Manual = 0; Auto1 = 1; Auto2 = 2;\nThe current mode = %d\n", fsd->adc_mode));
}/*
* 函数名: set_spi_adc_mode
* 功能:设置adc工作模式
*/
static ssize_t set_spi_adc_mode(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{u32 u32Data;fsu_spi_dev *fsd = dev_get_drvdata(dev);if (NULL == fsd){printk(KERN_ERR ": fsu spi device empty!!!\n");return 0;}spiadc_debug("set buf:%s\n", buf);if (kstrtou32(buf, 10, &u32Data)){printk(KERN_ERR "spi adc mode para error!\n");return -EINVAL;}spiadc_debug("set Data:%d\n", u32Data);if(u32Data < 3){fsd->adc_mode = u32Data;fsd->adc_valid = 1;}else{spiadc_debug("spi adc mode input error!\n");fsd->adc_valid = 0;}return count;
}/*
* 函数名: show_spi_adc_sleep_time
* 功能:显示adc数据更新读取时间
*/
static ssize_t show_spi_adc_sleep_time(struct device *dev, struct device_attribute *attr, char *buf)
{   fsu_spi_dev *fsd = dev_get_drvdata(dev);if (NULL == fsd){printk(KERN_ERR ": fsu spi device empty!!!\n");return 0;}spiadc_debug("task_sleep_time:%d\n", fsd->task_sleep_time);return (sprintf(buf, "%d\n", fsd->task_sleep_time));
}/*
* 函数名: set_spi_adc_sleep_time
* 功能:设置adc数据更新读取时间
*/
static ssize_t set_spi_adc_sleep_time(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{u32 u32Data;fsu_spi_dev *fsd = dev_get_drvdata(dev);if (NULL == fsd){printk(KERN_ERR ": fsu spi device empty!!!\n");return 0;}spiadc_debug("set buf:%s\n", buf);if (kstrtou32(buf, 10, &u32Data)){printk(KERN_ERR "spi adc time para error!\n");return -EINVAL;}if(u32Data > 0){spiadc_debug("set Data:%d\n", u32Data);fsd->task_sleep_time = u32Data;fsd->adc_valid = 1;}else{spiadc_debug("spi adc time input error!\n");fsd->adc_valid = 0;}return count;
}static DEVICE_ATTR(fsu_spi_adc_data, S_IRUGO, show_fsu_spi_data, NULL);
static DEVICE_ATTR(fsu_spi_adc_ver, S_IRUGO, show_spi_driver_ver, NULL);
static DEVICE_ATTR(fsu_spi_adc_mode, S_IWUSR | S_IRUGO, show_spi_adc_mode, set_spi_adc_mode);
static DEVICE_ATTR(fsu_spi_adc_chan, S_IWUSR | S_IRUGO, show_spi_adc_chan, set_spi_adc_chan);
static DEVICE_ATTR(fsu_spi_adc_sleep_time, S_IWUSR | S_IRUGO, show_spi_adc_sleep_time, set_spi_adc_sleep_time);static struct attribute *fsu_spiadc_attrs[] = {&dev_attr_fsu_spi_adc_data.attr,&dev_attr_fsu_spi_adc_ver.attr,&dev_attr_fsu_spi_adc_mode.attr,&dev_attr_fsu_spi_adc_chan.attr,&dev_attr_fsu_spi_adc_sleep_time.attr,NULL
};static struct attribute_group fsu_spiadc_group = {.attrs = fsu_spiadc_attrs
};static int fsu_add_all_spi_data_node(struct device *dev)
{int ret;ret = sysfs_create_group(&dev->kobj, &fsu_spiadc_group);if (ret){dev_warn(dev, "Create fsu all spi data sysfs node 'fsu_spi_data' failed, ret=%d\n", ret);}return 0;
}/*
* 函数名: spi_write_read
* 功能:写入TPC5120寄存器设置指令,并读取数据
*/
static u16 spi_write_read(fsu_spi_dev *adc, u16 cmd)
{u8 cmd_buf[2];u8 data_buf[2];u16 data = 0;cmd_buf[1] = cmd & 0x00FF;cmd_buf[0] = cmd >> 8;spi_write(adc->spi,cmd_buf,2);spi_read(adc->spi,data_buf,2);data = data_buf[0] <<8 |data_buf[1];spiadc_debug("==== spi_write_read cmd:%x%x data_buf:%x%x data:%x ====\n",cmd_buf[0],cmd_buf[1],data_buf[0],data_buf[1],data);return data;
}/*
* 函数名: set_auto1_prog_cfg
* 参数:  chscan 扫描的通道号
* 功能:设置TPC521程序寄存器 设置指定读取的通道数据
* 例: chscan = 0xaa55 扫描 0 2 4 6 9 11 13 15通道
*/
static void set_auto1_prog_cfg(fsu_spi_dev *adc, u16 chscan)
{   u16 adc_cfg;adc_cfg=TPC5120_AUTO1_PROG_REG<<12;spi_write_read(adc, adc_cfg);     // enters auto1 program sequencespi_write_read(adc, chscan);        // select channel to scan
}/*
* 函数名: set_auto2_prog_cfg
* 参数: chscan 扫描的通道号
* 功能:设置TPC521程序寄存器 设置自动扫描通道的范围
* 例: chscan = 0x5 扫描 0~5通道
*/
static void set_auto2_prog_cfg(fsu_spi_dev *adc, u16 chscan)
{u16 adc_cfg;adc_cfg=(TPC5120_AUTO2_PROG_REG<<12)|((chscan&0x000f)<<6);spi_write_read(adc, adc_cfg);
}/*
* 函数名: set_manual_mode_cfg
* 参数: lsbEn 是否开启第di10~0的编码chan 通道号range 范围选择 0 0~Vref 1 0~2Vrefpd 休眠模式ioEn GPIO功能iodata 设置GPIO_x为输出模式
* 功能:设置TPC521模式寄存器 ADC为手动模式
*/
static void set_manual_mode_cfg(fsu_spi_dev *adc, u8 lsbEn, u16 chan, u8 range, u8 pd, u8 ioEn, u8 iodata)
{u16 adc_cfg;adc_cfg=(TPC5120_MANNUAL_MODE_REG<<12);if(lsbEn) adc_cfg|=ManuLSBEN;adc_cfg|=(chan&0x000f)*ManuCHnl;if(range) adc_cfg|=ManuRangSel;if(pd)    adc_cfg|=ManuPD;if(ioEn)  adc_cfg|=ManuGPIOEn;adc_cfg|=iodata*ManuGPIOData;spi_write_read(adc, adc_cfg);   // config mannual mode reg
}/*
* 函数名: set_auto1_mode_cfg
* 参数: lsbEn 是否开启第di10~0的编码chanRst  通道是否复位range 范围选择 0 0~Vref 1 0~2Vrefpd 休眠模式ioEn GPIO功能iodata 设置GPIO_x为输出模式
* 功能:设置TPC521模式寄存器 ADC为自动1模式
*/
static void set_auto1_mode_cfg(fsu_spi_dev *adc, u8 lsbEn, u8 chanRst, u8 range, u8 pd, u8 ioEn, u8 iodata)
{u16 adc_cfg;adc_cfg=(TPC5120_AUTO1_MODE_REG <<12);if(lsbEn)   adc_cfg|=Auto1LSBEN;if(chanRst) adc_cfg|=Auto1CHnlRst;if(range)   adc_cfg|=Auto1RangSel;if(pd)      adc_cfg|=Auto1PD;if(ioEn)    adc_cfg|=Auto1GPIOEn;adc_cfg|=iodata*Auto1GPIOData;spi_write_read(adc, adc_cfg);   // config auto1 mode reg
}/*
* 函数名: set_auto2_mode
* 参数: lsbEn 是否开启第di10~0的编码chanRst  通道是否复位range 范围选择 0 0~Vref 1 0~2Vrefpd 休眠模式ioEn GPIO功能iodata 设置GPIO_x为输出模式
* 功能:设置TPC521模式寄存器 ADC为自动2模式
*/
static void set_auto2_mode_cfg(fsu_spi_dev *adc, u8 lsbEn, u8 chanRst, u8 range, u8 pd, u8 ioEn, u8 iodata)
{u16 adc_cfg;adc_cfg=TPC5120_AUTO2_MODE_REG<<12;if(lsbEn)   adc_cfg|=Auto2LSBEN;if(chanRst) adc_cfg|=Auto2CHnlRst;if(range)   adc_cfg|=Auto2RangSel;if(pd)      adc_cfg|=Auto2PD;if(ioEn)    adc_cfg|=Auto2GPIOEn;  adc_cfg|=iodata*Auto2GPIOData;spi_write_read(adc, adc_cfg);     // config auto2 mode reg
}/*
* 函数名: parse_adc_data
* 功能:解析spi读取到的数据,并更新
*/
static int parse_adc_data(fsu_spi_dev *adc, u16 spi_data)
{u16 adc_buf = spi_data;u8 adc_chan = adc_buf >> 12;u16 reg_value = adc_buf & 0x0fff;//换算数据u32  adc_value = 0;adc_value = (reg_value * adc->vref)/adc->max;int i;spiadc_debug("==== parse_adc_data chan:%x reg_value:%x adc_value:%d ====\n",adc_chan,reg_value,adc_value);for(i = 0; i < 16; i++){if ( adc_chan == adc->f_spi_adc_data[i].chan){if(reg_value != adc->f_spi_adc_data[i].reg_value){adc->f_spi_adc_data[i].reg_value = reg_value;adc->f_spi_adc_data[i].adc_value = adc_value;//数据更新adc->f_spi_adc_data[i].value_valid = 1;}else{//数据没有更新adc->f_spi_adc_data[i].value_valid = 0;}}else{//数据没有更新adc->f_spi_adc_data[i].value_valid = 0;}}
}//驱动运行线程
static int fsu_spi_adc_sample(void *arg)
{spiadc_debug("==== fsu_spi_adc_sample start ====\n");fsu_spi_dev *fspiadc = NULL;u16 spi_data;int i;fspiadc = (fsu_spi_dev *)arg;while(1){msleep(fspiadc->task_sleep_time);if(kthread_should_stop()) {  break;}if(1 == fspiadc->adc_valid){fspiadc->adc_valid = 0;//复位所有寄存器spi_write_read(fspiadc,TPC5120_RESET_CHIP);//配置寄存器switch(fspiadc->adc_mode){case Manual://设置模式寄存器set_manual_mode_cfg(fspiadc,lsd_code_enabled,fspiadc->adc_ch_scan,range_2v,power_down_disable,gpio_disable,gpio_0);break;case Auto1://设置模式寄存器set_auto1_mode_cfg(fspiadc,lsd_code_enabled,chan_reset,range_2v,power_down_disable,gpio_disable,gpio_0);//设置通道寄存器set_auto1_prog_cfg(fspiadc,fspiadc->adc_ch_scan);break;case Auto2://设置模式寄存器set_auto2_mode_cfg(fspiadc,lsd_code_enabled,chan_reset,range_2v,power_down_disable,gpio_disable,gpio_0);//设置通道寄存器set_auto2_prog_cfg(fspiadc,fspiadc->adc_ch_scan);break;}}for(i = 0; i < 16; i++){spi_data = spi_write_read(fspiadc,0);//解析数据parse_adc_data(fspiadc,spi_data);}}spiadc_debug("==== fsu_spi_adc_sample end ====\n");return 0;
}static int fsu_spi_adc_init(fsu_spi_dev *spi_dev)
{spiadc_debug("==== fsu_spi_adc_init start ====\n");if (NULL == spi_dev){return -1;}fsu_spi_dev *fspiadc = spi_dev;int i;fspiadc->adc_valid = 1;fspiadc->adc_mode = 2;fspiadc->adc_ch_scan = 0xb;fspiadc->task_sleep_time = 5000;for( i = 0;i < 16; i++){fspiadc->f_spi_adc_data[i].value_valid = 0;fspiadc->f_spi_adc_data[i].chan = i;fspiadc->f_spi_adc_data[i].reg_value = 0;fspiadc->f_spi_adc_data[i].adc_value = 0;}spiadc_debug("==== fsu_spi_adc_init end ====\n");return 0;
}static int fsu_spi_adc_parse_dt(fsu_spi_dev *spiadc)
{struct device_node *node = spiadc->dev->of_node;if (of_property_read_u32(node, "vref", &spiadc->vref)){dev_err(spiadc->dev, "Could not get 'vref'!!");return -1;}if (spiadc->vref <= 0){spiadc->vref = 5000;dev_warn(spiadc->dev, "Invalid 'vref', using default %d\n", spiadc->vref);}if (of_property_read_u32(node, "max", &spiadc->max)){dev_err(spiadc->dev, "Could not get 'max'!!");return -1;}if (spiadc->max <= 0){spiadc->max = 4096;dev_warn(spiadc->dev, "Invalid 'max', using default %d\n", spiadc->max);      }spiadc_debug("spi adc vref:%d, max:%d\n",spiadc->vref, spiadc->max);return 0;
}static int fsu_spi_adc_probe(struct spi_device *spi)
{spiadc_debug("==== fsu_spi_adc_probe start ====\n");int ret;fsu_spi_dev *spiadc;/*整个内核空间的调用链上只有4KB或8KB的栈,相对于应用程序来说是非常小,如果需要大内存的空间,需要使用专门的函数进行动态分配kmalloc*//* allocate device handle */spiadc = devm_kzalloc(&spi->dev, sizeof(*spiadc), GFP_KERNEL);if (NULL == spiadc){return -ENOMEM;}/* init adc */spiadc->dev = &spi->dev;spiadc->spi = spi;spiadc_debug("==== spi dev chip_select:%d, max_speed_hz:%d, mode:%d ====\n", spi->chip_select, spi->max_speed_hz, spi->mode);/* setup spi */spi->bits_per_word = 8;ret = spi_setup(spi);if (ret < 0){dev_err(&spi->dev, "Setup spi failed, ret = %d\n", ret);devm_kfree(&spi->dev, spiadc);return ret;}/* set drv data */dev_set_drvdata(&spi->dev, spiadc);/* parse dt */ //解析设备树fsu_spi_adc_parse_dt(spiadc);//初始化默认配置fsu_spi_adc_init(spiadc);//生成设备节点fsu_add_all_spi_data_node(spiadc->dev);//开启线程spiadc->spi_adc_task = kthread_create(fsu_spi_adc_sample, spiadc, "spi_adc_task");if(IS_ERR(spiadc->spi_adc_task)){spiadc_debug("creat spi_adc_task erro \n");spiadc->spi_adc_task = NULL;}else{//等待线程wake_up_process(spiadc->spi_adc_task);}spiadc_debug("==== fsu_spi_adc_probe end ====\n");return 0;
}static int fsu_spi_adc_remove(struct spi_device *spi)
{fsu_spi_dev *fsd = dev_get_drvdata(&spi->dev);if(fsd->spi_adc_task) {kthread_stop(fsd->spi_adc_task);fsd->spi_adc_task = NULL;}sysfs_remove_group(&spi->dev.kobj, &fsu_spiadc_group);devm_kfree(&spi->dev, fsd);   return 0;
}static const struct of_device_id fsu_spiadc_match_ids[] = {{ .compatible = "znv,fsu-spiadc", .data = NULL},{},
};MODULE_DEVICE_TABLE(of, fsu_spiadc_match_ids);static struct spi_driver fsu_spiadc_driver = {.probe       = fsu_spi_adc_probe,.remove        = fsu_spi_adc_remove,.driver       = {.name       = FSU_SPI_ADC_NAME,.owner      = THIS_MODULE,.of_match_table = fsu_spiadc_match_ids,},
};//驱动入口函数
static int __init fsu_spiadc_init(void)
{spi_register_driver(&fsu_spiadc_driver);return 0;
}
//驱动卸载函数
static void __exit fsu_spiadc_exit(void)
{spi_unregister_driver(&fsu_spiadc_driver);
}//驱动程序的入口
module_init(fsu_spiadc_init); //运行insmod指令的时候,就会调用
//驱动程序的出口
module_exit(fsu_spiadc_exit); //运行rmmod指令的时候,会被调用//驱动的描述
MODULE_AUTHOR("kamin");       //作者
MODULE_DESCRIPTION("ADC Driver Mode");    //模块功能说明
MODULE_LICENSE("GPL");    //许可证:驱动遵循GPL协议

驱动编程简单教程——PTC512(ADC芯片驱动)为例相关推荐

  1. Windows驱动编程基础教程

    前言     本书非常适合熟悉Windows应用编程的读者转向驱动开发.所有的内容都从最基础的编程方法入手.介绍相关的内核API,然后举出示范的例子.这本书只有不到70页,是一本非常精简的小册子.所以 ...

  2. Windows驱动编程基础教程 (转)

     Windows驱动编程基础教程(转) 我经常在网上遇到心如火燎的提问者.他们碰到很多工作中的技术问题,是关于驱动开发的.其实绝大部分他们碰到的"巨大困难"是被老牛们看成初级得 ...

  3. 【梅哥的Ring0湿润插入教程】第一课Windows内核/驱动编程概述及应用、商业驱动保护软件原理分析...

    [梅哥的Ring0湿润插入教程] Email:mlkui@163.com 转载请注明出处,谢绝喷子记者等,如引起各类不适请自觉滚J8蛋! 第一课Windows内核/驱动编程概述及应用. 商业驱动保护软 ...

  4. 楚狂人Windows驱动编程基础教程

    版权声明     本书是免费电子书.作者保留一切权利.但在保证本书完整性(包括版权声明.前言.正文内容.后记.以及作者的信息),并不增删.改变其中任何文字内容的前提下,欢迎任何读者以任何形式(包括各种 ...

  5. 不要驱动,简单粗暴的用树莓派驱动USB打印机

    不要驱动,简单粗暴的用树莓派驱动USB打印机 admin 2015年4月14日   5 Comments 网上很多文章都是再说如何用树莓派来做一个通用打印服务器,但是在很多应用场景下,配置CUPS什么 ...

  6. Linux驱动开发-编写PCF8591(ADC)芯片驱动

    1. PCF8591介绍 PCF8591是一个IIC总线接口的ADC/DAC转换芯片,功能比较强大,这篇文章就介绍在Linux系统里如何编写一个PCF8591的驱动,完成ADC数据采集,DAC数据输出 ...

  7. ADS1220 24位高精度ADC芯片驱动

    芯片引脚及功能描述: 驱动代码: 头文件:ads1220.h #ifndef __ADS1220_H #define __ADS1220_H#include "em_device.h&quo ...

  8. Linux驱动编程篇(三)——LED驱动(一)简单LED驱动

    一.LED驱动程序的实现目标及流程图 1.打开LED 2.关闭LED 二.LED驱动程序的实现部分 1.内核层LED驱动程序 2.应用层LED测试程序 三.内核层LED驱动程序的编程步骤 1.添加头文 ...

  9. linux 内核驱动编程 简单例子 与_IO, _IOR, _IOW, _IOWR 宏解析

    一._IO, _IOR, _IOW, _IOWR 宏的用法与解析 在驱动程序里, ioctl() 函数上传送的变量 cmd 是应用程序用于区别设备驱动程序请求处理内容的值.cmd除了可区别数字外,还包 ...

最新文章

  1. ASP.NET 的数据绑定,DataList,Repeater 的绑定示例
  2. python和javascript详细对比_python与javascript 引入模块的方法对比
  3. 【每周CV论文推荐】换脸算法都有哪些经典的思路?
  4. HDU2553 N皇后 回溯法+打表
  5. 码农不重视文档:开源项目深受其苦
  6. 【Bringing Old Photos Back to Life】How to train?如何训练
  7. 华为p9 android版本,华为P9的手机系统是什么
  8. 在linux下磁盘挂在操作,linux下挂载磁盘操作
  9. Ubuntu关闭防火墙
  10. ThinkPHP文件上传类的使用
  11. CSV 文件中的字段中的开头和结尾上,可能会存在空格或制表符,但是该如何处理呢?
  12. PCB----LayOut的一些准则
  13. 新型软件生命周期模型-RUP统一过程模型 迭代增量
  14. win10电脑安装android,Android 10安装到PC电脑上运行 PC端安装Android 10
  15. GameFramework框架 (一) 框架简介
  16. Excel 筛选唯一值或删除重复值
  17. 歪写数学史(数学界的花木兰——苏菲﹒热尔曼)
  18. ChatGPT使用案例之自然语言处理
  19. 哈夫曼树和哈夫曼树编码
  20. python爬取歌曲的全部评论

热门文章

  1. 艺赛旗(RPA)国家企业信用信息公示系统验证码破解(一)
  2. 写latex 遇到bib中参考文献 的俄文人名(类似于带有声调的拼音字母)如何转义?
  3. html文章标题列表,内容页(文章及产品页)标题该怎么写
  4. python交易是什么意思_py交易是什么意思?
  5. 青龙脚本-趣闲赚(更新)
  6. 齐齐哈尔鹤城计算机学校,齐齐哈尔阳光学校
  7. LabVIEW编程LabVIEW开发Fluke8858A/8588A万用表 例程与相关资料
  8. js写的 几款时间轴
  9. 阿里自研UED计件平台技术解析
  10. 背包型动态规划——零钱兑换