Проект перенесен в папку MDK-ARM

This commit is contained in:
2025-12-28 15:38:30 +03:00
parent 8b930ebe12
commit c63d98f431
75 changed files with 110 additions and 64 deletions

316
MDK-ARM/Core/App/sysclk.c Normal file
View File

@@ -0,0 +1,316 @@
/**
******************************************************************************
* @file sysclk.c
* @author Разваляев Алексей
* @brief Драйвер тактирования системы на основе PLIB035.
* Этот файл содержит:
* + Инициализацию системного тактирования (PLL, осцилляторы)
* + Настройку частоты ядра и периферии
* + Управление системным таймером (SysTick)
* + Функции для работы со временем (millis, micros)
* + Систему периодических коллбеков
* + Настройку тактирования периферии
*
******************************************************************************
* @attention
*
* Этот драйвер должен быть инициализирован ПЕРВЫМ в программе, до любой другой периферии.
* Неправильная настройка тактирования может привести к неработоспособности всего МК.
*
* Использование этого драйвера предполагает наличие корректных настроек:
* - Определены константы SYSCLK_CORE_CLOCK_MHZ и SYSCLK_Oscil_Type в periph_config.h
* - Определен тип системного тика SYSCLK_TickType в periph_config.h
*
******************************************************************************
* @verbatim
==============================================================================
##### Как использовать этот драйвер #####
==============================================================================
1. Настройка в periph_config.h:
(+) SYSCLK_CORE_CLOCK_MHZ - частота ядра в МГц (например, 100)
(+) SYSCLK_Oscil_Type - источник тактирования (RCU_Oscil_OSE или RCU_Oscil_OSI)
(+) SYSCLK_TickType - период системного тика (от SYSCLK_Tick_1us до SYSCLK_Tick_100ms)
2. Инициализация (в начале main()):
(+) sysclk_init() - настраивает PLL, SysTick и счетчики времени
(+) В SysTick_Handler() вызвать sysclk_irq_handler()
3. Работа со временем:
(+) millis() - текущее время в миллисекундах (переполнение через 49 дней)
(+) micros() - текущее время в микросекундах (точность зависит от SYSCLK_TickType)
4. Периодические задачи:
(+) SYSCLK_Set_Callback(func, period_ms) - регистрация функции для периодического вызова
(+) Максимум 16 функций, период должен быть >= периода системного тика
5. Настройка тактирования периферии:
(+) rcu_set_clock_adc(source, freq_mhz, enable) - для АЦП
(+) Частота АЦП не должна превышать 12.5 МГц согласно datasheet
==============================================================================
##### Особенности работы #####
==============================================================================
- Выбор SYSCLK_TickType влияет на:
- Точность micros() (при 1ms тике micros() дает значения с шагом 1000)
- Нагрузку на ЦП (1us = 1М прерываний в секунду, 100ms = 10 прерываний в секунду)
- Минимальный период коллбеков (не может быть меньше SYSCLK_TickType)
- При настройке PLL:
- Драйвер автоматически подбирает делители для заданной частоты
- Если частота недостижима - вызовется Error_Handler()
- Всегда проверяйте supported frequencies в datasheet
- Коллбеки выполняются в контексте прерывания SysTick:
- Не должны выполняться долго
- Не должны вызывать блокирующие функции
@endverbatim
******************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
#include "periph_config.h"
/* Private variables ---------------------------------------------------------*/
/** @brief Счетчик миллисекунд */
__IO uint32_t msTick = 0;
/** @brief Счетчик микросекунд */
__IO uint32_t usTick = 0;
/** @brief Инкремент для миллисекунд */
static __IO uint32_t msTickInc = 0;
/** @brief Инкремент для микросекунд */
static __IO uint32_t usTickInc = 0;
/** @brief Обработчик системных коллбеков */
static SYSCLK_CallbackHandleTypeDef hsyscb = {0};
//-- Defines -------------------------------------------------------------------
//-- Private function prototypes -----------------------------------------------
static inline void millis_inc(void);
static inline void micros_inc(void);
//-- Peripheral init functions -------------------------------------------------
/**
* @brief Инициализация системного тактирования.
* @details Настраивает PLL, SysTick и счетчики времени.
* Должна быть вызвана первой в функции main().
*/
void sysclk_init(void)
{
OperationStatus status;
status = RCU_PLL_AutoConfig(SYSCLK_CORE_CLOCK_MHZ*__MHZ, SYSCLK_Oscil_Type);
if (status == ERROR)
{
Error_Handler();
}
SystemCoreClockUpdate();
// RCU_ClkOutConfig(RCU_SysPeriphClk_PLLClk, 1, ENABLE);
// RCU_ClkOutCmd(ENABLE);
SysTick_Config(SYSCLK_CORE_CLOCK_MHZ*__MHZ/SYSCLK_TickType);
switch(SYSCLK_TickType)
{
case SYSCLK_Tick_1us:
usTickInc = 1;
msTickInc = 1;
break;
case SYSCLK_Tick_10us:
usTickInc = 10;
msTickInc = 1;
break;
case SYSCLK_Tick_100us:
usTickInc = 100;
msTickInc = 1;
break;
case SYSCLK_Tick_1ms:
usTickInc = 1000;
msTickInc = 1;
break;
case SYSCLK_Tick_10ms:
usTickInc = 10000;
msTickInc = 10;
break;
case SYSCLK_Tick_100ms:
usTickInc = 100000;
msTickInc = 100;
break;
default:
/* Должен быть определен в periph_config.h */
Error_Handler();
break;
}
}
/**
* @brief Общий обработчик прерываний SysClock
* @details Обрабатывает мс и мкс и вызывает коллбеки
*/
void sysclk_irq_handler(void)
{
static uint32_t usAccumulator = 0; // Накопитель мкс
/* Инкремент микросекунд */
micros_inc();
if(msTickInc == 1)
{
/* Аккумулятивный метод для миллисекунд (без деления) */
usAccumulator += usTickInc;
if (usAccumulator >= 1000) {
millis_inc();
usAccumulator -= 1000; // Вычитание быстрее деления
}
}
else
{
millis_inc();
}
/* Вызов зарегистрированных коллбеков */
for(int i = 0; i < hsyscb.CallbackInUse; i++)
{
if(hsyscb.Callback[i] != NULL)
{
/* Проверка истекшего времени */
uint32_t elapsed = msTick - hsyscb.CallbackPrevMs[i];
if(elapsed >= hsyscb.CallbackPeriod[i])
{
/* Обновление времени последнего вызова и вызов коллбека */
hsyscb.CallbackPrevMs[i] = msTick;
hsyscb.Callback[i]();
}
}
}
}
/**
* @brief Добавление периодического коллбека.
* @param Callback Указатель на функцию-коллбек
* @param PeriodInMs Период вызова коллбека в миллисекундах
* @retval OperationStatus OK при успехе, ERROR при ошибке
* @note Максимальное количество коллбеков: SYSCLK_NUMB_OF_CUSTOM_CALLBACKS
* @note Минимальный период: текущий период тика SysTick
*/
OperationStatus SYSCLK_Set_Callback(void (*Callback)(void), uint32_t PeriodInMs)
{
/* Проверка валидности указателя на функцию */
if(Callback == NULL)
return ERROR;
/* Проверка минимального периода */
if(PeriodInMs < msTickInc)
return ERROR;
/* Проверка доступности свободных слотов */
if(hsyscb.CallbackInUse >= SYSCLK_NUMB_OF_CUSTOM_CALLBACKS)
return ERROR;
/* Регистрация коллбека */
hsyscb.Callback[hsyscb.CallbackInUse] = Callback;
hsyscb.CallbackPeriod[hsyscb.CallbackInUse] = PeriodInMs;
hsyscb.CallbackPrevMs[hsyscb.CallbackInUse] = msTick;
hsyscb.CallbackInUse++;
return OK;
}
/**
* @brief Получение текущего времени в миллисекундах.
* @retval uint32_t Текущее время в миллисекундах
* @note Переполнение происходит через ~49 дней
*/
uint32_t millis(void)
{
return msTick;
}
/**
* @brief Получение текущего времени в микросекундах.
* @retval uint32_t Текущее время в микросекундах
* @note Переполнение происходит через ~71 минуту
*/
uint32_t micros(void)
{
return usTick;
}
/**
* @brief Настройка тактирования АЦП.
* @param ClkSrc Источник тактирования (RCU_PeriphClk_TypeDef)
* @param ClkMHz Желаемая частота АЦП в МГц
* @param state Состояние (ENABLE/DISABLE)
* @retval OperationStatus OK при успехе, ERROR при ошибке
* @note Временное отключает тактирование АЦП во время настройки
*/
OperationStatus rcu_set_clock_adc(RCU_PeriphClk_TypeDef ClkSrc, float ClkMHz, FunctionalState state)
{
uint32_t adc_raw_clock = 0;
float adc_clock_div = 0;
/* Отключение тактирования АЦП для настройки */
RCU_ADCClkCmd(DISABLE);
/* Определение частоты источника тактирования */
switch(ClkSrc)
{
case RCU_PeriphClk_OSEClk:
adc_raw_clock = RCU_GetOSEClkFreq();
break;
case RCU_PeriphClk_OSIClk:
adc_raw_clock = RCU_GetOSIClkFreq();
break;
case RCU_PeriphClk_PLLClk:
adc_raw_clock = RCU_GetPLLClkFreq();
break;
case RCU_PeriphClk_PLLDivClk:
adc_raw_clock = RCU_GetPLLDivClkFreq();
break;
default:
return ERROR;
}
/* Расчет делителя частоты */
adc_clock_div = adc_raw_clock / (ClkMHz * __MHZ);
if(adc_clock_div < 1)
return ERROR;
/* Настройка источника тактирования и делителя */
RCU_ADCClkConfig(ClkSrc, (uint32_t)(adc_clock_div - 1), ENABLE);
/* Включение тактирования, если запрошено */
if(state == ENABLE)
{
RCU_ADCClkCmd(ENABLE);
}
return OK;
}
/**
* @brief Инкремент счетчика миллисекунд.
*/
static inline void millis_inc(void)
{
msTick+=msTickInc;
}
/**
* @brief Инкремент счетчика микросекунд.
*/
static inline void micros_inc(void)
{
usTick+=usTickInc;
}