Descripción
Dado que a menudo se necesiat el parpadeo de LEDs, se ha elaborado una plantilal simple que utiliza botones para controlar LEDs. Es adecuada para múltiples LEDs parpadeando, o cuando la mayoría parpadea y unos pocos permanecen encendidos o apagados.
Se utilizan principalmente dos botones para controlar tres LEDs. Los requisitos de parpadeo están descritos en el código.
Código
main.c
// main.c
#include "stm32f10x.h" // Cabecera del dispositivo
#include "LED.h"
#include "Key.h"
#include "Timer.h"
int main(void)
{
LED_Init();
Key_Init();
Timer_Init();
while (1)
{
// Bucle principal vacío, el trabajo se realiza por interrupciones del temporizador
}
}
Temporizador (Timer)
// Timer.h
#ifndef __TIMER_H
#define __TIMER_H
void Timer_Init(void);
#endif
// Timer.c
#include "stm32f10x.h" // Cabecera del dispositivo
#include "LED.h"
#include "Key.h"
void Timer_Init(void)
{
/* Habilitar reloj */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
/* Configurar fuente de reloj */
TIM_InternalClockConfig(TIM2); // Seleccionar TIM2 como reloj interno (por defecto)
/* Inicializar unidad de base de tiempo */
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_Period = 100 - 1; // ARR: período de conteo
TIM_TimeBaseInitStructure.TIM_Prescaler = 720 - 1; // PSC: preescalador (72 MHz / 720 = 100 kHz)
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
/* Configurar interrupción por actualización */
TIM_ClearFlag(TIM2, TIM_FLAG_Update);
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
/* Agrupar prioridades NVIC */
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
/* Configurar NVIC */
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_Init(&NVIC_InitStructure);
/* Habilitar TIM2 */
TIM_Cmd(TIM2, ENABLE);
}
// Manejador de interrupción del temporizador (cada 1 ms)
void TIM2_IRQHandler(void)
{
if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
{
LED_Loop();
Key_Loop();
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
}
}
Botones (Key)
// Key.h
#ifndef __KEY_H
#define __KEY_H
void Key_Init(void);
void Key_Loop(void);
#endif
// Key.c
#include "stm32f10x.h" // Cabecera del dispositivo
#include "Key.h"
#include "LED.h"
// En esta plantilla hay tres botones: KEY1, KEY2, KEY3.
// KEY2 tiene funciones más completas: detección de pulsación corta, larga y múltiple (doble clic).
// KEY1 y KEY3 no tienen detección de pulsación múltiple.
// Variables externas definidas en LED.c
extern uint8_t LED1_FlashCount; // Número de parpadeos del LED1
extern uint8_t LED2_FlashCount; // Número de parpadeos del LED2
extern uint8_t LED3_FlashCount; // Número de parpadeos del LED3
extern uint8_t LED3_Mode; // Modo del LED3: 0 = parpadeo, 1 = encendido fijo
// KEY1: pulsación corta cambia LED1_FlashCount.
// KEY1: pulsación larga cambia LED2_FlashCount.
// KEY2: pulsación corta (sencilla) cambia LED3_FlashCount.
// KEY2: pulsación doble cambia LED3_Mode.
// Requisitos:
// 1. Al cambiar el número de parpadeos de un LED, solo ese LED parpadea para indicar que se está configurando.
// Si no hay operación durante 5 segundos, todos los LEDs parpadean juntos.
// 2. Cuando se presiona un botón, todos los LEDs se apagan hasta que se dispara un evento (pulsación larga, liberación).
// 3. Cuando LED3_Mode == 1, no se puede cambiar LED3_FlashCount.
#define KEY_NUM 2 // Cambiar según el número real de botones (máximo 3)
#if KEY_NUM >= 1
#define KEY1_GPIO_PIN GPIO_Pin_1 // Cambiar según el hardware
#define KEY1_GPIO_PORT GPIOB
#define KEY1_RCC_APB2Periph RCC_APB2Periph_GPIOB
#endif
#if KEY_NUM >= 2
#define KEY2_GPIO_PIN GPIO_Pin_11
#define KEY2_GPIO_PORT GPIOB
#define KEY2_RCC_APB2Periph RCC_APB2Periph_GPIOB
#endif
#if KEY_NUM >= 3
#define KEY3_GPIO_PIN GPIO_Pin_1
#define KEY3_GPIO_PORT GPIOB
#define KEY3_RCC_APB2Periph RCC_APB2Periph_GPIOB
#endif
typedef enum
{
#if KEY_NUM >= 1
KEY1 = 0,
#endif
#if KEY_NUM >= 2
KEY2 = 1,
#endif
#if KEY_NUM >= 3
KEY3 = 2,
#endif
} Key_Enum_TypeDef;
typedef struct
{
GPIO_TypeDef *port;
uint16_t pin;
uint32_t RCC_APB2Periph;
} Key_Struct_T;
const Key_Struct_T KeyArray[KEY_NUM] =
{
#if KEY_NUM >= 1
{KEY1_GPIO_PORT, KEY1_GPIO_PIN, KEY1_RCC_APB2Periph},
#endif
#if KEY_NUM >= 2
{KEY2_GPIO_PORT, KEY2_GPIO_PIN, KEY2_RCC_APB2Periph},
#endif
#if KEY_NUM >= 3
{KEY3_GPIO_PORT, KEY3_GPIO_PIN, KEY3_RCC_APB2Periph},
#endif
};
void KeyPin_Init(Key_Enum_TypeDef key)
{
/* Habilitar reloj del GPIO */
RCC_APB2PeriphClockCmd(KeyArray[key].RCC_APB2Periph, ENABLE);
/* Configurar GPIO como entrada con pull-up interno */
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = KeyArray[key].pin;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(KeyArray[key].port, &GPIO_InitStructure);
}
uint8_t Key_ReadState(Key_Enum_TypeDef key)
{
return GPIO_ReadInputDataBit(KeyArray[key].port, KeyArray[key].pin);
}
// Inicializar todos los botones
void Key_Init(void)
{
for (uint8_t i = 0; i < KEY_NUM; i++)
{
KeyPin_Init((Key_Enum_TypeDef)i);
}
}
// Obtener estado del botón (1 = presionado, 0 = liberado)
uint8_t Key_GetState(Key_Enum_TypeDef key)
{
return (Key_ReadState(key) == 0) ? 1 : 0;
}
// Esta función debe llamarse en la interrupción del temporizador cada 20 ms.
void Key_Loop(void)
{
static uint8_t sampleCounter = 0; // Contador para ejecutar cada 20 ms (20 iteraciones de 1 ms)
#if KEY_NUM >= 1
static uint8_t key1_NowState, key1_LastState;
static uint32_t key1_PressTime = 0;
static uint8_t key1_PressFlag = 0;
#endif
#if KEY_NUM >= 2
static uint8_t key2_NowState, key2_LastState;
static uint32_t key2_PressTime = 0;
static uint8_t key2_PressFlag = 0;
static uint8_t key2_ShortPressCount = 0;
static uint16_t key2_ReleaseTime = 0;
#endif
#if KEY_NUM >= 3
static uint8_t key3_NowState, key3_LastState;
static uint32_t key3_PressTime = 0;
static uint8_t key3_PressFlag = 0;
#endif
sampleCounter++;
if (sampleCounter < 20) return; // No procesar hasta que hayan pasado 20 ms
sampleCounter = 0;
// --------------- KEY1 ---------------
#if KEY_NUM >= 1
key1_LastState = key1_NowState;
key1_NowState = Key_GetState(KEY1);
if (key1_LastState == 0 && key1_NowState == 1) // Transición de liberado a presionado
{
key1_PressFlag = 1;
// Apagar todos los LEDs y entrar en modo de espera
LED_SetWorkMode(LED_Work_Mode_KeyPress);
}
else if (key1_LastState == 1 && key1_NowState == 1) // Mantenido presionado
{
key1_PressTime++;
}
else if (key1_LastState == 1 && key1_NowState == 0) // Liberación
{
if (key1_PressTime < (2000 / 20)) // Pulsación corta (menos de 2 segundos)
{
// Acción: incrementar contador de parpadeo del LED1
LED1_FlashCount++;
LED1_FlashCount %= 8; // 0..7
// Configurar parpadeo solo del LED1
LED_SetWorkMode(LED_Work_Mode_LED1_Flash);
// Configurar cambio automático a modo normal después de 5 segundos
LED_SetAutoSwitch(5000, LED_Work_Mode_Normal);
}
key1_PressFlag = 0;
key1_PressTime = 0;
}
// Pulsación larga (2 segundos, sin esperar liberación)
if ((key1_PressTime >= (2000 / 20)) && (key1_PressFlag == 1))
{
key1_PressFlag = 2; // Evitar disparo repetido
// Acción: incrementar contador de parpadeo del LED2
LED2_FlashCount++;
LED2_FlashCount %= 6;
LED_SetWorkMode(LED_Work_Mode_LED2_Flash);
LED_SetAutoSwitch(5000, LED_Work_Mode_Normal);
}
// Pulsación muy larga (5 segundos, opcional)
if ((key1_PressTime >= (5000 / 20)) && (key1_PressFlag == 2))
{
key1_PressFlag = 3;
// Aquí se podría añadir una acción específica para 5 segundos
}
#endif
// --------------- KEY2 ---------------
#if KEY_NUM >= 2
key2_LastState = key2_NowState;
key2_NowState = Key_GetState(KEY2);
if (key2_LastState == 0 && key2_NowState == 0) // Estado liberado prolongado
{
key2_ReleaseTime++;
}
else if (key2_LastState == 0 && key2_NowState == 1) // Presionado
{
key2_PressFlag = 1;
key2_ReleaseTime = 0; // Reiniciar tiempo de liberación
LED_SetWorkMode(LED_Work_Mode_KeyPress);
}
else if (key2_LastState == 1 && key2_NowState == 1) // Mantenido presionado
{
key2_PressTime++;
}
else if (key2_LastState == 1 && key2_NowState == 0) // Liberación
{
if (key2_PressTime < (2000 / 20)) // Pulsación corta
{
key2_ShortPressCount++; // Incrementar contador de pulsaciones cortas
}
else // Pulsación larga, reiniciar contador de pulsaciones múltiples
{
key2_ShortPressCount = 0;
}
key2_PressFlag = 0;
key2_PressTime = 0;
}
// Pulsación larga de 2 segundos
if ((key2_PressTime >= (2000 / 20)) && (key2_PressFlag == 1))
{
key2_PressFlag = 2;
// Acción para pulsación larga de KEY2 (actualmente vacía)
}
// Pulsación larga de 5 segundos
if ((key2_PressTime >= (5000 / 20)) && (key2_PressFlag == 2))
{
key2_PressFlag = 3;
// Acción para 5 segundos (vacía)
}
// Detección de pulsación múltiple (doble clic)
if (key2_ReleaseTime >= (500 / 20)) // Si han pasado más de 500 ms sin nueva pulsación
{
key2_ReleaseTime = 0;
if (key2_ShortPressCount == 1) // Una pulsación corta
{
key2_ShortPressCount = 0;
// Cambiar contador de parpadeo del LED3 (solo si no está en modo fijo)
if (LED3_Mode == 0)
{
LED3_FlashCount++;
LED3_FlashCount %= 4;
LED_SetWorkMode(LED_Work_Mode_LED3_Flash);
LED_SetAutoSwitch(5000, LED_Work_Mode_Normal);
}
}
else if (key2_ShortPressCount == 2) // Dos pulsaciones cortas (doble clic)
{
key2_ShortPressCount = 0;
// Cambiar modo del LED3 (encendido fijo / parpadeo)
LED3_Mode = (LED3_Mode == 0) ? 1 : 0;
LED_SetWorkMode(LED_Work_Mode_Normal);
}
else // Otras cantidades
{
key2_ShortPressCount = 0;
}
}
#endif
// --------------- KEY3 ---------------
#if KEY_NUM >= 3
key3_LastState = key3_NowState;
key3_NowState = Key_GetState(KEY3);
if (key3_LastState == 0 && key3_NowState == 1) // Presionado
{
key3_PressFlag = 1;
}
else if (key3_LastState == 1 && key3_NowState == 1) // Mantenido
{
key3_PressTime++;
}
else if (key3_LastState == 1 && key3_NowState == 0) // Liberación
{
if (key3_PressTime < (2000 / 20)) // Pulsación corta
{
// Acción para pulsación corta de KEY3 (vacía por ahora)
}
key3_PressFlag = 0;
key3_PressTime = 0;
}
// Pulsación larga de 2 segundos
if ((key3_PressTime >= (2000 / 20)) && (key3_PressFlag == 1))
{
key3_PressFlag = 2;
// Acción para pulsación larga de KEY3
}
// Pulsación larga de 5 segundos
if ((key3_PressTime >= (5000 / 20)) && (key3_PressFlag == 2))
{
key3_PressFlag = 3;
}
#endif
// --- Salida de la sección de botones: restaurar modo normal si todos los botones están liberados ---
#if KEY_NUM >= 3
if (key3_PressFlag == 0)
{
#endif
#if KEY_NUM >= 2
if (key2_PressFlag == 0)
{
#endif
#if KEY_NUM >= 1
if (key1_PressFlag == 0)
{
if (LED_GetWorkMode() == LED_Work_Mode_KeyPress)
{
LED_SetWorkMode(LED_Work_Mode_Normal);
}
}
#endif
#if KEY_NUM >= 2
}
#endif
#if KEY_NUM >= 3
}
#endif
}
LED
// LED.h
#ifndef __LED_H__
#define __LED_H__
#include "stdint.h"
// Estados de la máquina de estados para el ciclo de parpadeo
typedef enum {
LED_Step_SetMode = 0, // Fase de configuración del modo de trabajo
LED_Step_Prepare, // Fase preparatoria (pausa antes de empezar)
LED_Step_Running, // Fase de ejecución (parpadeo)
LED_Step_Stop // Fase de parada (pausa entre ciclos)
} LED_Step_T;
extern LED_Step_T LED_CurrentStep; // Paso actual
// Modos de trabajo de los LEDs
typedef enum {
LED_Work_Mode_Normal = 0, // Funcionamiento normal: todos parpadean según sus contadores
LED_Work_Mode_AllOn, // Todos encendidos fijos
LED_Work_Mode_AllOff, // Todos apagados
LED_Work_Mode_KeyPress, // Al presionar un botón: todos apagados
LED_Work_Mode_LED1_Flash, // Solo LED1 parpadea (indicando configuración)
LED_Work_Mode_LED2_Flash, // Solo LED2 parpadea
LED_Work_Mode_LED3_Flash // Solo LED3 parpadea
} LED_Work_Mode_T;
extern LED_Work_Mode_T LED_WorkMode;
// Estructura para cambio automático de modo (por ejemplo, después de un tiempo de inactividad)
typedef struct {
uint8_t enable; // 1: cambio automático activado
LED_Work_Mode_T targetMode; // Modo al que cambiar
uint32_t delayMs; // Tiempo de espera en ms antes del cambio
uint32_t counter; // Contador interno
} LED_AutoSwitch_T;
extern LED_AutoSwitch_T LED_AutoSwitch;
void LED_Init(void);
void LED_Loop(void);
// Funciones auxiliares añadidas para simplificar el código de Key.c
void LED_SetWorkMode(LED_Work_Mode_T newMode);
LED_Work_Mode_T LED_GetWorkMode(void);
void LED_SetAutoSwitch(uint32_t delayMs, LED_Work_Mode_T targetMode);
#endif
// LED.c
#include "stm32f10x.h" // Cabecera del dispositivo
#include "LED.h"
// Variables globales para controlar los LEDs (pueden ser modificadas desde Key.c)
uint8_t LED1_FlashCount = 0; // 0..7 (número de parpadeos: cuenta + 1)
uint8_t LED2_FlashCount = 0; // 0..5
uint8_t LED3_FlashCount = 0; // 0..3
uint8_t LED3_Mode = 0; // 0 = parpadeo, 1 = encendido fijo
// Cada ronda: los tres LEDs parpadean sincronizadamente según su cuenta.
// Los que terminan antes esperan (apagados). Cuando todos han terminado, comienza la siguiente ronda.
#define LED_FLASH_NUM 3 // Número de LEDs (máximo 5)
#if LED_FLASH_NUM >= 1
#define LED1_GPIO_PIN GPIO_Pin_1 // Cambiar según hardware
#define LED1_GPIO_PORT GPIOA
#define LED1_RCC_APB2Periph RCC_APB2Periph_GPIOA
#endif
#if LED_FLASH_NUM >= 2
#define LED2_GPIO_PIN GPIO_Pin_2
#define LED2_GPIO_PORT GPIOA
#define LED2_RCC_APB2Periph RCC_APB2Periph_GPIOA
#endif
#if LED_FLASH_NUM >= 3
#define LED3_GPIO_PIN GPIO_Pin_3
#define LED3_GPIO_PORT GPIOA
#define LED3_RCC_APB2Periph RCC_APB2Periph_GPIOA
#endif
#if LED_FLASH_NUM >= 4
#define LED4_GPIO_PIN GPIO_Pin_4
#define LED4_GPIO_PORT GPIOA
#define LED4_RCC_APB2Periph RCC_APB2Periph_GPIOA
#endif
#if LED_FLASH_NUM >= 5
#define LED5_GPIO_PIN GPIO_Pin_5
#define LED5_GPIO_PORT GPIOA
#define LED5_RCC_APB2Periph RCC_APB2Periph_GPIOA
#endif
typedef enum
{
#if LED_FLASH_NUM >= 1
LED1 = 0,
#endif
#if LED_FLASH_NUM >= 2
LED2 = 1,
#endif
#if LED_FLASH_NUM >= 3
LED3 = 2,
#endif
#if LED_FLASH_NUM >= 4
LED4 = 3,
#endif
#if LED_FLASH_NUM >= 5
LED5 = 4,
#endif
} Led_Enum_TypeDef;
typedef struct
{
GPIO_TypeDef *port;
uint16_t pin;
uint32_t RCC_APB2Periph;
} LED_Struct_T;
const LED_Struct_T LED_Array[LED_FLASH_NUM] =
{
#if LED_FLASH_NUM >= 1
{LED1_GPIO_PORT, LED1_GPIO_PIN, LED1_RCC_APB2Periph},
#endif
#if LED_FLASH_NUM >= 2
{LED2_GPIO_PORT, LED2_GPIO_PIN, LED2_RCC_APB2Periph},
#endif
#if LED_FLASH_NUM >= 3
{LED3_GPIO_PORT, LED3_GPIO_PIN, LED3_RCC_APB2Periph},
#endif
#if LED_FLASH_NUM >= 4
{LED4_GPIO_PORT, LED4_GPIO_PIN, LED4_RCC_APB2Periph},
#endif
#if LED_FLASH_NUM >= 5
{LED5_GPIO_PORT, LED5_GPIO_PIN, LED5_RCC_APB2Periph},
#endif
};
// Modos de funcionamiento interno para cada LED
typedef enum {
LED_RunMode_Flash = 0, // Parpadeo
LED_RunMode_On, // Encendido fijo
LED_RunMode_Off // Apagado
} LED_RunMode_T;
typedef enum {
LED_RunState_Normal = 0, // Normal: sigue el conteo de parpadeos
LED_RunState_None, // No participa en el conteo ni en el stop, se ejecuta continuamente
LED_RunState_ToEnd // No participa en el conteo, se mantiene hasta que todos los demás terminen
} LED_RunState_T;
typedef struct {
LED_RunMode_T runMode;
LED_RunState_T runState;
uint8_t runCount; // Número total de transiciones (parpadeos * 2)
uint8_t finished; // 1 si ya completó su ejecución en la ronda actual
} LED_Status_T;
LED_Status_T LED_Status[LED_FLASH_NUM];
// Variables de estado globales
LED_Step_T LED_CurrentStep = LED_Step_SetMode;
LED_Step_T LED_PreviousStep = LED_Step_SetMode;
LED_Work_Mode_T LED_WorkMode = LED_Work_Mode_Normal;
LED_AutoSwitch_T LED_AutoSwitch = {0, (LED_Work_Mode_T)0, 0, 0};
// Tiempos de las fases (en ms, considerando que LED_Loop se llama cada 1 ms)
static const uint32_t PREPARE_DURATION = 500; // Duración de la fase preparatoria
static const uint32_t FLASH_PERIOD = 250; // Periodo entre transiciones de parpadeo
static const uint32_t STOP_DURATION = 500; // Duración de la fase de parada
// Variables auxiliares para las fases
static uint32_t prepareCounter = 0;
static uint32_t runningCounter = 0;
static uint8_t flashToggleCount = 0; // Contador de transiciones actuales en la fase running
static uint8_t runningFinishFlag = 0; // LEDs que han terminado en la ronda actual
static uint32_t stopCounter = 0;
// Funciones privadas para manejo de GPIO
static void LED_Pin_Init(Led_Enum_TypeDef led)
{
RCC_APB2PeriphClockCmd(LED_Array[led].RCC_APB2Periph, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = LED_Array[led].pin;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(LED_Array[led].port, &GPIO_InitStructure);
}
static void LED_On(Led_Enum_TypeDef led)
{
GPIO_ResetBits(LED_Array[led].port, LED_Array[led].pin); // Ajustar según esquemático (activo bajo)
}
static void LED_Off(Led_Enum_TypeDef led)
{
GPIO_SetBits(LED_Array[led].port, LED_Array[led].pin); // Ajustar según esquemático
}
// Inicialización general de LEDs
void LED_Init(void)
{
for (uint8_t i = 0; i < LED_FLASH_NUM; i++)
{
LED_Pin_Init((Led_Enum_TypeDef)i);
LED_Off((Led_Enum_TypeDef)i);
}
}
// Establecer modo de trabajo (llamado desde Key.c)
void LED_SetWorkMode(LED_Work_Mode_T newMode)
{
LED_WorkMode = newMode;
LED_CurrentStep = LED_Step_SetMode;
}
LED_Work_Mode_T LED_GetWorkMode(void)
{
return LED_WorkMode;
}
// Configurar cambio automático después de un tiempo
void LED_SetAutoSwitch(uint32_t delayMs, LED_Work_Mode_T targetMode)
{
LED_AutoSwitch.enable = 1;
LED_AutoSwitch.targetMode = targetMode;
LED_AutoSwitch.delayMs = delayMs;
LED_AutoSwitch.counter = 0;
}
// Bucle principal de los LEDs: debe llamarse cada 1 ms desde la interrupción del temporizador
void LED_Loop(void)
{
// --- Paso 1: Configurar el modo de trabajo (se ejecuta solo cuando el paso es SetMode) ---
if (LED_CurrentStep == LED_Step_SetMode)
{
switch (LED_WorkMode)
{
case LED_Work_Mode_Normal:
// Configurar cada LED según sus contadores y modos
LED_Status[LED1].runMode = LED_RunMode_Flash;
LED_Status[LED1].runState = LED_RunState_Normal;
LED_Status[LED1].runCount = (LED1_FlashCount + 1) * 2; // Cada parpadeo = 2 transiciones
LED_Status[LED1].finished = 0;
LED_Status[LED2].runMode = LED_RunMode_Flash;
LED_Status[LED2].runState = LED_RunState_Normal;
LED_Status[LED2].runCount = (LED2_FlashCount + 1) * 2;
LED_Status[LED2].finished = 0;
if (LED3_Mode == 0) // Parpadeo
{
LED_Status[LED3].runMode = LED_RunMode_Flash;
LED_Status[LED3].runState = LED_RunState_Normal;
LED_Status[LED3].runCount = (LED3_FlashCount + 1) * 2;
LED_Status[LED3].finished = 0;
}
else // Encendido fijo
{
LED_Status[LED3].runMode = LED_RunMode_On;
LED_Status[LED3].runState = LED_RunState_None;
LED_Status[LED3].runCount = 0;
LED_Status[LED3].finished = 0;
}
break;
case LED_Work_Mode_AllOff:
case LED_Work_Mode_KeyPress:
for (uint8_t i = 0; i < LED_FLASH_NUM; i++)
{
LED_Status[i].runMode = LED_RunMode_Off;
LED_Status[i].runState = LED_RunState_None;
LED_Status[i].runCount = 0;
LED_Status[i].finished = 0;
}
break;
case LED_Work_Mode_LED1_Flash:
for (uint8_t i = 0; i < LED_FLASH_NUM; i++)
{
if (i == LED1)
{
LED_Status[i].runMode = LED_RunMode_Flash;
LED_Status[i].runState = LED_RunState_Normal;
LED_Status[i].runCount = (LED1_FlashCount + 1) * 2;
LED_Status[i].finished = 0;
}
else
{
LED_Status[i].runMode = LED_RunMode_Off;
LED_Status[i].runState = LED_RunState_None;
LED_Status[i].runCount = 0;
LED_Status[i].finished = 0;
}
}
break;
case LED_Work_Mode_LED2_Flash:
for (uint8_t i = 0; i < LED_FLASH_NUM; i++)
{
if (i == LED2)
{
LED_Status[i].runMode = LED_RunMode_Flash;
LED_Status[i].runState = LED_RunState_Normal;
LED_Status[i].runCount = (LED2_FlashCount + 1) * 2;
LED_Status[i].finished = 0;
}
else
{
LED_Status[i].runMode = LED_RunMode_Off;
LED_Status[i].runState = LED_RunState_None;
LED_Status[i].runCount = 0;
LED_Status[i].finished = 0;
}
}
break;
case LED_Work_Mode_LED3_Flash:
for (uint8_t i = 0; i < LED_FLASH_NUM; i++)
{
if (i == LED3)
{
LED_Status[i].runMode = LED_RunMode_Flash;
LED_Status[i].runState = LED_RunState_Normal;
LED_Status[i].runCount = (LED3_FlashCount + 1) * 2;
LED_Status[i].finished = 0;
}
else
{
LED_Status[i].runMode = LED_RunMode_Off;
LED_Status[i].runState = LED_RunState_None;
LED_Status[i].runCount = 0;
LED_Status[i].finished = 0;
}
}
break;
case LED_Work_Mode_AllOn:
for (uint8_t i = 0; i < LED_FLASH_NUM; i++)
{
LED_Status[i].runMode = LED_RunMode_On;
LED_Status[i].runState = LED_RunState_None;
LED_Status[i].runCount = 0;
LED_Status[i].finished = 0;
}
break;
default:
break;
}
LED_PreviousStep = LED_CurrentStep;
LED_CurrentStep = LED_Step_Prepare;
}
// --- Paso 2: Fase preparatoria (pausa antes de comenzar el parpadeo) ---
if (LED_CurrentStep == LED_Step_Prepare)
{
if (LED_PreviousStep != LED_CurrentStep)
{
LED_PreviousStep = LED_CurrentStep;
prepareCounter = 0;
// Apagar todos los LEDs
for (uint8_t i = 0; i < LED_FLASH_NUM; i++)
{
LED_Off((Led_Enum_TypeDef)i);
}
}
prepareCounter++;
if (prepareCounter >= PREPARE_DURATION)
{
prepareCounter = 0;
LED_CurrentStep = LED_Step_Running;
}
}
// --- Paso 3: Fase de ejecución (parpadeo) ---
if (LED_CurrentStep == LED_Step_Running)
{
if (LED_PreviousStep != LED_CurrentStep)
{
LED_PreviousStep = LED_CurrentStep;
runningCounter = 0;
flashToggleCount = 0; // Iniciar conteo de transiciones
}
runningCounter++;
if (runningCounter >= FLASH_PERIOD)
{
runningCounter = 0;
flashToggleCount++;
// Evaluar el estado de cada LED
runningFinishFlag = 0;
for (uint8_t i = 0; i < LED_FLASH_NUM; i++)
{
if (LED_Status[i].finished == 0)
{
switch (LED_Status[i].runMode)
{
case LED_RunMode_Flash:
if (LED_Status[i].runState == LED_RunState_Normal)
{
if (flashToggleCount <= LED_Status[i].runCount)
{
// Alternar LED según paridad
if (flashToggleCount % 2 == 1)
LED_On((Led_Enum_TypeDef)i);
else
LED_Off((Led_Enum_TypeDef)i);
}
else
{
LED_Off((Led_Enum_TypeDef)i);
LED_Status[i].finished = 1;
}
}
break;
case LED_RunMode_On:
if (LED_Status[i].runState == LED_RunState_Normal)
{
if (flashToggleCount <= LED_Status[i].runCount)
LED_On((Led_Enum_TypeDef)i);
else
{
LED_Off((Led_Enum_TypeDef)i);
LED_Status[i].finished = 1;
}
}
else if (LED_Status[i].runState == LED_RunState_None ||
LED_Status[i].runState == LED_RunState_ToEnd)
{
LED_On((Led_Enum_TypeDef)i);
LED_Status[i].finished = 1;
}
break;
case LED_RunMode_Off:
LED_Off((Led_Enum_TypeDef)i);
LED_Status[i].finished = 1;
break;
}
}
else
{
runningFinishFlag++;
}
}
// Si todos los LEDs han terminado, pasar a la fase de parada
if (runningFinishFlag >= LED_FLASH_NUM)
{
LED_CurrentStep = LED_Step_Stop;
}
}
}
// --- Paso 4: Fase de parada (pausa entre ciclos) ---
if (LED_CurrentStep == LED_Step_Stop)
{
if (LED_PreviousStep != LED_CurrentStep)
{
LED_PreviousStep = LED_CurrentStep;
stopCounter = 0;
// Apagar LEDs que estaban en estado 'ToEnd' (no implementado realmente en este flujo)
for (uint8_t i = 0; i < LED_FLASH_NUM; i++)
{
if (LED_Status[i].runState == LED_RunState_ToEnd)
{
LED_Off((Led_Enum_TypeDef)i);
}
LED_Status[i].finished = 0; // Reiniciar para la siguiente ronda
}
}
stopCounter++;
if (stopCounter >= STOP_DURATION)
{
stopCounter = 0;
LED_CurrentStep = LED_Step_Running; // Iniciar la siguiente ronda sin cambiar el modo
}
}
// --- Paso 5: Verificar cambio automático de modo ---
if (LED_AutoSwitch.enable == 1)
{
LED_AutoSwitch.counter++;
if (LED_AutoSwitch.counter >= LED_AutoSwitch.delayMs)
{
LED_AutoSwitch.counter = 0;
LED_AutoSwitch.enable = 0;
LED_WorkMode = LED_AutoSwitch.targetMode;
LED_CurrentStep = LED_Step_SetMode;
}
}
}