0
mirror of https://gitlab.com/hyperglitch/jellyfish.git synced 2025-12-26 15:16:30 +00:00
2025-07-20 02:55:38 +02:00

353 lines
10 KiB
C

/*
* SPDX-FileCopyrightText: 2025 Igor Brkic <igor@hyperglitch.com>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include "cmsis_os.h"
#include "../lib/ST7789/st7789.h"
#include "../images/splash.h"
#include "jf_common.h"
#include "jf_ui.h"
#include "aux_power.h"
#include "jf_adc.h"
#include "jf_measure.h"
#include "jf_qspi.h"
#include "jf_routing.h"
#include "jf_source.h"
#include "main.h"
#include "stm32f4xx_it.h"
extern TIM_HandleTypeDef htim4; // LED PWM timer
static jf_ui_t user_input_state = {0};
typedef enum {
//LED_BLINK,
//ADC_GET,
DAC_SET,
ISET_DAC,
EXTIN,
//RANGE,
VAUX_SET,
UI_FUNC_COUNT
} ui_function_t;
const char ui_func_labels[UI_FUNC_COUNT][13] = {
//"LED ",
//"ADC_GET",
"VOUT ",
"ILIM ",
"EXTIN ",
//"RANGE ",
"AUX SET",
};
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin){
static uint32_t last_change = 0;
static uint32_t enc_state_last_change = 0;
static int32_t state_last = 0;
const uint32_t enc_debounce_ms = 10;
switch(GPIO_Pin){
case BUTTON_Pin:
const uint32_t btn = HAL_GPIO_ReadPin(BUTTON_GPIO_Port, BUTTON_Pin);
if(btn!=user_input_state.btn){
user_input_state.btn = btn;
user_input_state.btn_evt_id++;
}
return;
case ENCODER_SW_Pin:
const uint32_t enc_sw = HAL_GPIO_ReadPin(ENCODER_SW_GPIO_Port, ENCODER_SW_Pin);
if(enc_sw!=user_input_state.enc_sw){
user_input_state.enc_sw = enc_sw;
user_input_state.enc_sw_evt_id++;
}
return;
default: ;
}
if(GPIO_Pin!=ENCODER_A_Pin && GPIO_Pin!=ENCODER_B_Pin) return;
const uint32_t tm = HAL_GetTick();
if((tm-last_change) < enc_debounce_ms) return;
last_change = tm;
const bool A = HAL_GPIO_ReadPin(ENCODER_A_GPIO_Port, ENCODER_A_Pin);
const bool B = HAL_GPIO_ReadPin(ENCODER_B_GPIO_Port, ENCODER_B_Pin);
int32_t state = 0;
if(GPIO_Pin==ENCODER_A_Pin) {
if(B==0) state--;
else state++;
}
else if(GPIO_Pin==ENCODER_B_Pin) {
if(A==1) state--;
else state++;
}
if(state==0) return;
if(tm-enc_state_last_change > 50) { // slow down the changes
// prevent abrupt direction change
if(state==state_last) {
enc_state_last_change = tm;
user_input_state.encoder += state;
}
state_last = state;
}
}
void ui_init(){
HAL_GPIO_WritePin(DISP_BLK_GPIO_Port, DISP_BLK_Pin, 1);
ST7789_Init();
/*
HAL_Delay(500);
uint16_t back = 0x0000;
for(int i=0; i<8; i++){
back += 0x1111;
ST7789_Fill_Color(back);
HAL_Delay(10);
}
*/
ST7789_Fill_Color(WHITE);
//HAL_Delay(300);
ST7789_DrawImage(0, 0, 280, 240, (uint16_t *)img_splash);
HAL_Delay(1000);
ST7789_Fill_Color(BLACK);
// initialize UI controls
HAL_GPIO_EXTI_Callback(BUTTON_Pin);
HAL_GPIO_EXTI_Callback(ENCODER_SW_Pin);
HAL_GPIO_EXTI_Callback(ENCODER_A_Pin);
HAL_GPIO_EXTI_Callback(ENCODER_B_Pin);
}
void format_voltage_string(char *str, int32_t voltage) {
const int32_t full = voltage/1000;
const int32_t frac = (voltage-full*1000)/10;
snprintf(str, 25, "%ld.%02ldV", full, frac);
}
void ui_task(void *argument){
(void)argument;
uint32_t btn_last = 1;
uint32_t enc_sw_last = 1;
ui_function_t ui_func = DAC_SET;
bool refresh_display = true;
const uint32_t ch[3] = {TIM_CHANNEL_1, TIM_CHANNEL_2, TIM_CHANNEL_3};
int curr = 0;
int32_t p = htim4.Init.Period/4;
for(int i=0; i<3; i++){
__HAL_TIM_SET_COMPARE(&htim4, ch[i], 0);
HAL_TIM_PWM_Start(&htim4, ch[i]);
}
uint32_t last_adc_trigger = 0;
char volt_in_dc[25] = "xx.xxV";
char volt_in_usb0[25] = "xx.xxV";
char volt_in_usb1[25] = "xx.xxV";
int volt_in_selected = 0;
char aux_state_volt[25] = "xx.xxV";
char aux_state_current[25] = "xxxxmA";
char aux_meas_volt[25] = "xx.xxV";
char aux_meas_current[25] = "xxxxmA";
bool aux_enabled = false;
char source_enabled = false;
char source_state_volt[25] = "xx.xxV";
char source_state_current_lim[35] = "(xxxx, xxxx) mA";
char source_meas_volt[25] = "xx.xxV";
char source_meas_current[25] = "xxxxmA";
uint32_t enc_last = user_input_state.encoder;
// task loop
while(true) {
// LED fading (move to the separate task or function)
__HAL_TIM_SET_COMPARE(&htim4, ch[curr], p);
p-=100;
if(p<=0){
p = (int32_t)(htim4.Init.Period/4);
curr++;
if(curr==3) curr = 0;
}
osDelay(10);
// update encoder state for display
const uint32_t enc_sw = user_input_state.enc_sw;
uint32_t enc = user_input_state.encoder;
if (enc!=enc_last) {
enc_last = enc;
if (enc<0) {
enc = 0;
}
if (enc>150) {
enc = 150;
}
refresh_display = true;
}
if(HAL_GetTick()-last_adc_trigger > 300){
last_adc_trigger = HAL_GetTick();
const int32_t* adc_values = jf_adc_get_values();
// format and sort input voltages
format_voltage_string(volt_in_dc, adc_values[JF_ADC_VSENSE_DC]);
format_voltage_string(volt_in_usb0, adc_values[JF_ADC_VSENSE_USB0]);
format_voltage_string(volt_in_usb1, adc_values[JF_ADC_VSENSE_USB1]);
if (adc_values[JF_ADC_VSENSE_DC]>adc_values[JF_ADC_VSENSE_USB0] && adc_values[JF_ADC_VSENSE_DC]>adc_values[JF_ADC_VSENSE_USB1]) {
volt_in_selected = 0;
}
else {
if (adc_values[JF_ADC_VSENSE_USB0]>adc_values[JF_ADC_VSENSE_USB1]) {
volt_in_selected = 1;
}
else {
volt_in_selected = 2;
}
}
// TODO: add set voltage and current voltage
format_voltage_string(aux_meas_volt, jf_vaux_measure_voltage());
snprintf(aux_meas_current, sizeof(aux_meas_current), "%4ldmA", jf_vaux_measure_current());
format_voltage_string(aux_state_volt, jf_vaux_get_voltage());
snprintf(aux_state_current, sizeof(aux_state_current), "%4ldmA", jf_vaux_get_current_limit());
aux_enabled = jf_vaux_is_enabled();
source_enabled = jf_routing_output_status();
const jf_measure_data_t source_data = jf_measure_get_current_values();
format_voltage_string(source_state_volt, jf_source_get_voltage());
int32_t source_lim_pos, source_lim_neg;
jf_source_get_current_limit(&source_lim_pos, &source_lim_neg);
snprintf(source_state_current_lim, sizeof(source_state_current_lim), "(-%ld, %ld) mA", source_lim_neg, source_lim_pos);
format_voltage_string(source_meas_volt, (int32_t)(source_data.Vsense*1000));
snprintf(source_meas_current, sizeof(source_meas_current), "%4ldmA", (int32_t)(source_data.Isense1));
refresh_display = true;
}
if(enc_sw!=enc_sw_last) {
enc_sw_last = enc_sw;
if(!enc_sw) {
/*
if(ui_func==ADC_GET) {
printf("average frames per request: %ld\r\n", jf_measure_avg_frames_parsed());
printf("average buffer full: %ld\r\n", jf_measure_avg_buffer_full());
}
else if(ui_func==RANGE) {
uint8_t range = enc;
if(range>6) range = 6;
jf_measure_set_range(range, false);
printf("send RANGE command: %d\r\n", range);
}
else if(ui_func==LED_BLINK) { // FPGA command 0x05
jf_qspi_add_to_queue(
JF_CMD_LED_BLINK,
enc,
NULL,
0,
false
);
}
*/
if(ui_func==DAC_SET) { // FPGA command 0x06
jf_source_set_voltage(enc*100, false);
printf("DAC_SET to %ld mV\r\n", enc*100);
}
else if(ui_func==ISET_DAC) {
const uint16_t val = enc*5;
jf_source_set_current_limit(val, JF_CURRENT_LIMIT_BOTH, false);
printf("send ISET_DAC command value %d\r\n", val);
}
else if (ui_func==EXTIN) {
jf_routing_extin_en(enc>0);
}
else if (ui_func==VAUX_SET) {
if (enc<18) {
jf_vaux_enable(false);
}
else {
jf_vaux_enable(true);
jf_vaux_set_current_limit(80);
printf("set voltage to %ld, %d\r\n", enc*100, jf_vaux_set_voltage(enc*100, false));
}
}
}
}
// switch function
const uint32_t btn = user_input_state.btn;
if(btn!=btn_last) {
btn_last = btn;
if(!btn) {
ui_func++;
if(ui_func==UI_FUNC_COUNT) ui_func = DAC_SET;
printf("ui_func: %s\r\n", ui_func_labels[ui_func]);
refresh_display = true;
}
}
if(refresh_display){
refresh_display = false;
char buf[10];
ST7789_WriteString(5, 20, "VDC=", Font_7x10, WHITE, BLACK);
ST7789_WriteString(90, 20, "USB0=", Font_7x10, WHITE, BLACK);
ST7789_WriteString(180, 20, "USB1=", Font_7x10, WHITE, BLACK);
const int vtop_off = 35;
ST7789_WriteString(5+vtop_off, 20, volt_in_dc, Font_7x10, WHITE, BLACK);
ST7789_WriteString(90+vtop_off, 20, volt_in_usb0, Font_7x10, WHITE, BLACK);
ST7789_WriteString(180+vtop_off, 20, volt_in_usb1, Font_7x10, WHITE, BLACK);
ST7789_DrawRectangle(3, 17, 85, 32, volt_in_selected==0 ? LIGHTGREEN : BLACK);
ST7789_DrawRectangle(86, 17, 175, 32, volt_in_selected==1 ? LIGHTGREEN : BLACK);
ST7789_DrawRectangle(176, 17, 270, 32, volt_in_selected==2 ? LIGHTGREEN : BLACK);
ST7789_WriteString(5, 60, aux_enabled ? "AUX ON " : "AUX OFF", Font_11x18, aux_enabled ? RED : WHITE, BLACK);
ST7789_WriteString(100, 60, aux_meas_volt, Font_11x18, WHITE, BLACK);
ST7789_WriteString(175, 60, aux_meas_current, Font_11x18, WHITE, BLACK);
ST7789_WriteString(100, 80, aux_state_volt, Font_7x10, WHITE, BLACK);
ST7789_WriteString(175, 80, aux_state_current, Font_7x10, WHITE, BLACK);
ST7789_WriteString(5, 120, source_enabled ? "OUT ON " : "OUT OFF", Font_11x18, source_enabled ? RED : WHITE, BLACK);
ST7789_WriteString(100, 120, source_meas_volt, Font_11x18, WHITE, BLACK);
ST7789_WriteString(175, 120, source_meas_current, Font_11x18, WHITE, BLACK);
ST7789_WriteString(100, 140, source_state_volt, Font_7x10, WHITE, BLACK);
ST7789_WriteString(155, 140, source_state_current_lim, Font_7x10, WHITE, BLACK);
ST7789_WriteString(110, 150, HAL_GPIO_ReadPin(ENABLE_EXT_IN_GPIO_Port, ENABLE_EXT_IN_Pin) ? "EXT IN":" ", Font_16x26, GREEN, BLACK);
ST7789_WriteString(5, 175, ui_func_labels[ui_func], Font_16x26, LIGHTBLUE, BLACK);
ST7789_WriteString(5, 200, "enc:", Font_16x26, WHITE, BLACK);
ST7789_WriteString(75, 200, " ", Font_16x26, WHITE, BLACK); // clear
ST7789_WriteString(75, 200, itoa(enc, buf, 10), Font_16x26, WHITE, BLACK);
}
}
}