/*******************************************************************************
* File Name : main.c
*
* Description : GPIO Integration - Interactive LED Controller
* Combines Button + LED + ADC (Potentiometer)
*
* Features:
* - Potentiometer controls LED blink speed (50ms - 1000ms)
* - Button toggles between Normal and Fast mode (2x speed)
* - Non-blocking design pattern
* - Real-time ASCII status bar display
*
*******************************************************************************/
/*******************************************************************************
* Header Files
*******************************************************************************/
#include "cybsp.h"
#include "retarget_io_init.h"
#include <stdbool.h>
/*******************************************************************************
* Macros
******************************************************************************/
/* System Configuration */
#define CM55_BOOT_WAIT_TIME_USEC (10U)
/* ADC Configuration */
#define SAR_ADC_INDEX (0U)
#define SAR_ADC_SEQUENCER (0U)
#define SAR_ADC_CHANNEL (0U)
#define SAR_ADC_VREF_MV (1800U)
#define ADC_READ_INTERVAL_MS (100U)
/* Button Configuration */
#define USER_BTN_PORT CYBSP_USER_BTN1_PORT
#define USER_BTN_PIN CYBSP_USER_BTN1_PIN
#define DEBOUNCE_DELAY_MS (50U)
/* Button is Active LOW: pressed = 0 */
#define IS_BUTTON_PRESSED() (Cy_GPIO_Read(USER_BTN_PORT, USER_BTN_PIN) == 0)
/* LED Control Macros */
#define LED_ON() Cy_GPIO_Clr(CYBSP_USER_LED1_PORT, CYBSP_USER_LED1_PIN)
#define LED_OFF() Cy_GPIO_Set(CYBSP_USER_LED1_PORT, CYBSP_USER_LED1_PIN)
#define LED_TOGGLE() Cy_GPIO_Inv(CYBSP_USER_LED1_PORT, CYBSP_USER_LED1_PIN)
/* LED Blink Speed Limits */
#define BLINK_DELAY_MIN_MS (50U)
#define BLINK_DELAY_MAX_MS (1000U)
/* Mode Definitions */
typedef enum {
MODE_NORMAL = 0,
MODE_FAST = 1
} led_mode_t;
/*******************************************************************************
* Global Variables
*******************************************************************************/
static bool adc_initialized = false;
static led_mode_t current_mode = MODE_NORMAL;
static uint32_t mode_change_count = 0;
/*******************************************************************************
* Function Prototypes
*******************************************************************************/
static cy_rslt_t init_adc(void);
static int16_t read_potentiometer_mv(void);
static uint8_t get_pot_percentage(int16_t voltage_mv);
static bool read_button_with_debounce(void);
static uint32_t map_pot_to_delay(uint8_t pot_percentage);
static void print_status_bar(uint8_t percentage, uint32_t delay_ms, led_mode_t mode);
/*******************************************************************************
* Function Name: init_adc
********************************************************************************
* Summary:
* Initializes the SAR ADC using autonomous analog control.
*
* Return:
* cy_rslt_t - CY_RSLT_SUCCESS if initialization successful
*******************************************************************************/
static cy_rslt_t init_adc(void)
{
cy_rslt_t result;
result = Cy_AutAnalog_Init(&autonomous_analog_init);
if (CY_AUTANALOG_SUCCESS != result)
{
return result;
}
Cy_AutAnalog_SetInterruptMask(CY_AUTANALOG_INT_SAR0_RESULT);
Cy_AutAnalog_StartAutonomousControl();
adc_initialized = true;
return CY_RSLT_SUCCESS;
}
/*******************************************************************************
* Function Name: read_potentiometer_mv
********************************************************************************
* Summary:
* Reads the potentiometer value from ADC and converts to millivolts.
*
* Return:
* int16_t - Voltage in millivolts (0-1800 mV)
*******************************************************************************/
static int16_t read_potentiometer_mv(void)
{
int32_t adc_count;
int16_t voltage_mv;
adc_count = Cy_AutAnalog_SAR_ReadResult(SAR_ADC_INDEX,
CY_AUTANALOG_SAR_INPUT_GPIO,
SAR_ADC_CHANNEL);
voltage_mv = Cy_AutAnalog_SAR_CountsTo_mVolts(SAR_ADC_INDEX,
false,
SAR_ADC_SEQUENCER,
CY_AUTANALOG_SAR_INPUT_GPIO,
SAR_ADC_CHANNEL,
SAR_ADC_VREF_MV,
adc_count);
return voltage_mv;
}
/*******************************************************************************
* Function Name: get_pot_percentage
********************************************************************************
* Summary:
* Converts potentiometer voltage to percentage (0-100%).
*
* Parameters:
* voltage_mv - Input voltage in millivolts
*
* Return:
* uint8_t - Percentage value (0-100)
*******************************************************************************/
static uint8_t get_pot_percentage(int16_t voltage_mv)
{
int32_t percentage;
if (voltage_mv < 0)
{
voltage_mv = 0;
}
else if (voltage_mv > SAR_ADC_VREF_MV)
{
voltage_mv = SAR_ADC_VREF_MV;
}
percentage = ((int32_t)voltage_mv * 100) / SAR_ADC_VREF_MV;
return (uint8_t)percentage;
}
/*******************************************************************************
* Function Name: read_button_with_debounce
********************************************************************************
* Summary:
* Reads the user button with software debouncing.
* Uses delay-based debounce suitable for PSoC Edge.
*
* Return:
* bool - true if a valid button press was detected
*******************************************************************************/
static bool read_button_with_debounce(void)
{
static bool last_state = false;
bool current_state = IS_BUTTON_PRESSED();
/* Detect rising edge (button just pressed) */
if (current_state && !last_state)
{
/* Wait for debounce period */
Cy_SysLib_Delay(DEBOUNCE_DELAY_MS);
/* Re-read to confirm button is still pressed */
if (IS_BUTTON_PRESSED())
{
last_state = true;
return true;
}
}
/* Update state when button is released */
if (!current_state)
{
last_state = false;
}
return false;
}
/*******************************************************************************
* Function Name: map_pot_to_delay
********************************************************************************
* Summary:
* Maps potentiometer percentage (0-100%) to LED blink delay.
* 0% = slowest (1000ms), 100% = fastest (50ms)
* Inverted mapping: higher pot value = faster blink
*
* Parameters:
* pot_percentage - Potentiometer reading as percentage (0-100)
*
* Return:
* uint32_t - Delay in milliseconds (50-1000)
*******************************************************************************/
static uint32_t map_pot_to_delay(uint8_t pot_percentage)
{
uint32_t delay;
/* Map 0-100% to 1000-50ms (inverted) */
/* delay = MAX - (percentage * (MAX - MIN) / 100) */
delay = BLINK_DELAY_MAX_MS - ((uint32_t)pot_percentage *
(BLINK_DELAY_MAX_MS - BLINK_DELAY_MIN_MS) / 100);
/* Ensure minimum delay */
if (delay < BLINK_DELAY_MIN_MS)
{
delay = BLINK_DELAY_MIN_MS;
}
return delay;
}
/*******************************************************************************
* Function Name: print_status_bar
********************************************************************************
* Summary:
* Prints an ASCII status bar showing potentiometer percentage,
* current delay, and operating mode.
*
* Parameters:
* percentage - Current pot percentage
* delay_ms - Current blink delay in ms
* mode - Current operating mode
*******************************************************************************/
static void print_status_bar(uint8_t percentage, uint32_t delay_ms, led_mode_t mode)
{
int i;
int bar_length = percentage / 5; /* 0-20 characters */
printf("\r[");
for (i = 0; i < 20; i++)
{
if (i < bar_length)
{
printf("#");
}
else
{
printf("-");
}
}
printf("] %3d%% | %4lums | %s ",
percentage,
(unsigned long)delay_ms,
(mode == MODE_FAST) ? "FAST" : "NORMAL");
fflush(stdout);
}
/*******************************************************************************
* Function Name: main
********************************************************************************
* Summary:
* Main function - GPIO Integration Demo
*
* Features:
* - Potentiometer controls LED blink speed (50-1000ms)
* - Button toggles between Normal and Fast mode
* - Fast mode doubles the blink speed (halves the delay)
* - Real-time ASCII status bar display
*
* Return:
* int
*******************************************************************************/
int main(void)
{
cy_rslt_t result = CY_RSLT_SUCCESS;
int16_t voltage_mv;
uint8_t percentage;
uint32_t base_delay;
uint32_t actual_delay;
uint32_t elapsed_time = 0;
uint32_t last_toggle_time = 0;
uint32_t last_adc_time = 0;
/* Initialize the device and board peripherals */
result = cybsp_init();
if (CY_RSLT_SUCCESS != result)
{
handle_app_error();
}
/* Enable global interrupts */
__enable_irq();
/* Initialize retarget-io middleware */
init_retarget_io();
/* Clear screen */
printf("\x1b[2J\x1b[;H");
printf("***********************************************************\r\n");
printf(" PSOC Edge MCU: Interactive LED Controller \r\n");
printf("***********************************************************\r\n\n");
/* Initialize SAR ADC */
result = init_adc();
if (CY_RSLT_SUCCESS != result)
{
printf("ADC initialization failed!\r\n");
handle_app_error();
}
/* Print instructions */
printf("Controls:\r\n");
printf(" - Potentiometer (P15_1): Adjust LED blink speed\r\n");
printf(" - Button (USER_BTN1): Toggle Normal/Fast mode\r\n\n");
printf("Speed Range: 50ms (fast) to 1000ms (slow)\r\n");
printf("Fast Mode: 2x speed (half delay)\r\n\n");
/* Enable CM55 */
Cy_SysEnableCM55(MXCM55, CY_CM55_APP_BOOT_ADDR, CM55_BOOT_WAIT_TIME_USEC);
/* Initial ADC read */
voltage_mv = read_potentiometer_mv();
percentage = get_pot_percentage(voltage_mv);
base_delay = map_pot_to_delay(percentage);
actual_delay = base_delay;
printf("Starting LED Controller...\r\n\n");
for(;;)
{
/* Check button for mode toggle */
if (read_button_with_debounce())
{
/* Toggle mode */
if (current_mode == MODE_NORMAL)
{
current_mode = MODE_FAST;
}
else
{
current_mode = MODE_NORMAL;
}
mode_change_count++;
printf("\n>>> Mode changed to: %s (count: %lu)\r\n\n",
(current_mode == MODE_FAST) ? "FAST" : "NORMAL",
(unsigned long)mode_change_count);
}
/* Read ADC periodically */
if ((elapsed_time - last_adc_time) >= ADC_READ_INTERVAL_MS)
{
voltage_mv = read_potentiometer_mv();
percentage = get_pot_percentage(voltage_mv);
base_delay = map_pot_to_delay(percentage);
/* Apply mode multiplier */
if (current_mode == MODE_FAST)
{
actual_delay = base_delay / 2;
if (actual_delay < BLINK_DELAY_MIN_MS / 2)
{
actual_delay = BLINK_DELAY_MIN_MS / 2;
}
}
else
{
actual_delay = base_delay;
}
/* Print status bar */
print_status_bar(percentage, actual_delay, current_mode);
last_adc_time = elapsed_time;
}
/* Toggle LED based on calculated delay */
if ((elapsed_time - last_toggle_time) >= actual_delay)
{
LED_TOGGLE();
last_toggle_time = elapsed_time;
}
/* Small delay for loop timing */
Cy_SysLib_Delay(10);
elapsed_time += 10;
}
}
/* [] END OF FILE */