Files
VK035_Template/MDK-ARM/Core/App/gpio.c

418 lines
16 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
******************************************************************************
* @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;
}