实验准备

开发板:STM32F411E-DISCO或其它开发板(Firmware Package根据开发板下载)

软件:Keil uVision5、STM32CubeMX(Firmware Package:STM32Cube FW_F4 V1.23.0)

实验原理

  利用CubeMX根据芯片手册配置定时器同步来实现自定义脉冲数PWM输出对电机进行控制。主定时器产生PWM波,从定时器对主定时器发出的脉冲进行计数。  

STM32F4xx定时器内部触发连接

 

       

 

 

CubeMX 时钟配置表

/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

/**Configure the main internal regulator output voltage
*/
__HAL_RCC_PWR_CLK_ENABLE();
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
/**Initializes the CPU, AHB and APB busses clocks
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLM = 4;
RCC_OscInitStruct.PLL.PLLN = 96;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV4;
RCC_OscInitStruct.PLL.PLLQ = 4;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/**Initializes the CPU, AHB and APB busses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1) != HAL_OK)
{
Error_Handler();
}
}

 

 主定时器TIM5配置(主:TIM5----从:TIM1)   


/**
* @brief TIM5 Initialization Function
* @param None
* @retval None
*/
static void MX_TIM5_Init(void)
{

/* USER CODE BEGIN TIM5_Init 0 */
// TIM_ClockConfigTypeDef sClockSourceConfig;
/* USER CODE END TIM5_Init 0 */

TIM_MasterConfigTypeDef sMasterConfig = {0};
TIM_OC_InitTypeDef sConfigOC = {0};

/* USER CODE BEGIN TIM5_Init 1 */

/* USER CODE END TIM5_Init 1 */
htim5.Instance = TIM5;
htim5.Init.Prescaler = 48-1;
htim5.Init.CounterMode = TIM_COUNTERMODE_UP;
htim5.Init.Period = 400-1;
htim5.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
if (HAL_TIM_PWM_Init(&htim5) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_ENABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim5, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = 200;
sConfigOC.OCPolarity = TIM_OCPOLARITY_LOW;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
if (HAL_TIM_PWM_ConfigChannel(&htim5, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN TIM5_Init 2 */

/* USER CODE END TIM5_Init 2 */
HAL_TIM_MspPostInit(&htim5);

}

从定时器TIM1配置(主:TIM5----从:TIM1) 从定时器TIM1需要开中断NVIC

/**
* @brief TIM1 Initialization Function
* @param None
* @retval None
*/
static void MX_TIM1_Init(void)
{

/* USER CODE BEGIN TIM1_Init 0 */

/* USER CODE END TIM1_Init 0 */

TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_SlaveConfigTypeDef sSlaveConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};

/* USER CODE BEGIN TIM1_Init 1 */

/* USER CODE END TIM1_Init 1 */
htim1.Instance = TIM1;
htim1.Init.Prescaler = 0;
htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
htim1.Init.Period = 65535;
htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim1.Init.RepetitionCounter = 0;
if (HAL_TIM_Base_Init(&htim1) != HAL_OK)
{
Error_Handler();
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim1, &sClockSourceConfig) != HAL_OK)
{
Error_Handler();
}
sSlaveConfig.SlaveMode = TIM_SLAVEMODE_GATED;
sSlaveConfig.InputTrigger = TIM_TS_ITR0;
if (HAL_TIM_SlaveConfigSynchronization(&htim1, &sSlaveConfig) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN TIM1_Init 2 */

/* USER CODE END TIM1_Init 2 */

}

 

主定时器TIM2配置(主:TIM2----从:TIM4)

/**
* @brief TIM2 Initialization Function
* @param None
* @retval None
*/
static void MX_TIM2_Init(void)
{

/* USER CODE BEGIN TIM2_Init 0 */

/* USER CODE END TIM2_Init 0 */

TIM_MasterConfigTypeDef sMasterConfig = {0};
TIM_OC_InitTypeDef sConfigOC = {0};

/* USER CODE BEGIN TIM2_Init 1 */

/* USER CODE END TIM2_Init 1 */
htim2.Instance = TIM2;
htim2.Init.Prescaler = 48-1;
htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
htim2.Init.Period = 400-1;
htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
if (HAL_TIM_PWM_Init(&htim2) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_ENABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = 200;
sConfigOC.OCPolarity = TIM_OCPOLARITY_LOW;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
if (HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN TIM2_Init 2 */

/* USER CODE END TIM2_Init 2 */
HAL_TIM_MspPostInit(&htim2);

}

 

从定时器TIM4配置(主:TIM2----从:TIM4)从定时器TIM4需要开中断NVIC

/**
* @brief TIM4 Initialization Function
* @param None
* @retval None
*/
static void MX_TIM4_Init(void)
{

/* USER CODE BEGIN TIM4_Init 0 */

/* USER CODE END TIM4_Init 0 */

TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_SlaveConfigTypeDef sSlaveConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};

/* USER CODE BEGIN TIM4_Init 1 */

/* USER CODE END TIM4_Init 1 */
htim4.Instance = TIM4;
htim4.Init.Prescaler = 0;
htim4.Init.CounterMode = TIM_COUNTERMODE_UP;
htim4.Init.Period = 65535;
htim4.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
if (HAL_TIM_Base_Init(&htim4) != HAL_OK)
{
Error_Handler();
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim4, &sClockSourceConfig) != HAL_OK)
{
Error_Handler();
}
sSlaveConfig.SlaveMode = TIM_SLAVEMODE_GATED;
sSlaveConfig.InputTrigger = TIM_TS_ITR1;
if (HAL_TIM_SlaveConfigSynchronization(&htim4, &sSlaveConfig) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim4, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN TIM4_Init 2 */

/* USER CODE END TIM4_Init 2 */

}

 

添加中断回调函数(注意次函数一定要添加在“/* USER CODE BEGIN 4 */”和“/* USER CODE END 4 */”之间,这样cubemx更新代码时不会清除掉)

/* USER CODE BEGIN 4 */

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{

if(htim==(&htim1))
{
PWM1_OK = 0;
if(__HAL_TIM_GET_FLAG(&htim1, TIM_FLAG_CC1) != RESET) //判断是否触发中断
{
__HAL_TIM_CLEAR_FLAG(&htim1, TIM_FLAG_CC1); //清除中断标志位
HAL_TIM_PWM_Stop_IT(&htim5, TIM_CHANNEL_1); //关闭主定时器的PWM输出
HAL_TIM_Base_Stop_IT(&htim1); //关闭从定时器的计数
}
}
if(htim==(&htim4))
{
PWM2_OK = 0;
if(__HAL_TIM_GET_FLAG(&htim4, TIM_FLAG_CC1) != RESET) //判断是否触发中断
{
__HAL_TIM_CLEAR_FLAG(&htim4, TIM_FLAG_CC1); //清除中断标志位
HAL_TIM_PWM_Stop_IT(&htim2, TIM_CHANNEL_1); //关闭主定时器的PWM输出
HAL_TIM_Base_Stop_IT(&htim4); //关闭从定时器的计数
}
}
}


void PWM_out1(uint32_t pulse_data)
{
if(PWM1_OK == 1) //标志判断
{
PWM1_OK = 0;
TIM5->ARR=speed1;
TIM5->CCR1=(speed1>>1);
__HAL_TIM_SET_AUTORELOAD(&htim1,pulse_data-1); //ARR装载要输出的PWM脉冲数
HAL_TIM_Base_Start_IT(&htim1); //从定时器计数开启
HAL_TIM_PWM_Start_IT(&htim5, TIM_CHANNEL_1); //主定时器PWM脉冲输出
}
}
void PWM_out2(uint32_t pulse_data)
{
if(PWM2_OK == 1) //标志判断
{
PWM2_OK = 0;
TIM2->ARR=speed2;
TIM2->CCR1=(speed2>>1);
__HAL_TIM_SET_AUTORELOAD(&htim4,PWM_pulse-1); //ARR装载要输出的PWM脉冲数
HAL_TIM_Base_Start_IT(&htim4); //从定时器计数开启
HAL_TIM_PWM_Start_IT(&htim2, TIM_CHANNEL_1); //主定时器PWM脉冲输出
}
}

 

/* USER CODE END 4 */

在主函数前 添加私有变量 添加私有函数声明

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN PV */
uint8_t PWM1_OK = 0;
uint8_t PWM2_OK = 0;
uint32_t PWM_pulse = 0;
uint16_t speed1=80; //电机默认速度
uint16_t speed2=80; //电机默认速度
/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
/* USER CODE BEGIN PFP */
void PWM_out1(uint32_t pulse_data);
void PWM_out2(uint32_t pulse_data);

/* USER CODE END PFP */

 

 在主函数 中添加PWM输出函数

/* Infinite loop */
/* USER CODE BEGIN WHILE */
PWM1_OK = 1;
PWM2_OK = 1;
PWM_pulse = 20;
speed1=400; //电机默认速度 数值越大脉冲周期越大 电机速度越慢
speed2=400; //电机默认速度 数值越大脉冲周期越大 电机速度越慢
while (1)
{
/* USER CODE END WHILE */

/* USER CODE BEGIN 3 */
PWM_out1(PWM_pulse);
PWM_out2(PWM_pulse);

}
/* USER CODE END 3 */

 

 

 

 引脚输出脉冲观察(PA0、PA5)