外设驱动库开发笔记44:DDC114 ADC驱动

  在产品设计过程中,很多时候都会用到ADC器件,而在一些特殊场合还需要一些特别的ADC器件。我们在这篇中将讨论常用于医疗器件方面的,DDC114这款电流输入ADC,并为其设计一个驱动程序。

1、功能概述

  模数转换器DDC114是一款电流输入型ADC,通过对微小电流信号采用电荷积分的方式进行模数转换。包括4路输入,每路输入有2路积分电容;有2路AD转换器,每2路输入共用1个AD转换器。其内部结构框图如下:

  DDC114采用48脚QFN封装,其量程范围、数据格式、转换周期、操作模式等都是通过配置硬件引脚来实现配置的。其封装样式及引脚定义如下:

  DDC114通过SPI接口输出数字化的结果,该SPI接口由一组数据时钟(DCLK),一个有效数据引脚(DVALID),一组串行数据输出引脚(DOUT),和一组串行数据输入引脚(DIN)组成。DDC114电流积分型AD芯片各路交叉积分与转换,积分与转换过程以及数据输出基本是独立进行的。DIN只在多个转换器级联时使用,否则应该绑定到DGND。当移位寄存器包含有效数据时,DVALID输出变低。相应的逻辑时序如下:

  DDC114存在连续模式和非连续模式两种。当积分时间大于数据转换时间时就切换到了连续模式,当积分时间小于数据转换时间时就转换到了非连续模式。有关连续与非连续模式的所需时钟周期如下:

  为了获得最好的噪声抑制特性,一般来说,我们需要将CONV转换信号与CLK时钟的上升沿同步。

2、驱动设计与实现

  我们已经了解了DDC114电流输入型ADC的基本特性,接下来我们就依据这些特性和要求实现DDC114电流输入型ADC的驱动程序。

2.1、对象定义

  与前面一样,我们依然是基于对象的思想来实现DDC114电流输入型ADC的驱动。所以我们首先来考虑DDC114电流输入型ADC作为对象的基本属性和操作。
  首先我们来考虑DDC114对象的属性,我们使用DDC114的目的就是获取各通道的数据,所以我们将当前时刻的数据作为属性记录下来。数据格式虽然是通过硬件管脚来设置的,但在不同的数据格式下解析数据的方式也是不一样的,所以我们将当前配置的数据格式当作属性记录下来。同样的,不同数据格式下量程和零点的设置也是不一样的,所以我你们将其作为属性记录下来,方便完成数据解析。我们知道DDC114连续工作时需要控制转换信号CONV,我们需要记录他每一时刻的状态,以便控制CONV信号的切换,所以我们将器状态也定义为对象的属性。
  而DDC114对象所需要进行的基本操作,主要有控制CONV信号、控制RESET信号、控制TEST信号、获取DVALID状态并从SPI获取,这些操作都依赖于具体的平台,所以我们将其定义为对象的操作,这样可以通过函数指针的方式方便操作。还有基于时序控制的需要,我们需要微秒延时函数,而延时函数也依赖于具体的软硬件平台,所以我们将其定义为对象的操作。综上所述,我们可以定义DDC114的对象类型如下:

/*定义DDC114对象类型*/
typedef struct Ddc114Object {uint32_t dCode[4];Ddc114PinSetType convStatus;Ddc114FormatType format;  //数据输出格式uint32_t codeRange;     //输出量程编码uint32_t codeZero;     //输出零点编码void (*GetDatas)(uint8_t *rData,uint16_t rSize);uint8_t (*GetValid)(void);void (*SetConv)(Ddc114PinSetType conv);void (*SetReset)(Ddc114PinSetType reset);void (*SetTest)(Ddc114PinSetType test);void (*Delayus)(volatile uint32_t nTime);       //实现us延时操作
}Ddc114ObjectType;

  我们定义了DDC114的对象类型,这样我们就可以很容易得到我们想要的DDC114对象变量。但是每个对象都是独一无二的,所以我们需要一个初始化对象变量的过程,所以我们考虑实现一个DDC114对象变量的初始化函数。而需要初始化的就是对象的属性和操作,据此我们定义DDC114对象变量的初始化函数如下:

/*DDC114对象初始化*/
void Ddc114Initialization(Ddc114ObjectType *ddc,    //DDC114对象变量Ddc114FormatType format,  //数据输出格式DDC114GetDatas getDatas,  //获取测量数据函数指针DDC114GetValid getValid,  //数据有效性状态获取函数指针DDC114SetConv conv,       //转换设置函数指针DDC114SetReset reset,     //复位操作函数指针DDC114SetTest test,       //测试模式操作函数指针DDC114Delayus delayus     //微秒延时函数指针)
{if((ddc==NULL)||(getDatas==NULL)||(getValid==NULL)||(conv==NULL)||(reset==NULL)||(test==NULL)||(delayus==NULL)){return ;}ddc->GetDatas=getDatas;ddc->GetValid=getValid;ddc->SetConv=conv;ddc->SetReset=reset;ddc->SetTest=test;ddc->Delayus=delayus;ddc->format=format;if(format==DDC114_OUT20){ddc->codeRange=1048575;ddc->codeZero=4096;}else{ddc->codeRange=65535;ddc->codeZero=256;}for(int i=0;i<4;i++){ddc->dCode[i]=ddc->codeZero;}ddc->SetTest(DDC114_Pin_Reset);ddc->SetReset(DDC114_Pin_Reset);ddc->Delayus(100);ddc->SetReset(DDC114_Pin_Set);ddc->convStatus=DDC114_Pin_Reset;ddc->SetConv(ddc->convStatus);
}

2.2、对象操作

  我们对DDC114要进行哪些操作呢?复位、测试、获取状态等都是我们可以进行的操作,但最重要的还是从DDC114获取转换数据。当DDC114的数据准备好之后,就会将DVALID信号拉低,检测到DVALID信号就可以通过SPI端口获取数据。DDC114通过SPI端口一次性传送4个通道的数据,根据数据格式的不同分别传送80位或者64位。其操作时序如下图所示:

  根据上述的描述及时序图我们可以编写获取DDC114转换数据的函数如下:

/*DDC114获取各通道的转换数据*/
void Ddc114GetDataCode(Ddc114ObjectType *ddc)
{uint8_t rData[10];uint16_t timeOut=0;if(ddc->convStatus==DDC114_Pin_Reset){ddc->convStatus=DDC114_Pin_Set;}else{ddc->convStatus=DDC114_Pin_Reset;}ddc->SetConv(ddc->convStatus);while((ddc->GetValid())&&(timeOut<500)){timeOut++;}if(ddc->format == DDC114_OUT20){ddc->GetDatas(rData,10);}else{ddc->GetDatas(rData,8);}Ddc114ParseDatas(ddc,rData);
}

  我们读到了DDC114的输出数据后,是一个80位或者64位的数据流。我们感兴趣的是各个通道的转换数据,所以我们还需要对数据进行解析。而各通道转换数据的格式如下:

  我们根据设定的数据格式以及读取的数据位,可以解析得到各个通道的转换值。

/*解析从DDC114读取的数据*/
static void Ddc114ParseDatas(Ddc114ObjectType *ddc,uint8_t *rData)
{uint32_t tCode[10]={0};if(ddc->format==DDC114_OUT20){for(int i=0;i<10;i++){tCode[i]=(uint32_t)(rData[i]);}ddc->dCode[0]=((tCode[7]&0x0F)<<16)+(tCode[8]<<8)+tCode[9];ddc->dCode[1]=(tCode[5]<<12)+(tCode[6]<<4)+((tCode[7]&0xF0)>>4);ddc->dCode[2]=((tCode[2]&0x0F)<<16)+(tCode[3]<<8)+tCode[4];ddc->dCode[3]=(tCode[0]<<12)+(tCode[1]<<4)+((tCode[2]&0xF0)>>4);}else{for(int i=0;i<8;i++){tCode[i]=(uint32_t)rData[i];}ddc->dCode[0]=(tCode[6]<<8)+tCode[7];ddc->dCode[1]=(tCode[4]<<8)+tCode[5];ddc->dCode[2]=(tCode[2]<<8)+tCode[3];ddc->dCode[3]=(tCode[0]<<8)+tCode[1];}
}

3、驱动的使用

  我们已经设计并实现了DDC114电流输入型ADC的驱动程序。接下来我们将简单的说明如何使用这一驱动,并设计一个简单的示例验证这一驱动程序的正确性。

3.1、声明并初始化对象

  我们是基于对象设计的DDC114电流输入型ADC的驱动程序,所以在使用驱动时,我们需要先声明一个对象变量,然后基于该对象变量来实现具体的对象操作。我们先声明对象如下:

Ddc114ObjectType ddc;

  声明了这个对象变量之后,我们还需要使用初始化函数对其进行初始化方可使用。这一初始化函数拥有8个参数:

Ddc114ObjectType *ddc,    //DDC114对象变量
Ddc114FormatType format,  //数据输出格式
DDC114GetDatas getDatas,  //获取测量数据函数指针
DDC114GetValid getValid,  //数据有效性状态获取函数指针
DDC114SetConv conv,       //转换设置函数指针
DDC114SetReset reset,     //复位操作函数指针
DDC114SetTest test,       //测试模式操作函数指针
DDC114Delayus delayus     //微秒延时函数指针

  第一个参数为所要初始化的对象变量。第二个参数是数据格式,根据具体的应用设置,我们这里是将它设置为20数据的格式。主要的实现的是后面6个函数指针,它们都是用于实现DDC114在具体的应用平台的操作函数。原型定义如下:

typedef void (*DDC114GetDatas)(uint8_t *rData,uint16_t rSize);
typedef uint8_t (*DDC114GetValid)(void);
typedef void (*DDC114SetConv)(Ddc114PinSetType conv);
typedef void (*DDC114SetReset)(Ddc114PinSetType reset);
typedef void (*DDC114SetTest)(Ddc114PinSetType test);
typedef void (*DDC114Delayus)(volatile uint32_t nTime);       //实现us延时操作

  在使用前我们先来实现这6个面向平台的操作接口函数,具体实现如下:

/* 读取数据 */
static void GetDatasFromDDC(uint8_t *rData,uint16_t rSize)
{HAL_SPI_Receive(&hspi1, rData, rSize, 1);
}/*读取有效引脚*/
static uint8_t ReadValidPin(void)
{return HAL_GPIO_ReadPin(DDC_DVALID_GPIO_Port,DDC_DVALID_Pin);
}/*设置转换引脚*/
static void SetConvPin(Ddc114PinSetType conv)
{if(conv==DDC114_Pin_Reset){HAL_GPIO_WritePin(DDC_CONV_CTL_GPIO_Port, DDC_CONV_CTL_Pin, GPIO_PIN_RESET);return;}HAL_GPIO_WritePin(DDC_CONV_CTL_GPIO_Port, DDC_CONV_CTL_Pin, GPIO_PIN_SET);return;
}/*设置复位引脚*/
static void SetResetPin(Ddc114PinSetType reset)
{if(reset==DDC114_Pin_Reset){HAL_GPIO_WritePin(DDC_RESET_GPIO_Port, DDC_RESET_Pin, GPIO_PIN_RESET);return;}HAL_GPIO_WritePin(DDC_RESET_GPIO_Port, DDC_RESET_Pin, GPIO_PIN_SET);return;
}/*设置测试引脚*/
static void SetTestPin(Ddc114PinSetType test)
{if(test==DDC114_Pin_Reset){HAL_GPIO_WritePin(DDC_TEST_GPIO_Port, DDC_TEST_Pin, GPIO_PIN_RESET);return;}HAL_GPIO_WritePin(DDC_TEST_GPIO_Port, DDC_TEST_Pin, GPIO_PIN_SET);return;
}

  而延时函数,则用我们在STM32平台统一使用的微秒延时函数。定义了这些函数后,我们就可以初始化对象变量如下:

/*DDC114对象初始化*/Ddc114Initialization(&ddc,              //DDC114对象变量DDC114_OUT20,      //数据输出格式GetDatasFromDDC,   //获取测量数据函数指针ReadValidPin,      //数据有效性状态获取函数指针SetConvPin,        //转换设置函数指针SetResetPin,       //复位操作函数指针SetTestPin,        //测试模式操作函数指针Delayus            //微秒延时函数指针);

3.2、基于对象进行操作

  完成了对象的初始化后,我们就可以基于对象来实现相应的操作了。在我们这个应用实例中,我们使用它来采集光度数据值。

/*光度检测处理*/
void DePhotometricMeasurement(void)
{if(aPara.phyPara.waveband>9){luxCode[0].dCode=0;luxCode[1].dCode=0;luxCode[2].dCode=0;luxCode[3].dCode=0;ddcPeriod=0;}else{ddcPeriod++;if(ddcPeriod==1){/*DDC114获取各通道的转换数据*/Ddc114GetDataCode(&ddc);if(dataOrder[aPara.phyPara.waveband]>15){dataOrder[aPara.phyPara.waveband]=0;}for(int i=0;i<4;i++){luxCode[i].fData.waveBand=aPara.phyPara.waveband+1;luxCode[i].fData.serialnumber=dataOrder[aPara.phyPara.waveband];luxCode[i].fData.dataCode=ddc.dCode[i]&0xFFFFF;}dataOrder[aPara.phyPara.waveband]++;ddcPeriod=0;}}aPara.phyPara.dataCode1=luxCode[0].dCode;aPara.phyPara.dataCode2=luxCode[1].dCode;aPara.phyPara.dataCode3=luxCode[2].dCode;aPara.phyPara.dataCode4=luxCode[3].dCode;
}

4、应用总结

  我们设计并实现了DDC114电流输入型ADC的驱动程序。在我们的一个应用实例中,我们使用这一驱动程序采集了光度数据。测试结果如下图:

  其中的红色数据就是我们通过调试工具查看到的DDC114的数据,对应4个通道。上面的为原始数据,通过上位软件查看的结果如下:

  经过上述测试,说明我们设计的DDC114的驱动程序是正确的。在使用时需要注意几点,一是,最好将MCU的SPI接口配置为主机模式而不仅仅是只读模式;二是,SPI的速度最好不要太快,虽然DDC114可以达到10M的频率,但高速时波形会变差;三是,积分时间一定要规划好,否则很容易出现超量程的现象。

欢迎关注:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/499296.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

PID控制器改进笔记之七:改进PID控制器之防超调设定

我们已经设计了PID控制器&#xff0c;并根据实际使用的情况对器进行了诸多的改进。在这一篇中我们将讨论如何改进PID控制器超调的问题。 1、问题提出 在前面的文章中&#xff0c;我们曾推导过增量式PID控制器的公式&#xff0c;并且对其进行了离散化以适用于程序实现&#xff…

软件设计开发笔记3:基于QT的Modbus RTU主站

Modbus是一种常见的工业系统通讯协议。在我们的设计开发工作中经常使用到它。在这一篇中我们将简单实现一个基于QT的Modbus RTU主站上位工具。 1、概述 Modbus RTU主站应用很常见&#xff0c;有一些是通用的&#xff0c;有一些是专用的。而这里我们希望实现一个主要针对我们的…

外设驱动库开发笔记45:MS4515DO压力传感器驱动

很多时候我们需要检测流量和压力这些参数&#xff0c;比如我们要检测大气压&#xff0c;或者通过测量差压来获得输送流体的流量等&#xff0c;都需要用到压力传感器。这一篇我们就来讨论MS4515DO压力传感器的数据获取。 1、功能概述 MS4515DO是TE公司推出的一款基于PCB安装的小…

外设驱动库开发笔记48:MCP4725单通道DAC驱动

在产品设计过程中&#xff0c;我们经常会遇到数模转换的应用需求。在本篇种我们就来讨论一下MCP4725单通道数模转换器的驱动设计与实现。 1、功能概述 MCP4725是一个低功耗&#xff0c;高精度&#xff0c;单通道&#xff0c;12位缓冲电压输出数字到模拟转换器(DAC)与非易失性存…

如何确保不使用动态内存

在许多嵌入式应用程序中&#xff0c;内存分配必须是静态的&#xff0c;而不是动态的。意味着在应用程序中不应使用对malloc()或free()等内容的调用&#xff0c;因为它们可能会在运行时失败&#xff08;内存不足、堆碎片&#xff09;。 但是&#xff0c;当与第三方库甚至 C/C 标…

go 单元测试 testing 打印输出_2020,你需掌握go 单元测试进阶篇

本文说明go语言自带的测试框架未提供或者未方便地提供的测试方案&#xff0c;主要是用于解决写单元测试中比较头痛的依赖问题。也就是伪造模式&#xff0c;经典的伪造模式有桩对象(stub),模拟对象(mock)和伪对象(fake)。比较幸运的是&#xff0c;社区有丰富的第三方测试框架支持…

一文读懂Git工作流

Git是目前最流行的代码管理工具&#xff0c;相信大家也都是在用Git来管理自己团队的源代码。 团队一般为了规范开发&#xff0c;保持良好的代码提交记录以及维护 Git 分支结构清晰&#xff0c;方便后续维护等&#xff0c;都会迫切需要一个比较规范的 Git 工作流。 本文就是在…

xbox360fsd更新游戏封面_游戏类短视频创作指南

一&#xff0e;起步阶段1.内容发布垂直&#xff0c;整体风格一致&#xff0c;选定一个品类的游戏内容风格持续更新注意&#xff1a;冷启动时期不要频繁更换游戏类型2.账号IP化 根据自身风格特色打造独特的风格账号。有利延长账号生命周期&#xff0c;提升粉丝转化率。搞笑、中二…

开发者们都在关注的网站

开发者们都在关注的网站 &#x1f609; 综合类&#xff08;5个&#xff09; 1、GitHub 全球最大的编程开源社区&#xff0c;很多优秀的开源项目都在上边&#xff0c;不知道这个都不要说自己是程序员&#x1f602; 访问地址&#xff1a;https://github.com 2、CSDN 全球最大中…

ios framework 调用第三方 framework_Python基础:标准库和常用的第三方库

Python的标准库有&#xff1a;名称作用datetime为日期和时间处理同时提供了简单和复杂的方法。zlib直接支持通用的数据打包和压缩格式&#xff1a;zlib&#xff0c;gzip&#xff0c;bz2&#xff0c;zipfile&#xff0c;以及 tarfile。random提供了生成随机数的工具。math为浮点…

作图神器ProcessOn - 免费好用

因工作需要&#xff0c;我经常需要花一些流程图&#xff0c;时序图&#xff0c;架构图什么的&#xff0c;之前使用的Windows系统&#xff0c;大部分情况下就用的Visio来画图。后来为了工作方便&#xff0c;换成了Mac电脑&#xff0c;结果发现Mac上没有Visio&#xff0c;然后就在…

三电平igbt死区时间计算_基于大功率三电平IGBT模块并联的参考设计

当前的可再生能源行业中&#xff0c;光伏和风力发电均面临着补贴逐步退坡&#xff0c;平价上网时代即将到来的挑战。为应对这一挑战&#xff0c;光伏逆变器和风力变流器厂家研发的新品单机功率越来越高&#xff0c;以取得更低的单位功率成本。市场上1.5MW的集中式光伏逆变器和3…

手把手教你搭建开发环境之Java开发

大家好呀&#xff0c;从今天开始&#xff0c;我们的手把手系列教程就正式开始啦。 如果你觉得本文对你有一些帮助&#xff0c;欢迎大家关注、点赞、分享给需要的小伙伴们&#xff0c;谢谢大家啦。 前言 Java虽然是一个比较老的语言&#xff0c;但到现在依然充满了活力&#x…

opc服务器组态文件已写保护_远程组态软件不仅方便了PLC无线远程监控,也大大降低了工程成本...

远程组态软件不仅方便了PLC无线远程监控&#xff0c;也大大降低了工程成本组态软件远程监控1.本地上位SCADA系统采集分布各地现场PLC等设备运行的数据&#xff0c;并可以下发控制指令&#xff1b;2.提供稳定的OPC接口服务&#xff0c;常年稳定运行&#xff0c;规模可达10万数据…

奇妙的安全旅行之加密算法概述

前言 hi&#xff0c;大家好呀&#xff0c;信息安全作为当前社会中比较重要的一个课题&#xff0c;已经覆盖了人们生活的方方面面&#xff0c;虽然有时候我们可能并没有意识到&#xff0c;其实信息安全防护已经在背后默默的保护我们的信息安全了。例如&#xff0c;当你在互联网…

怎么调节电机启动值_开关式智能充电机-全自动充电机-铅酸电池充电机品牌-济南能华...

开关式智能充电机-全自动充电机-铅酸电池充电机品牌-济南能华NHCD系列 全自动智能充电机&#xff0c;可调智能充电机&#xff0c;可调直流充电机&#xff0c;可调全自动充电机 &#xff0c;可调蓄电池充电机 便携式可调智能充电机 便携式全自动充电机 大功率可调充电机 大功率智…

奇妙的安全旅行之MD算法

hi&#xff0c;大家好&#xff0c;今天我们开始介绍消息摘要算法中的MD&#xff08;Message Digest&#xff09;算法&#xff0c;MD算法家族包括&#xff1a;MD2&#xff0c;MD4&#xff0c;MD5&#xff0c;MD算法生成的消息摘长度要都是128位的。 其中MD5算法是消息摘要算法的…

的图层类型有哪些_东莞都市领航平面设计培训班都学习哪些内容?

平面设计的工作稳定性是很高的&#xff0c;经济繁荣时期毫无疑问&#xff0c;即使经济下滑&#xff0c;仍不会有很大影响&#xff0c;以前两年为例&#xff0c;北美的大规模裁员浪潮&#xff0c;给高科技行业带来巨大冲击&#xff0c;放慢了高科技产品的开发速度&#xff0c;当…

dockerfile拉取私库镜像_还在用Alpine作为你Docker的Python开发基础镜像?其实Ubuntu更好一点...

原文转载自「刘悦的技术博客」https://v3u.cn/a_id_173一般情况下&#xff0c;当你想为你的Python开发环境选择一个基础镜像时&#xff0c;大多数人都会选择Alpine&#xff0c;为什么&#xff1f;因为它太小了&#xff0c;仅仅只有 5 MB 左右&#xff08;对比 Ubuntu 系列镜像接…

2020,再见;2021,我来了!

现在是2021年1月16日下午16点33分&#xff0c;星期六。此时北京正在通报昨日新冠肺炎新增病例情况&#xff0c;这种每天戴口罩的鬼日子还不知道什么时候能结束。最近由于天气变冷&#xff0c;病毒更容易存活和传播&#xff0c;最近一个月就突然又变的非常紧张起来了&#xff0c…