Plantilla Genérica para LEDs y Botones

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;
       }
   }
}

Etiquetas: LED botones STM32 temporizador Interrupciones

Publicado el 6-17 04:02