STM32H7的FMC总线应用之驱动AD7606(8通道同步采样, 16bit, 正负...
1 初学者重要提示[*]AD7606 的配置很简单,它没有内部寄存器,量程范围和过采样参数是通过外部IO控制的,采样速率由MCU或DSP提供的脉冲频率控制。
[*]AD7606必须使用单5V供电。而AD7606和MCU之间的通信接口电平由VIO(VDRIVE)引脚控制。也就是说VIO必须接单片机的电源,可以是3.3V也可以是5V(范围2.3V – 5V)。
[*]正确的理解过采样,比如我们设置是1Ksps采样率,64倍过采样。意思是指每次采样,AD7606会采样64次数据并求平均,相当于AD7606以64Ksps进行采样的,只是将每64个采样点的值做了平均,用户得到的值就是平均后的数值。因此,如果使用AD7606最高的200Ksps采样率,就不可以使用过采样了。
[*]STM32H7驱动AD7606配合J-Scope实时输出,效果绝了,堪比示波器http://www.armbbs.cn/forum.php?mod=viewthread&tid=97393 。使用方法详解本章节77.8小节。
[*]本章配套例子的串口数据展示推荐使用SecureCRT,因为数据展示做了特别处理,方便采集数据在串口软件同一个位置不断刷新。
[*]AD7606数据手册,模块原理图(通用版)和接线图都已经放到本章教程配置例子的Doc文件里。
[*]ADC 的专业术语诠释文档,推荐大家看看:http://www.armbbs.cn/forum.php?mod=viewthread&tid=89414 。
[*]测试本章配套例子前重要提示:
[*]测试时,务必使用外置电源为开发板供电,因为AD7606需要5V供电电压。板子上插入AD7606模块时,注意对齐。
[*]板子上电后,默认是软件定时采集,0.5秒一次,适合串口展示数据。
[*]如果需要使用J-Scope实时展示采集的波形效果,需要按下K2按键切换到FIFO模式。
[*]如果使用的JLINK速度不够快,导致J-Scope无法最高速度实时上传,可以使用摇杆上下键设置过采样来降低上传速度。
[*]默认情况下,程序仅上传了AD7606通道1采集的数据。
2 ADC结构分类
这里将六种DAC结构为大家做个普及。注,这些知识翻译自美信和TI的英文技术手册。
2.1 SAR ADC(逐次逼近型)
逐次逼近型ADC通常是中高分辨率的首选架构,采样速率通常低于5Msps。SAR ADC最常见的分辨率范围是8位到20位,并具有低功耗和小尺寸的特点。这种组合使其非常适合各种应用,例如自动测试设备,电池供电的设备,数据采集系统,医疗仪器,电机和过程控制,工业自动化,电信,测试和测量,便携式系统,高速闭环系统和窄带接收器。
2.2 Sigma-Delta ADC
Sigma-delta ADC主要用于低速应用中,该应用需要通过过采样来权衡速度和分辨率,然后进行滤波以降低噪声。24位sigma-delta转换器用于自动化测试设备,高精度便携式传感器,医疗和科学仪器以及地震数据采集等应用中。
2.3 Integrating ADC
集成ADC提供高分辨率,并且可以提供良好的线路频率和噪声抑制。集成架构提供了一种新颖且直接的方法,可将低带宽模拟信号转换为数字表示形式。这些类型的转换器通常包括用于LCD或LED显示器的内置驱动器,并且在许多便携式仪器应用中都可以找到,包括数字面板表和数字万用表。
2.4 FLASH ADC
Flash ADC是将模拟信号转换为数字信号的最快方法。它们适用于需要非常大带宽的应用。然而,闪存转换器功率高,具有相对较低的分辨率,并且可能非常昂贵。这将它们限制在通常无法以其他任何方式解决的高频应用中。示例包括数据采集,卫星通信,雷达处理,示波器和高密度磁盘驱动器。
2.5 Pipelined ADC
流水线ADC已成为最受欢迎的ADC体系结构,其采样率从每秒几兆采样(MS / s)到最高100MS / s +,分辨率为8至16位。它们提供的分辨率和采样率,可覆盖各种应用,包括CCD成像,超声医学成像,数字接收器,基*,数字视频(例如HDTV),xDSL,电缆调制解调器和快速以太网。
2.6 Two Step ADC
两步ADC也称为子范围转换器,有时也称为多步或half flash(比Flash架构慢)。这是Flash ADC和流水线ADC的交叉点。与Flash ADC相比,可以实现更高的分辨率或更小的裸片尺寸。
3 AD7606硬件设计
这里将开发板上的AD7606硬件接口,普通型AD7606模块,屏蔽型AD7606模块和磁耦高速隔离型AD7606模块为大家做个说明。
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接口方式,接线框图如下:
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 悬空(不贴)。
3.3 AD7606模块(屏蔽版)
屏蔽版主要是为了更好的应对复杂的电磁工作,软件代码与非屏蔽版是一样的:
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间距排针,节约主板面积。
产品效果:
引脚定义和接线图:
4 AD7606关键知识点整理(重要)
驱动AD7606需要对下面这些知识点有个认识。
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。
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平面。
4.3 AD7606输出电压计算公式
AD7606的计算公式如下:
采用二进制补码(其实就是16bit有符号数,将转换结果定义为int16_t即可),因为AD7606支持正负压采集。
[*]VIN
AD7606采集到的电压值范围-32768到32767。
[*]REF
一般使用内部基准,即2.5V。
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信号同步,每读取完一路,拉高一次。
4.5 AD7606的过采样
使用过采样可以改善SNR信噪比。SNR性能随着过采样倍率提高而改善,具体参数如下:
通过这个表,我们可以方便的了解不同过采样下的信噪比,3dB带宽时的频率和最高支持的采样率。
注意正确的理解过采样,比如我们设置是1Ksps采样率,64倍过采样。意思是指每次采样,AD7606会采样64次数据并求平均,相当于AD7606以64Ksps进行采样的,只是将每64个采样点的值做了平均,用户得到的值就是平均后的数值。因此,如果使用AD7606最高的200Ksps采样率,就不可以使用过采样了。
5 AD7606的FMC接口硬件设计
FMC硬件接口涉及到的知识点稍多,下面逐一为大家做个说明。
5.1 FMC的块区分配
FMC总线可操作的地址范围0x60000000到0xDFFFFFFF,具体的框图如下:
从上面的框图可以看出,NOR/PSRAM/SRAM块区有4个片选NE1,NE2,NE3和NE4,但由于引脚复用,部分片选对应的引脚要用于其他功能,而且要控制的总线外设较多,导致片选不够用。因此需要增加译码器。
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模式了。
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位模式,大家应该也都会计算了。
6 AD7606的FMC接口驱动设计
AD7606的程序驱动框架设计如下:
有了这个框图,程序设计就比较好理解了。
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口线。
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)
6.3 第3步,FMC的时钟源选择
使用FMC可以选择如下几种时钟源HCLK3,PLL1Q,PLL2R和PER_CK:
我们这里直接使用HCLK3,配置STM32H7的主频为400MHz的时候,HCLK3输出的200MHz,这个速度是FMC支持的最高时钟,正好用于这里:
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。
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通过译码器所管理的所有设备地址全部设置为此配置:
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 = AD7606_RESULT(); /* 读第1路样本 */
g_tAD7606.sNowAdc = AD7606_RESULT(); /* 读第2路样本 */
g_tAD7606.sNowAdc = AD7606_RESULT(); /* 读第3路样本 */
g_tAD7606.sNowAdc = AD7606_RESULT(); /* 读第4路样本 */
g_tAD7606.sNowAdc = AD7606_RESULT(); /* 读第5路样本 */
g_tAD7606.sNowAdc = AD7606_RESULT(); /* 读第6路样本 */
g_tAD7606.sNowAdc = AD7606_RESULT(); /* 读第7路样本 */
g_tAD7606.sNowAdc = 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();
}
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_tAD7606.sNowAdc;
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 = AD7606_RESULT(); /* 读第1路样本 */
g_tAD7606.sNowAdc = AD7606_RESULT(); /* 读第2路样本 */
g_tAD7606.sNowAdc = AD7606_RESULT(); /* 读第3路样本 */
g_tAD7606.sNowAdc = AD7606_RESULT(); /* 读第4路样本 */
g_tAD7606.sNowAdc = AD7606_RESULT(); /* 读第5路样本 */
g_tAD7606.sNowAdc = AD7606_RESULT(); /* 读第6路样本 */
g_tAD7606.sNowAdc = AD7606_RESULT(); /* 读第7路样本 */
g_tAD7606.sNowAdc = AD7606_RESULT(); /* 读第8路样本 */
AD7606_SEGGER_RTTOUT();
}
这里的FIFO比较好理解,与前面按键FIFO章节的实现是一样的,详情可重温下按键FIFO的实现。
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 = AD7606_RESULT(); /* 读第1路样本 */
g_tAD7606.sNowAdc = AD7606_RESULT(); /* 读第2路样本 */
g_tAD7606.sNowAdc = AD7606_RESULT(); /* 读第3路样本 */
g_tAD7606.sNowAdc = AD7606_RESULT(); /* 读第4路样本 */
g_tAD7606.sNowAdc = AD7606_RESULT(); /* 读第5路样本 */
g_tAD7606.sNowAdc = AD7606_RESULT(); /* 读第6路样本 */
g_tAD7606.sNowAdc = AD7606_RESULT(); /* 读第7路样本 */
g_tAD7606.sNowAdc = AD7606_RESULT(); /* 读第8路样本 */
AD7606_SEGGER_RTTOUT();
}
本章是将此函数用于实时采集数据并输出到J-Scope。
页:
[1]
2