Добавлено:

- рефакторинг названий:
	- маленькие буквы - инит периферии
	- ТакойСтильФункций - API для использования
- сделаны шапки с инструкциями к функциями
- доработан ацп секвенсора
- доработан систем тики. можно настроить его на разные частоты и подключить коллбеки на разный период
- в gpio добавлены функции для кнопок и диодов
- генерация бинарника
This commit is contained in:
2025-12-27 23:20:03 +03:00
parent c7fdf6776f
commit f3e76e105a
20 changed files with 2105 additions and 6274 deletions

View File

@@ -1,22 +1,115 @@
/*==============================================================================
* Инициализация АЦП с использованием бибилотеки PLIB035
*------------------------------------------------------------------------------
* ЦНИИ СЭТ, Разваляев Алексей <wot890089@mail.ru>
*==============================================================================
* ЦНИИ СЭТ
*==============================================================================
*/
/**
******************************************************************************
* @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:
a) Инициализация:
(+) adc_init_first() — обязательный вызов перед использованием ADC
b) Работа с каналами:
(+) ADC_Get_ChannelValue(&hadc, channel) — получение текущего значения канала
2. Секвенсоры (SEQ):
a) Настройка периферии (periph_config.h):
(+) Определить структуры ADC_SEQ_ExtInit_TypeDef для нужных секвенсоров:
adc_seq0_config, adc_seq1_config
(+) Настроить последовательность каналов, режимы работы
(+) Настроить прерывания (IT) и ITCount
(+) Определить callback-функции (можно NULL)
b) Инициализация:
(+) adc_init_first() — первичная настройка тактирования, сброс ADC, инициализация хендла hadc
(+) adc_seq_init(&hadc, ADC_SEQ_Num_0, &adc_seq0_config) — инициализация конкретного секвенсора
c) Callback-функции (опционально):
(+) ADC_SEQ_Set_Callback(&hadc, ADC_SEQ_Num_0, ADC_Callback_SeqCplt, Callback) — завершение секвенсора
(+) ADC_SEQ_Set_Callback(&hadc, ADC_SEQ_Num_0, ADC_Callback_BuffHalf, Callback) — половина буфера
(+) ADC_SEQ_Set_Callback(&hadc, ADC_SEQ_Num_0, ADC_Callback_BuffFull, Callback) — полный буфер
(+) ADC_SEQ_Set_Callback(&hadc, ADC_SEQ_Num_0, ADC_Callback_Error, Callback) — ошибки
d) Запуск и остановка:
(+) ADC_SEQ_Start(&hadc, ADC_SEQ_Num_0, data_buffer, buffer_size) — запуск с буфером
(+) ADC_SEQ_Stop(&hadc, ADC_SEQ_Num_0) — остановка секвенсора
e) Работа с данными:
(+) ADC_Get_ChannelValue(&hadc, channel) — чтение текущего значения канала из хендла
(+) ADC_SEQ_SoftwareStart() — программный запуск преобразования
f) Обработка прерываний:
(+) adc_seq_irq_handler(&hadc, ADC_SEQ_Num_0) — обработчик прерываний секвенсора
- В обработчике автоматически читаются данные из FIFO, обновляются каналы и буфер
- В обработчиках автоматически вызываются соответствующие callback-функции
и сбрасываются флаги
g) Особенности работы:
(+) Данные автоматически читаются из FIFO в прерывании
(+) Поддерживается кольцевой буфер (BufferCircular)
(+) Автоматический вызов callback при заполнении половины/всего буфера
3. Цифровые компараторы (DC):
@endverbatim
******************************************************************************
*/
//-- Includes ------------------------------------------------------------------
#include "periph_config.h"
ADC_HandleTypeDef hadc;
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 -------------------------------------------------------------------
//-- Peripheral init functions -------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////SEQUENCORS/////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
//-- 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)
@@ -41,11 +134,12 @@ void adc_init_first(void)
#endif
#if (USE_ADC_SEQ1==1)
adc_seq_init(&hadc, ADC_SEQ_Num_1, &adc_seq1_config);
if(hadc.SEQ_Config[ADC_SEQ_Num_1]->IT == ENABLE)
if(hadc.SEQ[ADC_SEQ_Num_1].Config->IT == ENABLE)
{
NVIC_EnableIRQ(ADC_SEQ1_IRQn);
}
#endif
#if (USE_ADC_DC0==1)
#endif
#if (USE_ADC_DC1==1)
@@ -64,6 +158,8 @@ void adc_init_first(void)
#endif
}
//-- ADC Sequencers API functions ----------------------------------------------
/**
* @brief Инициализация секвенсора АЦП
* @param hadc указатель на хендл АЦП
@@ -87,6 +183,16 @@ OperationStatus adc_seq_init(ADC_HandleTypeDef *hadc, ADC_SEQ_Num_TypeDef SEQ_Nu
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;
@@ -95,20 +201,20 @@ OperationStatus adc_seq_init(ADC_HandleTypeDef *hadc, ADC_SEQ_Num_TypeDef SEQ_Nu
/**
* @brief Установка коллбека секвенсора АЦП
* @param hadc указатель на хендл АЦП
* @param cb_type Тип коллбека
* @param CallbackType Тип коллбека
* @param Callback Функция коллбека
* @retval void
*/
OperationStatus adc_seq_set_callback(ADC_HandleTypeDef* hadc, ADC_SEQ_Num_TypeDef SEQ_Num, ADC_CallbackTypeDef cb_type, void (*Callback)(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(cb_type)
switch(CallbackType)
{
case ADC_Callback_Seq:
case ADC_Callback_SeqCplt:
hseq->Config->SEQCpltCallback = Callback;
break;
case ADC_Callback_Error:
@@ -135,7 +241,7 @@ OperationStatus adc_seq_set_callback(ADC_HandleTypeDef* hadc, ADC_SEQ_Num_TypeDe
* @param buffer_size размер буфера для каждого канала (0 если буфер не используется)
* @retval OperationStatus OK - если всё хорошо, ERROR - если ошибка
*/
OperationStatus adc_seq_start(ADC_HandleTypeDef *hadc,
OperationStatus ADC_SEQ_Start(ADC_HandleTypeDef *hadc,
ADC_SEQ_Num_TypeDef SEQ_Num,
uint16_t (*data_buffer)[],
uint32_t buffer_size)
@@ -151,18 +257,24 @@ OperationStatus adc_seq_start(ADC_HandleTypeDef *hadc,
hseq->buffer_size = buffer_size;
hseq->buffer_count = 0; // Сбрасываем счетчик
// DMA > IT
if(conf->SEQ_Init.DMAEn == ENABLE)
{
conf->IT = DISABLE;
}
// Настраиваем прерывания если нужно
if (conf->IT == ENABLE)
{
ADC_SEQ_ITConfig(SEQ_Num, conf->ITCount, DISABLE);
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);
// Помечаем как запущенный
hseq->running = 1;
return OK;
}
@@ -174,22 +286,19 @@ OperationStatus adc_seq_start(ADC_HandleTypeDef *hadc,
* @param SEQ_Num номер секвенсора
* @retval OperationStatus OK - если всё хорошо, ERROR - если ошибка
*/
OperationStatus adc_seq_stop(ADC_HandleTypeDef *hadc, ADC_SEQ_Num_TypeDef SEQ_Num)
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_HandleTypeDef *hseq = &hadc->SEQ[SEQ_Num];
// Выключаем секвенсор
ADC_SEQ_Cmd(SEQ_Num, DISABLE);
// Выключаем прерывания
ADC_SEQ_ITCmd(SEQ_Num, DISABLE);
// Сбрасываем флаг запуска
hseq->running = 1;
return OK;
}
@@ -199,40 +308,34 @@ OperationStatus adc_seq_stop(ADC_HandleTypeDef *hadc, ADC_SEQ_Num_TypeDef SEQ_Nu
* @param channel номер канала
* @retval значение ADC (0 если данные невалидны)
*/
uint16_t adc_get_channel_value(ADC_HandleTypeDef *hadc, ADC_CH_Num_TypeDef channel)
uint16_t ADC_Get_ChannelValue(ADC_HandleTypeDef *hadc, ADC_CH_Num_TypeDef channel)
{
if (!hadc || channel >= ADC_CH_Total)
if (!hadc || (int)channel >= ADC_CH_Total)
return 0;
if (hadc->ChannelValid[channel])
{
return hadc->ChannelData[channel];
}
return 0;
return hadc->ChannelData[channel];
}
/**
* @brief Программный запуск преобразования
*/
void adc_sw_start(void)
void ADC_SEQ_SoftwareStart(void)
{
ADC_SEQ_SwStartCmd();
}
int itcnt;
/**
* @brief Обработчик прерываний секвенсора
* @param hadc указатель на хендл АЦП
* @param SEQ_Num номер секвенсора
* @retval void
*/
void adc_seq_handler(ADC_HandleTypeDef *hadc, ADC_SEQ_Num_TypeDef SEQ_Num)
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);
itcnt++;
ADC_SEQ_HandleTypeDef *hseq = &hadc->SEQ[SEQ_Num];
ADC_SEQ_ExtInit_TypeDef *conf = hseq->Config;
@@ -242,73 +345,7 @@ void adc_seq_handler(ADC_HandleTypeDef *hadc, ADC_SEQ_Num_TypeDef SEQ_Num)
// Очищаем флаг
ADC_SEQ_ITStatusClear(SEQ_Num);
// Последний запрос в секвенсоре
ADC_SEQ_ReqNum_TypeDef last_request = conf->SEQ_Init.ReqMax;
// Читаем все доступные данные из FIFO
uint8_t channels_processed = 0;
while (ADC_SEQ_GetFIFOLoad(ADC_SEQ_Num_0))
{
uint32_t data = ADC_SEQ_GetFIFOData(SEQ_Num);
ADC_SEQ_ReqNum_TypeDef req_num = ADC_SEQ_GetReqCurrent(SEQ_Num);
if (req_num < ADC_SEQ_Req_Total)
{
ADC_CH_Num_TypeDef channel = conf->SEQ_Init.Req[req_num];
if (channel < ADC_CH_Total)
{
// 1. Обновляем текущее значение
hadc->ChannelData[channel] = (uint16_t)data;
hadc->ChannelValid[channel] = 1;
// 2. Записываем в буфер если он есть
if (hseq->data_buffer &&
hseq->buffer_size > 0)
{
// Вычисляем offset для [ch][buffer_size]
uint32_t offset = channel * hseq->buffer_size +
hseq->buffer_count;
hseq->data_buffer[offset] = (uint16_t)data;
}
channels_processed++;
// Если это последний канал в секвенсоре - увеличиваем счетчик буфера
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->buffer_circular)
hseq->buffer_count = 0;
else
adc_seq_stop(hadc, SEQ_Num);
if (conf->BuffFullCallback)
{
conf->BuffFullCallback();
}
}
}
}
}
}
// Вызываем коллбек пользователя
if (conf->SEQCpltCallback)
{
conf->SEQCpltCallback();
}
__adc_seq_fifo_read(hadc, SEQ_Num);
}
// Обработка ошибок
@@ -321,117 +358,127 @@ void adc_seq_handler(ADC_HandleTypeDef *hadc, ADC_SEQ_Num_TypeDef SEQ_Num)
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();
}
}
////////////////////////////////////////////////////////////////////////////////
////////////////////////////DIGITAL COMPARATORS/////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
///**
// * @brief Инициализация цифрового компаратора АЦП
// * @param hadc указатель на хендл АЦП
// * @param DC_Num номер компаратора
// * @param NewConfig указатель на конфигурацию
// * @retval OperationStatus OK - если всё хорошо, ERROR - если ошибка
// */
//OperationStatus adc_dc_init(ADC_HandleTypeDef *hadc, ADC_DC_Num_TypeDef DC_Num, ADC_DC_ExtInit_TypeDef *NewConfig)
//{
// if(!hadc || !hadc->Instance)
// return ERROR;
//
// if(NewConfig != NULL)
// {
// hadc->DC_Config[DC_Num] = NewConfig;
// }
//
// if(hadc->DC_Config[DC_Num] == NULL)
// {
// return ERROR;
// }
//
// // Инициализация компаратора
// ADC_DC_Init(DC_Num, &hadc->DC_Config[DC_Num]->DC_Init);
//
// return OK;
//}
///**
// * @brief Установка коллбека цифрового компаратора АЦП
// * @param hadc указатель на хендл АЦП
// * @param DC_Num номер компаратора
// * @param cb_type тип коллбека
// * @param Callback функция коллбека
// * @retval OperationStatus
// */
//OperationStatus adc_dc_set_callback(ADC_HandleTypeDef* hadc, ADC_DC_Num_TypeDef DC_Num, ADC_CallbackTypeDef cb_type, void (*Callback)(void))
//{
// if (!hadc || !hadc->Instance || !hadc->DC_Config[DC_Num])
// return ERROR;
//
// switch(cb_type)
// {
// case ADC_Callback_DC:
// hadc->DC_Config[DC_Num]->DC_TrigCallback = Callback;
// break;
// case ADC_Callback_Error:
// hadc->DC_Config[DC_Num]->ErrorCallback = Callback;
// break;
// default:
// return ERROR;
// }
// return OK;
//}
///**
// * @brief Запуск цифрового компаратора АЦП
// * @param hadc указатель на хендл АЦП
// * @param DC_Num номер компаратора
// * @retval OperationStatus OK - если всё хорошо, ERROR - если ошибка
// */
//OperationStatus adc_dc_start(ADC_HandleTypeDef *hadc, ADC_DC_Num_TypeDef DC_Num)
//{
// if (!hadc || !hadc->Instance || !hadc->DC_Config[DC_Num])
// return ERROR;
//
// ADC_DC_ExtInit_TypeDef *conf = hadc->DC_Config[DC_Num];
//
// // Включаем выход компаратора
// ADC_DC_OutputCmd(DC_Num, ENABLE);
//
// // Настраиваем прерывания если нужно
// if(conf->IT == ENABLE)
// {
// ADC_DC_ITCmd(DC_Num, ENABLE);
// }
//
// return OK;
//}
///**
// * @brief Остановка цифрового компаратора АЦП
// * @param hadc указатель на хендл АЦП
// * @param DC_Num номер компаратора
// * @retval OperationStatus OK - если всё хорошо, ERROR - если ошибка
// */
//OperationStatus adc_dc_stop(ADC_HandleTypeDef *hadc, ADC_DC_Num_TypeDef DC_Num)
//{
// if (!hadc || !hadc->Instance || !hadc->DC_Config[DC_Num])
// return ERROR;
//
// // Выключаем выход компаратора
// ADC_DC_OutputCmd(DC_Num, DISABLE);
//
// // Выключаем прерывания
// ADC_DC_ITCmd(DC_Num, DISABLE);
//
// return OK;
//}
//void adc_dc_handler(ADC_HandleTypeDef *hadc, ADC_DC_Num_TypeDef DC_Num)
//{
//}
//-- ADC Digital Comparators API functions -------------------------------------

View File

@@ -1,11 +1,15 @@
/*==============================================================================
* Инициализация таймеров с использованием бибилотеки PLIB035
*------------------------------------------------------------------------------
* ЦНИИ СЭТ, Разваляев Алексей <wot890089@mail.ru>
*==============================================================================
* ЦНИИ СЭТ
*==============================================================================
*/
/**
******************************************************************************
* @file adc.h
* @author Разваляев Алексей
* @brief Драйвер ADC на основе PLIB035.
* Данный файл содержит определения типов, структур и прототипы функций
* для работы с ADC, включая:
* + Структуры и typedef для ADC
* + Прототипы функций для инициализации и API драйвера
*
******************************************************************************
*/
#ifndef __ADC_H
#define __ADC_H
//-- Includes ------------------------------------------------------------------
@@ -13,23 +17,30 @@
#include "retarget_conf.h"
//-- Defines -------------------------------------------------------------------
// Дефайны для режима АЦП
//-- Types ---------------------------------------------------------------------
/**
* @brief Типы callback-функций ADC
*/
typedef enum
{
ADC_Callback_Seq,
ADC_Callback_BuffHalf,
ADC_Callback_BuffFull,
ADC_Callback_DC,
ADC_Callback_Error,
ADC_Callback_SeqCplt, /*!< Преобразование секвенсора завершено */
ADC_Callback_BuffHalf, /*!< Буфер заполнен наполовину */
ADC_Callback_BuffFull, /*!< Буфер заполнен полностью */
ADC_Callback_DCTrig, /*!< Компаратор сработал */
ADC_Callback_Error, /*!< Ошибка */
}ADC_CallbackTypeDef;
/**
* @brief Расширенная конфигурация секвенсора ADC
*/
typedef struct
{
ADC_SEQ_Init_TypeDef SEQ_Init; /* Стандартная конфигурация из PLIB */
FunctionalState IT; /*!< Включить прерывания */
uint8_t ITCount; /*!< Количество запросов для генерации прерывания */
uint32_t buffer_circular; /*!< Циклический буфер */
FunctionalState BufferCircular; /*!< Циклический буфер */
void (*SEQCpltCallback)(void); /*!< Вызывается при завершении секвенсора */
void (*BuffHalfCallback)(void); /*!< Вызывается при заполнении половины буфера */
@@ -37,63 +48,84 @@ typedef struct
void (*ErrorCallback)(); /*!< Вызывается при ошибке */
}ADC_SEQ_ExtInit_TypeDef;
/**
* @brief Расширенная конфигурация компаратора ADC
*/
typedef struct
{
ADC_DC_Init_TypeDef DC_Init;
FunctionalState IT; /*!< Включить прерывания */
void (*DC_TrigCallback); /*!< Вызывается при срабатывании компаратора */
void (*ErrorCallback)(); /*!< Вызывается при ошибке */
void (*ErrorCallback)(); /*!< Вызывается при ошибке */
}ADC_DC_ExtInit_TypeDef;
/**
* @brief Хендл секвенсора ADC
*/
typedef struct
{
ADC_SEQ_ExtInit_TypeDef *Config; /*!< Конфигурации секвенсоров */
uint8_t running;
ADC_SEQ_ExtInit_TypeDef *Config; /*!< Конфигурации секвенсоров */
uint16_t *data_buffer; /*!< Указатель на буфер данных [ch][buffer_size]*/
uint32_t buffer_size; /*!< Размер буфера (16-bit слова) */
uint32_t buffer_count; /*!< Счетчик текущего индекса буфера */
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; /*!< Конфигурации компараторов */
uint8_t running;
}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 ===== */
uint16_t ChannelData[ADC_CH_Total]; /*!< Текущие значения каналов */
uint8_t ChannelValid[ADC_CH_Total]; /*!< Флаги валидности данных */
}ADC_HandleTypeDef;
//-- External handles ----------------------------------------------------------
extern ADC_HandleTypeDef hadc;
//-- Exported functions prototypes ---------------------------------------------
/* Первичная инициализация ADC */
void adc_init_first(void);
/* Sequencer Init functions*/
/* Инициализация секвенсора АЦП */
OperationStatus adc_seq_init(ADC_HandleTypeDef *hadc, ADC_SEQ_Num_TypeDef SEQ_Num, ADC_SEQ_ExtInit_TypeDef *NewConfig);
OperationStatus adc_seq_set_callback(ADC_HandleTypeDef* hadc, ADC_SEQ_Num_TypeDef SEQ_Num, ADC_CallbackTypeDef cb_type, void (*Callback)(void));
/* Обработчик прерываний секвенсора */
void adc_seq_irq_handler(ADC_HandleTypeDef *hadc, ADC_SEQ_Num_TypeDef SEQ_Num);
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);
/* Sequencer API functions*/
uint16_t adc_get_channel_value(ADC_HandleTypeDef *hadc, ADC_CH_Num_TypeDef channel);
void adc_sw_start(void);
/* Установка коллбека секвенсора АЦП */
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);
/* Получение текущего значения канала */
uint16_t ADC_Get_ChannelValue(ADC_HandleTypeDef *hadc, ADC_CH_Num_TypeDef channel);
/* Программный запуск преобразования секвенсора */
void ADC_SEQ_SoftwareStart(void);
// Обработчики прерываний
void adc_seq_handler(ADC_HandleTypeDef *hadc, ADC_SEQ_Num_TypeDef SEQ_Num);
/* Comparator API functions*/
void adc_dc_handler(ADC_HandleTypeDef *hadc, ADC_DC_Num_TypeDef DC_Num);
#endif /*__ADC_H*/

View File

@@ -1,19 +1,65 @@
/*==============================================================================
* Инициализация портов с использованием бибилотеки PLIB035
*------------------------------------------------------------------------------
* ЦНИИ СЭТ, Разваляев Алексей <wot890089@mail.ru>
*==============================================================================
* Конфигурация портов настраивается в gpio.h
* ЦНИИ СЭТ
*==============================================================================
*/
/**
******************************************************************************
* @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(GPIOA, GPIO_PIN_5) — получение конфигурации пина
@endverbatim
******************************************************************************
*/
//-- Includes ------------------------------------------------------------------
#include "periph_config.h"
//-- Defines -------------------------------------------------------------------
//-- Peripheral init functions -------------------------------------------------
//-- GPIO init functions -------------------------------------------------------
void gpio_init(void)
{
RCU_AHBClkCmd(RCU_AHBClk_GPIOA, ENABLE);
@@ -36,43 +82,12 @@ void gpio_init(void)
{
GPIO_Init(GPIOB, &gpiob_config[i]);
}
// GPIO_StructInit(&gpio_init);
// gpio_init.Digital = ENABLE;
// GPIO_Init(GPIOA, &gpio_init);
// GPIO_Init(GPIOB, &gpio_init);
//
// /* Инициализация выходных пинов Пуш-Пулл */
// gpio_init.Out = ENABLE;
// gpio_init.OutMode = GPIO_OutMode_PP;
//
// gpio_init.Pin = GPIO_OUT_PP_PA_PINS;
// GPIO_Init(GPIOA, &gpio_init);
// gpio_init.Pin = GPIO_OUT_PP_PB_PINS;
// GPIO_Init(GPIOB, &gpio_init);
//
// /* Инициализация выходных пинов Открытый сток */
// gpio_init.Out = ENABLE;
// gpio_init.OutMode = GPIO_OutMode_OD;
//
// gpio_init.Pin = GPIO_OUT_OD_PA_PINS;
// GPIO_Init(GPIOA, &gpio_init);
// gpio_init.Pin = GPIO_OUT_OD_PB_PINS;
// GPIO_Init(GPIOB, &gpio_init);
}
GPIO_Init_TypeDef *gpio_get_init(GPIO_TypeDef *GPIOx, uint32_t GPIO_Pin)
{
uint8_t pin_index = __builtin_ctz(GPIO_Pin);
uint8_t pin_index = (31 - __CLZ(GPIO_Pin & -GPIO_Pin));
if (GPIOx == GPIOA)
{
@@ -93,3 +108,282 @@ GPIO_Init_TypeDef *gpio_get_init(GPIO_TypeDef *GPIOx, uint32_t GPIO_Pin)
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;
}

View File

@@ -1,12 +1,16 @@
/*==============================================================================
* Инициализация портов с использованием бибилотеки PLIB035
*------------------------------------------------------------------------------
* ЦНИИ СЭТ, Разваляев Алексей <wot890089@mail.ru>
*==============================================================================
* Реализация функций инициализации портов находится в gpio.c
* ЦНИИ СЭТ
*==============================================================================
*/
/**
******************************************************************************
* @file gpio.h
* @author Разваляев Алексей
* @brief Драйвер GPIO на основе PLIB035.
* Данный файл содержит определения типов, структур и прототипы функций
* для работы с UART, включая:
* + Структуры и typedef для работы с кнопками и светодиодами
* + Макросы для конфигурации режима пинов
* + Прототипы функций для инициализации и API драйвера
*
******************************************************************************
*/
#ifndef __GPIO_H
#define __GPIO_H
//-- Includes ------------------------------------------------------------------
@@ -21,8 +25,141 @@
#define GPIO_PinMode_AltFunc DISABLE, ENABLE, ENABLE
//#define GPIO_PinMode_Analog DISABLE, DISABLE, DISABLE
#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*/

View File

@@ -10,11 +10,8 @@
//-- Includes ------------------------------------------------------------------
#include "main.h"
void restart_receive(void);
void heartbit(void);
//-- Defines -------------------------------------------------------------------
uint8_t rxbuff[10] = {0};
uint16_t adc_buff[2][100];
//-- Peripheral init functions -------------------------------------------------
void periph_init()
{
@@ -26,60 +23,29 @@ void periph_init()
#ifdef RETARGET
retarget_init();
#endif
NVIC_SetAllPriorities();
printf("\nAll peripherals inited, SYSCLK = %3d MHz\n", (int)(SystemCoreClock / 1E6));
uart_set_callback(&huart1, UART_Callback_Rx, &restart_receive);
uart_start(&huart1, UART_FIFOLevel_1_8, UART_FIFOLevel_1_8);
tmr_set_callback(&htmr2, 0, &heartbit);
tmr_start(&htmr0);
tmr_start(&htmr1);
tmr_start(&htmr2);
adc_seq_start(&hadc, ADC_SEQ_Num_0, adc_buff, 100);
}
//-- Main ----------------------------------------------------------------------
int main()
{
{
periph_init();
uint8_t cnt;
uart_receive_it(&huart1, rxbuff, sizeof(rxbuff));
uint32_t prev_tick;
tmr_delay_start(&htmr0, &prev_tick);
while (1) {
if(tmr_delay_done(&htmr0, 1000000, &prev_tick))
{
tmr_delay_start(&htmr0, &prev_tick);
GPIO_ToggleBits(GPIOA, GPIO_Pin_8);
adc_sw_start();
}
tmr_delay(&htmr0, 100000);
GPIO_ToggleBits(GPIOA, GPIO_Pin_7);
// uart_transmit_it(&huart1, (uint8_t*)"Hello World: ", sizeof("Hello World: ")-1);
// uart_transmit_it(&huart1, (uint8_t*)"delay 500\r\n", sizeof("delay 500\r\n")-1);
while (1)
{
};
//return 0;
}
void restart_receive(void)
{
uart_receive_it(&huart1, rxbuff, sizeof(rxbuff));
uart_transmit_it(&huart1, rxbuff, sizeof(rxbuff));
}
void heartbit(void)
{
uart_transmit(&huart1, (uint8_t *)"\r\nTick 1 sec\r\n", sizeof("\r\nTick 1 sec\r\n")-1, 1000);
}
//-- Assert --------------------------------------------------------------------
void Error_Handler(void)
{
__disable_irq();
while (1)
{
}
}
}
#if defined USE_FULL_ASSERT

View File

@@ -1,19 +1,96 @@
/*==============================================================================
* Инициализация тактирования с использованием бибилотеки PLIB035
*------------------------------------------------------------------------------
* ЦНИИ СЭТ, Разваляев Алексей <wot890089@mail.ru>
*==============================================================================
* ЦНИИ СЭТ
*==============================================================================
*/
/**
******************************************************************************
* @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_1ms и т.д.)
//-- Includes ------------------------------------------------------------------
2. Инициализация тактирования:
(+) sysclk_init() — обязательный вызов в начале программы
- Автоматически настраивает PLL для заданной частоты
- Настраивает SysTick для генерации системных тиков
- Обновляет SystemCoreClock
3. Работа со временем:
(+) millis() — получение текущего времени в миллисекундах
(+) micros() — получение текущего времени в микросекундах
(+) sysclk_irq_handler() — обработчик прерываний SysTick (должен вызываться из SysTick_Handler)
4. Система коллбеков:
(+) SYSCLK_Set_Callback() — добавление периодического коллбека
- Коллбеки автоматически вызываются в sysclk_irq_handler() с заданным периодом
- Максимальное количество коллбеков: SYSCLK_NUMB_OF_CUSTOM_CALLBACKS
5. Настройка тактирования Периферии:
(+) АЦП: rcu_set_clock_adc(ClkSrc, ClkMHz, state)
- ClkSrc: источник тактирования (RCU_PeriphClk_OSEClk, RCU_PeriphClk_PLLClk и т.д.)
- ClkMHz: желаемая частота ADC в МГц
- state: включение/выключение тактирования
6. Особенности работы:
(+) Системные тики работают на основе SysTick
(+) Поддерживаются различные периоды тиков от 1 мкс до 100 мс
(+) Переполнение счетчиков происходит через ~49 дней
@endverbatim
******************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
#include "periph_config.h"
__IO uint32_t uwTick; // Milliseconds ticks
/* 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;
@@ -23,41 +100,158 @@ void sysclk_init(void)
Error_Handler();
}
SystemCoreClockUpdate();
RCU_ClkOutConfig(RCU_SysPeriphClk_PLLClk, 1, ENABLE);
RCU_ClkOutCmd(ENABLE);
// RCU_ClkOutConfig(RCU_SysPeriphClk_PLLClk, 1, ENABLE);
// RCU_ClkOutCmd(ENABLE);
/* Прерывание должно быть каждую миллисекунду:
Для тактирования N Гц это каждый N / 1000 тик */
SysTick_Config(SYSCLK_CORE_CLOCK_MHZ*__MHZ/1000);
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 uwTick;
}
void millis_inc(void)
{
uwTick++;
return msTick;
}
/**
* @brief Получение текущего времени в микросекундах.
* @retval uint32_t Текущее время в микросекундах
* @note Переполнение происходит через ~71 минуту
*/
uint32_t micros(void)
{
return 0;
}
void micros_inc(void)
{
uwTick++;
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)
{
RCU_ADCClkCmd(DISABLE);
uint32_t adc_raw_clock = 0;
float adc_clock_div = 0;
/* Отключение тактирования АЦП для настройки */
RCU_ADCClkCmd(DISABLE);
/* Определение частоты источника тактирования */
switch(ClkSrc)
{
case RCU_PeriphClk_OSEClk:
@@ -72,12 +266,41 @@ OperationStatus rcu_set_clock_adc(RCU_PeriphClk_TypeDef ClkSrc, float ClkMHz, Fu
case RCU_PeriphClk_PLLDivClk:
adc_raw_clock = RCU_GetPLLDivClkFreq();
break;
default:
return ERROR;
}
adc_clock_div = adc_raw_clock/(ClkMHz*__MHZ);
/* Расчет делителя частоты */
adc_clock_div = adc_raw_clock / (ClkMHz * __MHZ);
if(adc_clock_div < 1)
return ERROR;
RCU_ADCClkConfig(ClkSrc, 7, ENABLE); //12.5MHz
RCU_ADCClkCmd(ENABLE);
/* Настройка источника тактирования и делителя */
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;
}

View File

@@ -1,11 +1,19 @@
/*==============================================================================
* Инициализация тактирования с использованием бибилотеки PLIB035
*------------------------------------------------------------------------------
* ЦНИИ СЭТ, Разваляев Алексей <wot890089@mail.ru>
*==============================================================================
* ЦНИИ СЭТ
*==============================================================================
*/
/**
******************************************************************************
* @file sysclk.h
* @author Разваляев Алексей
* @brief Драйвер тактирования системы на основе PLIB035.
* Данный файл содержит определения типов, структур и прототипы функций
* для работы с системным тактированием, включая:
* + Типы системных тиков
* + Структуру для управления коллбеками
* + Прототипы функций для инициализации тактирования
* + Функции настройки тактирования периферии
* + Функции работы со временем
*
******************************************************************************
*/
#ifndef __RCU_H
#define __RCU_H
//-- Includes ------------------------------------------------------------------
@@ -13,18 +21,58 @@
#include "retarget_conf.h"
//-- Defines -------------------------------------------------------------------
extern __IO uint32_t uwTick;
/** @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);
void millis_inc(void);
/* Получение текущего времени в микросекундах */
uint32_t micros(void);
void micros_inc(void);
/* Настройка тактирования АЦП */
OperationStatus rcu_set_clock_adc(RCU_PeriphClk_TypeDef ClkSrc, float ClkMHz, FunctionalState state);
#endif /*__RCU_H*/

View File

@@ -1,22 +1,85 @@
/*==============================================================================
* Инициализация таймеров с использованием бибилотеки PLIB035
*------------------------------------------------------------------------------
* ЦНИИ СЭТ, Разваляев Алексей <wot890089@mail.ru>
*==============================================================================
* ЦНИИ СЭТ
*==============================================================================
*/
/**
******************************************************************************
* @file tmr.c
* @author Разваляев Алексей
* @brief Драйвер таймеров на основе PLIB035.
* Этот файл содержит:
* + Инициализацию таймеров TMR0-TMR3
* + Запуск и остановку таймера
* + Блокирующие и неблокирующие задержки
* + Настройку callback-функций
* + Общий обработчик прерываний таймера
* + Псевдо-PLIB функции для инициализации таймеров
*
******************************************************************************
* @attention
*
* Использование этого драйвера предполагает наличие корректных настроек:
* - Определены конфигурационные структуры tmrx_config в periph_config.h
*
******************************************************************************
* @verbatim
==============================================================================
##### Как использовать этот драйвер #####
==============================================================================
1. Настройка периферии (periph_config.h):
(+) Определить структуры TMR_ExtInit_TypeDef для нужных таймеров:
tmr0_config, tmr1_config, tmr2_config, tmr3_config
(+) Настроить частоту/период в мкс или тиках, прескалер
(+) Разрешить прерывания (IT) и при необходимости ADC SOC / DMA запросы
(+) Указать внешнее тактирование при необходимости
(+) Установить callback-функцию (можно NULL)
2. Инициализация таймеров:
(+) tmr_init_first() — первичная настройка тактирования, сброс периферии и NVIC
(+) tmr_init(&htmr, &config) — инициализация конкретного таймера с конфигурацией
3. Callback-функции (опционально):
(+) TMR_Set_Callback(&htmr, TMR_Callback_Update, Callback)
4. Запуск и остановка таймера:
(+) TMR_Start(&htmr) — запуск таймера
(+) TMR_Stop(&htmr) — остановка таймера
5. Задержки:
- Blocking (блокирующая):
(+) TMR_Delay(&htmr, ticks) — задержка в тиках таймера
- Non-blocking (неблокирующая):
(+) TMR_Delay_Start(&htmr, &var) — запоминает текущее значение таймера
(+) TMR_Delay_Done(&htmr, ticks, &var) — проверяет завершение задержки
6. Получение текущих значений:
(+) TMR_Get_Cnt(&htmr) — получение текущего счетчика с учетом псевдопрескалера
(+) TMR_Get_Period(&htmr) — получение периода таймера с учетом псевдопрескалера
7. Обработка прерываний:
(+) tmr_irq_handler(&htmr) — общий обработчик ISR
- В обработчиках автоматически вызываются соответствующие callback-функции
и сбрасываются флаги
8. Псевдо-PLIB функции:
(+) TMR_Init(TMRx, InitStruct) — инициализация таймера с расширенными параметрами
@endverbatim
******************************************************************************
*/
//-- Includes ------------------------------------------------------------------
#include "periph_config.h"
TMR_HandleTypeDef htmr0;
TMR_HandleTypeDef htmr1;
TMR_HandleTypeDef htmr2;
TMR_HandleTypeDef htmr3;
TMR_HandleTypeDef htmr0; /*!< Хендл TMR0 */
TMR_HandleTypeDef htmr1; /*!< Хендл TMR1 */
TMR_HandleTypeDef htmr2; /*!< Хендл TMR2 */
TMR_HandleTypeDef htmr3; /*!< Хендл TMR3 */
//-- Defines -------------------------------------------------------------------
//-- Peripheral init functions -------------------------------------------------
//-- TMR Init functions --------------------------------------------------------
/**
* @brief Первичная инициализация таймеров TMR0-TMR3
* @details Настройка таймеров и хендлов: тактирование, прерывания
*/
void tmr_init_first(void)
{
#if (USE_TMR0==1)
@@ -97,32 +160,43 @@ OperationStatus tmr_init(TMR_HandleTypeDef *htmr, TMR_ExtInit_TypeDef *NewConfig
/**
* @brief Установка коллбека таймера
* @param htmr указатель на хендл таймера
* @param cb_type Тип коллбека
* @param Callback Функция коллбека
* @param CallbackType Тип коллбека
* @param UpdCallback Функция коллбека
* @retval void
*/
OperationStatus tmr_set_callback(TMR_HandleTypeDef* htmr, int cb_type, void (*Callback)(void))
OperationStatus TMR_Set_Callback(TMR_HandleTypeDef* htmr, TMR_CallbackTypeDef CallbackType, void (*Callback)(void))
{
if(!htmr || !htmr->Instance || !htmr->Config)
{
return ERROR;
}
htmr->Config->Callback = Callback;
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)
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;
@@ -133,17 +207,59 @@ OperationStatus tmr_start(TMR_HandleTypeDef *htmr)
* @param htmr указатель на хендл таймера
* @retval OperationStatus OK - если всё хорошо, ERROR - если ошибка
*/
OperationStatus tmr_stop(TMR_HandleTypeDef *htmr)
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
*/
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->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 Указатель на хендл таймера.
@@ -151,7 +267,7 @@ OperationStatus tmr_stop(TMR_HandleTypeDef *htmr)
* @return OperationStatus.
* @details Формирует задержку с блокировкой программы.
*/
OperationStatus tmr_delay(TMR_HandleTypeDef *htmr, uint32_t delay)
OperationStatus TMR_Delay(TMR_HandleTypeDef *htmr, uint32_t delay)
{
if(!htmr || !htmr->Instance)
return ERROR;
@@ -183,9 +299,11 @@ OperationStatus tmr_delay(TMR_HandleTypeDef *htmr, uint32_t delay)
* @param var Указатель на переменную куда положить значение тиков.
* @return OperationStatus.
* @details Запоминает счетчик для начала отсчета неблокирующей задержки.
* @ref tmr_delay_done для проверки статуса задержки
* @ref TMR_Delay_Done для проверки статуса задержки.
* @note Переменная содержит сырые тики, без преобразования под псевдопрескалер,
* поэтому в var могут быть неадекватно большие значения тиков
*/
OperationStatus tmr_delay_start(TMR_HandleTypeDef *htmr, uint32_t *var)
OperationStatus TMR_Delay_Start(TMR_HandleTypeDef *htmr, uint32_t *var)
{
if(!htmr || !htmr->Instance)
return ERROR;
@@ -202,9 +320,11 @@ OperationStatus tmr_delay_start(TMR_HandleTypeDef *htmr, uint32_t *var)
* @param var Указатель на переменную где хранится тики в момент начала задержки.
* @return 1 - задержка прошла. 0 - задержка в процессе.
* @details Формирует задержку с блокировкой программы.
* Перед ожиданием задержки надо инициализировать задержку @ref tmr_delay_start
* Перед ожиданием задержки надо инициализировать задержку @ref TMR_Delay_Start
* @note Функция считает задержку через сырые тики, поэтому в var могут быть
* неадекватно большие значения тиков
*/
int tmr_delay_done(TMR_HandleTypeDef *htmr, uint32_t delay, uint32_t *var)
int TMR_Delay_Done(TMR_HandleTypeDef *htmr, uint32_t delay, uint32_t *var)
{
if(!htmr || !htmr->Instance)
return 0;
@@ -232,13 +352,14 @@ int tmr_delay_done(TMR_HandleTypeDef *htmr, uint32_t delay, uint32_t *var)
}
//-- TMR Handler functions ----------------------------------------------------
/**
* @brief Обработчик прерывания таймера
* @param htmr указатель на хендл таймера
* @retval void
*/
void tmr_handler(TMR_HandleTypeDef* htmr)
void tmr_irq_handler(TMR_HandleTypeDef* htmr)
{
if((htmr->Instance == NULL) || (htmr->Config == NULL))
{
@@ -249,8 +370,8 @@ void tmr_handler(TMR_HandleTypeDef* htmr)
if (TMR_ITStatus(htmr->Instance) == SET)
{
/* Если есть коллбек вызываем его */
if(htmr->Config->Callback)
htmr->Config->Callback();
if(htmr->Config->UpdCallback)
htmr->Config->UpdCallback();
/* Очистка флага прерывания */
TMR_ITStatusClear(htmr->Instance);
@@ -258,7 +379,7 @@ void tmr_handler(TMR_HandleTypeDef* htmr)
}
/* Расширение библиотеки plib */
//-- TMR pseudoPLIB functions -------------------------------------------------
/**
* @brief Инициализирует модуль TMRx согласно параметрам структуры InitStruct.
* @param TMRx Выбор таймера, где x=A|B

View File

@@ -1,11 +1,16 @@
/*==============================================================================
* Инициализация таймеров с использованием бибилотеки PLIB035
*------------------------------------------------------------------------------
* ЦНИИ СЭТ, Разваляев Алексей <wot890089@mail.ru>
*==============================================================================
* ЦНИИ СЭТ
*==============================================================================
*/
/**
******************************************************************************
* @file tmr.h
* @author Разваляев Алексей
* @brief Драйвер UART на основе PLIB035.
* Данный файл содержит определения типов, структур и прототипы функций
* для работы с TMR, включая:
* + Структуры и typedef для таймеров
* + Макросы для конфигурации периодов и частот
* + Прототипы функций для инициализации и API драйвера
*
******************************************************************************
*/
#ifndef __TMR_H
#define __TMR_H
//-- Includes ------------------------------------------------------------------
@@ -13,51 +18,94 @@
#include "retarget_conf.h"
//-- Defines -------------------------------------------------------------------
// Дефайны для режима таймера Период в мкс, Частота, ПсевдоПрескалер, Период в тиках
#define PERIOD_MKS(_per_) _per_, 0, 0, 0
#define FREQ_HZ(_freq_) 0, _freq_, 0, 0
#define LOAD(_load_, _presc_) 0, 0, _presc_, _load_
// Дефайны для режима таймера Период в мкс, Частота, ПсевдоПрескалер, Период в тиках
/** @brief Период в мкс */
#define PERIOD_MKS(_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; /*!< Частота тактирования таймера в МГц */
float ClkFreq; /*!< Частота тактирования таймера в МГц */
uint32_t PeriodUs; /*!< Период таймера в мкс */
uint32_t FreqHz; /*!< Частота таймера в Гц */
uint32_t Prescaler; /*!< Псевдопрескалер частоты */
uint32_t Load; /*!< Период таймера в тиках */
uint32_t PeriodUs; /*!< Период таймера в мкс */
uint32_t FreqHz; /*!< Частота таймера в Гц */
uint32_t Prescaler; /*!< Псевдопрескалер */
uint32_t Load; /*!< Период таймера в тиках */
FunctionalState IT; /*!< Разрешение работы прерывания */
FunctionalState ADCSOC; /*!< Разрешение генерации сигналов запуска АЦП */
FunctionalState DMAReq; /*!< Разрешение генерации запросов к DMA */
FunctionalState IT; /*!< Разрешение прерывания */
FunctionalState ADCSOC; /*!< Разрешение генерации запуска АЦП */
FunctionalState DMAReq; /*!< Разрешение генерации DMA-запросов */
TMR_ExtInput_TypeDef ExtInput; /*!< Настройка внешнего тактирования таймера */
void (*Callback)(void); /* Коллбек который вызовется по прерыванию таймера */
}TMR_ExtInit_TypeDef;
void (*UpdCallback)(void); /*!< Callback-функция по прерыванию таймера */
} TMR_ExtInit_TypeDef;
/**
* @brief Хендл таймера
*/
typedef struct
{
TMR_TypeDef *Instance;
TMR_ExtInit_TypeDef *Config;
}TMR_HandleTypeDef;
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);
OperationStatus tmr_set_callback(TMR_HandleTypeDef* htmr, int cb_type, void (*Callback)(void));
OperationStatus tmr_start(TMR_HandleTypeDef *htmr);
OperationStatus tmr_stop(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);
void tmr_handler(TMR_HandleTypeDef* htmr);
/* Инициализация таймера (её нет в 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*/

View File

@@ -1,28 +1,105 @@
/*==============================================================================
* Инициализация Instance с использованием бибилотеки PLIB035
*------------------------------------------------------------------------------
* ЦНИИ СЭТ, Разваляев Алексей <wot890089@mail.ru>
*==============================================================================
* ЦНИИ СЭТ
*==============================================================================
*/
/**
******************************************************************************
* @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):
(+) Определить структуры UART_ExtInit_TypeDef для нужных UART:
uart0_config, uart1_config
(+) Настроить скорость, стоп-биты, четность, длину данных
(+) Настроить FIFO и направление (Tx, Rx или оба)
(+) Определить callback-функции (можно NULL)
(+) Определить пины TX/RX для UART0/UART1
(+) Включить/отключить очередь сообщений для отправки: USE_TX_QUEUE в uart.h
2. Инициализация UART:
(+) uart_init_first() — первичная настройка GPIO, тактирования и NVIC
(+) uart_init(&huart, &config) — инициализация конкретного UART с конфигурацией
3. Callback-функции:
(+) UART_Set_Callback(&huart, UART_Callback_Rx, Callback) — вызов при приёме данных
(+) UART_Set_Callback(&huart, UART_Callback_Tx, Callback) — вызов при завершении передачи
(+) UART_Set_Callback(&huart, UART_Callback_Idle, Callback) — вызов при событии Idle
(+) UART_Set_Callback(&huart, UART_Callback_Error, Callback) — вызов при ошибках
4. Запуск UART и FIFO:
(+) UART_Start(&huart, TxFifoLevel, RxFifoLevel) — включает UART и настраивает FIFO
5. Передача и приём данных:
- Режим Polling (blocking):
(+) UART_Transmit(&huart, buf, size, timeout) — передача данных с ожиданием
(+) UART_Receive(&huart, buf, size, timeout) — приём данных с ожиданием
- Режим Interrupt (non-blocking):
(+) UART_Transmit_IT(&huart, buf, size) — передача данных через прерывания
(+) UART_Receive_IT(&huart, buf, size) — приём данных через прерывания
6. Обработка прерываний UART:
(+) uart_irq_handler(&huart) — общий обработчик ISR для RX/TX FIFO, ошибок и Idle
- В обработчиках автоматически вызываются соответствующие callback-функции
и сбрасываются флаги
- Поддерживается очередь передачи для предотвращения потери данных
7. GPIO для UART:
(+) uart0_gpio_init()/uart0_gpio_deinit() — инициализация и деинициализация UART0
(+) uart1_gpio_init()/uart1_gpio_deinit() — инициализация и деинициализация UART1
8. Режимы работы:
(+) Polling (blocking) — функции блокируются до завершения передачи/приёма
(+) Interrupt (non-blocking) — передача/приём через прерывания, управление через callback
@endverbatim
******************************************************************************
*/
//-- Includes ------------------------------------------------------------------
#include "periph_config.h"
UART_HandleTypeDef huart0;
UART_HandleTypeDef huart1;
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 -------------------------------------------------------------------
//-- Peripheral init functions -------------------------------------------------
//-- UART Init functions -------------------------------------------------------
/**
* @brief Первичная инициализация UART (UART0 / UART1)
* @details Настройка UART и хендлов: GPIO, тактирование и прерывания
*/
void uart_init_first(void)
{
@@ -68,9 +145,9 @@ void uart_init_first(void)
/**
* @brief Инициализация Instance
* @param htmr указатель на хендл Instance
* @param NewConfig указатель на новую конфигурацию Instance, иначе используется та, что в структуре
* @brief Инициализация UART
* @param htmr указатель на хендл UART
* @param NewConfig указатель на новую конфигурацию UART, иначе используется та, что в структуре
* @retval OperationStatus OK - если всё хорошо, ERROR - если ошибка
*/
OperationStatus uart_init(UART_HandleTypeDef *huart, UART_ExtInit_TypeDef *NewConfig)
@@ -92,7 +169,52 @@ OperationStatus uart_init(UART_HandleTypeDef *huart, UART_ExtInit_TypeDef *NewCo
return OK;
}
OperationStatus uart_start(UART_HandleTypeDef *huart, UART_FIFOLevel_TypeDef TxFifoLevel, UART_FIFOLevel_TypeDef RxFifoLevel)
/**
* @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;
@@ -111,12 +233,36 @@ OperationStatus uart_start(UART_HandleTypeDef *huart, UART_FIFOLevel_TypeDef TxF
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;
}
OperationStatus uart_transmit(UART_HandleTypeDef *huart,
/**
* @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)
@@ -143,8 +289,15 @@ OperationStatus uart_transmit(UART_HandleTypeDef *huart,
return OK;
}
OperationStatus uart_receive(UART_HandleTypeDef *huart,
/**
* @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)
@@ -172,14 +325,37 @@ OperationStatus uart_receive(UART_HandleTypeDef *huart,
return OK;
}
OperationStatus uart_transmit_it(UART_HandleTypeDef *huart,
/**
* @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;
@@ -195,11 +371,17 @@ OperationStatus uart_transmit_it(UART_HandleTypeDef *huart,
__uart_fifo_transmit(huart, 1);
return OK;
#endif
}
OperationStatus uart_receive_it(UART_HandleTypeDef *huart,
/**
* @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)
{
@@ -228,12 +410,20 @@ OperationStatus uart_receive_it(UART_HandleTypeDef *huart,
return OK;
}
void uart_handler(UART_HandleTypeDef *huart)
//-- 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;
@@ -267,7 +457,7 @@ void uart_handler(UART_HandleTypeDef *huart)
UART_ITStatusClear(uart, UART_ITSource_RecieveTimeout);
huart->RxBusy = 0;
// Выключаем RX прерывания до следующего вызова uart_receive_it
// Выключаем RX прерывания до следующего вызова UART_Receive_IT
UART_ITCmd(uart,
UART_ITSource_RxFIFOLevel |
UART_ITSource_RecieveTimeout,
@@ -287,6 +477,7 @@ void uart_handler(UART_HandleTypeDef *huart)
UART_ITStatusClear(uart, UART_ITSource_TxFIFOLevel);
}
// Конец передачи
if (mis & UART_ITSource_TransmitDone)
{
@@ -295,46 +486,23 @@ void uart_handler(UART_HandleTypeDef *huart)
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 Установка коллбека Instance
* @param htmr указатель на хендл Instance
* @param cb_type Тип коллбека
* @param Callback Функция коллбека
* @retval void
* @brief Инициализация GPIO для UART0
*/
OperationStatus uart_set_callback(UART_HandleTypeDef* huart, UART_CallbackTypeDef cb_type, void (*Callback)(void))
{
if (!huart || !huart->Instance || !huart->Config)
return ERROR;
switch(cb_type)
{
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;
}
void uart0_gpio_init(void)
{
#if USE_UART0==1
@@ -363,7 +531,9 @@ void uart0_gpio_init(void)
}
#endif
}
/**
* @brief Деинициализация GPIO для UART0
*/
void uart0_gpio_deinit(void)
{
#if USE_UART0==1
@@ -387,7 +557,9 @@ void uart0_gpio_deinit(void)
}
#endif
}
/**
* @brief Инициализация GPIO для UART1
*/
void uart1_gpio_init(void)
{
#if USE_UART1==1
@@ -417,7 +589,9 @@ void uart1_gpio_init(void)
}
#endif
}
/**
* @brief Деинициализация GPIO для UART1
*/
void uart1_gpio_deinit(void)
{
#if USE_UART1==1
@@ -442,7 +616,13 @@ void uart1_gpio_deinit(void)
#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) &&
@@ -470,6 +650,12 @@ static int __uart_fifo_receive(UART_HandleTypeDef *huart, uint8_t it_mode)
}
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) &&
@@ -499,4 +685,65 @@ static int __uart_fifo_transmit(UART_HandleTypeDef *huart, uint8_t it_mode)
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

View File

@@ -1,11 +1,17 @@
/*==============================================================================
* Инициализация UART с использованием бибилотеки PLIB035
*------------------------------------------------------------------------------
* ЦНИИ СЭТ, Разваляев Алексей <wot890089@mail.ru>
*==============================================================================
* ЦНИИ СЭТ
*==============================================================================
*/
/**
******************************************************************************
* @file uart.h
* @author Разваляев Алексей
* @brief Драйвер UART на основе PLIB035.
* Данный файл содержит определения типов, структур и прототипы функций
* для работы с UART, включая:
* + Структуры и typedef для UART
* + Макросы для конфигурации направления передачи
* + Прототипы функций для инициализации и API драйвера
*
******************************************************************************
*/
#ifndef __UART_H
#define __UART_H
//-- Includes ------------------------------------------------------------------
@@ -13,92 +19,148 @@
#include "retarget_conf.h"
//-- Defines -------------------------------------------------------------------
// Дефайны для кольцевых буферов UART
#ifndef UART_RX_BUF_SIZE
#define UART_RX_BUF_SIZE 128
#endif
#ifndef UART_TX_BUF_SIZE
#define UART_TX_BUF_SIZE 128
#endif
// Дефайны для режима направления UART
#define UART_Direction_None DISABLE, DISABLE
#define UART_Direction_RxTx ENABLE, ENABLE
#define UART_Direction_Tx DISABLE, ENABLE
#define UART_Direction_Rx ENABLE, DISABLE
// Дефайны для режима пина 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
#define UART0_Tx_Pin GPIO_Pin_10 /**< PB10 UART0 Tx */
#define UART0_Rx_Pin GPIO_Pin_1 /**< 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
#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,
UART_Callback_Error,
}UART_CallbackTypeDef;
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; /* Стандартная конфигурация из PLIB */
// Callback функции
void (*RxCallback)(void); /* Вызывается при приеме полного сообщения */
void (*TxCallback)(void); /* Вызывается при окончании передачи */
void (*IdleCallback)(void); /* Вызывается при IDLE линии на RX */
void (*ErrCallback)(void); /* Вызывается при ошибке */
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 */
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*/
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*/
}UART_HandleTypeDef;
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);
OperationStatus uart_set_callback(UART_HandleTypeDef* huart, UART_CallbackTypeDef cb_type, void (*Callback)(void));
OperationStatus uart_start(UART_HandleTypeDef *huart, UART_FIFOLevel_TypeDef TxFifoLevel, UART_FIFOLevel_TypeDef RxFifoLevel);
OperationStatus uart_transmit(UART_HandleTypeDef *huart, uint8_t *buf, uint16_t size, uint32_t timeout);
OperationStatus uart_receive(UART_HandleTypeDef *huart, uint8_t *buf, uint16_t size, uint32_t timeout);
OperationStatus uart_transmit_it(UART_HandleTypeDef *huart, uint8_t *buf, uint16_t size);
OperationStatus uart_receive_it(UART_HandleTypeDef *huart, uint8_t *buf, uint16_t size);
void uart_handler(UART_HandleTypeDef *huart);
/* Общий обработчик прерываний 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*/

View File

@@ -1,11 +1,59 @@
/*==============================================================================
* Прерывания микроконтроллера 1921ВК035
*------------------------------------------------------------------------------
* ЦНИИ СЭТ, Разваляев Алексей <wot890089@mail.ru>
*==============================================================================
* ЦНИИ СЭТ
*==============================================================================
*/
/**
******************************************************************************
* @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"
@@ -15,60 +63,78 @@
/* 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_handler(&htmr0);
tmr_irq_handler(&htmr0);
}
#endif
#if USE_TMR1==1
void TMR1_IRQHandler(void)
{
tmr_handler(&htmr1);
tmr_irq_handler(&htmr1);
}
#endif
#if USE_TMR2==1
void TMR2_IRQHandler(void)
{
tmr_handler(&htmr2);
tmr_irq_handler(&htmr2);
}
#endif
#if USE_TMR3==1
void TMR3_IRQHandler(void)
{
tmr_handler(&htmr3);
tmr_irq_handler(&htmr3);
}
#endif
#if USE_UART0==1
void UART0_TD_IRQHandler(void)
{
uart_handler(&huart0);
uart_irq_handler(&huart0);
}
void UART0_RX_IRQHandler(void)
{
uart_handler(&huart0);
uart_irq_handler(&huart0);
}
void UART0_TX_IRQHandler(void)
{
uart_handler(&huart0);
uart_irq_handler(&huart0);
}
void UART0_E_RT_IRQHandler(void)
{
uart_handler(&huart0);
uart_irq_handler(&huart0);
}
#endif
#if USE_UART1==1
void UART1_TD_IRQHandler(void)
{
uart_handler(&huart1);
uart_irq_handler(&huart1);
}
void UART1_RX_IRQHandler(void)
{
uart_handler(&huart1);
uart_irq_handler(&huart1);
}
void UART1_TX_IRQHandler(void)
{
uart_handler(&huart1);
uart_irq_handler(&huart1);
}
void UART1_E_RT_IRQHandler(void)
{
uart_handler(&huart1);
uart_irq_handler(&huart1);
}
#endif
#if USE_SPI==1
void SPI_RO_RT_IRQHandler(void)
{
}
@@ -78,18 +144,28 @@ 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)
{
}
@@ -99,6 +175,8 @@ void PWM0_HD_IRQHandler(void)
void PWM0_TZ_IRQHandler(void)
{
}
#endif
#if USE_PWM1==1
void PWM1_IRQHandler(void)
{
}
@@ -108,6 +186,8 @@ void PWM1_HD_IRQHandler(void)
void PWM1_TZ_IRQHandler(void)
{
}
#endif
#if USE_PWM2==1
void PWM2_IRQHandler(void)
{
}
@@ -117,20 +197,30 @@ 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_handler(&hadc, ADC_SEQ_Num_0);
adc_seq_irq_handler(&hadc, ADC_SEQ_Num_0);
}
#endif
#if USE_ADC_SEQ1==1
void ADC_SEQ1_IRQHandler(void)
{
adc_seq_handler(&hadc, ADC_SEQ_Num_1);
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)
{
}
@@ -179,9 +269,7 @@ void CAN14_IRQHandler(void)
void CAN15_IRQHandler(void)
{
}
void FPU_IRQHandler(void)
{
}
#endif
void DMA_CH0_IRQHandler(void)
{
}
@@ -230,7 +318,7 @@ void DMA_CH14_IRQHandler(void)
void DMA_CH15_IRQHandler(void)
{
}
void WDT_IRQHandler(void)
void FPU_IRQHandler(void)
{
}
void RCU_IRQHandler(void)
@@ -320,5 +408,5 @@ void PendSV_Handler(void)
*/
void SysTick_Handler(void)
{
millis_inc();
sysclk_irq_handler();
}