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.3、LDAC功能
除去单通道的设备外,其他的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