#include "buttons.h" #include "beeper.h" #include "powersave.h" #include #include static const ADCConversionGroup convgrp = { 0, // Circular 1, // Number of channels NULL, // Callback NULL, // Error callback 0, // CR1 ADC_CR2_SWSTART | ADC_CR2_DELS_0, // CR2 0, // SMPR1 0, // SMPR2 ADC_SMPR3_SMP_AN0(ADC_SAMPLE_4), // SMPR3 ADC_SQR1_NUM_CH(1), // SQR1 0, // SQR2 0, // SQR3 0, // SQR4 ADC_SQR5_SQ1_N(ADC_CHANNEL_IN0) // SQR5 }; buttons_t buttons_read_immediate() { int min, max, sum; chSysLock(); palSetPadMode(GPIOA, GPIOA_BUTTONS, PAL_MODE_INPUT_ANALOG); chSysUnlock(); // Debouncing loop: repeat until we get stable value do { min = 4096; max = 0; sum = 0; for (int i = 0; i < 5; i++) { adcsample_t value; adcAcquireBus(&ADCD1); adcConvert(&ADCD1, &convgrp, &value, 1); adcReleaseBus(&ADCD1); if (min > value) min = value; if (max < value) max = value; sum += value; chThdSleepMilliseconds(2); } } while (max - min > 30); // Switch back to INPUT mode so that EXTI works chSysLock(); palSetPadMode(GPIOA, GPIOA_BUTTONS, PAL_MODE_INPUT); chSysUnlock(); int avg = sum / 5; int admittance = avg * 100 / (4096 - avg); // In micro siemens return (admittance + 64) >> 7; } /* Buffering API */ static bool g_power_pressed = false; static bool g_clear_pressed = false; static int g_plusminus_delta = 0; buttons_t buttons_read_buffer() { buttons_t result = BT_NONE; chSysLock(); if (g_power_pressed) { g_power_pressed = false; result = BT_POWER; } else if (g_clear_pressed) { g_clear_pressed = false; result = BT_CLEAR; } else if (g_plusminus_delta > 0) { g_plusminus_delta--; result = BT_PLUS; } else if (g_plusminus_delta < 0) { g_plusminus_delta++; result = BT_MINUS; } chSysUnlock(); return result; } void buttons_clear_buffer() { chSysLock(); g_power_pressed = false; g_clear_pressed = false; g_plusminus_delta = 0; chSysUnlock(); } /* Interrupt handler for detecting button presses without polling */ static BSEMAPHORE_DECL(g_new_event, TRUE); static void exti_button_callback(EXTDriver *extp, expchannel_t channel) { chSysLockFromIsr(); chBSemSignalI(&g_new_event); chSysUnlockFromIsr(); } static const EXTConfig extconfig = { {[GPIOA_BUTTONS] = {EXT_CH_MODE_RISING_EDGE | EXT_CH_MODE_AUTOSTART | EXT_MODE_GPIOA, &exti_button_callback}} }; /* Thread for performing the ADC conversion */ static Thread *buttonThread = 0; static WORKING_AREA(buttonThread_wa, 128); static msg_t buttons_task(void *arg) { chRegSetThreadName("buttons"); extStart(&EXTD1, &extconfig); while (!chThdShouldTerminate()) { chBSemWaitTimeout(&g_new_event, MS2ST(100)); // We reset the watchdog here. Idea is that if the button task still // works, reset can be forced by pressing clear + power. If it doesn't, // the watchdog will reset us in 30 seconds. IWDG->KR = 0xAAAA; if (!palReadPad(GPIOA, GPIOA_BUTTONS)) continue; buttons_t btn = buttons_read_immediate(); int iter = 0; bool repeat = false; do { // Emulate reset button when pressing power and clear simultaneously. if (btn == (BT_POWER | BT_CLEAR)) { // Reset the processor palSetPad(GPIOH, GPIOH_LED); chThdSleepMilliseconds(100); beeper_click(); palClearPad(GPIOH, GPIOH_LED); usart1_putstring("Forced reboot\n"); SCB->AIRCR = 0x05FA0000 | SCB_AIRCR_SYSRESETREQ; } if (iter == 0 || repeat) { if (btn == BT_POWER || btn == BT_CLEAR || btn == BT_PLUS || btn == BT_MINUS) { chSysLock(); if (btn == BT_POWER) g_power_pressed = true; else if (btn == BT_CLEAR) g_clear_pressed = true; else if (btn == BT_PLUS) g_plusminus_delta++; else if (btn == BT_MINUS) g_plusminus_delta--; chSysUnlock(); beeper_click(); powersave_keep_alive(); } } /* Wait for button release and also repeat plus/minus keys. */ if (iter < 10) { /* No repeat for first 500 ms */ repeat = false; } else if (iter < 50) { /* For first 10 counts, repeat every 200ms */ repeat = ((iter & 3) == 0) && (btn == BT_PLUS || btn == BT_MINUS); } else { /* From there on, repeat every 50ms */ repeat = (btn == BT_PLUS || btn == BT_MINUS); } chThdSleepMilliseconds(50); btn = buttons_read_immediate(); iter++; } while (btn != BT_NONE); } extStop(&EXTD1); return 0; } void buttons_start() { if (buttonThread != NULL) return; buttonThread = chThdCreateStatic(buttonThread_wa, sizeof(buttonThread_wa), NORMALPRIO + 20, buttons_task, NULL); } void buttons_stop() { if (buttonThread) { chThdTerminate(buttonThread); chThdWait(buttonThread); buttonThread = NULL; } }