硬汉嵌入式论坛

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

[ADC] 使用 STM32H7B0 板子,降低 ADC 采样时间后,触发 ADC 多通道 DMA 中断存在卡死问题

[复制链接]

3

主题

16

回帖

25

积分

新手上路

积分
25
发表于 2024-6-26 08:51:48 | 显示全部楼层 |阅读模式
想请教一个问题,我使用的是STM32H7B0 的板子做一个提升采样率的功能。配置情况如下:ADC 时钟配置为 32 MHz,ADC 配置为 4 通道使用 DMA 传输并开启 DMA 中断,如果我把 ADC 的采样周期 sConfig.SamplingTime 从 ADC_SAMPLETIME_16CYCLES_5 设置到 ADC_SAMPLETIME_8CYCLES_5时,程序运行就会出现卡死,不知道问题出现在哪里?(猜测是中断频率太高出现的)如果设置在ADC_SAMPLETIME_16CYCLES_5 程序是正常运行的,采样也是正常的;如果设置为ADC_SAMPLETIME_8CYCLES_5 程序就会卡死,想请教是什么原因?
我的 ADC 和 DMA 配置如下:
RCC_PeriphCLKInitTypeDefPeriphClkInitStruct = {0};
ADC_MultiModeTypeDefmultimode = {0};
ADC_ChannelConfTypeDefsConfig = {0};
/* 使能 ADC 时钟 */
__HAL_RCC_ADC12_CLK_ENABLE();
/**   ADC 配置
*/
ADC1_Handle.Instance= ADC1;                                                  // 选择 ADC1
ADC1_Handle.Init.ClockPrescaler= ADC_CLOCK_ASYNC_DIV1;                        // ADC1 进行 1 分频
ADC1_Handle.Init.Resolution= ADC_RESOLUTION_16B;                              // 分辨率 16bit
ADC1_Handle.Init.ScanConvMode= ADC_SCAN_ENABLE;                               // 扫描模式开启
ADC1_Handle.Init.EOCSelection= ADC_EOC_SINGLE_CONV;                           //
ADC1_Handle.Init.LowPowerAutoWait= DISABLE;                                   // 关闭低功耗自动等待
ADC1_Handle.Init.ContinuousConvMode= ENABLE;                                  // 连续转换模式开启
ADC1_Handle.Init.NbrOfConversion= 4;                                         // 转换 4 个通道
ADC1_Handle.Init.DiscontinuousConvMode= DISABLE;                              // 不连续转换
ADC1_Handle.Init.ExternalTrigConv= ADC_SOFTWARE_START;                        // ADC 软件触发
ADC1_Handle.Init.ExternalTrigConvEdge= ADC_EXTERNALTRIGCONVEDGE_NONE;          // 不进行外部触发
ADC1_Handle.Init.ConversionDataManagement= ADC_CONVERSIONDATA_DMA_CIRCULAR;    // ADC-DMA 循环传输
ADC1_Handle.Init.Overrun= ADC_OVR_DATA_PRESERVED;                             // 数据溢出时,覆盖写入
ADC1_Handle.Init.LeftBitShift= ADC_LEFTBITSHIFT_NONE;                         // 对齐方式
ADC1_Handle.Init.OversamplingMode= DISABLE;                                   // 不进行过采样
if(HAL_ADC_Init(&ADC1_Handle) != HAL_OK)
{
}
/** 初始化外设时钟
*/
PeriphClkInitStruct.PeriphClockSelection= RCC_PERIPHCLK_ADC;
PeriphClkInitStruct.PLL2.PLL2M= 4;
PeriphClkInitStruct.PLL2.PLL2N= 8;
PeriphClkInitStruct.PLL2.PLL2P= 4;
PeriphClkInitStruct.PLL2.PLL2Q= 1;
PeriphClkInitStruct.PLL2.PLL2R= 2;
PeriphClkInitStruct.PLL2.PLL2RGE= RCC_PLL2VCIRANGE_3;
PeriphClkInitStruct.PLL2.PLL2VCOSEL= RCC_PLL2VCOWIDE;
PeriphClkInitStruct.PLL2.PLL2FRACN= 0.0;
PeriphClkInitStruct.AdcClockSelection= RCC_ADCCLKSOURCE_PLL2;
if(HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)
{
}
/* ADC1 DMA 初始化 */
DMA_ADC1_Handle.Instance= DMA1_Stream0;
DMA_ADC1_Handle.Init.Request= DMA_REQUEST_ADC1;
DMA_ADC1_Handle.Init.Direction= DMA_PERIPH_TO_MEMORY;
DMA_ADC1_Handle.Init.PeriphInc= DMA_PINC_DISABLE;
DMA_ADC1_Handle.Init.MemInc= DMA_MINC_ENABLE;
DMA_ADC1_Handle.Init.PeriphDataAlignment= DMA_PDATAALIGN_HALFWORD;
DMA_ADC1_Handle.Init.MemDataAlignment= DMA_MDATAALIGN_HALFWORD;
DMA_ADC1_Handle.Init.Mode= DMA_CIRCULAR;
DMA_ADC1_Handle.Init.Priority= DMA_PRIORITY_MEDIUM;
DMA_ADC1_Handle.Init.FIFOMode= DMA_FIFOMODE_DISABLE;
if(HAL_DMA_Init(&DMA_ADC1_Handle) != HAL_OK)
{
}
__HAL_LINKDMA(&ADC1_Handle,DMA_Handle,DMA_ADC1_Handle);
/** 配置多通道模式
*/
multimode.Mode= ADC_MODE_INDEPENDENT;
if(HAL_ADCEx_MultiModeConfigChannel(&ADC1_Handle, &multimode) != HAL_OK)
{
}
/** 配置规则转换通道 4
*/
sConfig.Channel= ADC_CHANNEL_4;
sConfig.Rank =ADC_REGULAR_RANK_1;
sConfig.SamplingTime= ADC_SAMPLETIME_16CYCLES_5;
sConfig.SingleDiff= ADC_SINGLE_ENDED;
sConfig.OffsetNumber= ADC_OFFSET_NONE;
sConfig.Offset= 0;
sConfig.OffsetSignedSaturation= DISABLE;
if(HAL_ADC_ConfigChannel(&ADC1_Handle, &sConfig) != HAL_OK)
{
}
/** 配置规则转换通道 7
*/
sConfig.Channel= ADC_CHANNEL_7;
sConfig.Rank =ADC_REGULAR_RANK_2;
if(HAL_ADC_ConfigChannel(&ADC1_Handle, &sConfig) != HAL_OK)
{
}
/** 配置规则转换通道 8
*/
sConfig.Channel= ADC_CHANNEL_8;
sConfig.Rank =ADC_REGULAR_RANK_3;
if(HAL_ADC_ConfigChannel(&ADC1_Handle, &sConfig) != HAL_OK)
{
}
/** C配置规则转换通道 18
*/
sConfig.Channel= ADC_CHANNEL_18;
sConfig.Rank =ADC_REGULAR_RANK_4;
if(HAL_ADC_ConfigChannel(&ADC1_Handle, &sConfig) != HAL_OK)
{
}
HAL_ADCEx_Calibration_Start(&ADC1_Handle,ADC_CALIB_OFFSET, ADC_SINGLE_ENDED);
HAL_ADC_Start_DMA(&ADC1_Handle,(uint32_t*)&ADC_ConvertedValue, 4);
开启 DMA 中断配置如下:
/* DMA 控制器时钟使能 */
__HAL_RCC_DMA1_CLK_ENABLE();
/* DMA 中断初始化 */
/*DMA1_Stream0_IRQn 中断配置 */
HAL_NVIC_SetPriority(DMA1_Stream0_IRQn,0, 0);
HAL_NVIC_EnableIRQ(DMA1_Stream0_IRQn);
DMA 中断程序如下:
/**
  *@brief This function handles DMA1 stream0 global interrupt.
  */
voidDMA1_Stream0_IRQHandler(void)
{
    uint16_t size = 4;
    uint16_t temp_buff1[4];
    // uint16_t temp_buff2[4];
    if(__HAL_DMA_GET_FLAG(&DMA_ADC1_Handle,DMA_FLAG_TCIF0_4))
    {
           // 把数据写入环形缓冲区
           for(uint16_t i = 0; i < size; i++)
           {
                  temp_buff1 =ADC_ConvertedValue;
                  // temp_buff2 = 0;
           }
           Write_RingBuff(temp_buff1, size);
           __HAL_DMA_CLEAR_FLAG(&DMA_ADC1_Handle,DMA_FLAG_TCIF0_4);
    }
HAL_DMA_IRQHandler(&DMA_ADC1_Handle);
}
其中 Write_RingBuff 这个是一个写入循环缓冲区,为了能够通过串口打印观察到采样的点,所以用缓冲区临时存储,这个函数测试是没有 bug 的,屏蔽以后也是运行不了。

主函数是这样的:
MPU_Config();              // 分配内存存储单元
HAL_Init();                // HAL 层初始化
SystemClock_Config();      // 配置系统时钟
LED_Init();                                               // 初始化 LED 引脚
KEY_Init();                                               // 初始化按键引脚
USART1_Init();                                     // USART1 初始化       
ADC_Init();                // 初始化 ADC
RingBuff_Init();

uint16_t size = 4;
// uint8_t temp_buff1[128] = {0};
uint16_t temp_buff2[4];
// 初始化
for(uint16_t i = 0; i < size; i++)
{
                temp_buff2 = i + 2;
}

while(1)
{
                if(RINGBUFF_LEN > Get_RingBuff_Ridx()) // + 4)
                {
                                Read_RingBuff(temp_buff2, size);

                                // sprintf((char*)temp_buff1, "%d,%d,%d,%d\n",temp_buff2[0], temp_buff2[1], temp_buff2[2], temp_buff2[3]);
                                // Usart_SendString(temp_buff1);
                                // HAL_Delay(1);
                                printf("%d,%d,%d,%d\n",temp_buff2[0], temp_buff2[1], temp_buff2[2], temp_buff2[3]);

                                // printf("%d,%d,%d,%d,",temp_buff2[0], temp_buff2[1], temp_buff2[2], temp_buff2[3]);
                }

}
主函数用于读出缓冲区数据,用串口打印出来。

ADC 配置的是快通道,计算卡死时候的中断频率应该是有 (32MHz) / (7.5 + 8.5) / 4 = 0.5MHz 左右(DMA 转换完成后开始中断,有 4 个通道)。

所以综上,麻烦看一下配置情况是否正确,中断卡死的具体原因是什么,如果我想调试了可以正常运行我需要怎么配置或者使用什么方法来处理。(任务期望尽量多获取采样点数)还麻烦大佬帮忙看看,感谢!!

回复

使用道具 举报

3

主题

16

回帖

25

积分

新手上路

积分
25
 楼主| 发表于 2024-6-26 08:59:26 | 显示全部楼层
上面这个配置是老版本的配置,后来通道使用的是 ADC1 的 0-5 通道内的,上面那个配置只有一个是快通道,后面都配置成快通道了,所以中断频率可以认为是 0.5MHz;还有这个代码是 CubeMX 生成的,没有 BoostMode 位,我用 LL 库函数 LL_ADC_SetBoostMode 设置 BoostMode 位,但是感觉设置成 50MHz 的参数采样点也是一样的,所以就没配置。这个是补充说明
回复

使用道具 举报

1万

主题

7万

回帖

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
115490
QQ
发表于 2024-6-26 10:31:11 | 显示全部楼层
剑心v 发表于 2024-6-26 08:59
上面这个配置是老版本的配置,后来通道使用的是 ADC1 的 0-5 通道内的,上面那个配置只有一个是快通道,后 ...

DMA缓冲太小了,怎么也得开128个缓冲

HAL_ADC_Start_DMA(&ADC1_Handle,(uint32_t*)&ADC_ConvertedValue, 4);
回复

使用道具 举报

3

主题

16

回帖

25

积分

新手上路

积分
25
 楼主| 发表于 2024-6-26 10:49:16 | 显示全部楼层
好的谢谢 eric,我试一下,如果开了 128个缓冲,那 DMA 里 ADC 存放的数据是不是按照 ch0 ch1 ch2 ch3 ch0 ch1 .... ch2 ch3 (如果存的是 ch0 - ch3 通道数据)顺序存放的啊
回复

使用道具 举报

1万

主题

7万

回帖

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
115490
QQ
发表于 2024-6-26 16:21:56 | 显示全部楼层
剑心v 发表于 2024-6-26 10:49
好的谢谢 eric,我试一下,如果开了 128个缓冲,那 DMA 里 ADC 存放的数据是不是按照 ch0 ch1 ch2 ch3 ch0  ...

按照这个参数设置存储的sConfig.Rank =ADC_REGULAR_RANK_1;
回复

使用道具 举报

3

主题

16

回帖

25

积分

新手上路

积分
25
 楼主| 发表于 2024-6-27 15:14:40 | 显示全部楼层
谢谢硬汉哥回复,还有一个问题,我的 buff 大小设置为 12(4 * 3),ADC 采样周期设置为 ADC_SAMPLETIME_32CYCLES_5 时,
HAL_ADC_Start_DMA(&ADC1_Handle,(uint32_t*)&ADC_ConvertedValue, 12);
采集到的波形不平滑,波形之间有明显的波动(我确定数据存放位置是对的),如下图所示
C:\Users\wangz\Desktop\图片1.png
如果 buff 换为 4,ADC 采样周期不变,
HAL_ADC_Start_DMA(&ADC1_Handle,(uint32_t*)&ADC_ConvertedValue, 4);
得到的波形是很平稳的,没有看到波动,这样可能是什么原因造成的啊?
回复

使用道具 举报

1万

主题

7万

回帖

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
115490
QQ
发表于 2024-6-28 09:33:08 | 显示全部楼层
剑心v 发表于 2024-6-27 15:14
谢谢硬汉哥回复,还有一个问题,我的 buff 大小设置为 12(4 * 3),ADC 采样周期设置为 ADC_SAMPLETIME_32 ...

这个跟你的数据处理方式有很大关系,首先你要加大缓冲,搞到2K.

然后开DMA双缓冲,一个DMA缓冲采集的时候,另一个缓冲处理数据。

最后就是你这个DMA中断处理,这种处理不合理,你应该在DMA传输完成回调和半传输完成回调里面处理数据。
回复

使用道具 举报

3

主题

16

回帖

25

积分

新手上路

积分
25
 楼主| 发表于 2024-7-26 14:26:01 | 显示全部楼层
你好,Eric,我按照你说的方式,配置处理成了双缓冲区模式(借鉴了STM32H7 的 FMC 总线应用之 DMA 双缓冲驱动 AD7606 的例程),在中断里通过半传输完成标识和传输完成标识进行数据处理。因为我配置的双 ADC 主从模式,并用 DMA 传输,所以启动的的函数是这个 HAL_ADCEx_MultiModeStart_DMA(&hadc1, ADC_Value, 512); 但是在转换过程中,明显看到丢失了很多数据,如下面图片所示 512.png:
如果设置成 HAL_ADCEx_MultiModeStart_DMA(&hadc1, ADC_Value, 24);  数据是比较完整的,但是放大看也是有丢失一部分数据,如 24.png 所示。

我设置了 hdma_adc1.Init.FIFOMode 为 enable,数据变得更为异常,数据还是会丢失,麻烦 Eric 帮忙分析一下是什么原因,感谢!
512.png
24.png
回复

使用道具 举报

1万

主题

7万

回帖

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
115490
QQ
发表于 2024-7-27 07:54:01 | 显示全部楼层
剑心v 发表于 2024-7-26 14:26
你好,Eric,我按照你说的方式,配置处理成了双缓冲区模式(借鉴了STM32H7 的 FMC 总线应用之 DMA 双缓冲驱 ...

建议先测试下,类似楼主位的单个ADC多通道,触发速度100Hz是否正常。
回复

使用道具 举报

3

主题

16

回帖

25

积分

新手上路

积分
25
 楼主| 发表于 2024-8-6 15:56:25 | 显示全部楼层
我配置了单个 ADC 多通道, 使用 HAL_ADC_Start_DMA 进行转换 ADC_Value 开到多少都是正常的,但是对于双 ADC 模式使用 HAL_ADCEx_MultiModeStart_DMA 就会出现数据丢失,我看了两个 HAL 的实现方式,感觉也没有特别的地方,最后调用的都是 HAL_DMA_Start_IT 函数。我看了参考手册 “用户必须将主序列和从序列中的转换次数编程为相同值。否则,多余的转换将不会生成 DMA 请求”,有没有可能是这个参数没有设置的原因,我需要怎么设置 转换次数编程为相同值 呢?或者我能不能用另外一种方式,实现两个 ADC 多通道都同步采样,但是两个 ADC 的数据处理不发送乱序,有没有相关例程或者思路?
图 1.png
回复

使用道具 举报

1万

主题

7万

回帖

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
115490
QQ
发表于 2024-8-7 09:07:52 | 显示全部楼层
剑心v 发表于 2024-8-6 15:56
我配置了单个 ADC 多通道, 使用 HAL_ADC_Start_DMA 进行转换 ADC_Value 开到多少都是正常的,但是对于双 A ...

他分享的这个就可以的。

STM32H743/H723 三ADC交替触发采样,理论速度可达14MSPS+
https://www.armbbs.cn/forum.php? ... 4871&fromuid=58
(出处: 硬汉嵌入式论坛)
回复

使用道具 举报

3

主题

16

回帖

25

积分

新手上路

积分
25
 楼主| 发表于 2024-8-10 13:34:39 来自手机 | 显示全部楼层
我用了这个方法,使用了 ADC1 和 ADC2,把库移植到我的工程里,并使用两个ADC各两通道,在DMA传输完成中断和半中断保存数据到缓冲区,在main函数把两个缓冲区数据读出ain,但是采集到数据存在一定相位差(ADC1采集SIN信号,ADC2采集COS信号),发现采集到的数据是同相的。对于这样的情况我需要怎么保证数据的严格同步啊?
回复

使用道具 举报

1万

主题

7万

回帖

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
115490
QQ
发表于 2024-8-11 09:44:37 | 显示全部楼层
剑心v 发表于 2024-8-10 13:34
我用了这个方法,使用了 ADC1 和 ADC2,把库移植到我的工程里,并使用两个ADC各两通道,在DMA传输完成中断 ...

同一个定时器的两个通道,这两个通道占空比设置一样即可
回复

使用道具 举报

3

主题

16

回帖

25

积分

新手上路

积分
25
 楼主| 发表于 2024-8-14 09:24:29 | 显示全部楼层
谢谢Eric,使用这个方法问题得到了解决,感谢你的答疑,帮助了很多开发者解决了很多紧迫问题,向你学习。关于使用 HAL_ADCEx_MultiModeStart_DMA 这个缓冲区开大了会出现数据丢失问题,我后续有时间再仔细了解下,如果知道了我再进行反馈。
回复

使用道具 举报

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

本版积分规则

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

GMT+8, 2025-4-29 03:09 , Processed in 0.459510 second(s), 28 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2023, Tencent Cloud.

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