【本文发布于https://blog.csdn.net/Stack_/article/details/116331897,未经许可不得转载,转载须注明出处】
一、电路
外部运放电路为电压跟随器,在ADS1158的手册内也有此电路。如果16路模拟信号直连AD转换器,转换结果和实际电压偏差大,尝试将ADS1158寄存器位[BYPAS]设为1(附件工程内搜索DATA_CONFIG0更改配置),AN0-AN15以及AINCOM信号将经过外部电压跟随器后再回到ADS1158内部进行AD转换,可有效改善。
二、SPI
根据时序配置SPI
@ CSDN Tyrion.Mon
/**
* @brief 初始化SPI0
* @note SPI0_NSS -- PA4
* SPI0_SCK -- PA5
* SPI0_MI -- PA6
* SPI0_MO -- PA7
* @param None
* @retval None
* @author PWH
* @date 2021/3
*/
void SPI0_Init(void)
{
RCU->RCU_APB2EN.Bits.SPI0EN = 1; //
/* SCK复用输出 */
Gd32f30x_Gpio_Init(GPIO_PA5, GPIO_MODE_OUT_50MHZ, GPIO_CTL_AF_PP);
/* MI上拉输入 */
Gd32f30x_Gpio_Init(GPIO_PA6, GPIO_MODE_IN, GPIO_CTL_IPU);
/* MO复用输出 */
Gd32f30x_Gpio_Init(GPIO_PA7, GPIO_MODE_OUT_50MHZ, GPIO_CTL_AF_PP);
/* SPI0 */
//1:主机模式
SPI0->SPI_CTL0.Bits.MSTMOD = 1;
//0: 2 线单向传输模式
SPI0->SPI_CTL0.Bits.BDEN = 0;
//0:全双工模式(当 BDEN 清零时)
SPI0->SPI_CTL0.Bits.RO = 0;
//0: 8 位数据帧格式
SPI0->SPI_CTL0.Bits.FF16 = 0;
//1: NSS 软件模式, NSS 电平取决于 SWNSS 位
SPI0->SPI_CTL0.Bits.SWNSSEN = 1;
SPI0->SPI_CTL0.Bits.SWNSS = 1;
//SPI0时钟来源:APB2 psc=011 : 16分频
//120M/16=7.5<(ADS1158 16M外部晶振时,SPI最高速度8M)
SPI0->SPI_CTL0.Bits.PSC = 3;
//0:先发送最高有效位
SPI0->SPI_CTL0.Bits.LF = 0;
//0: SPI 为空闲状态时, CLK 引脚拉低
SPI0->SPI_CTL0.Bits.CKPL = 0;
//0:在第1个时钟跳变沿采集第一个数据
SPI0->SPI_CTL0.Bits.CKPH = 0;
//1: RBNE 中断使能。当 RBNE 置位时,产生中断。
// SPI0->SPI_CTL1.Bits.RBNEIE = 1;
//1: SPI 设备使能
SPI0->SPI_CTL0.Bits.SPIEN = 1;
}
三、命令字节
@ CSDN Tyrion.Mon
/* 命令字节的bits[7:5] 表示命令类型 */
#define COMMAND_CH_DATA_DIRECT 0x00 //直接读取通道数据
#define COMMAND_CH_DATA_COMMAND 0x20 //命令读取通道数据
#define COMMAND_READ_REGISTER 0x40 //读寄存器
#define COMMAND_WRITE_REGISTER 0x60 //写寄存器
#define COMMAND_PULSE_CONVERT 0x80 //开始转换,此命令bit[4:0]无效
#define COMMAND_RESEVED 0xA0 //保留
#define COMMAND_RESET 0xC0 //reset,此命令bit[4:0]无效
//#define COMMAND_CH_DATA_DIRECT 0xE0 //和0x00↑一样
/* 命令字节的bits[4] */
#define ENABLE_MULTIPLE_REG_ACCESS 0x10 //启用多寄存器访问
#define DISABLE_MULTIPLE_REG_ACCESS 0x00 //禁用多寄存器访问
/* 命令字节的bits[3:0] 表示访问的寄存器地址 */
#define REG_ADDR_CONFIG0 0x00 //配置寄存器0
#define REG_ADDR_CONFIG1 0x01 //配置寄存器1
#define REG_ADDR_MUXSCH 0x02 //多路复用固定通道寄存器
#define REG_ADDR_MUXDIF 0x03 //多路复用器差分输入选择寄存器
#define REG_ADDR_MUXSG0 0x04 //多路复用单端输入选择寄存器0
#define REG_ADDR_MUXSG1 0x05 //多路复用单端输入选择寄存器1
#define REG_ADDR_SYSRED 0x06 //系统读取选择寄存器
#define REG_ADDR_GPIO_CONFIG 0x07 //GPIO配置寄存器(输入输出配置)
#define REG_ADDR_GPIO_DATA 0x08 //GPIO数据寄存器(置位、清零、读状态)
#define REG_ADDR_ID 0x09 //出厂ID
/* 软/命令复位 bit[4:0]无效 */
#define COMMAND_SOFE_RESET COMMAND_RESET
/* 软 脉冲触发单次转换 bit[4:0]无效 */
#define COMMAND_SOFE_PULSE_CONVERT COMMAND_PULSE_CONVERT
/* 命令读通道数据 bit[3:0]无效 */
#define COMMAND_CH_DATA_READ (COMMAND_CH_DATA_COMMAND | ENABLE_MULTIPLE_REG_ACCESS)
读单个寄存器时,发送COMMAND_READ_REGISTER | DISABLE_MULTIPLE_REG_ACCESS | 寄存器地址,再发送任意一个字节,即可获得该寄存器值;
写单个寄存器时,发送COMMAND_WRITE_REGISTER | DISABLE_MULTIPLE_REG_ACCESS | 寄存器地址,再发送配置值,即可更改该寄存器的值。
四、寄存器
CONFIG0 | @ CSDN Tyrion.Mon |
---|---|
BIT5 | 0:自动扫描模式;1:固定通道模式。在这里配为自动扫描 |
BIT4 | 0:AD转换器直接从16个通道采样;1:如下图,16个通道模拟量从MUXOUTP和MUXOUTN输出,经外部电路(如果设计有)处理过后,输入到ADCINP和ADCINN再到AD转换器。适用于需要放大输入信号或者对信号作其它处理的情况,可以节省硬件电路。 |
BIT3 | 0:使用外部晶振;1:晶振引脚输出时钟 |
BIT2 | 未明白,可维持默认值 |
BIT1 | 0:转换完成后读取AD值时伴随表示数据状态的一个字节 |
CONFIG1 | @ CSDN Tyrion.Mon |
---|---|
BIT7 | 如果对耗电没有要求,选择待机模式,理论上从待机模式下启动转换会快些 |
BIT[6:4] | 不是很明白,保持默认即可 |
BIT[3:2] | 不是很明白,保持默认即可 |
BIT[1:0] | 对转换速度没有要求就选最大值,转换速度选择越慢的越精确 |
MUXSCH | @ CSDN Tyrion.Mon |
---|---|
BIT[7:0] | 在固定通道模式下使用,测量有正有负的电压,参考电源须由5V - 0V改为+2.5V - -2.5V。AINPx指定电压对的正压通道,AINNx指定电压对的负压通道。(懵逼,不知道怎么用) |
MUXDIF | |
---|---|
BIT[7:0] | AIN0置位时,AIN0和AIN1组成一对差分信号对,电压为两者差值; AIN1置位时,AIN2和AIN3组成一对差分信号对;…在自动扫描模式下有效,未选中的在一轮扫描中会被跳过。 |
MUXSG0 | @ CSDN Tyrion.Mon |
---|---|
BIT[7:0] | AIN0置位时,通道AIN0被选中为单端模式,电压为AIN0和AINCOM的差值;AIN1置位时,通道AIN1被选中为单端模式,电压为AIN1和AINCOM的差值。…在自动扫描模式下有效,,未选中的在一轮扫描中会被跳过。 |
MUXSG1 | @ CSDN Tyrion.Mon |
---|---|
BIT[7:0] | AIN0置位时,通道AIN8被选中为单端模式,电压为AIN8和AINCOM的差值。…在自动扫描模式下有效,未选中的在一轮扫描中会被跳过。 |
SYSRED | @ CSDN Tyrion.Mon |
---|---|
BIT[7:0] | 未明 |
五、开启转换
在上电不进行配置时,ADS1158会自动扫描所有通道,用示波器能看到DRDY引脚有波形输出。
1.持续转换: 配置好ADS1158后,把START脚一直拉高,会自动扫描选中的所有通道。扫描完一个通道并准备好从SPI输出数据时,会拉低DRDY引脚一段时间,然后拉高继续转换下一通道,转换完成再次拉低DRDY。如果没能及时将数据读出来,将会导致数据被覆盖;2. 脉冲转换:配置好ADS1158后,把START拉高立刻又拉低,ADS1158将只转换当前通道,转换完成后拉低DRDY,并指向下一通道。而后进入待机或睡眠模式。
六、数据读取
将转换模式配置为脉冲转换:START脚触发转换,DRDY引脚变低后读取三字节通道数据。不断循环此操作
以第一个字节的bit7判断数据是否有效:为1时为新/已更新数据。
以第一个字节的bit[4:0]判断当前数据为哪个通道的数据。
七、数据解析
第二、第三个字节的数据需要根据公式进行转换为电压。.. ..
@ CSDN Tyrion.Mon
/**
* @brief 向一个寄存器写一个字节
* @note
* @param None
* @retval None
* @author PWH
* @date 2021/3
*/
static uint16_t Ads1158_WriteOneReg(uint8_t Reg, uint8_t Byte)
{
Ads1158_Select(CS_ENABLE);
SPIx_SendData(SPI0, COMMAND_WRITE_REGISTER | DISABLE_MULTIPLE_REG_ACCESS | Reg);
SPIx_SendData(SPI0, Byte);
Ads1158_Select(CS_DISABLE);
return 0;
}
/**
* @brief ADS 读寄存器
* @note
* @param None
* @retval
* @author PWH
* @date 2021/3
*/
static uint16_t Ads1158_ReadOneReg(uint8_t Reg)
{
uint16_t rec = 0;
Ads1158_Select(CS_ENABLE);
rec = SPIx_SendData(SPI0, COMMAND_READ_REGISTER | DISABLE_MULTIPLE_REG_ACCESS | Reg);
rec = SPIx_SendData(SPI0, 0x00);
Ads1158_Select(CS_DISABLE);
// printf("Reg = %#x\r\n", rec);
return rec;
}
/**
* @brief ADS软件复位
* @note
* @param None
* @retval None
* @author PWH
* @date 2021/3
*/
static void Ads1158_Reset(void)
{
Ads1158_Select(CS_ENABLE);
SPIx_SendData(SPI0, COMMAND_SOFE_RESET);
Ads1158_Select(CS_DISABLE);
}
/**
* @brief ADS 读ID寄存器进行通讯测试
* @note
* @param None
* @retval
* @author PWH
* @date 2021/3
*/
static int8_t Ads1158_CommTest(void)
{
if ((uint8_t)(Ads1158_ReadOneReg(REG_ADDR_ID)) == DEVICE_ID)
return 1;
return -1;
}
/**
* @brief 初始化ADS1158
* @note SPI -- SPI0
* CS -- PA4
* START-- PB2
* DRDY -- PF11
* RST -- PF12
* @param None
* @retval None
* @author PWH
* @date 2021/3
*/
void Ads1158_Init(void)
{
/* CS推挽输出 */
Gd32f30x_Gpio_Init(ADS1158_CS_PIN, GPIO_MODE_OUT_50MHZ, GPIO_CTL_OUT_PP);
Gd32f30x_Gpio_Write(ADS1158_CS_PIN, GPIO_SET);
/* START 推挽输出 */
Gd32f30x_Gpio_Init(ADS1158_START_PIN, GPIO_MODE_OUT_50MHZ, GPIO_CTL_OUT_PP);
Gd32f30x_Gpio_Write(ADS1158_START_PIN, GPIO_RESET);
/* DRDY(ADS1158数据准备好输出)上拉输入 */
Gd32f30x_Gpio_Init(ADS1158_DRDY_PIN, GPIO_MODE_IN, GPIO_CTL_IPU);
/* RST 推挽输出 */
Gd32f30x_Gpio_Init(ADS1158_RST_PIN, GPIO_MODE_OUT_50MHZ, GPIO_CTL_OUT_PP);
Gd32f30x_Gpio_Write(ADS1158_RST_PIN, GPIO_RESET);
SPI0_Init();
Gd32f30x_Gpio_Write(ADS1158_RST_PIN, GPIO_SET);
Ads1158_Reset();
while (Ads1158_CommTest() != 1);
//CONFIG0
Ads1158_WriteOneReg(REG_ADDR_CONFIG0, DATA_CONFIG0);
//CONFIG1
Ads1158_WriteOneReg(REG_ADDR_CONFIG1, DATA_CONFIG1);
//MUXSG0
Ads1158_WriteOneReg(REG_ADDR_MUXSG0, DATA_MUXSG0);
//MUXSG1
Ads1158_WriteOneReg(REG_ADDR_MUXSG1, DATA_MUXSG1);
//GPIO
Ads1158_WriteOneReg(REG_ADDR_GPIO_CONFIG, DATA_GPIO_CONFIG);
if (Ads1158_ReadOneReg(REG_ADDR_CONFIG0) != DATA_CONFIG0)
{
while (1);
}
if (Ads1158_ReadOneReg(REG_ADDR_CONFIG1) != DATA_CONFIG1)
{
while (1);
}
if (Ads1158_ReadOneReg(REG_ADDR_MUXSG0) != DATA_MUXSG0)
{
while (1);
}
if (Ads1158_ReadOneReg(REG_ADDR_MUXSG1) != DATA_MUXSG1)
{
while (1);
}
if (Ads1158_ReadOneReg(REG_ADDR_GPIO_CONFIG) != DATA_GPIO_CONFIG)
{
while (1);
}
}
/**
* @brief 检测到DRDY引脚下降沿后读取通道数据
* @note
* @param None
* @retval None
* @author PWH
* @date 2021/3
*/
void Ads1158_ReadChannelDataWithoutCmd(uint8_t *pBuff, uint8_t len)
{
uint8_t i = 0;
Ads1158_Select(CS_ENABLE);
for (i = 0; i < len; i++)
{
*pBuff++ = SPIx_SendData(SPI0, 0x00);
}
Ads1158_Select(CS_DISABLE);
}
/**
* @brief ADS开始转换
* @note
* @param None
* @retval None
* @author PWH
* @date 2021/3
*/
void Ads1158_StartStopConvert(uint8_t StartStop)
{
if (StartStop == CONVERT_START)
{
Gd32f30x_Gpio_Write(ADS1158_START_PIN, GPIO_SET);
}
else
{
Gd32f30x_Gpio_Write(ADS1158_START_PIN, GPIO_RESET);
}
}
/**
* @brief ADS数据是否准备输出
* @note
* @param None
* @retval 0 -- ready
* @author PWH
* @date 2021/3
*/
int8_t Ads1158_DataReady(void)
{
if (Gd32f30x_Gpio_Read(ADS1158_DRDY_PIN) == DATA_READY)
return DATA_READY;
return -1;
}
@ CSDN Tyrion.Mon
static uint8_t Ads1158_ReadComplete = true; // 1 表示已读取通道
uint16_t Ads1158_ChConvertData[16] = {0x00}; // 通道转换的原始数据
uint32_t Ads1158_ChRealVoltage[16] = {0x00}; // 通道电压(根据原始数据转换)
uint32_t Ads1158_AD[16] = {0x00}; // 通道AD(根据电压数据转换)
/**
* @brief
* @note
* @param None
* @retval None
* @author PWH
* @date 2021/4
*/
void Ads1158_Proc(void)
{
uint8_t i;
static uint32_t ads_time = 0;
uint8_t array[3];
/* 启动转换 */
if ((Ads1158_ReadComplete == true) && (Ads1158_DataReady() != DATA_READY))
{
Ads1158_ReadComplete = false;
Ads1158_StartStopConvert(CONVERT_START); // 脉冲触发转换,只转换一个通道
Ads1158_StartStopConvert(CONVERT_STOP); //
}
if (UserTimer_Read(&ads_time) > 5000) //超时
{
printf(" ADS ERROR RESET\r\n ");
UserTimer_Reset(&ads_time);
Ads1158_ReadComplete = true;
}
/* 读取数据 */
if ((Ads1158_ReadComplete == false) && (Ads1158_DataReady() == DATA_READY))
{
/* 读取转换数据 */
Ads1158_ReadChannelDataWithoutCmd(array, 3);
if (array[0] & 0x80) //数据已刷新
{
array[0] &= 0x1f; //bit4-bit0表示当前读回的数据所属通道
if ( (array[0] >= DATA_AIN0) && (array[0] <= DATA_AIN15) )
{
Ads1158_ChConvertData[array[0] - DATA_AIN0] = ((uint16_t)array[1]) << 8 | array[2];
Ads1158_ChRealVoltage[array[0] - DATA_AIN0] = (uint32_t)(Ads1158_ChConvertData[array[0] - DATA_AIN0] * REFERENCE_VOLTAGE / 30720.0);
Ads1158_AD[array[0] - DATA_AIN0] = Ads1158_ChRealVoltage[array[0] - DATA_AIN0] * 65536.0 / REFERENCE_VOLTAGE;
}
if (array[0] == DATA_AIN15)
{
UserTimer_Reset(&ads_time);
}
Ads1158_ReadComplete = true; //表示已读取通道
}
}
}