完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980
本章节为大家讲解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 总结
这里将六种DAC结构为大家做个普及。注,这些知识翻译自美信和TI的英文技术手册。
逐次逼近型ADC通常是中高分辨率的首选架构,采样速率通常低于5Msps。SAR ADC最常见的分辨率范围是8位到20位,并具有低功耗和小尺寸的特点。这种组合使其非常适合各种应用,例如自动测试设备,电池供电的设备,数据采集系统,医疗仪器,电机和过程控制,工业自动化,电信,测试和测量,便携式系统,高速闭环系统和窄带接收器。
Sigma-delta ADC主要用于低速应用中,该应用需要通过过采样来权衡速度和分辨率,然后进行滤波以降低噪声。24位sigma-delta转换器用于自动化测试设备,高精度便携式传感器,医疗和科学仪器以及地震数据采集等应用中。
集成ADC提供高分辨率,并且可以提供良好的线路频率和噪声抑制。集成架构提供了一种新颖且直接的方法,可将低带宽模拟信号转换为数字表示形式。这些类型的转换器通常包括用于LCD或LED显示器的内置驱动器,并且在许多便携式仪器应用中都可以找到,包括数字面板表和数字万用表。
Flash ADC是将模拟信号转换为数字信号的最快方法。它们适用于需要非常大带宽的应用。然而,闪存转换器功率高,具有相对较低的分辨率,并且可能非常昂贵。这将它们限制在通常无法以其他任何方式解决的高频应用中。示例包括数据采集,卫星通信,雷达处理,示波器和高密度磁盘驱动器。
流水线ADC已成为最受欢迎的ADC体系结构,其采样率从每秒几兆采样(MS / s)到最高100MS / s +,分辨率为8至16位。它们提供的分辨率和采样率,可覆盖各种应用,包括CCD成像,超声医学成像,数字接收器,基站,数字视频(例如HDTV),xDSL,电缆调制解调器和快速以太网。
两步ADC也称为子范围转换器,有时也称为多步或half flash(比Flash架构慢)。这是Flash ADC和流水线ADC的交叉点。与Flash ADC相比,可以实现更高的分辨率或更小的裸片尺寸。
这里将开发板上的AD7606硬件接口,普通型AD7606模块,屏蔽型AD7606模块和磁耦高速隔离型AD7606模块为大家做个说明。
AD7606的原理图下载:
http://www.armbbs.cn/forum.php?mod=viewthread&tid=97547 。
V7板子上AD7606模块的插座的原理图如下:
实际对应开发板的位置如下:
为了方便大家更好的理解接线,下面是框图:
模块引脚说明:
组合状态选择过采样模式。
过采样倍率越高,ADC转换时间越长,可得到的最大采样频率就越低。
启动AD转换的控制信号。CVA决定1-4通道,CVB决定5-8通道。2个信号可以错开短暂的时间。一般情况可以将CVA,CVB并联在一起。
量程范围选择。0表示正负5V, 1表示正负10V。
读信号。
复位信号。
忙信号。
片选信号。
第1个通道样本的指示信号。【注,此引脚可以省略不使用】
通信接口电平。
数据总线。
如果采用SPI接口方式,接线框图如下:
产品规格:
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 悬空(不贴)。
屏蔽版主要是为了更好的应对复杂的电磁工作,软件代码与非屏蔽版是一样的:
该款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间距排针,节约主板面积。
产品效果:
引脚定义和接线图:
驱动AD7606需要对下面这些知识点有个认识。
AD7606的封装形式:
这里把常用的几个引脚做个说明:
模拟电源电压,4.75V到5.25V。这是内部前端放大器和ADC内核的电源电压。应将这些电压引脚去偶接AGND。
模拟地,这些引脚是AD7606上所有模拟电路的接地基准点。所有模拟输入信号和外部基准信号都应参考这些引脚。
组合状态选择过采样模式。
过采样倍率越高,ADC转换时间越长,可得到的最大采样频率就越低。
启动AD转换的控制信号。CONVSTA决定1-4通道,CONVSTB决定5-8通道。2个信号可以错开短暂的时间。一般情况可以将CVA,CVB并联在一起。
量程范围选择。0表示正负5V, 1表示正负10V.
读信号,低电平有效。
复位信号。
CONVST A和CONVST B均达到上升沿后,此引脚变为逻辑高电平,表示转换过程已经开始,BUSY输出保持高电平,直到所有通道的转换过程完成为止。BUSY下降沿表示转换数据正被锁存至输出数据寄存器,此时用户就可以读取数据。
片选信号,低电平有效。
第1个通道样本的指示信号。【注,此引脚可以省略不使用】
通信接口电平。
数据总线。
内部/外部基准电压选择。如果设置此引脚设为逻辑高电平,使用内部基准电压。如果此引脚设为逻辑低电平,则内部基准电压禁止,必须将外部基准电压加到REFIN/REFOUT引脚。
基准电压输入(REFIN)/基准电压输出(REFOUT)引脚,如果REF SELECT引脚设置为逻辑高电平,此引脚将提供2.5V片内基准电压供外部使用。或者可以将REF SELECT引脚设置为逻辑低电平将禁止用内部基准电压。
模拟输入,此引脚为单端模拟输入,此通道的模拟输入范围由RANGE引脚决定。
模拟输入接地引脚,这些引脚与模拟输入引脚V1到V8对应,所有模拟输入AGND引脚都应连接到系统的AGND平面。
AD7606的计算公式如下:
采用二进制补码(其实就是16bit有符号数,将转换结果定义为int16_t即可),因为AD7606支持正负压采集。
AD7606采集到的电压值范围-32768到32767。
一般使用内部基准,即2.5V。
了解时序参数是驱动AD7606能否成功的关键,我们这里对几个重要的参数做个说明。
1、AD7606的CONVST转换时序(转换之后读取数据):
CONVST A和CONVST B上升沿之间最大允许的延迟时间。一般我们是用一根控制线同时控制CONVST A和CONVST B,因此可以不用管这个时间。
并行模式,转换后并读取数据的最大值是5us,即最高支持的时钟速度是20MHz及其以上。
转换时间。
最短的CONVST A/B电平脉冲,最小值25ns。
BUSY下降沿到CS下降沿设置时间,最小值0ns,所以可以忽略。
2、AD7606的并行驱动模式有两种时序图,一个是独立的CS片选和RD读信号时序图:
CS到RD的设置时间,最小值是0ns,可以忽略。
RD读信号的低电平脉冲宽度,通信电压不同,时间不同。对于STM32来说,FMC通信电平一般是3.3V,即最小值21ns。
RD高电平脉冲宽度,最小值15ns。
CS到RD保持时间,最小值0ns,可以忽略。
这几个参数了解下即可:
3、另一个是CS片选和RD相连的方式:
这个时序里面最重要的是t12。
CS和RD的高电平脉冲宽度,最小值22ns。
第2个和第3个时序图的主要区别是连续读取8路数据时,一个CS信号是全程低电平,另一个CS信号是与RD信号同步,每读取完一路,拉高一次。
使用过采样可以改善SNR信噪比。SNR性能随着过采样倍率提高而改善,具体参数如下:
通过这个表,我们可以方便的了解不同过采样下的信噪比,3dB带宽时的频率和最高支持的采样率。
注意正确的理解过采样,比如我们设置是1Ksps采样率,64倍过采样。意思是指每次采样,AD7606会采样64次数据并求平均,相当于AD7606以64Ksps进行采样的,只是将每64个采样点的值做了平均,用户得到的值就是平均后的数值。因此,如果使用AD7606最高的200Ksps采样率,就不可以使用过采样了。
FMC硬件接口涉及到的知识点稍多,下面逐一为大家做个说明。
FMC总线可操作的地址范围0x60000000到0xDFFFFFFF,具体的框图如下:
从上面的框图可以看出,NOR/PSRAM/SRAM块区有4个片选NE1,NE2,NE3和NE4,但由于引脚复用,部分片选对应的引脚要用于其他功能,而且要控制的总线外设较多,导致片选不够用。因此需要增加译码器。
有了前面的认识之后再来看下面的译码器电路:
SN74LVC1G139APWR是双2-4线地址译码器,也就是带了两个译码器。原理图上仅用了一个。下面是139的真值表和引脚功能:
通过上面的原理图和真值表就比较好理解了,真值表的输出是由片选FMC_NE1和地址线FMC_A10、FMC_A11控制。
FMC_NE1 输出低电平:
然后我们再计算译码器的地址,注意,这里地址的计算都是按照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位模式,大家应该也都会计算了。
AD7606的程序驱动框架设计如下:
有了这个框图,程序设计就比较好理解了。
主要实现了两种采集方式:
(1)软件定时获取方式,适合低速查询获取。
(2)FIFO工作模式,适合8路实时采集,支持最高采样率200Ksps。
可以在硬件定时器中断服务程序或者软件定时器里面实现。
定时器中断ISR: { 中断入口; 读取8个通道的采样结果保存到RAM; ----> 读取的是上次的采集结果,对于连续采集来说,是没有关系的 启动下次ADC采集;(翻转CVA和CVB) 中断返回; }
定时器的频率就是ADC采样频率。这种模式可以不连接BUSY口线。
配置CVA、CVB引脚为PWM输出模式,周期设置为需要的采样频率,之后MCU将产生周期非常稳定的AD转换信号
将BUSY口线设置为中断下降沿触发模式;
外部中断ISR: { 中断入口; 读取8个通道的采样结果保存到RAM; }
(1)方案1 可以少用 BUSY口线,但是其他中断服务程序或者主程序临时关闭全局中断时,可能导致ADC转换周期存在轻微抖动。
(2)方案2 可以确保采集时钟的稳定性,因为它是MCU硬件产生的,但是需要多接一根BUSY口线。
这里需要把用到的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)
使用FMC可以选择如下几种时钟源HCLK3,PLL1Q,PLL2R和PER_CK:
我们这里直接使用HCLK3,配置STM32H7的主频为400MHz的时候,HCLK3输出的200MHz,这个速度是FMC支持的最高时钟,正好用于这里:
由于操作AD7606仅需要读操作,而且使用的是FMC总线的Mode_A,那么仅需按照如下时序图配置好即可:
根据这个时序图,重点配置好ADDSET地址建立时间和DATAST数据建立时间即可。
DATAST实际上对应的就是76.4.4小节里面的t10 。RD读信号的低电平脉冲宽度,通信电压不同,时间不同,对于STM32来说,FMC通信电平一般是3.3V,即最小值21ns。
DATAST实际上对应的就是76.4.4小节里面的t11 或者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. }
这里把几个关键的地方阐释下:
实际测试发现,使能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通过译码器所管理的所有设备地址全部设置为此配置:
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(); }
通过下面的框图可以对AD7606的FIFO方式有个整体认识:
这个函数的主要作用是配置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); } }
下面这几个函数的调用关系是
/* ********************************************************************************************************* * 函 数 名: 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的实现。
为了方便大家实时处理采集的数据,专门预留了一个弱定义函数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。
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; } }
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 */ } }
在bsp_fmc_ad7606.c文件开头有个宏定义
#define AD7606_RESULT() *(__IO uint16_t *)0x60003000
特别注意,这里是要操作地址0x60003000上的16位数据空间,即做了一个强制转换uint16_t *。
AD7606驱动文件bsp_fmc_ad7606.c主要实现了如下几个API供用户调用:
函数原型:
void bsp_InitAD7606(void)
函数描述:
主要用于AD7606的初始化。
函数原型:
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。
函数参数:
函数原型:
void AD7606_SetInputRange(uint8_t _ucRange)
函数描述:
配置AD7606模拟信号输入量程。
函数参数:
函数原型:
void AD7606_Reset(void)
函数描述:
此函数用于硬件复位AD7606,复位之后恢复到正常工作状态。
函数原型:
void AD7606_StartConvst(void)
函数描述:
此函数用于启动1次ADC转换。
函数原型:
void AD7606_ReadNowAdc(void)
函数描述:
此函数用于读取8路采样结果,结果存储在全局变量 g_tAD7606。
函数原型:
void AD7606_EnterAutoMode(uint32_t _ulFreq)
函数描述:
此函数用于配置硬件工作在自动采集模式,结果存储在FIFO缓冲区。一般不单独调用,函数AD7606_StartRecord会调用。
函数参数:
函数原型:
void AD7606_StartRecord(uint32_t _ulFreq)
函数描述:
用于启动采集。
函数参数:
函数原型:
void AD7606_StopRecord(void)
函数描述:
此函数用于停止采集定时器。函数AD7606_StartRecord和AD7606_StopRecord是配套的。
函数原型:
uint8_t AD7606_HasNewData(void)
函数描述:
此函数用于判断FIFO中是否有新数据。
函数参数:
函数原型:
uint8_t AD7606_ReadFifo(uint16_t *_usReadAdc)
函数描述:
此函数用于从FIFO中读取一个ADC值。
函数参数:
函数原型:
uint8_t AD7606_FifoFull(void)
函数描述:
此函数用于判断FIFO是否满。
函数参数:
J-Scope专题教程(实时展示要用J-Scope的RTT模式),本章配套例子也做了支持:
http://www.armbbs.cn/forum.php?mod=viewthread&tid=86881 。
看完专题教程,基本就会操作了,这里有三点注意事项需要大家提前有个了解。另外,推荐使用MDK版工程做测试J-Scope,IAR版容易测试不正常。
如下界面,不要点击选择按钮,闪退就是因为点击了这个选择按钮。
直接手动填写型号即可,比如STM32H743XI,STM32F429BI,STM32F407IG,STM32F103ZE等。
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);
多路效果:
普通的JLINK时钟速度8 - 12MHz时, J-Scope的速度基本可以达到500KB/S(注意,单位是字节)AD7606的最高采样率是200Ksps,16bit,那么一路采集就有400KB/S的速速,所以要根据设置的采样率设置要显示的J-Scope通道数,如果超出了最高通信速度,波形显示会混乱。
200Ksps时,实时显示1路
100Ksps时,实时显示2路
50Ksps时, 实时显示4路
25Ksps时, 实时显示8路
实际速度以底栏的展示为准,如果与设置的速度差异较大,说明传输异常了。
AD7606移植步骤如下:
这里要特别注意过采样引脚,量程控制引脚和复位引脚是采用的扩展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)
bsp_InitAD7606(); /* 配置AD7606所用的GPIO */
通过程序设计框架,让大家先对配套例程有一个全面的认识,然后再理解细节,本次实验例程的设计框架如下:
第1阶段,上电启动阶段:
第2阶段,进入main函数:
配套例子:
V7-056_AD7606的FMC总线驱动方式实现(8通道同步采样, 16bit, 正负10V)
实验目的:
重要提示:
实验内容:
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转换信号。
实验操作:
上电后串口打印的信息:
波特率 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(); }
主功能:
主程序实现如下操作:
/* ********************************************************************************************************* * 函 数 名: 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; } } } }
配套例子:
V7-056_AD7606的FMC总线驱动方式实现(8通道同步采样, 16bit, 正负10V)
实验目的:
重要提示:
实验内容:
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转换信号。
实验操作:
上电后串口打印的信息:
波特率 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(); }
主功能:
主程序实现如下操作:
/* ********************************************************************************************************* * 函 数 名: 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; } } }
本章节涉及到的知识点非常多,实战性较强,需要大家稍花点精力去研究。