/** ****************************************************************************** * @file tmr.c * @author Разваляев Алексей * @brief Драйвер таймеров на основе PLIB035. * Этот файл содержит: * + Инициализацию таймеров TMR0-TMR3 * + Запуск и остановку таймера * + Блокирующие и неблокирующие задержки * + Настройку callback-функций * + Общий обработчик прерываний таймера * + Псевдо-PLIB функции для инициализации таймеров * ****************************************************************************** * @attention * * Использование этого драйвера предполагает наличие корректных настроек: * - Определены конфигурационные структуры tmrx_config в periph_config.h * ****************************************************************************** * @verbatim ============================================================================== ##### Как использовать этот драйвер ##### ============================================================================== 1. Настройка периферии (periph_config.h): (+) Определить структуры TMR_ExtInit_TypeDef для нужных таймеров: tmr0_config, tmr1_config, tmr2_config, tmr3_config (+) Настроить частоту/период в мкс или тиках, прескалер (+) Разрешить прерывания (IT) и при необходимости ADC SOC / DMA запросы (+) Указать внешнее тактирование при необходимости (+) Установить callback-функцию (можно NULL) 2. Инициализация таймеров: (+) tmr_init_first() — первичная настройка тактирования, сброс периферии и NVIC (+) tmr_init(&htmr, &config) — инициализация конкретного таймера с конфигурацией 3. Callback-функции (опционально): (+) TMR_Set_Callback(&htmr, TMR_Callback_Update, Callback) 4. Запуск и остановка таймера: (+) TMR_Start(&htmr) — запуск таймера (+) TMR_Stop(&htmr) — остановка таймера 5. Задержки: - Blocking (блокирующая): (+) TMR_Delay(&htmr, ticks) — задержка в тиках таймера - Non-blocking (неблокирующая): (+) TMR_Delay_Start(&htmr, &var) — запоминает текущее значение таймера (+) TMR_Delay_Done(&htmr, ticks, &var) — проверяет завершение задержки 6. Получение текущих значений: (+) TMR_Get_Cnt(&htmr) — получение текущего счетчика с учетом псевдопрескалера (+) TMR_Get_Period(&htmr) — получение периода таймера с учетом псевдопрескалера 7. Обработка прерываний: (+) tmr_irq_handler(&htmr) — общий обработчик ISR - В обработчиках автоматически вызываются соответствующие callback-функции и сбрасываются флаги 8. Псевдо-PLIB функции: (+) TMR_Init(TMRx, InitStruct) — инициализация таймера с расширенными параметрами @endverbatim ****************************************************************************** */ //-- Includes ------------------------------------------------------------------ #include "periph_config.h" TMR_HandleTypeDef htmr0; /*!< Хендл TMR0 */ TMR_HandleTypeDef htmr1; /*!< Хендл TMR1 */ TMR_HandleTypeDef htmr2; /*!< Хендл TMR2 */ TMR_HandleTypeDef htmr3; /*!< Хендл TMR3 */ //-- Defines ------------------------------------------------------------------- //-- TMR Init functions -------------------------------------------------------- /** * @brief Первичная инициализация таймеров TMR0-TMR3 * @details Настройка таймеров и хендлов: тактирование, прерывания */ void tmr_init_first(void) { #if (USE_TMR0==1) RCU_APBClkCmd(RCU_APBClk_TMR0, ENABLE); RCU_APBRstCmd(RCU_APBRst_TMR0, ENABLE); htmr0.Instance = TMR0; tmr_init(&htmr0, &tmr0_config); if(htmr0.Config->IT == ENABLE) { NVIC_EnableIRQ(TMR0_IRQn); } #endif #if (USE_TMR1==1) RCU_APBClkCmd(RCU_APBClk_TMR1, ENABLE); RCU_APBRstCmd(RCU_APBRst_TMR1, ENABLE); htmr1.Instance = TMR1; tmr_init(&htmr1, &tmr1_config); if(htmr1.Config->IT == ENABLE) { NVIC_EnableIRQ(TMR1_IRQn); } #endif #if (USE_TMR2==1) RCU_APBClkCmd(RCU_APBClk_TMR2, ENABLE); RCU_APBRstCmd(RCU_APBRst_TMR2, ENABLE); htmr2.Instance = TMR2; tmr_init(&htmr2, &tmr2_config); if(htmr2.Config->IT == ENABLE) { NVIC_EnableIRQ(TMR2_IRQn); } #endif #if (USE_TMR3==1) RCU_APBClkCmd(RCU_APBClk_TMR3, ENABLE); RCU_APBRstCmd(RCU_APBRst_TMR3, ENABLE); htmr3.Instance = TMR3; tmr_init(&htmr3, &tmr3_config); if(htmr3.Config->IT == ENABLE) { NVIC_EnableIRQ(TMR3_IRQn); } #endif } /** * @brief Инициализация таймера * @param htmr указатель на хендл таймера * @param NewConfig указатель на новую конфигурацию UART, иначе используется та, что в структуре * @retval OperationStatus OK - если всё хорошо, ERROR - если ошибка */ OperationStatus tmr_init(TMR_HandleTypeDef *htmr, TMR_ExtInit_TypeDef *NewConfig) { if(!htmr || !htmr->Instance) return ERROR; if(NewConfig != NULL) { htmr->Config = NewConfig; } if(htmr->Config == NULL) { return ERROR; } TMR_Init(htmr->Instance, htmr->Config); return OK; } /** * @brief Установка коллбека таймера * @param htmr указатель на хендл таймера * @param CallbackType Тип коллбека * @param UpdCallback Функция коллбека * @retval void */ OperationStatus TMR_Set_Callback(TMR_HandleTypeDef* htmr, TMR_CallbackTypeDef CallbackType, void (*Callback)(void)) { if(!htmr || !htmr->Instance || !htmr->Config) { return ERROR; } switch(CallbackType) { case TMR_Callback_Update: htmr->Config->UpdCallback = Callback; break; default: return ERROR; } return OK; } //-- TMR API functions -------------------------------------------------------- /** * @brief Запуск таймера * @param htmr указатель на хендл таймера * @retval OperationStatus OK - если всё хорошо, ERROR - если ошибка */ OperationStatus TMR_Start(TMR_HandleTypeDef *htmr, FunctionalState IT) { if(!htmr || !htmr->Instance) { return ERROR; } TMR_ITCmd(htmr->Instance, IT); TMR_Cmd(htmr->Instance, ENABLE); return OK; } /** * @brief Остановка таймера * @param htmr указатель на хендл таймера * @retval OperationStatus OK - если всё хорошо, ERROR - если ошибка */ OperationStatus TMR_Stop(TMR_HandleTypeDef *htmr, FunctionalState IT) { if(!htmr || !htmr->Instance) { return ERROR; } TMR_ITCmd(htmr->Instance, IT); TMR_Cmd(htmr->Instance, DISABLE); return OK; } /** * @brief Получить счетчик таймера (с псевдопрескалером) * @param htmr указатель на хендл таймера * @retval Текущие значение счетчика с псевдопрескалером * @details Если частота таймера 100 МГц, и псведопрескалер 100-1, * то при реальном счетчике 100,000, вернется значение 1,000 */ uint32_t TMR_Get_Cnt(TMR_HandleTypeDef *htmr) { if(!htmr || !htmr->Instance) { return ERROR; } uint32_t presc = htmr->Config->Prescaler+1; uint32_t currtick = htmr->Instance->VALUE; return currtick/presc; } /** * @brief Получить период таймера (с псевдопрескалером) * @param htmr указатель на хендл таймера * @retval Период с псевдопрескалером * @details Если частота таймера 100 МГц, и псведопрескалер 100-1, и период равен 1 мс, * то при реальном периоде 100,000, вернется значение 1,000 */ uint32_t TMR_Get_Period(TMR_HandleTypeDef *htmr) { if(!htmr || !htmr->Instance) { return ERROR; } uint32_t presc = htmr->Config->Prescaler+1; uint32_t currload = htmr->Instance->LOAD; return currload/presc; } //-- TMR Delays API functions ------------------------------------------------- /** * @brief Задержка в тиках таймера (блокирующая). * @param htmr Указатель на хендл таймера. * @param delay Задержка в тиках таймера. * @return OperationStatus. * @details Формирует задержку с блокировкой программы. */ OperationStatus TMR_Delay(TMR_HandleTypeDef *htmr, uint32_t delay) { if(!htmr || !htmr->Instance) return ERROR; uint32_t presc = htmr->Config->Prescaler+1; uint64_t delay_load = delay*presc; if(delay_load > 0xFFFFFFFF) { delay_load = 0xFFFFFFFF; } if(delay_load >= htmr->Instance->LOAD) { return ERROR; } uint32_t starttick = htmr->Instance->VALUE; while(1) { if((starttick - htmr->Instance->VALUE) >= delay_load) { return OK; } } } /** * @brief Начать отсчет неблокирующей задержки. * @param htmr Указатель на хендл таймера. * @param var Указатель на переменную куда положить значение тиков. * @return OperationStatus. * @details Запоминает счетчик для начала отсчета неблокирующей задержки. * @ref TMR_Delay_Done для проверки статуса задержки. * @note Переменная содержит сырые тики, без преобразования под псевдопрескалер, * поэтому в var могут быть неадекватно большие значения тиков */ OperationStatus TMR_Delay_Start(TMR_HandleTypeDef *htmr, uint32_t *var) { if(!htmr || !htmr->Instance) return ERROR; *var = htmr->Instance->VALUE; return OK; } /** * @brief Задержка в тиках таймера (неблокирующая). * @param htmr Указатель на хендл таймера. * @param delay Задержка в тиках таймера. * @param var Указатель на переменную где хранится тики в момент начала задержки. * @return 1 - задержка прошла. 0 - задержка в процессе. * @details Формирует задержку с блокировкой программы. * Перед ожиданием задержки надо инициализировать задержку @ref TMR_Delay_Start * @note Функция считает задержку через сырые тики, поэтому в var могут быть * неадекватно большие значения тиков */ int TMR_Delay_Done(TMR_HandleTypeDef *htmr, uint32_t delay, uint32_t *var) { if(!htmr || !htmr->Instance) return 0; uint32_t presc = htmr->Config->Prescaler+1; uint64_t delay_load = delay*presc; if(delay_load > 0xFFFFFFFF) { delay_load = 0xFFFFFFFF; } if(delay_load >= htmr->Instance->LOAD) { return 0; } if((*var - htmr->Instance->VALUE) >= delay_load) { return 1; // Задержка прошла } else { return 0; } } //-- TMR Handler functions ---------------------------------------------------- /** * @brief Обработчик прерывания таймера * @param htmr указатель на хендл таймера * @retval void */ void tmr_irq_handler(TMR_HandleTypeDef* htmr) { if((htmr->Instance == NULL) || (htmr->Config == NULL)) { return; } /* Проверка флага прерывания таймера */ if (TMR_ITStatus(htmr->Instance) == SET) { /* Если есть коллбек вызываем его */ if(htmr->Config->UpdCallback) htmr->Config->UpdCallback(); /* Очистка флага прерывания */ TMR_ITStatusClear(htmr->Instance); } } //-- TMR pseudoPLIB functions ------------------------------------------------- /** * @brief Инициализирует модуль TMRx согласно параметрам структуры InitStruct. * @param TMRx Выбор таймера, где x=A|B * @param InitStruct Указатель на структуру типа @ref TMR_ExtInit_TypeDef, * которая содержит конфигурационную информацию * @retval void */ void TMR_Init(TMR_TypeDef* TMRx, TMR_ExtInit_TypeDef* InitStruct) { uint32_t tim_clk = InitStruct->ClkFreq*__MHZ; uint32_t presc = InitStruct->Prescaler+1; if(InitStruct->PeriodUs) { TMR_PeriodConfig(TMRx, tim_clk, InitStruct->PeriodUs); } else if(InitStruct->FreqHz) { TMR_FreqConfig(TMRx, tim_clk, InitStruct->FreqHz); } else { uint64_t load = (uint64_t)InitStruct->Load*presc; if(load > 0xFFFFFFFF) load = 0xFFFFFFFF; TMR_SetLoad(TMRx, load); } TMR_ITCmd(TMRx, InitStruct->IT); TMR_DMAReqCmd(TMRx, InitStruct->DMAReq); TMR_ADCSOCCmd(TMRx, InitStruct->ADCSOC); TMR_ExtInputConfig(TMRx, InitStruct->ExtInput); }