硬汉嵌入式论坛

 找回密码
 立即注册
查看: 1955|回复: 17
收起左侧

[ADC] ADC 多通道DMA采集问题

  [复制链接]

12

主题

101

回帖

137

积分

初级会员

积分
137
发表于 2024-5-18 15:59:24 | 显示全部楼层 |阅读模式
各位大大,最近调试多通道ADC时出现问题,我是通过DMA来实现的,程序写好直接下载进去,是可以正常AD转换的,但我通过bootload下载进去后,就只能开机转换一次,是不是boodload下载程序会和DMA通道冲突了?因为当我不用DMA转换时,用boogload下载进去是可以正常AD转换的。
以下是我DMA连续转换的程序:




void bsp_Adc1Init(void)
{
        ADC_MultiModeTypeDef multimode = {0};
        ADC_ChannelConfTypeDef sConfig = {0};
       
        hadc1.Instance = ADC1;
//  hadc1.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV2;
#if defined (ADC_CLOCK_SOURCE_PLL)
        hadc1.Init.ClockPrescaler        = ADC_CLOCK_ASYNC_DIV2;          /* 采用PLL异步时钟,2分频,即72MHz/2 = 36MHz */
#elif defined (ADC_CLOCK_SOURCE_AHB)
        hadc1.Init.ClockPrescaler        = ADC_CLOCK_SYNC_PCLK_DIV4;      /* 采用AHB同步时钟,4分频,即200MHz/4 = 50MHz */
#endif
  hadc1.Init.Resolution = ADC_RESOLUTION_16B;
  hadc1.Init.ScanConvMode = ADC_SCAN_ENABLE;                                        //        多通道扫描用 ADC_SCAN_ENABLE        ADC_SCAN_DISABLE
  hadc1.Init.EOCSelection = ADC_EOC_SEQ_CONV;                //        ADC_EOC_SINGLE_CONV 单个通道转换结束时是否产生 EOS 中断或事件标志   ADC_EOC_SEQ_CONV 在所有通道转换完成后进入中断
  hadc1.Init.LowPowerAutoWait = DISABLE;                                                       
  hadc1.Init.ContinuousConvMode = ENABLE;                                                //        配置自动连续转换还是单次转换
  hadc1.Init.NbrOfConversion = 7;                                                                //        设置常规转换通道数目
  hadc1.Init.DiscontinuousConvMode = DISABLE;                                        //        禁止连续转换的通道数
  hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;                //        定时器触发ADC_EXTERNALTRIG_T3_TRGO
  hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;//ADC_EXTERNALTRIGCONVEDGE_RISING;
  hadc1.Init.ConversionDataManagement = ADC_CONVERSIONDATA_DMA_CIRCULAR;
  hadc1.Init.Overrun = ADC_OVR_DATA_OVERWRITTEN;                //        新数据时覆盖写入
  hadc1.Init.LeftBitShift = ADC_LEFTBITSHIFT_NONE;                //        左移
  hadc1.Init.OversamplingMode = DISABLE;                                //        是否采用过采样
        if (HAL_ADC_Init(&hadc1) != HAL_OK)
  {
    Error_Handler(__FILE__, __LINE__);
  }
       
        multimode.Mode = ADC_MODE_INDEPENDENT;
  if (HAL_ADCEx_MultiModeConfigChannel(&hadc1, &multimode) != HAL_OK)
  {
    Error_Handler(__FILE__, __LINE__);
  }
       
  sConfig.Channel = ADC_CHANNEL_10;
  sConfig.Rank = ADC_REGULAR_RANK_1;                //        Rank:在常规转换中的常规组的转换顺序,可以选择 1~16。
  sConfig.SamplingTime = ADC_SAMPLETIME_64CYCLES_5;
  sConfig.SingleDiff = ADC_SINGLE_ENDED;        //        单端对地 ADC_SINGLE_ENDED    差份方式 ADC_DIFFERENTIAL_ENDED
  sConfig.OffsetNumber = ADC_OFFSET_NONE;
  sConfig.Offset = 0;
  sConfig.OffsetSignedSaturation = DISABLE;        //        OffsetSignedSaturation:是否使能 ADC 采样值的最高位为符号位
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
    Error_Handler(__FILE__, __LINE__);
  }
  
  sConfig.Channel = ADC_CHANNEL_11;
  sConfig.Rank = ADC_REGULAR_RANK_2;                //        Rank:在常规转换中的常规组的转换顺序,可以选择 1~16。
  sConfig.SamplingTime = ADC_SAMPLETIME_64CYCLES_5;
  sConfig.SingleDiff = ADC_SINGLE_ENDED;        //        单端对地 ADC_SINGLE_ENDED    差份方式 ADC_DIFFERENTIAL_ENDED
  sConfig.OffsetNumber = ADC_OFFSET_NONE;
  sConfig.Offset = 0;
  sConfig.OffsetSignedSaturation = DISABLE;        //        OffsetSignedSaturation:是否使能 ADC 采样值的最高位为符号位
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
    Error_Handler(__FILE__, __LINE__);
  }
  sConfig.Channel = ADC_CHANNEL_12;
  sConfig.Rank = ADC_REGULAR_RANK_3;                //        Rank:在常规转换中的常规组的转换顺序,可以选择 1~16。
  sConfig.SamplingTime = ADC_SAMPLETIME_64CYCLES_5;
  sConfig.SingleDiff = ADC_SINGLE_ENDED;        //        单端对地 ADC_SINGLE_ENDED    差份方式 ADC_DIFFERENTIAL_ENDED
  sConfig.OffsetNumber = ADC_OFFSET_NONE;
  sConfig.Offset = 0;
  sConfig.OffsetSignedSaturation = DISABLE;        //        OffsetSignedSaturation:是否使能 ADC 采样值的最高位为符号位
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
    Error_Handler(__FILE__, __LINE__);
  }
  sConfig.Channel = ADC_CHANNEL_13;
  sConfig.Rank = ADC_REGULAR_RANK_4;                //        Rank:在常规转换中的常规组的转换顺序,可以选择 1~16。
  sConfig.SamplingTime = ADC_SAMPLETIME_64CYCLES_5;
  sConfig.SingleDiff = ADC_SINGLE_ENDED;        //        单端对地 ADC_SINGLE_ENDED    差份方式 ADC_DIFFERENTIAL_ENDED
  sConfig.OffsetNumber = ADC_OFFSET_NONE;
  sConfig.Offset = 0;
  sConfig.OffsetSignedSaturation = DISABLE;        //        OffsetSignedSaturation:是否使能 ADC 采样值的最高位为符号位
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
    Error_Handler(__FILE__, __LINE__);
  }
  
  sConfig.Channel = ADC_CHANNEL_4;
  sConfig.Rank = ADC_REGULAR_RANK_5;                //        Rank:在常规转换中的常规组的转换顺序,可以选择 1~16。
  sConfig.SamplingTime = ADC_SAMPLETIME_64CYCLES_5;
  sConfig.SingleDiff = ADC_SINGLE_ENDED;        //        单端对地 ADC_SINGLE_ENDED    差份方式 ADC_DIFFERENTIAL_ENDED
  sConfig.OffsetNumber = ADC_OFFSET_NONE;
  sConfig.Offset = 0;
  sConfig.OffsetSignedSaturation = DISABLE;        //        OffsetSignedSaturation:是否使能 ADC 采样值的最高位为符号位
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
    Error_Handler(__FILE__, __LINE__);
  }
  
  sConfig.Channel = ADC_CHANNEL_8;
  sConfig.Rank = ADC_REGULAR_RANK_6;                //        Rank:在常规转换中的常规组的转换顺序,可以选择 1~16。
  sConfig.SamplingTime = ADC_SAMPLETIME_64CYCLES_5;
  sConfig.SingleDiff = ADC_SINGLE_ENDED;        //        单端对地 ADC_SINGLE_ENDED    差份方式 ADC_DIFFERENTIAL_ENDED
  sConfig.OffsetNumber = ADC_OFFSET_NONE;
  sConfig.Offset = 0;
  sConfig.OffsetSignedSaturation = DISABLE;        //        OffsetSignedSaturation:是否使能 ADC 采样值的最高位为符号位
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
    Error_Handler(__FILE__, __LINE__);
  }
  
  sConfig.Channel = ADC_CHANNEL_TEMPSENSOR;
  sConfig.Rank = ADC_REGULAR_RANK_7;                //        Rank:在常规转换中的常规组的转换顺序,可以选择 1~16。
  sConfig.SamplingTime = ADC_SAMPLETIME_64CYCLES_5;
  sConfig.SingleDiff = ADC_SINGLE_ENDED;        //        单端对地 ADC_SINGLE_ENDED    差份方式 ADC_DIFFERENTIAL_ENDED
  sConfig.OffsetNumber = ADC_OFFSET_NONE;
  sConfig.Offset = 0;
  sConfig.OffsetSignedSaturation = DISABLE;        //        OffsetSignedSaturation:是否使能 ADC 采样值的最高位为符号位
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
    Error_Handler(__FILE__, __LINE__);
  }
  

//        HAL_ADCEx_Calibration_Start(&hadc1,ADC_CALIB_OFFSET,ADC_SINGLE_ENDED);//AD校准,通道处于模式校准偏移,通道输入模式单端

        if (HAL_ADCEx_Calibration_Start(&hadc1,ADC_CALIB_OFFSET,ADC_SINGLE_ENDED) != HAL_OK)
        {
                while(1);
        }
        if (HAL_ADC_Start_DMA(&hadc1,
                        (uint32_t *)aADCxConvertedData,
                        ADC_CONVERTED_DATA_BUFFER_SIZE
                        ) != HAL_OK)
        {
                while(1);
        }


       
}





void HAL_ADC_MspInit(ADC_HandleTypeDef* hadc)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(hadc->Instance==ADC1)
  {
  /* USER CODE BEGIN ADC1_MspInit 0 */

  /* USER CODE END ADC1_MspInit 0 */
    /* Peripheral clock enable */
    HAL_RCC_ADC12_CLK_ENABLED++;
    if(HAL_RCC_ADC12_CLK_ENABLED==1){
      __HAL_RCC_ADC12_CLK_ENABLE();
    }

//    __HAL_RCC_GPIOA_CLK_ENABLE();
    __HAL_RCC_GPIOC_CLK_ENABLE();
        __HAL_RCC_DMA1_CLK_ENABLE();
    /**ADC1 GPIO Configuration
    PA0     ------> ADC1_INP16
    */
    GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3|GPIO_PIN_4|GPIO_PIN_5;
    GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

    /* ADC1 DMA Init */
    /* ADC1 Init */
    hdma_adc1.Instance = DMA1_Stream0;
    hdma_adc1.Init.Request = DMA_REQUEST_ADC1;
    hdma_adc1.Init.Direction = DMA_PERIPH_TO_MEMORY;
    hdma_adc1.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_adc1.Init.MemInc = DMA_MINC_ENABLE;
    hdma_adc1.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
    hdma_adc1.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
    hdma_adc1.Init.Mode = DMA_CIRCULAR;
    hdma_adc1.Init.Priority = DMA_PRIORITY_HIGH;
    hdma_adc1.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
    if (HAL_DMA_Init(&hdma_adc1) != HAL_OK)
    {
      Error_Handler(__FILE__, __LINE__);
    }

    __HAL_LINKDMA(hadc,DMA_Handle,hdma_adc1);

  /* USER CODE BEGIN ADC1_MspInit 1 */

  /* USER CODE END ADC1_MspInit 1 */
  }

}

回复

使用道具 举报

1万

主题

7万

回帖

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
117564
QQ
发表于 2024-5-19 10:06:07 | 显示全部楼层
是不是修改为boot方式后,你的主RAM空间使用成TCM了,这个TCM空间是不支持通用DMA的。
回复

使用道具 举报

12

主题

101

回帖

137

积分

初级会员

积分
137
 楼主| 发表于 2024-5-19 22:15:35 | 显示全部楼层
eric2013 发表于 2024-5-19 10:06
是不是修改为boot方式后,你的主RAM空间使用成TCM了,这个TCM空间是不支持通用DMA的。

aADCxConvertedData                       0x240057fc   Data          28  bsp_adc.o(.bss)
回复

使用道具 举报

12

主题

101

回帖

137

积分

初级会员

积分
137
 楼主| 发表于 2024-5-19 22:16:48 | 显示全部楼层
本帖最后由 yuangt 于 2024-5-20 08:45 编辑
eric2013 发表于 2024-5-19 10:06
是不是修改为boot方式后,你的主RAM空间使用成TCM了,这个TCM空间是不支持通用DMA的。

aADCxConvertedData                       0x240057fc   Data          28  bsp_adc.o(.bss)

刚看了下 map文件,这个DMA内存是放在 SRAM这里了,这个应该可以的吧
回复

使用道具 举报

1万

主题

7万

回帖

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
117564
QQ
发表于 2024-5-20 08:54:23 | 显示全部楼层
yuangt 发表于 2024-5-19 22:15
aADCxConvertedData                       0x240057fc   Data          28  bsp_adc.o(.bss)

还有就是这个设置半字宽度比较合理。

    hdma_adc1.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
    hdma_adc1.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;

别的看不出还有那里有问题了。
回复

使用道具 举报

12

主题

101

回帖

137

积分

初级会员

积分
137
 楼主| 发表于 2024-5-20 09:03:59 | 显示全部楼层
eric2013 发表于 2024-5-20 08:54
还有就是这个设置半字宽度比较合理。

    hdma_adc1.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD ...

嗯嗯,刚试了改成半字也不行,
现在的情况是直接flash下载进去是可以运行的,然后通过bootoad方式ISP下载后,就只能开机执行一次AD转换,后面就没有新的数据更新了
回复

使用道具 举报

85

主题

798

回帖

1053

积分

至尊会员

积分
1053
发表于 2024-5-20 13:40:04 | 显示全部楼层
yuangt 发表于 2024-5-20 09:03
嗯嗯,刚试了改成半字也不行,
现在的情况是直接flash下载进去是可以运行的,然后通过bootoad方式ISP下 ...

bootload程序跳转前有没有将用过的东西关闭好清空
回复

使用道具 举报

12

主题

101

回帖

137

积分

初级会员

积分
137
 楼主| 发表于 2024-5-20 21:13:01 | 显示全部楼层
庄永 发表于 2024-5-20 13:40
bootload程序跳转前有没有将用过的东西关闭好清空

bootload程序就只用到了spi和uart ,这个在应用程序里都有重新初始化的
回复

使用道具 举报

1万

主题

7万

回帖

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
117564
QQ
发表于 2024-5-21 14:42:55 | 显示全部楼层
yuangt 发表于 2024-5-20 21:13
bootload程序就只用到了spi和uart ,这个在应用程序里都有重新初始化的

你的APP能调试不,能的话,调试看下你的DMA NDTR寄存器记录的DMA剩余传输次数,看看是不是一直在运行,如果在运行,说明可能是MPU/Cache配置问题。
回复

使用道具 举报

12

主题

101

回帖

137

积分

初级会员

积分
137
 楼主| 发表于 2024-5-22 19:56:48 | 显示全部楼层
eric2013 发表于 2024-5-21 14:42
你的APP能调试不,能的话,调试看下你的DMA NDTR寄存器记录的DMA剩余传输次数,看看是不是一直在运行,如 ...

app程序只有在非bootload下才能调试,就直接flash下载进去可以debug
通过bootload加载进去后没法调试了吧?
回复

使用道具 举报

1万

主题

7万

回帖

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
117564
QQ
发表于 2024-5-23 08:43:47 | 显示全部楼层
yuangt 发表于 2024-5-22 19:56
app程序只有在非bootload下才能调试,就直接flash下载进去可以debug
通过bootload加载进去后没法调试了 ...

可以,这个做了一期视频BSP视频教程第17期:单片机bootloader专题,启动,跳转配置和调试下载的各种用法(2022-06-10)
https://forum.anfulai.cn/forum.p ... 2792&fromuid=58
(出处: 硬汉嵌入式论坛)
回复

使用道具 举报

12

主题

101

回帖

137

积分

初级会员

积分
137
 楼主| 发表于 2024-5-25 10:57:53 | 显示全部楼层
eric2013 发表于 2024-5-21 14:42
你的APP能调试不,能的话,调试看下你的DMA NDTR寄存器记录的DMA剩余传输次数,看看是不是一直在运行,如 ...

222222.png

这是我的MPU配置,帮我看看这个有什么问题吗?
回复

使用道具 举报

12

主题

101

回帖

137

积分

初级会员

积分
137
 楼主| 发表于 2024-5-25 14:32:47 | 显示全部楼层
本帖最后由 yuangt 于 2024-5-25 16:05 编辑
eric2013 发表于 2024-5-21 14:42
你的APP能调试不,能的话,调试看下你的DMA NDTR寄存器记录的DMA剩余传输次数,看看是不是一直在运行,如 ...

我进入APP调试后,看NDTR值一直有变化的,那是不是意味着DMA这里没有问题?但此时 hadc1->Instance->DR的AD转换值一直有变化,而且转换的值也是正常的,就是没有传到DMA那个aADCxConvertedData 数组里面去

刚刚又发现一个新现象,我在APP中的dubug运行,当暂停一下,再马上点全速运行时,会传输一次DMA,每次暂停再运行,都会更新aADCxConvertedData数据,由此我把读取数据的频率放慢,或者把AD转换的周期设快了,也是同样的效果,全速运行时,就只能刚启动时DMA传输了5次左右然后就停止不更新了,hadc1->Instance->DR的值是一直有更新的
回复

使用道具 举报

1万

主题

7万

回帖

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
117564
QQ
发表于 2024-5-26 09:30:46 | 显示全部楼层
yuangt 发表于 2024-5-25 14:32
我进入APP调试后,看NDTR值一直有变化的,那是不是意味着DMA这里没有问题?但此时 hadc1->Instance->DR的 ...

这样测试,别用调试方式,以实际运行为准,定时打印下NDTR看看是不是一直有变化。

这个问题整准了,方便我们下一步测试。
回复

使用道具 举报

12

主题

101

回帖

137

积分

初级会员

积分
137
 楼主| 发表于 2024-6-8 13:57:40 | 显示全部楼层
eric2013 发表于 2024-5-26 09:30
这样测试,别用调试方式,以实际运行为准,定时打印下NDTR看看是不是一直有变化。

这个问题整准了,方 ...

经定时打印数据,确定 DMA1_Stream0->NDTR 一直有变化 ,人1~7,正好7个通道,而且 hadc1->Instance->DR 也是一直有数据更新的,就是这个DMA数组里面没有变化
接下来要乍测试了呢,问题还没解决
回复

使用道具 举报

12

主题

101

回帖

137

积分

初级会员

积分
137
 楼主| 发表于 2024-6-18 08:44:18 | 显示全部楼层
yuangt 发表于 2024-6-8 13:57
经定时打印数据,确定 DMA1_Stream0->NDTR 一直有变化 ,人1~7,正好7个通道,而且 hadc1->Instance->DR  ...

这个还有没啥招了呀
回复

使用道具 举报

5

主题

229

回帖

249

积分

高级会员

积分
249
发表于 2024-6-18 13:52:31 | 显示全部楼层
yuangt 发表于 2024-6-18 08:44
这个还有没啥招了呀

有没有可能你开通了cache DMA传输的时候你没清除cache, 这样会导致上电在内存中建立,但是后面你看到的内存数据都是cache里面的值而不是内存里面的数据。
回复

使用道具 举报

12

主题

101

回帖

137

积分

初级会员

积分
137
 楼主| 发表于 2024-6-20 17:21:21 | 显示全部楼层
旮旯旭 发表于 2024-6-18 13:52
有没有可能你开通了cache DMA传输的时候你没清除cache, 这样会导致上电在内存中建立,但是后面你看到的内 ...

能说一下我该怎么清除cache吗?这方面不太懂,就是拿 开发板的工程改过来的
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|小黑屋|Archiver|手机版|硬汉嵌入式论坛

GMT+8, 2025-8-14 21:30 , Processed in 0.049939 second(s), 27 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2023, Tencent Cloud.

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