424 lines
15 KiB
C
424 lines
15 KiB
C
/**
|
||
******************************************************************************
|
||
* @file tmr.c
|
||
* @author Разваляев Алексей
|
||
* @brief Драйвер таймеров на основе PLIB035.
|
||
* Этот файл содержит:
|
||
* + Инициализацию таймеров TMR0-TMR3
|
||
* + Запуск и остановку таймера
|
||
* + Блокирующие и неблокирующие задержки
|
||
* + Настройку callback-функций
|
||
* + Общий обработчик прерываний таймера
|
||
* + Псевдо-PLIB функции для инициализации таймеров
|
||
*
|
||
******************************************************************************
|
||
* @attention
|
||
*
|
||
* Использование этого драйвера предполагает наличие корректных настроек:
|
||
* - Определены конфигурационные структуры tmrx_config в periph_config.h
|
||
*
|
||
******************************************************************************
|
||
* @verbatim
|
||
==============================================================================
|
||
##### Как использовать этот драйвер #####
|
||
==============================================================================
|
||
|
||
1. Настройка в periph_config.h:
|
||
(+) Определите структуры tmr0_config, tmr1_config и т.д.
|
||
(+) Используйте макросы для задания периода:
|
||
• PERIOD_US(us) - период в микросекундах
|
||
• FREQ_HZ(hz) - частота в герцах
|
||
• LOAD(ticks, presc) - период в тиках с псевдопрескалером
|
||
|
||
2. Инициализация:
|
||
(+) tmr_init_first() - включает тактирование и сброс таймеров
|
||
(+) Для каждого таймера автоматически создается хендл (htmr0, htmr1...)
|
||
|
||
3. Работа с таймером:
|
||
(+) TMR_Start(&htmr, IT) / TMR_Stop(&htmr, IT) - запуск и остановка
|
||
(+) TMR_Set_Callback(&htmr, TMR_Callback_Update, func) - установка обработчика
|
||
(+) TMR_Get_Cnt(&htmr) - текущее значение (уже поделенное на прескалер)
|
||
(+) TMR_Get_Period(&htmr) - период (уже поделенный на прескалер)
|
||
|
||
4. Задержки:
|
||
(+) TMR_Delay(&htmr, ticks) - блокирующая задержка в тиках таймера с псевдопрескалером
|
||
(+) TMR_Delay_Start(&htmr, &var) + TMR_Delay_Done(&htmr, ticks, &var) - неблокирующая
|
||
задержка в тиках таймера с псевдопрескалером
|
||
|
||
5. Обработка прерываний:
|
||
(+) Callback-функция будет вызвана автоматически
|
||
|
||
==============================================================================
|
||
##### Особенности работы #####
|
||
==============================================================================
|
||
|
||
- Псевдопрескалер:
|
||
- Это программная абстракция, реальный счетчик всегда считает полные тики
|
||
- TMR_Get_Cnt() возвращает значение уже поделенное на (Prescaler+1)
|
||
- Позволяет увеличить максимальный период в (Prescaler+1) раз
|
||
- Пример: LOAD(1000, 99) даст реальный период в 100 раз больше
|
||
|
||
- Особенности счетчиков:
|
||
- Максимальный период: 42.9 сек при 100 МГц
|
||
- Таймеры используют убывающий счет (от LOAD до 0). Функция TMR_Get_Cnt
|
||
возвращает инвертированный счетчик (LOAD-VALUE)
|
||
|
||
- Задержки:
|
||
- TMR_Delay проверяет, что задержка < LOAD, иначе вернет ERROR
|
||
- Неблокирующие задержки используют "сырые" тики (без учета прескалера)
|
||
|
||
@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
|
||
* Также переводит счетчик в возрастающий (LOAD-VALUE)
|
||
*/
|
||
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->LOAD - 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);
|
||
}
|
||
|