一、背景
有个项目使用了AD5270数字电位器,用来替代了传统的电位器实现数字控制。但发送命令数据一直没有反应,电阻也出不来,搞了好久。
二、数字接口
兼容SPI接口,通讯时序也不细究了,直接上官网的参考代码(官网上也有不少人出现了各种各样的问题,但都没有具体说明是怎么解决的)。
2.1 C文件
#include <ADuCM360.h>
#include "AD5270.h"
//***************************************************************************
//define
//***************************************************************************
#define SYNC_HIGH pADI_GP1->GPOUT |= 0x80 ;
#define SYNC_LOW pADI_GP1->GPOUT &=~ 0x80 ;
#define SCLK_HIGH pADI_GP1->GPOUT |= 0x20 ;
#define SCLK_LOW pADI_GP1->GPOUT &=~ 0x20 ;
#define SDIN_HIGH pADI_GP1->GPOUT |= 0x40;
#define SDIN_LOW pADI_GP1->GPOUT &=~ 0x40;
#define SDO ((pADI_GP1->GPIN &0x10 ) ==0x10) ? 1 :0;
#define NOP_NUMBER (50)
//***************************************************************************
//declaration
//***************************************************************************
void AD5270_IO_Initialize(void);
void SPI_nop(unsigned long cnt);
unsigned int SPI_Tx_16bit(unsigned int sendData);
//***************************************************************************
//***************************************************************************
void AD5270_IO_Initialize(void)
{
pADI_GP1->GPOEN |= 0xE0;
SYNC_LOW;
SCLK_LOW;
SDIN_LOW;
}
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
void SPI_nop(unsigned long cnt)
{
unsigned long i=0;
for(i=0;i<cnt;i++)
{
__NOP();
}
}
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
unsigned int SPI_Tx_16bit(unsigned int sendData)
{
unsigned char i=0;
unsigned char temp= 0x0000;
unsigned int res= 0x0;
SPI_nop(NOP_NUMBER);
SYNC_LOW;
SCLK_LOW;
SPI_nop(NOP_NUMBER);
for(i=0;i<16;i++)
{
SCLK_HIGH;
if( (sendData & (1<< (15-i))) == (1<< (15-i)))
{SDIN_HIGH;}
else {SDIN_LOW;}
SPI_nop(NOP_NUMBER);
SCLK_LOW;
temp=SDO;
res |= (temp <<(15-i) );
SPI_nop(NOP_NUMBER);
}
SPI_nop(NOP_NUMBER);
SYNC_HIGH;
return res;
}
管脚配置就根据自己的MCU自行修改了,我自己用的是STM32F103,就按STM32的修改。
2.2 H头文件
描述的是与芯片手册对应的命令码
#define COMMAND_NOP (0x00<<10)
#define COMMAND_W_RDAC (0x01<<10)
#define COMMAND_R_RDAC (0x02<<10)
#define COMMAND_STORE_RDAC_50TP (0x03<<10)
#define COMMAND_SOFTWARE_RESET (0x04<<10)
#define COMMAND_R_50TP_NEXT_FRAME (0x05<<10)
#define COMMAND_R_LAST_50TP_LOCATION (0x06<<10)
#define COMMAND_W_CONTROL (0x07<<10)
#define COMMAND_R_CONTROL (0x08<<10)
#define COMMAND_SOFTWARE_SHUTDOWN (0x09<<10)
void AD5270_IO_Initialize(void);
unsigned int SPI_Tx_16bit(unsigned int sendData);
主函数
uint16_t RD_data = 255; //满量程1023
uint16_t RD_flag = 0x0;
uint32_t RDAC_val_read;
uint32_t RDAC_val_read2;
int main()
{
ADuCM360_Initialize();//配置自己的MCU管脚初始化
AD5270_IO_Initialize();//初始化管脚的电平
while(1)
{
if(RD_flag)
{//RD_flag自己仿真置1进入一次设定
//enable RDAC write
SPI_Tx_16bit(COMMAND_W_CONTROL|0x03);
//Write RDAC Reg
SPI_Tx_16bit(COMMAND_W_RDAC|RD_data);
//read RDAC Reg
SPI_Tx_16bit(COMMAND_R_RDAC);
RDAC_val_read=SPI_Tx_16bit(COMMAND_NOP);
delay_us(10);
//read _NEXT_FRAME
SPI_Tx_16bit(COMMAND_R_50TP_NEXT_FRAME);
RDAC_val_read2=SPI_Tx_16bit(COMMAND_NOP);
//save to 50-tp
//SPI_Tx_16bit(COMMAND_STORE_RDAC_50TP);
RD_flag = 0;
}
delay_ms(500);
}
}
三、调试
问题来了,数据下发下去,阻值怎么都没反应。用示波器量管脚,下发的波形都是正常的。并且读回来的数据异常,都是0,因为SDO一直是低电平。导致我都怀疑是不是芯片接口坏了,怎么会没有回复。没回复,我就不能保证接口是否正常通讯,陷入了沉思。
重要的来了,我回去再去看了一下数据手册,发现了一个地方可能存在问题,SDO管脚是一个开漏输出,要搭配上拉电阻使用。我心想就算不读数据,我只发数据也不影响设置阻值才对啊。但后面还有一句话“even if it is not use”,就算SDO不使用也得接上拉电阻。这不接上拉电阻难道会影响其他几个通讯脚,反正也没什么思路,那就再补上一个2.2k的上拉电阻吧(我的接口硬件是没带上拉电阻,管脚也没配上拉),反正也没什么好的思路了。
奇迹就发生,RDAC_val_read,RDAC_val_read2这两个变量有数值了,RDAC_val_read还跟RD_data的值对应上,说明读写正常。再去量一下AW的电阻,果然有了20k*255/1023=4.99k,修改RD_data的值,触发发生,电阻果然正常变化了。说明SDO这个上拉电阻是必须要接的,不然直接就不能正常工作。
那不读数据,只设数据能不能正常工作呢,是要每次都要发送使能命令COMMAND_W_CONTROL吗?修改代码测试一下。
uint16_t RD_data = 255; //满量程1023
uint16_t RD_flag = 0x0;
uint32_t RDAC_val_read;
uint32_t RDAC_val_read2;
int main()
{
ADuCM360_Initialize();//配置自己的MCU管脚初始化
AD5270_IO_Initialize();//初始化管脚的电平
SPI_Tx_16bit(COMMAND_W_CONTROL|0x02);//不用0x03,试试0x02
while(1)
{
if(RD_flag)
{//RD_flag自己仿真置1进入一次设定
//Write RDAC Reg
SPI_Tx_16bit(COMMAND_W_RDAC|RD_data);
RD_flag = 0;
}
delay_ms(500);
}
}
那不读数据,只设数据是可以正常设置阻值的,发送使能命令COMMAND_W_CONTROL可以在初始化只发一次,下次发送设置阻值就可以不用发。但使能命令必须发一次,不然芯片也是不能正常工作的。
四、总结
所以,不要忽略手册这句话“open-drain output requires an external pull-up resistor even if it is not use.”,SDO一定得接上拉电阻。保证数字接口通讯正常。