
在电子产品设计开发过程中,很多情况会涉及到AD的采集的需求,一般我们会选择单片机内部的ADC模块进行采集,但是会遇到一个问题,他们的采集的精度不能满足我们采集微小信号或差分信号的需求,一般我们会选择成品的ADC芯片进行电压信号的采集。我们市面上的ADC芯片的选型种类太多,对于设计产品我们比较关心的几个方面分别是精度,硬件设计的要求,软件调试的复杂程度和芯片的价格。今天我把公司刚开发一款产品上用的一款ADC芯片推荐给大家。
CS1238 是一款高精度、低功耗模数转换芯片,两路差分输入通道,内置温度传感器和
高精度振荡器。
CS1238 的 PGA 可选:1、2、64、128,默认为 128。
CS1238 的 ADC 数据输出速率可选:10Hz、40Hz、640Hz、1.28kHz,默认为 10Hz;
MCU 可以通过 2 线的 SPI 接口 SCLK、 DRDY / DOUT与 CS1238 进行通信,对其进行配置,例如通道选择、PGA 选择、输出速率选择等。2线的通信方式占用MCU资源较少,同时通信协议比较简单。
但是调试过程中遇到挺大的问题,下面我把调试的一些具体问题及解决的方法分享给大家。
1、硬件连接比较简单, SCLK、 DRDY / DOUT端分别连接到单片机的任意两脚(两脚需要能满足基本输入和输出功能),在程序的配置方面,SCLK配置为上拉输出,DRDY / DOUT端配置为上拉输入。
2、对于芯片一般会操作三个功能分别是进行芯片采集到的AD值得读取,读取芯片内部的配置信息,写入芯片内部的配置信息。这些所有的指令传输与SCLK端的电平的变化优化,通过电平的变化进行信息的传输。同时需要注意DRDY / DOUT端在传输过程中需要切换该端口的输入和输出的状态。如:在写命令时DRDY / DOUT端就是输入状态,当读芯片发出的信息时DRDY / DOUT端就是输出状态。
3、当 SCLK 从低电平变高电平并保持在高电平超过 100µs,CS1238 即进入 PowerDwon模式,此时功耗低于 0.1μA。当 SCLK 重新回到低电平时,芯片会重新进入正常工作状态。所以在做完相关的操作后要把SCLK的电平拉低,等待下一次信息的传输。
4、应保证 SCLK 的上升和下降时间都小于 50ns,否则会影响CS1238对电平的识别。
5、SCLK高低电平的持续时间要在2us~154us之间,超过这个时间长度会影响到芯片对电平的识别。
6、还有一个非常重要的问题就是,DRDY / DOUT端为高电平就绪状态时才能进行芯片的相关的通信操作。
在电子产品设计开发过程中,如果用到ADC芯片,我建议大家可以使用一下这款芯片,价格实惠,同时使用比较便捷,采集精度高等特点,如果针对调试CS1238的问题,可以随时留言或者联系我们。
具体例程程序如下:
//整个通讯时序的时间必须小于1个数据转换周期
//推荐每个CLK的时间长度:2uS ~ 15uS
/*—————————————————————————–
Name : F_AD_Clock
Function: ADC时序时钟
—————————————————————————–*/
void F_AD_Clock(void)
{
IO_CLK_AD = 1;
F_Delay();
IO_CLK_AD = 0;
F_Delay();
}
/*—————————————————————————–
Name : F_Read_AD_Byte
Function: 读3字节AD值
—————————————————————————–*/
void F_Read_AD(void)
{
_UCHAR i;
R_AD0.word = 0;
EIC20_EIE0 = 0; //关闭MCU_DAT管脚的外部中断(防止读写时误触发)
// while(IO_Data_AD);
for(i = 0; i < 24; i++) //发送24个CLK,接收数据
{
R_AD0.word <<= 1;
F_AD_Clock();
if(IO_Data_AD)
{
R_AD0.word ++;
}
}
F_AD_Clock();
F_AD_Clock();
F_AD_Clock(); //CLK27,拉高DRDY
//如果一个数据转换周期内对ADC进行两次或以上的读写操作,则需要发送46个CLK,而不是27个CLK
//(即:发送24个CLK读取数据,然后再发送22个CLK)
EIC20_EIE0 = 1; //重新开启MCU_DAT管脚的外部中断
}
/*—————————————————————————–
Name : F_Rd_AdReg
Function: 读取ADC寄存器
—————————————————————————–*/
_UCHAR F_Rd_AdReg(void)
{
_UCHAR i;
union _UCHAR_BIT R_Temp;
//———————————-
EIC20_EIE0 = 0; //关闭MCU_DAT管脚的外部中断(防止读写时误触发)
//———————————-
//1 ~ 3:clk1-clk27
//———————————-
for(i = 0; i < 27; i++)
{
F_AD_Clock();
}
//———————————-
//4:clk28-clk29
//———————————-
IO_Data_AD_Dir = 1; //把MCU_Data管脚设为输出
F_AD_Clock();
F_AD_Clock();
//———————————-
//5:clk30-clk36(发送读命令)
//———————————-
R_Temp.byte = 0xAC; //读命令(0x56)左移1位
for(i = 0; i < 7; i ++)
{
if(R_Temp.bit.b07) //MSB
{
IO_Data_AD = 1;
}
else
{
IO_Data_AD = 0;
}
R_Temp.byte = R_Temp.byte << 1;
F_AD_Clock();
}
//———————————-
//6:clk37
//———————————-
IO_Data_AD_Dir = 0; //把MCU_Data管脚设为输入
F_AD_Clock();
//———————————-
//7:clk38-clk45(读取寄存器)
//———————————-
R_Temp.byte = 0;
for(i = 0; i < 8; i++)
{
R_Temp.byte <<= 1;
F_AD_Clock();
if(IO_Data_AD)
{
R_Temp.byte ++;
}
}
//———————————-
//8:clk46
//———————————-
F_AD_Clock();
//———————————-
EIC20_EIE0 = 1; //重新开启MCU_DAT管脚的外部中断
return R_Temp.byte;
}
/*—————————————————————————–
Name : F_Wr_AdReg
Function: 写入ADC寄存器
—————————————————————————–*/
void F_Wr_AdReg(_UCHAR R_AdReg)
{
_UCHAR i;
union _UCHAR_BIT R_Temp;
//———————————-
EIC20_EIE0 = 0; //关闭MCU_DAT管脚的外部中断(防止读写时误触发)
//———————————-
//1 ~ 3:clk1-clk27
//———————————-
for(i = 0; i < 27; i++)
{
F_AD_Clock();
}
//———————————-
//4:clk28-clk29
//———————————-
IO_Data_AD_Dir = 1; //把MCU_Data管脚设为输出
F_AD_Clock();
F_AD_Clock();
//———————————-
//5:clk30-clk36(发送写命令)
//———————————-
R_Temp.byte = 0xCA; //写命令(0x65)左移1位
for(i = 0; i < 7; i ++)
{
if(R_Temp.bit.b07) //MSB
{
IO_Data_AD = 1;
}
else
{
IO_Data_AD = 0;
}
R_Temp.byte = R_Temp.byte << 1;
F_AD_Clock();
}
//———————————-
//6:clk37
//———————————-
F_AD_Clock();
//———————————-
//7:clk38-clk45(写入寄存器)
//———————————-
R_Temp.byte = R_AdReg;
for(i = 0; i < 8; i ++)
{
if(R_Temp.bit.b07) //MSB
{
IO_Data_AD = 1;
}
else
{
IO_Data_AD = 0;
}
R_Temp.byte = R_Temp.byte << 1;
F_AD_Clock();
}
//———————————-
//8:clk46
//———————————-
IO_Data_AD_Dir = 0; //把MCU_Data管脚设为输入
F_AD_Clock();
//———————————-
EIC20_EIE0 = 1; //重新开启MCU_DAT管脚的外部中断
}
不错的文档,对调试有帮助