我试图在运行FreeRTOS的STM32F7板上使用Timer12实现PWM输入捕获。作为概念证明,我尝试检测通常用于驱动伺服电机的PWM信号(整个周期为20 ms,脉冲为1ms,1.5ms或2 ms)。
我目前已设置Timer10以输出PWM信号。我有一个外部GPIO中断设置,每当我按板上的一个按钮时,就可以更改PWM信号的占空比(此中断实际上只是允许执行可更改占空比的FreeRTOS任务)。
我也将Timer12设置为PWM输入捕获。通道1设置为上升沿,以便可以检测信号的整个周期,而通道2设置为下降沿,以便可以检测信号的脉冲。但是,我希望此输入捕获是“可伸缩的”,以便可以将其用于许多不同周期的不同信号,因此我使输入捕获的周期非常小。通过这种方式,我可以使用PeriodElapsed回调函数来跟踪输入捕获周期已经经过了多少次。
int main(void)
{
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* Configure the system clock */
SystemClock_Config();
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_DMA_Init();
MX_USART1_UART_Init();
MX_TIM10_Init();
MX_TIM12_Init();
HAL_UART_Transmit(&huart1, (uint8_t *)"start\r\n", 7, 100);
HAL_TIM_PWM_Start_IT(&htim10, TIM_CHANNEL_1); // Start PWM signal
if(xTaskCreate(vDisplayPWM, "PWM Display", 200, NULL, 3, &DisplayPWM) == pdPASS)
{
HAL_UART_Transmit(&huart1, (uint8_t *)"PWM display task created\r\n", 26, 100);
}
if(xTaskCreate(vChangePWMPeriod, "Period Change Display", 200, NULL, 3, &ChangePWMPeriod) == pdPASS)
{
HAL_UART_Transmit(&huart1, (uint8_t *)"Change period task created\r\n", 28, 100);
}
vTaskStartScheduler();
/* Infinite loop */
while (1)
{
}
}
static void MX_TIM10_Init(void)
{
TIM_OC_InitTypeDef sConfigOC = {0};
htim10.Instance = TIM10;
htim10.Init.Prescaler = 1999;
htim10.Init.CounterMode = TIM_COUNTERMODE_UP;
htim10.Init.Period = 2075;
htim10.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim10.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim10) != HAL_OK)
{
Error_Handler();
}
if (HAL_TIM_PWM_Init(&htim10) != HAL_OK)
{
Error_Handler();
}
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = 103;
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
if (HAL_TIM_PWM_ConfigChannel(&htim10, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)
{
Error_Handler();
}
HAL_TIM_MspPostInit(&htim10);
}
static void MX_TIM12_Init(void)
{
TIM_SlaveConfigTypeDef sSlaveConfig = {0};
TIM_IC_InitTypeDef sConfigIC = {0};
htim12.Instance = TIM12;
htim12.Init.Prescaler = 0;
htim12.Init.CounterMode = TIM_COUNTERMODE_UP;
htim12.Init.Period = 65535;
htim12.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim12.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim12) != HAL_OK)
{
Error_Handler();
}
if (HAL_TIM_IC_Init(&htim12) != HAL_OK)
{
Error_Handler();
}
sSlaveConfig.SlaveMode = TIM_SLAVEMODE_RESET;
sSlaveConfig.InputTrigger = TIM_TS_TI1FP1;
sSlaveConfig.TriggerPolarity = TIM_INPUTCHANNELPOLARITY_RISING;
sSlaveConfig.TriggerFilter = 0;
if (HAL_TIM_SlaveConfigSynchro(&htim12, &sSlaveConfig) != HAL_OK)
{
Error_Handler();
}
sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_RISING;
sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI;
sConfigIC.ICPrescaler = TIM_ICPSC_DIV1;
sConfigIC.ICFilter = 10;
if (HAL_TIM_IC_ConfigChannel(&htim12, &sConfigIC, TIM_CHANNEL_1) != HAL_OK)
{
Error_Handler();
}
sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_FALLING;
sConfigIC.ICSelection = TIM_ICSELECTION_INDIRECTTI;
if (HAL_TIM_IC_ConfigChannel(&htim12, &sConfigIC, TIM_CHANNEL_2) != HAL_OK)
{
Error_Handler();
}
HAL_TIM_Base_Start_IT(&htim12);
HAL_TIM_IC_Start_IT(&htim12, TIM_CHANNEL_1);
HAL_TIM_IC_Start_IT(&htim12, TIM_CHANNEL_2);
}
void vDisplayPWM(void *pvParameters)
{
uint32_t control = 0;
uint32_t pwm_period_ticks = 0;
uint32_t pwm_pulse_ticks = 0;
for(;;)
{
if(ulTaskNotifyTake(pdTRUE, portMAX_DELAY) != 0)
{
control = ((float)pwm_pulse/(float)pwm_period) * 1000;
pwm_period_ticks = pwm_period * __HAL_TIM_GET_COMPARE(&htim12, TIM_CHANNEL_1);
pwm_pulse_ticks = pwm_pulse * __HAL_TIM_GET_COMPARE(&htim12, TIM_CHANNEL_2);
switch(control)
{
case 48 ... 52: HAL_UART_Transmit(&huart1, (uint8_t *) "Mode 1\r\n", 8, 100);
break;
case 73 ... 77: HAL_UART_Transmit(&huart1, (uint8_t *) "Mode 2\r\n", 8, 100);
break;
case 98 ... 102: HAL_UART_Transmit(&huart1, (uint8_t *) "Mode 3\r\n", 8, 100);
break;
}
sprintf(results, "period: %lu pulse: %lu input timer pulse: %lu\r\n", pwm_period_ticks, pwm_pulse_ticks, htim10.Instance->CCR1);
HAL_UART_Transmit(&huart1, (uint8_t *) results, strlen(results), 100);
//__HAL_TIM_SET_COUNTER(&htim12, 0);
pwm_period = 0;
pwm_pulse = 0;
vTaskDelay(1000);
}
}
vTaskDelete(NULL);
}
void vChangePWMPeriod(void *pvParameters)
{
for(;;)
{
if(ulTaskNotifyTake(pdTRUE, portMAX_DELAY) != 0)
{
htim10.Instance->CCR1 += 52;
if(htim10.Instance->CCR1 > 207)
{
htim10.Instance->CCR1 = 103;
}
}
vTaskDelay(1000);
}
vTaskDelete(NULL);
}
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance == TIM12)
{
pwm_period ++;
if(pulse_flag) // If signal is high
{
pwm_pulse ++;
}
}
}
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
if(!pulse_flag) // Rising edge
{
pulse_flag = !pulse_flag;
vTaskNotifyGiveFromISR(DisplayPWM,&xHigherPriorityTaskWoken);
if(xHigherPriorityTaskWoken != pdFALSE)
{
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
}
else // Falling edge
{
pulse_flag = !pulse_flag;
}
}
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
HAL_GPIO_TogglePin(GPIOJ, GPIO_PIN_5);
vTaskNotifyGiveFromISR(ChangePWMPeriod, &xHigherPriorityTaskWoken);
if(xHigherPriorityTaskWoken != pdFALSE)
{
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
}
我已经用示波器验证了Timer10的周期与我们的预期行为(20 ms周期,1ms 1.5ms或2 ms脉冲)匹配。但是,在串行监视器中监视反馈显示,从1ms脉冲到1.5ms脉冲,测量的脉冲值保持不变,然后对于2ms脉冲具有正确的值。任何反馈将不胜感激。