@[toc]

代码结构优化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : adc_dma.c
* @brief : ADC with DMA implementation for STM32F7 series
******************************************************************************
* @attention
* - STM32F7 L1-cache注意事项:
* - 开启D-cache会导致DMA数据不更新
* - 解决方案:
* - 使用DTCM内存(0x20000000)
* - 手动维护cache一致性(SCB_InvalidateDCache_by_Addr)
* - 配置为write-through模式
* - 内存地址分配:
* - 0x20020000 (SRAM1)需要特殊处理
* - 0x20000000 (DTCM)可直接使用
*
* @author HLY
* @version V1.0
* @date 2023-01-01
******************************************************************************
*/

功能模块划分

1
2
3
4
5
6
7
8
9
10
11
12
13
/* Includes -----------------------------------------------------------------*/
#include "adc_dma.h"
#include "adc.h"

/* 宏定义 -------------------------------------------------------------------*/
#define ADC1_BUFFER_SIZE (32*1) // ADC1使用4个通道,32组数据用于平均值计算
#define ADC_VREF 3.3f // ADC参考电压
#define ADC_RESOLUTION 4096 // 12位ADC分辨率

/* 内存分配 -----------------------------------------------------------------*/
// 32字节对齐(地址+大小),确保L1-CACHE兼容性
ALIGN_32BYTES (uint16_t adc1_data[ADC1_BUFFER_SIZE])
__attribute__((section(".ARM.__at_0x20020000")));

核心功能实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/**
* @brief ADC DMA初始化
* @param None
* @retval HAL状态
* @note 需在系统初始化阶段调用
*/
HAL_StatusTypeDef adc_dma_init(void)
{
return HAL_ADC_Start_DMA(&hadc1,
(uint32_t *)adc1_data,
ADC1_BUFFER_SIZE);
}

/**
* @brief 获取ADC转换值(带平均值计算)
* @param channel: 要读取的通道索引
* @retval 转换后的电压值(单位:V)
*/
float get_adc_voltage(uint8_t channel)
{
uint32_t sum = 0;

// 计算指定通道的平均值
for(int i = channel; i < ADC1_BUFFER_SIZE; i += ADC_CHANNEL_COUNT) {
sum += adc1_data[i];
}

uint32_t avg = sum / (ADC1_BUFFER_SIZE / ADC_CHANNEL_COUNT);
return (avg * ADC_VREF) / ADC_RESOLUTION;
}

Cache一致性处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/**
* @brief ADC DMA半传输完成回调
* @param hadc: ADC句柄指针
* @note 处理前半部分数据的cache一致性
*/
void HAL_ADC_ConvHalfCpltCallback(ADC_HandleTypeDef* hadc)
{
if(hadc->Instance == ADC1) {
SCB_InvalidateDCache_by_Addr(
(uint32_t *)&adc1_data[0],
ADC1_BUFFER_SIZE/2);
}
}

/**
* @brief ADC DMA传输完成回调
* @param hadc: ADC句柄指针
* @note 处理后半部分数据的cache一致性
*/
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
if(hadc->Instance == ADC1) {
SCB_InvalidateDCache_by_Addr(
(uint32_t *)&adc1_data[ADC1_BUFFER_SIZE/2],
ADC1_BUFFER_SIZE/2);
}
}

使用示例

1
2
3
4
5
6
7
8
// 初始化示例
if(adc_dma_init() != HAL_OK) {
Error_Handler();
}

// 读取数据示例
float voltage = get_adc_voltage(0); // 读取通道0
printf("Voltage: %.2fV\n", voltage);

注意事项

  1. 内存分配

    • DTCM内存(0x20000000)可直接使用
    • SRAM1(0x20020000)需要手动维护cache
  2. 多通道处理
    修改宏定义ADC_CHANNEL_COUNT以匹配实际使用通道数

  3. 性能优化

    • 使用Ping-Pong缓冲提高效率
    • 考虑使用DMA双缓冲模式
  4. 错误处理
    建议添加以下检查:

    1
    2
    3
    if(HAL_ADC_Stop_DMA(&hadc1) != HAL_OK) {
    // 错误处理
    }
  5. 校准
    建议在初始化时执行ADC校准:

    1
    HAL_ADCEx_Calibration_Start(&hadc1, ADC_SINGLE_ENDED);
  6. 采样时间配置
    根据实际需求调整ADC采样时钟周期:

    1
    hadc1.Init.SamplingTimeCommon = ADC_SAMPLETIME_15CYCLES;

这个优化版本改进了以下方面:

  • 更清晰的代码结构
  • 更完善的注释
  • 更强的错误处理
  • 更灵活的多通道支持
  • 更详细的注意事项说明
  • 更好的可维护性