学习记录(四)定时器发PWM波、定时器触发ADC通过DMA存入内存,串口发送温度传感器校正值。——stm32

本文详细介绍了如何使用STM32Cube配置外部24MHz晶振,生成170MHz时钟,并利用TIM1定时器产生20KHz PWM波形,占空比为20%。接着,通过TIM6以1KHz频率触发ADC转换内部温度传感器数据,利用DMA存储转换结果,并通过串口发送温度校正值。在代码实现中,使用了STM32的库函数进行定时器、ADC、DMA的配置,并展示了计算温度和参考电压的宏定义。最后,通过串口助手验证了结果的正确性。

4.1.1 调时钟,要选外部高速源HSE(24MHz晶振),右边填170MHz后Cube软件会自动生成选择方案。

 

4.1.2 先用定时器TIM1产生一个20KHz,正占空比为20%的PWM波。配置如下:使能内部时钟、打开通道1、参数如图配置。预分频PSC和Counter Period实际计算会算上0所以要-1。为方便配占空比设计数周期为10次,Pulse设为2。

频率为 170MHz/850/10 = 20KHz。

 

在PWM mode 1、Upcounting计数模式下,Counter Period参数存入寄存器ARR(Auto Reload Register),Pulse参数存入寄存器CCR(Capture Compare Register),参照用户手册中下图。本例中计数值为9,Pulse值为2,则0~9中小于2的部分0、1为高电平,2~9为低电平,使正占空比为20%。

 

4.1.3 MDK中代码需调用此函数打开PWM输出。

 

4.1.4 示波器验证效果成功实现。

 

4.2 再用定时器以1KHz频率触发ADC转换芯片内部温度传感器数据,用DMA存储,并通过串口发送温度校正值。

  4.2.1 打开TIM6调参数使频率为 170MHz/170/1000 = 1KHz。然后打开ADC1的Temperature Sensor和Vrefint(内部参考电压)通道,设为扫描、非连续模式,用TIM6触发。

 

4.2.2 添加DMA用于将ADC采集数据存入内存,设为循环模式,因为ADC1是12位所以内存地址每次增加16位,设为Half Word。DMA将会在内存某处环形缓冲区(设为 u16 ADC_Data[20] ,两个通道各取十次测量值,计算中/10取平均值)循环往复写入数据,如图是一次循环的内存快照,下次循环从base重新开始写入。

//取数据部分代码
	ADC_TS = 0;
	ADC_VREFINT = 0;

	for(u8 i=0; i<20; ){
		ADC_TS += ADC_Data[i++];
		ADC_VREFINT += ADC_Data[i++];
	}

	ADC_TS /= 10;
	ADC_VREFINT /= 10;

再在main()中调用此函数即可打开DMA:

 

4.2.2 MDK中代码需要调用这个函数打开定时器TIM6。

 

4.2.3 ①在用户手册中研究温度校正和内部参考电压校正,得到以下两个公式:

②在stm32g4xx_II_adc.c中找到以上两公式的计算宏

/**
  * @brief  Helper macro to calculate analog reference voltage (Vref+)
  *         (unit: mVolt) from ADC conversion data of internal voltage
  *         reference VrefInt.
  * @note   Computation is using VrefInt calibration value
  *         stored in system memory for each device during production.
  * @note   This voltage depends on user board environment: voltage level
  *         connected to pin Vref+.
  *         On devices with small package, the pin Vref+ is not present
  *         and internally bonded to pin Vdda.
  * @note   On this STM32 series, calibration data of internal voltage reference
  *         VrefInt corresponds to a resolution of 12 bits,
  *         this is the recommended ADC resolution to convert voltage of
  *         internal voltage reference VrefInt.
  *         Otherwise, this macro performs the processing to scale
  *         ADC conversion data to 12 bits.
  * @param  __VREFINT_ADC_DATA__ ADC conversion data (resolution 12 bits)
  *         of internal voltage reference VrefInt (unit: digital value).
  * @param  __ADC_RESOLUTION__ This parameter can be one of the following values:
  *         @arg @ref LL_ADC_RESOLUTION_12B
  *         @arg @ref LL_ADC_RESOLUTION_10B
  *         @arg @ref LL_ADC_RESOLUTION_8B
  *         @arg @ref LL_ADC_RESOLUTION_6B
  * @retval Analog reference voltage (unit: mV)
  */
#define __LL_ADC_CALC_VREFANALOG_VOLTAGE(__VREFINT_ADC_DATA__,\
                                         __ADC_RESOLUTION__)                   \
  (((uint32_t)(*VREFINT_CAL_ADDR) * VREFINT_CAL_VREF)                          \
   / __LL_ADC_CONVERT_DATA_RESOLUTION((__VREFINT_ADC_DATA__),                  \
                                      (__ADC_RESOLUTION__),                    \
                                      LL_ADC_RESOLUTION_12B))

/**
  * @brief  Helper macro to calculate the temperature (unit: degree Celsius)
  *         from ADC conversion data of internal temperature sensor.
  * @note   Computation is using temperature sensor calibration values
  *         stored in system memory for each device during production.
  * @note   Calculation formula:
  *           Temperature = ((TS_ADC_DATA - TS_CAL1)
  *                           * (TS_CAL2_TEMP - TS_CAL1_TEMP))
  *                         / (TS_CAL2 - TS_CAL1) + TS_CAL1_TEMP
  *           with TS_ADC_DATA = temperature sensor raw data measured by ADC
  *                Avg_Slope = (TS_CAL2 - TS_CAL1)
  *                            / (TS_CAL2_TEMP - TS_CAL1_TEMP)
  *                TS_CAL1   = equivalent TS_ADC_DATA at temperature
  *                            TEMP_DEGC_CAL1 (calibrated in factory)
  *                TS_CAL2   = equivalent TS_ADC_DATA at temperature
  *                            TEMP_DEGC_CAL2 (calibrated in factory)
  *         Caution: Calculation relevancy under reserve that calibration
  *                  parameters are correct (address and data).
  *                  To calculate temperature using temperature sensor
  *                  datasheet typical values (generic values less, therefore
  *                  less accurate than calibrated values),
  *                  use helper macro @ref __LL_ADC_CALC_TEMPERATURE_TYP_PARAMS().
  * @note   As calculation input, the analog reference voltage (Vref+) must be
  *         defined as it impacts the ADC LSB equivalent voltage.
  * @note   Analog reference voltage (Vref+) must be either known from
  *         user board environment or can be calculated using ADC measurement
  *         and ADC helper macro @ref __LL_ADC_CALC_VREFANALOG_VOLTAGE().
  * @note   On this STM32 series, calibration data of temperature sensor
  *         corresponds to a resolution of 12 bits,
  *         this is the recommended ADC resolution to convert voltage of
  *         temperature sensor.
  *         Otherwise, this macro performs the processing to scale
  *         ADC conversion data to 12 bits.
  * @param  __VREFANALOG_VOLTAGE__  Analog reference voltage (unit: mV)
  * @param  __TEMPSENSOR_ADC_DATA__ ADC conversion data of internal
  *                                 temperature sensor (unit: digital value).
  * @param  __ADC_RESOLUTION__      ADC resolution at which internal temperature
  *                                 sensor voltage has been measured.
  *         This parameter can be one of the following values:
  *         @arg @ref LL_ADC_RESOLUTION_12B
  *         @arg @ref LL_ADC_RESOLUTION_10B
  *         @arg @ref LL_ADC_RESOLUTION_8B
  *         @arg @ref LL_ADC_RESOLUTION_6B
  * @retval Temperature (unit: degree Celsius)
  */
#define __LL_ADC_CALC_TEMPERATURE(__VREFANALOG_VOLTAGE__,\
                                  __TEMPSENSOR_ADC_DATA__,\
                                  __ADC_RESOLUTION__)                              \
  (((( ((int32_t)((__LL_ADC_CONVERT_DATA_RESOLUTION((__TEMPSENSOR_ADC_DATA__),     \
                                                    (__ADC_RESOLUTION__),          \
                                                    LL_ADC_RESOLUTION_12B)         \
                   * (__VREFANALOG_VOLTAGE__))                                     \
                  / TEMPSENSOR_CAL_VREFANALOG)                                     \
        - (int32_t) *TEMPSENSOR_CAL1_ADDR)                                         \
     ) * (int32_t)(TEMPSENSOR_CAL2_TEMP - TEMPSENSOR_CAL1_TEMP)                    \
    ) / (int32_t)((int32_t)*TEMPSENSOR_CAL2_ADDR - (int32_t)*TEMPSENSOR_CAL1_ADDR) \
   ) + TEMPSENSOR_CAL1_TEMP                                                        \
  )

③通过串口打印结果,仍用TaskProcess()函数,以下是部分定义和实际功能函数代码。

//部分定义
/* USER CODE BEGIN PTD */
#define MCL_MAX 2
#define __VREFINT_ADC_DATA__ (ADC_VREFINT/10)
#define __ADC_RESOLUTION__ (LL_ADC_RESOLUTION_12B)
#define __VREFANALOG_VOLTAGE__ (__HAL_ADC_CALC_VREFANALOG_VOLTAGE(__VREFINT_ADC_DATA__,\
                                                                  __ADC_RESOLUTION__))
#define __TEMPSENSOR_ADC_DATA__ (ADC_TS/10)
/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
vu32 OSTick   = 0;
u16 ADC_Data[2*10];		//两组测十次取平均值.
u32 ADC_TS, ADC_VREFINT;

static void App_EndProcess(void);
void TimingDelay_Decrement(void);
void SetMacbRemarks(u16 dif);
static void TaskProcess(void);
void TP_Send(void);
/* USER CODE END PD */
void TP_Send(void)
{
	ADC_TS = 0;
	ADC_VREFINT = 0;

	for(u8 i=0; i<20; ){
		ADC_TS += ADC_Data[i++];
		ADC_VREFINT += ADC_Data[i++];
	}
	
	printf("TS_ADC_DATA = %d \n", (ADC_TS/10));
	printf("__VREFINT_ADC_DATA__ = %d \n", (ADC_VREFINT/10));
	printf("__HAL_ADC_CALC_VREFANALOG_VOLTAGE = %lu\n",         
            __HAL_ADC_CALC_VREFANALOG_VOLTAGE(__VREFINT_ADC_DATA__,\
                                              __ADC_RESOLUTION__));
	printf("__HAL_ADC_CALC_TEMPERATURE = %lu\n", 
            __HAL_ADC_CALC_TEMPERATURE(__VREFANALOG_VOLTAGE__,\
                                       __TEMPSENSOR_ADC_DATA__,\
                                       __ADC_RESOLUTION__));
}  

4.2.4 在串口调试助手上验证结果:

<think>好的,我现在需要帮助用户解决关于STM32微控制器的ADCPWM配置问题。首先,我得理清楚用户的具体需求。用户提到想了解ADC配置和PWM生成,可能需要一个教程或者示例代码。我应该先回顾一下STM32中这两个模块的基本知识,然后考虑如何将它们结合起来使用。 首先,ADC配置方面,STM32ADC有多种模式,比如单次转换、连续转换、扫描模式等。用户可能需要知道如何初始化ADC,设置采样时间,选择通道,以及如何处理转换完成的中断。同时,PWM部分涉及到定时器的配置,比如ARR和CCR的设置,预分频器,计数模式等。需要确定用户使用的具体型号,因为不同系列的STM32可能存在差异,但用户没有说明,所以可能需要给出通用步骤,或者提到根据型号调整的部分。 接下来,考虑ADCPWM如何协同工作。例如,使用定时器触发ADC转换,这样可以在PWM的特定时刻(比如周期结束)启动ADC采样,确保采样与PWM信号同步。这种情况下,需要配置定时器触发输出,并设置ADC为外部触发模式。 然后,示例代码部分需要展示ADCPWM的初始化,以及可能的触发配置。需要确保代码结构清晰,注释明确,但也要注意用户可能使用的开环境,比如HAL库、标准外设库还是LL库。用户没有指定,所以选择常见的HAL库作为示例比较合适,因为HAL库在STM32CubeMX中广泛使用,方便用户生成代码。 另外,用户的问题可能涉及到中断处理,比如ADC转换完成中断中处理数据,或者使用DMA传输数据以减少CPU负载。需要判断是否需要包含这部分内容。考虑到用户可能希望基础的示例,先提供中断处理的例子,再提到DMA作为优化选项。 还需要考虑错误处理,比如校准ADC,确保时钟配置正确,以及GPIO的初始化是否正确。在代码示例中加入这些步骤可以避免用户遗漏关键配置。 最后,生成相关问题的时候,应该围绕用户可能遇到的后续问题,比如如何提高ADC精度,多通道配置,触发源的选择,或者PWM频率调整等。这些问题可以帮助用户进一步探索相关功能,并解决实际应用中可能出现的挑战。 总结一下,回答的结构应该是先介绍基本概念和配置步骤,然后提供代码示例,最后列出相关问题。需要确保回答准确,符合STM32的实际情况,并参考官方文档或可靠资源,比如引用HAL库的参考资料或用户手册中的相关内容。</think>### STM32 ADC配置与PWM生成教程 #### 1. ADC基础配置步骤(以STM32 HAL库为例) - **时钟配置**:启用ADC和GPIO时钟(如`__HAL_RCC_ADC1_CLK_ENABLE()`)。 - **GPIO初始化**:将模拟输入引脚设为模拟模式(如`GPIO_MODE_ANALOG`)。 - **ADC参数设置**: ```c ADC_HandleTypeDef hadc; hadc.Instance = ADC1; hadc.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4; // 时钟分频 hadc.Init.Resolution = ADC_RESOLUTION_12B; // 12位分辨率 hadc.Init.ContinuousConvMode = DISABLE; // 单次转换模式 HAL_ADC_Init(&hadc); ``` - **校准与启动**: ```c HAL_ADCEx_Calibration_Start(&hadc); // ADC校准 HAL_ADC_Start(&hadc); // 启动转换 ``` #### 2. PWM生成配置(以TIM定时器为例) - **定时器基础配置**: ```c TIM_HandleTypeDef htim; htim.Instance = TIM2; htim.Init.Prescaler = 79; // 假设系统时钟80MHz,分频后1MHz htim.Init.CounterMode = TIM_COUNTERMODE_UP; htim.Init.Period = 999; // PWM频率=1MHz/(999+1)=1kHz HAL_TIM_PWM_Init(&htim); ``` - **PWM通道设置**: ```c TIM_OC_InitTypeDef sConfigOC; sConfigOC.OCMode = TIM_OCMODE_PWM1; sConfigOC.Pulse = 500; // 占空比50%(500/1000) HAL_TIM_PWM_ConfigChannel(&htim, &sConfigOC, TIM_CHANNEL_1); HAL_TIM_PWM_Start(&htim, TIM_CHANNEL_1); // 启动PWM ``` #### 3. ADCPWM联动(定时器触发ADC采样) - **配置定时器触发输出**: ```c // 在TIM初始化代码中添加 htim.Init.RepetitionCounter = 0; htim.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE; ``` - **设置ADC外部触发源**: ```c hadc.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T2_TRGO; // TIM2触发 ``` #### 示例代码片段 ```c // ADC初始化 void MX_ADC1_Init(void) { hadc1.Instance = ADC1; hadc1.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T2_TRGO; HAL_ADC_Init(&hadc1); } // PWM初始化 void MX_TIM2_Init(void) { htim2.Instance = TIM2; htim2.Init.Prescaler = 79; htim2.Init.Period = 999; HAL_TIM_PWM_Init(&htim2); } // 主函数中启动 int main(void) { HAL_Init(); MX_ADC1_Init(); MX_TIM2_Init(); HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1); HAL_ADC_Start_IT(&hadc1); // 启用中断模式 } // ADC转换完成中断回调 void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc) { uint16_t adc_value = HAL_ADC_GetValue(hadc); // 处理采样数据(例如调整PWM占空比) __HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1, adc_value); } ``` #### 关键配置要点 1. **时钟同步**:确保ADC定时器时钟源一致[^1] 2. **触发对齐**:PWM周期结束触发ADC可避免信号抖动 3. **中断优先级**:若使用DMA,需设置高于定时器中断的优先级
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值