引言:本文主要介绍高级定时器的配置参数及如何配置三相全桥。内容包括:1.HAL库定时器函数,2.高级定时器的配置参数解析,3.三相全桥的配置及stm32cubemx设置,4.keil5代码解释

1.HAL库定时器函数

        高级定时器(如STM32中的TIM1和TIM8)是功能强大的定时器模块,用于PWM、输入捕获、输出比较、编码器模式等高精度操作。HAL库提供了多种函数来操作高级定时器。这些函数分为初始化、控制、输入捕获、输出比较、PWM和中断等。

序号 当前任务用到的HAL库函数 作用
1 TIM1->ARR = 8000 - 1; 设置TIM1的ARR的值
2 TIM1->CCR4 = 8000 - 2; 设置TIM1_IN4的CCR值
3 HAL_TIM_Base_Start( &htim1); 启动TIM1
4 HAL_TIM_PWM_Start( &htim1, TIM_CHANNEL_4); 启动TIM1_IN4
5 __HAL_TIM_SET_PRESCALER(TIM_HandleTypeDef *htim, uint32_t Prescaler); 设置预分配PSC值
/*********************定时器**************************/
//初始化用的
HAL_TIM_Base_Init(TIM_HandleTypeDef *htim);
HAL_TIM_PWM_Init(TIM_HandleTypeDef *htim);
HAL_TIM_OC_Init(TIM_HandleTypeDef *htim);
HAL_TIM_IC_Init(TIM_HandleTypeDef *htim);
HAL_TIM_Encoder_Init(TIM_HandleTypeDef *htim, TIM_Encoder_InitTypeDef *sConfig);
HAL_TIMEx_MasterConfigSynchronization(TIM_HandleTypeDef *htim, TIM_MasterConfigTypeDef *sMasterConfig);
//启动暂停
HAL_TIM_Base_Start(TIM_HandleTypeDef *htim);
HAL_TIM_Base_Stop(TIM_HandleTypeDef *htim);
HAL_TIM_Base_Start_IT(TIM_HandleTypeDef *htim);
HAL_TIM_Base_Stop_IT(TIM_HandleTypeDef *htim);

/********************PWM******************************/
//初始化
HAL_TIM_Base_Init(TIM_HandleTypeDef *htim);
HAL_TIM_PWM_Init(TIM_HandleTypeDef *htim);
HAL_TIM_OC_Init(TIM_HandleTypeDef *htim);
HAL_TIM_IC_Init(TIM_HandleTypeDef *htim);
HAL_TIM_Encoder_Init(TIM_HandleTypeDef *htim, TIM_Encoder_InitTypeDef *sConfig);
HAL_TIMEx_MasterConfigSynchronization(TIM_HandleTypeDef *htim, TIM_MasterConfigTypeDef *sMasterConfig);
//启动暂停
HAL_TIM_Base_Start(TIM_HandleTypeDef *htim);
HAL_TIM_Base_Stop(TIM_HandleTypeDef *htim);
HAL_TIM_Base_Start_IT(TIM_HandleTypeDef *htim);
HAL_TIM_Base_Stop_IT(TIM_HandleTypeDef *htim);
//改变占空比
__HAL_TIM_SET_COMPARE(TIM_HandleTypeDef *htim, uint32_t Channel, uint32_t Compare);

/***********************输出比较**********************/
//初始化
HAL_TIM_OC_Init(TIM_HandleTypeDef *htim);
//启动暂停
HAL_TIM_OC_Start(TIM_HandleTypeDef *htim, uint32_t Channel);
HAL_TIM_OC_Stop(TIM_HandleTypeDef *htim, uint32_t Channel);
HAL_TIM_OC_Start_IT(TIM_HandleTypeDef *htim, uint32_t Channel);
HAL_TIM_OC_Stop_IT(TIM_HandleTypeDef *htim, uint32_t Channel);

/*********************输入捕获*********************/
//初始化
HAL_TIM_IC_Init(TIM_HandleTypeDef *htim);
//启动暂停
HAL_TIM_IC_Start(TIM_HandleTypeDef *htim, uint32_t Channel);
HAL_TIM_IC_Stop(TIM_HandleTypeDef *htim, uint32_t Channel);
HAL_TIM_IC_Start_IT(TIM_HandleTypeDef *htim, uint32_t Channel);
HAL_TIM_IC_Stop_IT(TIM_HandleTypeDef *htim, uint32_t Channel);
//读取捕获值
__HAL_TIM_GET_COMPARE(TIM_HandleTypeDef *htim, uint32_t Channel);

/******************编码器***********************/
//初始化
HAL_TIM_Encoder_Init(TIM_HandleTypeDef *htim, TIM_Encoder_InitTypeDef *sConfig);
//启动暂停
HAL_TIM_Encoder_Start(TIM_HandleTypeDef *htim, uint32_t Channel);
HAL_TIM_Encoder_Stop(TIM_HandleTypeDef *htim, uint32_t Channel);
HAL_TIM_Encoder_Start_IT(TIM_HandleTypeDef *htim, uint32_t Channel);
HAL_TIM_Encoder_Stop_IT(TIM_HandleTypeDef *htim, uint32_t Channel);

2.高级定时器的配置参数解析

2.1高级定时器框图

        大家看到这个图不要害怕,实际上我们配置三相驱动桥,只需要用到其中一些配置即可,并且也需要根据这个图的参数,配置我们的stm32cubemx上的高级定时器参数,大家专注一下1.时钟源,2.控制器,3.时基单元,4.输出比较,5.重复计数器,其他的暂时没用到,后续使用到再详细学习。

2.2时钟源参数设置

        时钟来源分为内部时钟internal Clock和外部时钟ETC,这里我建议直接使用内部时间即可,也就是APB总线上的时钟源,具体是哪个APB总线需要查看数据手册

2.3控制器选择

模式分为1.触发控制器,如本实验把TIM1_IN4设置为PWM不输出模式,作一个ADC的触发源;2.从模式控制器,3.编码器接口

2.4时基单元

1.预分配器PSC,范围是0~65535,2.自动重装载寄存器ARR,范围0~65535,3.那系统时钟进过PSC后,CNT计数器的时钟频率 = f / (PSC + 1)。

        最终定时器溢出需要计数ARR+1个周期, 则溢出时间T = (ARR + 1)(PSC + 1)/ f

        解释:(1)ARR +1  是因为在记了ARR次后,计数器满了,还需要一个时钟周期才溢出

                   (2)PSC + 1 是因为手册规定的。

2.5输入捕获

2.6捕获比较

2.7重复计算器

RCR = 0,则每过1个定时器周期,计数器记2次
RCR = 1,则每过1个定时器周期,计数器记1次
RCR = 2,则每过2个定时器周期,计数器记1次
RCR = 3,则每过3个定时器周期,计数器记1次
所以定时器溢出时间的计算,RCR也有影响

2.8输出比较

TIM1_CH1/TIM1_CH1N、TIM1_CH2/TIM1_CH2N、TIM1_CH1/TIM1_CH3N三个为PWM互补通道,TIM1_CH4为PWM不输出模式,用于给ADC作触发源

2.9刹车

下一节再说

3.三相全桥的配置及stm32cubemx设置

        实验目标:配置三相驱动桥为互补输出,TIM1_IN4给ADC采样三相电流的触发源,在下一章内部比较+刹车+DAC级联配置再讲解刹车如何配置。

        首先使用内部时钟,然后三个互补通道,一个PWM不输出

        预分频值PSC,计数器的时钟频率 = f / (PSC + 1),不分频

        计数模式,分为向上计数、向下计数、中央对齐计数,中央对齐:一个定时器周期内会计数两次

        PWM抖动模式,可以提高PWM波的分辨率,咱们不用使能

        自动重装载值ARR,范围0~65535,这里8000-1

        内部时钟分频CKD,只能2分聘,或者4分频,这里2分频

        重复计数器RCR,见2.7,这里设为1

        自动重装载预装载,如果使能,在需要改变ARR值的时候,会提前在此定时器周期结束前改变,也就是有一定的缓冲作用,减少定时器误差;如果不使能,则在需要改变ARR值的时候,会在此定时器周期结束时改变,没有缓冲作用,误差就包含了操作ARR所花费的时间。因三相全桥不需要改变ARR值,故不使能自动重装载。

        三个通道配置一样,PWM的模式1,通道4为ADC触发源,把他设为PWM模式2,占空比为8000-2,这样每次采用三相电流时,都在PMSM电机的 0 0 0位,较为准确,具体看欧拉电子吧,视频讲的明白。

        因为刹车需要触发源,所以下一节咱们通过内部比较器将DAC模拟输出与ADC比较作为触发源,再说刹车功能

4.keil5代码解释

具体使用就是

再main函数初始化那加

 //TIM1高级定时器
  TIM1->ARR = 8000 - 1;
    TIM1->CCR4 = 8000 - 2;
    HAL_TIM_Base_Start( &htim1);
    HAL_TIM_PWM_Start( &htim1, TIM_CHANNEL_4);

 然后随便在哪用都行

        TIM1->CCR1 = 0;
        TIM1->CCR2 = 0;
        TIM1->CCR3 = 0;


/**************************** 4.ADC+3.ABZ编码器+5.高级定时器+2.串口****************************/
void HAL_ADCEx_InjectedConvCpltCallback(ADC_HandleTypeDef *hadc)
{
	static uint8_t cnt;
  static uint8_t prev_state;
  /* Prevent unused argument(s) compilation warning */
  UNUSED(hadc);
	if(hadc == &hadc1)
	{
		if(ADC_offset == 0)
		{
			cnt++;
			adc1_in1 = hadc1.Instance->JDR1;//读取注入组三相电流
			adc1_in2 = hadc1.Instance->JDR2;
			adc1_in3 = hadc1.Instance->JDR3;

			IA_Offset += adc1_in1;//10次累计再做平均,实习滤波,算出电流0点偏移量
			IB_Offset += adc1_in2;
			IC_Offset += adc1_in3;

			if(cnt >= 10)
			{
				ADC_offset = 1;
				IA_Offset = IA_Offset/10;
				IB_Offset = IB_Offset/10;
				IC_Offset = IC_Offset/10;
			}
		}
		else
		{
			adc1_in1 = hadc1.Instance->JDR1 ;
			adc1_in2 = hadc1.Instance->JDR2 ;
			adc1_in3 = hadc1.Instance->JDR3 ;
                                      
			rtU.ia = (adc1_in1 - IA_Offset)*0.00149025f;
			rtU.ib = (adc1_in2 - IB_Offset)*0.00149025f;
			rtU.ic = (adc1_in3 - IC_Offset)*0.00149025f;

      
      //编码器读取ABZ
      Enc_cnt = __HAL_TIM_GET_COUNTER(&htim3) ;  
      
      m_Theta = (Enc_cnt + amend_Theta)*2*PI/16384;
      while(m_Theta >= 2*PI){m_Theta -= 2*PI;}
      while(m_Theta < 0){m_Theta += 2*PI;}//角度归一化
      //转速计算
      m_speed = (m_Theta - m_Theta_delay)/0.0001;
      m_Theta_delay = m_Theta;
       
      //电角度、机械角度计算
      e_Theta = m_Theta *pole_pairs;
      while(e_Theta >= 2*PI){e_Theta -= 2*PI;}
      while(e_Theta < 0){e_Theta += 2*PI;}//角度归一化
      // 电角度0对齐
      if(prev_state == 2 && state == 3) {
        __HAL_TIM_SET_COUNTER(&htim3, 0);
        e_Theta = 0;
      }
      prev_state = state;
      
      //位置计算
      m_positionfd = Position_Cal(m_prevTheta, m_Theta);
      m_prevTheta = m_Theta;
      
      //赋值给Simulink模型
      rtU.FOC_state = 1;
      rtU.PositionRef = 0;
      rtU.PositionFd = m_positionfd;
      rtU.theta = e_Theta;
      rtU.torque_ref = 1;
      FOC_Model_step();
      
      if(state == 2){
        TIM1->CCR1 = 0;
        TIM1->CCR2 = 30;
        TIM1->CCR3 = 100;
      }
      else{
			TIM1->CCR1 = rtY.tABC[0];
			TIM1->CCR2 = rtY.tABC[1];
			TIM1->CCR3 = rtY.tABC[2];
      }
//			TIM1->CCR1 = rtY.tABC[0];
//			TIM1->CCR2 = rtY.tABC[1];
//			TIM1->CCR3 = rtY.tABC[2];
      
			load_data[0] = rtU.ia;
			load_data[1] = adc1_in1;
			load_data[2] =IA_Offset;
			load_data[3] = state;
			load_data[4] = e_Theta;
			memcpy(tempData, (uint8_t *)&load_data, sizeof(load_data));
			HAL_UART_Transmit_DMA(&huart2,(uint8_t *)tempData,6*4);

		}
	}

  /* NOTE : This function should not be modified. When the callback is needed,
            function HAL_ADCEx_InjectedConvCpltCallback must be implemented in the user file.
  */
}
/**************************** 4.ADC+3.ABZ编码器+5.高级定时器+2.串口****************************/

结果:分别给三相桥0.5、0.375、0.625的占空比,计算三相电流(已重构为FOC使用的数据)


本章内容十分复杂,大家可以结合b站的欧拉电子和正点原子hal库视频理解

内容较多,难免出错,如有错误,感谢指正

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐