понеділок, 14 березня 2016 р.

Таймери STM32 -- внутрішні тригери/HAL

(c) Wiki
Повторимо "подвиг" CMSIS, розглянутий раніше, зробивши із двох 16-бітних таймерів один 32-бітний, засобами HAL.

Почнемо із візуального конфігурування:


Для головного (master) таймера просто вмикаємо тактування. Підлеглому, slave, вказуємо працювати в режимі External Clock Mode 1 та отримувати тактування від внутрішнього джерела ITR3, котре, як ми знаємо з попереднього поста, під'єднане до TRGO TIM4.


На вкладці Configure, для TIM4 встановлюємо потрібний період -- 24000 та, що значно важливіше для нашого розгляду, дозволяємо бути master-таймером, посилаючи тактовий імпульс кожної події оновлення:


Для TIM1 залишається хіба вказати період:

Звичайно, щоб мигати світлодіодом, слід також налаштувати GPIO, як це вже не раз розглядалося.

Можна починати писати код. Процедура обробки переривання вже знайома (подробиці див. відповідний пост):

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
 if( htim->Instance == TIM1 )
 //if( htim == &htim1 )
 {
  HAL_GPIO_TogglePin(GREEN_LED_PC8_GPIO_Port, GREEN_LED_PC8_Pin);
 }
}

Вміст main() тривіальний -- наведемо її повністю:

int main(void)
{

  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration----------------------------------------------------------*/

  /* 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_TIM1_Init();
  MX_TIM4_Init();

  /* USER CODE BEGIN 2 */
  HAL_TIM_Base_Start(&htim4);
  HAL_TIM_Base_Start_IT(&htim1);
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
   HAL_Delay(10000);
  /* USER CODE END WHILE */

  /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */

}

Ініціалізація


Конфігурування таймера TIM4 відрізняється хіба ввімкненням TRGO:

void MX_TIM4_Init(void)
{
  TIM_ClockConfigTypeDef sClockSourceConfig;
  TIM_MasterConfigTypeDef sMasterConfig;

  htim4.Instance = TIM4;
  htim4.Init.Prescaler = 0;
  htim4.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim4.Init.Period = 24000;
  htim4.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  HAL_TIM_Base_Init(&htim4);

  sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
  HAL_TIM_ConfigClockSource(&htim4, &sClockSourceConfig);

  sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_ENABLE;
  HAL_TIMEx_MasterConfigSynchronization(&htim4, &sMasterConfig);

}

Воно здійснюється функцією HAL_TIMEx_MasterConfigSynchronization(), за даними структури TIM_MasterConfigTypeDef. У ній вказано: MasterOutputTrigger = TIM_TRGO_UPDATE та MasterSlaveMode = TIM_MASTERSLAVEMODE_ENABLE. Знаходиться ця функція не в Drivers\STM32F1xx_HAL_Driver\Src\stm32f1xx_hal_tim.c, як більшість розглянутих функцій таймерів з HAL, а в stm32f1xx_hal_tim_ex.c -- вважається, що належить до розширених функцій таймера.

Вона зовсім проста (в тому сенсі, що її код легко співставити із ручною маніпуляцією відповідними регістрами):

HAL_StatusTypeDef HAL_TIMEx_MasterConfigSynchronization(TIM_HandleTypeDef *htim, TIM_MasterConfigTypeDef * sMasterConfig)
{
  /* Check the parameters */
  assert_param(IS_TIM_MASTER_INSTANCE(htim->Instance));
  assert_param(IS_TIM_TRGO_SOURCE(sMasterConfig->MasterOutputTrigger));
  assert_param(IS_TIM_MSM_STATE(sMasterConfig->MasterSlaveMode));

  __HAL_LOCK(htim);

  htim->State = HAL_TIM_STATE_BUSY;

  /* Reset the MMS Bits */
  htim->Instance->CR2 &= ~TIM_CR2_MMS;
  /* Select the TRGO source */
  htim->Instance->CR2 |=  sMasterConfig->MasterOutputTrigger;

  /* Reset the MSM Bit */
  htim->Instance->SMCR &= ~TIM_SMCR_MSM;
  /* Set or Reset the MSM Bit */
  htim->Instance->SMCR |= sMasterConfig->MasterSlaveMode;

  htim->State = HAL_TIM_STATE_READY;

  __HAL_UNLOCK(htim);

  return HAL_OK;
}

Автоматично згенерована ініціалізація TIM1, у свою чергу, налаштовує підлеглий режим:

void MX_TIM1_Init(void)
{
  TIM_SlaveConfigTypeDef sSlaveConfig;
  TIM_MasterConfigTypeDef sMasterConfig;

  htim1.Instance = TIM1;
  htim1.Init.Prescaler = 0;
  htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim1.Init.Period = 1000;
  htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim1.Init.RepetitionCounter = 0;
  HAL_TIM_Base_Init(&htim1);

  sSlaveConfig.SlaveMode = TIM_SLAVEMODE_EXTERNAL1;
  sSlaveConfig.InputTrigger = TIM_TS_ITR3;
  HAL_TIM_SlaveConfigSynchronization(&htim1, &sSlaveConfig);

  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig);

}

Користується вона HAL_TIM_SlaveConfigSynchronization() та структурою TIM_SlaveConfigTypeDef, у якій задається: SlaveMode = TIM_SLAVEMODE_EXTERNAL1, InputTrigger = TIM_TS_ITR3. Що цікаво, а ця функція, здавалося б -- симетрична до HAL_TIMEx_MasterConfigSynchronization, знаходиться в stm32f1xx_hal_tim.c:

HAL_StatusTypeDef HAL_TIM_SlaveConfigSynchronization(TIM_HandleTypeDef *htim, TIM_SlaveConfigTypeDef * sSlaveConfig)
{
  /* Check the parameters */
  assert_param(IS_TIM_SLAVE_INSTANCE(htim->Instance));
  assert_param(IS_TIM_SLAVE_MODE(sSlaveConfig->SlaveMode));
  assert_param(IS_TIM_TRIGGER_SELECTION(sSlaveConfig->InputTrigger));

  __HAL_LOCK(htim);

  htim->State = HAL_TIM_STATE_BUSY;

  TIM_SlaveTimer_SetConfig(htim, sSlaveConfig);

  /* Disable Trigger Interrupt */
  __HAL_TIM_DISABLE_IT(htim, TIM_IT_TRIGGER);

  /* Disable Trigger DMA request */
  __HAL_TIM_DISABLE_DMA(htim, TIM_DMA_TRIGGER);

  htim->State = HAL_TIM_STATE_READY;

  __HAL_UNLOCK(htim);

  return HAL_OK;
}

Для своєї роботи вона викликає "службову функцію" TIM_SlaveTimer_SetConfig(), яку ми вже розглядали раніше, тільки тепер працюватиме гілка switch для TIM_TS_ITR3, котра нічого не робить, крім перевірки аргументів (цілу функцію див. відповідний пост):

  case TIM_TS_ITR3:
    {
      /* Check the parameter */
      assert_param(IS_TIM_CC2_INSTANCE(htim->Instance));
    }
    break;

Ось і все. Хоча, нагадаю -- приклад штучний! Аж два таймери використовувати для такої дрібниці не варто. Тим більше, замість створювати 32-бітний таймер (наприклад, коли потрібні великі інтервали часу), в більшості випадків можна обійтися, акуратно налаштувавши подільники, повтори або зробити грубший відлік вручну -- в процедурі обробки переривання.

Скачати проект із кодом для цього, попереднього ("Таймери STM32 -- зовнішнє тактування/HAL") та наступного ("Таймери STM32 -- Автоматична зупинка/HAL") постів, можна тут.

Немає коментарів:

Дописати коментар