Проект перенесен в папку 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

588
MDK-ARM/Core/App/adc.c Normal file
View File

@@ -0,0 +1,588 @@
/**
******************************************************************************
* @file adc.c
* @author Разваляев Алексей
* @brief Драйвер ADC на основе PLIB035.
* Этот файл содержит:
* + Инициализацию ADC и аналогового модуля (AM)
* + Управление секвенсорами (SEQ0, SEQ1):
* - Инициализацию и конфигурацию секвенсоров
* - Запуск и остановку преобразований с буфером
* - Обработку прерываний секвенсоров
* - Установку callback-функций для событий:
* - завершение секвенсора
* - половина буфера
* - полный буфера
* - ошибка
* + Управление цифровыми компараторами (DC0-DC3):
* - Инициализацию и конфигурацию компараторов
* - Запуск и остановку компараторов
* - Обработку прерываний компараторов
* - Установку callback-функций для событий:
* - срабатывание компаратора
* - ошибка
* + Функции для работы с каналами ADC
* + Программный запуск преобразований
*
******************************************************************************
* @attention
*
* Использование этого драйвера предполагает наличие корректных настроек:
* - Определены конфигурационные структуры adc_seqx_config и adc_dcx_config
* в periph_config.h
* - Настроено тактирование ADC через RCU
* - Для использования прерываний - включены соответствующие NVIC IRQ
*
******************************************************************************
* @verbatim
==============================================================================
##### Как использовать этот драйвер #####
==============================================================================
-------------------------- Общие функции АЦП --------------------------------
1. Инициализация АЦП:
(+) adc_init_first() - обязательный вызов перед использованием АЦП
(+) Настраивает тактирование, GPIO и ожидает готовности аналогового модуля
2. Калибровка каналов:
(+) ADC_Channel_Calibr(&hadc, channel, OFFSET, GAIN) - калибровка смещения и усиления
(+) Формула калибровки: Dc = Dr * (4096+GAIN)/4096 + OFFSET
3. Чтение значений каналов:
(+) ADC_Channel_GetValue(&hadc, channel) - получение текущего значения канала
(+) Данные обновляются автоматически при работе секвенсоров
4. Инициализация GPIO:
(+) adc_gpio_init(hadc.ChannelEnable) - настраивает пины выбранных каналов
(+) Вызывается автоматически в adc_init_first()
------------------------ Секвенсоры (SEQ0, SEQ1) ----------------------------
1. Настройка в periph_config.h:
(+) Определите adc_seq0_config, adc_seq1_config
(+) Настройте ADC_ClockSource и ADC_ClockMHz для тактирования АЦП
(+) Определите adc_ch_config для включения нужных каналов
2. Инициализация секвенсоров:
(+) adc_seq_init(&hadc, ADC_SEQ_Num_0, &adc_seq0_config) - инициализация SEQ0
(+) Аналогично для SEQ1 при необходимости
3. Работа с секвенсорами:
(+) ADC_SEQ_Start(&hadc, ADC_SEQ_Num_0, buffer, buffer_size) - запуск с буфером
(+) ADC_SEQ_Stop(&hadc, ADC_SEQ_Num_0) - остановка секвенсора
(+) ADC_SEQ_Set_Callback(&hadc, ADC_SEQ_Num_0, тип, func) - установка обработчика
(+) ADC_SEQ_SoftwareStart() - программный запуск преобразования
4. Обработка прерываний секвенсоров:
(+) ADC_SEQ0_IRQHandler - вызвать adc_seq_irq_handler(&hadc, ADC_SEQ_Num_0)
(+) ADC_SEQ1_IRQHandler - вызвать adc_seq_irq_handler(&hadc, ADC_SEQ_Num_1)
------------------------ Цифровые компараторы (DC) --------------------------
==============================================================================
##### Особенности работы #####
==============================================================================
-------------------------- Общие особенности --------------------------------
- Частота ADC:
- Задается через ADC_ClockMHz в periph_config.h
- Автоматически настраивается делитель от источника тактирования
- Аналоговый модуль (AM):
- Требует времени для старта после включения (до 1 мс)
- adc_init_first() ожидает готовности AM с таймаутом 1 секунда
- При неудаче вызывает Error_Handler()
- Калибровка:
- OFFSET: смещение в диапазоне [-255, 255] квантов АЦП
- GAIN: коэффициент усиления в диапазоне [-255, 255] квантов АЦП (4096+GAIN)
- Калибровка применяется аппаратно для каждого канала отдельно
------------------------ Секвенсоры (SEQ0, SEQ1) ----------------------------
- Буферизация данных:
- Данные в буфере хранятся в формате [канал][время]
- При buffer_size=100 для 2 каналов буфер будет 200 элементов
- Буфер может быть кольцевым (BufferCircular=ENABLE)
- Прерывания секвенсоров:
- Генерируются после заданного количества рестартов (ITCount)
- ITCount=0 означает прерывание после каждого прохода секвенсора
- Данные автоматически читаются из FIFO в прерывании
- Работа с усреднением:
- Аппаратное усреднение измерения:
- 2, 4, 8, 16, 32 или 64 выборок (SEQ_Init.ReqAverage)
- Включается через SEQ_Init.ReqAverageEn
- Усредненные значения помещаются в FIFO как единый результат
- Аппаратное усреднение рестартов
- 2, 4, 8, 16, 32, 64, 128, 256 выборок (SER_Init.RestartCount + 1)
- Включается через SEQ_Init.RestartAverageEn
- Усредненные значения помещаются в FIFO как единый результат
- Таймер рестартов:
- Период в тиках ADC_ClockMHz/2 (драйвер автоматически делит на 2)
- Используется для периодического запуска секвенсора
- При RestartTimer=0 рестарт происходит немедленно
- Чтение FIFO:
- FIFO буфер на 32 элемента автоматически читается в прерывании
- Данные распределяются по каналам согласно последовательности в Req[0..3]
- При переполнении FIFO (>32 элементов до прерывания) возможна потеря данных
------------------------ Цифровые компараторы (DC) --------------------------
@endverbatim
******************************************************************************
*/
//-- Includes ------------------------------------------------------------------
#include "periph_config.h"
ADC_HandleTypeDef hadc; /*!< Хендл ADC */
//-- Private function prototypes -----------------------------------------------
static void __adc_seq_fifo_read(ADC_HandleTypeDef *hadc, ADC_SEQ_Num_TypeDef SEQ_Num);
static int __adc_seq_calc_fifo_load(ADC_HandleTypeDef *hadc, ADC_SEQ_Num_TypeDef SEQ_Num);
//-- Defines -------------------------------------------------------------------
//-- ADC Init functions --------------------------------------------------------
/**
* @brief Первичная инициализация ADC
* @details Настройка ADC и хендла: тактирования, прерывания
* и секвенсоры с компараторами
*/
void adc_init_first(void)
{
#if (USE_ADC_SEQ0==1) || (USE_ADC_SEQ1==1) || (USE_ADC_DC0==1) || (USE_ADC_DC1==1) || (USE_ADC_DC2==1) || (USE_ADC_DC3==1)
// Настройка тактирования
if(rcu_set_clock_adc(ADC_ClockSource, ADC_ClockMHz, ENABLE) != OK)
{
Error_Handler();
}
RCU_ADCRstCmd(ENABLE);
// Включаем аналоговый модуль
ADC_AM_Cmd(ENABLE);
// Настройка пинов для ADC
hadc.ChannelEnable = &adc_ch_config;
adc_gpio_init(hadc.ChannelEnable);
hadc.Instance = ADC;
#endif
#if (USE_ADC_SEQ0==1)
adc_seq_init(&hadc, ADC_SEQ_Num_0, &adc_seq0_config);
if(hadc.SEQ[ADC_SEQ_Num_0].Config->IT == ENABLE)
{
NVIC_EnableIRQ(ADC_SEQ0_IRQn);
}
#endif
#if (USE_ADC_SEQ1==1)
adc_seq_init(&hadc, ADC_SEQ_Num_1, &adc_seq1_config);
if(hadc.SEQ[ADC_SEQ_Num_1].Config->IT == ENABLE)
{
NVIC_EnableIRQ(ADC_SEQ1_IRQn);
}
#endif
#if (USE_ADC_SEQ0==1) || (USE_ADC_SEQ1==1) || (USE_ADC_DC0==1) || (USE_ADC_DC1==1) || (USE_ADC_DC2==1) || (USE_ADC_DC3==1)
uint32_t starttick = millis();
while (!ADC_AM_ReadyStatus()) {
if((millis() - starttick) > 1000)
Error_Handler();
}
#endif
}
/**
* @brief Записать калибровочные значения канала
* @param hadc указатель на хендл АЦП
* @param channel номер канала
* @param OFFSET Коэффициент корректировки смещения нуля АЦП в квантах [-255:255]
* @param GAIN Коэффициент корректировки усиления зн АЦП в квантах [-255:255]
* @retval значение ADC (0 если данные невалидны)
* @details Результат преобразования передается на схему коррекции, которая нивелирует
* ошибку усиления и смещения нуля и работа которой описывается формулой
* Dc = Dr * (4096+GAIN)/4096 + OFFSET
*/
OperationStatus ADC_Channel_Calibr(ADC_HandleTypeDef *hadc, ADC_CH_Num_TypeDef channel, int OFFSET, int GAIN)
{
if (!hadc || (int)channel >= ADC_CH_Total)
return ERROR;
ADC_CH_SetOffsetTrim(channel, OFFSET);
ADC_CH_SetGainTrim(channel, GAIN);
return OK;
}
/**
* @brief Получение текущего значения канала
* @param hadc указатель на хендл АЦП
* @param channel номер канала
* @retval значение ADC (0 если данные невалидны)
*/
uint16_t ADC_Channel_GetValue(ADC_HandleTypeDef *hadc, ADC_CH_Num_TypeDef channel)
{
if (!hadc || (int)channel >= ADC_CH_Total)
return 0;
return hadc->ChannelData[channel];
}
//-- ADC Sequencers API functions ----------------------------------------------
/**
* @brief Инициализация секвенсора АЦП
* @param hadc указатель на хендл АЦП
* @param SEQ_Num номер секвенсора
* @param NewConfig указатель на новую конфигурацию ADC, иначе используется та, что в структуре
* @retval OperationStatus OK - если всё хорошо, ERROR - если ошибка
*/
OperationStatus adc_seq_init(ADC_HandleTypeDef *hadc, ADC_SEQ_Num_TypeDef SEQ_Num, ADC_SEQ_ExtInit_TypeDef *NewConfig)
{
if(!hadc || !hadc->Instance)
return ERROR;
ADC_SEQ_HandleTypeDef *hseq = &hadc->SEQ[SEQ_Num];
if(NewConfig != NULL)
{
hseq->Config = NewConfig;
}
if(hseq->Config == NULL)
{
return ERROR;
}
ADC_SEQ_ExtInit_TypeDef *conf = hseq->Config;
if(conf->SEQ_Init.RestartTimer) // не значю почему но этот таймер работает в 2 раза медленее чем AdcClk
{
conf->SEQ_Init.RestartTimer = (conf->SEQ_Init.RestartTimer+1)/2-1; // поэтому дополнительно делим всё на два чтобы работало
}
if(__adc_seq_calc_fifo_load(hadc, SEQ_Num) < 0)
return ERROR;
ADC_SEQ_Init(SEQ_Num, &hseq->Config->SEQ_Init);
return OK;
}
/**
* @brief Установка коллбека секвенсора АЦП
* @param hadc указатель на хендл АЦП
* @param CallbackType Тип коллбека
* @param Callback Функция коллбека
* @retval void
*/
OperationStatus ADC_SEQ_Set_Callback(ADC_HandleTypeDef* hadc, ADC_SEQ_Num_TypeDef SEQ_Num, ADC_CallbackTypeDef CallbackType, void (*Callback)(void))
{
if (!hadc || !hadc->Instance || !hadc->SEQ[SEQ_Num].Config)
return ERROR;
ADC_SEQ_HandleTypeDef *hseq = &hadc->SEQ[SEQ_Num];
switch(CallbackType)
{
case ADC_Callback_SeqCplt:
hseq->Config->SEQCpltCallback = Callback;
break;
case ADC_Callback_Error:
hseq->Config->ErrorCallback = Callback;
break;
case ADC_Callback_BuffFull:
hseq->Config->BuffFullCallback = Callback;
break;
case ADC_Callback_BuffHalf:
hseq->Config->BuffFullCallback = Callback;
break;
default:
return ERROR;
}
return OK;
}
/**
* @brief Запуск секвенсора АЦП с буфером
* @param hadc указатель на хендл АЦП
* @param SEQ_Num номер секвенсора
* @param data_buffer указатель на буфер данных [ch][buffer_size] или NULL
* @param buffer_size размер буфера для каждого канала (0 если буфер не используется)
* @retval OperationStatus OK - если всё хорошо, ERROR - если ошибка
*/
OperationStatus ADC_SEQ_Start(ADC_HandleTypeDef *hadc,
ADC_SEQ_Num_TypeDef SEQ_Num,
uint16_t (*data_buffer)[],
uint32_t buffer_size)
{
if (!hadc || !hadc->Instance || !hadc->SEQ[SEQ_Num].Config)
return ERROR;
ADC_SEQ_HandleTypeDef *hseq = &hadc->SEQ[SEQ_Num];
ADC_SEQ_ExtInit_TypeDef *conf = hseq->Config;
// Сохраняем буферные параметры
hseq->data_buffer = (uint16_t *)data_buffer;
hseq->buffer_size = buffer_size;
hseq->buffer_count = 0; // Сбрасываем счетчик
// DMA > IT
if(conf->SEQ_Init.DMAEn == ENABLE)
{
conf->IT = DISABLE;
}
// Настраиваем прерывания если нужно
if (conf->IT == ENABLE)
{
uint32_t it_real_cnt = (uint32_t)(conf->ITCount+1)*(conf->SEQ_Init.ReqMax+1);
ADC_SEQ_ITConfig(SEQ_Num, it_real_cnt-1, DISABLE);
ADC_SEQ_ITCmd(SEQ_Num, ENABLE);
}
// Включаем секвенсор
ADC_SEQ_Cmd(SEQ_Num, ENABLE);
return OK;
}
/**
* @brief Остановка секвенсора АЦП
* @param hadc указатель на хендл АЦП
* @param SEQ_Num номер секвенсора
* @retval OperationStatus OK - если всё хорошо, ERROR - если ошибка
*/
OperationStatus ADC_SEQ_Stop(ADC_HandleTypeDef *hadc, ADC_SEQ_Num_TypeDef SEQ_Num)
{
if (!hadc || !hadc->Instance || !hadc->SEQ[SEQ_Num].Config)
return ERROR;
// ADC_SEQ_HandleTypeDef *hseq = &hadc->SEQ[SEQ_Num];
// Выключаем секвенсор
ADC_SEQ_Cmd(SEQ_Num, DISABLE);
// Выключаем прерывания
ADC_SEQ_ITCmd(SEQ_Num, DISABLE);
return OK;
}
/**
* @brief Программный запуск преобразования
*/
void ADC_SEQ_SoftwareStart(void)
{
ADC_SEQ_SwStartCmd();
}
/**
* @brief Обработчик прерываний секвенсора
* @param hadc указатель на хендл АЦП
* @param SEQ_Num номер секвенсора
* @retval void
*/
void adc_seq_irq_handler(ADC_HandleTypeDef *hadc, ADC_SEQ_Num_TypeDef SEQ_Num)
{
if (!hadc || !hadc->Instance || !hadc->SEQ[SEQ_Num].Config)
return;
// GPIO_SetBits(GPIOA, GPIO_Pin_7);
ADC_SEQ_HandleTypeDef *hseq = &hadc->SEQ[SEQ_Num];
ADC_SEQ_ExtInit_TypeDef *conf = hseq->Config;
// Проверяем флаг прерывания
if (ADC_SEQ_ITMaskedStatus(SEQ_Num) == SET)
{
// Очищаем флаг
ADC_SEQ_ITStatusClear(SEQ_Num);
__adc_seq_fifo_read(hadc, SEQ_Num);
}
// Обработка ошибок
if (ADC_SEQ_DMAErrorStatus(SEQ_Num) == SET)
{
ADC_SEQ_DMAErrorStatusClear(SEQ_Num);
if (conf->ErrorCallback)
{
conf->ErrorCallback();
}
}
// GPIO_ClearBits(GPIOA, GPIO_Pin_7);
}
/**
* @brief Внутренняя функция для расчета загрузки FIFO буфера
* @param hadc указатель на хендл АЦП
* @param SEQ_Num номер секвенсора
* @retval Кол-во данных FIFO буфера, или -1 если переполнение FIFO
* @details Рассчитывает сколько данных накопится в fifo буфере за одно прерывание
*/
static int __adc_seq_calc_fifo_load(ADC_HandleTypeDef *hadc, ADC_SEQ_Num_TypeDef SEQ_Num)
{
ADC_SEQ_HandleTypeDef *hseq = &hadc->SEQ[SEQ_Num];
ADC_SEQ_ExtInit_TypeDef *conf = hseq->Config;
int numb_of_data = 0;
uint32_t numb_of_req = ((uint32_t)conf->SEQ_Init.ReqMax+1);
uint32_t it_cnt = ((uint32_t)conf->ITCount+1);
if(conf->SEQ_Init.RestartAverageEn)
{
numb_of_data = numb_of_req;
}
else
{
numb_of_data = it_cnt*numb_of_req;
if(numb_of_data > 32) // Максимальный размер FIFO - 32 элемента, поэтому если слишком многшо данных за время работы перед прерыванием - то ошибка
return -1;
}
return numb_of_data;
}
/**
* @brief Внутренняя функция для чтения данных из FIFO
* @param hadc указатель на хендл АЦП
* @param SEQ_Num номер секвенсора
* @retval void
* @details Автоматически достает данные из FIFO и складывает их в заданный буфер
и в структуру hadc
*/
static void __adc_seq_fifo_read(ADC_HandleTypeDef *hadc, ADC_SEQ_Num_TypeDef SEQ_Num)
{
ADC_SEQ_HandleTypeDef *hseq = &hadc->SEQ[SEQ_Num];
_ADC_SEQ_TypeDef *SEQx = &hadc->Instance->SEQ[SEQ_Num];
ADC_SEQ_ExtInit_TypeDef *conf = hseq->Config;
// Последний запрос в секвенсоре
uint32_t last_request = SEQx->SRQCTL_bit.RQMAX;
uint32_t numb_of_request = last_request+1;
// Читаем все доступные данные из FIFO
uint32_t fifo_size = ADC_SEQ_GetFIFOLoad(SEQ_Num);
uint32_t req_num = ((int)ADC_SEQ_GetReqCurrent(SEQ_Num)-fifo_size)%(numb_of_request); // первая дата в fifo будет (следующий запрос - количество данных в буфере)
while (ADC_SEQ_GetFIFOLoad(SEQ_Num))
{
uint32_t data = ADC_SEQ_GetFIFOData(SEQ_Num);
if(ADC_SEQ_GetFIFOLoad(SEQ_Num) == 0)
{
__NOP();
}
if ((int)req_num < ADC_SEQ_Req_Total)
{
ADC_CH_Num_TypeDef channel = conf->SEQ_Init.Req[req_num];
if ((int)channel < ADC_CH_Total)
{
// 1. Обновляем текущее значение
hadc->ChannelData[channel] = (uint16_t)data;
// 2. Записываем в буфер если он есть
if (hseq->data_buffer &&
hseq->buffer_size > 0)
{
// Вычисляем offset для [ch][buffer_size]
uint32_t offset = req_num * hseq->buffer_size +
hseq->buffer_count;
hseq->data_buffer[offset] = (uint16_t)data;
}
}
// Если это последний канал в секвенсоре - увеличиваем счетчик буфера
if (req_num == last_request)
{
hseq->buffer_count++;
if(hseq->buffer_count == hseq->buffer_size/2)
{
if (conf->BuffHalfCallback)
{
conf->BuffHalfCallback();
}
}
if(hseq->buffer_count >= hseq->buffer_size)
{
// Кольцевой буфер
if(conf->BufferCircular)
hseq->buffer_count = 0;
else
ADC_SEQ_Stop(hadc, SEQ_Num);
if (conf->BuffFullCallback)
{
conf->BuffFullCallback();
}
}
}
}
// Переход на Следующий канал
req_num = (req_num+1)%numb_of_request;
}
// Вызываем коллбек пользователя
if (conf->SEQCpltCallback)
{
conf->SEQCpltCallback();
}
}
//-- ADC Digital Comparators API functions -------------------------------------
//-- ADC GPIO functions --------------------------------------------------------
/**
* @brief Инициализация GPIO для ADC
*/
void adc_gpio_init(ADC_ChannelEnableTypeDef *ChEn)
{
#if (USE_ADC_SEQ0==1) || (USE_ADC_SEQ1==1) || (USE_ADC_DC0==1) || (USE_ADC_DC1==1) || (USE_ADC_DC2==1) || (USE_ADC_DC3==1)
// Получаем структуру
GPIO_Init_TypeDef *ch0_config = gpio_get_init(ADC_Ch0_GPIO_Port, ADC_Ch0_Pin);
GPIO_Init_TypeDef *ch1_config = gpio_get_init(ADC_Ch1_GPIO_Port, ADC_Ch1_Pin);
GPIO_Init_TypeDef *ch2_config = gpio_get_init(ADC_Ch2_GPIO_Port, ADC_Ch2_Pin);
GPIO_Init_TypeDef *ch3_config = gpio_get_init(ADC_Ch3_GPIO_Port, ADC_Ch3_Pin);
if(ChEn->Ch0)
{
GPIO_StructInit(ch0_config);
ch0_config->Pin = ADC_Ch0_Pin;
GPIO_Init(ADC_Ch0_GPIO_Port, ch0_config);
}
if(ChEn->Ch1)
{
GPIO_StructInit(ch1_config);
ch1_config->Pin = ADC_Ch1_Pin;
GPIO_Init(ADC_Ch1_GPIO_Port, ch1_config);
}
if(ChEn->Ch2)
{
GPIO_StructInit(ch2_config);
ch2_config->Pin = ADC_Ch2_Pin;
GPIO_Init(ADC_Ch2_GPIO_Port, ch2_config);
}
if(ChEn->Ch3)
{
GPIO_StructInit(ch3_config);
ch3_config->Pin = ADC_Ch3_Pin;
GPIO_Init(ADC_Ch3_GPIO_Port, ch3_config);
}
#endif
}

162
MDK-ARM/Core/App/adc.h Normal file
View File

@@ -0,0 +1,162 @@
/**
******************************************************************************
* @file adc.h
* @author Разваляев Алексей
* @brief Драйвер ADC на основе PLIB035.
* Данный файл содержит определения типов, структур и прототипы функций
* для работы с ADC, включая:
* + Структуры и typedef для ADC
* + Прототипы функций для инициализации и API драйвера
*
******************************************************************************
*/
#ifndef __ADC_H
#define __ADC_H
//-- Includes ------------------------------------------------------------------
#include "plib035.h"
#include "retarget_conf.h"
//-- Defines -------------------------------------------------------------------
// Дефайны для пинов ADC
#define ADC_Ch0_Pin GPIO_Pin_0 /**< PB0 — ADC Channel 0 */
#define ADC_Ch0_GPIO_Port GPIOB /**< GPIO порт ADC Channel 0 */
#define ADC_Ch1_Pin GPIO_Pin_1 /**< PB1 — ADC Channel 1 */
#define ADC_Ch1_GPIO_Port GPIOB /**< GPIO порт ADC Channel 1 */
#define ADC_Ch2_Pin GPIO_Pin_2 /**< PB2 — ADC Channel 2 */
#define ADC_Ch2_GPIO_Port GPIOB /**< GPIO порт ADC Channel 2 */
#define ADC_Ch3_Pin GPIO_Pin_3 /**< PB3 — ADC Channel 3 */
#define ADC_Ch3_GPIO_Port GPIOB /**< GPIO порт ADC Channel 3 */
//-- Types ---------------------------------------------------------------------
/**
* @brief Типы callback-функций ADC
*/
typedef enum
{
ADC_Callback_SeqCplt, /*!< Преобразование секвенсора завершено */
ADC_Callback_BuffHalf, /*!< Буфер заполнен наполовину */
ADC_Callback_BuffFull, /*!< Буфер заполнен полностью */
ADC_Callback_DCTrig, /*!< Компаратор сработал */
ADC_Callback_Error, /*!< Ошибка */
}ADC_CallbackTypeDef;
/**
* @brief Типы callback-функций ADC
*/
typedef struct
{
unsigned Ch0:1; /*!< АЦП Канал 0 */
unsigned Ch1:1; /*!< АЦП Канал 1 */
unsigned Ch2:1; /*!< АЦП Канал 2 */
unsigned Ch3:1; /*!< АЦП Канал 3 */
}ADC_ChannelEnableTypeDef;
/**
* @brief Расширенная конфигурация секвенсора ADC
*/
typedef struct
{
ADC_SEQ_Init_TypeDef SEQ_Init; /* Стандартная конфигурация из PLIB */
FunctionalState IT; /*!< Включить прерывания */
uint8_t ITCount; /*!< Количество запросов для генерации прерывания */
FunctionalState BufferCircular; /*!< Циклический буфер */
void (*SEQCpltCallback)(void); /*!< Вызывается при завершении секвенсора */
void (*BuffHalfCallback)(void); /*!< Вызывается при заполнении половины буфера */
void (*BuffFullCallback)(void); /*!< Вызывается при заполнении буфера */
void (*ErrorCallback)(); /*!< Вызывается при ошибке */
}ADC_SEQ_ExtInit_TypeDef;
/**
* @brief Расширенная конфигурация компаратора ADC
*/
typedef struct
{
ADC_DC_Init_TypeDef DC_Init;
FunctionalState IT; /*!< Включить прерывания */
void (*DC_TrigCallback); /*!< Вызывается при срабатывании компаратора */
void (*ErrorCallback)(); /*!< Вызывается при ошибке */
}ADC_DC_ExtInit_TypeDef;
/**
* @brief Хендл секвенсора ADC
*/
typedef struct
{
ADC_SEQ_ExtInit_TypeDef *Config; /*!< Конфигурации секвенсоров */
uint16_t *data_buffer; /*!< Указатель на буфер данных [ch][buffer_size]*/
uint32_t buffer_size; /*!< Размер буфера (16-bit слова) */
uint32_t buffer_count; /*!< Счетчик текущего индекса буфера */
}ADC_SEQ_HandleTypeDef;
/**
* @brief Хендл компаратора ADC
*/
typedef struct
{
ADC_DC_ExtInit_TypeDef *Config; /*!< Конфигурации компараторов */
}ADC_DC_HandleTypeDef;
/**
* @brief Хендл ADC
*/
typedef struct
{
ADC_TypeDef *Instance; /*!< Регистры ADC */
ADC_SEQ_HandleTypeDef SEQ[ADC_SEQ_Total]; /*!< Хендл секвенсоров */
ADC_DC_ExtInit_TypeDef DC[ADC_DC_Total]; /*!< Хендл компараторов */
/* ===== Channels ===== */
ADC_ChannelEnableTypeDef *ChannelEnable; /*!< Какие каналы АЦП инициализировать (GPIO Pin) */
uint16_t ChannelData[ADC_CH_Total]; /*!< Текущие значения каналов */
}ADC_HandleTypeDef;
//-- External handles ----------------------------------------------------------
extern ADC_HandleTypeDef hadc;
//-- Exported functions prototypes ---------------------------------------------
/* Первичная инициализация ADC */
void adc_init_first(void);
void adc_gpio_init(ADC_ChannelEnableTypeDef *ChEn);
/* ADC API functions */
/* Записать калибровочные значения канала */
OperationStatus ADC_Channel_Calibr(ADC_HandleTypeDef *hadc, ADC_CH_Num_TypeDef channel, int OFFSET, int GAIN);
/* Получение текущего значения канала */
uint16_t ADC_Channel_GetValue(ADC_HandleTypeDef *hadc, ADC_CH_Num_TypeDef channel);
/* Sequencer Init functions*/
/* Инициализация секвенсора АЦП */
OperationStatus adc_seq_init(ADC_HandleTypeDef *hadc, ADC_SEQ_Num_TypeDef SEQ_Num, ADC_SEQ_ExtInit_TypeDef *NewConfig);
/* Обработчик прерываний секвенсора */
void adc_seq_irq_handler(ADC_HandleTypeDef *hadc, ADC_SEQ_Num_TypeDef SEQ_Num);
/* Sequencer API functions */
/* Установка коллбека секвенсора АЦП */
OperationStatus ADC_SEQ_Set_Callback(ADC_HandleTypeDef* hadc, ADC_SEQ_Num_TypeDef SEQ_Num, ADC_CallbackTypeDef CallbackType, void (*Callback)(void));
/* Запуск секвенсора АЦП с буфером */
OperationStatus ADC_SEQ_Start(ADC_HandleTypeDef *hadc, ADC_SEQ_Num_TypeDef SEQ_Num, uint16_t (*data_buffer)[], uint32_t buffer_size);
/* Остановка секвенсора АЦП */
OperationStatus ADC_SEQ_Stop(ADC_HandleTypeDef *hadc, ADC_SEQ_Num_TypeDef SEQ_Num);
/* Программный запуск преобразования секвенсора */
void ADC_SEQ_SoftwareStart(void);
/* Comparator API functions*/
void adc_dc_handler(ADC_HandleTypeDef *hadc, ADC_DC_Num_TypeDef DC_Num);
#endif /*__ADC_H*/

417
MDK-ARM/Core/App/gpio.c Normal file
View File

@@ -0,0 +1,417 @@
/**
******************************************************************************
* @file gpio.c
* @author Разваляев Алексей
* @brief Драйвер GPIO на основе PLIB035.
* Этот файл содержит:
* + Инициализацию портов GPIOA и GPIOB
* + Функции для работы со светодиодами:
* - Инициализацию структуры светодиода
* - Включение, выключение, переключение светодиода
* - Динамические режимы: моргание и плавное затухание
* + Функции для работы с кнопками:
* - Инициализацию структуры кнопки
* - Чтение состояния кнопки с защитой от дребезга
* + Утилитарные функции для получения конфигурации пинов
*
******************************************************************************
* @attention
*
* Использование этого драйвера предполагает наличие корректных настроек:
* - Определены массивы конфигурации gpioa_config и gpiob_config в periph_config.h
*
******************************************************************************
* @verbatim
==============================================================================
##### Как использовать этот драйвер #####
==============================================================================
1. Настройка периферии (periph_config.h):
(+) Определить массивы GPIO_Init_TypeDef для портов:
gpioa_config[32], gpiob_config[32]
(+) Настроить режим пинов: Input, Output, AltFunc или не используется (аналог режим)
2. Инициализация GPIO:
(+) gpio_init() — инициализация портов GPIOA и GPIOB
3. Работа со светодиодами:
(+) GPIO_LED_Init(&led, GPIOA, GPIO_PIN_5, SET) — инициализация светодиода
(+) GPIO_LED_On(&led) — включение светодиода
(+) GPIO_LED_Off(&led) — выключение светодиода
(+) GPIO_LED_Toggle(&led) — переключение светодиода
(+) GPIO_LED_Blink_Start(&led, 500) — запуск моргания (период 500 мс)
(+) GPIO_LED_Fading_Start(&led, 1000) — запуск плавного затухания (период 1 секунда)
(+) GPIO_LED_Dynamic_Handle(&led) — обработка динамических режимов в основном цикле
4. Работа с кнопками:
(+) GPIO_Switch_Init(&sw, GPIOB, GPIO_PIN_0, SET) — инициализация кнопки
(+) GPIO_Read_Switch(&sw) — чтение состояния кнопки с фильтрацией
5. Получение конфигурации пинов:
(+) gpio_get_init(порт, пин) - для периферии
==============================================================================
##### Особенности работы #####
==============================================================================
- Динамические режимы светодиодов:
- Моргание: простой on/off с заданным периодом
- Плавное затухание: квадратичное изменение яркости через ШИМ
- Требуют вызова GPIO_LED_Dynamic_Handle() в основном цикле
- Защита от дребезга:
- Фильтрация по времени (Sw_FilterDelay в миллисекундах)
- Неблокирующий алгоритм с отслеживанием состояния
- При изменении уровня запускается таймер, состояние обновляется после задержки
- ШИМ для плавного затухания:
- Программная генерация с 15 уровнями яркости (LED_PWM_TICKS)
- Полный цикл затухания-разтухания: 2 × LED_PWM_TICKS шагов
- Конфигурация пинов:
- Периферия может перезаписывать конфигурацию пинов (например, UART на AltFunc)
- gpio_get_init() позволяет периферии получить исходную конфигурацию
- После использования периферии можно восстановить исходные настройки
- Табличный подход:
- Каждый пин имеет свою строку в массиве конфигурации
- Позволяет централизованно управлять всеми пинами
- Упрощает отладку и модификацию конфигурации
@endverbatim
******************************************************************************
*/
//-- Includes ------------------------------------------------------------------
#include "periph_config.h"
//-- Defines -------------------------------------------------------------------
//-- GPIO init functions -------------------------------------------------------
void gpio_init(void)
{
RCU_AHBClkCmd(RCU_AHBClk_GPIOA, ENABLE);
RCU_AHBRstCmd(RCU_AHBRst_GPIOA, ENABLE);
RCU_AHBClkCmd(RCU_AHBClk_GPIOB, ENABLE);
RCU_AHBRstCmd(RCU_AHBRst_GPIOB, ENABLE);
/* Сброс пинов */
GPIO_DeInit(GPIOA);
GPIO_DeInit(GPIOB);
// Инициализация порта A
for (int i = 0; i < sizeof(gpioa_config) / sizeof(gpioa_config[0]); i++)
{
GPIO_Init(GPIOA, &gpioa_config[i]);
}
// Инициализация порта B
for (int i = 0; i < sizeof(gpiob_config) / sizeof(gpiob_config[0]); i++)
{
GPIO_Init(GPIOB, &gpiob_config[i]);
}
}
GPIO_Init_TypeDef *gpio_get_init(GPIO_TypeDef *GPIOx, uint32_t GPIO_Pin)
{
uint8_t pin_index = (31 - __CLZ(GPIO_Pin & -GPIO_Pin));
if (GPIOx == GPIOA)
{
// Проверяем границы массива
if (pin_index < sizeof(gpioa_config) / sizeof(gpioa_config[0]))
{
return &gpioa_config[pin_index];
}
}
else if (GPIOx == GPIOB)
{
// У тебя была опечатка: возвращал gpioa_config вместо gpiob_config
if (pin_index < sizeof(gpiob_config) / sizeof(gpiob_config[0]))
{
return &gpiob_config[pin_index];
}
}
return NULL;
}
//-- GPIO LED functions --------------------------------------------------------
/**
* @brief Инициализировать светодиод (структуру светодиода)
* @param led Указатель на структуру светодиода
* @param GPIOx Указатель на структуру порта для светодиода
* @param GPIO_PIN_X Пин для светодиода
* @param LED_ActiveLevel Состояния пина, при котором светодиод будет включен
*/
OperationStatus GPIO_LED_Init(GPIO_LEDTypeDef *led, GPIO_TypeDef *GPIOx, uint32_t GPIO_PIN_X, BitState LED_ActiveLevel)
{
if(!led || !GPIOx ||!GPIO_PIN_X)
return ERROR;
led->LED_Port = GPIOx;
led->LED_Pin = GPIO_PIN_X;
led->LED_ActiveLvl = LED_ActiveLevel;
GPIO_LED_Off(led);
return OK;
}
/**
* @brief Включить светодиод
* @param led Указатель на структуру светодиода
* @return Operation Status
*/
OperationStatus GPIO_LED_On(GPIO_LEDTypeDef *led)
{
if(!led || !led->LED_Port || !led->LED_Pin)
return ERROR;
led->state = LED_IS_ON;
GPIO_WriteBit(led->LED_Port, led->LED_Pin, led->LED_ActiveLvl);
return OK;
}
/**
* @brief Выключить светодиод
* @param led Указатель на структуру светодиода
* @return Operation Status
*/
OperationStatus GPIO_LED_Off(GPIO_LEDTypeDef *led)
{
if(!led || !led->LED_Port || !led->LED_Pin)
return ERROR;
led->state = LED_IS_OFF;
BitState offstate = (led->LED_ActiveLvl == SET)? CLEAR: SET;
GPIO_WriteBit(led->LED_Port, led->LED_Pin, offstate);
return OK;
}
/**
* @brief Переключить светодиод
* @param led Указатель на структуру светодиода
* @return Operation Status
*/
OperationStatus GPIO_LED_Toggle(GPIO_LEDTypeDef *led)
{
if(!led || !led->LED_Port || !led->LED_Pin)
return ERROR;
if(led->state == LED_IS_ON || led->state == LED_IS_OFF)
{
if(led->state == LED_IS_OFF)
led->state = LED_IS_ON;
else
led->state = LED_IS_OFF;
GPIO_ToggleBits(led->LED_Port, led->LED_Pin);
return OK;
}
else
return ERROR;
}
/**
* @brief Выставить светодиод по переменной
* @param led Указатель на структуру светодиода
* @param led_state Состояние светодиода
* @return Operation Status
*/
OperationStatus GPIO_LED_Set(GPIO_LEDTypeDef *led, uint8_t led_state)
{
if(!led || !led->LED_Port || !led->LED_Pin)
return ERROR;
if(led_state)
{
return GPIO_LED_On(led);
}
else
{
return GPIO_LED_Off(led);
}
}
/**
* @brief Активировать моргание светодиодом
* @param led Указатель на структуру светодиода
* @param period Период плавного моргания светодиода
* @return Operation Status
* @details Функция ставит режим моргания, который после управляется в @ref GPIO_LED_Dynamic_Handle
*/
OperationStatus GPIO_LED_Blink_Start(GPIO_LEDTypeDef *led, uint32_t period)
{
if(!led || !led->LED_Port || !led->LED_Pin)
return ERROR;
led->state = LED_IS_BLINKING;
led->LED_Period = period;
return OK;
}
/**
* @brief Активировать моргание светодиодом
* @param led Указатель на структуру светодиода
* @param period Период плавного моргания светодиода
* @return Operation Status
* @details Функция ставит режим моргания, который после управляется в @ref GPIO_LED_Dynamic_Handle
*/
OperationStatus GPIO_LED_Fading_Start(GPIO_LEDTypeDef *led, uint32_t period)
{
if(!led || !led->LED_Port || !led->LED_Pin)
return ERROR;
led->state = LED_IS_FADING;
led->LED_Period = period;
return OK;
}
//uint8_t LED_PWM_FADING_DUTYS[LED_PWM_TICKS] = {0 1 2 3 4 5 6 7 8 9 10 11 12 }
/**
* @brief Управление динамическими режимами свечения светодиода
* @param Указатель на структуру светодиода
* @details Функция моргает/плавно моргает светодиодом в неблокирующем режиме
* Т.е. функцию надо вызывать постоянно, чтобы она мониторила тики
* и в нужный момент переключала светодиод
*/
void GPIO_LED_Dynamic_Handle(GPIO_LEDTypeDef *led)
{
if(!led || !led->LED_Port || !led->LED_Pin)
return;
/* Режим моргания светодиода */
if(led->state == LED_IS_BLINKING)
{
uint32_t tickcurrent = millis();
/* Ожидание истечения периода моргания */
if((tickcurrent - led->tickprev) > led->LED_Period)
{
/* Моргание */
GPIO_ToggleBits(led->LED_Port, led->LED_Pin);
led->tickprev = tickcurrent;
}
}
/* Режим плавного моргания светодиода */
else if(led->state == LED_IS_FADING)
{
static unsigned direction = 0;
static int duty = 0;
uint32_t tickcurrent = millis();
/* Ожидание момента изменения яркости */
/* Период ШИМ 20 мс, поэтому менять яроксть надо 40 раз за период (туда обратно) */
if((tickcurrent - led->tickprev) > led->LED_Period/(LED_PWM_TICKS*2))
{
/* Формирование разтухания */
if(direction == 0)
{
if(++duty >= LED_PWM_TICKS)
{
direction = 1;
duty = LED_PWM_TICKS;
}
}
/* Формирование затухания */
else
{
if(--duty <= 0)
{
direction = 0;
duty = 0;
}
}
led->tickprev = tickcurrent;
}
/* Формирование ШИМ для изменения яркости */
int duty_crt = (duty*duty/LED_PWM_TICKS);
if(tickcurrent%LED_PWM_TICKS < duty_crt)
{
GPIO_WriteBit(led->LED_Port, led->LED_Pin, led->LED_ActiveLvl);
}
else
{
BitState offstate = (led->LED_ActiveLvl == SET)? CLEAR: SET;
GPIO_WriteBit(led->LED_Port, led->LED_Pin, offstate);
}
}
}
//-- GPIO Switch functions -----------------------------------------------------
/**
* @brief Инициализировать кнопку (структуру кнопки)
* @param sw Указатель на структуру кнопки
* @param GPIOx Указатель на структуру порта для кнопки
* @param GPIO_PIN_X Пин для кнопки
* @param SW_ActiveLevel Состояния пина, когда кнопка нажата
* @return Operation Status
*/
OperationStatus GPIO_Switch_Init(GPIO_SwitchTypeDef *sw, GPIO_TypeDef *GPIOx, uint32_t GPIO_PIN_X, BitState SW_ActiveLevel)
{
if(!sw || !GPIOx || !GPIO_PIN_X)
return ERROR;
sw->Sw_Port = GPIOx;
sw->Sw_Pin = GPIO_PIN_X;
sw->Sw_ActiveLvl = SW_ActiveLevel;
return OK;
}
/**
* @brief Считать состоянии кнопки
* @param sw Указатель на структуру кнопки
* @return 1 - если кнопка нажата,
* 0 - если отжата,
* -1 - если ошибка
* @details Функция включает в себя неблокирующую проверку на дребезг
* Т.е. функцию надо вызывать постоянно, чтобы она мониторила состояние кнопки
*/
int GPIO_Read_Switch(GPIO_SwitchTypeDef *sw)
{
if(!sw || !sw->Sw_Port || !sw->Sw_Pin)
return -1;
int current_level = (GPIO_ReadBit(sw->Sw_Port, sw->Sw_Pin) == sw->Sw_ActiveLvl);
if(sw->Sw_FilterDelay) // если включена защита от дребезга
{
// Если таймер не запущен и состояние изменилось - запускаем таймер
if(sw->tickprev == 0 && current_level != sw->Sw_CurrentState)
{
sw->tickprev = millis();
}
// Если таймер запущен
if(sw->tickprev != 0)
{
// Проверяем, прошел ли достаточный интервал для фильтрации
if((millis() - sw->tickprev) >= sw->Sw_FilterDelay)
{
// Обновляем состояние только если оно все еще отличается
if(current_level != sw->Sw_CurrentState)
{
sw->Sw_CurrentState = current_level;
}
// Останавливаем таймер в любом случае
sw->tickprev = 0;
}
}
}
else // если нет защиты от дребезга
{
sw->Sw_CurrentState = current_level;
}
return sw->Sw_CurrentState;
}

164
MDK-ARM/Core/App/gpio.h Normal file
View File

@@ -0,0 +1,164 @@
/**
******************************************************************************
* @file gpio.h
* @author Разваляев Алексей
* @brief Драйвер GPIO на основе PLIB035.
* Данный файл содержит определения типов, структур и прототипы функций
* для работы с UART, включая:
* + Структуры и typedef для работы с кнопками и светодиодами
* + Макросы для конфигурации режима пинов
* + Прототипы функций для инициализации и API драйвера
*
******************************************************************************
*/
#ifndef __GPIO_H
#define __GPIO_H
//-- Includes ------------------------------------------------------------------
#include "plib035.h"
#include "retarget_conf.h"
//-- Defines -------------------------------------------------------------------
// Дефайны для режима пина OutEnable, AltFuncEnable, DigitalEnable
#define GPIO_PinMode_Unused DISABLE, DISABLE, DISABLE
#define GPIO_PinMode_Input DISABLE, DISABLE, ENABLE
#define GPIO_PinMode_Output ENABLE, DISABLE, ENABLE
#define GPIO_PinMode_AltFunc DISABLE, ENABLE, ENABLE
#ifndef LED_PWM_TICKS
#define LED_PWM_TICKS 15 ///< Количество тиков в периоде ШИМ
#endif
//-- Types ---------------------------------------------------------------------
/**
* @brief Режимы работы светодиода
*/
typedef enum
{
LED_IS_OFF = 0, ///< Светодиод выключен
LED_IS_ON = 1, ///< Светодиод включен
LED_IS_BLINKING = 2, ///< Моргание светодиодом
LED_IS_FADING = 3, ///< Плавное моргание светодиодом
}GPIO_LEDStateTypeDef;
/**
* @brief Структура светодиода
*/
typedef struct
{
GPIO_LEDStateTypeDef state; ///< Текущий режим работы светодиода
GPIO_TypeDef *LED_Port; ///< GPIO порт ножки светодиода
uint32_t LED_Pin; ///< GPIO пин ножки светодиода
BitState LED_ActiveLvl; ///< Активный уровень ножки (при котором светодиод горит)
uint32_t LED_Period; ///< Период моргания светодиода
uint32_t tickprev;
}GPIO_LEDTypeDef;
/**
* @brief Структура кнопки
*/
typedef struct
{
GPIO_TypeDef *Sw_Port; ///< GPIO порт ножки кнопки
uint32_t Sw_Pin; ///< GPIO пин ножки кнопки
BitState Sw_ActiveLvl; ///< Активный уровень ножки (при котором кнопка нажата)
uint32_t Sw_CurrentState; ///< Текущее состояние кнопки
uint32_t Sw_FilterDelay; ///< Фильтр от дребезга (в мс)
uint32_t tickprev;
}GPIO_SwitchTypeDef;
//-- Exported functions prototypes ---------------------------------------------
/* Init functions */
void gpio_init(void);
GPIO_Init_TypeDef *gpio_get_init(GPIO_TypeDef *GPIOx, uint32_t GPIO_Pin);
/* API functions*/
/**
* @addtogroup GPIO_SWITCH Switch tools
* @brief Функции для работы с GPIO, как с кнопкой
* @par Пример использования:
@code
MX_GPIO_Init(); // инициализация пина аппаратная
// Инициализация кнопки на порте GPIOB, пин 0, активный уровень 1
GPIO_SwitchTypeDef sw1;
GPIO_Switch_Init(&sw1, GPIOB, GPIO_PIN_0, 1); // или дефайн SW_ON/SW_OFF
// Считываем состояние кнопки
if(GPIO_Read_Switch(&sw1))
{
// Кнопка нажата
LED_ON();
}
else
{
// Кнопка отжата
LED_OFF();
}
@endcode
* @{
*/
/* Инициализировать кнопку (структуру кнопки) */
OperationStatus GPIO_Switch_Init(GPIO_SwitchTypeDef *sw, GPIO_TypeDef *GPIOx, uint32_t GPIO_PIN_X, BitState SW_On_State);
/* Считать состоянии кнопки запуска */
int GPIO_Read_Switch(GPIO_SwitchTypeDef *swstart);
/** GPIO_SWITCH
* @}
*/
/**
* @addtogroup GPIO_LEDS LED tools
* @brief Функции для работы с GPIO, для управления светодиодом
* @par Пример использования:
@code
// Инициализация светодиода на порте GPIOA, пин 5, активный уровень 0
GPIO_LEDTypeDef led;
GPIO_LED_Init(&led, GPIOA, GPIO_PIN_5, 0); // или дефайн LED_ON/LED_OFF
// Включение светодиода
GPIO_LED_On(&led);
// Запуск моргания
GPIO_LED_Blink_Start(&led, 500); // Период 500 мс
// В основном цикле
while (1) {
GPIO_LED_Dynamic_Handle(&led);
}
@endcode
* @{
*/
/* Инициализировать светодиод (структуру светодиода) */
OperationStatus GPIO_LED_Init(GPIO_LEDTypeDef *led, GPIO_TypeDef *GPIOx, uint32_t GPIO_PIN_X, BitState LED_On_State);
/* Включить светодиод */
OperationStatus GPIO_LED_On (GPIO_LEDTypeDef *led);
/* Выключить светодиод */
OperationStatus GPIO_LED_Off (GPIO_LEDTypeDef *led);
/* Переключить светодиод */
OperationStatus GPIO_LED_Toggle(GPIO_LEDTypeDef *led);
/* Выставить светодиод по переменной */
OperationStatus GPIO_LED_Set (GPIO_LEDTypeDef *led, uint8_t led_state);
/* Активировать моргание светодиодом */
OperationStatus GPIO_LED_Blink_Start (GPIO_LEDTypeDef *led, uint32_t period);
/* Активировать моргание светодиодом */
OperationStatus GPIO_LED_Fading_Start(GPIO_LEDTypeDef *led, uint32_t period);
/* Управление динамическими режимами свечения светодиода */
void GPIO_LED_Dynamic_Handle(GPIO_LEDTypeDef *led);
/** GPIO_LEDS
* @}
*/
#endif /*__GPIO_H*/

104
MDK-ARM/Core/App/main.c Normal file
View File

@@ -0,0 +1,104 @@
/*==============================================================================
* Шаблон проекта для К1921ВК035 с использованием бибилотеки PLIB035
*------------------------------------------------------------------------------
* ЦНИИ СЭТ, Разваляев Алексей <wot890089@mail.ru>
*==============================================================================
* ЦНИИ СЭТ
*==============================================================================
*/
//-- Includes ------------------------------------------------------------------
#include "main.h"
void restart_receive(void);
void heartbit(void);
//-- Defines -------------------------------------------------------------------
uint8_t rxbuff[10] = {0};
uint8_t txbuff[20] = {0};
uint16_t seq0_buff[2][100] = {0};
uint16_t seq1_buff[2][100] = {0};
//-- Peripheral init functions -------------------------------------------------
void periph_init()
{
sysclk_init();
uart_init_first();
adc_init_first();
tmr_init_first();
gpio_init();
#ifdef RETARGET
retarget_init();
#endif
NVIC_SetAllPriorities();
printf("\nAll peripherals inited, SYSCLK = %3d MHz\n", (int)(SystemCoreClock / 1E6));
UART_Start(&huart1, UART_FIFOLevel_1_8, UART_FIFOLevel_1_8);
// TMR_Set_Callback(&htmr2, TMR_Callback_Update, &heartbit);
TMR_Start(&htmr0, DISABLE);
TMR_Start(&htmr1, DISABLE);
TMR_Start(&htmr2, ENABLE);
ADC_SEQ_Set_Callback(&hadc, ADC_SEQ_Num_1, ADC_Callback_SeqCplt, &heartbit);
// ADC_SEQ_Start(&hadc, ADC_SEQ_Num_0, seq0_buff, 100);
ADC_SEQ_Start(&hadc, ADC_SEQ_Num_1, NULL, 100);
}
//-- Main ----------------------------------------------------------------------
uint32_t startmillis = 0;
uint32_t prev_tick;
int main()
{
periph_init();
// UART_Receive_IT(&huart1, rxbuff, sizeof(rxbuff));
TMR_Delay_Start(&htmr0, &prev_tick);
ADC_SEQ_SoftwareStart();
while (1) {
if(TMR_Delay_Done(&htmr0, 2000000, &prev_tick))
{
TMR_Delay_Start(&htmr0, &prev_tick);
GPIO_ToggleBits(GPIOA, GPIO_Pin_8);
startmillis = millis();
// ADC_SEQ_SoftwareStart();
}
TMR_Delay(&htmr0, 100000);
// GPIO_ToggleBits(GPIOA, GPIO_Pin_7);
// UART_Transmit_IT(&huart1, (uint8_t*)"Hello World: ", sizeof("Hello World: ")-1);
// int len = sprintf((char*)txbuff, "\r\nValue: %d", ADC_Channel_GetValue(&hadc, ADC_CH_Num_3));
// UART_Transmit_IT(&huart1, txbuff, len);
};
//return 0;
}
void restart_receive(void)
{
// UART_Receive_IT(&huart1, rxbuff, sizeof(rxbuff));
// UART_Transmit_IT(&huart1, rxbuff, sizeof(rxbuff));
}
char tickbuff[40] = {0};
void heartbit(void)
{
static uint32_t prev_tick = 0;
int len = sprintf(tickbuff, "\r\nTick: %d, Value: %d", TMR_Get_Cnt(&htmr0) - prev_tick, ADC_Channel_GetValue(&hadc, ADC_CH_Num_3));
prev_tick = TMR_Get_Cnt(&htmr0);
UART_Transmit_IT(&huart1, (uint8_t *)tickbuff, len);
}
//-- Assert --------------------------------------------------------------------
void Error_Handler(void)
{
__disable_irq();
while (1)
{
}
}
#if defined USE_FULL_ASSERT
void assert_failed(uint8_t* file, uint32_t line)
{
printf("Assert failed: file %s on line %d\n", file, (int)line);
while (1) {
};
}
#endif /* USE_FULL_ASSERT */

25
MDK-ARM/Core/App/main.h Normal file
View File

@@ -0,0 +1,25 @@
/*==============================================================================
* Шаблон проекта для К1921ВК035 с использованием бибилотеки PLIB035
*------------------------------------------------------------------------------
* ЦНИИ СЭТ, Разваляев Алексей <wot890089@mail.ru>
*==============================================================================
* ЦНИИ СЭТ
*==============================================================================
*/
#ifndef __MAIN_H
#define __MAIN_H
//-- Includes ------------------------------------------------------------------
#include "periph_config.h"
#include "plib035.h"
#include "retarget_conf.h"
#include "mylibs_include.h"
//-- Defines -------------------------------------------------------------------
//-- Exported variables --------------------------------------------------------
//-- Exported functions prototypes ---------------------------------------------
void Error_Handler(void);
#endif /* __MAIN_H */

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;
}

78
MDK-ARM/Core/App/sysclk.h Normal file
View File

@@ -0,0 +1,78 @@
/**
******************************************************************************
* @file sysclk.h
* @author Разваляев Алексей
* @brief Драйвер тактирования системы на основе PLIB035.
* Данный файл содержит определения типов, структур и прототипы функций
* для работы с системным тактированием, включая:
* + Типы системных тиков
* + Структуру для управления коллбеками
* + Прототипы функций для инициализации тактирования
* + Функции настройки тактирования периферии
* + Функции работы со временем
*
******************************************************************************
*/
#ifndef __RCU_H
#define __RCU_H
//-- Includes ------------------------------------------------------------------
#include "plib035.h"
#include "retarget_conf.h"
//-- Defines -------------------------------------------------------------------
/** @brief Конвертация МГц в Гц */
#define __MHZ 1000000
/** @brief Максимальное количество коллбеков */
#define SYSCLK_NUMB_OF_CUSTOM_CALLBACKS 16
//-- Exported variables --------------------------------------------------------
extern __IO uint32_t msTick;
extern __IO uint32_t usTick;
//-- Types ---------------------------------------------------------------------
/**
* @brief Типы частот системных тиков
*/
typedef enum
{
SYSCLK_Tick_1us = 1000000, /**< Тик 1 микросекунда */
SYSCLK_Tick_10us = 100000, /**< Тик 10 микросекунд */
SYSCLK_Tick_100us = 10000, /**< Тик 100 микросекунд */
SYSCLK_Tick_1ms = 1000, /**< Тик 1 миллисекунда */
SYSCLK_Tick_10ms = 100, /**< Тик 10 миллисекунд */
SYSCLK_Tick_100ms = 10, /**< Тик 100 миллисекунд */
}SYSCLK_TickHz_TypeDef;
/**
* @brief Структура для управления системными коллбеками
*/
typedef struct
{
int CallbackInUse; /**< Количество активных коллбеков */
void (*Callback[SYSCLK_NUMB_OF_CUSTOM_CALLBACKS])(void); /**< Массив указателей на функции-коллбеки */
uint32_t CallbackPeriod[SYSCLK_NUMB_OF_CUSTOM_CALLBACKS]; /**< Периоды вызова коллбеков в мс */
uint32_t CallbackPrevMs[SYSCLK_NUMB_OF_CUSTOM_CALLBACKS]; /**< Время последнего вызова коллбеков */
} SYSCLK_CallbackHandleTypeDef;
//-- Exported functions prototypes ---------------------------------------------
/* Инициализация системного тактирования */
void sysclk_init(void);
/* Общий обработчик прерываний SysClock */
void sysclk_irq_handler(void);
/* Добавление периодического коллбека */
OperationStatus SYSCLK_Set_Callback(void (*Callback)(void), uint32_t PeriodInMs);
/* Получение текущего времени в миллисекундах */
uint32_t millis(void);
/* Получение текущего времени в микросекундах */
uint32_t micros(void);
/* Настройка тактирования АЦП */
OperationStatus rcu_set_clock_adc(RCU_PeriphClk_TypeDef ClkSrc, float ClkMHz, FunctionalState state);
#endif /*__RCU_H*/

423
MDK-ARM/Core/App/tmr.c Normal file
View File

@@ -0,0 +1,423 @@
/**
******************************************************************************
* @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);
}

111
MDK-ARM/Core/App/tmr.h Normal file
View File

@@ -0,0 +1,111 @@
/**
******************************************************************************
* @file tmr.h
* @author Разваляев Алексей
* @brief Драйвер UART на основе PLIB035.
* Данный файл содержит определения типов, структур и прототипы функций
* для работы с TMR, включая:
* + Структуры и typedef для таймеров
* + Макросы для конфигурации периодов и частот
* + Прототипы функций для инициализации и API драйвера
*
******************************************************************************
*/
#ifndef __TMR_H
#define __TMR_H
//-- Includes ------------------------------------------------------------------
#include "plib035.h"
#include "retarget_conf.h"
//-- Defines -------------------------------------------------------------------
// Дефайны для режима таймера Период в мкс, Частота, ПсевдоПрескалер, Период в тиках
/** @brief Период в мкс */
#define PERIOD_US(_per_) (_per_), 0, 0, 0
/** @brief Период в Гц */
#define FREQ_HZ(_freq_) 0, (_freq_), 0, 0
/** @brief Период в прескалере и тиках */
#define LOAD(_load_, _presc_) 0, 0, (_presc_), (_load_)
//-- Types ---------------------------------------------------------------------
/**
* @brief Типы callback-функций TMR
*/
typedef enum
{
TMR_Callback_Update, /*!< Переполнение (опустошение) таймера */
} TMR_CallbackTypeDef;
/**
* @brief Расширенная конфигурация таймера
*/
typedef struct
{
float ClkFreq; /*!< Частота тактирования таймера в МГц */
uint32_t PeriodUs; /*!< Период таймера в мкс */
uint32_t FreqHz; /*!< Частота таймера в Гц */
uint32_t Prescaler; /*!< Псевдопрескалер */
uint32_t Load; /*!< Период таймера в тиках */
FunctionalState IT; /*!< Разрешение прерывания */
FunctionalState ADCSOC; /*!< Разрешение генерации запуска АЦП */
FunctionalState DMAReq; /*!< Разрешение генерации DMA-запросов */
TMR_ExtInput_TypeDef ExtInput; /*!< Настройка внешнего тактирования таймера */
void (*UpdCallback)(void); /*!< Callback-функция по прерыванию таймера */
} TMR_ExtInit_TypeDef;
/**
* @brief Хендл таймера
*/
typedef struct
{
TMR_TypeDef *Instance; /*!< Указатель на регистры таймера */
TMR_ExtInit_TypeDef *Config; /*!< Конфигурация таймера */
} TMR_HandleTypeDef;
//-- External handles ----------------------------------------------------------
extern TMR_HandleTypeDef htmr0;
extern TMR_HandleTypeDef htmr1;
extern TMR_HandleTypeDef htmr2;
extern TMR_HandleTypeDef htmr3;
//-- Exported functions prototypes ---------------------------------------------
/* Init functions */
/* Первичная инициализация таймеров */
void tmr_init_first(void);
/* Общий обработчик прерывания таймера */
void tmr_irq_handler(TMR_HandleTypeDef* htmr);
/* Инициализация TMRx */
OperationStatus tmr_init(TMR_HandleTypeDef *htmr, TMR_ExtInit_TypeDef *NewConfig);
/* Инициализация таймера (её нет в PLIB, поэтому она здесь) */
void TMR_Init(TMR_TypeDef* TMRx, TMR_ExtInit_TypeDef* InitStruct);
/* Установка callback-функции таймера */
OperationStatus TMR_Set_Callback(TMR_HandleTypeDef* htmr, TMR_CallbackTypeDef CallbackType, void (*Callback)(void));
/* API functions*/
/* Запуск таймера */
OperationStatus TMR_Start(TMR_HandleTypeDef *htmr, FunctionalState IT);
/* Остановка таймера */
OperationStatus TMR_Stop(TMR_HandleTypeDef *htmr, FunctionalState IT);
/*Получить счетчик таймера (с псевдопрескалером) */
uint32_t TMR_Get_Cnt(TMR_HandleTypeDef *htmr);
/* Получить период таймера (с псевдопрескалером) */
uint32_t TMR_Get_Period(TMR_HandleTypeDef *htmr);
/* Задержка в тиках таймера (блокирующая). */
OperationStatus TMR_Delay(TMR_HandleTypeDef *htmr, uint32_t delay);
/* Проверка завершения неблокирующей задержки. */
OperationStatus TMR_Delay_Start(TMR_HandleTypeDef *htmr, uint32_t *var);
/* Проверка завершения неблокирующей задержки. */
int TMR_Delay_Done(TMR_HandleTypeDef *htmr, uint32_t delay, uint32_t *var);
#endif /*__TMR_H*/

776
MDK-ARM/Core/App/uart.c Normal file
View File

@@ -0,0 +1,776 @@
/**
******************************************************************************
* @file uart.c
* @author Разваляев Алексей
* @brief Драйвер UART на основе PLIB035.
* Этот файл содержит:
* + Инициализацию UART0/UART1
* + Управление FIFO
* + Передачу и приём данных в blocking и interrupt режимах
* + Общий обработчик прерываний UART
* + Функции настройки GPIO для UART
* + Очередь передачи для предотвращения потери данных
*
******************************************************************************
* @attention
*
* Использование этого драйвера предполагает наличие корректных настроек:
* - Определены конфигурационные структуры uartx_config в periph_config.h
* - Определены пины TX/RX для UART0/UART1
*
******************************************************************************
* @verbatim
==============================================================================
##### Как использовать этот драйвер #####
==============================================================================
1. Настройка в periph_config.h:
(+) Определите uart0_config, uart1_config для нужных UART
(+) Используйте макросы для направления:
• UART_Direction_None - прием и передача отключены
• UART_Direction_RxTx - полный дуплекс
• UART_Direction_Tx - только передача
• UART_Direction_Rx - только прием
2. Инициализация:
(+) uart_init_first() - настройка GPIO, тактирования и прерываний
(+) uart_init(&huart, &config) - инициализация конкретного UART
3. Callback-функции:
(+) UART_Set_Callback(&huart, UART_Callback_Rx, func) - при завершении приема
(+) UART_Set_Callback(&huart, UART_Callback_Tx, func) - при завершении передачи
(+) UART_Set_Callback(&huart, UART_Callback_Idle, func) - при обнаружении IDLE
(+) UART_Set_Callback(&huart, UART_Callback_Error, func) - при ошибках
4. Запуск UART:
(+) UART_Start(&huart, TxFifoLevel, RxFifoLevel) - включение UART и настройка FIFO
5. Передача и приём данных:
- Режим Polling:
(+) UART_Transmit(&huart, buf, size, timeout) - блокирующая передача
(+) UART_Receive(&huart, buf, size, timeout) - блокирующий прием
- Режим Interrupt:
(+) UART_Transmit_IT(&huart, buf, size) - неблокирующая передача
(+) UART_Receive_IT(&huart, buf, size) - неблокирующий прием
6. Обработка прерываний:
(+) Прерывания обрабатывают TX FIFO, RX FIFO, ошибки и состояние IDLE
7. GPIO для UART:
(+) Пины настраиваются автоматически при вызове uart_init_first()
(+) При необходимости можно вызвать uart0_gpio_deinit() для восстановления
==============================================================================
##### Особенности работы #####
==============================================================================
- Очередь передачи (USE_TX_QUEUE):
- Циклический буфер на 32 сообщения (TX_QUEUE_SIZE)
- Предотвращает потерю данных при частой отправке
- Включается автоматически при USE_TX_QUEUE=1 в uart.h
- Сообщения обрабатываются последовательно по завершении предыдущей передачи
- FIFO и прерывания:
- Аппаратный FIFO 16 байт для приема и передачи
- Уровни прерываний настраиваются через TxFifoLevel и RxFifoLevel
- Прерывание TX FIFO генерируется когда FIFO не полон
- Прерывание RX FIFO генерируется когда FIFO не пуст
- Состояние IDLE:
- Обнаруживается по таймауту приема (32 бит-времени)
- Генерирует прерывание RecieveTimeout
- Полезно для определения конца пакета переменной длины
- Обработка ошибок:
- Обрабатываются все типы ошибок UART: фрейм, паритет, переполнение, break
- При ошибке вызывается ErrCallback и сбрасываются флаги ошибок
- Ошибки не останавливают работу UART
- GPIO автоматическая настройка:
- Пины TX/RX настраиваются в AltFunc режим при инициализации
- Поддерживаются стандартные пины (PB10/PB11 для UART0, PB8/PB9 для UART1)
- Режимы работы:
- Polling: простой, но блокирующий, подходит для инициализации и отладки
- Interrupt: неблокирующий, требует прерываний и поддерживает callback-функций
- Queue: расширенный interrupt режим с буферизацией сообщений
@endverbatim
******************************************************************************
*/
//-- Includes ------------------------------------------------------------------
#include "periph_config.h"
UART_HandleTypeDef huart0; /*!< Хендл UART0 */
UART_HandleTypeDef huart1; /*!< Хендл UART1 */
//-- Private function prototypes -----------------------------------------------
static int __uart_fifo_receive(UART_HandleTypeDef *huart, uint8_t it_mode);
static int __uart_fifo_transmit(UART_HandleTypeDef *huart, uint8_t it_mode);
#if USE_TX_QUEUE==1
static void __uart_tx_queue_process(UART_HandleTypeDef *huart);
static OperationStatus __uart_tx_queue_push(UART_HandleTypeDef *huart, const uint8_t *buf, uint16_t size);
static OperationStatus __uart_tx_queue_pop(UART_HandleTypeDef *huart);
#endif
//-- Defines -------------------------------------------------------------------
//-- UART Init functions -------------------------------------------------------
/**
* @brief Первичная инициализация UART (UART0 / UART1)
* @details Настройка UART и хендлов: GPIO, тактирование и прерывания
*/
void uart_init_first(void)
{
#if (USE_UART0==1)
// Настройка пинов для UART0
uart0_gpio_init();
// Включаем тактирование UART0
RCU_UARTClkConfig(UART0_Num, RCU_PeriphClk_PLLClk, 0, DISABLE);
RCU_UARTClkCmd(UART0_Num, ENABLE);
UART_DeInit(UART0);
// Инициализируем UART0
huart0.Instance = UART0;
uart_init(&huart0, &uart0_config);
NVIC_EnableIRQ(UART0_TD_IRQn);
NVIC_EnableIRQ(UART0_RX_IRQn);
NVIC_EnableIRQ(UART0_TX_IRQn);
NVIC_EnableIRQ(UART0_E_RT_IRQn);
#endif
#if (USE_UART1==1)
// Настройка пинов для UART1
uart1_gpio_init();
// Включаем тактирование UART1
RCU_UARTClkConfig(UART1_Num, RCU_PeriphClk_PLLClk, 0, DISABLE);
RCU_UARTClkCmd(UART1_Num, ENABLE);
UART_DeInit(UART1);
// Инициализируем UART1
NVIC_EnableIRQ(UART1_TD_IRQn);
NVIC_EnableIRQ(UART1_RX_IRQn);
NVIC_EnableIRQ(UART1_TX_IRQn);
NVIC_EnableIRQ(UART1_E_RT_IRQn);
huart1.Instance = UART1;
uart_init(&huart1, &uart1_config);
#endif
}
/**
* @brief Инициализация UART
* @param htmr указатель на хендл UART
* @param NewConfig указатель на новую конфигурацию UART, иначе используется та, что в структуре
* @retval OperationStatus OK - если всё хорошо, ERROR - если ошибка
*/
OperationStatus uart_init(UART_HandleTypeDef *huart, UART_ExtInit_TypeDef *NewConfig)
{
if(!huart || !huart->Instance)
return ERROR;
if(NewConfig != NULL)
{
huart->Config = NewConfig;
}
if(huart->Config == NULL)
{
return ERROR;
}
UART_Init(huart->Instance, &huart->Config->UART_Init);
return OK;
}
/**
* @brief Установка коллбека UART
* @param htmr указатель на хендл UART
* @param CallbackType Тип коллбека
* @param Callback Функция коллбека
* @retval void
*/
OperationStatus UART_Set_Callback(UART_HandleTypeDef* huart, UART_CallbackTypeDef CallbackType, void (*Callback)(void))
{
if (!huart || !huart->Instance || !huart->Config)
return ERROR;
switch(CallbackType)
{
case UART_Callback_Rx:
huart->Config->RxCallback = Callback;
break;
case UART_Callback_Tx:
huart->Config->TxCallback = Callback;
break;
case UART_Callback_Idle:
huart->Config->IdleCallback = Callback;
break;
case UART_Callback_Error:
huart->Config->ErrCallback = Callback;
break;
default:
return ERROR;
}
return OK;
}
//-- UART API functions --------------------------------------------------------
/**
* @brief Запуск UART и инициализация FIFO
* @param huart указатель на хендл UART
* @param TxFifoLevel уровень прерывания TX FIFO
* @param RxFifoLevel уровень прерывания RX FIFO
* @retval OperationStatus OK - если успешно, ERROR - при ошибке
*/
OperationStatus UART_Start(UART_HandleTypeDef *huart, UART_FIFOLevel_TypeDef TxFifoLevel, UART_FIFOLevel_TypeDef RxFifoLevel)
{
if (!huart)
return ERROR;
UART_ITFIFOLevelRxConfig(huart->Instance, RxFifoLevel);
UART_ITFIFOLevelTxConfig(huart->Instance, TxFifoLevel);
huart->TxBufPtr = NULL;
huart->TxCount = 0;
huart->TxSize = 0;
huart->TxBusy = 0;
huart->RxBufPtr = NULL;
huart->RxCount = 0;
huart->RxSize = 0;
huart->RxBusy = 0;
#if USE_TX_QUEUE==1
huart->TxQueue.QueueHead = 0;
huart->TxQueue.QueueTail = 0;
huart->TxQueue.QueueCount = 0;
huart->TxQueue.QueueEmpty = 1;
huart->TxQueue.QueueFull = 0;
for (uint8_t i = 0; i < TX_QUEUE_SIZE; i++)
{
huart->TxQueue.Queue[i].Buf = NULL;
huart->TxQueue.Queue[i].Size = 0;
huart->TxQueue.Queue[i].InUse = 0;
}
#endif
UART_Cmd(huart->Instance, ENABLE);
return OK;
}
/**
* @brief Передача данных по UART (блокирующий режим)
* @param huart указатель на хендл UART
* @param buf указатель на буфер данных
* @param size размер данных в байтах
* @param timeout таймаут ожидания (мс)
* @retval OperationStatus OK - если успешно, ERROR - при ошибке или таймауте
*/
OperationStatus UART_Transmit(UART_HandleTypeDef *huart,
uint8_t *buf,
uint16_t size,
uint32_t timeout)
{
if (!huart || !buf || size == 0)
return ERROR;
// Если TX занят, возвращаем ERROR
if (huart->TxBusy)
return ERROR;
huart->TxBufPtr = buf;
huart->TxSize = size;
huart->TxCount = 0;
huart->TxBusy = 1;
uint32_t starttick = millis();
// Отправляем пока всё не отправится
while(__uart_fifo_transmit(huart, 0) == 0)
{
if(millis() - starttick > timeout)
return ERROR;
}
return OK;
}
/**
* @brief Приём данных по UART (блокирующий режим)
* @param huart указатель на хендл UART
* @param buf указатель на буфер приёма
* @param size количество принимаемых байт
* @param timeout таймаут ожидания (мс)
* @retval OperationStatus OK - если успешно, ERROR - при ошибке или таймауте
*/
OperationStatus UART_Receive(UART_HandleTypeDef *huart,
uint8_t *buf,
uint16_t size,
uint32_t timeout)
{
if (!huart || !buf || size == 0)
return ERROR;
// Если RX занят, возвращаем ERROR
if (huart->RxBusy)
return ERROR;
huart->RxBufPtr = buf;
huart->RxSize = size;
huart->RxCount = 0;
huart->RxBusy = 1;
uint32_t starttick = millis();
// Принимаем всё пока всё не примется
while(__uart_fifo_transmit(huart, 0) == 0)
{
if(millis() - starttick > timeout)
return ERROR;;
}
return OK;
}
/**
* @brief Передача данных по UART (прерывания)
* @param huart указатель на хендл UART
* @param buf указатель на буфер данных
* @param size размер данных в байтах
* @retval OperationStatus OK - если успешно, ERROR - если передача уже идёт
*/
OperationStatus UART_Transmit_IT(UART_HandleTypeDef *huart,
uint8_t *buf,
uint16_t size)
{
if (!huart || !buf || size == 0)
return ERROR;
#if USE_TX_QUEUE==1
// Автоматически используем очередь
if (huart->TxQueue.QueueFull)
return ERROR;
OperationStatus status = __uart_tx_queue_push(huart, buf, size);
if (status != OK)
return ERROR;
if (!huart->TxBusy)
{
__uart_tx_queue_process(huart);
}
return OK;
#else
// Без очереди
// Если TX занят, возвращаем ERROR
if (huart->TxBusy)
return ERROR;
huart->TxBufPtr = buf;
huart->TxSize = size;
huart->TxCount = 0;
huart->TxBusy = 1;
// Включаем прерывания по TX FIFO
UART_ITCmd(huart->Instance, UART_ITSource_TxFIFOLevel, ENABLE);
__uart_fifo_transmit(huart, 1);
return OK;
#endif
}
/**
* @brief Приём данных по UART (прерывания)
* @param huart указатель на хендл UART
* @param buf указатель на буфер приёма
* @param size количество принимаемых байт
* @retval OperationStatus OK - если успешно, ERROR - если приём уже идёт
*/
OperationStatus UART_Receive_IT(UART_HandleTypeDef *huart,
uint8_t *buf,
uint16_t size)
{
if (!huart || !buf || size == 0)
return ERROR;
// Если RX занят, возвращаем ERROR
if (huart->RxBusy)
return ERROR;
huart->RxBufPtr = buf;
huart->RxSize = size;
huart->RxCount = 0;
huart->RxBusy = 1;
// Включаем только RX прерывания
UART_ITCmd(huart->Instance,
UART_ITSource_RxFIFOLevel |
UART_ITSource_RecieveTimeout |
UART_ITSource_ErrorFrame |
UART_ITSource_ErrorParity |
UART_ITSource_ErrorOverflow |
UART_ITSource_ErrorBreak,
ENABLE);
return OK;
}
//-- UART Handler functions ---------------------------------------------------
/**
* @brief Общий обработчик прерываний UART
* @param huart указатель на хендл UART
* @details Обрабатывает RX/TX FIFO, ошибки и события Idle
* @retval void
*/
void uart_irq_handler(UART_HandleTypeDef *huart)
{
if (!huart || !huart->Instance || !huart->Config)
return;
// GPIO_SetBits(GPIOA, GPIO_Pin_7);
UART_TypeDef *uart = huart->Instance;
uint32_t mis = uart->MIS;
// Ошибки
if (mis & (UART_ITSource_ErrorFrame |
UART_ITSource_ErrorParity |
UART_ITSource_ErrorOverflow |
UART_ITSource_ErrorBreak))
{
if (huart->Config->ErrCallback)
huart->Config->ErrCallback();
UART_ErrorStatusClear(uart, UART_Error_All);
UART_ITStatusClear(uart, mis);
}
// RX FIFO
if (mis & UART_ITSource_RxFIFOLevel)
{
// Принимаем
if(__uart_fifo_receive(huart, 1))
{ // Когда всё приняли, флаги сбросили внутри функции
}
UART_ITStatusClear(uart, UART_ITSource_RxFIFOLevel);
}
// IDLE / Receive Timeout
if (mis & UART_ITSource_RecieveTimeout)
{
UART_ITStatusClear(uart, UART_ITSource_RecieveTimeout);
huart->RxBusy = 0;
// Выключаем RX прерывания до следующего вызова UART_Receive_IT
UART_ITCmd(uart,
UART_ITSource_RxFIFOLevel |
UART_ITSource_RecieveTimeout,
DISABLE);
if (huart->Config->IdleCallback)
huart->Config->IdleCallback();
}
// TX FIFO
if (mis & UART_ITSource_TxFIFOLevel)
{
// Отправляем
if(__uart_fifo_transmit(huart, 1))
{ // Когда всё отправили, флаги сбросили внутри функции
}
UART_ITStatusClear(uart, UART_ITSource_TxFIFOLevel);
}
// Конец передачи
if (mis & UART_ITSource_TransmitDone)
{
UART_ITStatusClear(uart, UART_ITSource_TransmitDone);
huart->TxBusy = 0;
if (huart->Config->TxCallback)
huart->Config->TxCallback();
#if USE_TX_QUEUE==1
// Если есть очередь, обрабатываем следующий пакет
__uart_tx_queue_process(huart);
#endif
}
// GPIO_ClearBits(GPIOA, GPIO_Pin_7);
}
//-- UART GPIO functions -------------------------------------------------------
/**
* @brief Инициализация GPIO для UART0
*/
void uart0_gpio_init(void)
{
#if USE_UART0==1
// Получаем структуры
GPIO_Init_TypeDef *tx_config = gpio_get_init(UART0_GPIO_Port, UART0_Tx_Pin);
GPIO_Init_TypeDef *rx_config = gpio_get_init(UART0_GPIO_Port, UART0_Rx_Pin);
// TX пин
if (uart0_config.UART_Init.Tx == ENABLE && tx_config != NULL)
{
GPIO_StructInit(tx_config);
tx_config->AltFunc = ENABLE;
tx_config->Digital = ENABLE;
tx_config->Pin = UART0_Tx_Pin;
GPIO_Init(UART0_GPIO_Port, tx_config);
}
// RX пин
if (uart0_config.UART_Init.Rx == ENABLE && rx_config != NULL)
{
GPIO_StructInit(rx_config);
rx_config->AltFunc = ENABLE;
rx_config->Digital = ENABLE;
rx_config->Pin = UART0_Rx_Pin;
GPIO_Init(UART0_GPIO_Port, rx_config);
}
#endif
}
/**
* @brief Деинициализация GPIO для UART0
*/
void uart0_gpio_deinit(void)
{
#if USE_UART0==1
// Получаем структуры
GPIO_Init_TypeDef *tx_config = gpio_get_init(UART0_GPIO_Port, UART0_Tx_Pin);
GPIO_Init_TypeDef *rx_config = gpio_get_init(UART0_GPIO_Port, UART0_Rx_Pin);
// Восстанавливаем оригинальные настройки из таблицы
if (tx_config != NULL)
{
GPIO_StructInit(rx_config);
rx_config->Pin = UART0_Tx_Pin;
GPIO_Init(UART0_GPIO_Port, tx_config);
}
if (rx_config != NULL)
{
GPIO_StructInit(rx_config);
rx_config->Pin = UART0_Rx_Pin;
GPIO_Init(UART0_GPIO_Port, rx_config);
}
#endif
}
/**
* @brief Инициализация GPIO для UART1
*/
void uart1_gpio_init(void)
{
#if USE_UART1==1
// Получаем структуры
GPIO_Init_TypeDef *tx_config = gpio_get_init(UART1_GPIO_Port, UART1_Tx_Pin);
GPIO_Init_TypeDef *rx_config = gpio_get_init(UART1_GPIO_Port, UART1_Rx_Pin);
// TX пин
if (uart1_config.UART_Init.Tx == ENABLE && tx_config != NULL)
{
GPIO_StructInit(tx_config);
tx_config->AltFunc = ENABLE;
tx_config->Digital = ENABLE;
tx_config->Pin = UART1_Tx_Pin;
GPIO_Init(UART1_GPIO_Port, tx_config);
}
// RX пин
if (uart1_config.UART_Init.Rx == ENABLE && rx_config != NULL)
{
;
GPIO_StructInit(rx_config);
rx_config->AltFunc = ENABLE;
rx_config->Digital = ENABLE;
rx_config->Pin = UART1_Rx_Pin;
GPIO_Init(UART1_GPIO_Port, rx_config);
}
#endif
}
/**
* @brief Деинициализация GPIO для UART1
*/
void uart1_gpio_deinit(void)
{
#if USE_UART1==1
// Получаем структуры
GPIO_Init_TypeDef *tx_config = gpio_get_init(UART1_GPIO_Port, UART1_Tx_Pin);
GPIO_Init_TypeDef *rx_config = gpio_get_init(UART1_GPIO_Port, UART1_Rx_Pin);
// Восстанавливаем оригинальные настройки из таблицы
if (tx_config != NULL)
{
GPIO_StructInit(rx_config);
rx_config->Pin = UART1_Tx_Pin;
GPIO_Init(UART1_GPIO_Port, tx_config);
}
if (rx_config != NULL)
{
GPIO_StructInit(rx_config);
rx_config->Pin = UART1_Rx_Pin;
GPIO_Init(UART1_GPIO_Port, rx_config);
}
#endif
}
//-- UART private functions ----------------------------------------------------
/**
* @brief Приём данных из RX FIFO
* @param huart указатель на хендл UART
* @param it_mode режим работы (0 — polling, 1 — прерывания)
* @retval int 1 — приём завершён, 0 — данные ещё принимаются
*/
static int __uart_fifo_receive(UART_HandleTypeDef *huart, uint8_t it_mode)
{
while (!UART_FlagStatus(huart->Instance, UART_Flag_RxFIFOEmpty) &&
huart->RxCount < huart->RxSize)
{
huart->RxBufPtr[huart->RxCount++] = UART_RecieveData(huart->Instance);
if (huart->RxCount == huart->RxSize)
{
huart->RxBusy = 0;
if(it_mode)
{
// Выключаем RX прерывания
UART_ITCmd(huart->Instance,
UART_ITSource_RxFIFOLevel |
UART_ITSource_RecieveTimeout,
DISABLE);
}
if (huart->Config->RxCallback)
huart->Config->RxCallback();
return 1;
}
}
return 0;
}
/**
* @brief Передача данных в TX FIFO
* @param huart указатель на хендл UART
* @param it_mode режим работы (0 — polling, 1 — прерывания)
* @retval int 1 — передача завершена, 0 — данные ещё передаются
*/
static int __uart_fifo_transmit(UART_HandleTypeDef *huart, uint8_t it_mode)
{
while (!UART_FlagStatus(huart->Instance, UART_Flag_TxFIFOFull) &&
huart->TxCount < huart->TxSize)
{
UART_SendData(huart->Instance, huart->TxBufPtr[huart->TxCount++]);
}
if (huart->TxCount == huart->TxSize)
{
if(it_mode)
{
// Выключаем FIFO прерывание
UART_ITCmd(huart->Instance, UART_ITSource_TxFIFOLevel, DISABLE);
// Включаем TransmitDone прерывание, коллбек будет по нему
UART_ITCmd(huart->Instance, UART_ITSource_TransmitDone, ENABLE);
}
else
{
while(!UART_FlagStatus(huart->Instance, UART_Flag_TxFIFOEmpty)); // ждем пока не опустошится буфер
huart->TxBusy = 0;
if (huart->Config->TxCallback)
huart->Config->TxCallback();
}
return 1;
}
return 0;
}
#if USE_TX_QUEUE==1
static void __uart_tx_queue_process(UART_HandleTypeDef *huart)
{
if (!huart->TxBusy && !huart->TxQueue.QueueEmpty)
{
__uart_tx_queue_pop(huart);
}
}
static OperationStatus __uart_tx_queue_push(UART_HandleTypeDef *huart, const uint8_t *buf, uint16_t size)
{
if (!huart || !buf || size == 0 || huart->TxQueue.QueueFull)
return ERROR;
huart->TxQueue.Queue[huart->TxQueue.QueueHead].Buf = buf;
huart->TxQueue.Queue[huart->TxQueue.QueueHead].Size = size;
huart->TxQueue.Queue[huart->TxQueue.QueueHead].InUse = 1;
huart->TxQueue.QueueHead = (huart->TxQueue.QueueHead + 1) % TX_QUEUE_SIZE;
huart->TxQueue.QueueCount++;
huart->TxQueue.QueueEmpty = 0;
if (huart->TxQueue.QueueCount == TX_QUEUE_SIZE)
huart->TxQueue.QueueFull = 1;
return OK;
}
static OperationStatus __uart_tx_queue_pop(UART_HandleTypeDef *huart)
{
if (!huart || huart->TxQueue.QueueEmpty)
return ERROR;
const uint8_t *buf = huart->TxQueue.Queue[huart->TxQueue.QueueTail].Buf;
uint16_t size = huart->TxQueue.Queue[huart->TxQueue.QueueTail].Size;
if (huart->TxBusy)
return ERROR;
huart->TxBufPtr = buf;
huart->TxSize = size;
huart->TxCount = 0;
huart->TxBusy = 1;
UART_ITCmd(huart->Instance, UART_ITSource_TxFIFOLevel, ENABLE);
__uart_fifo_transmit(huart, 1);
huart->TxQueue.Queue[huart->TxQueue.QueueTail].Buf = NULL;
huart->TxQueue.Queue[huart->TxQueue.QueueTail].Size = 0;
huart->TxQueue.Queue[huart->TxQueue.QueueTail].InUse = 0;
huart->TxQueue.QueueTail = (huart->TxQueue.QueueTail + 1) % TX_QUEUE_SIZE;
huart->TxQueue.QueueCount--;
huart->TxQueue.QueueFull = 0;
if (huart->TxQueue.QueueCount == 0)
huart->TxQueue.QueueEmpty = 1;
return OK;
}
#endif

166
MDK-ARM/Core/App/uart.h Normal file
View File

@@ -0,0 +1,166 @@
/**
******************************************************************************
* @file uart.h
* @author Разваляев Алексей
* @brief Драйвер UART на основе PLIB035.
* Данный файл содержит определения типов, структур и прототипы функций
* для работы с UART, включая:
* + Структуры и typedef для UART
* + Макросы для конфигурации направления передачи
* + Прототипы функций для инициализации и API драйвера
*
******************************************************************************
*/
#ifndef __UART_H
#define __UART_H
//-- Includes ------------------------------------------------------------------
#include "plib035.h"
#include "retarget_conf.h"
//-- Defines -------------------------------------------------------------------
// Дефайны для режима направления UART
// Дефайны для режима пина RxEnable, TxEnable
/** @brief Направление: никакого */
#define UART_Direction_None DISABLE, DISABLE
/** @brief Направление: Rx и Tx */
#define UART_Direction_RxTx ENABLE, ENABLE
/** @brief Направление: только Tx */
#define UART_Direction_Tx DISABLE, ENABLE
/** @brief Направление: только Rx */
#define UART_Direction_Rx ENABLE, DISABLE
// Дефайны для пинов UART
#define UART0_Tx_Pin GPIO_Pin_10 /**< PB10 — UART0 Tx */
#define UART0_Rx_Pin GPIO_Pin_11 /**< PB11 — UART0 Rx */
#define UART0_GPIO_Port GPIOB /**< GPIO порт UART0 */
#define UART1_Tx_Pin GPIO_Pin_8 /**< PB8 — UART1 Tx */
#define UART1_Rx_Pin GPIO_Pin_9 /**< PB9 — UART1 Rx */
#define UART1_GPIO_Port GPIOB /**< GPIO порт UART1 */
#define USE_TX_QUEUE 1 /*!< Использовать очередь для отправки, чтобы не терять данные */
#define TX_QUEUE_SIZE 32 /*!< Размер очереди в кол-ве буферов для отправки */
//-- Types ---------------------------------------------------------------------
/**
* @brief Типы callback-функций UART
*/
typedef enum
{
UART_Callback_Rx, /*!< Приём данных завершён */
UART_Callback_Tx, /*!< Передача данных завершена */
UART_Callback_Idle, /*!< Обнаружено состояние IDLE */
UART_Callback_Error, /*!< Ошибка UART */
} UART_CallbackTypeDef;
/**
* @brief Расширенная конфигурация UART
*/
typedef struct
{
UART_Init_TypeDef UART_Init; /*!< Стандартная конфигурация UART из PLIB */
/* Callback функции */
void (*RxCallback)(void); /*!< Вызывается при приёме полного сообщения */
void (*TxCallback)(void); /*!< Вызывается при окончании передачи */
void (*IdleCallback)(void); /*!< Вызывается при IDLE линии RX */
void (*ErrCallback)(void); /*!< Вызывается при ошибке UART */
} UART_ExtInit_TypeDef;
/**
* @brief Структура элемента очереди
*/
typedef struct {
const uint8_t *Buf; /*!< Указатель на буфер данных */
uint16_t Size; /*!< Размер данных в буфере */
uint8_t InUse; /*!< Флаг занятости элемента */
} UART_QueueItemTypeDef;
/**
* @brief Структура очереди UART
*/
typedef struct {
UART_QueueItemTypeDef Queue[TX_QUEUE_SIZE]; /*!< Циклический буфер очереди */
uint8_t QueueHead; /*!< Индекс головы очереди (куда добавляем) */
uint8_t QueueTail; /*!< Индекс хвоста очереди (откуда берем) */
uint8_t QueueCount; /*!< Количество элементов в очереди */
uint8_t QueueEmpty; /*!< Флаг пустой очереди */
uint8_t QueueFull; /*!< Флаг полной очереди */
} UART_TxQueueTypeDef;
/**
* @brief Хендл UART
*/
typedef struct
{
UART_TypeDef *Instance; /*!< Регистры UART */
UART_ExtInit_TypeDef *Config; /*!< Конфигурация UART */
/* ===== TX ===== */
const uint8_t *TxBufPtr; /*!< Указатель на буфер передачи */
uint16_t TxCount; /*!< Счётчик переданных байт */
uint16_t TxSize; /*!< Размер передаваемых данных */
volatile uint8_t TxBusy; /*!< Флаг занятости TX */
/* ===== RX ===== */
uint8_t *RxBufPtr; /*!< Указатель на буфер приёма */
uint16_t RxCount; /*!< Счётчик принятых байт */
uint16_t RxSize; /*!< Размер принимаемых данных */
volatile uint8_t RxBusy; /*!< Флаг занятости RX */
#if (USE_TX_QUEUE==1)
/* == TX QUEUE == */
UART_TxQueueTypeDef TxQueue;
#endif
} UART_HandleTypeDef;
//-- External handles ----------------------------------------------------------
extern UART_HandleTypeDef huart0;
extern UART_HandleTypeDef huart1;
//-- Exported functions prototypes ---------------------------------------------
/* Init functions */
/* Первичная инициализация UART (UART0 / UART1) */
void uart_init_first(void);
/* Инициализация UARTx */
OperationStatus uart_init(UART_HandleTypeDef *huart, UART_ExtInit_TypeDef *NewConfig);
/* Общий обработчик прерываний UART */
void uart_irq_handler(UART_HandleTypeDef *huart);
/* Инициализация GPIO для UART0 */
void uart0_gpio_init(void);
/* Деинициализация GPIO для UART0 */
void uart0_gpio_deinit(void);
/* Инициализация GPIO для UART1 */
void uart1_gpio_init(void);
/* Деинициализация GPIO для UART1 */
void uart1_gpio_deinit(void);
/* API functions*/
/* Установка callback-функции UART */
OperationStatus UART_Set_Callback(UART_HandleTypeDef *huart, UART_CallbackTypeDef CallbackType, void (*Callback)(void));
/* Запуск UART и настройка FIFO */
OperationStatus UART_Start(UART_HandleTypeDef *huart, UART_FIFOLevel_TypeDef TxFifoLevel, UART_FIFOLevel_TypeDef RxFifoLevel);
/* Передача данных по UART (блокирующий режим) */
OperationStatus UART_Transmit(UART_HandleTypeDef *huart, uint8_t *buf, uint16_t size, uint32_t timeout);
/* Приём данных по UART (блокирующий режим) */
OperationStatus UART_Receive(UART_HandleTypeDef *huart, uint8_t *buf, uint16_t size, uint32_t timeout);
/* Передача данных по UART (режим прерываний) */
OperationStatus UART_Transmit_IT(UART_HandleTypeDef *huart, uint8_t *buf, uint16_t size);
/* Приём данных по UART (режим прерываний) */
OperationStatus UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *buf, uint16_t size);
#endif /*__UART_H*/

412
MDK-ARM/Core/App/vk035_it.c Normal file
View File

@@ -0,0 +1,412 @@
/**
******************************************************************************
* @file vk035_it.c
* @author Разваляев Алексей
* @brief Обработчики прерываний для микроконтроллера 1921ВК035.
* Этот файл содержит:
* + Обработчики прерываний периферии (UART, TMR, ADC, DMA и др.)
* + Обработчики исключений Cortex-M4 (HardFault, SysTick и др.)
* + Интеграцию с драйверами периферии через вызовы обработчиков
* + Управление системным временем через SysTick
*
******************************************************************************
* @attention
*
* Использование этого файла предполагает наличие корректных настроек:
* - Определены USE_xxx макросы в periph_config.h для включения периферии
* - Инициализированы соответствующие драйверы периферии
* - Включены прерывания в NVIC через функции драйверов
*
******************************************************************************
* @verbatim
==============================================================================
##### Как использовать этот файл #####
==============================================================================
1. Настройка периферии (periph_config.h):
(+) Определить USE_TMRx для используемых таймеров (0-3)
(+) Определить USE_UARTx для используемых UART (0-1)
(+) Определить USE_ADC_SEQx для используемых секвенсоров ADC (0-1)
2. Инициализация драйверов:
(+) Вызвать функции инициализации драйверов (uart_init_first, tmr_init_first и т.д.)
- Драйверы автоматически включат соответствующие прерывания в NVIC
3. Обработка прерываний:
(+) Прерывания периферии автоматически перенаправляются в драйверы
(+) Системные прерывания (SysTick) управляют временем millis/micros
(+) Обработчики исключений обрабатывают критические ошибки
4. Интеграция с драйверами:
(+) UART прерывания -> uart_irq_handler(&huartx)
(+) TMR прерывания -> tmr_irq_handler(&htmrx)
(+) ADC SEQ прерывания -> adc_seq_irq_handler(&hadc, SEQ_Num)
5. Системное время:
(+) SysTick_Handler() автоматически обновляет millis() и micros()
(+) Период SysTick настраивается в sysclk_init()
6. Особенности работы:
(+) Неиспользуемые обработчики оставлены пустыми для будущего расширения
(+) Критические ошибки (HardFault и др.) уходят в бесконечный цикл
(+) Прерывания обрабатываются неблокирующе с минимальными задержками
@endverbatim
******************************************************************************
*/
//-- Includes ------------------------------------------------------------------
#include "main.h"
/******************************************************************************/
/* 1921VK035 Peripheral Interrupt Handlers */
/* Add here the Interrupt Handlers for the used peripherals. */
/******************************************************************************/
#if USE_WDT==1
void WDT_IRQHandler(void)
{
}
#endif
void GPIOA_IRQHandler(void)
{
}
void GPIOB_IRQHandler(void)
{
}
#if USE_TMR0==1
void TMR0_IRQHandler(void)
{
tmr_irq_handler(&htmr0);
}
#endif
#if USE_TMR1==1
void TMR1_IRQHandler(void)
{
tmr_irq_handler(&htmr1);
}
#endif
#if USE_TMR2==1
void TMR2_IRQHandler(void)
{
tmr_irq_handler(&htmr2);
}
#endif
#if USE_TMR3==1
void TMR3_IRQHandler(void)
{
tmr_irq_handler(&htmr3);
}
#endif
#if USE_UART0==1
void UART0_TD_IRQHandler(void)
{
uart_irq_handler(&huart0);
}
void UART0_RX_IRQHandler(void)
{
uart_irq_handler(&huart0);
}
void UART0_TX_IRQHandler(void)
{
uart_irq_handler(&huart0);
}
void UART0_E_RT_IRQHandler(void)
{
uart_irq_handler(&huart0);
}
#endif
#if USE_UART1==1
void UART1_TD_IRQHandler(void)
{
uart_irq_handler(&huart1);
}
void UART1_RX_IRQHandler(void)
{
uart_irq_handler(&huart1);
}
void UART1_TX_IRQHandler(void)
{
uart_irq_handler(&huart1);
}
void UART1_E_RT_IRQHandler(void)
{
uart_irq_handler(&huart1);
}
#endif
#if USE_SPI==1
void SPI_RO_RT_IRQHandler(void)
{
}
void SPI_RX_IRQHandler(void)
{
}
void SPI_TX_IRQHandler(void)
{
}
#endif
#if USE_I2C==1
void I2C_IRQHandler(void)
{
}
#endif
#if USE_ECAP0==1
void ECAP0_IRQHandler(void)
{
}
#endif
#if USE_ECAP1==1
void ECAP1_IRQHandler(void)
{
}
#endif
#if USE_ECAP2==1
void ECAP2_IRQHandler(void)
{
}
#endif
#if USE_PWM0==1
void PWM0_IRQHandler(void)
{
}
void PWM0_HD_IRQHandler(void)
{
}
void PWM0_TZ_IRQHandler(void)
{
}
#endif
#if USE_PWM1==1
void PWM1_IRQHandler(void)
{
}
void PWM1_HD_IRQHandler(void)
{
}
void PWM1_TZ_IRQHandler(void)
{
}
#endif
#if USE_PWM2==1
void PWM2_IRQHandler(void)
{
}
void PWM2_HD_IRQHandler(void)
{
}
void PWM2_TZ_IRQHandler(void)
{
}
#endif
#if USE_QEP==1
void QEP_IRQHandler(void)
{
}
#endif
#if USE_ADC_SEQ0==1
void ADC_SEQ0_IRQHandler(void)
{
adc_seq_irq_handler(&hadc, ADC_SEQ_Num_0);
}
#endif
#if USE_ADC_SEQ1==1
void ADC_SEQ1_IRQHandler(void)
{
adc_seq_irq_handler(&hadc, ADC_SEQ_Num_1);
}
#endif
#if (USE_ADC_DC0==1) || (USE_ADC_DC1==1) || (USE_ADC_DC2==1) || (USE_ADC_DC3==1)
void ADC_DC_IRQHandler(void)
{
}
#endif
#if USE_ADC_CAN==1
void CAN0_IRQHandler(void)
{
}
void CAN1_IRQHandler(void)
{
}
void CAN2_IRQHandler(void)
{
}
void CAN3_IRQHandler(void)
{
}
void CAN4_IRQHandler(void)
{
}
void CAN5_IRQHandler(void)
{
}
void CAN6_IRQHandler(void)
{
}
void CAN7_IRQHandler(void)
{
}
void CAN8_IRQHandler(void)
{
}
void CAN9_IRQHandler(void)
{
}
void CAN10_IRQHandler(void)
{
}
void CAN11_IRQHandler(void)
{
}
void CAN12_IRQHandler(void)
{
}
void CAN13_IRQHandler(void)
{
}
void CAN14_IRQHandler(void)
{
}
void CAN15_IRQHandler(void)
{
}
#endif
void DMA_CH0_IRQHandler(void)
{
}
void DMA_CH1_IRQHandler(void)
{
}
void DMA_CH2_IRQHandler(void)
{
}
void DMA_CH3_IRQHandler(void)
{
}
void DMA_CH4_IRQHandler(void)
{
}
void DMA_CH5_IRQHandler(void)
{
}
void DMA_CH6_IRQHandler(void)
{
}
void DMA_CH7_IRQHandler(void)
{
}
void DMA_CH8_IRQHandler(void)
{
}
void DMA_CH9_IRQHandler(void)
{
}
void DMA_CH10_IRQHandler(void)
{
}
void DMA_CH11_IRQHandler(void)
{
}
void DMA_CH12_IRQHandler(void)
{
}
void DMA_CH13_IRQHandler(void)
{
}
void DMA_CH14_IRQHandler(void)
{
}
void DMA_CH15_IRQHandler(void)
{
}
void FPU_IRQHandler(void)
{
}
void RCU_IRQHandler(void)
{
}
void MFLASH_IRQHandler(void)
{
}
/******************************************************************************/
/* Cortex-M4 Processor Interruption and Exception Handlers */
/******************************************************************************/
/**
* @brief This function handles Non maskable interrupt.
*/
void NMI_Handler(void)
{
while (1)
{
}
}
/**
* @brief This function handles Hard fault interrupt.
*/
void HardFault_Handler(void)
{
while (1)
{
}
}
/**
* @brief This function handles Memory management fault.
*/
void MemManage_Handler(void)
{
while (1)
{
}
}
/**
* @brief This function handles Prefetch fault, memory access fault.
*/
void BusFault_Handler(void)
{
while (1)
{
}
}
/**
* @brief This function handles Undefined instruction or illegal state.
*/
void UsageFault_Handler(void)
{
while (1)
{
}
}
/**
* @brief This function handles System service call via SWI instruction.
*/
void SVC_Handler(void)
{
}
/**
* @brief This function handles Debug monitor.
*/
void DebugMon_Handler(void)
{
}
/**
* @brief This function handles Pendable request for system service.
*/
void PendSV_Handler(void)
{
}
/**
* @brief This function handles System tick timer.
*/
void SysTick_Handler(void)
{
sysclk_irq_handler();
}