1. 程式人生 > 實用技巧 >STM32-定時器輸出比較模式輸出方波(中斷方式)

STM32-定時器輸出比較模式輸出方波(中斷方式)

一、工具

  1、硬體:STM32F103VET6微控制器;

  2、編譯工具:TrueSTUDIO;

  3、輔助工具:STM32CubeMX。

二、微控制器系統時鐘配置

三、微控制器定時器配置

  

  這裡引數設定說明下:

  根據時鐘樹可以知道定時器2在APB1總線上(如下圖所示),APB1的匯流排時鐘設定的為72MHz,所以定時器2時鐘源也為72MHz,那麼定時器2的週期計算公式t=(psc+1)x(period+1)/72MHz;根據公式可推匯出:(7199+1)x(9999+1)/72000000=1s。

  1、CounterSetting:

  • Prescaler預分頻器,可以將計數器時鐘頻率除以1到65536之間的任何因子,是一個16bit的值。設定的值寫入微控制器定時器的TIMx_PSC暫存器中,設定值 =(預期值-1)
  • CounterMode計數模式,設定向上計數(計數器從小到大遞增計數 0-1-0)、向下計數(計數器從大到小遞減計數 1-0-1)和居中對齊1~3(計數器先遞增再遞減 0-1-1-0或者先遞減在遞增1-0-0-1等 );
  • CountPeriod計數週期,決定定時器週期,是一個16bit的值。設定的值寫入微控制器定時器的TIMx_ARR暫存器中,設定值=(預期值-1);
  • Internal Clock Division內部時鐘分割,表示由數字濾波器(TIx)使用的時鐘(CK_INT)頻率和取樣時鐘之間的分割比率(什麼亂七八糟的,不管);
  • auto-reloadpreload自動過載預載入,使能就啟動預載入,不使能就不啟動預載入。設定的值寫入微控制器定時器的TIMx_CR1暫存器的ARPE位,具體作用如下圖所示:

  在定時器工作中,當不啟動預載入,改變ARR的值,會立即生效,如下圖所示:

  在定時器工作中,當啟動預載入,改變ARR的值,會等待下一次計數生效,而在當前計數週期會由影子自動重載入暫存器的值決定,如下圖所示:

   2、TriggerOutputParameters:

  • 這裡用不到,暫時不介紹,預設即可。

   3、OutputCompareChannel n:

  • Mode通道模式設定,設定定時器計數器與比較值相等時輸出引腳的狀態;
  • Pulse脈衝,設定比較暫存器的值(這裡建議設定為0,在中斷中改變比較暫存器的值)
  • Outputcomparepreload輸出比較預載入,作用和auto-reloadpreload類似;
  • CHPolarity通道起始電平。

  開啟定時器中斷,如果沒要求優先順序預設即可。

四、生成程式碼

  1、關於生成的定時器配置的程式碼如下所示:

static void MX_TIM2_Init(void)
{

  /* USER CODE BEGIN TIM2_Init 0 */

  /* USER CODE END TIM2_Init 0 */

  TIM_ClockConfigTypeDef sClockSourceConfig = {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 = 7199;
  htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim2.Init.Period = 9999;
  htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
  if (HAL_TIM_Base_Init(&htim2) != HAL_OK)
  {
    Error_Handler();
  }
  sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
  if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_TIM_OC_Init(&htim2) != HAL_OK)
  {
    Error_Handler();
  }
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK)
  {
    Error_Handler();
  }
  sConfigOC.OCMode = TIM_OCMODE_TOGGLE;
  sConfigOC.Pulse = 0;
  sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
  sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
  if (HAL_TIM_OC_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_3) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_TIM_OC_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_4) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN TIM2_Init 2 */

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

}

  2、定時器的中斷程式碼如下所示:

/**
  * @brief This function handles TIM2 global interrupt.
  */
void TIM2_IRQHandler(void)
{
  /* USER CODE BEGIN TIM2_IRQn 0 */

  /* USER CODE END TIM2_IRQn 0 */
  HAL_TIM_IRQHandler(&htim2);
  /* USER CODE BEGIN TIM2_IRQn 1 */

  /* USER CODE END TIM2_IRQn 1 */
}

  3、引腳和與之相關的配置程式碼如下所示:

/**
* @brief TIM_Base MSP Initialization
* This function configures the hardware resources used in this example
* @param htim_base: TIM_Base handle pointer
* @retval None
*/
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* htim_base)
{
  if(htim_base->Instance==TIM2)
  {
  /* USER CODE BEGIN TIM2_MspInit 0 */

  /* USER CODE END TIM2_MspInit 0 */
    /* Peripheral clock enable */
    __HAL_RCC_TIM2_CLK_ENABLE();
    /* TIM2 interrupt Init */
    HAL_NVIC_SetPriority(TIM2_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(TIM2_IRQn);
  /* USER CODE BEGIN TIM2_MspInit 1 */

  /* USER CODE END TIM2_MspInit 1 */
  }

}

void HAL_TIM_MspPostInit(TIM_HandleTypeDef* htim)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(htim->Instance==TIM2)
  {
  /* USER CODE BEGIN TIM2_MspPostInit 0 */

  /* USER CODE END TIM2_MspPostInit 0 */
  
    __HAL_RCC_GPIOA_CLK_ENABLE();
    /**TIM2 GPIO Configuration    
    PA2     ------> TIM2_CH3
    PA3     ------> TIM2_CH4 
    */
    GPIO_InitStruct.Pin = GPIO_PIN_2|GPIO_PIN_3;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  /* USER CODE BEGIN TIM2_MspPostInit 1 */

  /* USER CODE END TIM2_MspPostInit 1 */
  }

}

五、完善中斷服務函式:

  定時器輸出比較模式產生中斷事件後,會呼叫函式HAL_TIM_OC_DelayElapsedCallback(TIM_HandleTypeDef *htim),需要定義一個該函式,新增中斷需要處理的內容,具體操作如下所示:

  函式的內容主要是獲取定時器比較寄存TIMx_CCRn的值(即比較值),然後改變比較器下一次希望比較的值,每次比較器的值和計數器的值相等時就會觸發中斷;注意觀察設定比較值的特點(我這裡定時器暫存器TIMx_ARR的值設定的是10000),每個通道的最後一次設定的比較值非常重要,它決定了定時器的該通道是否能夠重複輸出期望的方波(如果你只在初始化定時器的時候設定了一次比較暫存器CCRn的值或者只在中斷中改變一次比較暫存器CCRn的值,微控制器對應的引腳只會在第一次啟動程式的第一個週期輸出你期望的方波,之後微控制器會以定時器的週期為基準進行電平的反轉,這是我在除錯過程中發現的現象,如果你有不同的看法希望能指出)。

void HAL_TIM_OC_DelayElapsedCallback(TIM_HandleTypeDef *htim)
{
    if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_3)
    {
        uhCapture = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_3);
        /* Set the Capture Compare Register value */
        if(uhCapture == 0)
        {
            __HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_3, 1000);
        }
        else if(uhCapture == 1000)
        {
            __HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_3, 2000);
        }
        else if(uhCapture == 2000)
        {
            __HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_3, 10000);
        }
        else if(uhCapture == 10000)
        {
            __HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_3, 1000);
        }
    }

    if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_4)
    {
        uhCapture = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_4);
        /* Set the Capture Compare Register value */
        if(uhCapture == 0)
        {
            __HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_4, 3000);
        }
        else if(uhCapture == 3000)
        {
            __HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_4, 6000);
        }
        else if(uhCapture == 6000)
        {
            __HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_4, 10000);
        }
        else if(uhCapture == 10000)
        {
            __HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_4, 3000);
        }
    }
}

六、波形圖

  從波形圖可看出,在一個週期內電平多次發生了反轉。

#endif