完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
扫一扫,分享给好友
|
|
相关推荐
1个回答
|
|
这里将六种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模块的插座的原理图如下: 实际对应开发板的位置如下: 为了方便大家更好的理解接线,下面是框图: 模块引脚说明:
如果采用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基础信息
76.4.2 AD7606常用引脚的作用 AD7606的封装形式: 这里把常用的几个引脚做个说明:
76.4.3 AD7606输出电压计算公式 AD7606的计算公式如下: 采用二进制补码(其实就是16bit有符号数,将转换结果定义为int16_t即可),因为AD7606支持正负压采集。
76.4.4 AD7606时序图 了解时序参数是驱动AD7606能否成功的关键,我们这里对几个重要的参数做个说明。 1、AD7606的CONVST转换时序(转换之后读取数据):
2、AD7606的并行驱动模式有两种时序图,一个是独立的CS片选和RD读信号时序图:
3、另一个是CS片选和RD相连的方式: 这个时序里面最重要的是t12。
第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的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口线。
将BUSY口线设置为中断下降沿触发模式; 外部中断ISR: { 中断入口; 读取8个通道的采样结果保存到RAM; }
(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数据建立时间即可。
有了这些认识后,再来看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通过译码器所管理的所有设备地址全部设置为此配置: 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 * 功能说明: 开始采集 * 形 参: 无 * 返 回 值: 无 ********************************************************************************************************* */ 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; 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表示未满。 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多通道传输实现 /* 配置通道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移植步骤如下:
/* 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)
通过程序设计框架,让大家先对配套例程有一个全面的认识,然后再理解细节,本次实验例程的设计框架如下: 第1阶段,上电启动阶段:
配套例子: 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(); } 主功能: 主程序实现如下操作: 启动一个自动重装软件定时器,每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通道同步采集)...rn"); 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通道同步采集)...rn"); 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)软件定时获取方式,适合低速查询获取。 (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(); } 主功能: 主程序实现如下操作: 启动一个自动重装软件定时器,每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通道同步采集)...rn"); 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通道同步采集)...rn"); 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 总结 本章节涉及到的知识点非常多,实战性较强,需要大家稍花点精力去研究。 |
|
|
|
只有小组成员才能发言,加入小组>>
调试STM32H750的FMC总线读写PSRAM遇到的问题求解?
1602 浏览 1 评论
X-NUCLEO-IHM08M1板文档中输出电流为15Arms,15Arms是怎么得出来的呢?
1536 浏览 1 评论
967 浏览 2 评论
STM32F030F4 HSI时钟温度测试过不去是怎么回事?
680 浏览 2 评论
ST25R3916能否对ISO15693的标签芯片进行分区域写密码?
1579 浏览 2 评论
1860浏览 9评论
STM32仿真器是选择ST-LINK还是选择J-LINK?各有什么优势啊?
641浏览 4评论
STM32F0_TIM2输出pwm2后OLED变暗或者系统重启是怎么回事?
513浏览 3评论
527浏览 3评论
stm32cubemx生成mdk-arm v4项目文件无法打开是什么原因导致的?
500浏览 3评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-11-19 14:33 , Processed in 0.567960 second(s), Total 46, Slave 39 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号