赞
踩
项目要求采集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的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 }
基于软件模拟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复位函数如下,根据时序图:
实现如下程序:
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
进行扫描:
根据时序图可知,在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_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 * 功能说明: 时序延迟 * 形 参: 无 * 返 回 值: 无 ********************************************************************************************************* */ 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 }
分辨率是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量程 } }
/*
函 数 名: 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;
}
测量电压范围是-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; }
参考链接
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。