Other Parts Discussed in Thread: ADS1292
您好!本人在使用STM32H723系列进行HAL库开发时,发现单片机始终无法采集ADS1292R模块的信息,就连正常的读取ADS1292R的寄存器功能也无法实现,困扰了好几天。为了排除是模块的问题,我使用C8T6的例程在C8T6上测试了一遍,发现能够正常读取寄存器,说明模块是正常工作的,只能是软件方面的问题。我的软件配置如下:


我检查过原理图,SPI4的几个引脚并没有被其他SPI外设占用。ADS1292R的供电为3.3V,SCLK周期没有低于最小值50ns。以下是我的寄存器读取代码:
void ADS1292_Init(void)
{
// 屏蔽外部中断线10
HAL_NVIC_DisableIRQ(EXTI9_5_IRQn);
ADS_CS_H; // 设置引脚状态为1
//__HAL_SPI_ENABLE(&hspi4); //使能SPI2
//SPI2_ReadWriteByte(0X00);
ADS1292_PowerOnInit();//上电复位,进入待机模式
while(Set_ADS1292_Collect(0))//0 正常采集 //1 1mV1Hz内部侧试信号 //2 内部短接噪声测试
{
printf("1292寄存器设置失败\r\n");
HAL_Delay(1000);
}
printf("寄存器设置成功\r\n");
HAL_Delay(1000);
HAL_NVIC_EnableIRQ(EXTI9_5_IRQn);//开DRDY中断
}
void ADS1292_SET_REGBUFF(void)
{
ADS1292_REG[ID] = ADS1292_DEVICE;//ID只读
ADS1292_REG[CONFIG1] = 0x00; //0000 0aaa [7] 0连续转换模式 [6:3] 必须为0
ADS1292_REG[CONFIG1] |= Ads1292_Config1.Data_Rate;//[2:0] aaa 采样率设置采样率
ADS1292_REG[CONFIG2] = 0x00; //1abc d0e1 [7] 必须为1 [2] 必须为0 [0] 设置测试信号为1HZ、±1mV方波
ADS1292_REG[CONFIG2] |= Ads1292_Config2.Pdb_Loff_Comp<<6; //[6]a 导联脱落比较器是否掉电
ADS1292_REG[CONFIG2] |= Ads1292_Config2.Pdb_Refbuf<<5; //[5]b 内部参考缓冲器是否掉电
ADS1292_REG[CONFIG2] |= Ads1292_Config2.Vref<<4; //[4]c 内部参考电压设置,默认2.42V
ADS1292_REG[CONFIG2] |= Ads1292_Config2.Clk_EN<<3; //[3]d CLK引脚输出时钟脉冲?
ADS1292_REG[CONFIG2] |= Ads1292_Config2.Int_Test<<1; //[1]e 是否打开内部测试信号,
ADS1292_REG[CONFIG2] |= 0x81;//设置默认位
ADS1292_REG[LOFF] = 0x10;//[7:5] 设置导联脱落比较器阈值 [4] 必须为1 [3:2] 导联脱落电流幅值 [1] 必须为0 [0] 导联脱落检测方式 0 DC 1 AC
ADS1292_REG[CH1SET] = 0x00; //abbb cccc
ADS1292_REG[CH1SET] |=Ads1292_Ch1set.PD<<7; //[7] a 通道1断电?
ADS1292_REG[CH1SET] |=Ads1292_Ch1set.GAIN<<4; //[6:4]bbb 设置PGA增益
ADS1292_REG[CH1SET] |=Ads1292_Ch1set.MUX; //[3:0]cccc 设置通道1输入方式
ADS1292_REG[CH2SET] = 0x00; //abbb cccc
ADS1292_REG[CH2SET] |=Ads1292_Ch2set.PD<<7; //[7] a 通道2断电?
ADS1292_REG[CH2SET] |=Ads1292_Ch2set.GAIN<<4; //[6:4]bbb 设置PGA增益
ADS1292_REG[CH2SET] |=Ads1292_Ch2set.MUX; //[3:0]cccc 设置通道2输入方式
ADS1292_REG[RLD_SENS] = 0X00; //11ab cdef [7:6] 11 PGA斩波频率 fMOD/4
ADS1292_REG[RLD_SENS] |=Ads1292_Rld_Sens.Pdb_Rld<<5; //[5]a 该位决定RLD缓冲电源状态
ADS1292_REG[RLD_SENS] |=Ads1292_Rld_Sens.Rld_Loff_Sense<<4; //[4]b 该位使能RLD导联脱落检测功能
ADS1292_REG[RLD_SENS] |=Ads1292_Rld_Sens.Rld2N<<3; //[3]c 这个位控制通道2负输入 用于右腿驱动的输出
ADS1292_REG[RLD_SENS] |=Ads1292_Rld_Sens.Rld2P<<2; //[2]d 该位控制通道2正输入 用于右腿驱动的输出
ADS1292_REG[RLD_SENS] |=Ads1292_Rld_Sens.Rld1N<<1; //[1]e 这个位控制通道1负输入 用于右腿驱动的输出
ADS1292_REG[RLD_SENS] |=Ads1292_Rld_Sens.Rld1P; //[0]f 该位控制通道1正输入 用于右腿驱动的输出
ADS1292_REG[RLD_SENS] |= 0xc0;//设置默认位
ADS1292_REG[LOFF_SENS] = 0X00; //00ab cdef [7:6] 必须为0
ADS1292_REG[LOFF_SENS] |=Ads1292_Loff_Sens.Flip2<<5; //[5]a 这个位用于控制导联脱落检测通道2的电流的方向
ADS1292_REG[LOFF_SENS] |=Ads1292_Loff_Sens.Flip1<<4; //[4]b 这个位控制用于导联脱落检测通道1的电流的方向
ADS1292_REG[LOFF_SENS] |=Ads1292_Loff_Sens.Loff2N<<3; //[3]c 该位控制通道2负输入端的导联脱落检测
ADS1292_REG[LOFF_SENS] |=Ads1292_Loff_Sens.Loff2P<<2; //[2]d 该位控制通道2正输入端的导联脱落检测
ADS1292_REG[LOFF_SENS] |=Ads1292_Loff_Sens.Loff1N<<1; //[1]e 该位控制通道1负输入端的导联脱落检测
ADS1292_REG[LOFF_SENS] |=Ads1292_Loff_Sens.Loff1P; //[0]f 该位控制通道1正输入端的导联脱落检测
ADS1292_REG[LOFF_STAT] = 0x00; //[6]0 设置fCLK和fMOD之间的模分频比 fCLK=fMOD/4 [4:0]只读,导联脱落和电极连接状态
ADS1292_REG[RESP1] = 0X00;//abcc cc1d
ADS1292_REG[RESP1] |=Ads1292_Resp1.RESP_DemodEN<<7;//[7]a 这个位启用和禁用通道1上的解调电路
ADS1292_REG[RESP1] |=Ads1292_Resp1.RESP_modEN<<6; //[6]b 这个位启用和禁用通道1上的调制电路
ADS1292_REG[RESP1] |=Ads1292_Resp1.RESP_ph<<2; //[5:2]c 这些位控制呼吸解调控制信号的相位
ADS1292_REG[RESP1] |=Ads1292_Resp1.RESP_Ctrl; //[0]d 这个位设置呼吸回路的模式
ADS1292_REG[RESP1] |= 0x02;//设置默认位
ADS1292_REG[RESP2] = 0x00; //a000 0bc1 [6:3]必须为0 [0]必须为1
ADS1292_REG[RESP2] |= Ads1292_Resp2.Calib<<7; //[7]a 启动通道偏移校正?
ADS1292_REG[RESP2] |= Ads1292_Resp2.freq<<2; //[2]b 呼吸频率设置
ADS1292_REG[RESP2] |= Ads1292_Resp2.Rldref_Int<<1; //[1]c RLDREF信号源外部馈电?
ADS1292_REG[RESP2] |= 0X01;//设置默认位
ADS1292_REG[GPIO] = 0x0C; //GPIO设为输入 [7:4]必须为0 [3:2]11 GPIO为输入 [1:0] 设置输入时,指示引脚电平,设置输出时控制引脚电平
}
//通过SPI总线与ADS1292通信
u8 ADS1292_SPI(u8 com)
{
return SPI2_ReadWriteByte(com);
}
//写命令
void ADS1292_Send_CMD(u8 data)
{
ADS_CS_L;
HAL_Delay(1);
ADS1292_SPI(data);
HAL_Delay(1);
ADS_CS_H;
}
/*ADS1291、ADS1292和ADS1292R串行接口以字节形式解码命令,需要4个tCLK周期来解码和执行.
因此,在发送多字节命令时,4 tCLK周期必须将一个字节(或操作码)的结束与下一个字节(或操作码)分开。
假设CLK(时钟)为512 kHz,则tSDECODE (4 tCLK)为7.8125 us。
当SCLK(数据速率)为16mhz时,一个字节可以在500ns中传输,此字节传输时间不符合tSDECODE规范;
因此,必须插入一个延迟,以便第二个字节的末尾晚于7.3125us到达。
如果SCLK为1 MHz,则在8u秒内传输一个字节。由于此传输时间超过tSDECODE规范,处理器可以不延迟地发送后续字节。
在后面的场景中,可以对串行端口进行编程,使其从每个循环的单字节传输转移到多个字节*/
//读写多个寄存器
void ADS1292_WR_REGS(u8 reg,u8 len,u8 *data)
{
u8 i;
ADS_CS_L;
HAL_Delay(1);
ADS1292_SPI(reg);
HAL_Delay(1);
ADS1292_SPI(len-1);
if(reg&0x40) //写
{
for(i=0;i<len;i++)
{
HAL_Delay(1);
ADS1292_SPI(*data);
data++;
}
}
else //读
{
for(i=0;i<len;i++)
{
HAL_Delay(1);
*data = ADS1292_SPI(0);
data++;
}
}
HAL_Delay(1);
ADS_CS_H;
}
//寄存器数组写入寄存器
u8 ADS1292_WRITE_REGBUFF(void)
{
u8 i,res=0;
u8 REG_Cache[12]; //存储寄存器数据
ADS1292_SET_REGBUFF();//设置寄存器数组
ADS1292_WR_REGS(WREG|CONFIG1,11,ADS1292_REG+1);//数组变量写入寄存器
HAL_Delay(10);
ADS1292_WR_REGS(RREG|ID,12,REG_Cache);//读寄存器
HAL_Delay(10);
for(i=0;i<12;i++ ) //检查寄存器
{
//printf("1111 device_id value: 0x%02X\r\n", REG_Cache[i]);
//printf("Write: 0x%02X, Read: 0x%02X\r\n", ADS1292_REG[i], REG_Cache[i]);
if(ADS1292_REG[i] != REG_Cache[i])
{
if(i!= 0 && i!=8 && i != 11) //0 8 和11是ID 导联脱落和GPIO相关
res=1;
else
continue;
}
}
return res;
}
void ADS1292_PowerOnInit(void)
{
//u8 i;
//u8 REG_Cache[12];
ADS1292_Send_CMD(SDATAC);//发送停止连续读取数据命令
HAL_Delay(100);
ADS1292_Send_CMD(RESET);//复位
HAL_Delay(1000);
ADS1292_Send_CMD(SDATAC);//发送停止连续读取数据命令
HAL_Delay(100);
}
//设置通道1内部1mV测试信号
u8 ADS1292_Single_Test(void) //注意1292R开了呼吸解调,会对通道一的内部测试信号波形造成影响,这里只参考通道2即可,1292不受影响
{
u8 res=0;
Ads1292_Config2.Int_Test = INT_TEST_ON;//打开内部测试信号
Ads1292_Ch1set.MUX=MUX_Test_signal;//测试信号输入
Ads1292_Ch2set.MUX=MUX_Test_signal;//测试信号输入
if(ADS1292_WRITE_REGBUFF())//写入寄存器
res=1;
HAL_Delay(10);
return res;
}
//设置内部噪声测试
u8 ADS1292_Noise_Test(void)
{
u8 res=0;
Ads1292_Config2.Int_Test = INT_TEST_OFF;//关内部测试信号
Ads1292_Ch1set.MUX = MUX_input_shorted;//输入短路
Ads1292_Ch2set.MUX = MUX_input_shorted;//输入短路
if(ADS1292_WRITE_REGBUFF())//写入寄存器
res=1;
HAL_Delay(10);
return res;
}
//正常信号采集模式
u8 ADS1292_Single_Read(void)
{
u8 res=0;
Ads1292_Config2.Int_Test = INT_TEST_OFF;//关内部测试信号
Ads1292_Ch1set.MUX = MUX_Normal_input;//普通电极输入
Ads1292_Ch2set.MUX = MUX_Normal_input;//普通电极输入
if(ADS1292_WRITE_REGBUFF())//写入寄存器
res=1;
HAL_Delay(10);
return res;
}
//配置ads1292采集方式
u8 Set_ADS1292_Collect(u8 mode)
{
u8 res;
HAL_Delay(10);
switch(mode)//设置采集方式
{
case 0:
res =ADS1292_Single_Read();
break;
case 1:
res =ADS1292_Single_Test();
break;
case 2:
res =ADS1292_Noise_Test();
break;
}
if(res)return 1;//寄存器设置失败
ADS1292_Send_CMD(RDATAC); //启动连续模式
HAL_Delay(10);
ADS1292_Send_CMD(START); //发送开始数据转换(等效于拉高START引脚)
HAL_Delay(10);
return 0;
}
uint8_t SPI2_ReadWriteByte(uint8_t TxData)
{
uint8_t retry=0;
uint8_t RxData=0;
while (__HAL_SPI_GET_FLAG(&hspi4, SPI_FLAG_TXE) == RESET) //检查指定的SPI标志位设置与否:发送缓存空标志位
{
retry++;
printf("1111 device_id value: 0x%02X\r\n", RxData);
if(retry>200)return 0;
}
HAL_SPI_Transmit(&hspi4, &TxData, 1, HAL_MAX_DELAY); //通过外设SPIx发送一个数据
retry=0;
while (__HAL_SPI_GET_FLAG(&hspi4, SPI_FLAG_RXNE) == RESET) //检查指定的SPI标志位设置与否:接受缓存非空标志位
{
//printf("1111 device_id value: 0x%02X\r\n", RxData);
printf("SPI State: %d\n", hspi4.State);
retry++;
if(retry>200)return 0;
}
HAL_SPI_Receive(&hspi4, &RxData, 1, HAL_MAX_DELAY);
//printf("1111 device_id value: 0x%02X\r\n", RxData);
return RxData; //返回通过SPIx最近接收的数据
}
经过调试,发现是卡在SPI2_ReadWriteByte(uint8_t TxData)函数的第二个循环里了,也就是说标志位__HAL_SPI_GET_FLAG(&hspi4, SPI_FLAG_RXNE) == RESET一直没有置位,说明接收缓存区始终为空。我自己xiel一个ADC采集,来采集SCLK的电压,发现SCLK一直没有波形,而在C8T6上SCLK是有波形的。这到底是为什么?求解答!