笔者在近期操作ADC与DMA过程中,发现DMA一直使用不正常,原因就在于MPU及内存区域的划分方面导致DMA访问不到对应内存区域
。
当前芯片厂商出的 M7 内核芯片基本都做了一级 Cache 支持,Cache 又分数据缓存 D-Cache
和指令缓冲 I-Cache
。STM32H7 的数据缓存和指令缓存大小都是 16KB
。对于指令缓冲,用户不用管,这里主要说的是数据缓存 D-Cache。以 STM32H7 为例,主频是 400MHz,除了 TCM 和 Cache 以 400MHz工作,其它 AXI SRAM,SRAM1,SRAM2 等都是以 200MHz 工作。数据缓存 D-Cache 就是解决 CPU加速访问 SRAM。
在实际项目中,充分发挥STM32H7的性能,必须将频繁存取的数据存放在DTCM内存。TCM:Tightly-Coupled Memory 紧密耦合内存,特点是跟内核速度一样(480M)。但是,其他内存(SRAM1,AXI SRAM,SRAM2,SRAM3等)跟CPU的通讯速度只有200M,CPU需要白白等待一段时间,才能把数据读取出来或者将数据存放进去。
为了提高CPU与其他内存的通讯效率,Cortex-M7有了Cache(高速缓冲区,与CPU通讯速度400M)
除了 TCM 和 Cache 以 400MHz工作,其它 AXI SRAM,SRAM1,SRAM2 等都是以 200MHz 工作。
1 H743 MPU内存管理单元
- 防止不受信用的应用程序访问受保护的内存区域
- 防止用户应用程序破坏操作系统使用的数据
- 通过阻止任务访问其他任务的数据区
- 允许将内存区域定义为只读,以便保护重要数据。
- 检测意外的内存访问
- 简单来说,就是内存保护、外设保护和代码访问保护。
- 如果每次 CPU 要读写 SRAM 区的数据,都能够在 Cache 里面进行,自然是最好的,实现了200MHz到 400MHz 的飞跃,实际是做不到的,因为数据 Cache 只有 16KB 大小,总有用完的时候。
1.1 支持的Cache配置
- 读操作:如果 CPU 要读取的 SRAM 区数据在 Cache 中已经加载好,这就叫读命中(Cache hit),如果 Cache里面没有怎么办,这就是所谓的读 Cache Miss。
- 写操作:如果 CPU 要写的 SRAM 区数据在 Cache 中已经开辟了对应的区域(专业词汇叫 Cache Line,以 32字节为单位),这就叫写命中(Cache hit),如果 Cache 里面没有开辟对应的区域怎么办,这就是所谓的写 Cache Miss。
1.2 配置 Write through,read allocate,no write allocate
1.3 配置 Write back,read allocate,no write allocate
1.4 配置 Write back,read allocate,write allocate
2 STM32 H7的TCM ,SRAM等五块内存区域
2.1 总线所外挂的外设,共分为三个域:D1 Domain,D2 Domain 和 D3 Domain
2.2 各块RAM特性
2.3 MPU设置
- Write back,read allocate,write allocate
3 双AD+DMA采集(ADC1+DMA1 ADC3+BDMA)
3.1 环境准备
- 开DCache
- ADC1+DMA1生成数据定义到AXI SRAM, 如0x24000000
- ADC3+BDMA生成数据定义到SRAM4, 如0x38000000
- 调SCB_InvalidateDCache_by_Addr (void *addr, int32_t dsize) 使得数据更新
- DMA1-MEM访问不了默认的DTCM(0x20000000开始), 但可以访问AXI SRAM(0x24000000)
- BDMA只能访问SRAM4(0x38000000开始)
3.2 ADC1+DMA1(AXI SRAM (0x24000000))
3.3 ADC3+BDMA(SRAM4 (0x38000000))
3.4 时钟
3.5 内存区域管理(_D1_Area,_D3_Area)
3.6 ADC DMA启动
3.6.1 基于中断的ADC DMA模拟量处理
- 中断回调
- ADC转换半满中断中把数据存到数组的前半部分
- ADC转换完成中断中把数据存到数组的后半部分
- 结果
3.6.2 不基于中断的ADC DMA模拟量处理
- void SCB_InvalidateDCache_by_Addr (void *addr, int32_t dsize)中
dsize是字节数, 所以ADC1_BUFFER_SIZE刚好是uint16_t类型adc1_data数组一半的容量
, 这点要特别注意 - 如果嫌中断太频繁, 可以把SCB_InvalidateDCache_by_Addr((uint32_t *)adc1_data, sizeof(adc1_data));放到读取之前.
3.7 采样率计算