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

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

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

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