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

1、功能概述

AD56xx系列DAC属于nanoDAC系列,是低功耗,12位、14位或者16位缓冲电压输出数模转换器(DAC),采用2.7V至5.5V单电源供电。AD56xx采用多功能三线式串行接口,能够以最高50 MHz的时钟速率工作,并与标准SPI、QSPI、MICROWIRE、DSP接口标准兼容。它内置片内精密输出放大器,能够实现轨到轨输出摆幅。其功能框图如下所示:

AD56xx系列DAC都有一个24位的移位寄存器,所有的操作都是通过写移位寄存器来实现的。对于不同的型号其移位寄存器的各位略有差异。具体如下图所示:

单通道没有通道选择位,命令位只有2位,所以我们的软件实际上就是针对不同的功能需求配置移位寄存器。

2、驱动设计与实现

前面已经说过,对AD56xx的操作,实际就是根据需要配置移位寄存器。接下来我们将在此基础上分析并实现AD56xx系列DAC的驱动。

2.1、对象定义

在使用对象之前,我们需要抽象对象的定义。对于AD56xx系列DAC我们需要定义类型枚举,因为该系列包含有多种DAC模块。此外我们还要定义AD56xx系列DAC的对象类型。

/*定义DAC器件的类型*/
typedef enum AD56xx{AD5662=0,AD5623=1,AD5643=2,AD5663=3,AD5624=4,AD5644=5,AD5664=6,TypeNumber,
}AD56xxType;/* 定义AD56XX对象类型 */
typedef struct AD56xxObject {AD56xxType objectType;        //DAC的类型void (*WriteDataToDAC)(uint8_t *tData,uint16_t tSize);        //向DAC发送数据void (*ChipSelcet)(AD56xxCSType cs);     //片选信号
}AD56xxObjectType;

我们抽象了对象,在我们使用这个对象定义声明了一个具体的对象时,它只是一个代表对象的变量,我们需要对它进行初始化才可使用。于是我们定义初始化对象函数。

/* 初始化AD56xx对象 */
void AD56xxInitialization(AD56xxObjectType *dacObj,AD56xxType objectType,AD56xxWrite write,AD56xxChipSelcet cs)
{if((dacObj==NULL)||(write==NULL)||(cs==NULL)){return;}if(objectType<TypeNumber){dacObj->objectType=objectType;}dacObj->WriteDataToDAC=write;dacObj->ChipSelcet=cs;
}

2.2、对象操作

我们已经将AD56xx抽象为对象,那么对AD56xx的操作都转化为对某一个对象的操作。接下来我们来实现对象的操作函数。

2.2.1、软件复位

软件复位也是通过操作输入移位寄存器来实现的。命令位的定义没有变化,数据段的最后一位作为软件复位的模式设定,其它位无效。最后一位为0时,会清除DAC寄存器和输入寄存器,而最后一位为1时清除掉全部寄存器。最后一位为1时,实际就是上电复位模式。输入移位寄存器的数据格式如下:

其软件实现如下:

/* 对AD56xx进行软件复位 */
void Ad56xxSoftwareReset(AD56xxObjectType *dacObj,AD56xxResetType resetMode)
{uint32_t inputShiftData=0;if(resetMode==ResetSoftware){inputShiftData=RESET|Register_Reset_Software;}if(resetMode==ResetPoweron){inputShiftData=RESET|Register_Reset_Poweron;}uint8_t txData[3];txData[0]=inputShiftData>>16;txData[1]=inputShiftData>>8;txData[2]=inputShiftData;dacObj->ChipSelcet(AD56xxCS_Enable);dacObj->WriteDataToDAC(txData,3);dacObj->ChipSelcet(AD56xxCS_Disable);
}

2.2.2、上电复位

上电复位也是通过操作输入移位寄存器来实现的。命令位的定义没有变化,数据段的DB5和DB4定义掉电的模式,而DB1和DB0定义操作的通道。输入移位寄存器的数据格式如下:

其软件实现如下:

/* 设置AD56xx上电/掉电工作模式 */
void Ad56xxPowerUpDownMode(AD56xxObjectType *dacObj,AD56xxChannelType channel,AD56xxPowerdownType powerdownType)
{uint32_t inputShiftData=0;uint32_t pdc=0;uint32_t pdm=0;uint32_t cmd=Power_Down;uint32_t pdChannel[]={DAC_A,DAC_B,DAC_C,DAC_D,DAC_ALL,DAC_None};pdc=pdChannel[channel];uint32_t pdMode[]={Normal_Operation,_1K_GND,_100K_GND,Three_State};pdm=pdMode[powerdownType];if(dacObj->objectType==AD5662){pdc=DAC_None;pdm=(pdm<<12);cmd=Write_to_Input_Register;}inputShiftData=cmd|pdc|pdm;uint8_t txData[3];txData[0]=inputShiftData>>16;txData[1]=inputShiftData>>8;txData[2]=inputShiftData;dacObj->ChipSelcet(AD56xxCS_Enable);dacObj->WriteDataToDAC(txData,3);dacObj->ChipSelcet(AD56xxCS_Disable);
}

2.2.3LDAC功能

除去单通道的设备外,其他的AD56xx设备都具有LDAC操作功能。而对LDAC操作的寄存器设置如下图所示:

其软件实现如下:

/* 设置AD56xx及同类器件LDAC功能 */
void SetAd56xxLdacFunction(AD56xxObjectType *dacObj,AD56xxChannelType channel)
{uint32_t inputShiftData=0;uint32_t pdChannel[]={DAC_A,DAC_B,DAC_C,DAC_D,DAC_ALL,DAC_None};inputShiftData=pdChannel[channel];inputShiftData=inputShiftData|LDAC_Register_Setup;uint8_t txData[3];txData[0]=(uint8_t)(inputShiftData>>16);txData[1]=(uint8_t)(inputShiftData>>8);txData[2]=(uint8_t)inputShiftData;dacObj->ChipSelcet(AD56xxCS_Enable);dacObj->WriteDataToDAC(txData,3);dacObj->ChipSelcet(AD56xxCS_Disable);
}

2.2.4、内部基准电压源设置

有一些型号以R结尾的AD56xx器件是包含有内部参考电源的。片内基准电压源在上电时默认关闭。通过设置控制寄存器中的软件可编程位DB0,可以开启或关闭此基准电压源。 具体的寄存器设置如下图所示:

其软件实现如下:

/* 开启或关闭内部参考电压源 */
void SetInternalReference(AD56xxObjectType *dacObj,AD56xxRefType ref)
{uint32_t inputShiftData=0;inputShiftData=Reference_Set;if(ref==AD56xxRef_ON){inputShiftData=inputShiftData|Reference_ON;}uint8_t txData[3];txData[0]=(uint8_t)(inputShiftData>>16);txData[1]=(uint8_t)(inputShiftData>>8);txData[2]=(uint8_t)inputShiftData;dacObj->ChipSelcet(AD56xxCS_Enable);dacObj->WriteDataToDAC(txData,3);dacObj->ChipSelcet(AD56xxCS_Disable);
}

2.2.5、输出操作

对各输出通道值的操作也是通过输入移位寄存器来完成。其数据格式如前面输入移位寄存器的介绍。后16位是数据(0-65535),然后是3位地址和3位命令。通讯的时序图如下所示:

其软件实现如下:

/* 设置DA通道的值 */
void SetAD56xxChannelValue(AD56xxObjectType *dacObj,AD56xxChannelType channel,uint16_t data)
{uint32_t inputShiftData=0;uint32_t dac=0;uint32_t cmd=WriteTo_Update_DAC_Channel;uint32_t dacChannel[]={DAC_Channel_A,DAC_Channel_B,DAC_Channel_C,DAC_Channel_D,DAC_Channel_ALL};uint32_t shiftV[]={0,4,2,0,4,0};if(channel>=ChannelNone){return;}dac=dacChannel[channel];if(dacObj->objectType==AD5662){dac=DAC_Channel_A;cmd=Write_to_Input_Register;}inputShiftData=dac|cmd|(data<<shiftV[dacObj->objectType]);uint8_t txData[3];txData[0]=(uint8_t)(inputShiftData>>16);txData[1]=(uint8_t)(inputShiftData>>8);txData[2]=(uint8_t)inputShiftData;dacObj->ChipSelcet(AD56xxCS_Enable);dacObj->WriteDataToDAC(txData,3);dacObj->ChipSelcet(AD56xxCS_Disable);
}

3、驱动的使用

我们已经实现了AD56xx系列DAC的驱动,接下来我们就可以使用我们的这个驱动实现我们的应用了。

3.1、声明并初始化对象

首先需要使用AD56xx对象类型AD56xxObjectType声明一个对象变量。具体声明形式如下:AD56xxObjectType ad56xx;

对象变量需要使用AD56xxInitialization函数进行初始化。这个函数的参数除了对象变量外还有对象类型以及写数据操作和片选操作2个函数指针。在调用初始化函数之前必须准备好这些参数。所以我们需要按如下类型定义相关函数。

/* 向DAC发送数据函数指针类型 */
typedef void (*AD56xxWrite)(uint8_t *tData,uint16_t tSize);/* 片选操作函数指针类型 */
typedef void (*AD56xxChipSelcet)(AD56xxCSType cs);

我们实现这几个函数并将函数指针作为参数传递给初始化函数。初始化函数的调用样式如下:

AD56xxInitialization(&ad56xx,objectType,write,cs);

后两个参数即是上面定义的2个函数的函数指针。这两个函数根据具体的硬件电路来实现。第二个参数为对象类型,为AD56xxType枚举类型。

3.2、基于对象进行操作

初始化完成后就可以操作对象了。对AD56xx系列DAC对象的操作包括:软件复位操作,上下电模式控制,LDAC控制,参考电压操作以及输出控制。下面将调用驱动函数实现相应的应用。

AD56xx系列DAC拥有1到4个通道,所以我们需要为操作制定通道。还有向该通道设定的数据,由于AD56xx系列DAC为12到16位,所以我们要发送一个不超过16位的无符号整数。有了这两个参数我们就可以调用SetAD56xxChannelValue函数为AD56xx系列DAC设定输出了。

SetAD56xxChannelValue(&ad56xx,channel,data);

第二个参数为设定的通道,是一个AD56xxChannelType类型的枚举,以此决定我们当前操作的是哪一个通道。而其它的函数:

/*设置AD56xx上电/掉电工作模式*/
void Ad56xxPowerUpDownMode(AD56xxObjectType *dacObj,AD56xxChannelType channel,AD56xxPowerdownType powerdownType);/*对AD56xx进行软件复位*/
void Ad56xxSoftwareReset(AD56xxObjectType *dacObj,AD56xxResetType resetMode);/* 开启或关闭内部参考电压源 */
void SetInternalReference(AD56xxObjectType *dacObj,AD56xxRefType ref);/*设置AD56xx及同类器件LDAC功能*/
void SetAd56xxLdacFunction(AD56xxObjectType *dacObj,AD56xxChannelType channel);

其调用方式是类似的。需要指出的是SetInternalReference函数,只有具有内部参考电源的器件调用这个函数才有作用。

4、应用总结

我们使用AD56xx系列DAC实现过多种应用,都是基于我们的驱动开发的。使用的结果基本都与我们的预期一样。

需要注意对象的类型,特别是AD5662的移位寄存器有很大区别。虽然驱动在这一方面作了处理,但是基于初始化时配置的类型执行的。所以在调用初始化函数初始化对象时一定要传递正确的类型。

​源码公布到GitHUB:https://github.com/foxclever/ExPeriphDriver

欢迎关注:

外设驱动库开发笔记1:AD56xx系列DAC驱动相关推荐

  1. 外设驱动库开发笔记42:DAC8552 DAC驱动

      模拟信号输出是经常会遇到的应用需求,解决的办法应多种,但我们使用最多的还是数模转换.对于不同的数模转换器我们需要为其编写适用的驱动程序,在这一篇中我们就来考虑如何实现DAC8552高精度模数转换器 ...

  2. 外设驱动库开发笔记23:AT24Cxx外部存储器驱动

    在我们的应用开发过程中,经常会使用到外部的EEPROM外部存储器来保存一些参数和配置数据等.而比较常用的就是AT24Cxx系列产品,这一节我们来开发用于操作AT24Cxx系列产品的驱动. 1.功能概述 ...

  3. 外设驱动库开发笔记54:外设库驱动设计改进的思考

      不知不觉中我们已经发布了五十多篇外设驱动的文章.前段时间有一位网友提出了一些非常中肯的建议,这也让我们开始考虑怎么优化驱动程序设计的问题.在这一篇中我们将来讨论这一问题. 1.问题分析   首先我 ...

  4. 外设驱动库开发笔记16:MS5536C压力变送器驱动

    压力检测也是经常会遇到的需求,比如环境压力或者低压气体等都会用到压力检测.这类检测压力都比较低,一般不会超过大气压,有时甚至是负压.这一篇我们要讨论的MS5536C就属于这类器件.接下来我们将设计并实 ...

  5. 外设驱动库开发笔记40:AT25xxx外部存储器驱动

      我们在前面开发过AT24CXX系列EEPROM存储器,它使用的是I2C接口.不过有时候我们也会使用SPI接口的EEPROM存储器.在这一篇我们将来讨论AT25XXX系列EEPROM存储器的驱动设计 ...

  6. 外设驱动库开发笔记17:MS5803压力变送器驱动

    很多时候我们需要检测被控对象的绝对压力,而且在我们的多款产品中也有这样的需求.当然检测绝对压力的传感器有很多,我们经常使用MS5803来实现压力检测.本篇中我们将设计并实现MS5803系列压力传感器的 ...

  7. 外设驱动库开发笔记34:OLED显示屏驱动

      现在OLED显示屏在嵌入式系统中应用的越来越多.对于一些显示信息不太复杂,以显示信息为主的需求,我们一般会选择OLED显示屏.在这一篇中,我们将讨论OLED显示屏驱动的设计与实现. 1.功能概述 ...

  8. 外设驱动库开发笔记18:MS5837压力变送器驱动

    绝对压力的检测是常见的需求.在我们的系统中也常常会遇到.而MS5837压力传感器也是我们进场会采用的方案.在这篇里我们将讨论并实现MS5837压力传感器的驱动. 1.功能概述 MS5837压力传感器是 ...

  9. 外设驱动库开发笔记14:DS18B20温度变送器驱动

    在一时候我们需要相对简单的检测温度信号,而DS18B20就是一款功能和应用都相对简单的温度传感器,通过单线就可以实现检测温度信号的需求.这一篇我们就来实现操作DS18B20获取温度数据的驱动. 1.功 ...

  10. 外设驱动库开发笔记33:LCD1602液晶显示屏驱动

      LCD1602是一种工业字符型液晶,能够同时显示16x02即32个字符.LCD1602液晶显示的原理是利用液晶的物理特性,通过电压对其显示区域进行控制,即可以显示出图形.在这一章我们就来讨论LCD ...

最新文章

  1. python快速写入hbase_Python生成HBase 10w+ 条数据说明
  2. hdu2041java
  3. Oracle查找重复数据
  4. C++实现circular queue循环队列(使用数组)(附完整源码)
  5. iTunes 降级安装 12.6
  6. C# 将List中的数据导入csv文件中
  7. 如何在SAP UI5应用里添加使用摄像头拍照的功能
  8. 【POJ - 3624 】Charm Bracelet (dp,0-1背包裸题)
  9. sa无法映射到数据 报错:创建对于用户sa失败
  10. Linux监控工具Spotlight on Unix
  11. Redis安装教程(各种坑)
  12. matlab找不到vs编译器
  13. 会议论文集出版地总结_codestorm_新浪博客
  14. React Native版本升级
  15. arduino 矩阵肩膀_表情矩阵——卖萌无需多言(arduino)
  16. 技术领导力 程序员如何才能带团队 文摘 (一)
  17. 柠檬班unitest和数据驱动
  18. Android 实现拍照功能,并将图片保存到本地存储
  19. LabVIEW控制高速微快门
  20. 87.假定输入的字符串中只包含字母和*号。请编写函数fun,它的功能是:除了字符串前导的*号之外,将串中其他*号全部删除。在编写函数时,不得使用C语言提供的字符串函数。

热门文章

  1. 【Java】如何理解Java中的双列集合Map?
  2. 基于Hadoop的产品大数据分布式存储优化
  3. sql中join与left-join图解区别
  4. 数据结构-王道2017-第5章 图
  5. BZOJ2819 Nim(DFS序)
  6. GWT(Google Web Tookit) Eclipse Plugin的zip下载地址(同时提供GWT Designer下载地址)
  7. 3.1_ 6_ 基本分页存储管理的基本概念
  8. springdata学习笔记-01-helloworld-(暂时不全223)
  9. 【剑指offer】面试题10- II:青蛙跳台阶问题(Java)
  10. Leetcode--229. 求众数Ⅱ