【STM32F429开发板用户手册】第36章 STM32F429的FMC总线应用之DMA双缓冲驱动AD7606(8通道同步采样, 16bit, 正负10V)

最新教程下载:http://www.armbbs.cn/forum.php?mod=viewthread&tid=93255

第36章       STM32F429的FMC总线应用之DMA双缓冲驱动AD7606(8通道同步采样, 16bit, 正负10V)

本章节为大家讲解FMC DMA双缓冲方式驱动数模转换器AD7606,实战性较强。

36.1 初学者重要提示

36.2 ADC结构分类

36.3 AD7606硬件设计

36.4 AD7606关键知识点整理(重要)

36.5 AD7606的FMC DMA实现思路

36.6 AD7606的FMC接口硬件设计

36.7 AD7606的FMC接口驱动设计

36.8 AD7606板级支持包(bsp_fmc_ad7606)

36.9 J-Scope实时展示AD7606采集数据说明

36.10 AD7606驱动移植和使用

36.11 实验例程设计框架

36.12 实验例程说明(MDK)

36.13 实验例程说明(IAR)

36.14 总结

 

 

36.1 初学者重要提示

  1.   学习本章节前,务必优先学习第35章,本章是建立在35章的基础上。
  2.   本章36.6小节的知识点对于本章的理解尤其重要。
  3.   AD7606 的配置很简单,它没有内部寄存器,量程范围和过采样参数是通过外部IO控制的,采样速率由MCU或DSP提供的脉冲频率控制。
  4.   AD7606必须使用单5V供电。而AD7606和MCU之间的通信接口电平由VIO(VDRIVE)引脚控制。也就是说VIO必须接单片机的电源,可以是3.3V也可以是5V(范围2.3V – 5V)。
  5.   正确的理解过采样,比如我们设置是1Ksps采样率,64倍过采样。意思是指每次采样,AD7606会采样64次数据并求平均,相当于AD7606以64Ksps进行采样的,只是将每64个采样点的值做了平均,用户得到的值就是平均后的数值。因此,如果使用AD7606最高的200Ksps采样率,就不可以使用过采样了。
  6.   STM32驱动AD7606配合J-Scope实时输出,效果绝了,堪比示波器http://www.armbbs.cn/forum.php?mod=viewthread&tid=97393 。使用方法详解本章节36.9小节。
  7.   本章配套例子的串口数据展示推荐使用SecureCRT,因为数据展示做了特别处理,方便采集数据在串口软件同一个位置不断刷新。
  8.   AD7606数据手册,模块原理图(通用版)和接线图都已经放到本章教程配置例子的Doc文件里。
  9.   ADC 的专业术语诠释文档,推荐大家看看:http://www.armbbs.cn/forum.php?mod=viewthread&tid=89414 。
  10.   测试本章配套例子前重要提示:
  •   测试时,务必使用外置电源为开发板供电,因为AD7606需要5V供电电压。板子上插入AD7606模块时,注意对齐。
  •   板子上电后,默认是100Ksps,2倍过采样。
  •   如果使用的JLINK速度不够快,导致J-Scope无法最高速度实时上传,可以使用摇杆上下键设置过采样来降低上传速度。
  •   默认情况下,程序仅上传了AD7606通道1采集的数据。

36.2 ADC结构分类

36.3 AD7606硬件设计

36.4 AD7606关键知识点整理(重要)

36.5 AD7606的FMC接口硬件设计

36.2,36.3,36.4和36.5小节的知识在第35章节有详细说明,本章不再赘述。

36.6 AD7606的FMC DMA实现思路

FMC的并行接线方式如下:

【STM32F429开发板用户手册】第36章 STM32F429的FMC总线应用之DMA双缓冲驱动AD7606(8通道同步采样, 16bit, 正负10V)_第1张图片 

这里实现FMC DMA方式的关键就是BUSY引脚去触发DMA控制,如果是单纯的DMA正常模式,实现比较简单,接收到INT引脚的就绪状态,使用FMC DMA将8路数据全部读取出来即可。

难点在于驱动AD7606不像SRAM,SDRAM,仅需一个FMC接口就行,它还需要一个独立的时钟引脚,每次时钟触发要连续读取8次数据。实现这套方案有如下几点:

36.6.1 定时器PWM输出控制AD7606转换

通过定时器PWM输出控制AD7606转换比较容易实现,我们上一个章节就是这种方式控制的。

36.6.2 定时器UP更新事件触发DMA实现突发传输

有了定时器PWM控制AD7606转换。还需要保证每个PWM脉冲读取一次数据,而且是连续读取8路。这就需要用到下面两个知识点,非常关键:

  •   同时开启同一个定时器的PWM输出和UP更新事件。

这样可以保证每个PWM后都配有一个UP更新,通过UP更新来触发DMA传输。

  •   DMA突发功能实现每次触发连续读取8路数据。

STM32H7支持的突发方式如下,下面这个表格尤其重要,配置突发务必要按照这个表格来配置:

 【STM32F429开发板用户手册】第36章 STM32F429的FMC总线应用之DMA双缓冲驱动AD7606(8通道同步采样, 16bit, 正负10V)_第2张图片

我们要实现的是连续读取8路16bit数据,上面表格中红色方框部分刚好支持。

36.6.3 不使用BUSY引脚如何保证读取正确的数据

解决这个问题的关键就是AD7606支持转换期间读取:

 【STM32F429开发板用户手册】第36章 STM32F429的FMC总线应用之DMA双缓冲驱动AD7606(8通道同步采样, 16bit, 正负10V)_第3张图片

这个功能正好用在本设计中,这里有四个关键时序参数:

  •   t2

表示最短的CONVST低电平脉冲,最小值25ns,这个时间我们用PWM脉冲低电平控制。

  •   t3

表示最短的CONVST高电平脉冲,最小值25ns,这个时间我们用PWM脉冲高电平控制。

  •   t6

表示CS上升沿和BUSY下降沿之间的最长时间,最大值25ns。这个参数的主要作用是限制用户一定要在BUSY转换有效之前立即读取。

  •   tconv

表示AD7606转换时间,对于AD7606-8来说,最小值范围是3.45us到4.15us。

 【STM32F429开发板用户手册】第36章 STM32F429的FMC总线应用之DMA双缓冲驱动AD7606(8通道同步采样, 16bit, 正负10V)_第4张图片

有了这三个参数,配置PWM的占空比就比较考究了,我们仅需配置好PMW低电平宽度,将其设置为接近于25ns的低电平脉宽时间,其余时间全是高电平即可。这样我们就保证了每次脉冲立即读取上一次的转换数据。

36.6.4 FMC DMA双缓冲实现

DMA双缓冲的实现比较简单,我们借助DMA半传输完成中断和DMA传输完成中断即可。其中半传输完成中断就是DMA数据传输完成一半的中断。

36.7 AD7606的FMC接口驱动设计

AD7606的程序驱动框架设计如下:

【STM32F429开发板用户手册】第36章 STM32F429的FMC总线应用之DMA双缓冲驱动AD7606(8通道同步采样, 16bit, 正负10V)_第5张图片

有了这个框图,程序设计就比较好理解了。

36.7.1 第1步,AD7606所涉及到的GPIO配置

这里需要把用到的GPIO时钟、FMC时钟、GPIO引脚和复用配置好即可:

/*
*********************************************************************************************************
*    函 数 名: AD7606_CtrlLinesConfig
*    功能说明: 配置GPIO口线,FMC管脚设置为复用功能
*    形    参: 无
*    返 回 值: 无
*********************************************************************************************************
*/
/*
    安富莱STM32-V6开发板接线方法:
    PD0/FMC_D2
    PD1/FMC_D3
    PD4/FMC_NOE        ---- 读控制信号,OE = Output Enable , N 表示低有效
    PD5/FMC_NWE        -XX- 写控制信号,AD7606 只有读,无写信号
    PD8/FMC_D13
    PD9/FMC_D14
    PD10/FMC_D15
    PD14/FMC_D0
    PD15/FMC_D1

    PE7/FMC_D4
    PE8/FMC_D5
    PE9/FMC_D6
    PE10/FMC_D7
    PE11/FMC_D8
    PE12/FMC_D9
    PE13/FMC_D10
    PE14/FMC_D11
    PE15/FMC_D12
    
    PG0/FMC_A10        --- 和主片选FMC_NE2一起译码
    PG1/FMC_A11        --- 和主片选FMC_NE2一起译码
    PG9/FMC_NE2        --- 主片选(TFT, OLED 和 AD7606)    
*/

/* 
    控制AD7606参数的其他IO分配在扩展的74HC574上
    D13 - AD7606_OS0
    D14 - AD7606_OS1
    D15 - AD7606_OS2
    D24 - AD7606_RESET
    D25 - AD7606_RAGE    
*/
static void AD7606_CtrlLinesConfig(void)
{
    /* bsp_fm_io 已配置fmc,bsp_InitExtIO();
       此处可以不必重复配置FMC,其它的要配置。
    */
    GPIO_InitTypeDef gpio_init_structure;

    /* 使能 GPIO时钟 */
    __HAL_RCC_GPIOD_CLK_ENABLE();
    __HAL_RCC_GPIOE_CLK_ENABLE();
    __HAL_RCC_GPIOF_CLK_ENABLE();
    __HAL_RCC_GPIOG_CLK_ENABLE();
    __HAL_RCC_GPIOH_CLK_ENABLE();
    __HAL_RCC_GPIOI_CLK_ENABLE();

    /* 使能FMC时钟 */
    __HAL_RCC_FMC_CLK_ENABLE();

    /* 设置 GPIOD 相关的IO为复用推挽输出 */
    gpio_init_structure.Mode = GPIO_MODE_AF_PP;
    gpio_init_structure.Pull = GPIO_PULLUP;
    gpio_init_structure.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    gpio_init_structure.Alternate = GPIO_AF12_FMC;
    
    /* 配置GPIOD */
    gpio_init_structure.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_4 | GPIO_PIN_5  |
                                GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_14 |
                                GPIO_PIN_15;
    HAL_GPIO_Init(GPIOD, &gpio_init_structure);

    /* 配置GPIOE */
    gpio_init_structure.Pin = GPIO_PIN_7 | GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 |
                                GPIO_PIN_11 | GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14 |
                                GPIO_PIN_15;
    HAL_GPIO_Init(GPIOE, &gpio_init_structure);

    /* 配置GPIOG */
    gpio_init_structure.Pin = GPIO_PIN_0 | GPIO_PIN_1| GPIO_PIN_9;
    HAL_GPIO_Init(GPIOG, &gpio_init_structure);
    
    /* 配置BUSY引脚,默认是普通IO状态 */
    {
        GPIO_InitTypeDef   GPIO_InitStructure;
    
        __HAL_RCC_SYSCFG_CLK_ENABLE();
                
        BUSY_RCC_GPIO_CLK_ENABLE();        /* 打开GPIO时钟 */

        /* BUSY信号,使用的PE5,用于转换完毕检测 */
        GPIO_InitStructure.Mode = GPIO_MODE_INPUT;   /* 设置推挽输出 */
        GPIO_InitStructure.Pull = GPIO_NOPULL;       /* 无上拉下拉 */
        GPIO_InitStructure.Pin = BUSY_PIN;           
        HAL_GPIO_Init(BUSY_GPIO, &GPIO_InitStructure);    
    }
    
    /* CONVST 启动ADC转换的GPIO = PC6 */
    {
        GPIO_InitTypeDef   GPIO_InitStructure;
        CONVST_RCC_GPIO_CLK_ENABLE();

        /* 配置PC6 */
        GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;        /* 设置推挽输出 */
        GPIO_InitStructure.Pull = GPIO_NOPULL;            /* 上下拉电阻不使能 */
        GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH;    /* GPIO速度等级 */    

        GPIO_InitStructure.Pin = CONVST_PIN;    
        HAL_GPIO_Init(CONVST_GPIO, &GPIO_InitStructure);    
    }
}

这里重点注意AD7606_BUSY引脚,上电后的默认配置是普通IO。另外还有过采样的3个引脚,量程配置的1个引脚和复位控制的1个引脚,均通过V6板子的扩展IO实现:

/* 设置过采样的IO, 在扩展的74HC574上 */
#define OS0_1()        HC574_SetPin(AD7606_OS0, 1)
#define OS0_0()        HC574_SetPin(AD7606_OS0, 0)
#define OS1_1()        HC574_SetPin(AD7606_OS1, 1)
#define OS1_0()        HC574_SetPin(AD7606_OS1, 0)
#define OS2_1()        HC574_SetPin(AD7606_OS2, 1)
#define OS2_0()        HC574_SetPin(AD7606_OS2, 0)

/* 设置输入量程的GPIO, 在扩展的74HC574上 */
#define RANGE_1()    HC574_SetPin(AD7606_RANGE, 1)
#define RANGE_0()    HC574_SetPin(AD7606_RANGE, 0)

/* AD7606复位口线, 在扩展的74HC574上 */
#define RESET_1()    HC574_SetPin(AD7606_RESET, 1)
#define RESET_0()    HC574_SetPin(AD7606_RESET, 0)

36.7.2 第3步,FMC时钟源

STM32F429的FMC总线是挂在AHB3上的,频率与内核主频一样:

 【STM32F429开发板用户手册】第36章 STM32F429的FMC总线应用之DMA双缓冲驱动AD7606(8通道同步采样, 16bit, 正负10V)_第6张图片

一般我们都是将F429的主频设置为168MHz或者180MHz,那么FMC的频率就是168MHz或者180MHz。

36.7.3 第4步,FMC的时序配置(重要)

由于操作AD7606仅需要读操作,而且使用的是FMC总线的Mode_A,那么仅需按照如下时序图配置好即可:

【STM32F429开发板用户手册】第36章 STM32F429的FMC总线应用之DMA双缓冲驱动AD7606(8通道同步采样, 16bit, 正负10V)_第7张图片 

根据这个时序图,重点配置好ADDSET地址建立时间和DATAST数据建立时间即可。

  DATAST(DataSetupTime,数据建立时间)

DATAST实际上对应的就是35.4.4小节里面的t10 。RD读信号的低电平脉冲宽度,通信电压不同,时间不同,对于STM32来说,FMC通信电平一般是3.3V,即最小值21ns。

 【STM32F429开发板用户手册】第36章 STM32F429的FMC总线应用之DMA双缓冲驱动AD7606(8通道同步采样, 16bit, 正负10V)_第8张图片

  ADDST(AddressSetupTime,地址建立时间)

DATAST实际上对应的就是35.4.4小节里面的t11 或者t12

  •   如果采用CS(NEx)片选和RD(NOE)读信号独立方式,对应的时间最小15ns,即t11
  •   如果采用CS(NEx)片选和RD(NOE)读信号并联方式,对应的时间最小22ns,即t12 

我们这里将t12作为最小值更合理,因为CS(NEx)片选信号,每读取完一路,拉高一次。

 

有了这些认识后,再来看FMC的时序配置就比较好理解了:

1.    /*
2.    ******************************************************************************************************
3.    *    函 数 名: AD7606_FSMCConfig
4.    *    功能说明: 配置FSMC并口访问时序
5.    *    形    参: 无
6.    *    返 回 值: 无
7.    ******************************************************************************************************
8.    */
9.    static void AD7606_FSMCConfig(void)
10.    {
11.        /* 
12.           DM9000,扩展IO,OLED和AD7606公用一个FMC配置,如果都开启,请以FMC速度最慢的为准。
13.           从而保证所有外设都可以正常工作。
14.        */
15.        SRAM_HandleTypeDef hsram = {0};
16.        FMC_NORSRAM_TimingTypeDef SRAM_Timing = {0};
17.            
18.        /*
19.        AD7606规格书要求(3.3V时,通信电平Vdriver):RD读信号低电平脉冲宽度最短21ns,对应DataSetupTime
20.        CS片选和RD读信号独立方式的高电平脉冲最短宽度15ns。
21.        CS片选和RD读信号并联方式的高电平脉冲最短宽度22ns。
22.        这里将22ns作为最小值更合理些,对应FMC的AddressSetupTime
23.        
24.            4-x-6-x-x-x  : RD高持续35.7ns,低电平持续23.8ns. 读取8路样本数据到内存差不多就是476ns。
25.        */
26.        hsram.Instance  = FMC_NORSRAM_DEVICE;
27.        hsram.Extended  = FMC_NORSRAM_EXTENDED_DEVICE;
28.        
29.        /* FMC使用的HCLK,主频168MHz,1个FMC时钟周期就是5.95ns */
30.        SRAM_Timing.AddressSetupTime       = 4;  /* 4*5.95ns=23.8ns,地址建立时间,范围0 -15个FMC时钟周
31.                                                     期个数 */
32.        SRAM_Timing.AddressHoldTime        = 0;  /* 地址保持时间,配置为模式A时,用不到此参数 范围1 -15
33.                                                     个时钟周期个数 */
34.        SRAM_Timing.DataSetupTime          = 6;  /* 6*5.95ns=35.7ns,数据保持时间,范围1 -255个时钟周期个
35.                                                   数 */
36.        SRAM_Timing.BusTurnAroundDuration  = 0;  /* 此配置用不到这个参数 */
37.        SRAM_Timing.CLKDivision            = 0;  /* 此配置用不到这个参数 */
38.        SRAM_Timing.DataLatency            = 0;  /* 此配置用不到这个参数 */
39.        SRAM_Timing.AccessMode             = FMC_ACCESS_MODE_A; /* 配置为模式A */
40.    
41.        hsram.Init.NSBank             = FMC_NORSRAM_BANK2;              /* 使用的BANK2,即使用的片选
42.                                                                            FMC_NE2 */
43.        hsram.Init.DataAddressMux     = FMC_DATA_ADDRESS_MUX_DISABLE;   /* 禁止地址数据复用 */
44.        hsram.Init.MemoryType         = FMC_MEMORY_TYPE_SRAM;           /* 存储器类型SRAM */
45.        hsram.Init.MemoryDataWidth    = FMC_NORSRAM_MEM_BUS_WIDTH_32;   /* 32位总线宽度 */
46.        hsram.Init.BurstAccessMode    = FMC_BURST_ACCESS_MODE_DISABLE;  /* 关闭突发模式 */
47.        hsram.Init.WaitSignalPolarity = FMC_WAIT_SIGNAL_POLARITY_LOW;   /* 用于设置等待信号的极性,关闭突
48.                                                                            发模式,此参数无效 */
49.        hsram.Init.WaitSignalActive   = FMC_WAIT_TIMING_BEFORE_WS;      /* 关闭突发模式,此参数无效 */
50.        hsram.Init.WriteOperation     = FMC_WRITE_OPERATION_ENABLE;     /* 用于使能或者禁止写保护 */
51.        hsram.Init.WaitSignal         = FMC_WAIT_SIGNAL_DISABLE;        /* 关闭突发模式,此参数无效 */
52.        hsram.Init.ExtendedMode       = FMC_EXTENDED_MODE_DISABLE;      /* 禁止扩展模式 */
53.        hsram.Init.AsynchronousWait   = FMC_ASYNCHRONOUS_WAIT_DISABLE;  /* 用于异步传输期间,使能或者禁止
54.                                                                            等待信号,这里选择关闭 */
55.        hsram.Init.WriteBurst         = FMC_WRITE_BURST_DISABLE;        /* 禁止写突发 */
56.        hsram.Init.ContinuousClock    = FMC_CONTINUOUS_CLOCK_SYNC_ONLY; /* 仅同步模式才做时钟输出 */
57.         hsram.Init.WriteFifo          = FMC_WRITE_FIFO_ENABLE;          /* 使能写FIFO */
58.    
59.        /* 初始化SRAM控制器 */
60.        if (HAL_SRAM_Init(&hsram, &SRAM_Timing, &SRAM_Timing) != HAL_OK)
61.        {
62.            /* 初始化错误 */
63.            Error_Handler(__FILE__, __LINE__);
64.        }    
65.    }

这里把几个关键的地方阐释下:

  •   第15- 16行,对作为局部变量的HAL库结构体做初始化,防止不确定值配置时出问题。
  •   第30行,地址建立时间,对于AD7606来说,这个地方最小值22ns。这里取值4个FMC时钟周期,即23.8ns。
  •   第32行,地址保持时间,对于FMC模式A来说,此参数用不到。
  •   第34行,数据建立时间,对于AD7606来说,这个地方最小值是21ns。由于DM9000,扩展IO,OLED和AD7606公用一个FMC配置,如果都开启,请以FMC速度最慢的为准,从而保证所有外设都可以正常工作,我们这里取值6个FMC时钟周期,即35.7ns。
  •   第36 – 38行,当前配置用不到这三个参数。
  •   第41行,使用的BANK2,即使用的片选FMC_NE2。

36.7.4 第5步,AD7606的FMC DMA实现(核心)

这部分代码是本章36.6小节的完美体现:

1.    static void AD7606_SetTIMOutPWM(TIM_TypeDef* TIMx, uint32_t _ulFreq)
2.    {
3.        TIM_OC_InitTypeDef sConfig = {0};    
4.        GPIO_InitTypeDef   GPIO_InitStruct;
5.        uint16_t usPeriod;
6.        uint16_t usPrescaler;
7.        uint32_t uiTIMxCLK;
8.        uint32_t pulse;
9.    
10.        
11.        /* 配置时钟 */
12.        CONVST_RCC_GPIO_CLK_ENABLE();
13.        CONVST_TIM8_CLK_ENABLE();
14.        TIMx_UP_DMA_STREAM_CLK_ENABLE();
15.        
16.        /* 配置引脚 */
17.        GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
18.        GPIO_InitStruct.Pull = GPIO_PULLUP;
19.        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
20.        GPIO_InitStruct.Alternate = CONVST_AF;
21.        GPIO_InitStruct.Pin = CONVST_PIN;
22.        HAL_GPIO_Init(CONVST_GPIO, &GPIO_InitStruct);
23.        
24.          /*-----------------------------------------------------------------------
25.            system_stm32f4xx.c 文件中 void SetSysClock(void) 函数对时钟的配置如下:
26.    
27.            HCLK = SYSCLK / 1     (AHB1Periph)
28.            PCLK2 = HCLK / 2      (APB2Periph)
29.            PCLK1 = HCLK / 4      (APB1Periph)
30.    
31.            因为APB1 prescaler != 1, 所以 APB1上的TIMxCLK = PCLK1 x 2 = SystemCoreClock / 2;
32.            因为APB2 prescaler != 1, 所以 APB2上的TIMxCLK = PCLK2 x 2 = SystemCoreClock;
33.    
34.            APB1 定时器有 TIM2, TIM3 ,TIM4, TIM5, TIM6, TIM7, TIM12, TIM13,TIM14
35.            APB2 定时器有 TIM1, TIM8 ,TIM9, TIM10, TIM11
36.    
37.        ----------------------------------------------------------------------- */
38.        if ((TIMx == TIM1) || (TIMx == TIM8) || (TIMx == TIM9) || (TIMx == TIM10) || (TIMx == TIM11))
39.        {
40.            /* APB2 定时器时钟 = 168M */
41.            uiTIMxCLK = SystemCoreClock;
42.            
43.            if (_ulFreq < 100)
44.            {
45.                usPrescaler = 10000 - 1;                      /* 分频比 = 10000 */
46.                usPeriod =  (uiTIMxCLK / 10000) / _ulFreq  - 1; /* 自动重装的值, usPeriod最小值168, 单
47.                                                                     位59us */
48.                pulse = usPeriod;       /* 设置低电平时间59us,注意usPeriod已经进行了减1操作 */
49.            } 
50.            else if (_ulFreq < 3000)
51.            {
52.                usPrescaler = 100 - 1;                        /* 分频比 = 100 */
53.                usPeriod =  (uiTIMxCLK / 100) / _ulFreq  - 1;    /* 自动重装的值, usPeriod最小值560,单位
54.                                                                    595ns */
55.                pulse = usPeriod-1;  /* 设置低电平时间1.19us,注意usPeriod已经进行了减1操作 */
56.            }
57.            else    /* 大于4K的频率,无需分频 */
58.            {
59.                usPrescaler = 0;                                /* 分频比 = 1 */
60.                usPeriod = uiTIMxCLK / _ulFreq - 1;    /* 自动重装的值, usPeriod最小值840,单位5.95ns */
61.                pulse = usPeriod - 199;       /* 设置低电平时间1.19us,注意usPeriod已经进行了减1操作 */
62.            }
63.        }
64.        else    
65.        {
66.            /* APB1 定时器 = 84M */
67.            uiTIMxCLK = SystemCoreClock / 2;
68.            
69.            if (_ulFreq < 100)
70.            {
71.                usPrescaler = 10000 - 1;            /* 分频比 = 10000 */
72.                usPeriod =  (uiTIMxCLK / 10000) / _ulFreq  - 1; /* 自动重装的值, usPeriod最小值84, 单位
73.                                                                     119us */
74.                pulse = usPeriod;           /* 设置低电平时间119us,注意usPeriod已经进行了减1操作 */
75.            } 
76.            else if (_ulFreq < 3000)
77.            {
78.                usPrescaler = 100 - 1;                /* 分频比 = 100 */
79.                usPeriod =  (uiTIMxCLK / 100) / _ulFreq  - 1;    /* 自动重装的值, usPeriod最小值280,单位
80.                                                                    1.19us */
81.                pulse = usPeriod;         /* 设置低电平时间1.19us,注意usPeriod已经进行了减1操作 */
82.            }
83.            else    /* 大于4K的频率,无需分频 */
84.            {
85.                usPrescaler = 0;            /* 分频比 = 1 */
86.                usPeriod = uiTIMxCLK / _ulFreq - 1;    /* 自动重装的值, usPeriod最小值420,单位11.9ns */
87.                pulse = usPeriod - 99;    /* 设置低电平时间1.19us,注意usPeriod已经进行了减1操作 */
88.            }
89.        }
90.    
91.        
92.        if (HAL_TIM_PWM_DeInit(&TimHandle) != HAL_OK)
93.        {
94.            Error_Handler(__FILE__, __LINE__);        
95.        }
96.        
97.        if (HAL_TIM_PWM_Init(&TimHandle) != HAL_OK)
98.        {
99.            Error_Handler(__FILE__, __LINE__);
100.        }
101.    
102.        /* 配置定时器PWM输出通道 */
103.        sConfig.OCMode       = TIM_OCMODE_PWM1;         /* 配置输出比较模式 */
104.        sConfig.OCPolarity   = TIM_OCPOLARITY_HIGH;     /* 设置输出高电平有效 */
105.        sConfig.OCFastMode   = TIM_OCFAST_DISABLE;      /* 关闭快速输出模式 */
106.        sConfig.OCNPolarity  = TIM_OCNPOLARITY_HIGH;    /* 配置互补输出高电平有效 */
107.        sConfig.OCIdleState  = TIM_OCIDLESTATE_SET;     /* 空闲状态时,设置输出比较引脚为高电平 */
108.        sConfig.OCNIdleState = TIM_OCNIDLESTATE_RESET;  /* 空闲状态时,设置互补输出比较引脚为低电平 */
109.    
110.        /* 占空比 */
111.        sConfig.Pulse = pulse;
112.        if (HAL_TIM_PWM_ConfigChannel(&TimHandle, &sConfig, CONVST_TIMCH) != HAL_OK)
113.        {
114.            Error_Handler(__FILE__, __LINE__);
115.        }
116.        
117.        /* 使能定时器中断  */
118.        __HAL_TIM_ENABLE_DMA(&TimHandle, TIM_DMA_UPDATE);
119.        
120.        /* 启动PWM输出 */
121.        if (HAL_TIM_PWM_Start(&TimHandle, CONVST_TIMCH) != HAL_OK)
122.        {
123.            Error_Handler(__FILE__, __LINE__);
124.        }
125.        
126.        /* 定时器UP更新触发DMA传输 */        
127.        TIMDMA.Instance                 = TIMx_UP_DMA_STREAM;      /* 例化使用的DMA数据流 */
128.        TIMDMA.Init.FIFOMode            = DMA_FIFOMODE_ENABLE;     /* 使能FIFO*/
129.        TIMDMA.Init.FIFOThreshold       = DMA_FIFO_THRESHOLD_FULL; /* 用于设置阀值 */
130.        TIMDMA.Init.MemBurst            = DMA_MBURST_INC8;           /* 用于存储器突发 */
131.        TIMDMA.Init.PeriphBurst         = DMA_PBURST_INC8;           /* 用于外设突发 */
132.        TIMDMA.Init.Request             = TIMx_UP_DMA_REQUEST;     /* 请求类型 */  
133.        TIMDMA.Init.Direction           = DMA_PERIPH_TO_MEMORY;    /* 传输方向是从外设到存储器 */  
134.        TIMDMA.Init.PeriphInc           = DMA_PINC_DISABLE;        /* 外设地址自增禁止 */ 
135.        TIMDMA.Init.MemInc              = DMA_MINC_ENABLE;         /* 存储器地址自增使能 */  
136.        TIMDMA.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD; /* 外设数据传输位宽选择半字,即16bit */ 
137.        TIMDMA.Init.MemDataAlignment    = DMA_MDATAALIGN_HALFWORD; /* 存储器数据传输位宽选择半字,即16bit */    
138.        TIMDMA.Init.Mode                = DMA_CIRCULAR;            /* 循环模式 */
139.        TIMDMA.Init.Priority            = DMA_PRIORITY_LOW;        /* 优先级低 */
140.        
141.         /* 复位DMA */
142.        if(HAL_DMA_DeInit(&TIMDMA) != HAL_OK)
143.        {
144.            Error_Handler(__FILE__, __LINE__);     
145.        }
146.        
147.         /* 初始化DMA */
148.        if(HAL_DMA_Init(&TIMDMA) != HAL_OK)
149.        {
150.            Error_Handler(__FILE__, __LINE__);     
151.        }
152.        
153.        /* 关联DMA句柄到TIM */
154.        //__HAL_LINKDMA(&TimHandle, hdma[TIM_DMA_ID_UPDATE], TIMDMA);    
155.        
156.        /* 配置DMA中断 */
157.        HAL_NVIC_SetPriority(TIMx_UP_DMA_IRQn, 1, 0);
158.        HAL_NVIC_EnableIRQ(TIMx_UP_DMA_IRQn);
159.        
160.        /* 注册半传输完成中断和传输完成中断 */
161.        HAL_DMA_RegisterCallback(&TIMDMA, HAL_DMA_XFER_CPLT_CB_ID, AD7606_DmaCplCb);
162.        HAL_DMA_RegisterCallback(&TIMDMA, HAL_DMA_XFER_HALFCPLT_CB_ID, AD7606_DmaHalfCplCb);
163.        
164.        /* 启动DMA传输 */
165.        HAL_DMA_Start_IT(&TIMDMA, (uint32_t)AD7606_BASE, (uint32_t)g_sAd7606Buf, AD7606_BUFSIZE);
166.    }
  •   第38 – 89行,配置PWM频率和占空比,特别是占比设计比较考究。
  •   第127-139行,配置DMA,特别注意突发和FIFO设置,完全按照36.6.2小节配置。
  •   第161-162行,注册半传输完成中断和传输完成中断的回调函数。

36.7.5 第6步,FMC DMA双缓冲

通过注册半传输完成中断和传输完成中断回调函数实现双缓冲:

/* DMA传输完成回调函数,弱定义 */
__weak void AD7606_DmaCplCb(DMA_HandleTypeDef *hdma)
{
    
}

/* DMA半传输完成回调函数,弱定义 */
__weak void AD7606_DmaHalfCplCb(DMA_HandleTypeDef *hdma)
{
    
}

比如用户设置的DMA缓冲是int16_t buf[16],那么进入半传输完成回调,用户就可以处理buf[0]到buf[7]里面的数据,进入传输完成中断里面,处理buf[8]到buf[15]里面的数据。

36.7.6 第7步,AD7606过采样设置

AD7606的过采样实现比较简单,通过IO引脚就可以控制,支持2倍,4倍,8倍,16倍,32倍和64倍过采样设置。

/*
*********************************************************************************************************
*    函 数 名: AD7606_SetOS
*    功能说明: 配置AD7606数字滤波器,也就设置过采样倍率。
*              通过设置 AD7606_OS0、OS1、OS2口线的电平组合状态决定过采样倍率。
*              启动AD转换之后,AD7606内部自动实现剩余样本的采集,然后求平均值输出。
*
*              过采样倍率越高,转换时间越长。
*              0、无过采样时,AD转换时间 = 3.45us - 4.15us
*              1、2倍过采样时 = 7.87us - 9.1us
*              2、4倍过采样时 = 16.05us - 18.8us
*              3、8倍过采样时 = 33us - 39us
*              4、16倍过采样时 = 66us - 78us
*              5、32倍过采样时 = 133us - 158us
*              6、64倍过采样时 = 257us - 315us
*
*    形    参: _ucOS : 过采样倍率, 0 - 6
*    返 回 值: 无
*********************************************************************************************************
*/
void AD7606_SetOS(uint8_t _ucOS)
{
    g_tAD7606.ucOS = _ucOS;
    switch (_ucOS)
    {
        case AD_OS_X2:
            OS2_0();
            OS1_0();
            OS0_1();
            break;

        case AD_OS_X4:
            OS2_0();
            OS1_1();
            OS0_0();
            break;

        case AD_OS_X8:
            OS2_0();
            OS1_1();
            OS0_1();
            break;

        case AD_OS_X16:
            OS2_1();
            OS1_0();
            OS0_0();
            break;

        case AD_OS_X32:
            OS2_1();
            OS1_0();
            OS0_1();
            break;

        case AD_OS_X64:
            OS2_1();
            OS1_1();
            OS0_0();
            break;

        case AD_OS_NO:
        default:
            g_tAD7606.ucOS = AD_OS_NO;
            OS2_0();
            OS1_0();
            OS0_0();
            break;
    }
}

36.7.7 第8步,AD7606量程设置

AD7606支持两种量程,±5V和±10V,实现代码如下:

/*
*********************************************************************************************************
*    函 数 名: AD7606_SetInputRange
*    功能说明: 配置AD7606模拟信号输入量程。
*    形    参: _ucRange : 0 表示正负5V   1表示正负10V
*    返 回 值: 无
*********************************************************************************************************
*/
void AD7606_SetInputRange(uint8_t _ucRange)
{
    if (_ucRange == 0)
    {
        g_tAD7606.ucRange = 0;
        RANGE_0();    /* 设置为正负5V */
    }
    else
    {
        g_tAD7606.ucRange = 1;
        RANGE_1();    /* 设置为正负10V */
    }
}

36.7.8 第9步,DMA突发传输的1KB边界处理

针对突发传输,参考手册DMA章节有如下说明:

 

注意正确理解这段话的含义,意思是说突发传输期间,不可以跨越1KB对齐的地址,比如0x2000 0400、0x2000 0800、0x2000 0C00等地址。我们程序里面是设置的每次突发传输16个字节数据,这16个连续数据不能有跨越这些地址的情况。这对这个问题,有个比较巧妙的解决办法,直接设置DMA缓冲区16字节对齐即可,这样每次突发都不会有跨越这些地址的情况:

/* 8路同步采集,每次采集16字节数据,防止DMA突发方式1KB边界问题,即每次采集不要有跨边界的情况 */
#define AD7606_BUFSIZE        16
__align(16) int16_t g_sAd7606Buf[AD7606_BUFSIZE];   /* DMA双缓冲使用 */

36.8 AD7606板级支持包(bsp_fmcdma_ad7606.c)

AD7606驱动文件bsp_fmcdma_ad7606.c主要实现了如下几个API供用户调用:

  •   bsp_InitAD7606
  •   AD7606_SetOS
  •   AD7606_SetInputRange
  •   AD7606_Reset
  •   AD7606_StartConvst
  •   AD7606_ReadNowAdc
  •   AD7606_EnterAutoMode
  •   AD7606_StartRecord
  •   AD7606_StopRecord
  •   AD7606_FifoNewData
  •   AD7606_ReadFifo
  •   AD7606_FifoFull

36.8.1 函数bsp_InitAD7606

函数原型:

void bsp_InitAD7606(void)

函数描述:

主要用于AD7606的初始化。

36.8.2 函数AD7606_SetOS

函数原型:

void AD7606_SetOS(uint8_t _ucOS)

函数描述:

此函数用于配置AD7606数字滤波器,也就设置过采样倍率。通过设置 AD7606_OS0、OS1、OS2口线的电平组合状态决定过采样倍率。启动AD转换之后,AD7606内部自动实现剩余样本的采集,然后求平均值输出。

过采样倍率越高,转换时间越长。

无过采样时,AD转换时间 = 3.45us - 4.15us。

2倍过采样时 = 7.87us - 9.1us。

4倍过采样时 = 16.05us - 18.8us。

8倍过采样时 = 33us - 39us。

16倍过采样时 = 66us - 78us。

32倍过采样时 = 133us - 158us。

64倍过采样时 = 257us - 315us。

函数参数:

  •   第1个参数为范围0 – 6,分别对应无过采样,2倍过采样,4倍过采样,8倍过采样,16倍过采样,32倍过采样和64倍过采样。

36.8.3 函数AD7606_SetInputRange

函数原型:

void AD7606_SetInputRange(uint8_t _ucRange)

函数描述:

配置AD7606模拟信号输入量程。

函数参数:

  •   第1个参数为0 表示正负5V ,1表示正负10V。

36.8.4 函数AD7606_Reset

函数原型:

void AD7606_Reset(void)

函数描述:

此函数用于硬件复位AD7606,复位之后恢复到正常工作状态。

36.8.5 函数AD7606_StartRecord

函数原型:

void AD7606_StartRecord(uint32_t _ulFreq)

函数描述:

用于启动采集。

函数参数:

  •   第1个参数是采样频率,范围1-200KHz,单位Hz。

36.8.6 函数AD7606_StopRecord

函数原型:

void AD7606_StopRecord(void)

函数描述:

此函数用于停止采集定时器。函数AD7606_StartRecord和AD7606_StopRecord是配套的。

36.9 J-Scope实时展示AD7606采集数据说明

J-Scope专题教程(实时展示要用J-Scope的RTT模式),本章配套例子也做了支持:

http://www.armbbs.cn/forum.php?mod=viewthread&tid=86881 。

看完专题教程,基本就会操作了,这里有三点注意事项需要大家提前有个了解。另外,推荐使用MDK版工程做测试J-Scope,IAR版容易测试不正常。

36.9.1 J-Scope闪退问题解决办法

如下界面,不要点击选择按钮,闪退就是因为点击了这个选择按钮。

【STM32F429开发板用户手册】第36章 STM32F429的FMC总线应用之DMA双缓冲驱动AD7606(8通道同步采样, 16bit, 正负10V)_第9张图片 

直接手动填写型号即可,比如STM32H743XI,STM32F429BI,STM32F407IG,STM32F103ZE等。

【STM32F429开发板用户手册】第36章 STM32F429的FMC总线应用之DMA双缓冲驱动AD7606(8通道同步采样, 16bit, 正负10V)_第10张图片 

36.9.2 J-Scope多通道传输实现

J-Scope的多通道传输配置好函数SEGGER_RTT_ConfigUpBuffer即可,主要是通过第2个参数实现的。

    /*
        配置通道1,上行配置
        默认情况下,J-Scope仅显示1个通道。
        上传1个通道的波形,配置第2个参数为JScope_i2
        上传2个通道的波形,配置第2个参数为JScope_i2i2
        上传3个通道的波形,配置第2个参数为JScope_i2i2i2
        上传4个通道的波形,配置第2个参数为JScope_i2i2i2i2
        上传5个通道的波形,配置第2个参数为JScope_i2i2i2i2i2
        上传6个通道的波形,配置第2个参数为JScope_i2i2i2i2i2i2
        上传7个通道的波形,配置第2个参数为JScope_i2i2i2i2i2i2i2
        上传8个通道的波形,配置第2个参数为JScope_i2i2i2i2i2i2i2i2
    */    
    SEGGER_RTT_ConfigUpBuffer(1, "JScope_i2", buf, 20480, SEGGER_RTT_MODE_NO_BLOCK_SKIP);

使用函数SEGGER_RTT_Write上传数据时,要跟配置的通道数匹配,比如配置的三个通道,就需要调用三次函数:

SEGGER_RTT_Write(1, &(g_tAD7606.sNowAdc[0]), 2);
SEGGER_RTT_Write(1, &(g_tAD7606.sNowAdc[1]), 2);    
SEGGER_RTT_Write(1, &(g_tAD7606.sNowAdc[2]), 2);

多路效果:

 【STM32F429开发板用户手册】第36章 STM32F429的FMC总线应用之DMA双缓冲驱动AD7606(8通道同步采样, 16bit, 正负10V)_第11张图片

36.9.3 J-Scope带宽问题

普通的JLINK时钟速度8 - 12MHz时, J-Scope的速度基本可以达到500KB/S(注意,单位是字节)AD7606的最高采样率是200Ksps,16bit,那么一路采集就有400KB/S的速速,所以要根据设置的采样率设置要显示的J-Scope通道数,如果超出了最高通信速度,波形显示会混乱。

 

       200Ksps时,实时显示1路

       100Ksps时,实时显示2路

       50Ksps时, 实时显示4路

       25Ksps时, 实时显示8路

 

实际速度以底栏的展示为准,如果与设置的速度差异较大,说明传输异常了。

 【STM32F429开发板用户手册】第36章 STM32F429的FMC总线应用之DMA双缓冲驱动AD7606(8通道同步采样, 16bit, 正负10V)_第12张图片

36.10          AD7606驱动移植和使用

AD7606移植步骤如下:

  •   第1步:复制bsp_fmc_ad7606.c和bsp_fmc_ad7606.h到自己的工程目录,并添加到工程里面。
  •   第2步:根据使用的CONVST引脚,FMC DMA,过采样引脚,量程控制引脚,复位引脚,修改bsp_fmc_ad7606.c开头的宏定义。

这里要特别注意过采样引脚,量程控制引脚和复位引脚是采用的扩展IO,需要大家根据自己的情况修改。

/* CONVST 启动ADC转换的GPIO = PC6 */
#define CONVST_RCC_GPIO_CLK_ENABLE    __HAL_RCC_GPIOC_CLK_ENABLE
#define CONVST_TIM8_CLK_ENABLE      __HAL_RCC_TIM8_CLK_ENABLE
#define CONVST_RCC_GPIO_CLK_DISBALE    __HAL_RCC_GPIOC_CLK_DISABLE
#define CONVST_TIM8_CLK_DISABLE     __HAL_RCC_TIM8_CLK_DISABLE
#define CONVST_GPIO        GPIOC
#define CONVST_PIN        GPIO_PIN_6
#define CONVST_AF        GPIO_AF3_TIM8
#define CONVST_TIMX        TIM8
#define CONVST_TIMCH    TIM_CHANNEL_1

· FMC DMA */
#define TIMx_UP_DMA_STREAM_CLK_ENABLE      __HAL_RCC_DMA2_CLK_ENABLE
#define TIMx_UP_DMA_STREAM_CLK_DISABLE  __HAL_RCC_DMA2_CLK_DISABLE
#define TIMx_UP_DMA_STREAM             DMA2_Stream1
#define TIMx_UP_DMA_CHANNEL            DMA_CHANNEL_7
#define TIMx_UP_DMA_IRQn               DMA2_Stream1_IRQn
#define TIMx_UP_DMA_IRQHandler         DMA2_Stream1_IRQHandler

/* 设置过采样的IO, 在扩展的74HC574上 */
#define OS0_1()        HC574_SetPin(AD7606_OS0, 1)
#define OS0_0()        HC574_SetPin(AD7606_OS0, 0)
#define OS1_1()        HC574_SetPin(AD7606_OS1, 1)
#define OS1_0()        HC574_SetPin(AD7606_OS1, 0)
#define OS2_1()        HC574_SetPin(AD7606_OS2, 1)
#define OS2_0()        HC574_SetPin(AD7606_OS2, 0)

/* 启动AD转换的GPIO : PC6 */
#define CONVST_1()        CONVST_GPIO->BSRR = CONVST_PIN
#define CONVST_0()        CONVST_GPIO->BSRR = ((uint32_t)CONVST_PIN << 16U)

/* 设置输入量程的GPIO, 在扩展的74HC574上 */
#define RANGE_1()    HC574_SetPin(AD7606_RANGE, 1)
#define RANGE_0()    HC574_SetPin(AD7606_RANGE, 0)

/* AD7606复位口线, 在扩展的74HC574上 */
#define RESET_1()    HC574_SetPin(AD7606_RESET, 1)
#define RESET_0()    HC574_SetPin(AD7606_RESET, 0)
  •   第3步:根据需要设置DMA缓冲大小:
/* 8路同步采集,每次采集16字节数据,防止DMA突发方式1KB边界问题,即每次采集不要有跨边界的情况 */
#define AD7606_BUFSIZE        16
__align(16) int16_t g_sAd7606Buf[AD7606_BUFSIZE];   /* DMA双缓冲使用 */
  •   第4步:根据具体用到的FMC引脚,修改函数AD7606_CtrlLinesConfig里面做的IO配置。
  •   第5步:根据使用的FMC BANK,修改函数AD7606_FSMCConfig里面的BANK配置,这点非常容易疏忽。
  •   第6步:初始化AD7606。
bsp_InitAD7606();    /* 配置AD7606所用的GPIO */
  •   第7步:AD7606驱动主要用到HAL库的FMC驱动文件,简单省事些可以添加所有HAL库C源文件进来。
  •   第8步:应用方法看本章节配套例子即可。

36.11          实验例程设计框架

通过程序设计框架,让大家先对配套例程有一个全面的认识,然后再理解细节,本次实验例程的设计框架如下:

【STM32F429开发板用户手册】第36章 STM32F429的FMC总线应用之DMA双缓冲驱动AD7606(8通道同步采样, 16bit, 正负10V)_第13张图片

  第1阶段,上电启动阶段:

  • 这部分在第14章进行了详细说明。

  第2阶段,进入main函数:

  •   第1部分,硬件初始化,主要是HAL库,系统时钟,滴答定时器和LED。
  •   第2部分,应用程序设计部分,测试AD7606。

36.12          实验例程说明(MDK)

配套例子:

V6-017_ AD7606的FMC DMA双缓冲总线驱动方式实现(8通道同步采样, 16bit, 正负10V)

实验目的:

  1. 学习AD7606的FMC DMA双缓冲驱动方式实现。

重要提示:

  1. 板子上电后,默认是100Ksps的2倍过采样。
  2. 如果使用的JLINK速度不够快,导致J-Scope无法最高速度实时上传,可以使用摇杆上下键设置过采样来降低上传速度。
  3. 默认情况下,程序仅上传了AD7606通道1采集的数据。
  4. 串口数据展示推荐使用SecureCRT,因为数据展示做了特别处理,方便采集数据在串口软件同一个位置不断刷新。

实验内容:

1、AD7606的FMC驱动做了两种采集方式

(1)软件定时获取方式,适合低速查询获取。

(2)FIFO工作模式,适合8路实时采集,支持最高采样率200Ksps。

2、将模拟输入接地时,采样值是0左右。

3、模拟输入端悬空时,采样值在某个范围浮动(这是正常的,这是AD7606内部输入电阻导致的浮动电压)。

4、出厂的AD7606模块缺省是8080 并行接口。如果用SPI接口模式,需要修改 R1 R2电阻配置。

5、配置CVA CVB 引脚为PWM输出模式,周期设置为需要的采样频率,之后MCU将产生周期非常稳定的AD转换信号。

实验操作:

  1. 启动一个自动重装软件定时器,每100ms翻转一次LED2。
  2. K1键       : 切换量程(5V或10V)。
  3. K2键       : 进入FIFO工作模式。
  4. K3键       : 进入软件定时采集模式。
  5. 摇杆上下键 : 调节过采样参数。

上电后串口打印的信息:

波特率 115200,数据位 8,奇偶校验位无,停止位 1。

【STM32F429开发板用户手册】第36章 STM32F429的FMC总线应用之DMA双缓冲驱动AD7606(8通道同步采样, 16bit, 正负10V)_第14张图片 

J-Scope波形效果:

【STM32F429开发板用户手册】第36章 STM32F429的FMC总线应用之DMA双缓冲驱动AD7606(8通道同步采样, 16bit, 正负10V)_第15张图片 

模块插入位置:

【STM32F429开发板用户手册】第36章 STM32F429的FMC总线应用之DMA双缓冲驱动AD7606(8通道同步采样, 16bit, 正负10V)_第16张图片 

程序设计:

  系统栈大小分配:

【STM32F429开发板用户手册】第36章 STM32F429的FMC总线应用之DMA双缓冲驱动AD7606(8通道同步采样, 16bit, 正负10V)_第17张图片 

  硬件外设初始化

硬件外设的初始化是在 bsp.c 文件实现:

/*
*********************************************************************************************************
*    函 数 名: bsp_Init
*    功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次
*    形    参:无
*    返 回 值: 无
*********************************************************************************************************
*/
void bsp_Init(void)
{
    /* 
       STM32F429 HAL 库初始化,此时系统用的还是F429自带的16MHz,HSI时钟:
       - 调用函数HAL_InitTick,初始化滴答时钟中断1ms。
       - 设置NVIV优先级分组为4。
     */
    HAL_Init();

    /* 
       配置系统时钟到168MHz
       - 切换使用HSE。
       - 此函数会更新全局变量SystemCoreClock,并重新配置HAL_InitTick。
    */
    SystemClock_Config();

    /* 
       Event Recorder:
       - 可用于代码执行时间测量,MDK5.25及其以上版本才支持,IAR不支持。
       - 默认不开启,如果要使能此选项,务必看V5开发板用户手册第8章
    */    
#if Enable_EventRecorder == 1  
    /* 初始化EventRecorder并开启 */
    EventRecorderInitialize(EventRecordAll, 1U);
    EventRecorderStart();
#endif
    
    bsp_InitKey();        /* 按键初始化,要放在滴答定时器之前,因为按钮检测是通过滴答定时器扫描 */
    bsp_InitTimer();      /* 初始化滴答定时器 */
    bsp_InitUart();    /* 初始化串口 */
    bsp_InitExtIO();   /* 初始化扩展IO */
    bsp_InitLed();        /* 初始化LED */    
    BEEP_InitHard();   /* 初始化蜂鸣器 */

    /* 针对不同的应用程序,添加需要的底层驱动模块初始化函数 */    
    bsp_InitAD7606();    /* 配置AD7606所用的GPIO */

}

  主功能:

主程序实现如下操作:

  •   启动一个自动重装软件定时器,每100ms翻转一次LED2。
  •   K1键       : 切换量程(5V或10V)。
  •   K2键       : 进入FIFO工作模式。
  •   K3键       : 进入软件定时采集模式。
  •   摇杆上下键 : 调节过采样参数。
/*
*********************************************************************************************************
*    函 数 名: main
*    功能说明: c程序入口
*    形    参: 无
*    返 回 值: 错误代码(无需处理)
*********************************************************************************************************
*/
int main(void)
{
    bsp_Init();        /* 硬件初始化 */
    
    PrintfLogo();    /* 打印例程名称和版本等信息 */

    DemoFmcAD7606(); /* AD7606测试 */
}

/*
*********************************************************************************************************
*    函 数 名: DemoFmcAD7606
*    功能说明: AD7606测试
*    形    参: 无
*    返 回 值: 无
*********************************************************************************************************
*/
void DemoFmcAD7606(void)
{
    uint8_t ucKeyCode;
    uint8_t ucRefresh = 0;

    
    sfDispMenu();        /* 打印命令提示 */

    ucRefresh = 0;        /* 数据在串口刷新的标志 */
    
    AD7606_SetOS(AD_OS_NO);        /* 无过采样 */
    AD7606_SetInputRange(1);    /* 0表示输入量程为正负5V, 1表示正负10V */
    AD7606_StartConvst();        /* 启动1次转换 */
    
    /* 上电默认采样率 */
    g_tAD7606.ucOS = 1;                /* 2倍过采样 */
    AD7606_StartRecord(100000);        /* 启动100kHz采样速率 */
    AD7606_SetOS(g_tAD7606.ucOS);   /* 设置2倍过采样 */
    

    bsp_StartAutoTimer(0, 500);    /* 启动1个500ms的自动重装的定时器 */
    bsp_StartAutoTimer(3, 200);    /* 启动1个200ms的自动重装的定时器 */
    
    /*
        配置通道1,上行配置
        默认情况下,J-Scope仅显示1个通道。
        上传1个通道的波形,配置第2个参数为JScope_i2
        上传2个通道的波形,配置第2个参数为JScope_i2i2
        上传3个通道的波形,配置第2个参数为JScope_i2i2i2
        上传4个通道的波形,配置第2个参数为JScope_i2i2i2i2
        上传5个通道的波形,配置第2个参数为JScope_i2i2i2i2i2
        上传6个通道的波形,配置第2个参数为JScope_i2i2i2i2i2i2
        上传7个通道的波形,配置第2个参数为JScope_i2i2i2i2i2i2i2
        上传8个通道的波形,配置第2个参数为JScope_i2i2i2i2i2i2i2i2
    */    
    SEGGER_RTT_ConfigUpBuffer(1, "JScope_i2", buf, 20480, SEGGER_RTT_MODE_NO_BLOCK_SKIP);

    while(1)
    {
        bsp_Idle();        /* 这个函数在bsp.c文件。用户可以修改这个函数实现CPU休眠和喂狗 */
        
        /* 判断定时器超时时间 */
        if (bsp_CheckTimer(3))    
        {
            /* 每隔100ms 进来一次 */  
            bsp_LedToggle(4);
        }

        if (bsp_CheckTimer(0))
        {
            ucRefresh = 1;    /* 刷新显示 */
        }
        
        if (ucRefresh == 1)
        {
            ucRefresh = 0;

            /* 处理数据 */
            AD7606_Mak();
                                         
            /* 打印ADC采样结果 */
            AD7606_Disp();        
        }

        /* 按键检测由后台systick中断服务程序实现,我们只需要调用bsp_GetKey读取键值即可。这个函数不会
        等待按键按下,这样我们可以在while循环内做其他的事情 */
        ucKeyCode = bsp_GetKey();    /* 读取键值, 无键按下时返回 KEY_NONE = 0 */
        if (ucKeyCode != KEY_NONE)
        {
            
            switch (ucKeyCode)
            {
                case KEY_DOWN_K1:                        /* K1键按下 切换量程 */
                    if (g_tAD7606.ucRange == 0)
                    {
                        AD7606_SetInputRange(1);
                    }
                    else
                    {
                        AD7606_SetInputRange(0);
                    }
                    ucRefresh = 1;
                    break;

                case KEY_DOWN_K2:                        /* K2键按下 */
                    g_tAD7606.ucOS = 1;                    /* 2倍过采样 */
                    AD7606_StartRecord(100000);            /* 启动100kHz采样速率 */
                    AD7606_SetOS(g_tAD7606.ucOS);       /* 设置2倍过采样 */
                    break;

                case KEY_DOWN_K3:                        /* K3键按下 */
                    AD7606_StopRecord();                /* 停止记录 */
                    break;

                case JOY_DOWN_U:                        /* 摇杆UP键按下 */
                    if (g_tAD7606.ucOS < 6)
                    {
                        g_tAD7606.ucOS++;
                    }
                    
                    AD7606_SetOS(g_tAD7606.ucOS);

                AD7606_StartRecord(AD7606_SampleFreq[g_tAD7606.ucOS]);/* 启动当前过采样下最高速度 */
                    
                    ucRefresh = 1;
                    break;

                case JOY_DOWN_D:                        /* 摇杆DOWN键按下 */
                    if (g_tAD7606.ucOS > 0)
                    {
                        g_tAD7606.ucOS--;
                    }
                    AD7606_SetOS(g_tAD7606.ucOS);
                    ucRefresh = 1;

                AD7606_StartRecord(AD7606_SampleFreq[g_tAD7606.ucOS]);    /* 启动当前过采样下最高速度 */
                    break;

                default:
                    /* 其他的键值不处理 */
                    break;
            }
        }
    }
}

36.13   总结

本章节涉及到的知识点非常多,实战性较强,需要大家稍花点精力去研究。

 

你可能感兴趣的:(【STM32F429开发板用户手册】第36章 STM32F429的FMC总线应用之DMA双缓冲驱动AD7606(8通道同步采样, 16bit, 正负10V))