【STM32H7教程】第76章 STM32H7的FMC总线应用之驱动AD7606(8通道同步采样, 16bit, 正负10V)

完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980

第76章       STM32H7的FMC总线应用之驱动AD7606(8通道同步采样, 16bit, 正负10V)

本章节为大家讲解FMC总线驱动数模转换器AD7606,实战性较强。

目录

第76章       STM32H7的FMC总线应用之驱动AD7606(8通道同步采样, 16bit, 正负10V)

76.1 初学者重要提示

76.2 ADC结构分类

76.2.1 SAR ADC(逐次逼近型)

76.2.2 Sigma-Delta ADC

76.2.3 Integrating ADC

76.2.4 FLASH ADC

76.2.5 Pipelined ADC

76.2.6 Two Step ADC

76.3 AD7606硬件设计

76.3.1 AD7606硬件接口

76.3.2 AD7606模块(通用版)

76.3.3 AD7606模块(屏蔽版)

76.3.4 AD7606模块(磁耦高速隔离)

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

76.4.1 AD7606基础信息

76.4.2 AD7606常用引脚的作用

76.4.3 AD7606输出电压计算公式

76.4.4 AD7606时序图

76.4.5 AD7606的过采样

76.5 AD7606的FMC接口硬件设计

76.5.1 FMC的块区分配

76.5.2 译码器及其地址计算

76.6 AD7606的FMC接口驱动设计

76.6.1 第1步,AD7606整体驱动框架设计

76.6.2 第2步,AD7606所涉及到的GPIO配置

76.6.3 第3步,FMC的时钟源选择

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

76.6.5 第5步,FMC的MPU配置

76.6.6 第6步,AD7606的软件定时器读取数据(方案一)

76.6.7 第7步,AD7606的FIFO方式实时读取数据(方案二)

76.6.8 第8步,AD7606的双缓冲方式存储思路

76.6.9 第9步,AD7606过采样设置

76.6.10   第10步,AD7606量程设置

76.6.11   第11步,操作数据位宽注意事项

76.7 AD7606板级支持包(bsp_fmc_ad7606.c)

76.7.1 函数bsp_InitAD7606

76.7.2 函数AD7606_SetOS

76.7.3 函数AD7606_SetInputRange

76.7.4 函数AD7606_Reset

76.7.5 函数AD7606_StartConvst

76.7.6 函数AD7606_ReadNowAdc

76.7.7 函数AD7606_EnterAutoMode

76.7.8 函数AD7606_StartRecord

76.7.9 函数AD7606_StopRecord

76.7.10   函数AD7606_FifoNewData

76.7.11   函数AD7606_ReadFifo

76.7.12   函数AD7606_FifoFull

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

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

76.8.2 J-Scope多通道传输实现

76.8.3 J-Scope带宽问题

76.9 AD7606驱动移植和使用

76.10          实验例程设计框架

76.11          实验例程说明(MDK)

76.12          实验例程说明(IAR)

76.13   总结


 

76.1 初学者重要提示

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

76.2 ADC结构分类

这里将六种DAC结构为大家做个普及。注,这些知识翻译自美信和TI的英文技术手册。

 

76.2.1 SAR ADC(逐次逼近型)

逐次逼近型ADC通常是中高分辨率的首选架构,采样速率通常低于5Msps。SAR ADC最常见的分辨率范围是8位到20位,并具有低功耗和小尺寸的特点。这种组合使其非常适合各种应用,例如自动测试设备,电池供电的设备,数据采集系统,医疗仪器,电机和过程控制,工业自动化,电信,测试和测量,便携式系统,高速闭环系统和窄带接收器。

76.2.2 Sigma-Delta ADC

Sigma-delta ADC主要用于低速应用中,该应用需要通过过采样来权衡速度和分辨率,然后进行滤波以降低噪声。24位sigma-delta转换器用于自动化测试设备,高精度便携式传感器,医疗和科学仪器以及地震数据采集等应用中。

76.2.3 Integrating ADC

集成ADC提供高分辨率,并且可以提供良好的线路频率和噪声抑制。集成架构提供了一种新颖且直接的方法,可将低带宽模拟信号转换为数字表示形式。这些类型的转换器通常包括用于LCD或LED显示器的内置驱动器,并且在许多便携式仪器应用中都可以找到,包括数字面板表和数字万用表。

76.2.4 FLASH ADC

Flash ADC是将模拟信号转换为数字信号的最快方法。它们适用于需要非常大带宽的应用。然而,闪存转换器功率高,具有相对较低的分辨率,并且可能非常昂贵。这将它们限制在通常无法以其他任何方式解决的高频应用中。示例包括数据采集,卫星通信,雷达处理,示波器和高密度磁盘驱动器。

76.2.5 Pipelined ADC

流水线ADC已成为最受欢迎的ADC体系结构,其采样率从每秒几兆采样(MS / s)到最高100MS / s +,分辨率为8至16位。它们提供的分辨率和采样率,可覆盖各种应用,包括CCD成像,超声医学成像,数字接收器,基站,数字视频(例如HDTV),xDSL,电缆调制解调器和快速以太网。

76.2.6 Two Step ADC

两步ADC也称为子范围转换器,有时也称为多步或half flash(比Flash架构慢)。这是Flash ADC和流水线ADC的交叉点。与Flash ADC相比,可以实现更高的分辨率或更小的裸片尺寸。

76.3 AD7606硬件设计

这里将开发板上的AD7606硬件接口,普通型AD7606模块,屏蔽型AD7606模块和磁耦高速隔离型AD7606模块为大家做个说明。

AD7606的原理图下载:

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

76.3.1 AD7606硬件接口

V7板子上AD7606模块的插座的原理图如下:

实际对应开发板的位置如下:

为了方便大家更好的理解接线,下面是框图:

模块引脚说明:

  •   OS2 OS1 OS2 :

组合状态选择过采样模式。

    •   000表示无过采样,最大200Ksps采样速率。
    •   001表示2倍过采样, 也就是硬件内部采集2个样本求平均。
    •   010表示4倍过采样, 也就是硬件内部采集4个样本求平均。
    •   011表示8倍过采样, 也就是硬件内部采集8个样本求平均。
    •   100表示16倍过采样, 也就是硬件内部采集16个样本求平均。
    •   101表示32倍过采样, 也就是硬件内部采集32个样本求平均。
    •   110表示64倍过采样, 也就是硬件内部采集64个样本求平均。

过采样倍率越高,ADC转换时间越长,可得到的最大采样频率就越低。

  •   CVA,CVB :

启动AD转换的控制信号。CVA决定1-4通道,CVB决定5-8通道。2个信号可以错开短暂的时间。一般情况可以将CVA,CVB并联在一起。

  •   RAGE :

量程范围选择。0表示正负5V, 1表示正负10V。

  •  RD :

读信号。

  •   RST :

复位信号。

  •   BUSY :

忙信号。

  •   CS :

片选信号。

  •   FRST :

第1个通道样本的指示信号。【注,此引脚可以省略不使用】

  •   VIO :

通信接口电平。

  •   DB0-DB15 :

数据总线。

 

如果采用SPI接口方式,接线框图如下:

76.3.2 AD7606模块(通用版)

产品规格:

1、 16bit分辨率,内置基准,单5V供电。

2、 8路模拟输入,阻抗1M欧姆。【无需负电源,无需前端模拟运放电路,可直接接传感器输出】

3、 输入范围可以选择正负5V或者正负10V,可通过IO控制量程。

4、 最大采样频率 200Ksps,支持8档过采样设置(可以有效降低抖动)。

5、 通信接口支持SPI或16位总线方式(也支持8位总线,一般用的比较少),接口IO电平可以是5V或3.3V。

重要提示:

1、 AD7606的配置很简单,它没有内部寄存器。量程范围和过采样参数是通过外部IO控制的。采样速率由MCU或DSP提供的脉冲频率控制。

2、 AD7606必须使用单5V供电。

3、 AD7606和MCU之间的通信接口电平由VIO(VDRIVE)引脚控制。也就是说VIO必须接单片机的电源,可以是3.3V也可以是5V。

产品效果:

 

 

 

8080或者SPI接口方式选择

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

并口模式跳线:R1 悬空(不贴),R2贴10K电阻。

SPI接口模式跳线:R1 贴10K电阻,R2 悬空(不贴)。

76.3.3 AD7606模块(屏蔽版)

屏蔽版主要是为了更好的应对复杂的电磁工作,软件代码与非屏蔽版是一样的:

 

 

 

 

 

76.3.4 AD7606模块(磁耦高速隔离)

该款ADC模块采用磁耦隔离技术隔离SPI通信接口,采用DC-DC隔离电源模块隔离供电电源。高速SPI接口,ADC主芯片采用AD7606芯片。8通道200KHz采样。量程和滤波设置通过短路焊点设置。

产品规格

模拟通道 : 8路同步采集。

采样频率 : 最大200KHz。

ADC分辨率 : 16bit。

输入量程 : 正负5V或正负10V (通过焊点切换)。

滤波设置 : 0 - 64 共7级硬件均值滤波。

供电电压 : 5.0V,  耗电最大50mA。

通信接口 : SPI,最大时钟频率 16MHz。

接口电平 : 3.3V 或 5V  (3.3V时,耗电15mA)。

产品特点

1、电源隔离,隔离电压1500V。

2、SPI通信接口隔离,高速磁耦隔离技术。

3、短路点切换量程和过采样(滤波)参数。

4、体积小,2.0mm间距排针,节约主板面积。

产品效果:

 

引脚定义和接线图:

 

 

 

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

驱动AD7606需要对下面这些知识点有个认识。

76.4.1 AD7606基础信息

  •   支持8通道同步采样,每个通道最高200Ksps,16bit分辨率。
  •   真双极模拟输入范围:±10V、±5V。
  •   5V单模拟电源,VDRIVER支持2.3V到5V。
  •   完全集成的数据采集解决方案:
    •   模拟输入钳位保护,可以耐受±16.5V的电压。
    •   具有1MΩ模拟输入阻抗的输入缓冲器。
    •   二阶抗混叠模拟滤波器。
    •  片内精密基准电压及缓冲。
    •   通过数字滤波器,提供过采样功能。
  •   灵活的并行/串行即可,支持SPI/QSPI/MICROWIRE/DSP等。
  •   性能
    •   模拟输入通道提供7KV ESD。
    •   95.5dB SNR,-107dB THD,±0.5 LSB INL,±0.5 LSB DNL。
    •   低功耗:100mW。
    •   待机功耗:25mW。

 

76.4.2 AD7606常用引脚的作用

AD7606的封装形式:

 

这里把常用的几个引脚做个说明:

  •   AVcc

模拟电源电压,4.75V到5.25V。这是内部前端放大器和ADC内核的电源电压。应将这些电压引脚去偶接AGND。

  •   AGND

模拟地,这些引脚是AD7606上所有模拟电路的接地基准点。所有模拟输入信号和外部基准信号都应参考这些引脚。

  •   OS2 OS1 OS2 :

组合状态选择过采样模式。

    •   000表示无过采样,最大200Ksps采样速率。
    •   001表示2倍过采样, 也就是硬件内部采集2个样本求平均。
    •   010表示4倍过采样, 也就是硬件内部采集4个样本求平均。
    •   011表示8倍过采样, 也就是硬件内部采集8个样本求平均。
    •   100表示16倍过采样, 也就是硬件内部采集16个样本求平均。
    •   101表示32倍过采样, 也就是硬件内部采集32个样本求平均。
    •   110表示64倍过采样, 也就是硬件内部采集64个样本求平均。

过采样倍率越高,ADC转换时间越长,可得到的最大采样频率就越低。

  •   CONVSTA,CONVSTB :

启动AD转换的控制信号。CONVSTA决定1-4通道,CONVSTB决定5-8通道。2个信号可以错开短暂的时间。一般情况可以将CVA,CVB并联在一起。

  •   RAGE :

量程范围选择。0表示正负5V, 1表示正负10V.

  •  RD /SCL:

读信号,低电平有效。

  •   RESET

复位信号。

  •   BUSY :

CONVST A和CONVST B均达到上升沿后,此引脚变为逻辑高电平,表示转换过程已经开始,BUSY输出保持高电平,直到所有通道的转换过程完成为止。BUSY下降沿表示转换数据正被锁存至输出数据寄存器,此时用户就可以读取数据。

  •   CS :

片选信号,低电平有效。

  •   FRST :

第1个通道样本的指示信号。【注,此引脚可以省略不使用】

  •   VDriver:

通信接口电平。

  •   DB0-DB15 :

数据总线。

  •   REF SELECT

内部/外部基准电压选择。如果设置此引脚设为逻辑高电平,使用内部基准电压。如果此引脚设为逻辑低电平,则内部基准电压禁止,必须将外部基准电压加到REFIN/REFOUT引脚。

  •   REFIN/REFOUT

基准电压输入(REFIN)/基准电压输出(REFOUT)引脚,如果REF SELECT引脚设置为逻辑高电平,此引脚将提供2.5V片内基准电压供外部使用。或者可以将REF SELECT引脚设置为逻辑低电平将禁止用内部基准电压。

  •   V1到V8

模拟输入,此引脚为单端模拟输入,此通道的模拟输入范围由RANGE引脚决定。

  •   V1GND到V8GND

模拟输入接地引脚,这些引脚与模拟输入引脚V1到V8对应,所有模拟输入AGND引脚都应连接到系统的AGND平面。

76.4.3 AD7606输出电压计算公式

AD7606的计算公式如下:

 

采用二进制补码(其实就是16bit有符号数,将转换结果定义为int16_t即可),因为AD7606支持正负压采集。

  •   VIN

AD7606采集到的电压值范围-32768到32767。

  •   REF

一般使用内部基准,即2.5V。

76.4.4 AD7606时序图

了解时序参数是驱动AD7606能否成功的关键,我们这里对几个重要的参数做个说明。

1、AD7606的CONVST转换时序(转换之后读取数据):

 

  •   t5

CONVST A和CONVST B上升沿之间最大允许的延迟时间。一般我们是用一根控制线同时控制CONVST A和CONVST B,因此可以不用管这个时间。

  •   tCYCLE

并行模式,转换后并读取数据的最大值是5us,即最高支持的时钟速度是20MHz及其以上。

  •   tCONV

转换时间。

  •   t3

最短的CONVST A/B电平脉冲,最小值25ns。

  •   t4

BUSY下降沿到CS下降沿设置时间,最小值0ns,所以可以忽略。

 

2、AD7606的并行驱动模式有两种时序图,一个是独立的CS片选和RD读信号时序图:

 

  •   t8

CS到RD的设置时间,最小值是0ns,可以忽略。

  •   t10

RD读信号的低电平脉冲宽度,通信电压不同,时间不同。对于STM32来说,FMC通信电平一般是3.3V,即最小值21ns。

  •   t11

RD高电平脉冲宽度,最小值15ns。

  •   t9

CS到RD保持时间,最小值0ns,可以忽略。

  •   13到t17 

这几个参数了解下即可:

 

3、另一个是CS片选和RD相连的方式:

 

这个时序里面最重要的是t12。

  •   t12

CS和RD的高电平脉冲宽度,最小值22ns。

 

第2个和第3个时序图的主要区别是连续读取8路数据时,一个CS信号是全程低电平,另一个CS信号是与RD信号同步,每读取完一路,拉高一次。

76.4.5 AD7606的过采样

使用过采样可以改善SNR信噪比。SNR性能随着过采样倍率提高而改善,具体参数如下:

 

通过这个表,我们可以方便的了解不同过采样下的信噪比,3dB带宽时的频率和最高支持的采样率。

注意正确的理解过采样,比如我们设置是1Ksps采样率,64倍过采样。意思是指每次采样,AD7606会采样64次数据并求平均,相当于AD7606以64Ksps进行采样的,只是将每64个采样点的值做了平均,用户得到的值就是平均后的数值。因此,如果使用AD7606最高的200Ksps采样率,就不可以使用过采样了。

76.5 AD7606的FMC接口硬件设计

FMC硬件接口涉及到的知识点稍多,下面逐一为大家做个说明。

76.5.1 FMC的块区分配

FMC总线可操作的地址范围0x60000000到0xDFFFFFFF,具体的框图如下:

 

从上面的框图可以看出,NOR/PSRAM/SRAM块区有4个片选NE1,NE2,NE3和NE4,但由于引脚复用,部分片选对应的引脚要用于其他功能,而且要控制的总线外设较多,导致片选不够用。因此需要增加译码器。

76.5.2 译码器及其地址计算

有了前面的认识之后再来看下面的译码器电路:

 

SN74LVC1G139APWR是双2-4线地址译码器,也就是带了两个译码器。原理图上仅用了一个。下面是139的真值表和引脚功能:

 

通过上面的原理图和真值表就比较好理解了,真值表的输出是由片选FMC_NE1和地址线FMC_A10、FMC_A11控制。

FMC_NE1 输出低电平:

  •   FMC_A11(B),FMC_A10(A) = 00时,1Y0输出的低电平,选择的是OLED。
  •   FMC_A11(B),FMC_A10(A) = 01时,1Y1输出的低电平,选择的是74HC574。
  •   FMC_A11(B),FMC_A10(A) = 10时,1Y2输出的低电平,选择的是DM9000。
  •   FMC_A11(B),FMC_A10(A) = 11时,1Y3输出的低电平,选择的是AD7606。

然后我们再计算译码器的地址,注意,这里地址的计算都是按照FMC的32bit访问模式计算的,因为我们的V7程序中是将NE1对应的FMC配置为32bit模式了。

具体FMC的32bit访问模式,16bit访问模式和8bit访问模式的区别在第47章的2.4小节有详细讲解。

 

32bit模式下,我们计算A10和A11的时候,实际上需要按HADDR12和HADDR13计算的。

如果来算NE1 + HADDR12 + HADDR13的四种组合地址就是如下:

NE1 + HADDR13 + HADDR12 = 0x60000000 +  0<<13 + 0<<12 = 0x60000000

NE1 + HADDR13 + HADDR12 = 0x60000000 +  0<<13 + 1<<12 = 0x60001000

NE1 + HADDR13 + HADDR12 = 0x60000000 +  1<<13 + 0<<12 = 0x60002000

NE1 + HADDR13 + HADDR12 = 0x60000000 +  1<<13 + 1<<12 = 0x60003000

这样一来,原理图里面给的地址就对应上了。同理如果配置为16位模式和8位模式,大家应该也都会计算了。

76.6 AD7606的FMC接口驱动设计

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

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

76.6.1 第1步,AD7606整体驱动框架设计

主要实现了两种采集方式:

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

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

 

  •   方案一:软件定时获取方式代码框架:

可以在硬件定时器中断服务程序或者软件定时器里面实现。

定时器中断ISR:
{
    中断入口;
读取8个通道的采样结果保存到RAM;  ----> 读取的是上次的采集结果,对于连续采集来说,是没有关系的
    启动下次ADC采集;(翻转CVA和CVB)
    中断返回;
}

 

定时器的频率就是ADC采样频率。这种模式可以不连接BUSY口线。

  •   方案二:FIFO工作模式框架:

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

    将BUSY口线设置为中断下降沿触发模式;

外部中断ISR:
{
    中断入口;
    读取8个通道的采样结果保存到RAM;
}

 

  •   方案1和方案2的差异

(1)方案1 可以少用 BUSY口线,但是其他中断服务程序或者主程序临时关闭全局中断时,可能导致ADC转换周期存在轻微抖动。

(2)方案2 可以确保采集时钟的稳定性,因为它是MCU硬件产生的,但是需要多接一根BUSY口线。

76.6.2 第2步,AD7606所涉及到的GPIO配置

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

/*
*********************************************************************************************************
*    函 数 名: AD7606_CtrlLinesConfig
*    功能说明: 配置GPIO口线,FMC管脚设置为复用功能
*    形    参: 无
*    返 回 值: 无
*********************************************************************************************************
*/
/*
    安富莱STM32-H7开发板接线方法:4片74HC574挂在FMC 32位总线上。1个地址端口可以扩展出32个IO
    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一起译码
    PD7/FMC_NE1        --- 主片选(OLED, 74HC574, DM9000, AD7606)    

     +-------------------+------------------+
     +   32-bits Mode: D31-D16              +
     +-------------------+------------------+
     | PH8 <-> FMC_D16   | PI0 <-> FMC_D24  |
     | PH9 <-> FMC_D17   | PI1 <-> FMC_D25  |
     | PH10 <-> FMC_D18  | PI2 <-> FMC_D26  |
     | PH11 <-> FMC_D19  | PI3 <-> FMC_D27  |
     | PH12 <-> FMC_D20  | PI6 <-> FMC_D28  |
     | PH13 <-> FMC_D21  | PI7 <-> FMC_D29  |
     | PH14 <-> FMC_D22  | PI9 <-> FMC_D30  |
     | PH15 <-> FMC_D23  | PI10 <-> FMC_D31 |
     +------------------+-------------------+
*/

/* 
    控制AD7606参数的其他IO分配在扩展的74HC574上
    X13 - AD7606_OS0
    X14 - AD7606_OS1
    X15 - AD7606_OS2
    X24 - AD7606_RESET
    X25 - AD7606_RAGE    
    
    PE5 - AD7606_BUSY
*/
static void AD7606_CtrlLinesConfig(void)
{
    /* bsp_fm_io 已配置fmc,bsp_InitExtIO();
       此处可以不必重复配置 
    */

    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_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_7 |
                                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;
    HAL_GPIO_Init(GPIOG, &gpio_init_structure);
    
    /* 配置GPIOH */
    gpio_init_structure.Pin = 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(GPIOH, &gpio_init_structure);

    /* 配置GPIOI */
    gpio_init_structure.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3 | GPIO_PIN_6
                        | GPIO_PIN_7 | GPIO_PIN_9 | GPIO_PIN_10;
    HAL_GPIO_Init(GPIOI, &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_MEDIUM;  /* GPIO速度等级 */    

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

 

这里重点注意AD7606_CONVST和AD7606_BUSY引脚,上电后的默认配置是普通IO。另外还有过采样的3个引脚,量程配置的1个引脚和复位控制的1个引脚,均通过V7板子的扩展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)

 

76.6.3 第3步,FMC的时钟源选择

使用FMC可以选择如下几种时钟源HCLK3,PLL1Q,PLL2R和PER_CK:

我们这里直接使用HCLK3,配置STM32H7的主频为400MHz的时候,HCLK3输出的200MHz,这个速度是FMC支持的最高时钟,正好用于这里:

 

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

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

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

  •   DATAST(DataSetupTime,数据建立时间)

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

 

  •   ADDST(AddressSetupTime,地址建立时间)

DATAST实际上对应的就是76.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.            5-x-5-x-x-x  : RD高持续25ns, 低电平持续25ns. 读取8路样本数据到内存差不多就是400ns。
25.        */
26.        hsram.Instance  = FMC_NORSRAM_DEVICE;
27.        hsram.Extended  = FMC_NORSRAM_EXTENDED_DEVICE;
28.        
29.        /* FMC使用的HCLK3,主频200MHz,1个FMC时钟周期就是5ns */
30.        SRAM_Timing.AddressSetupTime       = 5; /* 5*5ns=25ns,地址建立时间,范围0 -15个FMC时钟周期个数 */
31.        SRAM_Timing.AddressHoldTime        = 2; /* 地址保持时间,配置为模式A时,用不到此参数 范围1 -15个
32.                                                    时钟周期个数 */
33.        SRAM_Timing.DataSetupTime          = 5;  /* 5*5ns=25ns,数据建立时间,范围1 -255个时钟周期个数 */
34.        SRAM_Timing.BusTurnAroundDuration  = 1;  /* 此配置用不到这个参数 */
35.        SRAM_Timing.CLKDivision            = 2;  /* 此配置用不到这个参数 */
36.        SRAM_Timing.DataLatency            = 2;  /* 此配置用不到这个参数 */
37.        SRAM_Timing.AccessMode             = FMC_ACCESS_MODE_A; /* 配置为模式A */
38.        hsram.Init.NSBank             = FMC_NORSRAM_BANK1;              /* 使用的BANK1,即使用的片选
39.                                                                            FMC_NE1 */
40.        hsram.Init.DataAddressMux     = FMC_DATA_ADDRESS_MUX_DISABLE;   /* 禁止地址数据复用 */
41.        hsram.Init.MemoryType         = FMC_MEMORY_TYPE_SRAM;           /* 存储器类型SRAM */
42.        hsram.Init.MemoryDataWidth    = FMC_NORSRAM_MEM_BUS_WIDTH_32;   /* 32位总线宽度 */
43.        hsram.Init.BurstAccessMode    = FMC_BURST_ACCESS_MODE_DISABLE;  /* 关闭突发模式 */
44.        hsram.Init.WaitSignalPolarity = FMC_WAIT_SIGNAL_POLARITY_LOW;   /* 用于设置等待信号的极性,关闭突
45.                                                                            发模式,此参数无效 */
46.        hsram.Init.WaitSignalActive   = FMC_WAIT_TIMING_BEFORE_WS;      /* 关闭突发模式,此参数无效 */
47.        hsram.Init.WriteOperation     = FMC_WRITE_OPERATION_ENABLE;     /* 用于使能或者禁止写保护 */
48.        hsram.Init.WaitSignal         = FMC_WAIT_SIGNAL_DISABLE;        /* 关闭突发模式,此参数无效 */
49.        hsram.Init.ExtendedMode       = FMC_EXTENDED_MODE_DISABLE;      /* 禁止扩展模式 */
50.        hsram.Init.AsynchronousWait   = FMC_ASYNCHRONOUS_WAIT_DISABLE;  /* 用于异步传输期间,使能或者禁止
51.                                                                            等待信号,这里选择关闭 */
52.        hsram.Init.WriteBurst         = FMC_WRITE_BURST_DISABLE;        /* 禁止写突发 */
53.        hsram.Init.ContinuousClock    = FMC_CONTINUOUS_CLOCK_SYNC_ONLY; /* 仅同步模式才做时钟输出 */
54.        hsram.Init.WriteFifo          = FMC_WRITE_FIFO_ENABLE;          /* 使能写FIFO */
55.    
56.        /* 初始化SRAM控制器 */
57.        if (HAL_SRAM_Init(&hsram, &SRAM_Timing, &SRAM_Timing) != HAL_OK)
58.        {
59.            /* 初始化错误 */
60.            Error_Handler(__FILE__, __LINE__);
61.        }    
62.    }

 

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

  •   第15- 16行,对作为局部变量的HAL库结构体做初始化,防止不确定值配置时出问题。
  •   第30行,地址建立时间,对于AD7606来说,这个地方最小值22ns。保险起见,这里取值5个FMC时钟周期,即25ns。
  •   第31行,地址保持时间,对于FMC模式A来说,此参数用不到。
  •   第33行,数据建立时间,对于AD7606来说,这个地方最小值是21ns,保险起见,这里取值5个FMC时钟周期,即25ns。
  •   第34 – 36行,当前配置用不到这三个参数。
  •   第38行,使用的BANK1,即使用的片选FMC_NE1。

76.6.5 第5步,FMC的MPU配置

实际测试发现,使能FMC_NE1所管理的存储区的Cache功能后,会出现扩展IO的NE片选和NWE信号输出2次的问题。经过各种Cache方式配置、FMC带宽配置、操作FMC时的数据位宽设置,发现禁止了Cache功能就正常了,也就是说,设置FMC_NE1所管理的存储区MPU属性为Device或者Strongly Ordered即可。

/* 配置FMC扩展IO的MPU属性为Device或者Strongly Ordered */
    MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
    MPU_InitStruct.BaseAddress      = 0x60000000;
    MPU_InitStruct.Size             = ARM_MPU_REGION_SIZE_64KB;    
    MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;
    MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE;
    MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
    MPU_InitStruct.Number           = MPU_REGION_NUMBER1;
    MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL0;
    MPU_InitStruct.SubRegionDisable = 0x00;
    MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;
    
    HAL_MPU_ConfigRegion(&MPU_InitStruct);

 

MPU配置中直接从FMC_NE1的首地址开始配置,设置了64KB空间的属性。将FMC_NE1通过译码器所管理的所有设备地址全部设置为此配置:

 

76.6.6 第6步,AD7606的软件定时器读取数据(方案一)

AD7606的软件定时器读取方式比较简单,周期性调用下面两个函数即可:

AD7606_ReadNowAdc();        /* 读取采样结果 */
AD7606_StartConvst();        /* 启动下次转换 */

函数AD7606_ReadNowAdc的实现如下:

/* AD7606 FSMC总线地址,只能读,无需写 */
#define AD7606_RESULT()    *(__IO uint16_t *)0x60003000

void AD7606_ReadNowAdc(void)
{
    g_tAD7606.sNowAdc[0] = AD7606_RESULT();    /* 读第1路样本 */
    g_tAD7606.sNowAdc[1] = AD7606_RESULT();    /* 读第2路样本 */
    g_tAD7606.sNowAdc[2] = AD7606_RESULT();    /* 读第3路样本 */
    g_tAD7606.sNowAdc[3] = AD7606_RESULT();    /* 读第4路样本 */
    g_tAD7606.sNowAdc[4] = AD7606_RESULT();    /* 读第5路样本 */
    g_tAD7606.sNowAdc[5] = AD7606_RESULT();    /* 读第6路样本 */
    g_tAD7606.sNowAdc[6] = AD7606_RESULT();    /* 读第7路样本 */
    g_tAD7606.sNowAdc[7] = AD7606_RESULT();    /* 读第8路样本 */

    AD7606_SEGGER_RTTOUT();
}

启动ADC转换的函数实现如下:

/*
*********************************************************************************************************
*    函 数 名: AD7606_StartConvst
*    功能说明: 启动1次ADC转换
*    形    参: 无
*    返 回 值: 无
*********************************************************************************************************
*/
void AD7606_StartConvst(void)
{
    /* page 7:  CONVST 高电平脉冲宽度和低电平脉冲宽度最短 25ns */
    /* CONVST平时为高 */
    CONVST_0();
    CONVST_0();
    CONVST_0();

    CONVST_1();
}

 

76.6.7 第7步,AD7606的FIFO方式实时读取数据(方案二)

通过下面的框图可以对AD7606的FIFO方式有个整体认识:

 

  •   启动采集函数AD7606_StartRecord

这个函数的主要作用是配置TIM8的CH1 PWM输出并使能BUSY引脚的EXTI中断。

/*
*********************************************************************************************************
*    函 数 名: AD7606_StartRecord
*    功能说明: 开始采集
*    形    参: 无
*    返 回 值: 无
*********************************************************************************************************
*/
void AD7606_StartRecord(uint32_t _ulFreq)
{
    AD7606_StopRecord();

    AD7606_Reset();            /* 复位硬件 */
    AD7606_StartConvst();        /* 启动采样,避免第1组数据全0的问题 */

    g_tAdcFifo.usRead = 0;        /* 必须在开启定时器之前清0 */
    g_tAdcFifo.usWrite = 0;
    g_tAdcFifo.usCount = 0;
    g_tAdcFifo.ucFull = 0;

    AD7606_EnterAutoMode(_ulFreq);
}
/*
*********************************************************************************************************
*    函 数 名: AD7606_EnterAutoMode
*    功能说明: 配置硬件工作在自动采集模式,结果存储在FIFO缓冲区。
*    形    参:  _ulFreq : 采样频率,单位Hz,    1k,2k,5k,10k,20K,50k,100k,200k
*    返 回 值: 无
*********************************************************************************************************
*/
void AD7606_EnterAutoMode(uint32_t _ulFreq)
{
    /* 配置PC6为TIM8_CH1功能,输出占空比50%的方波 */
    bsp_SetTIMOutPWM(CONVST_GPIO, CONVST_PIN, CONVST_TIMX,  CONVST_TIMCH, _ulFreq, 5000);
    
    /* 配置PE5, BUSY 作为中断输入口,下降沿触发 */
    {
        GPIO_InitTypeDef   GPIO_InitStructure;
    
        CONVST_RCC_GPIO_CLK_ENABLE();    /* 打开GPIO时钟 */
        __HAL_RCC_SYSCFG_CLK_ENABLE();

        GPIO_InitStructure.Mode = GPIO_MODE_IT_FALLING;    /* 中断下降沿触发 */
        GPIO_InitStructure.Pull = GPIO_NOPULL;
        GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_LOW;        
        GPIO_InitStructure.Pin = BUSY_PIN;
        HAL_GPIO_Init(BUSY_GPIO, &GPIO_InitStructure);    

        HAL_NVIC_SetPriority(BUSY_IRQn, 2, 0);
        HAL_NVIC_EnableIRQ(BUSY_IRQn);    
    }        
}

 

  •   AD7606转换完毕后,中断服务程序的处理。

下面这几个函数的调用关系是

    •   EXTI9_5_IRQHandler调用HAL_GPIO_EXTI_IRQHandler。
    •   HAL_GPIO_EXTI_IRQHandler调用HAL_GPIO_EXTI_Callback。
    •   HAL_GPIO_EXTI_Callback调用AD7606_ISR。
    •   AD7606_ISR调用AD7606_ReadNowAdc。
/*
*********************************************************************************************************
*    函 数 名: EXTI9_5_IRQHandler
*    功能说明: 外部中断服务程序。
*    形    参:无
*    返 回 值: 无
*********************************************************************************************************
*/
void EXTI9_5_IRQHandler(void)
{
    HAL_GPIO_EXTI_IRQHandler(BUSY_PIN);
}

/*
*********************************************************************************************************
*    函 数 名: EXTI9_5_IRQHandler
*    功能说明: 外部中断服务程序入口, AD7606_BUSY 下降沿中断触发
*    形    参: 无
*    返 回 值: 无
*********************************************************************************************************
*/
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
    if (GPIO_Pin == BUSY_PIN)
    {
        AD7606_ISR();
    }
}

/*
*********************************************************************************************************
*    函 数 名: AD7606_ISR
*    功能说明: 定时采集中断服务程序
*    形    参: 无
*    返 回 值: 无
*********************************************************************************************************
*/
void AD7606_ISR(void)
{
    uint8_t i;

    AD7606_ReadNowAdc();

    for (i = 0; i < 8; i++)
    {
        g_tAdcFifo.sBuf[g_tAdcFifo.usWrite] = g_tAD7606.sNowAdc[i];
        if (++g_tAdcFifo.usWrite >= ADC_FIFO_SIZE)
        {
            g_tAdcFifo.usWrite = 0;
        }
        if (g_tAdcFifo.usCount < ADC_FIFO_SIZE)
        {
            g_tAdcFifo.usCount++;
        }
        else
        {
            g_tAdcFifo.ucFull = 1;        /* FIFO 满,主程序来不及处理数据 */
        }
    }
}

/*
*********************************************************************************************************
*    函 数 名: AD7606_ReadNowAdc
*    功能说明: 读取8路采样结果。结果存储在全局变量 g_tAD7606
*    形    参: 无
*    返 回 值: 无
*********************************************************************************************************
*/
void AD7606_ReadNowAdc(void)
{
    g_tAD7606.sNowAdc[0] = AD7606_RESULT();    /* 读第1路样本 */
    g_tAD7606.sNowAdc[1] = AD7606_RESULT();    /* 读第2路样本 */
    g_tAD7606.sNowAdc[2] = AD7606_RESULT();    /* 读第3路样本 */
    g_tAD7606.sNowAdc[3] = AD7606_RESULT();    /* 读第4路样本 */
    g_tAD7606.sNowAdc[4] = AD7606_RESULT();    /* 读第5路样本 */
    g_tAD7606.sNowAdc[5] = AD7606_RESULT();    /* 读第6路样本 */
    g_tAD7606.sNowAdc[6] = AD7606_RESULT();    /* 读第7路样本 */
    g_tAD7606.sNowAdc[7] = AD7606_RESULT();    /* 读第8路样本 */

    AD7606_SEGGER_RTTOUT();
}

 

这里的FIFO比较好理解,与前面按键FIFO章节的实现是一样的,详情可重温下按键FIFO的实现。

76.6.8 第8步,AD7606的双缓冲方式存储思路

为了方便大家实时处理采集的数据,专门预留了一个弱定义函数AD7606_SEGGER_RTTOUT,方便大家将采集函数存储到双缓冲里面,这个函数是在中断服务程序里面调用的。

/*
*********************************************************************************************************
*    函 数 名: AD7606_ReadNowAdc
*    功能说明: 读取8路采样结果。结果存储在全局变量 g_tAD7606
*    形    参: 无
*    返 回 值: 无
*********************************************************************************************************
*/
/* 弱定义,方便用户将采集的结果实时输出 */
__weak void AD7606_SEGGER_RTTOUT(void)
{
    
}

void AD7606_ReadNowAdc(void)
{
    g_tAD7606.sNowAdc[0] = AD7606_RESULT();    /* 读第1路样本 */
    g_tAD7606.sNowAdc[1] = AD7606_RESULT();    /* 读第2路样本 */
    g_tAD7606.sNowAdc[2] = AD7606_RESULT();    /* 读第3路样本 */
    g_tAD7606.sNowAdc[3] = AD7606_RESULT();    /* 读第4路样本 */
    g_tAD7606.sNowAdc[4] = AD7606_RESULT();    /* 读第5路样本 */
    g_tAD7606.sNowAdc[5] = AD7606_RESULT();    /* 读第6路样本 */
    g_tAD7606.sNowAdc[6] = AD7606_RESULT();    /* 读第7路样本 */
    g_tAD7606.sNowAdc[7] = AD7606_RESULT();    /* 读第8路样本 */

    AD7606_SEGGER_RTTOUT();
}

 

本章是将此函数用于实时采集数据并输出到J-Scope。

76.6.9 第9步,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;
    }
}

 

76.6.10   第10步,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 */
    }
}

 

76.6.11   第11步,操作数据位宽注意事项

在bsp_fmc_ad7606.c文件开头有个宏定义

#define AD7606_RESULT() *(__IO uint16_t *)0x60003000

特别注意,这里是要操作地址0x60003000上的16位数据空间,即做了一个强制转换uint16_t *。

76.7 AD7606板级支持包(bsp_fmc_ad7606.c)

AD7606驱动文件bsp_fmc_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

76.7.1 函数bsp_InitAD7606

函数原型:

void bsp_InitAD7606(void)

函数描述:

主要用于AD7606的初始化。

76.7.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倍过采样。

76.7.3 函数AD7606_SetInputRange

函数原型:

void AD7606_SetInputRange(uint8_t _ucRange)

函数描述:

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

函数参数:

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

76.7.4 函数AD7606_Reset

函数原型:

void AD7606_Reset(void)

函数描述:

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

76.7.5 函数AD7606_StartConvst

函数原型:

void AD7606_StartConvst(void)

函数描述:

此函数用于启动1次ADC转换。

76.7.6 函数AD7606_ReadNowAdc

函数原型:

void AD7606_ReadNowAdc(void)

函数描述:

此函数用于读取8路采样结果,结果存储在全局变量 g_tAD7606。

76.7.7 函数AD7606_EnterAutoMode

函数原型:

void AD7606_EnterAutoMode(uint32_t _ulFreq)

函数描述:

此函数用于配置硬件工作在自动采集模式,结果存储在FIFO缓冲区。一般不单独调用,函数AD7606_StartRecord会调用。

函数参数:

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

76.7.8 函数AD7606_StartRecord

函数原型:

void AD7606_StartRecord(uint32_t _ulFreq)

函数描述:

用于启动采集。

函数参数:

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

76.7.9 函数AD7606_StopRecord

函数原型:

void AD7606_StopRecord(void)

函数描述:

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

76.7.10   函数AD7606_FifoNewData

函数原型:

uint8_t AD7606_HasNewData(void)

函数描述:

此函数用于判断FIFO中是否有新数据。

函数参数:

  •   返回值,1 表示有,0表示暂无数据。

76.7.11   函数AD7606_ReadFifo

函数原型:

uint8_t AD7606_ReadFifo(uint16_t *_usReadAdc)

函数描述:

此函数用于从FIFO中读取一个ADC值。

函数参数:

  •   第1个参数是存放ADC结果的变量指针。
  •   返回值,1 表示OK,0表示暂无数据。

76.7.12   函数AD7606_FifoFull

函数原型:

uint8_t AD7606_FifoFull(void)

函数描述:

此函数用于判断FIFO是否满。

函数参数:

  •   返回值,1 表示满,0表示未满。

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

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

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

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

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

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

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

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

 

多路效果:

 

76.8.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路

 

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

 

76.9 AD7606驱动移植和使用

AD7606移植步骤如下:

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

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

/* CONVST 启动ADC转换的GPIO = PC6 */
#define CONVST_RCC_GPIO_CLK_ENABLE    __HAL_RCC_GPIOC_CLK_ENABLE
#define CONVST_TIM8_CLK_DISABLE     __HAL_RCC_TIM8_CLK_DISABLE
#define CONVST_GPIO        GPIOC
#define CONVST_PIN        GPIO_PIN_6
#define CONVST_TIMX        TIM8
#define CONVST_TIMCH    1

/* BUSY 转换完毕信号 = PE5 */
#define BUSY_RCC_GPIO_CLK_ENABLE __HAL_RCC_GPIOE_CLK_ENABLE
#define BUSY_GPIO        GPIOE
#define BUSY_PIN        GPIO_PIN_5
#define BUSY_IRQn        EXTI9_5_IRQn
#define BUSY_IRQHandler    EXTI9_5_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步:根据具体用到的FMC引脚,修改函数AD7606_CtrlLinesConfig里面做的IO配置。
  •   第4步:根据使用的FMC BANK,修改函数AD7606_FSMCConfig里面的BANK配置,这点非常容易疏忽。
  •   第5步:注意MPU配置,详情见本章77.7.5小节。
  •   第6步:初始化AD7606。

bsp_InitAD7606();  /* 配置AD7606所用的GPIO */

  •   第7步:AD7606驱动主要用到HAL库的FMC驱动文件,简单省事些可以添加所有HAL库C源文件进来。
  •   第8步:应用方法看本章节配套例子即可。

76.10          实验例程设计框架

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

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

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

  第2阶段,进入main函数:

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

76.11          实验例程说明(MDK)

配套例子:

V7-056_AD7606的FMC总线驱动方式实现(8通道同步采样, 16bit, 正负10V)

实验目的:

  1. 学习AD7606的FMC驱动方式实现。

重要提示:

  1. 板子上电后,默认是软件定时采集,0.5秒一次,适合串口展示数据。
  2. 如果需要使用J-Scope实时展示采集的波形效果,需要按下K2按键切换到FIFO模式。
  3. 如果使用的JLINK速度不够快,导致J-Scope无法最高速度实时上传,可以使用摇杆上下键设置过采样来降低上传速度。
  4. 默认情况下,程序仅上传了AD7606通道1采集的数据。
  5. 串口数据展示推荐使用SecureCRT,因为数据展示做了特别处理,方便采集数据在串口软件同一个位置不断刷新。

实验内容:

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

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

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

2、数据展示方式:

(1)软件查询方式,数据通过串口打印输出。

(2)FIFO工作模式,数据通过J-Scope实时输出。

(3)J-Scope的实时输出方法请看V7板子用户手册对应的AD7606章节。

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

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

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

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

实验操作:

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

上电后串口打印的信息:

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

J-Scope波形效果:

模块插入位置:

程序设计:

  系统栈大小分配:

  RAM空间用的DTCM:

  硬件外设初始化

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

/*
*********************************************************************************************************
*    函 数 名: bsp_Init
*    功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次
*    形    参:无
*    返 回 值: 无
*********************************************************************************************************
*/
void bsp_Init(void)
{
    /* 配置MPU */
    MPU_Config();
    
    /* 使能L1 Cache */
    CPU_CACHE_Enable();

    /* 
       STM32H7xx HAL 库初始化,此时系统用的还是H7自带的64MHz,HSI时钟:
       - 调用函数HAL_InitTick,初始化滴答时钟中断1ms。
       - 设置NVIV优先级分组为4。
     */
    HAL_Init();

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

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

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

 

  MPU配置和Cache配置:

数据Cache和指令Cache都开启。配置了AXI SRAM区(本例子未用到AXI SRAM)和FMC的扩展IO区。

/*
*********************************************************************************************************
*    函 数 名: MPU_Config
*    功能说明: 配置MPU
*    形    参: 无
*    返 回 值: 无
*********************************************************************************************************
*/
static void MPU_Config( void )
{
    MPU_Region_InitTypeDef MPU_InitStruct;

    /* 禁止 MPU */
    HAL_MPU_Disable();

    /* 配置AXI SRAM的MPU属性为Write back, Read allocate,Write allocate */
    MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
    MPU_InitStruct.BaseAddress      = 0x24000000;
    MPU_InitStruct.Size             = MPU_REGION_SIZE_512KB;
    MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;
    MPU_InitStruct.IsCacheable      = MPU_ACCESS_CACHEABLE;
    MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
    MPU_InitStruct.Number           = MPU_REGION_NUMBER0;
    MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL1;
    MPU_InitStruct.SubRegionDisable = 0x00;
    MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;

    HAL_MPU_ConfigRegion(&MPU_InitStruct);
    
    
    /* 配置FMC扩展IO的MPU属性为Device或者Strongly Ordered */
    MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
    MPU_InitStruct.BaseAddress      = 0x60000000;
    MPU_InitStruct.Size             = ARM_MPU_REGION_SIZE_64KB;    
    MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;
    MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE;    
    MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
    MPU_InitStruct.Number           = MPU_REGION_NUMBER1;
    MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL0;
    MPU_InitStruct.SubRegionDisable = 0x00;
    MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;
    
    HAL_MPU_ConfigRegion(&MPU_InitStruct);

    /*使能 MPU */
    HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
}

/*
*********************************************************************************************************
*    函 数 名: CPU_CACHE_Enable
*    功能说明: 使能L1 Cache
*    形    参: 无
*    返 回 值: 无
*********************************************************************************************************
*/
static void CPU_CACHE_Enable(void)
{
    /* 使能 I-Cache */
    SCB_EnableICache();

    /* 使能 D-Cache */
    SCB_EnableDCache();
}

 

  每10ms调用一次按键处理:

按键处理是在滴答定时器中断里面实现,每10ms执行一次检测。

/*
*********************************************************************************************************
*    函 数 名: bsp_RunPer10ms
*    功能说明: 该函数每隔10ms被Systick中断调用1次。详见 bsp_timer.c的定时中断服务程序。一些处理时间要求
*              不严格的任务可以放在此函数。比如:按键扫描、蜂鸣器鸣叫控制等。
*    形    参: 无
*    返 回 值: 无
*********************************************************************************************************
*/
void bsp_RunPer10ms(void)
{
    bsp_KeyScan10ms();
}

 

  主功能:

主程序实现如下操作:

  •   启动一个自动重装软件定时器,每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;
    uint8_t ucFifoMode;
    
    sfDispMenu();        /* 打印命令提示 */

    ucFifoMode = 0;         /* AD7606进入普通工作模式 */
    ucRefresh = 0;        /* 数据在串口刷新的标志 */
    
    AD7606_SetOS(AD_OS_NO);        /* 无过采样 */
    AD7606_SetInputRange(1);    /* 0表示输入量程为正负5V, 1表示正负10V */
    AD7606_StartConvst();        /* 启动1次转换 */

    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(2);
        }
        
        if (ucRefresh == 1)
        {
            ucRefresh = 0;

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

        if (ucFifoMode == 0)    /* AD7606 普通工作模式 */
        {
            if (bsp_CheckTimer(0))
            {
                /* 每隔500ms 进来一次. 由软件启动转换 */
                AD7606_ReadNowAdc();        /* 读取采样结果 */
                AD7606_StartConvst();        /* 启动下次转换 */

                ucRefresh = 1;    /* 刷新显示 */
            }
        }
        else
        {
            /*
                在FIFO工作模式,bsp_AD7606自动进行采集,数据存储在FIFO缓冲区。
                结果可以通过下面的函数读取:
                uint8_t AD7606_ReadFifo(uint16_t *_usReadAdc)

                大家可以将数据保存到SD卡,或者保存到外部SRAM。

                本例未对FIFO中的数据进行处理,进行打印当前最新的样本值和J-Scope的实时输出展示。

                如果主程序不能及时读取FIFO数据,那么 AD7606_FifoFull() 将返回真。

                8通道200K采样时,数据传输率 = 200 000 * 2 * 8 = 3.2MB/S
            */

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

        /* 按键检测由后台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键按下 */
                    ucFifoMode = 1;                      /* AD7606进入FIFO工作模式 */
                    g_tAD7606.ucOS = 1;                    /* 无过采样 */
                    AD7606_StartRecord(AD7606_SampleFreq[g_tAD7606.ucOS]);    /* 启动100kHz采样速率 */
                    AD7606_SetOS(g_tAD7606.ucOS);       /* 设置无过采样 */
                    printf("\33[%dA", (int)1);          /* 光标上移n行 */    
                    printf("AD7606进入FIFO工作模式 (200KHz 8通道同步采集)...\r\n");
                    break;

                case KEY_DOWN_K3:            /* K3键按下 */
                    AD7606_StopRecord();    /* 停止记录 */
                    ucFifoMode = 0;         /* AD7606进入普通工作模式 */
                    g_tAD7606.ucOS = 0;     /* 无过采样 */
                    AD7606_SetOS(g_tAD7606.ucOS);
                    printf("\33[%dA", (int)1);  /* 光标上移n行 */
                    printf("AD7606进入普通工作模式(0.5s定时8通道同步采集)...\r\n");
                    break;

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

                    /* 如果是FIFO模式,*/
                    if(ucFifoMode == 1)
                    {
                            /* 启动当前过采样下最高速度 */
                        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;
                    
                    /* 如果是FIFO模式,*/
                    if(ucFifoMode == 1)
                    {
/* 启动当前过采样下最高速度 */
                        AD7606_StartRecord(AD7606_SampleFreq[g_tAD7606.ucOS]);    
                    }
                    break;

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

 

76.12          实验例程说明(IAR)

配套例子:

V7-056_AD7606的FMC总线驱动方式实现(8通道同步采样, 16bit, 正负10V)

实验目的:

  1. 学习AD7606的FMC驱动方式实现。

重要提示:

  1. 板子上电后,默认是软件定时采集,0.5秒一次,适合串口展示数据。
  2. 如果需要使用J-Scope实时展示采集的波形效果,需要按下K2按键切换到FIFO模式。
  3. 如果使用的JLINK速度不够快,导致J-Scope无法最高速度实时上传,可以使用摇杆上下键设置过采样来降低上传速度。
  4. 默认情况下,程序仅上传了AD7606通道1采集的数据。
  5. 串口数据展示推荐使用SecureCRT,因为数据展示做了特别处理,方便采集数据在串口软件同一个位置不断刷新。

实验内容:

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

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

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

2、数据展示方式:

(1)软件查询方式,数据通过串口打印输出。

(2)FIFO工作模式,数据通过J-Scope实时输出。

(3)J-Scope的实时输出方法请看V7板子用户手册对应的AD7606章节。

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

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

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

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

实验操作:

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

上电后串口打印的信息:

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

J-Scope波形效果:

模块插入位置:

程序设计:

  系统栈大小分配:

  RAM空间用的DTCM:

  硬件外设初始化

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

/*
*********************************************************************************************************
*    函 数 名: bsp_Init
*    功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次
*    形    参:无
*    返 回 值: 无
*********************************************************************************************************
*/
void bsp_Init(void)
{
    /* 配置MPU */
    MPU_Config();
    
    /* 使能L1 Cache */
    CPU_CACHE_Enable();

    /* 
       STM32H7xx HAL 库初始化,此时系统用的还是H7自带的64MHz,HSI时钟:
       - 调用函数HAL_InitTick,初始化滴答时钟中断1ms。
       - 设置NVIV优先级分组为4。
     */
    HAL_Init();

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

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

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

 

  MPU配置和Cache配置:

数据Cache和指令Cache都开启。配置了AXI SRAM区(本例子未用到AXI SRAM)和FMC的扩展IO区。

/*
*********************************************************************************************************
*    函 数 名: MPU_Config
*    功能说明: 配置MPU
*    形    参: 无
*    返 回 值: 无
*********************************************************************************************************
*/
static void MPU_Config( void )
{
    MPU_Region_InitTypeDef MPU_InitStruct;

    /* 禁止 MPU */
    HAL_MPU_Disable();

    /* 配置AXI SRAM的MPU属性为Write back, Read allocate,Write allocate */
    MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
    MPU_InitStruct.BaseAddress      = 0x24000000;
    MPU_InitStruct.Size             = MPU_REGION_SIZE_512KB;
    MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;
    MPU_InitStruct.IsCacheable      = MPU_ACCESS_CACHEABLE;
    MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
    MPU_InitStruct.Number           = MPU_REGION_NUMBER0;
    MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL1;
    MPU_InitStruct.SubRegionDisable = 0x00;
    MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;

    HAL_MPU_ConfigRegion(&MPU_InitStruct);
    
    
    /* 配置FMC扩展IO的MPU属性为Device或者Strongly Ordered */
    MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
    MPU_InitStruct.BaseAddress      = 0x60000000;
    MPU_InitStruct.Size             = ARM_MPU_REGION_SIZE_64KB;    
    MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;
    MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE;    
    MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
    MPU_InitStruct.Number           = MPU_REGION_NUMBER1;
    MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL0;
    MPU_InitStruct.SubRegionDisable = 0x00;
    MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;
    
    HAL_MPU_ConfigRegion(&MPU_InitStruct);

    /*使能 MPU */
    HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
}

/*
*********************************************************************************************************
*    函 数 名: CPU_CACHE_Enable
*    功能说明: 使能L1 Cache
*    形    参: 无
*    返 回 值: 无
*********************************************************************************************************
*/
static void CPU_CACHE_Enable(void)
{
    /* 使能 I-Cache */
    SCB_EnableICache();

    /* 使能 D-Cache */
    SCB_EnableDCache();
}

 

  每10ms调用一次按键处理:

按键处理是在滴答定时器中断里面实现,每10ms执行一次检测。

/*
*********************************************************************************************************
*    函 数 名: bsp_RunPer10ms
*    功能说明: 该函数每隔10ms被Systick中断调用1次。详见 bsp_timer.c的定时中断服务程序。一些处理时间要求
*              不严格的任务可以放在此函数。比如:按键扫描、蜂鸣器鸣叫控制等。
*    形    参: 无
*    返 回 值: 无
*********************************************************************************************************
*/
void bsp_RunPer10ms(void)
{
    bsp_KeyScan10ms();
}

 

  主功能:

主程序实现如下操作:

  •   启动一个自动重装软件定时器,每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;
    uint8_t ucFifoMode;
    
    sfDispMenu();        /* 打印命令提示 */

    ucFifoMode = 0;         /* AD7606进入普通工作模式 */
    ucRefresh = 0;        /* 数据在串口刷新的标志 */
    
    AD7606_SetOS(AD_OS_NO);        /* 无过采样 */
    AD7606_SetInputRange(1);    /* 0表示输入量程为正负5V, 1表示正负10V */
    AD7606_StartConvst();        /* 启动1次转换 */

    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(2);
        }
        
        if (ucRefresh == 1)
        {
            ucRefresh = 0;

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

        if (ucFifoMode == 0)    /* AD7606 普通工作模式 */
        {
            if (bsp_CheckTimer(0))
            {
                /* 每隔500ms 进来一次. 由软件启动转换 */
                AD7606_ReadNowAdc();        /* 读取采样结果 */
                AD7606_StartConvst();        /* 启动下次转换 */

                ucRefresh = 1;    /* 刷新显示 */
            }
        }
        else
        {
            /*
                在FIFO工作模式,bsp_AD7606自动进行采集,数据存储在FIFO缓冲区。
                结果可以通过下面的函数读取:
                uint8_t AD7606_ReadFifo(uint16_t *_usReadAdc)

                大家可以将数据保存到SD卡,或者保存到外部SRAM。

                本例未对FIFO中的数据进行处理,进行打印当前最新的样本值和J-Scope的实时输出展示。

                如果主程序不能及时读取FIFO数据,那么 AD7606_FifoFull() 将返回真。

                8通道200K采样时,数据传输率 = 200 000 * 2 * 8 = 3.2MB/S
            */

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

        /* 按键检测由后台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键按下 */
                    ucFifoMode = 1;                      /* AD7606进入FIFO工作模式 */
                    g_tAD7606.ucOS = 1;                    /* 无过采样 */
                    AD7606_StartRecord(AD7606_SampleFreq[g_tAD7606.ucOS]);    /* 启动100kHz采样速率 */
                    AD7606_SetOS(g_tAD7606.ucOS);       /* 设置无过采样 */
                    printf("\33[%dA", (int)1);          /* 光标上移n行 */    
                    printf("AD7606进入FIFO工作模式 (200KHz 8通道同步采集)...\r\n");
                    break;

                case KEY_DOWN_K3:            /* K3键按下 */
                    AD7606_StopRecord();    /* 停止记录 */
                    ucFifoMode = 0;         /* AD7606进入普通工作模式 */
                    g_tAD7606.ucOS = 0;     /* 无过采样 */
                    AD7606_SetOS(g_tAD7606.ucOS);
                    printf("\33[%dA", (int)1);  /* 光标上移n行 */
                    printf("AD7606进入普通工作模式(0.5s定时8通道同步采集)...\r\n");
                    break;

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

                    /* 如果是FIFO模式,*/
                    if(ucFifoMode == 1)
                    {
                            /* 启动当前过采样下最高速度 */
                        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;
                    
                    /* 如果是FIFO模式,*/
                    if(ucFifoMode == 1)
                    {
/* 启动当前过采样下最高速度 */
                        AD7606_StartRecord(AD7606_SampleFreq[g_tAD7606.ucOS]);    
                    }
                    break;

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

 

76.13   总结

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