Проект перенесен в папку MDK-ARM
This commit is contained in:
588
MDK-ARM/Core/App/adc.c
Normal file
588
MDK-ARM/Core/App/adc.c
Normal 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
162
MDK-ARM/Core/App/adc.h
Normal 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
417
MDK-ARM/Core/App/gpio.c
Normal 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
164
MDK-ARM/Core/App/gpio.h
Normal 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
104
MDK-ARM/Core/App/main.c
Normal 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
25
MDK-ARM/Core/App/main.h
Normal 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
316
MDK-ARM/Core/App/sysclk.c
Normal 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
78
MDK-ARM/Core/App/sysclk.h
Normal 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
423
MDK-ARM/Core/App/tmr.c
Normal 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
111
MDK-ARM/Core/App/tmr.h
Normal 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
776
MDK-ARM/Core/App/uart.c
Normal 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
166
MDK-ARM/Core/App/uart.h
Normal 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
412
MDK-ARM/Core/App/vk035_it.c
Normal 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();
|
||||
}
|
||||
Reference in New Issue
Block a user