(c) Wiki |
Почнемо із візуального конфігурування:
Для головного (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") постів, можна тут.
Немає коментарів:
Дописати коментар