参考电路,其中Re为200欧高精度电阻。

AD7793的SPI工作方式第二个edge数据有效,空闲为高电平。另外一个需要注意的是AD7793的数据收发是高字节到低字节,STM32的SPI是从低字节到高字节发送的,所以在编写SPI_Write和SPI_Read的时候需要把数据高低字节交换位置。注意分清高低位和高低字节。


/**
  * @brief  从高字节到低字节发送数据到从机。
  * @param  data 要发送的数据的指针。
  * @param  bytesNumber 发送多少个字节到从机。
  */
void SPI_Write(uint8_t* data, uint8_t bytesNumber){
  uint8_t tmpData[bytesNumber];
  
  memcpy(tmpData, data, bytesNumber);
  for(int i=0; i<bytesNumber/2; i++){
    tmpData[i] ^= tmpData[bytesNumber-1-i];
    tmpData[bytesNumber-1-i] ^= tmpData[i];
    tmpData[i] ^= tmpData[bytesNumber-1-i];
  }
  
  ADI_PART_CS_LOW;
  HAL_SPI_Transmit(&hspi1, tmpData, bytesNumber, 0xFF);
  ADI_PART_CS_HIGH;
}

注意N个数据只需要交换N/2次,使用 ^= 可以完成交换,同样也可以使用 -=、+= 或者设置中间变量来完成。最好不要去对参数中的data进行交换,由于传入的是指针,所以data会被修改,保险方式是复制到局部变量进行操作。

/**
  * @brief  从从机从高字节到低字节接收数据。
  * @param  data 接收数据保存的指针。
  * @param  bytesNumber 要接收的字节数。
  */
void SPI_Read(uint8_t* data, uint8_t bytesNumber){
  ADI_PART_CS_LOW;
  HAL_SPI_Receive(&hspi1, data, bytesNumber, 0xFF);
  ADI_PART_CS_HIGH;

  for(int i=0; i<bytesNumber/2; i++){
    data[i] ^= data[bytesNumber-1-i];
    data[bytesNumber-1-i] ^= data[i];
    data[i] ^= data[bytesNumber-1-i];
  }
}

同理SPI_Read也进行交换。以下是AD7793操作的函数。在编写AD7793驱动的时候,使用了比较多的枚举enum来表示某些寄存器参数。你也可以根据喜好修改为宏定义。

/**
  * @brief  写AD7793指定寄存器。
  * @param  regSel 要写入的数据的寄存器。
  * @param  regVal 要写入的寄存器的数据结构体指针。
  */
void AD7793_WriteReg(RegSel regSel, RegValTypeDef *regVal){
  uint8_t regSize;
  uint8_t comRegVal;
  uint8_t regSelVal;
  
  comRegVal = (uint8_t)(regSel)<<3;
  regSelVal = (uint8_t)regSel;
  switch(regSelVal){
    case ComReg:
    case IOReg:
      regSize = 1;
    break;
    case ModeReg:
    case ConfReg:
      regSize = 2;
    break;
    case OffsetReg:
    case FScaleReg:
      regSize = 3;
  }
  
  SPI_Write(&comRegVal, 1);
  SPI_Write(regVal->reg8, regSize);
}

由于AD7793的寄存器大小有1、2、3字节的,在对AD7793寄存器的值保存时使用了联合体变量。在读写寄存器的时候,只需要选择对应寄存器,并且给数据联合体,不需要指定寄存器大小,在函数内使用了switch自动判断字节数。AD7793在使用之前需要先复位,复位的条件是使DIN保持高电平至少32个时钟,保险起见保持了40个时钟。

/**
  * @brief  复位AD7793。
  */
void AD7793_Reset(void){
  uint8_t dataToSend[5] = {0xff, 0xff, 0xff, 0xff, 0xff};
  SPI_Write(dataToSend,5);
}

AD7793有一个ID寄存器,该寄存器为只读,可以判断AD7793是否工作,也适用于AD7792,两者ID略有不同。ID寄存器的高4为为无效值,只需要低四位。

/**
  * @brief  读AD7793的ID。
  * @retval ID值。
  */
uint8_t AD7793_GetID(void){
  RegValTypeDef reg;
  AD7793_ReadReg(IDReg, &reg);
  return reg.reg8[0]&0x0F;
}

AD7793有多种方式来判断ADC数据是否有效,1.查询状态寄存器的RDY位,2.查询DOUT引脚电平,3.将RDY作为中断。由于使用的是硬件SPI,不太便于使用2、3方式。

/**
  * @brief  等待数据寄存器有效。
  */
void AD7793_WaitDataReady(){
  RegValTypeDef reg;
  
  AD7793_ReadReg(StatusReg, &reg);
  while(reg.reg8[0] & STATUS_BIT){
    AD7793_ReadReg(StatusReg, &reg);
  }
}

以上是查询状态寄存器的RDY位来判断数据是否有效的方法。以下是使用四线PT100采样计算温度的例子。

/**
  * @brief  对电阻采样进行设置。
  */
void Sensor_SetForRes(){
  AD7793_SetCurrent(DirSequential, CurrentIEXC1_210u);
  AD7793_SelectClock(Internal64NoCLK);
  AD7793_SetGain(GAIN_1);
  AD7793_SetBuff(BuffOff);
  AD7793_SetInputMode(InputUnipolar);
  AD7793_SetReferenceVoltage(ExternalRefVolt);
  AD7793_SetChannel(InputAIN1P_AIN1N);
}

/**
  * @brief  获取单次采样值。
  * @retval 采样计算后的电阻值。
  */
double Sensor_GetRes(){
  double resistor;
  uint32_t adcVal;
  RegValTypeDef reg;
  uint8_t gain;
  
  AD7793_ReadReg(ConfReg, &reg);
  gain = 0x01 << (reg.reg8[1] & 0x07);
  
  AD7793_SetMode(ModeSingle);
  adcVal = AD7793_GetData();
  
  resistor = (double)adcVal*REF_RESISTOR/(gain*0x1000000);
  return resistor;
}

/**
  * @brief  根据PT100电阻值计算温度。
  * @retval 温度值。
  */
double Sensor_CalculateTemperature(double res){
  double TmTemp=0, PtTemp = 0;

  TmTemp = ParamA*ParamA-4*(ParamB)*(1-res/100.0);
  PtTemp = (-ParamA+sqrt(TmTemp))/(2*(ParamB));

  return PtTemp;
}

那么在主函数中,可以按照以下方法单次读取温度。

double res;
double temp;
void main(){
  AD7793_Reset();
  Sensor_SetForRes();
  res = Sensor_GetRes();
  temp = Sensor_CalculateTemperature(res);
  while(1);
}

如果需要连续读取温度,可以使用连续采集模式,或者重复使用单次采集。

double res;
double temp;
void main(){
  AD7793_Reset();
  Sensor_SetForRes();
  while(1){
    res = Sensor_GetRes();
    temp = Sensor_CalculateTemperature(res);
  }
}

驱动源码参考:

https://flomen.lanzoub.com/iA2cw0usmr0d