From 93d5eebb74977b64624ba3dcc819d33b109be6e5 Mon Sep 17 00:00:00 2001 From: whowechina Date: Tue, 3 Dec 2024 14:21:08 +0800 Subject: [PATCH] Supports TMAG5273 sensor --- Production/Firmware/iidx_pico.uf2 | Bin 112640 -> 112128 bytes firmware/src/CMakeLists.txt | 68 ++++++++--------- firmware/src/as5600.c | 59 +++++++++++++++ firmware/src/as5600.h | 17 +++++ firmware/src/board_defs.h | 8 +- firmware/src/tmag5273.c | 120 ++++++++++++++++++++++++++++++ firmware/src/tmag5273.h | 27 +++++++ firmware/src/turntable.c | 62 +++++++-------- firmware/src/turntable.h | 34 ++++----- 9 files changed, 309 insertions(+), 86 deletions(-) create mode 100644 firmware/src/as5600.c create mode 100644 firmware/src/as5600.h create mode 100644 firmware/src/tmag5273.c create mode 100644 firmware/src/tmag5273.h diff --git a/Production/Firmware/iidx_pico.uf2 b/Production/Firmware/iidx_pico.uf2 index 4367cad2d761c1f81ab2e1cb27dbdd0e7be60b65..f0d67bf941a52f39ea07f23cecfad0c426682aef 100644 GIT binary patch delta 34058 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 HBKUs+<$9;} delta 34497 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 diff --git a/firmware/src/CMakeLists.txt b/firmware/src/CMakeLists.txt index 82b068c..bcd82f4 100644 --- a/firmware/src/CMakeLists.txt +++ b/firmware/src/CMakeLists.txt @@ -1,34 +1,34 @@ -set(BTSTACK_ROOT ${PICO_SDK_PATH}/lib/btstack) -set(LWIP_ROOT ${PICO_SDK_PATH}/lib/lwip) - -function(make_firmware board board_def) - pico_sdk_init() - add_executable(${board} - main.c cli.c commands.c buttons.c rgb.c save.c config.c setup.c - turntable.c tt_rainbow.c tt_blade.c - usb_descriptors.c) - target_compile_definitions(${board} PUBLIC ${board_def}) - pico_enable_stdio_usb(${board} 1) - pico_enable_stdio_uart(${board} 0) - - pico_generate_pio_header(${board} ${CMAKE_CURRENT_LIST_DIR}/ws2812.pio) - - target_compile_options(${board} PRIVATE -Wfatal-errors -O3) - target_include_directories(${board} PRIVATE ${CMAKE_CURRENT_LIST_DIR}) - target_include_directories(${board} PRIVATE - ${BTSTACK_ROOT}/src - ${LWIP_ROOT}/src/include) - - target_link_libraries(${board} PRIVATE - pico_multicore pico_stdlib hardware_pio hardware_pwm hardware_flash - hardware_adc hardware_i2c hardware_watchdog - tinyusb_device tinyusb_board) - - pico_add_extra_outputs(${board}) - - add_custom_command(TARGET ${board} POST_BUILD - COMMAND cp ${board}.uf2 ${CMAKE_CURRENT_LIST_DIR}/..) -endfunction() - -make_firmware(iidx_pico BOARD_IIDX_PICO) - +set(BTSTACK_ROOT ${PICO_SDK_PATH}/lib/btstack) +set(LWIP_ROOT ${PICO_SDK_PATH}/lib/lwip) + +function(make_firmware board board_def) + pico_sdk_init() + add_executable(${board} + main.c cli.c commands.c buttons.c rgb.c save.c config.c setup.c + turntable.c as5600.c tmag5273.c tt_rainbow.c tt_blade.c + usb_descriptors.c) + target_compile_definitions(${board} PUBLIC ${board_def}) + pico_enable_stdio_usb(${board} 1) + pico_enable_stdio_uart(${board} 0) + + pico_generate_pio_header(${board} ${CMAKE_CURRENT_LIST_DIR}/ws2812.pio) + + target_compile_options(${board} PRIVATE -Wfatal-errors -O3) + target_include_directories(${board} PRIVATE ${CMAKE_CURRENT_LIST_DIR}) + target_include_directories(${board} PRIVATE + ${BTSTACK_ROOT}/src + ${LWIP_ROOT}/src/include) + + target_link_libraries(${board} PRIVATE + pico_multicore pico_stdlib hardware_pio hardware_pwm hardware_flash + hardware_adc hardware_i2c hardware_watchdog + tinyusb_device tinyusb_board) + + pico_add_extra_outputs(${board}) + + add_custom_command(TARGET ${board} POST_BUILD + COMMAND cp ${board}.uf2 ${CMAKE_CURRENT_LIST_DIR}/..) +endfunction() + +make_firmware(iidx_pico BOARD_IIDX_PICO) + diff --git a/firmware/src/as5600.c b/firmware/src/as5600.c new file mode 100644 index 0000000..d052901 --- /dev/null +++ b/firmware/src/as5600.c @@ -0,0 +1,59 @@ +/* + * AS5600 Angular Hall Sensor + * WHowe + * + */ + +#include "turntable.h" + +#include +#include +#include +#include + +#include "hardware/gpio.h" +#include "hardware/i2c.h" + +#include "board_defs.h" +#include "config.h" + +#define AS5600_ADDR 0x36 + +static i2c_inst_t *as5600_i2c = i2c0; +static uint16_t raw_angle = 0; + +void as5600_init(i2c_inst_t *i2c_port) +{ + as5600_i2c = i2c_port; +} + +bool as5600_init_sensor() +{ + return true; +} + +static bool as5600_is_present() +{ + uint8_t buf[1] = {0x0c}; + int ret = i2c_write_blocking_until(as5600_i2c, AS5600_ADDR, buf, 1, true, + make_timeout_time_ms(1)); + return ret == 1; +} + +int as5600_read_angle() +{ + uint8_t buf[2] = {0x0c, 0x00}; + int ret = i2c_write_blocking_until(as5600_i2c, AS5600_ADDR, buf, 1, true, + make_timeout_time_ms(1)); + if (ret != 1) { + return -1; + } + + ret = i2c_read_blocking_until(as5600_i2c, AS5600_ADDR, buf, 2, false, + make_timeout_time_ms(1)); + if (ret != 2) { + return -1; + } + + return (buf[0] & 0x0f) << 8 | buf[1]; +} diff --git a/firmware/src/as5600.h b/firmware/src/as5600.h new file mode 100644 index 0000000..e13d84d --- /dev/null +++ b/firmware/src/as5600.h @@ -0,0 +1,17 @@ +/* + * AS5600 Angular Hall Sensor + * WHowe + */ + +#ifndef AS5600_H +#define AS5600_H + +#include +#include + +void as5600_init(i2c_inst_t *i2c_port); +bool as5600_init_sensor(); +bool as5600_is_present(); +int as5600_read_angle(); + +#endif diff --git a/firmware/src/board_defs.h b/firmware/src/board_defs.h index 899dc34..8333d27 100644 --- a/firmware/src/board_defs.h +++ b/firmware/src/board_defs.h @@ -13,14 +13,12 @@ #define BUTTON_RGB_NUM 11 #define BUTTON_RGB_MAP { 6, 0, 5, 1, 4, 2, 3, 7, 8, 9, 10} - #define TT_RGB_PIN 28 #define TT_RGB_ORDER GRB // or RGB -#define TT_AS5600_ANALOG 26 -#define TT_AS5600_SCL 27 -#define TT_AS5600_SDA 26 -#define TT_AS5600_I2C i2c1 +#define TT_SENSOR_SCL 27 +#define TT_SENSOR_SDA 26 +#define TT_SENSOR_I2C i2c1 // Alternative I2C pins //#define TT_AS5600_SCL 21 diff --git a/firmware/src/tmag5273.c b/firmware/src/tmag5273.c new file mode 100644 index 0000000..629af9b --- /dev/null +++ b/firmware/src/tmag5273.c @@ -0,0 +1,120 @@ +/* + * TMAG5273 Angular Hall Sensor + * WHowe + * + */ + +#include +#include +#include + +#include "hardware/i2c.h" +#include "board_defs.h" + +#include "tmag5273.h" + +#define TMAG5273_DEF_ADDR 0x35 + +#define IO_TIMEOUT_US 1000 + +static struct { + i2c_inst_t *port; + uint8_t addr; + uint16_t cache; + bool present; +} instances[16] = { { i2c0, TMAG5273_DEF_ADDR } }; +#define INSTANCE_NUM count_of(instances) + +static int current_instance = 0; +#define INSTANCE instances[current_instance] + +#define I2C_PORT INSTANCE.port +#define I2C_ADDR INSTANCE.addr + +static void write_reg(uint8_t reg, uint8_t value) +{ + uint8_t data[2] = { reg, value }; + i2c_write_blocking_until(I2C_PORT, I2C_ADDR, data, 2, false, + time_us_64() + IO_TIMEOUT_US); +} + +static uint8_t read_reg(uint8_t reg) +{ + uint8_t value; + i2c_write_blocking_until(I2C_PORT, I2C_ADDR, ®, 1, true, + time_us_64() + IO_TIMEOUT_US); + i2c_read_blocking_until(I2C_PORT, I2C_ADDR, &value, 1, false, + time_us_64() + IO_TIMEOUT_US); + return value; +} + +static void read_many(uint8_t reg, uint8_t *dst, uint8_t len) +{ + i2c_write_blocking_until(I2C_PORT, I2C_ADDR, ®, 1, true, + time_us_64() + IO_TIMEOUT_US); + i2c_read_blocking_until(I2C_PORT, I2C_ADDR, dst, len, false, + time_us_64() + IO_TIMEOUT_US * len); +} + +void tmag5273_init(unsigned instance, i2c_inst_t *i2c_port) +{ + if (instance < INSTANCE_NUM) { + current_instance = instance; + INSTANCE.port = i2c_port; + INSTANCE.addr = TMAG5273_DEF_ADDR; + INSTANCE.present = tmag5273_change_addr(TMAG5273_DEF_ADDR + 1 + instance); + } +} + +void tmag5273_use(unsigned instance) +{ + if (instance < INSTANCE_NUM) { + current_instance = instance; + } +} + +bool tmag5273_is_present(unsigned instance) +{ + if (instance >= INSTANCE_NUM) { + return false; + } + return instances[instance].present; +} + +bool tmag5273_change_addr(uint8_t i2c_addr) +{ + tmag5273_write_reg(0x0c, (i2c_addr << 1) | 0x01); + INSTANCE.addr = i2c_addr; + tmag5273_read_reg(0x0c); // Dummy read + uint8_t new_addr = tmag5273_read_reg(0x0c) >> 1; + return new_addr == i2c_addr; +} + +bool tmag5273_init_sensor() +{ + tmag5273_write_reg(0x03, 0x01 << 2); // Enable angle calculation + tmag5273_write_reg(0x02, 0x03 << 4); // X-Y + tmag5273_write_reg(0x00, 0x03 << 2); // 8x average mode + tmag5273_write_reg(0x01, 0x02); // Continuous mode + return true; +} + +uint8_t tmag5273_read_reg(uint8_t addr) +{ + return read_reg(addr); +} + +void tmag5273_write_reg(uint8_t addr, uint8_t value) +{ + write_reg(addr, value); +} + +uint16_t tmag5273_read_angle() +{ + uint8_t buf[3] = { 0 }; + read_many(0x18, buf, sizeof(buf)); + if (buf[0] & 0x01) { + INSTANCE.cache = (buf[1] << 8 | buf[2]) & 0x1fff; + } + return INSTANCE.cache; +} diff --git a/firmware/src/tmag5273.h b/firmware/src/tmag5273.h new file mode 100644 index 0000000..297cd0c --- /dev/null +++ b/firmware/src/tmag5273.h @@ -0,0 +1,27 @@ +/* + * TMAG5273 Angular Hall Sensor + * WHowe + * + */ + +#ifndef TMAG5273_H +#define TMAG5273_H + +#include +#include + +#include "hardware/i2c.h" + +void tmag5273_init(unsigned instance, i2c_inst_t *i2c_port); +void tmag5273_use(unsigned instance); +bool tmag5273_is_present(unsigned instance); +bool tmag5273_change_addr(uint8_t i2c_addr); + +bool tmag5273_init_sensor(); + +uint8_t tmag5273_read_reg(uint8_t addr); +void tmag5273_write_reg(uint8_t addr, uint8_t value); + +uint16_t tmag5273_read_angle(); + +#endif \ No newline at end of file diff --git a/firmware/src/turntable.c b/firmware/src/turntable.c index 3d87d1e..8f6b056 100644 --- a/firmware/src/turntable.c +++ b/firmware/src/turntable.c @@ -15,45 +15,47 @@ #include "hardware/adc.h" #include "hardware/i2c.h" +#include "as5600.h" +#include "tmag5273.h" + #include "board_defs.h" #include "config.h" -static uint16_t angle = 0; - -static void init_i2c() -{ - i2c_init(TT_AS5600_I2C, 400 * 1000); - gpio_set_function(TT_AS5600_SCL, GPIO_FUNC_I2C); - gpio_set_function(TT_AS5600_SDA, GPIO_FUNC_I2C); - gpio_set_drive_strength(TT_AS5600_SCL, GPIO_DRIVE_STRENGTH_8MA); - gpio_set_drive_strength(TT_AS5600_SDA, GPIO_DRIVE_STRENGTH_8MA); - gpio_pull_up(TT_AS5600_SCL); - gpio_pull_up(TT_AS5600_SDA); -} +static uint16_t raw_angle = 0; +static bool use_as5600 = true; void turntable_init() { - init_i2c(); + i2c_init(TT_SENSOR_I2C, 400 * 1000); + gpio_init(TT_SENSOR_SCL); + gpio_init(TT_SENSOR_SDA); + gpio_set_function(TT_SENSOR_SCL, GPIO_FUNC_I2C); + gpio_set_function(TT_SENSOR_SDA, GPIO_FUNC_I2C); + gpio_pull_up(TT_SENSOR_SCL); + gpio_pull_up(TT_SENSOR_SDA); + + tmag5273_init(0, TT_SENSOR_I2C); + if (tmag5273_is_present(0)) { + tmag5273_use(0); + tmag5273_init_sensor(); + use_as5600 = false; + return; + } + + as5600_init(TT_SENSOR_I2C); + as5600_init_sensor(); + use_as5600 = true; } static int read_angle() { - const uint8_t as5600_addr = 0x36; - uint8_t buf[2] = {0x0c, 0x00}; - int ret = i2c_write_blocking_until(TT_AS5600_I2C, as5600_addr, buf, 1, true, - make_timeout_time_ms(1)); - if (ret != 1) { - return -1; + if (use_as5600) { + return as5600_read_angle(); } - ret = i2c_read_blocking_until(TT_AS5600_I2C, as5600_addr, buf, 2, false, - make_timeout_time_ms(1)); - if (ret != 2) { - return -1; - } - - return (buf[0] & 0x0f) << 8 | buf[1]; + return tmag5273_read_angle() * 0x1000 / 360 / 16; } + void turntable_update() { int candidate = read_angle(); @@ -75,12 +77,12 @@ void turntable_update() } } - angle = candidate; + raw_angle = candidate; } uint16_t turntable_raw() { - return iidx_cfg->tt_sensor.reversed ? 4095 - angle : angle; // 12bit + return iidx_cfg->tt_sensor.reversed ? 4095 - raw_angle : raw_angle; // 12bit } uint8_t turntable_read() @@ -96,10 +98,10 @@ uint8_t turntable_read() } else if (iidx_cfg->tt_sensor.ppr == 3) { step = 4096 / 64; } else { - return angle >> 4; + return raw_angle >> 4; } - int16_t delta = angle - old_angle; + int16_t delta = raw_angle - old_angle; if (delta == 0) { return counter; } else if (delta > 2048) { diff --git a/firmware/src/turntable.h b/firmware/src/turntable.h index 33adfca..5337c21 100644 --- a/firmware/src/turntable.h +++ b/firmware/src/turntable.h @@ -1,17 +1,17 @@ -/* - * IIDX Controller Turntable - * WHowe - */ - -#ifndef TURNTABLE_H -#define TURNTABLE_H - -#include -#include - -void turntable_init(); -uint8_t turntable_read(); -uint16_t turntable_raw(); -void turntable_update(); - -#endif +/* + * IIDX Controller Turntable + * WHowe + */ + +#ifndef TURNTABLE_H +#define TURNTABLE_H + +#include +#include + +void turntable_init(); +uint8_t turntable_read(); +uint16_t turntable_raw(); +void turntable_update(); + +#endif