state machine encoder logic and first simple menu

This commit is contained in:
Alessandro Mauri 2026-05-04 00:47:57 +02:00
parent 65ac37fd62
commit 5e12e9a4a0

125
fw/main.c
View File

@ -69,20 +69,64 @@ void handle_usbfs_input(int numbytes, uint8_t *data)
} }
// triggered on the falling edge of the rotary encoder PIN_A /*
* __ ____ ____
* A: |____| |____| |____
* ____ ____ __
* B: ____| |____| |____|
* ^ ^ ^ ^ ^ ^ ^ ^ ^ ^
* f r r f f r r f f r
*/
void update_encoder(void)
{
// 0 = 00, 1 = 01, 2 = 10, 3 = 11
static uint8_t last_state = 0;
// Lookup table: state_table[last_state][current_state]
// 0 = invalid move or bounce (ignore)
// 1 = valid forward step
// -1 = valid backward step
static const int8_t state_table[4][4] = {
{ 0, 1, -1, 0}, // from 00
{-1, 0, 0, 1}, // from 01
{ 1, 0, 0, -1}, // from 10
{ 0, -1, 1, 0} // from 11
};
// FIXME: even with interrupts disabled and debounce, a single detent triggers
// multiple interrupts, leading to multiple encoder updates in a short time.
bool a = funDigitalRead(PIN_ENC_A);
bool b = funDigitalRead(PIN_ENC_B);
Delay_Us(100);
if (a != funDigitalRead(PIN_ENC_A) || b != funDigitalRead(PIN_ENC_B))
return; // debounce
uint8_t current_state = (a << 1) | b;
// Find the movement direction based on transition
encoder += state_table[last_state][current_state];
// Save current state for the next interrupt
last_state = current_state;
}
// Attached to PIN_ENC_A rising and falling edges
void EXTI7_0_IRQHandler(void) __attribute__((interrupt));
void EXTI7_0_IRQHandler(void)
{
__disable_irq();
update_encoder();
EXTI->INTFR = EXTI_Line3;
__enable_irq();
}
// Attached to PIN_ENC_B rising and falling edges
void EXTI15_8_IRQHandler(void) __attribute__((interrupt)); void EXTI15_8_IRQHandler(void) __attribute__((interrupt));
void EXTI15_8_IRQHandler(void) void EXTI15_8_IRQHandler(void)
{ {
uint32_t now = funSysTick32(); __disable_irq();
if (now - last_interrupt > ENCODER_DEBOUNCE) { update_encoder();
last_interrupt = now;
if (funDigitalRead(PIN_ENC_A)) {
encoder++;
} else {
encoder--;
}
}
EXTI->INTFR = EXTI_Line11; EXTI->INTFR = EXTI_Line11;
__enable_irq();
} }
@ -354,12 +398,21 @@ __attribute__((noreturn)) int main(void)
// Configure the IO as an interrupt. // Configure the IO as an interrupt.
// PIN_ENC_B is on port B, channel 11 // PIN_ENC_B is on port B, channel 11
AFIO->EXTICR1 = AFIO_EXTICR1_EXTI11_PB; // Port B channel (pin) 11 AFIO->EXTICR1 |= AFIO_EXTICR1_EXTI11_PB; // Port B channel (pin) 11
EXTI->INTENR = EXTI_INTENR_MR11; // Enable EXT11 EXTI->INTENR |= EXTI_INTENR_MR11; // Enable EXT11
EXTI->FTENR = EXTI_FTENR_TR11; // Falling edge trigger EXTI->FTENR |= EXTI_FTENR_TR11; // Falling edge trigger
EXTI->RTENR |= EXTI_RTENR_TR11; // Rising edge trigger
// enable interrupt // enable interrupt
NVIC_EnableIRQ(EXTI15_8_IRQn); NVIC_EnableIRQ(EXTI15_8_IRQn);
// PIN_ENC_A is on port B, channel 3
AFIO->EXTICR1 |= AFIO_EXTICR1_EXTI3_PB; // Port B channel (pin) 3
EXTI->INTENR |= EXTI_INTENR_MR3; // Enable EXT3
EXTI->FTENR |= EXTI_FTENR_TR3; // Falling edge trigger
EXTI->RTENR |= EXTI_RTENR_TR3; // Rising edge trigger
// enable interrupt
NVIC_EnableIRQ(EXTI7_0_IRQn);
Delay_Ms(500); Delay_Ms(500);
u8g2 = display_init(); u8g2 = display_init();
@ -388,7 +441,7 @@ __attribute__((noreturn)) int main(void)
// TODO: let the user decide the power profile // TODO: let the user decide the power profile
pd_profile.set_temp = 200; pd_profile.set_temp = 200;
pd_profile.set_power = 95; // Slightly below max power to avoid overloading pd_profile.set_power = 60; // Slightly below max power to avoid overloading
pd_profile.tip_r = 2500; // TODO: tip check and resistance calculator pd_profile.tip_r = 2500; // TODO: tip check and resistance calculator
pd_profile.max_duty = MIN( pd_profile.max_duty = MIN(
(100*(u32)isqrt(( (100*(u32)isqrt((
@ -416,6 +469,10 @@ __attribute__((noreturn)) int main(void)
} }
enum {
STATE_MENU,
STATE_HEATING,
} state = STATE_MENU;
for (;;) { for (;;) {
u32 start = funSysTick32(); u32 start = funSysTick32();
poll_input(); // usb poll_input(); // usb
@ -428,6 +485,9 @@ __attribute__((noreturn)) int main(void)
static bool pwm = false; // PWM status static bool pwm = false; // PWM status
static bool enabled = false; // Power electronics enabled static bool enabled = false; // Power electronics enabled
static uint8_t count = 0; // Loop cycles with PWM on static uint8_t count = 0; // Loop cycles with PWM on
static s32 elapsed = 0;
static bool prev_btn = true;
bool btn = funDigitalRead(PIN_BTN);
if (has_pd) { if (has_pd) {
static uint16_t vbus_mv, current_ma; static uint16_t vbus_mv, current_ma;
@ -446,6 +506,26 @@ __attribute__((noreturn)) int main(void)
tip_temp_c = I16_FP_EMA_K2(tip_temp_c, (tip_mv*TC_CONV_NOM)/TC_CONV_DEN) + temp_c; tip_temp_c = I16_FP_EMA_K2(tip_temp_c, (tip_mv*TC_CONV_NOM)/TC_CONV_DEN) + temp_c;
} }
switch (state) {
case STATE_MENU:
u8g2_DrawStr(u8g2, x_off+0, y_off+7, "TEMP:");
u8g2_DrawStr(u8g2, x_off+25, y_off+7, u8g2_u16toa(pd_profile.set_temp, 4));
if (encoder != 0) {
pd_profile.set_temp += encoder;
encoder = 0;
#define MIN_TEMP 100
#define MAX_TEMP 360
if (pd_profile.set_temp < MIN_TEMP) pd_profile.set_temp = MIN_TEMP;
if (pd_profile.set_temp > MAX_TEMP) pd_profile.set_temp = MAX_TEMP;
}
if (btn == 0 && prev_btn == 1) {
state = STATE_HEATING;
enabled = false;
}
break;
case STATE_HEATING:
// Display tip temperature // Display tip temperature
u8g2_DrawStr(u8g2, x_off+0, y_off+7, "TIP:"); u8g2_DrawStr(u8g2, x_off+0, y_off+7, "TIP:");
u8g2_DrawStr(u8g2, x_off+20, y_off+7, u8g2_u16toa(tip_temp_c, 4)); u8g2_DrawStr(u8g2, x_off+20, y_off+7, u8g2_u16toa(tip_temp_c, 4));
@ -497,9 +577,14 @@ __attribute__((noreturn)) int main(void)
funDigitalWrite(PIN_12V, 0); funDigitalWrite(PIN_12V, 0);
} }
// move to menu mode when encoder is turned
if (encoder != 0) {
state = STATE_MENU;
funDigitalWrite(PIN_12V, 0);
pwm_set(0);
}
// Check button to toggle enable // Check button to toggle enable
static bool prev_btn = true;
bool btn = funDigitalRead(PIN_BTN);
if (btn == 0 && prev_btn == 1) { if (btn == 0 && prev_btn == 1) {
enabled = !enabled; enabled = !enabled;
if (enabled) { if (enabled) {
@ -508,8 +593,8 @@ __attribute__((noreturn)) int main(void)
pwm_off(); pwm_off();
} }
} }
prev_btn = btn; break;
}
} else { } else {
// No PD capability, just display a message // No PD capability, just display a message
u8g2_DrawStr(u8g2, x_off+0, y_off+7, "NO PD"); u8g2_DrawStr(u8g2, x_off+0, y_off+7, "NO PD");
@ -517,7 +602,9 @@ __attribute__((noreturn)) int main(void)
u8g2_SendBuffer(u8g2); u8g2_SendBuffer(u8g2);
s32 elapsed = funSysTick32() - start; prev_btn = btn;
elapsed = funSysTick32() - start;
if (elapsed > 0 && elapsed < Ticks_from_Ms(FRAME_TIME_MS)) { if (elapsed > 0 && elapsed < Ticks_from_Ms(FRAME_TIME_MS)) {
DelaySysTick(Ticks_from_Ms(FRAME_TIME_MS) - elapsed); DelaySysTick(Ticks_from_Ms(FRAME_TIME_MS) - elapsed);
} }