complete the switch to SS49E hall sensor

This commit is contained in:
2026-09-26 22:18:51 +03:00
parent d0fd39cffa
commit 50a893f536
4 changed files with 181 additions and 68 deletions
+3 -2
View File
@@ -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)
+110 -66
View File
@@ -1,4 +1,6 @@
#include <boot/picobin.h>
#include <hardware/adc.h>
#include <hardware/dma.h>
#include <hardware/flash.h>
#include <hardware/gpio.h>
#include <hardware/irq.h>
@@ -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) {
+38
View File
@@ -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();
}
}
}
+30
View File
@@ -0,0 +1,30 @@
#ifndef VELOMETER_SS49E_H
#define VELOMETER_SS49E_H
#include <stdbool.h>
#include <stdint.h>
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