硬汉嵌入式论坛

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

[ADC] ADC+TIM+DMA 100kHz以上无法采样

[复制链接]

15

主题

57

回帖

102

积分

初级会员

积分
102
发表于 2025-3-21 13:57:02 | 显示全部楼层 |阅读模式
主频200MHz,配置为100kHz定时触发ADC采集5kHz信号,每个周期可以正常采集20个点,将定时器周期改为999,理论计算触发频率应该是200kHz,但是实际运行无法进入DMA中断了,这是什么问题?
[C] 纯文本查看 复制代码
static void TIM1_Config(u32 period)
{
	TIM_HandleTypeDef  htim ={0};
	TIM_OC_InitTypeDef sConfig = {0};

	__HAL_RCC_TIM1_CLK_ENABLE();
      
    /*-----------------------------------------------------------------------

    System Clock source       = PLL (HSE)
    SYSCLK(Hz)                = 200000000 (CPU Clock)
    HCLK(Hz)                  = 200000000 (AXI and AHBs Clock)
    AHB Prescaler             = 2
    D2 APB1 Prescaler         = 2 (APB1 Clock  100MHz)
    D2 APB2 Prescaler         = 2 (APB2 Clock  100MHz)


    因为APB1 prescaler != 1, 所以 APB1上TIMxCLK = APB1 x 2 = 100MHz;
    因为APB2 prescaler != 1,   所以 APB2上TIMxCLK = APB2 x 2 = 200MHz;

    APB1 定时器有TIM2, TIM3 ,TIM4, TIM5, TIM6, TIM7, TIM12
    APB2 定时器有TIM1, TIM8 , TIM15

	period = 	20000
    TIM1CLK = 200MHz/(Period + 1) / (Prescaler + 1) = 200MHz / 20000 / 1 = 10KHz
    period = 	2000
    TIM1CLK = 200MHz/(Period + 1) / (Prescaler + 1) = 200MHz / 2000 / 1 = 100KHz
	----------------------------------------------------------------------- */  
    HAL_TIM_Base_DeInit(&htim);
    
    htim.Instance = TIM1;
	htim.Init.Period            = 1999;
	htim.Init.Prescaler         = 0;
	htim.Init.ClockDivision     = 0;
	htim.Init.CounterMode       = TIM_COUNTERMODE_UP;
	htim.Init.RepetitionCounter = 0;
	HAL_TIM_Base_Init(&htim);
    
    sConfig.OCMode     = TIM_OCMODE_PWM1;
    sConfig.OCPolarity = TIM_OCPOLARITY_LOW;

    /* 占空比50% */
    sConfig.Pulse = 1000;  
    if(HAL_TIM_OC_ConfigChannel(&htim, &sConfig, TIM_CHANNEL_1) != HAL_OK)
    {
		Error_Handler();
    }

    /* 启动OC1 */
    if(HAL_TIM_OC_Start(&htim, TIM_CHANNEL_1) != HAL_OK)
    {
		Error_Handler();
    }
}
/*
*********************************************************************************************************
*	函 数 名: bsp_InitADC
*	功能说明: 初始化ADC
*	形    参:	无
*	返 回 值: 无
*********************************************************************************************************
*/
void bsp_InitADC1(void)
{
	ADC_ChannelConfTypeDef   sConfig = {0};
	GPIO_InitTypeDef         GPIO_InitStruct;

	/* ## - 2 - 配置ADC采样使用的引脚 ####################################### */
	__HAL_RCC_GPIOA_CLK_ENABLE();

    GPIO_InitStruct.Pin = ISEN_C_Pin|CLIP_C_Pin;
    GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(OVP_V_GPIO_Port, &GPIO_InitStruct);


	/* ## - 3 - 配置ADC采样使用的时钟 ####################################### */
	__HAL_RCC_DMA2_CLK_ENABLE();
	hdma_adc1.Instance                 = DMA2_Stream0;            /* 使用的DMA2 Stream0 */
	hdma_adc1.Init.Channel             = DMA_CHANNEL_0;  	  	/* 请求类型采用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_HALFWORD;  /* 外设数据传输位宽选择半字,即16bit */     
	hdma_adc1.Init.MemDataAlignment    = DMA_MDATAALIGN_HALFWORD;  /* 存储器数据传输位宽选择半字,即16bit */    
	hdma_adc1.Init.Mode                = DMA_CIRCULAR;            /* 循环模式 */   
	hdma_adc1.Init.Priority            = DMA_PRIORITY_LOW;        /* 优先级低 */  
	hdma_adc1.Init.FIFOMode            = DMA_FIFOMODE_DISABLE;    /* 禁止FIFO*/
	hdma_adc1.Init.FIFOThreshold       = DMA_FIFO_THRESHOLD_FULL; /* 禁止FIFO此位不起作用,用于设置阀值 */
	hdma_adc1.Init.MemBurst            = DMA_MBURST_SINGLE;       /* 禁止FIFO此位不起作用,用于存储器突发 */
	hdma_adc1.Init.PeriphBurst         = DMA_PBURST_SINGLE;       /* 禁止FIFO此位不起作用,用于外设突发 */
 
    /* 初始化DMA */
    if(HAL_DMA_Init(&hdma_adc1) != HAL_OK)
    {
		Error_Handler();     
    }
    
    /* 开启DMA1 Stream1的中断 */
    HAL_NVIC_SetPriority(DMA2_Stream0_IRQn, 2, 0);
    HAL_NVIC_EnableIRQ(DMA2_Stream0_IRQn);
    
    /* 关联ADC句柄和DMA句柄 */
	__HAL_LINKDMA(&hadc1, DMA_Handle, hdma_adc1);

	/* ## - 4 - 配置ADC ########################################################### */
	__HAL_RCC_ADC1_CLK_ENABLE();
	hadc1.Instance = ADC1;
	hadc1.Init.ClockPrescaler        = ADC_CLOCK_SYNC_PCLK_DIV4;      /* 采用AHB同步时钟,4分频,即200MHz/4 = 50MHz */
	hadc1.Init.Resolution            = ADC_RESOLUTION_12B;            /* 16位分辨率 */
	hadc1.Init.ScanConvMode          = ADC_SCAN_ENABLE;              /* 禁止扫描,因为仅开了一个通道 */
	hadc1.Init.EOCSelection          = ADC_EOC_SINGLE_CONV;           /* EOC转换结束标志 */
	hadc1.Init.ContinuousConvMode    = DISABLE;                       /* 禁止自动转换,采用的定时器触发转换 */
	hadc1.Init.NbrOfConversion       = 2;                             /* 使用了1个转换通道 */
	hadc1.Init.DiscontinuousConvMode = DISABLE;                       /* 禁止不连续模式 */
	hadc1.Init.NbrOfDiscConversion   = 1;                             /* 禁止不连续模式后,此参数忽略,此位是用来配置不连续子组中通道数 */
	hadc1.Init.ExternalTrigConv      = ADC_EXTERNALTRIGCONV_T1_CC1;            /* 定时器1的CC1触发 */
	hadc1.Init.ExternalTrigConvEdge  = ADC_EXTERNALTRIGCONVEDGE_RISING;    /* 上升沿触发 */
	hadc1.Init.DMAContinuousRequests = ENABLE;

    /* 初始化ADC */
	if (HAL_ADC_Init(&hadc1) != HAL_OK)
	{
		Error_Handler();
	}
  
  
	/* 配置ADC通道  */
	sConfig.Channel      = ADC_CHANNEL_5;              /* 配置使用的ADC通道 */
	sConfig.Rank         = ADC_REGULAR_RANK_1;          /* 采样序列里的第1个 */
	sConfig.SamplingTime = ADC_SAMPLETIME_3CYCLES;     /* 采样周期 */
	sConfig.Offset = 0;                                 /* 无偏移的情况下,此参数忽略 */

	if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
	{
		Error_Handler();
	}
    /** Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time.
	*/
	sConfig.Channel = ADC_CHANNEL_3;
	sConfig.Rank = ADC_REGULAR_RANK_2;			/* 采样序列里的第2个 */
	if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
	{
		Error_Handler();
	}
	/* ## - 5 - 配置ADC的定时器触发 ####################################### */
	TIM1_Config(ADC1_FS_100KHZ);
  
	/* ## - 6 - 启动ADC的DMA方式传输 ####################################### */
	//if (HAL_ADC_Start_DMA(&hadc1, (uint32_t *)ADC1Buf, ADC_SIZE*2) != HAL_OK)
	//{
	//	Error_Handler();
	//}
}
[C] 纯文本查看 复制代码
/*

*********************************************************************************************************
*	函 数 名: DMA2_Stream0_IRQHandler
*	功能说明: DMA2 Stream0中断服务程序
*	形    参: 无
*	返 回 值: 无
*********************************************************************************************************
*/
void DMA2_Stream0_IRQHandler(void)
{
	/* 传输完成中断 */
	if((DMA2->LISR & DMA_FLAG_TCIF0_4) != RESET)
	{
				
		/*
		   1、使用此函数要特别注意,第1个参数地址要32字节对齐,第2个参数要是32字节的整数倍。
		   2、进入传输完成中断,当前DMA正在使用缓冲区的前半部分,用户可以操作后半部分。
		*/
		SCB_InvalidateDCache_by_Addr((uint32_t *)(&ADC1Buf[512][0]), ADC_SIZE*2);
		
		s_ADC1DmaAllFlag = 1;
		
		/* 清除标志 */
		DMA2->LIFCR = DMA_FLAG_TCIF0_4;
	}

	/* 半传输完成中断 */    
	if((DMA2->LISR & DMA_FLAG_HTIF0_4) != RESET)
	{
		/*
		   1、使用此函数要特别注意,第1个参数地址要32字节对齐,第2个参数要是32字节的整数倍。
		   2、进入半传输完成中断,当前DMA正在使用缓冲区的后半部分,用户可以操作前半部分。
		*/
		SCB_InvalidateDCache_by_Addr((uint32_t *)(&ADC1Buf[0][0]), ADC_SIZE*2);	
		s_ADC1DmaHarfFlag = 1;
        
		/* 清除标志 */
		DMA2->LIFCR = DMA_FLAG_HTIF0_4;
	}

	/* 传输错误中断 */
	if((DMA2->LISR & DMA_FLAG_TEIF0_4) != RESET)
	{
		/* 清除标志 */
		DMA2->LIFCR = DMA_FLAG_TEIF0_4;
	}

	/* 直接模式错误中断 */
	if((DMA2->LISR & DMA_FLAG_DMEIF0_4) != RESET)
	{
		/* 清除标志 */
		DMA2->LIFCR = DMA_FLAG_DMEIF0_4;
	}
}
回复

使用道具 举报

1万

主题

7万

回帖

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
115434
QQ
发表于 2025-3-24 00:25:42 | 显示全部楼层
配置上看着没什么问题。

你的HAL_ADC_Start_DMA在那里调用的,缓冲配置的多少。另外就是计算下当前ADC主频下,依次转换两个通道需要的时间多少。看看最大可以触发的速度满足需求不
回复

使用道具 举报

15

主题

57

回帖

102

积分

初级会员

积分
102
 楼主| 发表于 2025-3-24 09:24:28 | 显示全部楼层
eric2013 发表于 2025-3-24 00:25
配置上看着没什么问题。

你的HAL_ADC_Start_DMA在那里调用的,缓冲配置的多少。另外就是计算下当前ADC主 ...

[C] 纯文本查看 复制代码
void Clip_Sample_Start(void)
{
	HAL_ADC_Start_DMA(&hadc1,(uint32_t*)ADC1Buf,ADC_SIZE*2);	
}

缓冲2048Bytes。
ADC主频目前配置到最大36MHz了,双通道都是3采样周期,单通道转换时间(3+12.5)/36=0.43us,双通道0.86us. 计算的话是不是超过最大触发速度了。
回复

使用道具 举报

15

主题

57

回帖

102

积分

初级会员

积分
102
 楼主| 发表于 2025-3-24 09:25:15 | 显示全部楼层
eric2013 发表于 2025-3-24 00:25
配置上看着没什么问题。

你的HAL_ADC_Start_DMA在那里调用的,缓冲配置的多少。另外就是计算下当前ADC主 ...

启动DMA是定时调用
回复

使用道具 举报

1万

主题

7万

回帖

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
115434
QQ
发表于 2025-3-25 09:07:03 | 显示全部楼层
lishang4650 发表于 2025-3-24 09:24
[mw_shl_code=c,true]void Clip_Sample_Start(void)
{
        HAL_ADC_Start_DMA(&hadc1,(uint32_t*)ADC1Buf, ...

特别注意

HAL_ADC_Start_DMA(&hadc1,(uint32_t*)ADC1Buf,ADC_SIZE*2);   

最后一个参数单位是DMA的传输次数,不是字节数,你看你是不是设置错了。
回复

使用道具 举报

15

主题

57

回帖

102

积分

初级会员

积分
102
 楼主| 发表于 2025-3-25 10:54:15 | 显示全部楼层
eric2013 发表于 2025-3-25 09:07
特别注意

HAL_ADC_Start_DMA(&hadc1,(uint32_t*)ADC1Buf,ADC_SIZE*2);   

感谢指正。我理解的是传输次数也是定时触发一次传输一次吧,设置为2048次 为什么在100kHz时能正常进入DMA中断,200k就不行了呢
回复

使用道具 举报

1万

主题

7万

回帖

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
115434
QQ
发表于 2025-3-26 09:12:13 | 显示全部楼层
lishang4650 发表于 2025-3-25 10:54
感谢指正。我理解的是传输次数也是定时触发一次传输一次吧,设置为2048次 为什么在100kHz时能正常进入DMA ...

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

剩下就是慢速,直接,快速传输通道问题了。


回复

使用道具 举报

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

本版积分规则

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

GMT+8, 2025-4-26 08:40 , Processed in 0.508283 second(s), 24 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2023, Tencent Cloud.

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