/** * @file board_io.c * @brief Board-specific GPIO and shared low-level control helpers. */ #include "board_io.h" #define UI_LCD_CONTRAST_PWM_FREQUENCY_HZ 20000u #define UI_LCD_CONTRAST_PWM_PERIOD_COUNTS 1000u #define UI_LCD_CONTRAST_PWM_DUTY_PERMILLE 300u static TIM_HandleTypeDef g_ui_lcd_contrast_pwm_timer; static uint32_t board_io_get_apb1_timer_clock_hz(void); static void board_io_init_lcd_contrast_pwm(void); void board_io_enable_uart_rx_irq(void) { LL_USART_EnableIT_PE(USART1); LL_USART_EnableIT_RXNE(USART1); LL_USART_EnableIT_ERROR(USART1); NVIC_SetPriority(USART1_IRQn, 0); NVIC_EnableIRQ(USART1_IRQn); } void board_io_init_standalone_ui(void) { GPIO_InitTypeDef gpio_init = {0}; __HAL_RCC_GPIOG_CLK_ENABLE(); __HAL_RCC_GPIOD_CLK_ENABLE(); HAL_GPIO_WritePin(UI_LCD_RS_GPIO_Port, UI_LCD_RS_Pin | UI_LCD_E_Pin | UI_LCD_D4_Pin | UI_LCD_D5_Pin | UI_LCD_D6_Pin | UI_LCD_D7_Pin, GPIO_PIN_RESET); /* * A fixed contrast pin is only a compromise until a potentiometer is * added. Driving V0 low is the safest default for HD44780/SPLC780D-style * modules because it makes characters visible instead of appearing blank. */ HAL_GPIO_WritePin(UI_LCD_V0_REF_GPIO_Port, UI_LCD_V0_REF_Pin, GPIO_PIN_RESET); gpio_init.Pin = UI_LCD_RS_Pin | UI_LCD_E_Pin | UI_LCD_D4_Pin | UI_LCD_D5_Pin | UI_LCD_D6_Pin | UI_LCD_D7_Pin; gpio_init.Mode = GPIO_MODE_OUTPUT_PP; gpio_init.Pull = GPIO_NOPULL; gpio_init.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(GPIOG, &gpio_init); gpio_init.Pin = UI_LCD_V0_REF_Pin; gpio_init.Mode = GPIO_MODE_OUTPUT_PP; gpio_init.Pull = GPIO_NOPULL; gpio_init.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(UI_LCD_V0_REF_GPIO_Port, &gpio_init); gpio_init.Pin = UI_BUTTON_Pin; gpio_init.Mode = GPIO_MODE_INPUT; gpio_init.Pull = GPIO_PULLUP; HAL_GPIO_Init(UI_BUTTON_GPIO_Port, &gpio_init); board_io_init_lcd_contrast_pwm(); } static uint32_t board_io_get_apb1_timer_clock_hz(void) { uint32_t pclk1_hz = HAL_RCC_GetPCLK1Freq(); if ((RCC->CFGR & RCC_CFGR_PPRE1) == RCC_CFGR_PPRE1_DIV1) { return pclk1_hz; } return pclk1_hz * 2u; } static void board_io_init_lcd_contrast_pwm(void) { GPIO_InitTypeDef gpio_init = {0}; TIM_OC_InitTypeDef pwm_channel = {0}; uint32_t timer_clock_hz = board_io_get_apb1_timer_clock_hz(); uint32_t prescaler_divisor = timer_clock_hz / (UI_LCD_CONTRAST_PWM_FREQUENCY_HZ * UI_LCD_CONTRAST_PWM_PERIOD_COUNTS); uint32_t pulse_counts = ((UI_LCD_CONTRAST_PWM_PERIOD_COUNTS * UI_LCD_CONTRAST_PWM_DUTY_PERMILLE) + 999u) / 1000u; if (prescaler_divisor == 0u) { prescaler_divisor = 1u; } if (pulse_counts >= UI_LCD_CONTRAST_PWM_PERIOD_COUNTS) { pulse_counts = UI_LCD_CONTRAST_PWM_PERIOD_COUNTS - 1u; } __HAL_RCC_GPIOB_CLK_ENABLE(); __HAL_RCC_TIM4_CLK_ENABLE(); /* * This PWM output is intended for the LCD contrast input (V0) through a * simple RC low-pass filter. Keeping it local to board_io.c avoids * spreading board wiring assumptions into higher-level UI code. */ gpio_init.Pin = UI_LCD_CONTRAST_PWM_Pin; gpio_init.Mode = GPIO_MODE_AF_PP; gpio_init.Pull = GPIO_NOPULL; gpio_init.Speed = GPIO_SPEED_FREQ_LOW; gpio_init.Alternate = GPIO_AF2_TIM4; HAL_GPIO_Init(UI_LCD_CONTRAST_PWM_GPIO_Port, &gpio_init); g_ui_lcd_contrast_pwm_timer.Instance = TIM4; g_ui_lcd_contrast_pwm_timer.Init.Prescaler = (uint32_t)(prescaler_divisor - 1u); g_ui_lcd_contrast_pwm_timer.Init.CounterMode = TIM_COUNTERMODE_UP; g_ui_lcd_contrast_pwm_timer.Init.Period = UI_LCD_CONTRAST_PWM_PERIOD_COUNTS - 1u; g_ui_lcd_contrast_pwm_timer.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; g_ui_lcd_contrast_pwm_timer.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; if (HAL_TIM_PWM_Init(&g_ui_lcd_contrast_pwm_timer) != HAL_OK) { return; } pwm_channel.OCMode = TIM_OCMODE_PWM1; pwm_channel.Pulse = pulse_counts; pwm_channel.OCPolarity = TIM_OCPOLARITY_HIGH; pwm_channel.OCFastMode = TIM_OCFAST_DISABLE; if (HAL_TIM_PWM_ConfigChannel(&g_ui_lcd_contrast_pwm_timer, &pwm_channel, TIM_CHANNEL_3) != HAL_OK) { return; } (void)HAL_TIM_PWM_Start(&g_ui_lcd_contrast_pwm_timer, TIM_CHANNEL_3); } void board_io_reset_runtime_outputs(void) { HAL_GPIO_WritePin(EN_5V1_GPIO_Port, EN_5V1_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(EN_5V2_GPIO_Port, EN_5V2_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(LD1_EN_GPIO_Port, LD1_EN_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(LD2_EN_GPIO_Port, LD2_EN_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(REF0_EN_GPIO_Port, REF0_EN_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(REF2_ON_GPIO_Port, REF2_ON_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(TECEN1_GPIO_Port, TECEN1_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(TECEN2_GPIO_Port, TECEN2_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(TEC1_PD_GPIO_Port, TEC1_PD_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(TEC2_PD_GPIO_Port, TEC2_PD_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(ADC_MPD1_CS_GPIO_Port, ADC_MPD1_CS_Pin, GPIO_PIN_SET); HAL_GPIO_WritePin(ADC_MPD2_CS_GPIO_Port, ADC_MPD2_CS_Pin, GPIO_PIN_SET); HAL_GPIO_WritePin(SPI4_CNV_GPIO_Port, SPI4_CNV_Pin, GPIO_PIN_SET); HAL_GPIO_WritePin(SPI5_CNV_GPIO_Port, SPI5_CNV_Pin, GPIO_PIN_SET); HAL_GPIO_WritePin(DAC_LD1_CS_GPIO_Port, DAC_LD1_CS_Pin, GPIO_PIN_SET); HAL_GPIO_WritePin(DAC_LD2_CS_GPIO_Port, DAC_LD2_CS_Pin, GPIO_PIN_SET); HAL_GPIO_WritePin(DAC_TEC1_CS_GPIO_Port, DAC_TEC1_CS_Pin, GPIO_PIN_SET); HAL_GPIO_WritePin(DAC_TEC2_CS_GPIO_Port, DAC_TEC2_CS_Pin, GPIO_PIN_SET); HAL_GPIO_WritePin(AD9102_CS_GPIO_Port, AD9102_CS_Pin, GPIO_PIN_SET); HAL_GPIO_WritePin(AD9833_CS_GPIO_Port, AD9833_CS_Pin, GPIO_PIN_SET); } void board_io_configure_spi2_mode(uint32_t polarity, uint32_t phase) { if (LL_SPI_IsEnabled(SPI2)) { LL_SPI_Disable(SPI2); } LL_SPI_SetClockPolarity(SPI2, polarity); LL_SPI_SetClockPhase(SPI2, phase); if (!LL_SPI_IsEnabled(SPI2)) { LL_SPI_Enable(SPI2); } } bool board_io_is_usb_connected(void) { return HAL_GPIO_ReadPin(USB_FLAG_GPIO_Port, USB_FLAG_Pin) == GPIO_PIN_SET; } bool board_io_is_sd_card_present(void) { return HAL_GPIO_ReadPin(SDMMC1_EN_GPIO_Port, SDMMC1_EN_Pin) == GPIO_PIN_RESET; } bool board_io_is_standalone_ui_button_pressed(void) { return HAL_GPIO_ReadPin(UI_BUTTON_GPIO_Port, UI_BUTTON_Pin) == GPIO_PIN_RESET; } void board_io_set_supply_enabled(uint8_t supply_index, bool enabled) { GPIO_PinState pin_state = enabled ? GPIO_PIN_SET : GPIO_PIN_RESET; if (supply_index == 1u) { HAL_GPIO_WritePin(EN_5V1_GPIO_Port, EN_5V1_Pin, pin_state); } else if (supply_index == 2u) { HAL_GPIO_WritePin(EN_5V2_GPIO_Port, EN_5V2_Pin, pin_state); } } void board_io_set_laser_enabled(uint8_t laser_index, bool enabled) { GPIO_PinState pin_state = enabled ? GPIO_PIN_SET : GPIO_PIN_RESET; if (laser_index == 1u) { HAL_GPIO_WritePin(LD1_EN_GPIO_Port, LD1_EN_Pin, pin_state); } else if (laser_index == 2u) { HAL_GPIO_WritePin(LD2_EN_GPIO_Port, LD2_EN_Pin, pin_state); } } void board_io_set_reference_enabled(uint8_t reference_index, bool enabled) { GPIO_PinState pin_state = enabled ? GPIO_PIN_SET : GPIO_PIN_RESET; if (reference_index == 1u) { HAL_GPIO_WritePin(REF0_EN_GPIO_Port, REF0_EN_Pin, pin_state); } else if (reference_index == 2u) { HAL_GPIO_WritePin(REF2_ON_GPIO_Port, REF2_ON_Pin, pin_state); } } void board_io_set_tec_channel_enabled(uint8_t tec_index, bool enabled) { GPIO_PinState pin_state = enabled ? GPIO_PIN_SET : GPIO_PIN_RESET; if (tec_index == 1u) { HAL_GPIO_WritePin(TEC1_PD_GPIO_Port, TEC1_PD_Pin, pin_state); HAL_GPIO_WritePin(TECEN1_GPIO_Port, TECEN1_Pin, pin_state); } else if (tec_index == 2u) { HAL_GPIO_WritePin(TEC2_PD_GPIO_Port, TEC2_PD_Pin, pin_state); HAL_GPIO_WritePin(TECEN2_GPIO_Port, TECEN2_Pin, pin_state); } } void board_io_write_signal(GPIO_TypeDef *port, uint16_t pin, bool level_high) { HAL_GPIO_WritePin(port, pin, level_high ? GPIO_PIN_SET : GPIO_PIN_RESET); } void board_io_toggle_debug_pin(void) { HAL_GPIO_TogglePin(TEST_01_GPIO_Port, TEST_01_Pin); }