当前位置:   article > 正文

正点原子STM32F407+AD7606+RT-Thread Studio 调试记录_正点原子ad7606

正点原子ad7606

介绍

项目要求采集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

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

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脚设置为高电平 */
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

引脚配置函数

AD7606_ConfigGPIO();        /* 配置GPIO */
  • 1

引脚配置函数如下:包括CS_Pin(片选信号),RANGE_Pin(量程),RESET_Pin(复位),CONVST_Pin(转换),BUSY_Pin(忙)。OS0OS1OS2

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);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

将转换引脚设置为高电平,开启一次转换。

AD7606_CONVST_H;            /* CONVST脚设置为高电平 */
  • 1

初始化完成之后,在主程序中对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();   /* 给开始信号,继续下一次转换 */

    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

软件读取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
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
时序延迟: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
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

处理数据: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量程
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
读取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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

结果打印: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
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

结果显示

未测量时候电压有浮动。
在这里插入图片描述

测量后结果:
通道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;
  
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

参考链接

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/205666
推荐阅读
相关标签
  

闽ICP备14008679号