介绍
项目要求采集6路压力传感器1-5V数据。
经过选型比较,选择AD7606 8通道采样器件,从淘宝买的采样模块。
时序图
说明:图一是整体的一个时序框图,大体的逻辑就是在使用AD7606之前要先复位一下,复位信号是高电平有效,时间至少为50ns。然后就是对采样速率和量程的配置,也就是对OS0,OS1,OS2和RANGE脚的配置,然后再对一些引脚进行一些初始化(也可以直接在GPIO配置的时候进行初始化)。之后就是发送启动信号,也就是将CVA,CVB拉低至少25ns后再拉高(启动信号上升沿有效)。之后AD7606开始转换,BUSY信号线拉高,如果BUSY信号线拉低则表明转换已经完成。转换完成后将CS片选信号线拉低才可以进行数据读取,读取完成后将CS片选信号线拉高即可。
图二是串行通讯对数据进行读取的时序框图,讲的是在AD7606转换完成后将CS片选信号拉低后的操作。转换完成后CS片选信号拉低,开始读取数据。由于是16位8通道ADC,一次读取一个字节,所以一个通道需要读取两次数据。因为是高位在前低位在后所以就是先读取的是MSB,后读取的LSB,数据需要SCLK下降沿有效。经过16*8 = 128个SCLK读取后已经全部将ADC转换的数据全部读取完了,之后就可以将CS片选信号拉高了(由于串行通讯FRSTDATA数据线可以不接,所以并没有用到这个脚)。
图三是对一个字节的读取,顺序也就是现将时钟线拉高后拉低然后读取一下当前的值然后拉高,重复八次就是一个字节的读取。(MSB的最高位为符号位,若为1则数据为负数,若为0则数据为正数)
调试
主控板是正点原子探索者STM32F407ZGT6,环境:RT-Thread Studio 。
从初始化到跑通的整个过程。
首先,控制器和AD7606采用的是软件模拟SPI通信,对软件SPI先进行初始化。
SPI引脚初始化函数:bsp_InitSPIBus();
分别对SPI的MOSI、MISO和SCLK引脚进行初始化。(因为是主机接收从机发送,因此MOSI引脚可以不接)
void bsp_InitSPIBus(void)
{
#ifdef SOFT_SPI /* 软件SPI */
GPIO_InitTypeDef GPIO_InitStruct = {0};
__HAL_RCC_GPIOF_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
/*Configure GPIO pins : PIN_SCK=PF0 PORT_MOSI=PB5*/
GPIO_InitStruct.Pin = PIN_MOSI;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
GPIO_InitStruct.Pin = PIN_SCK;
HAL_GPIO_Init(GPIOF, &GPIO_InitStruct);
/*Configure GPIO pins : PIN_MISO = PF1*/
GPIO_InitStruct.Pin = PIN_MISO;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOF, &GPIO_InitStruct);
#endif
}
AD7606初始化:bsp_spi_InitAD7606();
基于软件模拟SPI对AD7606器件进行初始化。
初始化函数如下所示:其中包含3个函数,分别是引脚配置,硬件复位和开启转换。
*********************************************************************************************************
* 函 数 名: bsp_InitAD7606
* 功能说明: 初始化AD7606 SPI口线
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
void bsp_spi_InitAD7606(void)
{
AD7606_ConfigGPIO(); /* 配置GPIO */
AD7606_RESET(); /* 硬件复位复AD7606 */
AD7606_CONVST_H; /* CONVST脚设置为高电平 */
}
引脚配置函数
AD7606_ConfigGPIO(); /* 配置GPIO */
引脚配置函数如下:包括CS_Pin
(片选信号),RANGE_Pin
(量程),RESET_Pin
(复位),CONVST_Pin
(转换),BUSY_Pin
(忙)。OS0
、OS1
、OS2
。
void AD7606_ConfigGPIO(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOE_CLK_ENABLE();
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOF_CLK_ENABLE();
__HAL_RCC_GPIOH_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
/*Configure GPIO pins : PE2 PE3 PE4 */
GPIO_InitStruct.Pin = GPIO_PIN_2|GPIO_PIN_3|GPIO_PIN_4;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);
/*Configure GPIO pins : RANGE_Pin CS_Pin */
GPIO_InitStruct.Pin = RANGE_Pin|CS_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/*Configure GPIO pins : RESET_Pin CONVST_Pin */
GPIO_InitStruct.Pin = RESET_Pin|CONVST_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
/*Configure GPIO pin : BUSY_Pin */
GPIO_InitStruct.Pin = BUSY_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(BUSY_GPIO_Port, &GPIO_InitStruct);
}
AD7606复位函数:AD7606_RESET(void)
AD7606复位函数如下,根据时序图:
实现如下程序:
void AD7606_RESET(void)
{
AD7606_CS_H;
AD7606_RESET_L;
AD7606_RESET_H;
AD7606_RESET_H;
AD7606_RESET_H;
AD7606_RESET_H;
AD7606_RESET_L;
}
将转换引脚设置为高电平,开启一次转换。
AD7606_CONVST_H; /* CONVST脚设置为高电平 */
初始化完成之后,在主程序中对AD7606
进行扫描:
扫描:AD7606_Scan()
根据时序图可知,在BUSY线信号为低并且片选CS信号为低时,表示一次转换完成。
读取数据的时序是在t4之后的那段时序的细化表现。同时,根据下图所示的串行读取数据操作可看出,SPI的SCLK工作在空间高电平,在sclk第一个边沿读取数据的模式。
//定义 数组接收8路数据
static int16_t s_adc_now[8];
/*函 数 名: AD7606_scan
功能说明: 扫描调用本函数,用于读取AD转换器数据
/* 此函数代码按照时序编写 */
//一次读取8个字节,时序是16字节,分开读取。
//宏定义判断BUSY_IS_LOW是否为低
#define BUSY_IS_LOW() ((GPIOA->IDR & GPIO_PIN_5) == 0)
void AD7606_Scan(void)
{
uint8_t i;
/* BUSY = 0 时.ad7606处于空闲状态ad转换结束 */
if (BUSY_IS_LOW())
{
AD7606_CS_L; /* SPI片选 = 0 */
for (i = 0; i < 8; i++)
{
s_adc_now[i] = bsp_spiRead1();
s_adc_now[i] = s_adc_now[i] * 256 + bsp_spiRead1(); /* 读数据 */
}
AD7606_CS_H; /* 读取数据完成之后,拉高SPI片选 = 1 */
AD7606_STARTCONV(); /* 给开始信号,继续下一次转换 */
}
}
软件读取SPI函数:
读取AD7606的采样结果:ad7606_ReadBytes
/*
*********************************************************************************************************
* 函 数 名: ad7606_ReadBytes
* 功能说明: 读取AD7606的采样结果
* 形 参:
* 返 回 值: 无
*********************************************************************************************************
*/
uint8_t bsp_spiRead1(void)
{
#ifdef SOFT_SPI /* 软件SPI */
uint8_t i;
uint8_t read = 0;
for (i = 0; i < 8; i++)
{
SCK_0();
bsp_spiDelay();
read = read << 1;
if (MISO_IS_HIGH())
{
read++;
}
SCK_1();
bsp_spiDelay();
}
return read;
#endif
}
时序延迟:bsp_SpiDelay()
通过循环实现物理延迟。
/*
*********************************************************************************************************
* 函 数 名: bsp_SpiDelay
* 功能说明: 时序延迟
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
void bsp_spiDelay(void)
{
#if 1
uint32_t i;
/*
延迟5时, F407 (168MHz主频)GPIO模拟,实测 SCK 周期 = 480ns (大约2M)
*/
for (i = 0; i < 5; i++);
#else
/*
不添加延迟语句, F407 (168MHz主频)GPIO模拟,实测 SCK 周期 = 200ns (大约5M)
*/
#endif
}
处理数据:AD7606_Mak()
分辨率是16位,因此除以16。
/*
函 数 名: AD7606_Mak
功能说明: 处理采样后的数据
形 参:无
返 回 值: 无
*/
static int16_t s_dat[8];
static int16_t s_volt[8];
void AD7606_Mak(void)
{
uint8_t i;
int16_t adc;
for (i = 0;i < 8; i++)
{
s_dat[i] = AD7606_ReadAdc(i);
adc = s_dat[i];
s_volt[i] = (adc * 10000) / 32767;//10V量程
}
}
读取ADC函数:AD7606_ReadAdc(uint8_t _ch)
/*
函 数 名: AD7606_ReadAdc
功能说明:从FIFO中读取一个ADC值
形参:_ch
返 回 值: adc数据
*/
int16_t AD7606_ReadAdc(uint8_t _ch)
{
int16_t sAdc;
DISABLE_INT();//关闭全部中断
sAdc = s_adc_now[_ch];
ENABLE_INT();//开启全部中断
return sAdc;
}
结果打印:AD7606_Disp();
测量电压范围是-5V到+5V。
/*
函 数 名: AD7606_Disp
功能说明: 显示采样后的数据
形 参:无
返 回 值: 无
*/
void AD7606_Disp(void)
{
int16_t i;
int16_t iTemp;
/* 打印采集数据 */
for (i = 0; i < 8; i++)
{
iTemp = s_volt[i]; /* uV */
if (s_dat[i] < 0)
{
iTemp = -iTemp;
rt_kprintf(" CH%d = %6d,0x%04X (-%d.%d%d%d V) \r\n", i+1, s_dat[i], (uint16_t)s_dat[i], iTemp /1000, (iTemp%1000)/100, (iTemp%100)/10,iTemp%10);
}
else
{
rt_kprintf(" CH%d = %6d,0x%04X ( %d.%d%d%d V) \r\n", i+1, s_dat[i], s_dat[i] , iTemp /1000, (iTemp%1000)/100, (iTemp%100)/10,iTemp%10);
}
}
}
结果显示
未测量时候电压有浮动。
测量后结果:
通道1接3.3V电源。
通道2接3.3V电源。
测量结果稳定。
展望
卡尔曼滤波
在对数据进行采集之后,添加卡尔曼滤波,对数据信息过滤处理。
#define KalMan_Q 1
#define KalMan_R 1000
float Kalman(float z)
{
static float x_1;
float x_mid = x_1;
float x_now;
static float p_1;
float p_mid = p_1;
float p_now;
float k;
x_mid = x_1;
p_mid = p_1 +KalMan_Q;
k = p_mid/(p_mid+KalMan_R);
x_now = x_mid+k*(z-x_mid);
p_now = (1-k)*p_mid;
p_1 = p_now;
x_1 = x_now;
return x_now;
}
参考链接