MCU定时器控制ADC采样频率实现详解

目录

一、工程依赖的硬件及背景

二、设计目的

三、 建立工程

1.选择时钟源和Debug模式

2.配置系统时钟和ADC时钟

3.配置串口

4.配置ADC

5.设置TIM3

6.设置TIM4 

7.配置中断

8.GPIO

四、代码修改

1.重新定义ADC回调函数

2.在主程序中编写数据发送代码

3.使能ADC和开启定时器

4.查看结果


        在细说MCU的ADC模块单通道连续采样的实现方法-CSDN博客  https://blog.csdn.net/wenchm/article/details/140008718http://xn--https-rfa//blog.csdn.net/wenchm/article/details/140008718中,通过使能AD配置参数中的连续转换模式(Continuous Conversion Mode),并结合ADC中断,实现了连续采样。该例中,ADC的采样频率约为1.58 kHz,是通过设置ADC的时钟频率和采样时间得到的。实际中,有时希望ADC以给定的采样频率转换数据,譬如1 kHz。在这种情况下靠配置ADC时钟频率和采样时间的方法就非常不方便。

        本例子使用定时器来控制ADC连续采样。

一、工程依赖的硬件及背景

        文章依赖的硬件及工程配置参考本文作者的其他文章:细说MCU的ADC模块单通道连续采样的实现方法-CSDN博客  https://blog.csdn.net/wenchm/article/details/140008718http://xn--https-rfa//blog.csdn.net/wenchm/article/details/140008718

二、设计目的

        信号源为TIM4的TIM_CHANNEL_1,管脚PA11,给ADC1_IN1输入信号;也可以用管脚PA5的LD2作为信号源给ADC1_IN1输入信号;

       ADC1_IN1采集到信号后,按缓冲大小的规定,循环给串口发送数据;

       ADC采集样品的频率由TIM3设置的规则控制;

三、 建立工程

1.选择时钟源和Debug模式

        使用片外时钟晶体作为HSE的时钟源。将Debug设置为Serial Wire。

2.配置系统时钟和ADC时钟

        将系统时钟(SYSCLK)频率配置为170 MHz,并设置ADC1的时钟为34 MHz。

3.配置串口

        配置USART2模式(Mode)为异步(Asychronous),其他参数设置均保持默认(波特率为115200),不开启中断。将USART2的两引脚PA2和PA3均设置为上拉(Pull-up)。

4.配置ADC

        配置ADC1的通道1(IN1),选择IN1 Single-ended;在下面的配置(Configuration)区,对几个参数进行调整:

  • ADC的时钟预分频参数(Clock Prescaler)选择Asynchronous clock mode divided by 1,也就是不分频(前面的例子是分频256倍,目的是想得到所需要的采样频率)。本例将用定时器实现对采样频率的控制,所以ADC的时钟可以不用进行分频处理。
  • 将ADC设置(ADC_Settings)参数栏中连续转换模式(Continuous Conversion Mode)设置为Disabled,即不使能,因为本例中ADC采样频率要通过定时器来控制。
  • 转换结束选择(End Of Conversion Selection)参数仍保持单次转换结束(End of single conversion);由于目前只使用了一个ADC通道,还可以选择序列转换结束(End of sequence conversion),对结果没有影响。
  • 在ADC规则转换模式(ADC_Regular_Conversion Mode)栏中,外部触发转换源(External Trigger Conversion Source)选择Timer 3 Trigger Out event,使用TIM3的触发输出作为ADC的触发源。
  • 位于Rank下的采样时间选择2.5个周期。前面提到过,这个参数决定着ADC的转换时间。如果选择2.5个周期,则在12位分辨率时ADC的转换时间为2.5+12+0.5=15个周期。
  • 使能ADC1的中断。
  • 5.设置TIM3

            TIM3的模式(Mode)区,选择时钟源(Clock Source)为内部时钟(InterClock);计数器的预分频因子设置为169,则定时器的时钟频率为系统频率的1/170,如果系统频率为170 MHz,则定时器的时钟频率为1 MHz;计数器的周期设置为999,则计数器的溢出频率为1MHz/1000 =1 kHz。在Trigger Output参数栏中将发事件设置为更新事件(Update Event)。

    6.设置TIM4 

            TIM4的模式(Mode)区,选择时钟源(Clock Source)为内部时钟(InterClock);通道1选择PWM Generation CH1;GPIO管脚默认PA11;计数器的预分频因子设置为999,计数器的周期设置为8499,auto reload=Enable;Pulse=2125;

    7.配置中断

            将ADC1中断的优先级设为1。此外,由于将会用到HAL_Delay函数,所以要将tick timer中断的占式优先级设为0。

            TIM4的全局中断的优先级设置为2;

    8.GPIO

            设置 PA5(LD2) 为GPIO OUTPUT,OUTput level默认LOW,PP,Pullup,High;别名LED;

    四、代码修改

    1.重新定义ADC回调函数

            在主程序中重写回调函数HAL_ADC_ConvCpltCallback()和串口发数据的putchar函数,并且将它们放到main.c的注释对中:

    /* USER CODE BEGIN 4 */
    void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc)
    {
    	ADC1ConvertedData[ADC1Data_index] = HAL_ADC_GetValue(&hadc1);
    	if(ADCSampleFlag == 0)
    		ADC1Data_index ++;
    	if(ADC1Data_index == ADC_CONVERTED_DATA_BUFFER_SIZE)
    	{
    		ADCSampleFlag = 1;
    		ADC1Data_index = 0;
    	}
    }
    
    //信号源
    void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
    {
    	HAL_GPIO_TogglePin(LED_GPIO_Port,LED_Pin);
    }
    
    //串口打印
    int __io_putchar(int ch)
    {
    	HAL_UART_Transmit(&huart2,(uint8_t *)&ch,1,0xFFFF);
    	return ch;
    }
    /* USER CODE END 4 */

    2.在主程序中编写数据发送代码

            将数据发送代码放置到main函数的while(1)循环中。

    while(1)
    /* USER CODE BEGIN 3 */
    if(ADCSampleFlag == 1)
    {
        for(uint16_t i=1;i<ADC_CONVERTED_DATA_BUFFER_SIZE;i++)
        {
            printf("ADC1ConvertedData[%d] = %d\r\n",i,ADC1Convertedata[i]);
        }
        ADCSampleFlag = 0;
    }
    HAL_Delay(1000);
    /*USER CODE END 3 */

            上述函数中用到的变量需要定义。仍然是将它们定义为全局变量,放到主程序中的注释中:

    /*USER CODE BEGIN PV*/
    uint16_t ADC1ConvertedData[ADC_CONVERTED_DATA_BUFFER_SIZE];
    uint16_t ADC1Data_index =0;
    uint8_t ADCSampleFlag =0;
    /*USER CODE END PV*/

            其中,数组长度ADC_CONVERTED_DATA_BUFFER_SIZE可以定义到main.h中:

    /* USER CODE BEGIN Private defines */
    #define ADC_CONVERTED_DATA_BUFFER_SIZE(uint16_t) 65
    /* USER CODE END Private defines */

            同时,在main.c中,包含头文件stdio.h:

    /* USER CODE BEGIN Includes */
    #include "stdio.h"
    /* USER CODE END Includes */

    3.使能ADC和开启定时器

            在主程序初始化代码中使能ADC中断,并开启定时器TIM3、TIM4。将HAL_ADC_Sta IT()、HAL_ADCEx_Calibration_Start()、HAL_TIM_Base_Start()、HAL_TIM_Base_Start_IT()和HAL_TIM_PWM_Start()放到while(1)之前, MX_ADC1_Init()之后的注释对中: 

      /* USER CODE BEGIN 2 */
      HAL_ADCEx_Calibration_Start(&hadc1,ADC_SINGLE_ENDED);
      HAL_ADC_Start_IT(&hadc1);
      HAL_TIM_Base_Start(&htim3);
      HAL_TIM_Base_Start_IT(&htim4);
      HAL_TIM_PWM_Start(&htim4,TIM_CHANNEL_1);
      /* USER CODE END 2 */

    4.查看结果

            把PA5(LD2)信号或者PA11(TIM4_CH1)信号施加信号到ADC1_IN1输入端PA0上,打开串口助手即可收到送上来的数据。

            为了实测ADC的采样频率,同样可以配置PC3作为输出引脚,在回调函数HAL_ADC _ ConvCpltCallback()中加入控制PC3输出状态的语句,并通过示波器测量PC3引脚的输出 形,此时测得的频率为采样频率。

            实测的PA5(LD2)信号:

             实测的串口接收的数据:

     

    作者:wenchm

    物联沃分享整理
    物联沃-IOTWORD物联网 » MCU定时器控制ADC采样频率实现详解

    发表回复