From 50a893f5363d9bc16e77b63eb64286b6b4637f8c Mon Sep 17 00:00:00 2001 From: Slavasil Date: Sat, 26 Sep 2026 18:58:38 +0300 Subject: [PATCH] complete the switch to SS49E hall sensor --- CMakeLists.txt | 5 +- main.c | 176 ++++++++++++++++++++++++++++++------------------- ss49e.c | 38 +++++++++++ ss49e.h | 30 +++++++++ 4 files changed, 181 insertions(+), 68 deletions(-) create mode 100644 ss49e.c create mode 100644 ss49e.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 12f7a37..991ed63 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,7 @@ cmake_minimum_required(VERSION 3.27) set(PICO_SDK_PATH /home/sl/pico-sdk) set(PICO_PLATFORM rp2350) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) include(pico_sdk_import.cmake) project(GPIOTest) @@ -9,8 +10,8 @@ set(PICO_PSRAM_CS_PIN 47 CACHE STRING "GPIO used as Chip Select for PSRAM") set(PICO_PSRAM_SIZE_BYTES 8388608 CACHE STRING "PSRAM size in bytes") pico_sdk_init() -add_executable(program main.c st7789.c graphics.c utf8.c ds1302.c beep.c) -target_link_libraries(program pico_stdlib pico_bootrom pico_rand hardware_gpio hardware_pwm hardware_spi hardware_regs hardware_psram pico_stdio_usb pico_time pico_sync) +add_executable(program main.c st7789.c graphics.c graphics_fb.c utf8.c ds1302.c beep.c ss49e.c) +target_link_libraries(program pico_stdlib pico_bootrom pico_rand hardware_adc hardware_dma hardware_gpio hardware_pwm hardware_spi hardware_regs hardware_psram pico_stdio_usb pico_time pico_sync) #target_compile_definitions(program PRIVATE PICO_PSRAM_CS_PIN=${PICO_PSRAM_CS_PIN} PICO_PSRAM_SIZE_BYTES=${PICO_PSRAM_CS_PIN}) target_compile_definitions(program PRIVATE PICO_PSRAM_CS_PIN=0 PICO_PSRAM_SIZE_BYTES=8388608) diff --git a/main.c b/main.c index c90de5e..7e888a7 100644 --- a/main.c +++ b/main.c @@ -1,4 +1,6 @@ #include +#include +#include #include #include #include @@ -16,11 +18,11 @@ #include "ds1302.h" #include "graphics.h" #include "pins.h" +#include "ss49e.h" #include "st7789.h" #define LOGGING -#define HALL_SENSOR_DEBOUNCE_DELAY_us 10000 #define BUTTON_DEBOUNCE_DELAY_us 3000 #define BUTTON_HOLD_TIME_ms 1000 #define TICKS_PER_REVOLUTION 6 @@ -57,12 +59,12 @@ struct GUIState { }; void on_gpio_interrupt(uint pin, uint32_t eventMask); -int64_t hall_sensor_debounce_callback(alarm_id_t alarmId, void *userData); +static void __not_in_flash_func(dma_irq_handler)(); int64_t button_debounce_callback(alarm_id_t alarmId, void *userData); int64_t button_hold_callback(alarm_id_t alarmId, void *userData); -bool speed_measure_timer_callback(repeating_timer_t *rt); void on_button_short_press(uint button); void on_button_long_press(uint button); +void on_wheel_hall_sensor_tick(); void update_lcd(); void gui_init(struct GUIState *state); @@ -95,25 +97,26 @@ int16_t convert_brightness(int16_t input) { return -1; } -// for debouncing -alarm_id_t hallSensorDebounceAlarm = 0; -bool hallSensorLastState = 0; -bool hallSensorPrevState = 0; - -struct { +struct PushButtonState { alarm_id_t debounceAlarm, holdAlarm; bool currentState, prevState, isHeld; -} buttonStates[4]; +}; + +struct PushButtonState buttonStates[4]; // for debug (and for the future) absolute_time_t lastWheelTickTime = 0; +// for hall sensors +int adcDmaChannelA, adcDmaChannelB; +uint16_t adcBufferA[1600]; +uint16_t adcBufferB[1600]; +SS49E_State wheelSensorState; + // for speed measurement uint8_t intervalWheelTicks[SPEED_MEASUREMENT_WINDOW_SIZE]; uint8_t currentInterval = 0; uint16_t ticksPerWindow = 0; -// critical_section_t intervalWheelTicksCritSec; -repeating_timer_t speedMeasureTimer; // odometer counters uint totalWheelTicks = 0; @@ -172,18 +175,46 @@ int main() { } #endif - // critical_section_init(&intervalWheelTicksCritSec); - add_repeating_timer_ms(SPEED_MEASUREMENT_INTERVAL_ms, speed_measure_timer_callback, NULL, &speedMeasureTimer); - gpio_set_irq_callback(on_gpio_interrupt); - gpio_init(PIN_BEEPER); - gpio_set_dir(PIN_BEEPER, GPIO_OUT); + // hall sensor (wheel only for now) + adc_init(); + adc_gpio_init(42); + adc_select_input(2); + adc_set_round_robin(1 << 2); + adc_set_clkdiv(3000 / 1); // 16kHz for 1 sensor + adc_fifo_setup(true, true, 1, false, false); - // hall sensor - gpio_init(PIN_MAIN_HALL_SENSOR); - gpio_pull_up(PIN_MAIN_HALL_SENSOR); - gpio_set_irq_enabled(PIN_MAIN_HALL_SENSOR, GPIO_IRQ_EDGE_RISE | GPIO_IRQ_EDGE_FALL, true); + // wheel sensor (with N-S alternating magnet arrangement) + ss49e_init(&wheelSensorState, MAGNET_POLTYPE_BIPOLAR, on_wheel_hall_sensor_tick); + + // DMA for hall sensor + adcDmaChannelA = dma_claim_unused_channel(true); + adcDmaChannelB = dma_claim_unused_channel(true); + + dma_channel_config dmaCfg = dma_channel_get_default_config(adcDmaChannelA); + channel_config_set_transfer_data_size(&dmaCfg, DMA_SIZE_16); + channel_config_set_read_increment(&dmaCfg, false); + channel_config_set_write_increment(&dmaCfg, true); + channel_config_set_dreq(&dmaCfg, DREQ_ADC); + channel_config_set_chain_to(&dmaCfg, adcDmaChannelB); + dma_channel_configure(adcDmaChannelA, &dmaCfg, adcBufferA, &adc_hw->fifo, 1600, false); + + dmaCfg = dma_channel_get_default_config(adcDmaChannelB); + channel_config_set_transfer_data_size(&dmaCfg, DMA_SIZE_16); + channel_config_set_read_increment(&dmaCfg, false); + channel_config_set_write_increment(&dmaCfg, true); + channel_config_set_dreq(&dmaCfg, DREQ_ADC); + channel_config_set_chain_to(&dmaCfg, adcDmaChannelA); + dma_channel_configure(adcDmaChannelB, &dmaCfg, adcBufferB, &adc_hw->fifo, 1600, false); + + dma_channel_set_irq0_enabled(adcDmaChannelA, true); + dma_channel_set_irq0_enabled(adcDmaChannelB, true); + irq_set_exclusive_handler(DMA_IRQ_0, dma_irq_handler); + irq_set_enabled(DMA_IRQ_0, true); + + adc_run(true); + dma_channel_start(adcDmaChannelA); // on-board LED gpio_init(25); @@ -248,17 +279,7 @@ int main() { } void on_gpio_interrupt(uint pin, uint32_t eventMask) { - if (pin == PIN_MAIN_HALL_SENSOR) { - if (eventMask & GPIO_IRQ_EDGE_RISE) - hallSensorLastState = 0; - if (eventMask & GPIO_IRQ_EDGE_FALL) - hallSensorLastState = 1; - if (hallSensorDebounceAlarm) - cancel_alarm(hallSensorDebounceAlarm); - hallSensorDebounceAlarm = - add_alarm_in_us(HALL_SENSOR_DEBOUNCE_DELAY_us, hall_sensor_debounce_callback, NULL, false); - gpio_acknowledge_irq(pin, eventMask); - } else if (pin == PIN_BUTTON_0 || pin == PIN_BUTTON_1 || pin == PIN_BUTTON_2 || pin == PIN_BUTTON_3) { + if (pin == PIN_BUTTON_0 || pin == PIN_BUTTON_1 || pin == PIN_BUTTON_2 || pin == PIN_BUTTON_3) { uint buttonIndex; switch (pin) { case PIN_BUTTON_0: @@ -273,12 +294,7 @@ void on_gpio_interrupt(uint pin, uint32_t eventMask) { case PIN_BUTTON_3: buttonIndex = 3; } - auto buttonState = &buttonStates[buttonIndex]; - // if (eventMask & GPIO_IRQ_EDGE_RISE) - // buttonState->currentState = 0; - // if (eventMask & GPIO_IRQ_EDGE_FALL) - // buttonState->currentState = 1; - // buttonState->currentState = !gpio_get(pin); + struct PushButtonState *buttonState = &buttonStates[buttonIndex]; if (buttonState->debounceAlarm != -1) cancel_alarm(buttonState->debounceAlarm); buttonState->debounceAlarm = @@ -287,26 +303,63 @@ void on_gpio_interrupt(uint pin, uint32_t eventMask) { } } -int64_t hall_sensor_debounce_callback(alarm_id_t alarmId, void *userData) { - (void)alarmId; - (void)userData; - if (hallSensorLastState != hallSensorPrevState) { - absolute_time_t now = get_absolute_time(); - lastWheelTickTime = now; - ++intervalWheelTicks[currentInterval]; - ++totalWheelTicks; - ++wheelTicksFromPowerOn; +static void __not_in_flash_func(dma_irq_handler)() { + if (dma_hw->ints0 & (1u << adcDmaChannelA)) { + dma_hw->ints0 = 1u << adcDmaChannelA; + for (uint i = 0; i < 1600; ++i) + ss49e_process_sample(&wheelSensorState, adcBufferA[i]); + float sum = 0.0f, max = 0.0f, min = 9999.0f; + for (uint i = 0; i < 1600; ++i) { + sum += adcBufferA[i]; + if (adcBufferA[i] > max) + max = adcBufferA[i]; + if (adcBufferA[i] < min) + min = adcBufferA[i]; + } + sum /= 1600; + printf("%f ~ %f ~ %f\n", min, sum, max); + dma_channel_set_write_addr(adcDmaChannelA, adcBufferA, false); + dma_channel_set_trans_count(adcDmaChannelA, 1600, false); - gpio_put(25, !gpio_get(25)); - hallSensorPrevState = hallSensorLastState; + sum = 0; + for (int i = 0; i < 16; ++i) { + sum += intervalWheelTicks[i]; + } + currentInterval = (currentInterval + 1) % 16; + intervalWheelTicks[currentInterval] = 0; + ticksPerWindow = sum; + } + if (dma_hw->ints0 & (1u << adcDmaChannelB)) { + dma_hw->ints0 = 1u << adcDmaChannelB; + for (uint i = 0; i < 1600; ++i) + ss49e_process_sample(&wheelSensorState, adcBufferB[i]); + float sum = 0.0f, max = 0.0f, min = 9999.0f; + for (uint i = 0; i < 1600; ++i) { + sum += adcBufferB[i]; + if (adcBufferB[i] > max) + max = adcBufferB[i]; + if (adcBufferB[i] < min) + min = adcBufferB[i]; + } + sum /= 1600; + printf("%f ~ %f ~ %f\n", min, sum, max); + dma_channel_set_write_addr(adcDmaChannelB, adcBufferB, false); + dma_channel_set_trans_count(adcDmaChannelB, 1600, false); + + sum = 0; + for (int i = 0; i < 16; ++i) { + sum += intervalWheelTicks[i]; + } + currentInterval = (currentInterval + 1) % 16; + intervalWheelTicks[currentInterval] = 0; + ticksPerWindow = sum; } - return 0; } int64_t button_debounce_callback(alarm_id_t alarmId, void *userData) { (void)alarmId; uint buttonIndex = (uint)userData; - auto buttonState = &buttonStates[buttonIndex]; + struct PushButtonState *buttonState = &buttonStates[buttonIndex]; bool currentState = !gpio_get(FRONT_BUTTON_PINS[buttonIndex]); if (currentState != buttonState->prevState) { buttonState->prevState = currentState; @@ -329,7 +382,7 @@ int64_t button_debounce_callback(alarm_id_t alarmId, void *userData) { int64_t button_hold_callback(alarm_id_t alarmId, void *userData) { (void)alarmId; uint buttonIndex = (uint)userData; - auto buttonState = &buttonStates[buttonIndex]; + struct PushButtonState *buttonState = &buttonStates[buttonIndex]; buttonState->holdAlarm = -1; buttonState->isHeld = true; on_button_long_press(buttonIndex); @@ -337,7 +390,6 @@ int64_t button_hold_callback(alarm_id_t alarmId, void *userData) { } void on_button_short_press(uint button) { - // beep(60, 900); #ifdef LOGGING printf("button %i short press\n", button); #endif @@ -354,7 +406,6 @@ void on_button_short_press(uint button) { } void on_button_long_press(uint button) { - // beep(60, 1273); #ifdef LOGGING printf("button %i long press\n"); #endif @@ -364,18 +415,11 @@ void on_button_long_press(uint button) { } } -bool speed_measure_timer_callback(repeating_timer_t *rt) { - (void)rt; - - uint16_t sum = 0; - for (int i = 0; i < 16; ++i) { - sum += intervalWheelTicks[i]; - } - currentInterval = (currentInterval + 1) % 16; - intervalWheelTicks[currentInterval] = 0; - ticksPerWindow = sum; - // printf("Total %i \r", sum); - return true; +void on_wheel_hall_sensor_tick() { + ++intervalWheelTicks[currentInterval]; + ++totalWheelTicks; + ++wheelTicksFromPowerOn; + printf("\t\t\tTICK\7\n"); } uint16_t color565_lerp(uint16_t a, uint16_t b, uint alphaPercent) { diff --git a/ss49e.c b/ss49e.c new file mode 100644 index 0000000..543911f --- /dev/null +++ b/ss49e.c @@ -0,0 +1,38 @@ +#include "ss49e.h" + +void ss49e_init(SS49E_State *self, enum MagnetPolarityType magnetPolarityType, ss49e_tick_callback_t tickCallback) { + self->workingPhase = SS49E_WORKING_PHASE_NORMAL; + self->tickCallback = tickCallback; + + self->magnetPolType = magnetPolarityType; + const float level0 = 2445.0f / 4095.0f; + const float deviation = 380.0f / 4095.0f; + if (magnetPolarityType == MAGNET_POLTYPE_BIPOLAR) { + // self->threshold01 = level0 + deviation * 0.75; + // self->threshold10 = level0 - deviation * 0.75; + self->threshold01 = (2447.0 + 520.0 * 2.0 / 3.0) / 4095.0; + self->threshold10 = (2447.0 - 800.0 * 2.0 / 3.0) / 4095.0; + } else { + float activeLevel = level0 + (magnetPolarityType == MAGNET_POLTYPE_UNIPOLAR_POSITIVE ? deviation : -deviation); + self->threshold01 = level0 * 1 / 3 + activeLevel * 2 / 3; + self->threshold10 = activeLevel * 1 / 3 + level0 * 2 / 3; + } + self->currentLevel = 0; +} + +void ss49e_process_sample(SS49E_State *self, uint16_t sample) { + float normalized = (float)sample / 4095.0f; + if (self->currentLevel == 0) { + if (normalized >= self->threshold01) { + self->currentLevel = 1; + if (self->magnetPolType == MAGNET_POLTYPE_BIPOLAR || self->magnetPolType == MAGNET_POLTYPE_UNIPOLAR_POSITIVE) + self->tickCallback(); + } + } else { + if (normalized <= self->threshold10) { + self->currentLevel = 0; + if (self->magnetPolType == MAGNET_POLTYPE_BIPOLAR || self->magnetPolType == MAGNET_POLTYPE_UNIPOLAR_NEGATIVE) + self->tickCallback(); + } + } +} diff --git a/ss49e.h b/ss49e.h new file mode 100644 index 0000000..1a4eef7 --- /dev/null +++ b/ss49e.h @@ -0,0 +1,30 @@ +#ifndef VELOMETER_SS49E_H +#define VELOMETER_SS49E_H +#include +#include + +enum SS49E_WorkingPhase { + SS49E_WORKING_PHASE_NORMAL, + SS49E_WORKING_PHASE_CALIBRATING, +}; + +enum MagnetPolarityType { + MAGNET_POLTYPE_BIPOLAR, + MAGNET_POLTYPE_UNIPOLAR_POSITIVE, + MAGNET_POLTYPE_UNIPOLAR_NEGATIVE, +}; + +typedef void (*ss49e_tick_callback_t)(); + +typedef struct { + enum SS49E_WorkingPhase workingPhase; + enum MagnetPolarityType magnetPolType; + ss49e_tick_callback_t tickCallback; + float threshold01, threshold10; + bool currentLevel; +} SS49E_State; + +void ss49e_init(SS49E_State *self, enum MagnetPolarityType magnetPolarityType, ss49e_tick_callback_t tickCallback); +void ss49e_process_sample(SS49E_State *self, uint16_t sample); + +#endif