Files
VK035_Template/MDK-ARM/Core/App/tmr.c

424 lines
15 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
******************************************************************************
* @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);
}