在产品设计过程中,我们经常会遇到数模转换的应用需求。在本篇种我们就来讨论一下MCP4725单通道数模转换器的驱动设计与实现。

1、功能概述

  MCP4725是一个低功耗,高精度,单通道,12位缓冲电压输出数字到模拟转换器(DAC)与非易失性存储器(EEPROM)。它的板载精度输出放大器允许它实现轨到轨模拟输出摆动。
  DAC输入和配置数据可以被编程到非易失性存储器(EEPROM)由用户使用I2C接口命令。非易失性存储器特性使DAC设备能够在断电时保存DAC输入代码,并且在通电后立即提供DAC输出。当DAC设备被用作网络中其他设备的支持设备时,这个特性非常有用。MCP4725的引脚定义及排布如下:

  MCP4725有一个外部A0地址位选择引脚。这个A0引脚可以绑定到用户应用PCB板的VDD或VSS上。这个引脚被用户用来选择A0地址位。用户可以将这个引脚绑定到VSS(逻辑’ 0 ‘),或VDD(逻辑’ 1 '),或可以由数字逻辑级别主动驱动,如I2C主输出。
  MCP4725的地址字节由两个部分组成,第一部分为4位设备代码,固定设置为1100的,设备代码后面是三位为地址位(A2, A1, A0),如下图所示:

  A2和A1位的选择可由客户提供,作为订购过程的一部分。两位在出厂前设定好,如果客户没有特别要求的话,A2和A1会默认编程为“00”。而A0位则由A0引脚的逻辑状态决定。
  MCP4725设备包括一个上电复位(POR)电路,以确保可靠的上电,以及一个用于EEPROM编程电压的板载电荷泵。DAC引用是直接从VDD驱动的。在down模式下,输出放大器可以配置为已知的低、中或高阻输出负载,如下图。

  MCP472的写命令用于将配置位和DAC输入码加载到DAC寄存器,或写入设备的EEPROM。写命令类型由三个写命令类型位(C2、C1、C0)定义。写命令类型及其作用如下表所示。

2、驱动设计与实现

  在前一节中,我们梳理了MCP4725单通道数模转换器的基本技术参数。在这一节中,我们将依据这些技术参数来设计MCP4725单通道数模转换器的驱动程序。我们依然是基于对象的思想来实现之。

2.1、对象定义

  我们基于对象来实现驱动程序,所以我们就需要先得到对象,在这里我们首先将抽象出MCP4725单通道数模转换器的对象类型。一版来说,对象皆包含属性与操作两个方面的内容。在抽象对象类型的过程中,我们需要分析MCP4725单通道数模转换器都有哪些属性和操作。
  我们先来分析MCP4725单通道数模转换器的对象的属性。每一台I2C从设备都有一个设备地址,这个地址实际上标识了总线上设备的身份,MCP4725亦如此,所以我们将设备地址作为对象的一个属性。对于MCP4725单通道数模转换器,有一个掉电处理模式是需要配置的,为了掌握其配置状态我们将其作为对象的一个属性记录下来。
  接下来分析MCP4725单通道数模转换器的对象的操作。MCP4725单通道数模转换器的基本操作无非就是读写数据,而要实现读和写则依赖于具体的软硬件平台,所以我们将读和写MCP4725单通道数模转换器都作为对象的操作来实现。
  根据上述关于MCP4725单通道数模转换器对象属性和操作的分析,我们可以抽象得到其对象类型如下:

/*定义MCP4725对象类型*/
typedef struct Mcp4725Object {uint8_t devAddress;Mcp4725PDModeType pdMode;void (*Write)(struct Mcp4725Object *mcp,uint8_t *wData, uint16_t wSize);void (*Read)(struct Mcp4725Object *mcp,uint8_t *rData, uint16_t rSize);
}Mcp4725ObjectType;

  抽象了对象类型后就可声明对象变量,可是这个对象变量必须作必要的初始化才能使用。所以我们需要一个初始化函数来对其进行初始化。在此函数中,我们将检测变量的有效性和初始状态赋值,并对设备进行必要的配置。根据这些要求我们设计MCP4725单通道数模转换器的对象初始化函数如下:

/*MCP4725初始化配置*/
void Mcp4725Initialization(Mcp4725ObjectType *mcp,  //MCP4725对象变量uint8_t slaveAddress,    //从站设备的地址Mcp4725PDModeType pdMode,//掉电操作模式Mcp4725Write write,      //写数据函数指针Mcp4725Read read         //读数据函数指针)
{if((mcp==NULL)||(write==NULL)||(read==NULL)){return;}mcp->Write=write;mcp->Read=read;if((slaveAddress==0x60)||(slaveAddress==0x61)){mcp->devAddress=(slaveAddress<<1);}else if((slaveAddress==0xC0)||(slaveAddress==0xC2)){mcp->devAddress=slaveAddress;}else{mcp->devAddress=0x00;}mcp->pdMode=pdMode;}

2.2、对象操作

  有了对象变量,也完成了初始化,那么我们就可以用其来操作MCP4725单通道数模转换器了。所以我们来看看实现对MCP4725单通道数模转换器的读写操作。
首先我们来看看写MCP4725单通道数模转换器的实现。写MCP4725单通道数模转换器有两种模式:快速模式和正常模式。快速模式就是将命令与数据结合在一起,这要只需发送三个字节就可完成写数据的过程。具体的操作时序如下:

  而正常模式则是命令是单独的字节,数据是另外的2个字节,所以正常模式一次发送4个字节才能完成写的过程。正常模式可以操作寄存器也可操作EEPROM,这一点与快速模式是不一样的。具体的操作时序如下:

  根据前面的描述和时序图,我们可以设计写MCP4725单通道数模转换器的函数。下面的函数可以快速模式和普通模式,有命令类型来决定最终的操作方式。

/*设置MCP4725输出*/
void Mcp4725SetDatas(Mcp4725ObjectType *mcp,Mcp4725CommandType cmd,uint16_t data)
{uint8_t wData[3];uint8_t pdMode=0;uint16_t wSize=0;uint8_t command[]={Fast_Mode,Write_DAC_Register,Write_DAC_Register_EEPROM};pdMode=(uint8_t)(mcp->pdMode);if(cmd==Mcp4725_Fast_Mode)  //快速模式{wData[1]=(uint8_t)data;wData[0]=(uint8_t)(data>>8);wData[0]=wData[0]|command[cmd];wData[0]=wData[0]|(pdMode<<4);wSize=2;}else    //普通模式{wData[0]=command[cmd];wData[0]=wData[0]|(pdMode<<1);wData[1]=(uint8_t)(data>>4);wData[2]=(uint8_t)(data<<4);wSize=3;}mcp->Write(mcp,wData,wSize);
}

  MCP4725单通道数模转换器不断可以写数据也可以读数据。读回来的数据包括状态命令字、DAC寄存器数据以及EEPROM数据,总共是5个字节。具体的操作时序如下:

  根据前速的分析以及时序图,我们可以简单实现读操作如下:

/*读取MCP4725数据*/
void Mcp4725GetDatas(Mcp4725ObjectType *mcp,uint8_t *rData)
{mcp->Read(mcp,rData,5);
}

3、驱动的使用

  我们已经实现了MCP4725单通道数模转换器的驱动程序。我们还需要将这一驱动程序实际应用一下以确认驱动程序的正确性。

3.1、声明并初始化对象

  同样,我们先声明一个MCP4725单通道数模转换器对象变量。前面我们已经抽象了对象类型,使用MCP4725单通道数模转换器对象类型声明如下:

Mcp4725ObjectType mcp4725;

  对于这个对象变量,我们还需要使用Mcp4725Initialization函数对它进行初始化才能使用。这个初始化函数有多个输入参数:

Mcp4725ObjectType *mcp,  //MCP4725对象变量
uint8_t slaveAddress,    //从站设备的地址
Mcp4725PDModeType pdMode,//掉电操作模式
Mcp4725Write write,      //写数据函数指针
Mcp4725Read read         //读数据函数指针

  这些参数中,第一个参数是我们要初始化的对象变量,已经在前面声明了。slaveAddress是指MCP4725单通道数模转换器的设备地址。掉电操作模式是枚举类型,根据使用需要选择就可以了。最后两个读写操作函数指针则需要我们实现相应的函数。这两个函数的原型定义如下:

typedef void (*Mcp4725Write)(struct Mcp4725Object *mcp,uint8_t *wData, uint16_t wSize);
typedef void (*Mcp4725Read)(struct Mcp4725Object *mcp,uint8_t *rData, uint16_t rSize);

  读写操作函数的实现与具体的软硬件平台是相关的,这里我们实现STM32F103硬件平台和HAL库的对应函数:

/*通过I2C1端口写MCP4725*/
static void BmcbMcp4725Write(struct Mcp4725Object *mcp,uint8_t *wData, uint16_t wSize)
{HAL_I2C_Master_Transmit(&hi2c1,mcp->devAddress,wData,wSize,1000);
}/*通过I2C1端口读MCP4725*/
static void BmcbMcp4725Read(struct Mcp4725Object *mcp,uint8_t *rData, uint16_t rSize)
{HAL_I2C_Master_Receive(&hi2c1,mcp->devAddress,rData,rSize,1000);
}
有了这些参数后,我们就可以使用这些参数来初始化MCP4725单通道数模转换器的对象变量了。
Mcp4725Initialization(&mcp4725,         //MCP4725对象变量0xC0,             //从站设备的地址MCP4725_Normal,   //掉电操作模式BmcbMcp4725Write, //写数据函数指针BmcbMcp4725Read   //读数据函数指针);

3.2、基于对象进行操作

  关于对象的应用这块,我们将实际工程中的应用代码节选过来。具体很简单就是计算当前应该下发的数字编码并将其下发给MCP4725单通道数模转换器就可以了。

uint16_t code=0;
code=(uint16_t)((aPara.phyPara.presControl/100.0)*4095.0);
Mcp4725SetDatas(&mcp4725,Mcp4725_Write_DAC,code);

4、应用总结

  我们设计并实现了MCP4725单通道数模转换器的驱动程序,而且将其运用到了实际的工程当中,使用情况符合我们的预期。
源码下载:https://github.com/foxclever/ExPeriphDriver

欢迎关注:

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

  1. 外设驱动库开发笔记1:AD56xx系列DAC驱动

    DAC在我们的项目中经常使用到,而使用最多的就是AD56xx系列,包括有单通道的AD5662.双通道的AD5623和AD5663.以及四通道的AD5624和AD5664等.出于方便复用的原因,我们设计 ...

  2. 外设驱动库开发笔记6:AD719x系列ADC驱动

    前面我们讨论了AD7705这种ADC器件的驱动开发,在实际中我们使用更多的是AD719x系列的ADC芯片.包括有AD7191.AD7192和AD7193等.接下来我们就来设计并开发AD719x的驱动程 ...

  3. 外设驱动库开发笔记52:PM3003S激光粉尘仪驱动

      空气质量是现代日常生活中人们所关注的事情,也是生存环境好坏的一种体现.其中粉尘数量监测更是空气质量检测中最常见的对象,在我们的检测设备中也经常会有这种需求.检测手段也是多种多样,这一篇中,我们就来 ...

  4. 外设驱动库开发笔记27:ESP8266无线通讯驱动

    我们的物联网产品所使用的平台都支持无线通讯,而且无线通讯本身更的成本较低,受到大家的欢迎.在本篇文章中,我们将详细讨论并实现ESP8266无线通讯模块的驱动. 1.功能概述 ESP8266是由乐鑫公司 ...

  5. 外设驱动库开发笔记26:nRF24L01无线通讯驱动

    现在无线在我们的生活中无处不在.而我们开发的物联网产品也大量使用无线通讯.在这一篇文章中,我们将讨论nRF24L01无线通讯模块驱动程序的开发与实现. 1.功能概述 nRF24L01是一款工作在2.4 ...

  6. 外设驱动库开发笔记25:FM25xxx FRAM存储器驱动

    在我们的项目中,时常会有参数或数据需要保存.铁电存储器的优良性能和操作方便常常被我们选用.FM25xxx FRAM存储器就是我们经常使用到的一系列铁电存储器,这一篇我们将讨论FM25xxx FRAM存 ...

  7. 外设驱动库开发笔记8:GPIO模拟I2C驱动

    I2C总线简单方便,是我们经常使用的一种总线.但有时候我们的MCU没有足够多的I2C控制器来实现我们的应用,所幸我可以使用普通的GPIO引脚来模拟低速的I2C总线通信.这一节我们就来实现使用软件通过普 ...

  8. 外设驱动库开发笔记5:AD7705系列ADC驱动

    我们的经常需要采集一些精度要求较高的模拟信号,使用MCU集成的ADC难以达到要求.所以我们需要独立的ADC芯片.这一节我们就来设计并实现AD7705芯片的驱动.并探讨驱动的使用方法. 1.功能概述 A ...

  9. 外设驱动库开发笔记7:LTC2400系列ADC驱动

    有些时候我们需要对高精度的ADC来处理一些要求较高的模拟量采集.在处理温控器的过程中我们就使用到了LTC2400这款ADC.接下来我们就来设计并实现LTC2400的驱动. 1.功能概述 LTC2400 ...

最新文章

  1. CUDA之单thread单block多thread单block多thread多block
  2. c++ double 只输出五位_c 语言第四章 在控制台上数据的输入和输出
  3. Leangoo敏捷工具如何升级至企业版
  4. python语言怎么学-怎么学Python语言呢?粤嵌Python学习路线入门
  5. Oracle Compile 编译 无效对象(ORA-04063: package body SYS.DBMS_XPLAN 有错误)
  6. 计算机学校的奖项,2017年度国家科学技术奖各奖项公示
  7. 添加图标_图标变化、小程序添加到桌面...微信电脑版这些功能更新!
  8. oracle注意事项
  9. js 线性最小二乘回归线方程
  10. 计算机技术发展5大阶段,现阶段计算机技术应用及发展
  11. 台湾通泰TTP223-BA6和TTP223N-BA6有什么区别?
  12. 【学习笔记】分布式追踪Tracing
  13. 淘宝商城,亚洲最大网上购物网站
  14. 第十三届蓝桥杯《EDA设计与开发》赛后总结
  15. Android中的动画(二)和ButterKnife (黄油刀)
  16. Microsoft Edge 浏览器cpu占用率莫名升高
  17. Gatsby中怎么使用MDX?
  18. HTML网页制作入门
  19. 团队从小到大,再到体验团队
  20. Windows系统的基础上装Ubuntu双系统系统分区问题

热门文章

  1. HTML特效页面(2)--倒计时显示代码(页面美观)
  2. php 电话中转 保护用户隐私,打车APP的隐私保护通话是如何保护用户号码隐私的...
  3. AirPods无法充电的解决方法
  4. InDesign 教程:如何更改内容颜色?
  5. Conquer English 成功征服英语20个好习惯
  6. mysql 延时 注入_MySQL时间盲注五种延时方法
  7. 美国技术人员常用招聘求职网站
  8. win8安装.net framework 3.5 满足游戏 easybcd等需求
  9. Jena对本体、RDF三元组的API操作记录
  10. Bailian2712 细菌繁殖【日期计算+位运算】