找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索

adc_dma进行采集时打印数据为0,不开dma数据能够正常显示,想问一下是什么原因

查看数: 1417 | 评论数: 1 | 收藏 0
关灯 | 提示:支持键盘翻页<-左 右->
    组图打开中,请稍候......
发布时间: 2024-10-21 16:10

正文摘要:

/** **************************************************************************************************** * @file        adc.c * @author       ...

回复

ID:404160 发表于 2024-10-22 10:00
调整了以下几点代码,看可否解决DMA传输数据为0的问题。修改点包括:

1. **确保DMA和ADC的初始化顺序正确**。
2. **添加调试日志,监控DMA传输状态和数据**。
3. **确保DMA中断优先级和中断处理函数工作正常**。
4. **保证ADC DMA启动后的数据读取流程正确**。

### 修改后的代码如下:

```c
#include "./BSP/ADC/adc.h"
#include "./SYSTEM/delay/delay.h"
#include "./SYSTEM/usart/usart.h"

ADC_HandleTypeDef g_adc_handle;   /* ADC句柄 */
DMA_HandleTypeDef g_dma_adc_handle;     /* 与ADC关联的DMA句柄 */
uint8_t g_adc_dma_sta = 0;              /* DMA传输状态标志, 0,未完成; 1, 已完成 */

uint16_t g_adc_value[ADC_CH_NUM * ADC_COLL] = {0};  /* 存储ADC原始值 */
uint16_t g_adc_val[ADC_CH_NUM] = {0};               /* 存储处理后的ADC平均值 */

/********************************************************************/
/**
* @brief       ADC初始化函数
* @retval      无
*/
void adc_init(void)
{
    ADC_ChannelConfTypeDef sConfig = {0};

    g_adc_handle.Instance = ADC_ADCX;
    g_adc_handle.Init.ContinuousConvMode = ENABLE;
    g_adc_handle.Init.DataAlign = ADC_DATAALIGN_RIGHT;
    g_adc_handle.Init.DiscontinuousConvMode = DISABLE;
    g_adc_handle.Init.ExternalTrigConv = ADC_SOFTWARE_START;
    g_adc_handle.Init.NbrOfConversion = ADC_CH_NUM;
    g_adc_handle.Init.ScanConvMode = ENABLE;
    HAL_ADC_Init(&g_adc_handle);

    HAL_ADCEx_Calibration_Start(&g_adc_handle);  // ADC校准

    sConfig.Channel = ADC_ADCX_CH4;
    sConfig.Rank = 1;
    sConfig.SamplingTime = ADC_SAMPLETIME_239CYCLES_5;
    HAL_ADC_ConfigChannel(&g_adc_handle, &sConfig);

    sConfig.Channel = ADC_ADCX_CH5;
    sConfig.Rank = 2;
    sConfig.SamplingTime = ADC_SAMPLETIME_239CYCLES_5;
    HAL_ADC_ConfigChannel(&g_adc_handle, &sConfig);
}

/********************************************************************/
/**
* @brief       DMA初始化函数
* @retval      无
*/
void adc_dma_init(void)
{
    if ((uint32_t)ADC_ADCX_DMACx > (uint32_t)DMA1_Channel7)     /* 大于DMA1_Channel7, 则为DMA2的通道了 */
    {
        __HAL_RCC_DMA2_CLK_ENABLE();                            /* DMA2时钟使能 */
    }
    else
    {
        __HAL_RCC_DMA1_CLK_ENABLE();                            /* DMA1时钟使能 */
    }

    g_dma_adc_handle.Instance = DMA1_Channel1;
    g_dma_adc_handle.Init.Direction = DMA_PERIPH_TO_MEMORY;
    g_dma_adc_handle.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
    g_dma_adc_handle.Init.MemInc = DMA_MINC_ENABLE;
    g_dma_adc_handle.Init.Mode = DMA_CIRCULAR;  // 设置为循环模式,确保DMA持续工作
    g_dma_adc_handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
    g_dma_adc_handle.Init.PeriphInc = DMA_PINC_DISABLE;
    g_dma_adc_handle.Init.Priority = DMA_PRIORITY_MEDIUM;
    HAL_DMA_Init(&g_dma_adc_handle);

    __HAL_LINKDMA(&g_adc_handle, DMA_Handle, g_dma_adc_handle);  // 关联DMA和ADC
}

/********************************************************************/
/**
* @brief       DMA传输完成中断处理函数
* @retval      无
*/
void ADC_ADCX_DMASX_IRQHandler(void)
{
    HAL_DMA_IRQHandler(&g_dma_adc_handle);
    g_adc_dma_sta = 1;  // 标记DMA传输完成
}

/********************************************************************/
/**
* @brief       ADC转换完成回调函数
* @retval      无
*/
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc)
{
    if (hadc->Instance == ADC_ADCX)
    {
        HAL_ADC_Stop_DMA(hadc);  // 停止DMA,防止数据被覆盖
        calc_adc_val(g_adc_val);  // 计算ADC平均值
        g_adc_dma_sta = 1;  // 标记DMA传输完成
        HAL_ADC_Start_DMA(hadc, (uint32_t *)g_adc_value, ADC_CH_NUM * ADC_COLL);  // 重新启动DMA
    }
}

/********************************************************************/
/**
* @brief       计算ADC的平均值(滤波)
* @param       *p : 存放ADC值的指针地址
* @retval      无
*/
void calc_adc_val(uint16_t *p)
{
    uint32_t temp[ADC_CH_NUM] = {0};  /* 缓存数组 */
    for (int i = 0; i < ADC_COLL; i++)
    {
        for (int j = 0; j < ADC_CH_NUM; j++)
        {
            temp[j] += g_adc_value[j + i * ADC_CH_NUM];  /* 累加 */
        }
    }
    for (int j = 0; j < ADC_CH_NUM; j++)
    {
        p[j] = temp[j] / ADC_COLL;  /* 计算平均值 */
    }
}

int main(void)
{
    HAL_Init();                         /* 初始化HAL库 */
    sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
    delay_init(72);                     /* 延时初始化 */
    usart_init(115200);                 /* 串口初始化为115200 */

    adc_init();  // 初始化ADC
    adc_dma_init();  // 初始化DMA

    // 启动DMA
    if (HAL_ADC_Start_DMA(&g_adc_handle, (uint32_t *)g_adc_value, ADC_CH_NUM * ADC_COLL) != HAL_OK)
    {
        printf("ADC DMA start failed\r\n");
    }

    while (1)
    {
        if (g_adc_dma_sta)  // DMA传输完成
        {
            g_adc_dma_sta = 0;  // 重置标志位
            printf("ADC Value Channel 1: %d, Channel 2: %d\r\n", g_adc_val[0], g_adc_val[1]);
        }
    }
}
```

### 程序修改了:
1. **DMA初始化**:确保正确初始化DMA,并关联到ADC句柄。
2. **DMA模式**:将DMA模式设置为 `DMA_CIRCULAR`,这样DMA会持续工作,而不需要手动重新启动。
3. **中断处理**:在 `ADC_ADCX_DMASX_IRQHandler()` 中设置DMA传输完成标志位,并在 `HAL_ADC_ConvCpltCallback()` 中处理传输完成的逻辑。
4. **ADC转换完成处理**:在回调函数 `HAL_ADC_ConvCpltCallback()` 中停止DMA、计算ADC平均值,并重新启动DMA。
5. **主循环**:通过轮询 `g_adc_dma_sta` 检测DMA传输完成,并在主循环中打印ADC转换后的数据。

### 调试提示:
- 在 `HAL_ADC_Start_DMA()` 后面添加错误处理,确保DMA和ADC启动正常。
- 你可以通过串口日志查看 `g_adc_val[]` 是否有正确的值输出。如果还存在问题,可以进一步调试DMA的中断是否正确触发。

小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表