当前位置:   article > 正文

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

ad7606 stm32

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

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

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

目录

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

36.1 初学者重要提示

36.2 ADC结构分类

36.3 AD7606硬件设计

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

36.5 AD7606的FMC接口硬件设计

36.6 AD7606的FMC DMA实现思路

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

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

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

36.6.4 FMC DMA双缓冲实现

36.7 AD7606的FMC接口驱动设计

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

36.7.2 第3步,FMC时钟源

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

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

36.7.5 第6步,FMC DMA双缓冲

36.7.6 第7步,AD7606过采样设置

36.7.7 第8步,AD7606量程设置

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

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

36.8.1 函数bsp_InitAD7606

36.8.2 函数AD7606_SetOS

36.8.3 函数AD7606_SetInputRange

36.8.4 函数AD7606_Reset

36.8.5 函数AD7606_StartRecord

36.8.6 函数AD7606_StopRecord

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

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

36.9.2 J-Scope多通道传输实现

36.9.3 J-Scope带宽问题

36.10          AD7606驱动移植和使用

36.11          实验例程设计框架

36.12          实验例程说明(MDK)

36.13   总结


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的并行接线方式如下:

这里实现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支持的突发方式如下,下面这个表格尤其重要,配置突发务必要按照这个表格来配置:

 

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

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

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

 

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

  •   t2

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

  •   t3

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

  •   t6

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

  •   tconv

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

 

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

36.6.4 FMC DMA双缓冲实现

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

36.7 AD7606的FMC接口驱动设计

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

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

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上的,频率与内核主频一样:

 

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

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

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

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

  DATAST(DataSetupTime,数据建立时间)

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

 

  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闪退问题解决办法

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

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

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);

多路效果:

 

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路

 

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

 

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          实验例程设计框架

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

  第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。

J-Scope波形效果:

 

模块插入位置:

程序设计:

  系统栈大小分配:

  硬件外设初始化

硬件外设的初始化是在 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   总结

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

 

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

闽ICP备14008679号