From b98f3bd17b542842ba5edf48ed3b716aa8222188 Mon Sep 17 00:00:00 2001 From: Ian Jamison Date: Tue, 7 Feb 2023 10:50:57 +0000 Subject: [PATCH 01/18] Add CMakeLists.txt and ignore built artifacts --- firmware/tinyg/.gitignore | 2 ++ firmware/tinyg/CMakeLists.txt | 53 +++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+) create mode 100644 firmware/tinyg/.gitignore create mode 100644 firmware/tinyg/CMakeLists.txt diff --git a/firmware/tinyg/.gitignore b/firmware/tinyg/.gitignore new file mode 100644 index 000000000..01f9cb9fb --- /dev/null +++ b/firmware/tinyg/.gitignore @@ -0,0 +1,2 @@ +build/ +.vscode/ \ No newline at end of file diff --git a/firmware/tinyg/CMakeLists.txt b/firmware/tinyg/CMakeLists.txt new file mode 100644 index 000000000..36cb8893f --- /dev/null +++ b/firmware/tinyg/CMakeLists.txt @@ -0,0 +1,53 @@ +cmake_minimum_required(VERSION 3.10) +project(tinyg VERSION 0.1.0) + +set(CMAKE_SYSTEM_NAME Generic) +set(CMAKE_C_COMPILER avr-gcc) + +add_executable(tinyg.elf + canonical_machine.c + config.c + config_app.c + controller.c + cycle_homing.c + cycle_jogging.c + cycle_probing.c + encoder.c + gcode_parser.c + gpio.c + hardware.c + help.c + json_parser.c + kinematics.c + main.c + network.c + persistence.c + planner.c + plan_arc.c + plan_exec.c + plan_line.c + plan_zoid.c + pwm.c + report.c + spindle.c + stepper.c + switch.c + test.c + text_parser.c + util.c + xio.c + xio/xio_file.c + xio/xio_pgm.c + xio/xio_rs485.c + xio/xio_spi.c + xio/xio_usart.c + xio/xio_usb.c + xmega/xmega_adc.c + xmega/xmega_eeprom.c + xmega/xmega_init.c + xmega/xmega_interrupts.c + xmega/xmega_rtc.c +) + +target_compile_options(tinyg.elf PRIVATE -mmcu=atxmega192a3 -Os) +target_link_options(tinyg.elf PRIVATE -mmcu=atxmega192a3 -Os) \ No newline at end of file From 63f24e6d2f6fa9158d24c8913399d68e6acd2147 Mon Sep 17 00:00:00 2001 From: Ian Jamison Date: Tue, 7 Feb 2023 10:51:26 +0000 Subject: [PATCH 02/18] Move static data structs from headers to code --- firmware/tinyg/hardware.c | 14 ++++--- firmware/tinyg/hardware.h | 80 +++++++++++++++++++-------------------- firmware/tinyg/settings.h | 4 +- firmware/tinyg/switch.c | 2 + firmware/tinyg/switch.h | 74 ++++++++++++++++++------------------ firmware/tinyg/xio.c | 40 +++++++++++--------- firmware/tinyg/xio.h | 11 +++--- 7 files changed, 117 insertions(+), 108 deletions(-) diff --git a/firmware/tinyg/hardware.c b/firmware/tinyg/hardware.c index d8f185d9c..52445b5a5 100755 --- a/firmware/tinyg/hardware.c +++ b/firmware/tinyg/hardware.c @@ -40,9 +40,11 @@ #include "xmega/xmega_rtc.h" #endif -#ifdef __cplusplus -extern "C"{ -#endif +#ifdef __cplusplus +extern "C"{ +#endif + +hwSingleton_t hw; /* * _port_bindings - bind XMEGA ports to hardware - these changed at board revision 7 @@ -257,6 +259,6 @@ void hw_print_id(nvObj_t *nv) { text_print_str(nv, fmt_id);} #endif //__TEXT_MODE -#ifdef __cplusplus -} -#endif +#ifdef __cplusplus +} +#endif diff --git a/firmware/tinyg/hardware.h b/firmware/tinyg/hardware.h index 9ac1c53c9..c3ecc332a 100755 --- a/firmware/tinyg/hardware.h +++ b/firmware/tinyg/hardware.h @@ -1,11 +1,11 @@ /* - * hardware.h - system hardware configuration - * THIS FILE IS HARDWARE PLATFORM SPECIFIC - AVR Xmega version - * - * This file is part of the TinyG project - * - * Copyright (c) 2013 - 2015 Alden S. Hart, Jr. - * Copyright (c) 2013 - 2015 Robert Giseburt + * hardware.h - system hardware configuration + * THIS FILE IS HARDWARE PLATFORM SPECIFIC - AVR Xmega version + * + * This file is part of the TinyG project + * + * Copyright (c) 2013 - 2015 Alden S. Hart, Jr. + * Copyright (c) 2013 - 2015 Robert Giseburt * * This file ("the software") is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2 as published by the @@ -48,34 +48,34 @@ #ifndef HARDWARE_H_ONCE #define HARDWARE_H_ONCE -/*--- Hardware platform enumerations ---*/ - -enum hwPlatform { - HM_PLATFORM_NONE = 0, - - HW_PLATFORM_TINYG_XMEGA, // TinyG code base on Xmega boards. - // hwVersion 7 = TinyG v7 and earlier - // hwVersion 8 = TinyG v8 - - HW_PLATFORM_G2_DUE, // G2 code base on native Arduino Due - - HW_PLATFORM_TINYG_V9 // G2 code base on v9 boards - // hwVersion 0 = v9c - // hwVersion 1 = v9d - // hwVersion 2 = v9f - // hwVersion 3 = v9h - // hwVersion 4 = v9i -}; - -#define HW_VERSION_TINYGV6 6 -#define HW_VERSION_TINYGV7 7 -#define HW_VERSION_TINYGV8 8 - -#define HW_VERSION_TINYGV9C 0 -#define HW_VERSION_TINYGV9D 1 -#define HW_VERSION_TINYGV9F 2 -#define HW_VERSION_TINYGV9H 3 -#define HW_VERSION_TINYGV9I 4 +/*--- Hardware platform enumerations ---*/ + +enum hwPlatform { + HM_PLATFORM_NONE = 0, + + HW_PLATFORM_TINYG_XMEGA, // TinyG code base on Xmega boards. + // hwVersion 7 = TinyG v7 and earlier + // hwVersion 8 = TinyG v8 + + HW_PLATFORM_G2_DUE, // G2 code base on native Arduino Due + + HW_PLATFORM_TINYG_V9 // G2 code base on v9 boards + // hwVersion 0 = v9c + // hwVersion 1 = v9d + // hwVersion 2 = v9f + // hwVersion 3 = v9h + // hwVersion 4 = v9i +}; + +#define HW_VERSION_TINYGV6 6 +#define HW_VERSION_TINYGV7 7 +#define HW_VERSION_TINYGV8 8 + +#define HW_VERSION_TINYGV9C 0 +#define HW_VERSION_TINYGV9D 1 +#define HW_VERSION_TINYGV9F 2 +#define HW_VERSION_TINYGV9H 3 +#define HW_VERSION_TINYGV9I 4 //////////////////////////// /////// AVR VERSION //////// @@ -86,14 +86,14 @@ enum hwPlatform { #include "xmega/xmega_rtc.h" // Xmega only. Goes away with RTC refactoring // uncomment once motate Xmega port is available -//#include "motatePins.h" -//#include "motateTimers.h" // for Motate::timer_number +//#include "motatePins.h" +//#include "motateTimers.h" // for Motate::timer_number /************************* * Global System Defines * *************************/ -#undef F_CPU // CPU clock - set for delays +#undef F_CPU // CPU clock - set for delays #define F_CPU 32000000UL // should always precede #define MILLISECONDS_PER_TICK 1 // MS for system tick (systick * N) #define SYS_ID_LEN 12 // length of system ID string from sys_get_id() @@ -256,7 +256,7 @@ typedef struct hmSingleton { PORT_t *sw_port[MOTORS]; // bindings for switch ports (GPIO2) PORT_t *out_port[MOTORS]; // bindings for output ports (GPIO1) } hwSingleton_t; -hwSingleton_t hw; +extern hwSingleton_t hw; /*** function prototypes ***/ @@ -290,4 +290,4 @@ stat_t hw_get_id(nvObj_t *nv); #endif // __TEXT_MODE -#endif // end of include guard: HARDWARE_H_ONCE +#endif // end of include guard: HARDWARE_H_ONCE diff --git a/firmware/tinyg/settings.h b/firmware/tinyg/settings.h index 1a9f5904e..1eb1a69f4 100755 --- a/firmware/tinyg/settings.h +++ b/firmware/tinyg/settings.h @@ -105,10 +105,10 @@ // machine default profiles - choose only one: -#include "settings/settings_default.h" // Default settings for release +//#include "settings/settings_default.h" // Default settings for release //#include "settings/settings_cnc3040.h" //#include "settings/settings_test.h" // Settings for testing - not for release -//#include "settings/settings_openpnp.h" // OpenPnP +#include "settings/settings_openpnp.h" // OpenPnP //#include "settings/settings_othermill.h" // OMC OtherMill //#include "settings/settings_probotixV90.h" // Probotix Fireball V90 //#include "settings/settings_shapeoko2.h" // Shapeoko2 - standard kit diff --git a/firmware/tinyg/switch.c b/firmware/tinyg/switch.c index 6f258979f..527480159 100755 --- a/firmware/tinyg/switch.c +++ b/firmware/tinyg/switch.c @@ -48,6 +48,8 @@ #include "canonical_machine.h" #include "text_parser.h" +struct swStruct sw; + static void _switch_isr_helper(uint8_t sw_num); /* diff --git a/firmware/tinyg/switch.h b/firmware/tinyg/switch.h index cd01607dd..08603708a 100755 --- a/firmware/tinyg/switch.h +++ b/firmware/tinyg/switch.h @@ -98,20 +98,20 @@ enum swNums { // indexes into switch arrays * Defines for new switch handling code */ -// switch array configuration / sizing -#define SW_PAIRS HOMING_AXES // number of axes that can have switches -#define SW_POSITIONS 2 // swPosition is either SW_MIN or SW)MAX - -enum swPosition { - SW_MIN = 0, - SW_MAX -}; - -enum swEdge { - SW_NO_EDGE = 0, - SW_LEADING, - SW_TRAILING, -}; +// switch array configuration / sizing +#define SW_PAIRS HOMING_AXES // number of axes that can have switches +#define SW_POSITIONS 2 // swPosition is either SW_MIN or SW)MAX + +enum swPosition { + SW_MIN = 0, + SW_MAX +}; + +enum swEdge { + SW_NO_EDGE = 0, + SW_LEADING, + SW_TRAILING, +}; /* * Interrupt levels and vectors - The vectors are hard-wired to xmega ports @@ -146,31 +146,31 @@ struct swStruct { // switch state volatile uint8_t debounce[NUM_SWITCHES]; // switch debouncer state machine - see swDebounce volatile int8_t count[NUM_SWITCHES]; // deglitching and lockout counter }; -struct swStruct sw; +extern struct swStruct sw; //*** Structures from new-style switch code --- NOT YET FOLDED IN ***// - -typedef struct swSwitch { // one struct per switch - // public - uint8_t type; // swType: 0=NO, 1=NC - uint8_t mode; // 0=disabled, 1=homing, 2=limit, 3=homing+limit - uint8_t state; // set true if switch is closed - - // private - uint8_t edge; // keeps a transient record of edges for immediate inquiry - uint16_t debounce_ticks; // number of millisecond ticks for debounce lockout - uint32_t debounce_timeout; // time to expire current debounce lockout, or 0 if no lockout - void (*when_open)(struct swSwitch *s); // callback to action function when sw is open - passes *s, returns void - void (*when_closed)(struct swSwitch *s); // callback to action function when closed - void (*on_leading)(struct swSwitch *s); // callback to action function for leading edge onset - void (*on_trailing)(struct swSwitch *s); // callback to action function for trailing edge -} switch_t; -typedef void (*sw_callback)(switch_t *s); // typedef for switch action callback - -typedef struct swSwitchArray { // array of switches - uint8_t type; // switch type for entire array (default) - switch_t s[SW_PAIRS][SW_POSITIONS]; -} switches_t; + +typedef struct swSwitch { // one struct per switch + // public + uint8_t type; // swType: 0=NO, 1=NC + uint8_t mode; // 0=disabled, 1=homing, 2=limit, 3=homing+limit + uint8_t state; // set true if switch is closed + + // private + uint8_t edge; // keeps a transient record of edges for immediate inquiry + uint16_t debounce_ticks; // number of millisecond ticks for debounce lockout + uint32_t debounce_timeout; // time to expire current debounce lockout, or 0 if no lockout + void (*when_open)(struct swSwitch *s); // callback to action function when sw is open - passes *s, returns void + void (*when_closed)(struct swSwitch *s); // callback to action function when closed + void (*on_leading)(struct swSwitch *s); // callback to action function for leading edge onset + void (*on_trailing)(struct swSwitch *s); // callback to action function for trailing edge +} switch_t; +typedef void (*sw_callback)(switch_t *s); // typedef for switch action callback + +typedef struct swSwitchArray { // array of switches + uint8_t type; // switch type for entire array (default) + switch_t s[SW_PAIRS][SW_POSITIONS]; +} switches_t; /**************************************************************************************** * Function prototypes diff --git a/firmware/tinyg/xio.c b/firmware/tinyg/xio.c index bfdde90ca..0a639db67 100755 --- a/firmware/tinyg/xio.c +++ b/firmware/tinyg/xio.c @@ -88,6 +88,12 @@ typedef struct xioSingleton { } xioSingleton_t; xioSingleton_t xio; +// Static structure allocations +xioDev_t ds[XIO_DEV_COUNT]; // allocate top-level dev structs +xioUsart_t us[XIO_DEV_USART_COUNT]; // USART extended IO structs +xioSpi_t spi[XIO_DEV_SPI_COUNT]; // SPI extended IO structs +xioFile_t fs[XIO_DEV_FILE_COUNT]; // FILE extended IO structs + /******************************************************************************** * XIO Initializations, Resets and Assertions */ @@ -143,23 +149,23 @@ uint8_t xio_test_assertions() /* * xio_isbusy() - return TRUE if XIO sub-system is busy - * - * This function is here so that the caller can detect that the serial system is active - * and therefore generating interrupts. This is a hack for the earlier AVRs that require - * interrupts to be disabled for EEPROM write so the caller can see if the XIO system is - * quiescent. This is used by the G10 deferred writeback persistence functions. - * - * Idle conditions: - * - The serial RX buffer is empty, indicating with some probability that data is not being sent - * - The serial TX buffers are empty - */ - -uint8_t xio_isbusy() -{ - if (xio_get_rx_bufcount_usart(&USBu) != 0) return (false); - if (xio_get_tx_bufcount_usart(&USBu) != 0) return (false); - return (true); -} + * + * This function is here so that the caller can detect that the serial system is active + * and therefore generating interrupts. This is a hack for the earlier AVRs that require + * interrupts to be disabled for EEPROM write so the caller can see if the XIO system is + * quiescent. This is used by the G10 deferred writeback persistence functions. + * + * Idle conditions: + * - The serial RX buffer is empty, indicating with some probability that data is not being sent + * - The serial TX buffers are empty + */ + +uint8_t xio_isbusy() +{ + if (xio_get_rx_bufcount_usart(&USBu) != 0) return (false); + if (xio_get_tx_bufcount_usart(&USBu) != 0) return (false); + return (true); +} /* * xio_reset_working_flags() diff --git a/firmware/tinyg/xio.h b/firmware/tinyg/xio.h index 709945c2f..5185bd913 100755 --- a/firmware/tinyg/xio.h +++ b/firmware/tinyg/xio.h @@ -163,11 +163,10 @@ typedef void (*x_flow_t)(xioDev_t *d); #include "xio/xio_usart.h" #include "xio/xio_spi.h" -// Static structure allocations -xioDev_t ds[XIO_DEV_COUNT]; // allocate top-level dev structs -xioUsart_t us[XIO_DEV_USART_COUNT]; // USART extended IO structs -xioSpi_t spi[XIO_DEV_SPI_COUNT]; // SPI extended IO structs -xioFile_t fs[XIO_DEV_FILE_COUNT]; // FILE extended IO structs +extern xioDev_t ds[XIO_DEV_COUNT]; // allocate top-level dev structs +extern xioUsart_t us[XIO_DEV_USART_COUNT]; // USART extended IO structs +extern xioSpi_t spi[XIO_DEV_SPI_COUNT]; // SPI extended IO structs +extern xioFile_t fs[XIO_DEV_FILE_COUNT]; // FILE extended IO structs extern struct controllerSingleton tg; // needed by init() for default source /************************************************************************* @@ -182,7 +181,7 @@ extern struct controllerSingleton tg; // needed by init() for default source void xio_init(void); void xio_init_assertions(void); uint8_t xio_test_assertions(void); -uint8_t xio_isbusy(void); +uint8_t xio_isbusy(void); void xio_reset_working_flags(xioDev_t *d); FILE *xio_open(const uint8_t dev, const char *addr, const flags_t flags); From 7d9d74014159346cf28270d315de04af65946621 Mon Sep 17 00:00:00 2001 From: Ian Jamison Date: Thu, 9 Feb 2023 00:03:21 +0000 Subject: [PATCH 03/18] Add compile flags matching Debug Makefile. Include min pagesize workaround for avr-gcc version 12 bug, see here: https://gcc.gnu.org/bugzilla//show_bug.cgi?id=105523 --- firmware/tinyg/CMakeLists.txt | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/firmware/tinyg/CMakeLists.txt b/firmware/tinyg/CMakeLists.txt index 36cb8893f..564f0e2ad 100644 --- a/firmware/tinyg/CMakeLists.txt +++ b/firmware/tinyg/CMakeLists.txt @@ -49,5 +49,25 @@ add_executable(tinyg.elf xmega/xmega_rtc.c ) -target_compile_options(tinyg.elf PRIVATE -mmcu=atxmega192a3 -Os) -target_link_options(tinyg.elf PRIVATE -mmcu=atxmega192a3 -Os) \ No newline at end of file +target_compile_definitions(tinyg.elf PRIVATE F_CPU=32000000UL) +target_compile_options(tinyg.elf PRIVATE + -mmcu=atxmega192a3 + -O2 -g2 -Wall + --param=min-pagesize=0 + -funsigned-char + -funsigned-bitfields + -fno-align-functions + -fno-align-jumps + -fno-align-loops + -fno-align-labels + -fno-reorder-blocks + -fno-reorder-blocks-and-partition + -fno-prefetch-loop-arrays + -fno-tree-vect-loop-version + -ffunction-sections + -fdata-sections + -fpack-struct + -fshort-enums +) +target_link_options(tinyg.elf PRIVATE -mmcu=atxmega192a3) + From f534ec2ee3ad1ab21b671f9fa9a1fdb2f7daa34d Mon Sep 17 00:00:00 2001 From: Ian Jamison Date: Thu, 9 Feb 2023 00:03:37 +0000 Subject: [PATCH 04/18] Add custom command to generate Intel hex --- firmware/tinyg/CMakeLists.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/firmware/tinyg/CMakeLists.txt b/firmware/tinyg/CMakeLists.txt index 564f0e2ad..ae2af8761 100644 --- a/firmware/tinyg/CMakeLists.txt +++ b/firmware/tinyg/CMakeLists.txt @@ -71,3 +71,9 @@ target_compile_options(tinyg.elf PRIVATE ) target_link_options(tinyg.elf PRIVATE -mmcu=atxmega192a3) +add_custom_command( + TARGET tinyg.elf + POST_BUILD + COMMAND ${CMAKE_OBJCOPY} + ARGS -O ihex $ ${PROJECT_BINARY_DIR}/tinyg.hex +) \ No newline at end of file From 6a3a690156be06b8a918b9d43a1f2afdbd139310 Mon Sep 17 00:00:00 2001 From: Ian Jamison Date: Thu, 9 Feb 2023 00:12:10 +0000 Subject: [PATCH 05/18] Use avr-objcopy instead of default objcopy Not that it seems to make any difference. --- firmware/tinyg/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware/tinyg/CMakeLists.txt b/firmware/tinyg/CMakeLists.txt index ae2af8761..95ac4c0ba 100644 --- a/firmware/tinyg/CMakeLists.txt +++ b/firmware/tinyg/CMakeLists.txt @@ -74,6 +74,6 @@ target_link_options(tinyg.elf PRIVATE -mmcu=atxmega192a3) add_custom_command( TARGET tinyg.elf POST_BUILD - COMMAND ${CMAKE_OBJCOPY} + COMMAND avr-objcopy ARGS -O ihex $ ${PROJECT_BINARY_DIR}/tinyg.hex ) \ No newline at end of file From 040cc50d6805d57d61b6c98a8d4f80fa47a1cf44 Mon Sep 17 00:00:00 2001 From: Ian Jamison Date: Tue, 14 Feb 2023 13:49:17 +0000 Subject: [PATCH 06/18] Fix GCC string format and buffer overflow warnings --- firmware/tinyg/controller.c | 111 +++++----- firmware/tinyg/json_parser.c | 379 ++++++++++++++++++----------------- 2 files changed, 247 insertions(+), 243 deletions(-) diff --git a/firmware/tinyg/controller.c b/firmware/tinyg/controller.c index 940ebe22d..a813cf3ad 100755 --- a/firmware/tinyg/controller.c +++ b/firmware/tinyg/controller.c @@ -3,7 +3,7 @@ * This file is part of the TinyG project * * Copyright (c) 2010 - 2015 Alden S. Hart, Jr. - * Copyright (c) 2013 - 2015 Robert Giseburt + * Copyright (c) 2013 - 2015 Robert Giseburt * * This file ("the software") is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2 as published by the @@ -36,7 +36,7 @@ #include "plan_arc.h" #include "planner.h" #include "stepper.h" - + #include "encoder.h" #include "hardware.h" #include "switch.h" @@ -146,7 +146,7 @@ void controller_run() _controller_HSM(); } } - + #define DISPATCH(func) if (func == STAT_EAGAIN) return; static void _controller_HSM() { @@ -200,54 +200,54 @@ static void _controller_HSM() static stat_t _command_dispatch() { -#ifdef __AVR - stat_t status; - - // read input line or return if not a completed line - // xio_gets() is a non-blocking workalike of fgets() - while (true) { - if ((status = xio_gets(cs.primary_src, cs.in_buf, sizeof(cs.in_buf))) == STAT_OK) { - cs.bufp = cs.in_buf; - break; - } - // handle end-of-file from file devices - if (status == STAT_EOF) { // EOF can come from file devices only - if (cfg.comm_mode == TEXT_MODE) { - fprintf_P(stderr, PSTR("End of command file\n")); - } else { - rpt_exception(STAT_EOF); // not really an exception - } - tg_reset_source(); // reset to default source - } - return (status); // Note: STAT_EAGAIN, errors, etc. will drop through - } -#endif // __AVR -#ifdef __ARM - // detect USB connection and transition to disconnected state if it disconnected - if (SerialUSB.isConnected() == false) cs.state = CONTROLLER_NOT_CONNECTED; - - // read input line and return if not a completed line - if (cs.state == CONTROLLER_READY) { - if (read_line(cs.in_buf, &cs.read_index, sizeof(cs.in_buf)) != STAT_OK) { - cs.bufp = cs.in_buf; - return (STAT_OK); // This is an exception: returns OK for anything NOT OK, so the idler always runs - } - } else if (cs.state == CONTROLLER_NOT_CONNECTED) { - if (SerialUSB.isConnected() == false) return (STAT_OK); - cm_request_queue_flush(); - rpt_print_system_ready_message(); - cs.state = CONTROLLER_STARTUP; - - } else if (cs.state == CONTROLLER_STARTUP) { // run startup code - cs.state = CONTROLLER_READY; - - } else { - return (STAT_OK); - } - cs.read_index = 0; +#ifdef __AVR + stat_t status; + + // read input line or return if not a completed line + // xio_gets() is a non-blocking workalike of fgets() + while (true) { + if ((status = xio_gets(cs.primary_src, cs.in_buf, sizeof(cs.in_buf))) == STAT_OK) { + cs.bufp = cs.in_buf; + break; + } + // handle end-of-file from file devices + if (status == STAT_EOF) { // EOF can come from file devices only + if (cfg.comm_mode == TEXT_MODE) { + fprintf_P(stderr, PSTR("End of command file\n")); + } else { + rpt_exception(STAT_EOF); // not really an exception + } + tg_reset_source(); // reset to default source + } + return (status); // Note: STAT_EAGAIN, errors, etc. will drop through + } +#endif // __AVR +#ifdef __ARM + // detect USB connection and transition to disconnected state if it disconnected + if (SerialUSB.isConnected() == false) cs.state = CONTROLLER_NOT_CONNECTED; + + // read input line and return if not a completed line + if (cs.state == CONTROLLER_READY) { + if (read_line(cs.in_buf, &cs.read_index, sizeof(cs.in_buf)) != STAT_OK) { + cs.bufp = cs.in_buf; + return (STAT_OK); // This is an exception: returns OK for anything NOT OK, so the idler always runs + } + } else if (cs.state == CONTROLLER_NOT_CONNECTED) { + if (SerialUSB.isConnected() == false) return (STAT_OK); + cm_request_queue_flush(); + rpt_print_system_ready_message(); + cs.state = CONTROLLER_STARTUP; + + } else if (cs.state == CONTROLLER_STARTUP) { // run startup code + cs.state = CONTROLLER_READY; + + } else { + return (STAT_OK); + } + cs.read_index = 0; #endif // __ARM - // set up the buffers + // set up the buffers cs.linelen = strlen(cs.in_buf)+1; // linelen only tracks primary input strncpy(cs.saved_buf, cs.bufp, SAVED_BUFFER_LEN-1); // save input buffer for reporting @@ -276,8 +276,9 @@ static stat_t _command_dispatch() } default: { // anything else must be Gcode if (cfg.comm_mode == JSON_MODE) { // run it as JSON... - strncpy(cs.out_buf, cs.bufp, INPUT_BUFFER_LEN -8); // use out_buf as temp - sprintf((char *)cs.bufp,"{\"gc\":\"%s\"}\n", (char *)cs.out_buf); // '-8' is used for JSON chars + strncpy(cs.out_buf, cs.bufp, INPUT_BUFFER_LEN -10); // use out_buf as temp + cs.out_buf[INPUT_BUFFER_LEN-10] = '\0'; + snprintf(cs.bufp,INPUT_BUFFER_LEN,"{\"gc\":\"%.244s\"}\n", cs.out_buf); // 10 chars used for JSON shell json_parser(cs.bufp); } else { //...or run it as text text_response(gc_gcode_parser(cs.bufp), cs.saved_buf); @@ -287,7 +288,7 @@ static stat_t _command_dispatch() return (STAT_OK); } - + /**** Local Utilities ********************************************************/ /* * _shutdown_idler() - blink rapidly and prevent further activity from occurring @@ -406,7 +407,7 @@ static stat_t _sync_to_planner() } return (STAT_OK); } - + /* static stat_t _sync_to_time() { @@ -426,7 +427,7 @@ static stat_t _sync_to_time() static stat_t _limit_switch_handler(void) { if (cm_get_machine_state() == MACHINE_ALARM) { return (STAT_NOOP);} - + if (get_limit_switch_thrown() == false) return (STAT_NOOP); return(cm_hard_alarm(STAT_LIMIT_SWITCH_HIT)); return (STAT_OK); @@ -435,7 +436,7 @@ static stat_t _limit_switch_handler(void) /* * _system_assertions() - check memory integrity and other assertions */ -#define emergency___everybody_to_get_from_street(a) if((status_code=a) != STAT_OK) return (cm_hard_alarm(status_code)); +#define emergency___everybody_to_get_from_street(a) if((status_code=a) != STAT_OK) return (cm_hard_alarm(status_code)); stat_t _system_assertions() { diff --git a/firmware/tinyg/json_parser.c b/firmware/tinyg/json_parser.c index aec4393c9..9c8915be7 100755 --- a/firmware/tinyg/json_parser.c +++ b/firmware/tinyg/json_parser.c @@ -1,47 +1,47 @@ /* * json_parser.c - JSON parser for TinyG - * This file is part of the TinyG project - * - * Copyright (c) 2011 - 2015 Alden S. Hart, Jr. + * This file is part of the TinyG project * - * This file ("the software") is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License, version 2 as published by the - * Free Software Foundation. You should have received a copy of the GNU General Public - * License, version 2 along with the software. If not, see . - * - * As a special exception, you may use this file as part of a software library without - * restriction. Specifically, if other files instantiate templates or use macros or - * inline functions from this file, or you compile this file and link it with other - * files to produce an executable, this file does not by itself cause the resulting - * executable to be covered by the GNU General Public License. This exception does not - * however invalidate any other reasons why the executable file might be covered by the - * GNU General Public License. - * - * THE SOFTWARE IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL, BUT WITHOUT ANY - * WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT - * SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF - * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * Copyright (c) 2011 - 2015 Alden S. Hart, Jr. + * + * This file ("the software") is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2 as published by the + * Free Software Foundation. You should have received a copy of the GNU General Public + * License, version 2 along with the software. If not, see . + * + * As a special exception, you may use this file as part of a software library without + * restriction. Specifically, if other files instantiate templates or use macros or + * inline functions from this file, or you compile this file and link it with other + * files to produce an executable, this file does not by itself cause the resulting + * executable to be covered by the GNU General Public License. This exception does not + * however invalidate any other reasons why the executable file might be covered by the + * GNU General Public License. + * + * THE SOFTWARE IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL, BUT WITHOUT ANY + * WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT + * SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF + * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "tinyg.h" #include "config.h" // JSON sits on top of the config system #include "controller.h" -#include "json_parser.h" +#include "json_parser.h" #include "text_parser.h" #include "canonical_machine.h" #include "report.h" #include "util.h" #include "xio.h" // for char definitions - -#ifdef __cplusplus -extern "C"{ -#endif - -/**** Allocation ****/ - -jsSingleton_t js; + +#ifdef __cplusplus +extern "C"{ +#endif + +/**** Allocation ****/ + +jsSingleton_t js; /**** local scope stuff ****/ @@ -117,13 +117,15 @@ static stat_t _json_parser_kernal(char_t *str) // propagate the group from previous NV pair (if relevant) if (group[0] != NUL) { strncpy(nv->group, group, GROUP_LEN); // copy the parent's group to this child + nv->group[GROUP_LEN] = '\0'; } - // validate the token and get the index + // validate the token and get the index if ((nv->index = nv_get_index(nv->group, nv->token)) == NO_MATCH) { - return (STAT_UNRECOGNIZED_NAME); + return (STAT_UNRECOGNIZED_NAME); } if ((nv_index_is_group(nv->index)) && (nv_group_is_prefixed(nv->token))) { strncpy(group, nv->token, GROUP_LEN); // record the group ID + group[GROUP_LEN] = '\0'; } if ((nv = nv->nx) == NULL) return (STAT_JSON_TOO_MANY_PAIRS); // Not supposed to encounter a NULL @@ -134,8 +136,8 @@ static stat_t _json_parser_kernal(char_t *str) if (nv->valuetype == TYPE_NULL){ // means GET the value ritorno(nv_get(nv)); // ritorno returns w/status on any errors } else { - if (cm.machine_state == MACHINE_ALARM) - return (STAT_MACHINE_ALARMED); + if (cm.machine_state == MACHINE_ALARM) + return (STAT_MACHINE_ALARMED); ritorno(nv_set(nv)); // set value or call a function (e.g. gcode) nv_persist(nv); } @@ -194,14 +196,14 @@ static stat_t _normalize_json_string(char_t *str, uint16_t size) /* RELAXED RULES * * Quotes are accepted but not needed on names - * Quotes are required for string values - * + * Quotes are required for string values + * * See build 406.xx or earlier for strict JSON parser - deleted in 407.03 - */ - -#define MAX_PAD_CHARS 8 -#define MAX_NAME_CHARS 32 - + */ + +#define MAX_PAD_CHARS 8 +#define MAX_NAME_CHARS 32 + static stat_t _get_nv_pair(nvObj_t *nv, char_t **pstr, int8_t *depth) { uint8_t i; @@ -216,36 +218,37 @@ static stat_t _get_nv_pair(nvObj_t *nv, char_t **pstr, int8_t *depth) // --- Process name part --- // Find, terminate and set pointers for the name. Allow for leading and trailing name quotes. char_t * name = *pstr; - for (i=0; true; i++, (*pstr)++) { - if (strchr(leaders, (int)**pstr) == NULL) { // find leading character of name - name = (*pstr)++; - break; - } - if (i == MAX_PAD_CHARS) - return (STAT_JSON_SYNTAX_ERROR); - } - - // Find the end of name, NUL terminate and copy token - for (i=0; true; i++, (*pstr)++) { - if (strchr(separators, (int)**pstr) != NULL) { + for (i=0; true; i++, (*pstr)++) { + if (strchr(leaders, (int)**pstr) == NULL) { // find leading character of name + name = (*pstr)++; + break; + } + if (i == MAX_PAD_CHARS) + return (STAT_JSON_SYNTAX_ERROR); + } + + // Find the end of name, NUL terminate and copy token + for (i=0; true; i++, (*pstr)++) { + if (strchr(separators, (int)**pstr) != NULL) { *(*pstr)++ = NUL; - strncpy(nv->token, name, TOKEN_LEN+1); // copy the string to the token - break; - } - if (i == MAX_NAME_CHARS) - return (STAT_JSON_SYNTAX_ERROR); - } - - // --- Process value part --- (organized from most to least frequently encountered) - - // Find the start of the value part - for (i=0; true; i++, (*pstr)++) { - if (isalnum((int)**pstr)) break; - if (strchr(value, (int)**pstr) != NULL) break; - if (i == MAX_PAD_CHARS) - return (STAT_JSON_SYNTAX_ERROR); - } - + strncpy(nv->token, name, TOKEN_LEN); // copy the string to the token + nv->token[TOKEN_LEN] = '\0'; + break; + } + if (i == MAX_NAME_CHARS) + return (STAT_JSON_SYNTAX_ERROR); + } + + // --- Process value part --- (organized from most to least frequently encountered) + + // Find the start of the value part + for (i=0; true; i++, (*pstr)++) { + if (isalnum((int)**pstr)) break; + if (strchr(value, (int)**pstr) != NULL) break; + if (i == MAX_PAD_CHARS) + return (STAT_JSON_SYNTAX_ERROR); + } + // nulls (gets) if ((**pstr == 'n') || ((**pstr == '\"') && (*(*pstr+1) == '\"'))) { // process null value nv->valuetype = TYPE_NULL; @@ -253,7 +256,7 @@ static stat_t _get_nv_pair(nvObj_t *nv, char_t **pstr, int8_t *depth) // numbers } else if (isdigit(**pstr) || (**pstr == '-')) {// value is a number - nv->value = (float)strtod(*pstr, &tmp); // tmp is the end pointer + nv->value = (float)strtod(*pstr, &tmp); // tmp is the end pointer if(tmp == *pstr) return (STAT_BAD_NUMBER_FORMAT); nv->valuetype = TYPE_FLOAT; @@ -273,17 +276,17 @@ static stat_t _get_nv_pair(nvObj_t *nv, char_t **pstr, int8_t *depth) return (STAT_JSON_SYNTAX_ERROR); // find the end of the string *tmp = NUL; - // if string begins with 0x it might be data, needs to be at least 3 chars long - if( strlen(*pstr)>=3 && (*pstr)[0]=='0' && (*pstr)[1]=='x') - { - uint32_t *v = (uint32_t*)&nv->value; - *v = strtoul((const char *)*pstr, 0L, 0); - nv->valuetype = TYPE_DATA; - } else { - ritorno(nv_copy_string(nv, *pstr)); - } - - *pstr = ++tmp; + // if string begins with 0x it might be data, needs to be at least 3 chars long + if( strlen(*pstr)>=3 && (*pstr)[0]=='0' && (*pstr)[1]=='x') + { + uint32_t *v = (uint32_t*)&nv->value; + *v = strtoul((const char *)*pstr, 0L, 0); + nv->valuetype = TYPE_DATA; + } else { + ritorno(nv_copy_string(nv, *pstr)); + } + + *pstr = ++tmp; // boolean true/false } else if (**pstr == 't') { @@ -352,17 +355,17 @@ static stat_t _get_nv_pair(nvObj_t *nv, char_t **pstr, int8_t *depth) */ #define BUFFER_MARGIN 8 // safety margin to avoid buffer overruns during footer checksum generation - -uint16_t json_serialize(nvObj_t *nv, char_t *out_buf, uint16_t size) -{ -#ifdef __SILENCE_JSON_RESPONSES - return (0); -#else + +uint16_t json_serialize(nvObj_t *nv, char_t *out_buf, uint16_t size) +{ +#ifdef __SILENCE_JSON_RESPONSES + return (0); +#else char_t *str = out_buf; char_t *str_max = out_buf + size - BUFFER_MARGIN; int8_t initial_depth = nv->depth; int8_t prev_depth = 0; - uint8_t need_a_comma = false; + uint8_t need_a_comma = false; *str++ = '{'; // write opening curly @@ -370,25 +373,25 @@ uint16_t json_serialize(nvObj_t *nv, char_t *out_buf, uint16_t size) if (nv->valuetype != TYPE_EMPTY) { if (need_a_comma) { *str++ = ',';} need_a_comma = true; - if (js.json_syntax == JSON_SYNTAX_RELAXED) { // write name - str += sprintf((char *)str, "%s:", nv->token); + if (js.json_syntax == JSON_SYNTAX_RELAXED) { // write name + str += sprintf((char *)str, "%s:", nv->token); } else { str += sprintf((char *)str, "\"%s\":", nv->token); - } - - // check for illegal float values - if (nv->valuetype == TYPE_FLOAT) { - if (isnan((double)nv->value) || isinf((double)nv->value)) { nv->value = 0;} - } - - // serialize output value + } + + // check for illegal float values + if (nv->valuetype == TYPE_FLOAT) { + if (isnan((double)nv->value) || isinf((double)nv->value)) { nv->value = 0;} + } + + // serialize output value if (nv->valuetype == TYPE_NULL) { str += (char_t)sprintf((char *)str, "null");} // Note that that "" is NOT null. else if (nv->valuetype == TYPE_INTEGER) { str += (char_t)sprintf((char *)str, "%1.0f", (double)nv->value); } - else if (nv->valuetype == TYPE_DATA) { - uint32_t *v = (uint32_t*)&nv->value; - str += (char_t)sprintf((char *)str, "\"0x%lx\"", *v); + else if (nv->valuetype == TYPE_DATA) { + uint32_t *v = (uint32_t*)&nv->value; + str += (char_t)sprintf((char *)str, "\"0x%lx\"", *v); } else if (nv->valuetype == TYPE_STRING) { str += (char_t)sprintf((char *)str, "\"%s\"",(char *)*nv->stringp);} else if (nv->valuetype == TYPE_ARRAY) { str += (char_t)sprintf((char *)str, "[%s]", (char *)*nv->stringp);} @@ -419,9 +422,9 @@ uint16_t json_serialize(nvObj_t *nv, char_t *out_buf, uint16_t size) while (prev_depth-- > initial_depth) { *str++ = '}';} str += sprintf((char *)str, "}\n"); // using sprintf for this last one ensures a NUL termination if (str > out_buf + size) { return (-1);} - return (str - out_buf); -#endif -} + return (str - out_buf); +#endif +} /* * json_print_object() - serialize and print the nvObj array directly (w/o header & footer) @@ -431,27 +434,27 @@ uint16_t json_serialize(nvObj_t *nv, char_t *out_buf, uint16_t size) * Object list should be terminated by nv->nx == NULL */ void json_print_object(nvObj_t *nv) -{ -#ifdef __SILENCE_JSON_RESPONSES - return; -#endif +{ +#ifdef __SILENCE_JSON_RESPONSES + return; +#endif json_serialize(nv, cs.out_buf, sizeof(cs.out_buf)); fprintf(stderr, "%s", (char *)cs.out_buf); } - -/* - * json_print_list() - command to select and produce a JSON formatted output - */ - -void json_print_list(stat_t status, uint8_t flags) -{ - switch (flags) { - case JSON_NO_PRINT: { break; } - case JSON_OBJECT_FORMAT: { json_print_object(nv_body); break; } - case JSON_RESPONSE_FORMAT: { json_print_response(status); break; } - } -} + +/* + * json_print_list() - command to select and produce a JSON formatted output + */ + +void json_print_list(stat_t status, uint8_t flags) +{ + switch (flags) { + case JSON_NO_PRINT: { break; } + case JSON_OBJECT_FORMAT: { json_print_object(nv_body); break; } + case JSON_RESPONSE_FORMAT: { json_print_response(status); break; } + } +} /* * json_print_response() - JSON responses with headers, footers and observing JSON verbosity @@ -474,10 +477,10 @@ void json_print_list(stat_t status, uint8_t flags) #define MAX_TAIL_LEN 8 void json_print_response(uint8_t status) -{ -#ifdef __SILENCE_JSON_RESPONSES - return; -#endif +{ +#ifdef __SILENCE_JSON_RESPONSES + return; +#endif if (js.json_verbosity == JV_SILENT) return; // silent responses @@ -547,64 +550,64 @@ void json_print_response(uint8_t status) sprintf((char *)cs.out_buf + strcount2 + 1, "%d%s", compute_checksum(cs.out_buf, strcount2), tail); fprintf(stderr, "%s", cs.out_buf); } - -/*********************************************************************************** - * CONFIGURATION AND INTERFACE FUNCTIONS - * Functions to get and set variables from the cfgArray table - ***********************************************************************************/ - -/* - * json_set_jv() - */ - -stat_t json_set_jv(nvObj_t *nv) -{ - if (nv->value > JV_VERBOSE) - return (STAT_INPUT_VALUE_RANGE_ERROR); - js.json_verbosity = nv->value; - - js.echo_json_footer = false; - js.echo_json_messages = false; - js.echo_json_configs = false; - js.echo_json_linenum = false; - js.echo_json_gcode_block = false; - - if (nv->value >= JV_FOOTER) { js.echo_json_footer = true;} - if (nv->value >= JV_MESSAGES) { js.echo_json_messages = true;} - if (nv->value >= JV_CONFIGS) { js.echo_json_configs = true;} - if (nv->value >= JV_LINENUM) { js.echo_json_linenum = true;} - if (nv->value >= JV_VERBOSE) { js.echo_json_gcode_block = true;} - - return(STAT_OK); -} - - -/*********************************************************************************** - * TEXT MODE SUPPORT - * Functions to print variables from the cfgArray table - ***********************************************************************************/ - -#ifdef __TEXT_MODE - -/* - * js_print_ej() - * js_print_jv() - * js_print_j2() - * js_print_fs() - */ - -static const char fmt_ej[] PROGMEM = "[ej] enable json mode%13d [0=text,1=JSON]\n"; -static const char fmt_jv[] PROGMEM = "[jv] json verbosity%15d [0=silent,1=footer,2=messages,3=configs,4=linenum,5=verbose]\n"; -static const char fmt_js[] PROGMEM = "[js] json serialize style%9d [0=relaxed,1=strict]\n"; -static const char fmt_fs[] PROGMEM = "[fs] footer style%17d [0=new,1=old]\n"; - -void js_print_ej(nvObj_t *nv) { text_print_ui8(nv, fmt_ej);} -void js_print_jv(nvObj_t *nv) { text_print_ui8(nv, fmt_jv);} -void js_print_js(nvObj_t *nv) { text_print_ui8(nv, fmt_js);} -void js_print_fs(nvObj_t *nv) { text_print_ui8(nv, fmt_fs);} - -#endif // __TEXT_MODE - -#ifdef __cplusplus -} -#endif // __cplusplus + +/*********************************************************************************** + * CONFIGURATION AND INTERFACE FUNCTIONS + * Functions to get and set variables from the cfgArray table + ***********************************************************************************/ + +/* + * json_set_jv() + */ + +stat_t json_set_jv(nvObj_t *nv) +{ + if (nv->value > JV_VERBOSE) + return (STAT_INPUT_VALUE_RANGE_ERROR); + js.json_verbosity = nv->value; + + js.echo_json_footer = false; + js.echo_json_messages = false; + js.echo_json_configs = false; + js.echo_json_linenum = false; + js.echo_json_gcode_block = false; + + if (nv->value >= JV_FOOTER) { js.echo_json_footer = true;} + if (nv->value >= JV_MESSAGES) { js.echo_json_messages = true;} + if (nv->value >= JV_CONFIGS) { js.echo_json_configs = true;} + if (nv->value >= JV_LINENUM) { js.echo_json_linenum = true;} + if (nv->value >= JV_VERBOSE) { js.echo_json_gcode_block = true;} + + return(STAT_OK); +} + + +/*********************************************************************************** + * TEXT MODE SUPPORT + * Functions to print variables from the cfgArray table + ***********************************************************************************/ + +#ifdef __TEXT_MODE + +/* + * js_print_ej() + * js_print_jv() + * js_print_j2() + * js_print_fs() + */ + +static const char fmt_ej[] PROGMEM = "[ej] enable json mode%13d [0=text,1=JSON]\n"; +static const char fmt_jv[] PROGMEM = "[jv] json verbosity%15d [0=silent,1=footer,2=messages,3=configs,4=linenum,5=verbose]\n"; +static const char fmt_js[] PROGMEM = "[js] json serialize style%9d [0=relaxed,1=strict]\n"; +static const char fmt_fs[] PROGMEM = "[fs] footer style%17d [0=new,1=old]\n"; + +void js_print_ej(nvObj_t *nv) { text_print_ui8(nv, fmt_ej);} +void js_print_jv(nvObj_t *nv) { text_print_ui8(nv, fmt_jv);} +void js_print_js(nvObj_t *nv) { text_print_ui8(nv, fmt_js);} +void js_print_fs(nvObj_t *nv) { text_print_ui8(nv, fmt_fs);} + +#endif // __TEXT_MODE + +#ifdef __cplusplus +} +#endif // __cplusplus From b157592d34df09b619487fe4af01b6eb67e1e702 Mon Sep 17 00:00:00 2001 From: Ian Jamison Date: Wed, 22 Feb 2023 22:34:44 +0000 Subject: [PATCH 07/18] Revert back to default settings at Mark's suggestion since settings_openpnp.h is of unknown quality. --- firmware/tinyg/settings.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/firmware/tinyg/settings.h b/firmware/tinyg/settings.h index 1eb1a69f4..e6cd1c965 100755 --- a/firmware/tinyg/settings.h +++ b/firmware/tinyg/settings.h @@ -45,9 +45,9 @@ #define SOFT_LIMIT_ENABLE 0 // 0 = off, 1 = on #define SWITCH_TYPE SW_TYPE_NORMALLY_OPEN // one of: SW_TYPE_NORMALLY_OPEN, SW_TYPE_NORMALLY_CLOSED -#define MOTOR_POWER_MODE MOTOR_POWERED_IN_CYCLE // one of: MOTOR_DISABLED (0) - // MOTOR_ALWAYS_POWERED (1) - // MOTOR_POWERED_IN_CYCLE (2) +#define MOTOR_POWER_MODE MOTOR_POWERED_IN_CYCLE // one of: MOTOR_DISABLED (0) + // MOTOR_ALWAYS_POWERED (1) + // MOTOR_POWERED_IN_CYCLE (2) // MOTOR_POWERED_ONLY_WHEN_MOVING (3) #define MOTOR_IDLE_TIMEOUT 2.00 // seconds to maintain motor at full power before idling @@ -105,13 +105,13 @@ // machine default profiles - choose only one: -//#include "settings/settings_default.h" // Default settings for release +#include "settings/settings_default.h" // Default settings for release //#include "settings/settings_cnc3040.h" //#include "settings/settings_test.h" // Settings for testing - not for release -#include "settings/settings_openpnp.h" // OpenPnP +//#include "settings/settings_openpnp.h" // OpenPnP //#include "settings/settings_othermill.h" // OMC OtherMill //#include "settings/settings_probotixV90.h" // Probotix Fireball V90 -//#include "settings/settings_shapeoko2.h" // Shapeoko2 - standard kit +//#include "settings/settings_shapeoko2.h" // Shapeoko2 - standard kit //#include "settings/settings_ultimaker.h" // Ultimaker 3D printer //#include "settings/settings_zen7x12.h" // Zen Toolworks 7x12 From 66280ca8156a3eca07701ec20dc79613c3bc548f Mon Sep 17 00:00:00 2001 From: Ian Jamison Date: Wed, 22 Feb 2023 22:45:42 +0000 Subject: [PATCH 08/18] Make avr-gcc 12 fix version dependant --- firmware/tinyg/CMakeLists.txt | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/firmware/tinyg/CMakeLists.txt b/firmware/tinyg/CMakeLists.txt index 95ac4c0ba..acf381749 100644 --- a/firmware/tinyg/CMakeLists.txt +++ b/firmware/tinyg/CMakeLists.txt @@ -53,7 +53,6 @@ target_compile_definitions(tinyg.elf PRIVATE F_CPU=32000000UL) target_compile_options(tinyg.elf PRIVATE -mmcu=atxmega192a3 -O2 -g2 -Wall - --param=min-pagesize=0 -funsigned-char -funsigned-bitfields -fno-align-functions @@ -69,6 +68,14 @@ target_compile_options(tinyg.elf PRIVATE -fpack-struct -fshort-enums ) + +if (CMAKE_COMPILER_IS_GNUCC AND CMAKE_C_COMPILER_VERSION VERSION_GREATER 12.0) + message(STATUS "Addding workaround for avr-gcc 12 bug") + target_compile_options(tinyg.elf PRIVATE + --param=min-pagesize=0 + ) +endif() + target_link_options(tinyg.elf PRIVATE -mmcu=atxmega192a3) add_custom_command( From 792b580fe7b619fa3d487d59b670f804ee7246be Mon Sep 17 00:00:00 2001 From: Ian Jamison Date: Wed, 22 Feb 2023 23:31:50 +0000 Subject: [PATCH 09/18] Add feature tags to firmware response string --- firmware/tinyg/canonical_machine.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware/tinyg/canonical_machine.c b/firmware/tinyg/canonical_machine.c index 163bd417f..6278d61e2 100755 --- a/firmware/tinyg/canonical_machine.c +++ b/firmware/tinyg/canonical_machine.c @@ -850,7 +850,7 @@ stat_t cm_get_adc() stat_t cm_get_firmware() { // M115: Get Firmware Version and Capabilities, see https://www.reprap.org/wiki/G-code#M115:_Get_Firmware_Version_and_Capabilities. - static const char m115_response[] PROGMEM = "FIRMWARE_NAME:TinyG, FIRMWARE_URL:https%%3A//github.com/synthetos/TinyG, FIRMWARE_VERSION:%0.2f, FIRMWARE_BUILD:%0.2f, HARDWARE_PLATFORM:%0.2f, HARDWARE_VERSION:%0.2f\n"; + static const char m115_response[] PROGMEM = "FIRMWARE_NAME:TinyG, FIRMWARE_URL:https%%3A//github.com/synthetos/TinyG, FIRMWARE_VERSION:%0.2f, FIRMWARE_BUILD:%0.2f, HARDWARE_PLATFORM:%0.2f, HARDWARE_VERSION:%0.2f, FEATURE_TAGS:OPENPNP/CMAKE\n"; fprintf_P(stderr, m115_response, cs.fw_version, cs.fw_build, cs.hw_platform, cs.hw_version); return (STAT_OK); } From 54382184780ebcf58eba52685ab0ec2b0305f58f Mon Sep 17 00:00:00 2001 From: Ian Jamison Date: Thu, 23 Feb 2023 09:02:32 +0000 Subject: [PATCH 10/18] Switch M115 tags to use better keywords --- firmware/tinyg/canonical_machine.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware/tinyg/canonical_machine.c b/firmware/tinyg/canonical_machine.c index 6278d61e2..77b928b71 100755 --- a/firmware/tinyg/canonical_machine.c +++ b/firmware/tinyg/canonical_machine.c @@ -850,7 +850,7 @@ stat_t cm_get_adc() stat_t cm_get_firmware() { // M115: Get Firmware Version and Capabilities, see https://www.reprap.org/wiki/G-code#M115:_Get_Firmware_Version_and_Capabilities. - static const char m115_response[] PROGMEM = "FIRMWARE_NAME:TinyG, FIRMWARE_URL:https%%3A//github.com/synthetos/TinyG, FIRMWARE_VERSION:%0.2f, FIRMWARE_BUILD:%0.2f, HARDWARE_PLATFORM:%0.2f, HARDWARE_VERSION:%0.2f, FEATURE_TAGS:OPENPNP/CMAKE\n"; + static const char m115_response[] PROGMEM = "FIRMWARE_NAME:TinyG, FIRMWARE_URL:https%%3A//github.com/synthetos/TinyG, FIRMWARE_VERSION:%0.2f, FIRMWARE_BUILD:%0.2f, HARDWARE_PLATFORM:%0.2f, HARDWARE_VERSION:%0.2f, MACHINE_TYPE:OpenPnP, X-BUILD_SYSTEM:CMake\n"; fprintf_P(stderr, m115_response, cs.fw_version, cs.fw_build, cs.hw_platform, cs.hw_version); return (STAT_OK); } From f31ddac5ec21c3768ea1bf5efdb982422278df07 Mon Sep 17 00:00:00 2001 From: Ian Jamison Date: Tue, 28 Feb 2023 14:07:00 +0000 Subject: [PATCH 11/18] Only include CMake M115 tag in CMake builds --- firmware/tinyg/CMakeLists.txt | 5 ++++- firmware/tinyg/canonical_machine.c | 6 +++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/firmware/tinyg/CMakeLists.txt b/firmware/tinyg/CMakeLists.txt index acf381749..544189e50 100644 --- a/firmware/tinyg/CMakeLists.txt +++ b/firmware/tinyg/CMakeLists.txt @@ -49,7 +49,10 @@ add_executable(tinyg.elf xmega/xmega_rtc.c ) -target_compile_definitions(tinyg.elf PRIVATE F_CPU=32000000UL) +target_compile_definitions(tinyg.elf PRIVATE + F_CPU=32000000UL + M115_BUILD_SYSTEM_STRING=", X-BUILD_SYSTEM:CMake" +) target_compile_options(tinyg.elf PRIVATE -mmcu=atxmega192a3 -O2 -g2 -Wall diff --git a/firmware/tinyg/canonical_machine.c b/firmware/tinyg/canonical_machine.c index 77b928b71..09ed75078 100755 --- a/firmware/tinyg/canonical_machine.c +++ b/firmware/tinyg/canonical_machine.c @@ -847,10 +847,14 @@ stat_t cm_get_adc() return (STAT_OK); } +#ifndef M115_BUILD_SYSTEM_STRING +#define M115_BUILD_SYSTEM_STRING "" +#endif + stat_t cm_get_firmware() { // M115: Get Firmware Version and Capabilities, see https://www.reprap.org/wiki/G-code#M115:_Get_Firmware_Version_and_Capabilities. - static const char m115_response[] PROGMEM = "FIRMWARE_NAME:TinyG, FIRMWARE_URL:https%%3A//github.com/synthetos/TinyG, FIRMWARE_VERSION:%0.2f, FIRMWARE_BUILD:%0.2f, HARDWARE_PLATFORM:%0.2f, HARDWARE_VERSION:%0.2f, MACHINE_TYPE:OpenPnP, X-BUILD_SYSTEM:CMake\n"; + static const char m115_response[] PROGMEM = "FIRMWARE_NAME:TinyG, FIRMWARE_URL:https%%3A//github.com/synthetos/TinyG, FIRMWARE_VERSION:%0.2f, FIRMWARE_BUILD:%0.2f, HARDWARE_PLATFORM:%0.2f, HARDWARE_VERSION:%0.2f, MACHINE_TYPE:OpenPnP" M115_BUILD_SYSTEM_STRING "\n"; fprintf_P(stderr, m115_response, cs.fw_version, cs.fw_build, cs.hw_platform, cs.hw_version); return (STAT_OK); } From da00c7bb74fcda19527aa40efe52bd63aeb75d70 Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Mon, 4 Mar 2024 11:01:12 +1100 Subject: [PATCH 12/18] Successfully compile on OSX --- firmware/tinyg/CMakeLists.txt | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/firmware/tinyg/CMakeLists.txt b/firmware/tinyg/CMakeLists.txt index 544189e50..0fbaf74d1 100644 --- a/firmware/tinyg/CMakeLists.txt +++ b/firmware/tinyg/CMakeLists.txt @@ -1,7 +1,14 @@ cmake_minimum_required(VERSION 3.10) + +# https://stackoverflow.com/questions/54482519/avoid-cmake-to-add-the-flags-search-paths-first-and-headerpad-max-install-name +set(HAVE_FLAG_SEARCH_PATHS_FIRST 0) project(tinyg VERSION 0.1.0) -set(CMAKE_SYSTEM_NAME Generic) +# https://gitlab.kitware.com/cmake/cmake/-/issues/24599 +unset(_CMAKE_APPLE_ARCHS_DEFAULT) +#set(CMAKE_C_COMPILER_TARGET atxmega192a3) +#set(CMAKE_SYSTEM_PROCESSOR ARM) +#set(CMAKE_SYSTEM_NAME Linux) set(CMAKE_C_COMPILER avr-gcc) add_executable(tinyg.elf @@ -86,4 +93,4 @@ add_custom_command( POST_BUILD COMMAND avr-objcopy ARGS -O ihex $ ${PROJECT_BINARY_DIR}/tinyg.hex -) \ No newline at end of file +) From 445a80c5f3632c2c1161f5d81e78890230b36287 Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Mon, 4 Mar 2024 13:51:59 +1100 Subject: [PATCH 13/18] Add example VSCode-CMake config --- .vscode/c_cpp_properties.json | 17 +++++++++++++++++ .vscode/settings.json | 6 ++++++ .vscode/tasks.json | 19 +++++++++++++++++++ 3 files changed, 42 insertions(+) create mode 100644 .vscode/c_cpp_properties.json create mode 100644 .vscode/settings.json create mode 100644 .vscode/tasks.json diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json new file mode 100644 index 000000000..a71c11a92 --- /dev/null +++ b/.vscode/c_cpp_properties.json @@ -0,0 +1,17 @@ +{ + "configurations": [ + { + "name": "Mac", + "includePath": [ + "${workspaceFolder}/**" + ], + "defines": [], + "macFrameworkPath": [ + "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks" + ], + "compilerPath": "/opt/homebrew/opt/avr-gcc@12/bin/avr-gcc", + "cStandard": "c17" + } + ], + "version": 4 +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 000000000..58b3ce938 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,6 @@ +{ + "files.associations": { + "tinyg.h": "c" + }, + "cmake.sourceDirectory": "${workspaceFolder}/firmware/tinyg" +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 000000000..5fb03c01f --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,19 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "type": "cmake", + "label": "CMake: build", + "command": "build", + "targets": [ + "all" + ], + "group": { + "kind": "build", + "isDefault": true + }, + "problemMatcher": [], + "detail": "CMake template build task" + } + ] +} \ No newline at end of file From 2da375ecd079c379b1395a8de5215d9dd97f6dc6 Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Tue, 5 Mar 2024 18:08:07 +1100 Subject: [PATCH 14/18] Add xmega TWI (I2C) definitions and AS5600 encoder driver --- .gitignore | 1 + .vscode/c_cpp_properties.json | 18 +- .vscode/settings.json | 3 +- .vscode/tasks.json | 14 +- firmware/.gitignore | 1 + firmware/tinyg/.gitignore | 3 +- firmware/tinyg/CMakeLists.txt | 2 + firmware/tinyg/encoder.c | 45 +- firmware/tinyg/encoder_as5600.c | 1421 ++++++++++++++++++++++++++++++ firmware/tinyg/encoder_as5600.h | 735 ++++++++++++++++ firmware/tinyg/main.c | 128 +-- firmware/tinyg/xmega/xmega_twi.c | 249 ++++++ firmware/tinyg/xmega/xmega_twi.h | 141 +++ 13 files changed, 2670 insertions(+), 91 deletions(-) create mode 100644 firmware/tinyg/encoder_as5600.c create mode 100644 firmware/tinyg/encoder_as5600.h create mode 100644 firmware/tinyg/xmega/xmega_twi.c create mode 100644 firmware/tinyg/xmega/xmega_twi.h diff --git a/.gitignore b/.gitignore index 05547699a..6c352e165 100755 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ firmware/tinyg/default/path firmware/tinyg/Release/Makefile firmware/tinyg/Release/makedep.mk firmware/tinyg/default/path +build tinyg.hex diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json index a71c11a92..03b0c15ec 100644 --- a/.vscode/c_cpp_properties.json +++ b/.vscode/c_cpp_properties.json @@ -3,14 +3,26 @@ { "name": "Mac", "includePath": [ - "${workspaceFolder}/**" + "${workspaceFolder}/**", + "/opt/homebrew/Cellar/avr-gcc@12/12.2.0_2/avr/include/**" + ], + "defines": [ + "__AVR", + "__DOXYGEN__", + "__AVR_ATxmega192A3__", + "AS_5600_ENCODER" ], - "defines": [], "macFrameworkPath": [ "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks" ], "compilerPath": "/opt/homebrew/opt/avr-gcc@12/bin/avr-gcc", - "cStandard": "c17" + "cStandard": "c17", + "intelliSenseMode": "linux-gcc-x64", + "compilerArgs": [ + "-mmcu=atmega192a3", // Will ensure MCU defines are set correctly + "-DF_CPU=16000000UL", // Will ensure F_CPU is set correctly + "-Os" // Will avoid optimization warnings re: _delay + ] } ], "version": 4 diff --git a/.vscode/settings.json b/.vscode/settings.json index 58b3ce938..a7d176832 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,6 +1,7 @@ { "files.associations": { - "tinyg.h": "c" + "tinyg.h": "c", + "xio.h": "c" }, "cmake.sourceDirectory": "${workspaceFolder}/firmware/tinyg" } \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 5fb03c01f..cfd806a1e 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -12,8 +12,20 @@ "kind": "build", "isDefault": true }, - "problemMatcher": [], + "problemMatcher": [ + "$gcc" + ], "detail": "CMake template build task" } + ], + "inputs": [ + { + "type": "pickString", + "id": "makeTarget", "description": "Select a build target", + "options": [ + "make -j $(nprocs)", "sudo make flash", "make disassemble", "make squeaky_clean", "make size", "make debug", + ], + "default": "make -j $(nprocs)" + } ] } \ No newline at end of file diff --git a/firmware/.gitignore b/firmware/.gitignore index 583796896..78bc2fd24 100755 --- a/firmware/.gitignore +++ b/firmware/.gitignore @@ -17,5 +17,6 @@ *.srec *.aws *.atsuo +build .DS_Store TcfTransactionLog.csv diff --git a/firmware/tinyg/.gitignore b/firmware/tinyg/.gitignore index 01f9cb9fb..1d74e2196 100644 --- a/firmware/tinyg/.gitignore +++ b/firmware/tinyg/.gitignore @@ -1,2 +1 @@ -build/ -.vscode/ \ No newline at end of file +.vscode/ diff --git a/firmware/tinyg/CMakeLists.txt b/firmware/tinyg/CMakeLists.txt index 0fbaf74d1..025b5d71c 100644 --- a/firmware/tinyg/CMakeLists.txt +++ b/firmware/tinyg/CMakeLists.txt @@ -20,6 +20,7 @@ add_executable(tinyg.elf cycle_jogging.c cycle_probing.c encoder.c + encoder_as5600.c gcode_parser.c gpio.c hardware.c @@ -54,6 +55,7 @@ add_executable(tinyg.elf xmega/xmega_init.c xmega/xmega_interrupts.c xmega/xmega_rtc.c + xmega/xmega_twi.c ) target_compile_definitions(tinyg.elf PRIVATE diff --git a/firmware/tinyg/encoder.c b/firmware/tinyg/encoder.c index 3878465aa..58c81c879 100755 --- a/firmware/tinyg/encoder.c +++ b/firmware/tinyg/encoder.c @@ -29,9 +29,9 @@ #include "config.h" #include "encoder.h" -#ifdef __cplusplus -extern "C"{ -#endif +#ifdef __cplusplus +extern "C"{ +#endif /**** Allocate Structures ****/ @@ -47,6 +47,9 @@ enEncoders_t en; void encoder_init() { +#ifdef AS_5600_ENCODER + as5600_init(); +#endif memset(&en, 0, sizeof(en)); // clear all values, pointers and status encoder_init_assertions(); } @@ -81,21 +84,21 @@ void en_set_encoder_steps(uint8_t motor, float steps) { en.en[motor].encoder_steps = (int32_t)round(steps); } - -/* - * en_read_encoder() - * - * The stepper ISR count steps into steps_run(). These values are accumulated to - * encoder_position during LOAD (HI interrupt level). The encoder position is - * therefore always stable. But be advised: the position lags target and position - * valaues elsewherein the system becuase the sample is taken when the steps for - * that segment are complete. - */ - -float en_read_encoder(uint8_t motor) -{ - return((float)en.en[motor].encoder_steps); -} + +/* + * en_read_encoder() + * + * The stepper ISR count steps into steps_run(). These values are accumulated to + * encoder_position during LOAD (HI interrupt level). The encoder position is + * therefore always stable. But be advised: the position lags target and position + * valaues elsewherein the system becuase the sample is taken when the steps for + * that segment are complete. + */ + +float en_read_encoder(uint8_t motor) +{ + return((float)en.en[motor].encoder_steps); +} /*********************************************************************************** * CONFIGURATION AND INTERFACE FUNCTIONS @@ -111,6 +114,6 @@ float en_read_encoder(uint8_t motor) #endif // __TEXT_MODE -#ifdef __cplusplus -} -#endif +#ifdef __cplusplus +} +#endif diff --git a/firmware/tinyg/encoder_as5600.c b/firmware/tinyg/encoder_as5600.c new file mode 100644 index 000000000..cb14f4f64 --- /dev/null +++ b/firmware/tinyg/encoder_as5600.c @@ -0,0 +1,1421 @@ +// https://github.com/libdriver/as5600 + +/** + * Copyright (c) 2015 - present LibDriver All rights reserved + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * @file driver_as5600.c + * @brief driver as5600 source file + * @version 1.0.0 + * @author Shifeng Li + * @date 2022-09-30 + * + *

history

+ * + *
Date Version Author Description + *
2022/09/30 1.0 Shifeng Li first upload + *
+ */ + +#define NULL 0 +#include "encoder_as5600.h" + +/** + * @brief chip information definition + */ +#define CHIP_NAME "AMS AS5600" /**< chip name */ +#define MANUFACTURER_NAME "AMS" /**< manufacturer name */ +#define SUPPLY_VOLTAGE_MIN 4.5f /**< chip min supply voltage */ +#define SUPPLY_VOLTAGE_MAX 5.5f /**< chip max supply voltage */ +#define MAX_CURRENT 100.0f /**< chip max current */ +#define TEMPERATURE_MIN -40.0f /**< chip min operating temperature */ +#define TEMPERATURE_MAX 125.0f /**< chip max operating temperature */ +#define DRIVER_VERSION 1000 /**< driver version */ + +/** + * @brief chip address definition + */ +#define AS5600_ADDRESS 0x6C /**< iic device address */ + +/** + * @brief chip register definition + */ +#define AS5600_REG_ZMCO 0x00 /**< written times register */ +#define AS5600_REG_ZPOS_H 0x01 /**< start position register high */ +#define AS5600_REG_ZPOS_L 0x02 /**< start position register low */ +#define AS5600_REG_MPOS_H 0x03 /**< stop position register high */ +#define AS5600_REG_MPOS_L 0x04 /**< stop position register low */ +#define AS5600_REG_MANG_H 0x05 /**< maximum angle register high */ +#define AS5600_REG_MANG_L 0x06 /**< maximum angle register low */ +#define AS5600_REG_CONF_H 0x07 /**< conf register high */ +#define AS5600_REG_CONF_L 0x08 /**< conf register low */ +#define AS5600_REG_RAW_ANGLE_H 0x0C /**< raw angle register high */ +#define AS5600_REG_RAW_ANGLE_L 0x0D /**< raw angle register low */ +#define AS5600_REG_ANGLE_H 0x0E /**< angle register high */ +#define AS5600_REG_ANGLE_L 0x0F /**< angle register low */ +#define AS5600_REG_STATUS 0x0B /**< status register */ +#define AS5600_REG_AGC 0x1A /**< automatic gain control register */ +#define AS5600_REG_MAGNITUDE_H 0x1B /**< magnitude register high */ +#define AS5600_REG_MAGNITUDE_L 0x1C /**< magnitude register low */ +#define AS5600_REG_BURN 0xFF /**< burn register */ + +/** + * @brief read bytes + * @param[in] *handle points to an as5600 handle structure + * @param[in] reg is the iic register address + * @param[out] *data points to a data buffer + * @param[in] len is the data length + * @return status code + * - 0 success + * - 1 read failed + * @note none + */ +static uint8_t a_as5600_iic_read(as5600_handle_t *handle, uint8_t reg, uint8_t *data, uint16_t len) +{ + if (handle->iic_read(AS5600_ADDRESS, reg, data, len) != 0) /* read the register */ + { + return 1; /* return error */ + } + else + { + return 0; /* success return 0 */ + } +} + +/** + * @brief write bytes + * @param[in] *handle points to an as5600 handle structure + * @param[in] reg is the iic register address + * @param[in] *data points to a data buffer + * @param[in] len is the data length + * @return status code + * - 0 success + * - 1 write failed + * @note none + */ +static uint8_t a_as5600_iic_write(as5600_handle_t *handle, uint8_t reg, uint8_t *data, uint16_t len) +{ + if (handle->iic_write(AS5600_ADDRESS, reg, data, len) != 0) /* write the register */ + { + return 1; /* return error */ + } + else + { + return 0; /* success return 0 */ + } +} + +/** + * @brief initialize the chip + * @param[in] *handle points to an as5600 handle structure + * @return status code + * - 0 success + * - 1 iic initialization failed + * - 2 handle is NULL + * - 3 linked functions is NULL + * @note none + */ +uint8_t as5600_init(as5600_handle_t *handle) +{ + if (handle == NULL) /* check handle */ + { + return 2; /* return error */ + } + if (handle->debug_print == NULL) /* check debug_print */ + { + return 3; /* return error */ + } + if (handle->iic_init == NULL) /* check iic_init */ + { + handle->debug_print("as5600: iic_init is null.\n"); /* iic_init is null */ + + return 3; /* return error */ + } + if (handle->iic_deinit == NULL) /* check iic_init */ + { + handle->debug_print("as5600: iic_deinit is null.\n"); /* iic_deinit is null */ + + return 3; /* return error */ + } + if (handle->iic_read == NULL) /* check iic_read */ + { + handle->debug_print("as5600: iic_read is null.\n"); /* iic_read is null */ + + return 3; /* return error */ + } + if (handle->iic_write == NULL) /* check iic_write */ + { + handle->debug_print("as5600: iic_write is null.\n"); /* iic_write is null */ + + return 3; /* return error */ + } + if (handle->delay_ms == NULL) /* check delay_ms */ + { + handle->debug_print("as5600: delay_ms is null.\n"); /* delay_ms is null */ + + return 3; /* return error */ + } + + if (handle->iic_init() != 0) /* iic init */ + { + handle->debug_print("as5600: iic init failed.\n"); /* iic init failed */ + + return 1; /* return error */ + } + handle->inited = 1; /* flag finish initialization */ + + return 0; /* success return 0 */ +} + +/** + * @brief close the chip + * @param[in] *handle points to an as5600 handle structure + * @return status code + * - 0 success + * - 1 iic deinit failed + * - 2 handle is NULL + * - 3 handle is not initialized + * @note none + */ +uint8_t as5600_deinit(as5600_handle_t *handle) +{ + if (handle == NULL) /* check handle */ + { + return 2; /* return error */ + } + if (handle->inited != 1) /* check handle initialization */ + { + return 3; /* return error */ + } + + if (handle->iic_deinit() != 0) /* iic deinit */ + { + handle->debug_print("as5600: iic deinit failed.\n"); /* iic deinit failed */ + + return 1; /* return error */ + } + handle->inited = 0; /* flag close */ + + return 0; /* success return 0 */ +} + +/** + * @brief read the magnetic angle + * @param[in] *handle points to an as5600 handle structure + * @param[out] *angle_raw points to a raw angle buffer + * @param[out] *deg points to a converted angle buffer + * @return status code + * - 0 success + * - 1 read failed + * - 2 handle is NULL + * - 3 handle is not initialized + * @note none + */ +uint8_t as5600_read(as5600_handle_t *handle, uint16_t *angle_raw, float *deg) +{ + uint8_t buf[2]; + + if (handle == NULL) /* check handle */ + { + return 2; /* return error */ + } + if (handle->inited != 1) /* check handle initialization */ + { + return 3; /* return error */ + } + + if (a_as5600_iic_read(handle, AS5600_REG_RAW_ANGLE_H, buf, 2) != 0) /* read conf */ + { + handle->debug_print("as5600: get raw angle failed.\n"); /* get raw angle failed */ + + return 1; /* return error */ + } + else + { + *angle_raw = (uint16_t)(((buf[0] >> 0) & 0xF) << 8) | buf[1]; /* set the raw angle */ + *deg = (float)(*angle_raw ) * (360.0f / 4096.0f); /* convert the raw data to the real data */ + + return 0; /* success return 0 */ + } +} + +/** + * @brief convert the angle to the register raw data + * @param[in] *handle points to an as5600 handle structure + * @param[in] deg is the angle + * @param[out] *reg points to a register raw buffer + * @return status code + * - 0 success + * - 2 handle is NULL + * - 3 handle is not initialized + * @note none + */ +uint8_t as5600_angle_convert_to_register(as5600_handle_t *handle, float deg, uint16_t *reg) +{ + if (handle == NULL) /* check handle */ + { + return 2; /* return error */ + } + if (handle->inited != 1) /* check handle initialization */ + { + return 3; /* return error */ + } + + *reg = (uint16_t)(deg / (360.0f / 4096.0f)); /* convert real data to register data */ + + return 0; /* success return 0 */ +} + +/** + * @brief convert the register raw data to the angle + * @param[in] *handle points to an as5600 handle structure + * @param[in] reg is the register raw data + * @param[out] *deg points to an angle buffer + * @return status code + * - 0 success + * - 2 handle is NULL + * - 3 handle is not initialized + * @note none + */ +uint8_t as5600_angle_convert_to_data(as5600_handle_t *handle, uint16_t reg, float *deg) +{ + if (handle == NULL) /* check handle */ + { + return 2; /* return error */ + } + if (handle->inited != 1) /* check handle initialization */ + { + return 3; /* return error */ + } + + *deg = (float)(reg) * (360.0f / 4096.0f); /* convert raw data to real data */ + + return 0; /* success return 0 */ +} + +/** + * @brief set the start position + * @param[in] *handle points to an as5600 handle structure + * @param[in] pos is the start position + * @return status code + * - 0 success + * - 1 set start position failed + * - 2 handle is NULL + * - 3 handle is not initialized + * - 4 pos is over 0xFFF + * @note none + */ +uint8_t as5600_set_start_position(as5600_handle_t *handle, uint16_t pos) +{ + uint8_t buf[2]; + + if (handle == NULL) /* check handle */ + { + return 2; /* return error */ + } + if (handle->inited != 1) /* check handle initialization */ + { + return 3; /* return error */ + } + if (pos > 0xFFF) /* check the pos */ + { + handle->debug_print("as5600: pos is over 0xFFF.\n"); /* pos is over 0xFFF */ + + return 4; /* return error */ + } + + buf[0] = (pos >> 8) & 0x0F; /* set high part */ + buf[1] = (pos >> 0) & 0xFF; /* set low part */ + if (a_as5600_iic_write(handle, AS5600_REG_ZPOS_H, buf, 2) != 0) /* write conf */ + { + handle->debug_print("as5600: set start position failed.\n"); /* set start position failed */ + + return 1; /* return error */ + } + else + { + return 0; /* success return 0 */ + } +} + +/** + * @brief get the start position + * @param[in] *handle points to an as5600 handle structure + * @param[out] *pos points to a start position buffer + * @return status code + * - 0 success + * - 1 get start position failed + * - 2 handle is NULL + * - 3 handle is not initialized + * @note none + */ +uint8_t as5600_get_start_position(as5600_handle_t *handle, uint16_t *pos) +{ + uint8_t buf[2]; + + if (handle == NULL) /* check handle */ + { + return 2; /* return error */ + } + if (handle->inited != 1) /* check handle initialization */ + { + return 3; /* return error */ + } + + if (a_as5600_iic_read(handle, AS5600_REG_ZPOS_H, buf, 2) != 0) /* read conf */ + { + handle->debug_print("as5600: get start position failed.\n"); /* get start position failed */ + + return 1; /* return error */ + } + else + { + *pos = (uint16_t)(((buf[0] >> 0) & 0xF) << 8) | buf[1]; /* set the position */ + + return 0; /* success return 0 */ + } +} + +/** + * @brief set the stop position + * @param[in] *handle points to an as5600 handle structure + * @param[in] pos is the stop position + * @return status code + * - 0 success + * - 1 set stop position failed + * - 2 handle is NULL + * - 3 handle is not initialized + * - 4 pos is over 0xFFF + * @note none + */ +uint8_t as5600_set_stop_position(as5600_handle_t *handle, uint16_t pos) +{ + uint8_t buf[2]; + + if (handle == NULL) /* check handle */ + { + return 2; /* return error */ + } + if (handle->inited != 1) /* check handle initialization */ + { + return 3; /* return error */ + } + if (pos > 0xFFF) /* check the pos */ + { + handle->debug_print("as5600: pos is over 0xFFF.\n"); /* pos is over 0xFFF */ + + return 4; /* return error */ + } + + buf[0] = (pos >> 8) & 0x0F; /* set high part */ + buf[1] = (pos >> 0) & 0xFF; /* set low part */ + if (a_as5600_iic_write(handle, AS5600_REG_MPOS_H, buf, 2) != 0) /* write conf */ + { + handle->debug_print("as5600: set stop position failed.\n"); /* set stop position failed */ + + return 1; /* return error */ + } + else + { + return 0; /* success return 0 */ + } +} + +/** + * @brief get the stop position + * @param[in] *handle points to an as5600 handle structure + * @param[out] *pos points to a stop position buffer + * @return status code + * - 0 success + * - 1 get stop position failed + * - 2 handle is NULL + * - 3 handle is not initialized + * @note none + */ +uint8_t as5600_get_stop_position(as5600_handle_t *handle, uint16_t *pos) +{ + uint8_t buf[2]; + + if (handle == NULL) /* check handle */ + { + return 2; /* return error */ + } + if (handle->inited != 1) /* check handle initialization */ + { + return 3; /* return error */ + } + + if (a_as5600_iic_read(handle, AS5600_REG_MPOS_H, buf, 2) != 0) /* read conf */ + { + handle->debug_print("as5600: get stop position failed.\n"); /* get stop position failed */ + + return 1; /* return error */ + } + else + { + *pos = (uint16_t)(((buf[0] >> 0) & 0xF) << 8) | buf[1]; /* set the position */ + + return 0; /* success return 0 */ + } +} + +/** + * @brief set the max angle + * @param[in] *handle points to an as5600 handle structure + * @param[in] ang is the max angle + * @return status code + * - 0 success + * - 1 set max angle failed + * - 2 handle is NULL + * - 3 handle is not initialized + * - 4 ang is over 0xFFF + * @note none + */ +uint8_t as5600_set_max_angle(as5600_handle_t *handle, uint16_t ang) +{ + uint8_t buf[2]; + + if (handle == NULL) /* check handle */ + { + return 2; /* return error */ + } + if (handle->inited != 1) /* check handle initialization */ + { + return 3; /* return error */ + } + if (ang > 0xFFF) /* check the ang */ + { + handle->debug_print("as5600: ang is over 0xFFF.\n"); /* ang is over 0xFFF */ + + return 4; /* return error */ + } + + buf[0] = (ang >> 8) & 0x0F; /* set high part */ + buf[1] = (ang >> 0) & 0xFF; /* set low part */ + if (a_as5600_iic_write(handle, AS5600_REG_MANG_H, buf, 2) != 0) /* write conf */ + { + handle->debug_print("as5600: set max angle failed.\n"); /* set max angle failed */ + + return 1; /* return error */ + } + else + { + return 0; /* success return 0 */ + } +} + +/** + * @brief get the max angle + * @param[in] *handle points to an as5600 handle structure + * @param[out] *ang points to a max angle buffer + * @return status code + * - 0 success + * - 1 get max angle failed + * - 2 handle is NULL + * - 3 handle is not initialized + * @note none + */ +uint8_t as5600_get_max_angle(as5600_handle_t *handle, uint16_t *ang) +{ + uint8_t buf[2]; + + if (handle == NULL) /* check handle */ + { + return 2; /* return error */ + } + if (handle->inited != 1) /* check handle initialization */ + { + return 3; /* return error */ + } + + if (a_as5600_iic_read(handle, AS5600_REG_MANG_H, buf, 2) != 0) /* read conf */ + { + handle->debug_print("as5600: get max angle failed.\n"); /* get max angle failed */ + + return 1; /* return error */ + } + else + { + *ang = (uint16_t)(((buf[0] >> 0) & 0xF) << 8) | buf[1]; /* set the position */ + + return 0; /* success return 0 */ + } +} + +/** + * @brief enable or disable the watch dog + * @param[in] *handle points to an as5600 handle structure + * @param[in] enable is a bool value + * @return status code + * - 0 success + * - 1 set watchdog failed + * - 2 handle is NULL + * - 3 handle is not initialized + * @note none + */ +uint8_t as5600_set_watch_dog(as5600_handle_t *handle, as5600_bool_t enable) +{ + uint8_t prev; + + if (handle == NULL) /* check handle */ + { + return 2; /* return error */ + } + if (handle->inited != 1) /* check handle initialization */ + { + return 3; /* return error */ + } + + if (a_as5600_iic_read(handle, AS5600_REG_CONF_H, &prev, 1) != 0) /* read conf */ + { + handle->debug_print("as5600: get conf failed.\n"); /* get conf failed */ + + return 1; /* return error */ + } + prev &= ~(1 << 5); /* clear the settings */ + prev |= enable << 5; /* set the bool */ + if (a_as5600_iic_write(handle, AS5600_REG_CONF_H, &prev, 1) != 0) /* write conf */ + { + handle->debug_print("as5600: set conf failed.\n"); /* set conf failed */ + + return 1; /* return error */ + } + + return 0; /* success return 0 */ +} + +/** + * @brief get the watch dog status + * @param[in] *handle points to an as5600 handle structure + * @param[out] *enable points to a bool value buffer + * @return status code + * - 0 success + * - 1 get watchdog failed + * - 2 handle is NULL + * - 3 handle is not initialized + * @note none + */ +uint8_t as5600_get_watch_dog(as5600_handle_t *handle, as5600_bool_t *enable) +{ + uint8_t prev; + + if (handle == NULL) /* check handle */ + { + return 2; /* return error */ + } + if (handle->inited != 1) /* check handle initialization */ + { + return 3; /* return error */ + } + + if (a_as5600_iic_read(handle, AS5600_REG_CONF_H, &prev, 1) != 0) /* read conf */ + { + handle->debug_print("as5600: get conf failed.\n"); /* get conf failed */ + + return 1; /* return error */ + } + *enable = (as5600_bool_t)((prev >> 5) & 0x1); /* get the bool */ + + return 0; /* success return 0 */ +} + +/** + * @brief set the fast filter threshold + * @param[in] *handle points to an as5600 handle structure + * @param[in] threshold is the fast filter threshold + * @return status code + * - 0 success + * - 1 set fast filter threshold failed + * - 2 handle is NULL + * - 3 handle is not initialized + * @note none + */ +uint8_t as5600_set_fast_filter_threshold(as5600_handle_t *handle, as5600_fast_filter_threshold_t threshold) +{ + uint8_t prev; + + if (handle == NULL) /* check handle */ + { + return 2; /* return error */ + } + if (handle->inited != 1) /* check handle initialization */ + { + return 3; /* return error */ + } + + if (a_as5600_iic_read(handle, AS5600_REG_CONF_H, &prev, 1) != 0) /* read conf */ + { + handle->debug_print("as5600: get conf failed.\n"); /* get conf failed */ + + return 1; /* return error */ + } + prev &= ~(7 << 2); /* clear the settings */ + prev |= threshold << 2; /* set the threshold */ + if (a_as5600_iic_write(handle, AS5600_REG_CONF_H, &prev, 1) != 0) /* write conf */ + { + handle->debug_print("as5600: set conf failed.\n"); /* set conf failed */ + + return 1; /* return error */ + } + + return 0; /* success return 0 */ +} + +/** + * @brief get the fast filter threshold + * @param[in] *handle points to an as5600 handle structure + * @param[out] *threshold points to a fast filter threshold buffer + * @return status code + * - 0 success + * - 1 get fast filter threshold failed + * - 2 handle is NULL + * - 3 handle is not initialized + * @note none + */ +uint8_t as5600_get_fast_filter_threshold(as5600_handle_t *handle, as5600_fast_filter_threshold_t *threshold) +{ + uint8_t prev; + + if (handle == NULL) /* check handle */ + { + return 2; /* return error */ + } + if (handle->inited != 1) /* check handle initialization */ + { + return 3; /* return error */ + } + + if (a_as5600_iic_read(handle, AS5600_REG_CONF_H, &prev, 1) != 0) /* read conf */ + { + handle->debug_print("as5600: get conf failed.\n"); /* get conf failed */ + + return 1; /* return error */ + } + *threshold = (as5600_fast_filter_threshold_t)((prev >> 2) & 0x7); /* set the threshold */ + + return 0; /* success return 0 */ +} + +/** + * @brief set the slow filter + * @param[in] *handle points to an as5600 handle structure + * @param[in] filter is the slow filter + * @return status code + * - 0 success + * - 1 set slow filter failed + * - 2 handle is NULL + * - 3 handle is not initialized + * @note none + */ +uint8_t as5600_set_slow_filter(as5600_handle_t *handle, as5600_slow_filter_t filter) +{ + uint8_t prev; + + if (handle == NULL) /* check handle */ + { + return 2; /* return error */ + } + if (handle->inited != 1) /* check handle initialization */ + { + return 3; /* return error */ + } + + if (a_as5600_iic_read(handle, AS5600_REG_CONF_H, &prev, 1) != 0) /* read conf */ + { + handle->debug_print("as5600: get conf failed.\n"); /* get conf failed */ + + return 1; /* return error */ + } + prev &= ~(3 << 0); /* clear the settings */ + prev |= filter << 0; /* set the filter */ + if (a_as5600_iic_write(handle, AS5600_REG_CONF_H, &prev, 1) != 0) /* write conf */ + { + handle->debug_print("as5600: set conf failed.\n"); /* set conf failed */ + + return 1; /* return error */ + } + + return 0; /* success return 0 */ +} + +/** + * @brief get the slow filter + * @param[in] *handle points to an as5600 handle structure + * @param[out] *filter points to a slow filter buffer + * @return status code + * - 0 success + * - 1 get slow filter failed + * - 2 handle is NULL + * - 3 handle is not initialized + * @note none + */ +uint8_t as5600_get_slow_filter(as5600_handle_t *handle, as5600_slow_filter_t *filter) +{ + uint8_t prev; + + if (handle == NULL) /* check handle */ + { + return 2; /* return error */ + } + if (handle->inited != 1) /* check handle initialization */ + { + return 3; /* return error */ + } + + if (a_as5600_iic_read(handle, AS5600_REG_CONF_H, &prev, 1) != 0) /* read conf */ + { + handle->debug_print("as5600: get conf failed.\n"); /* get conf failed */ + + return 1; /* return error */ + } + *filter = (as5600_slow_filter_t)(prev & 0x3); /* get the filter */ + + return 0; /* success return 0 */ +} + +/** + * @brief set the pwm frequency + * @param[in] *handle points to an as5600 handle structure + * @param[in] freq is the pwm frequency + * @return status code + * - 0 success + * - 1 set pwm frequency failed + * - 2 handle is NULL + * - 3 handle is not initialized + * @note none + */ +uint8_t as5600_set_pwm_frequency(as5600_handle_t *handle, as5600_pwm_frequency_t freq) +{ + uint8_t prev; + + if (handle == NULL) /* check handle */ + { + return 2; /* return error */ + } + if (handle->inited != 1) /* check handle initialization */ + { + return 3; /* return error */ + } + + if (a_as5600_iic_read(handle, AS5600_REG_CONF_L, &prev, 1) != 0) /* read conf */ + { + handle->debug_print("as5600: get conf failed.\n"); /* get conf failed */ + + return 1; /* return error */ + } + prev &= ~(3 << 6); /* clear the settings */ + prev |= freq << 6; /* set the freq */ + if (a_as5600_iic_write(handle, AS5600_REG_CONF_L, &prev, 1) != 0) /* write conf */ + { + handle->debug_print("as5600: set conf failed.\n"); /* set conf failed */ + + return 1; /* return error */ + } + + return 0; /* success return 0 */ +} + +/** + * @brief get the pwm frequency + * @param[in] *handle points to an as5600 handle structure + * @param[out] *freq points to a pwm frequency buffer + * @return status code + * - 0 success + * - 1 get pwm frequency failed + * - 2 handle is NULL + * - 3 handle is not initialized + * @note none + */ +uint8_t as5600_get_pwm_frequency(as5600_handle_t *handle, as5600_pwm_frequency_t *freq) +{ + uint8_t prev; + + if (handle == NULL) /* check handle */ + { + return 2; /* return error */ + } + if (handle->inited != 1) /* check handle initialization */ + { + return 3; /* return error */ + } + + if (a_as5600_iic_read(handle, AS5600_REG_CONF_L, &prev, 1) != 0) /* read conf */ + { + handle->debug_print("as5600: get conf failed.\n"); /* get conf failed */ + + return 1; /* return error */ + } + *freq = (as5600_pwm_frequency_t)((prev >> 6) & 0x3); /* set the frequency */ + + return 0; /* success return 0 */ +} + +/** + * @brief set the output stage + * @param[in] *handle points to an as5600 handle structure + * @param[in] stage is the output stage + * @return status code + * - 0 success + * - 1 set output stage failed + * - 2 handle is NULL + * - 3 handle is not initialized + * @note none + */ +uint8_t as5600_set_output_stage(as5600_handle_t *handle, as5600_output_stage_t stage) +{ + uint8_t prev; + + if (handle == NULL) /* check handle */ + { + return 2; /* return error */ + } + if (handle->inited != 1) /* check handle initialization */ + { + return 3; /* return error */ + } + + if (a_as5600_iic_read(handle, AS5600_REG_CONF_L, &prev, 1) != 0) /* read conf */ + { + handle->debug_print("as5600: get conf failed.\n"); /* get conf failed */ + + return 1; /* return error */ + } + prev &= ~(3 << 4); /* clear the settings */ + prev |= stage << 4; /* set the stage */ + if (a_as5600_iic_write(handle, AS5600_REG_CONF_L, &prev, 1) != 0) /* write conf */ + { + handle->debug_print("as5600: set conf failed.\n"); /* set conf failed */ + + return 1; /* return error */ + } + + return 0; /* success return 0 */ +} + +/** + * @brief get the output stage + * @param[in] *handle points to an as5600 handle structure + * @param[out] *stage points to an output stage buffer + * @return status code + * - 0 success + * - 1 get output stage failed + * - 2 handle is NULL + * - 3 handle is not initialized + * @note none + */ +uint8_t as5600_get_output_stage(as5600_handle_t *handle, as5600_output_stage_t *stage) +{ + uint8_t prev; + + if (handle == NULL) /* check handle */ + { + return 2; /* return error */ + } + if (handle->inited != 1) /* check handle initialization */ + { + return 3; /* return error */ + } + + if (a_as5600_iic_read(handle, AS5600_REG_CONF_L, &prev, 1) != 0) /* read conf */ + { + handle->debug_print("as5600: get conf failed.\n"); /* get conf failed */ + + return 1; /* return error */ + } + *stage = (as5600_output_stage_t)((prev >> 4) & 0x3); /* get the output stage */ + + return 0; /* success return 0 */ +} + +/** + * @brief set the hysteresis + * @param[in] *handle points to an as5600 handle structure + * @param[in] hysteresis is the set hysteresis + * @return status code + * - 0 success + * - 1 set hysteresis failed + * - 2 handle is NULL + * - 3 handle is not initialized + * @note none + */ +uint8_t as5600_set_hysteresis(as5600_handle_t *handle, as5600_hysteresis_t hysteresis) +{ + uint8_t prev; + + if (handle == NULL) /* check handle */ + { + return 2; /* return error */ + } + if (handle->inited != 1) /* check handle initialization */ + { + return 3; /* return error */ + } + + if (a_as5600_iic_read(handle, AS5600_REG_CONF_L, &prev, 1) != 0) /* read conf */ + { + handle->debug_print("as5600: get conf failed.\n"); /* get conf failed */ + + return 1; /* return error */ + } + prev &= ~(3 << 2); /* clear the settings */ + prev |= hysteresis << 2; /* set the hysteresis */ + if (a_as5600_iic_write(handle, AS5600_REG_CONF_L, &prev, 1) != 0) /* write conf */ + { + handle->debug_print("as5600: set conf failed.\n"); /* set conf failed */ + + return 1; /* return error */ + } + + return 0; /* success return 0 */ +} + +/** + * @brief get the hysteresis + * @param[in] *handle points to an as5600 handle structure + * @param[out] *hysteresis points to a hysteresis buffer + * @return status code + * - 0 success + * - 1 get hysteresis failed + * - 2 handle is NULL + * - 3 handle is not initialized + * @note none + */ +uint8_t as5600_get_hysteresis(as5600_handle_t *handle, as5600_hysteresis_t *hysteresis) +{ + uint8_t prev; + + if (handle == NULL) /* check handle */ + { + return 2; /* return error */ + } + if (handle->inited != 1) /* check handle initialization */ + { + return 3; /* return error */ + } + + if (a_as5600_iic_read(handle, AS5600_REG_CONF_L, &prev, 1) != 0) /* read conf */ + { + handle->debug_print("as5600: get conf failed.\n"); /* get conf failed */ + + return 1; /* return error */ + } + *hysteresis = (as5600_hysteresis_t)((prev >> 2) & 0x3); /* get the hysteresis */ + + return 0; /* success return 0 */ +} + +/** + * @brief set the power mode + * @param[in] *handle points to an as5600 handle structure + * @param[in] mode is the power mode + * @return status code + * - 0 success + * - 1 set power mode failed + * - 2 handle is NULL + * - 3 handle is not initialized + * @note none + */ +uint8_t as5600_set_power_mode(as5600_handle_t *handle, as5600_power_mode_t mode) +{ + uint8_t prev; + + if (handle == NULL) /* check handle */ + { + return 2; /* return error */ + } + if (handle->inited != 1) /* check handle initialization */ + { + return 3; /* return error */ + } + + if (a_as5600_iic_read(handle, AS5600_REG_CONF_L, &prev, 1) != 0) /* read conf */ + { + handle->debug_print("as5600: get conf failed.\n"); /* get conf failed */ + + return 1; /* return error */ + } + prev &= ~(3 << 0); /* clear the settings */ + prev |= mode << 0; /* set the power mode */ + if (a_as5600_iic_write(handle, AS5600_REG_CONF_L, &prev, 1) != 0) /* write conf */ + { + handle->debug_print("as5600: set conf failed.\n"); /* set conf failed */ + + return 1; /* return error */ + } + + return 0; /* success return 0 */ +} + +/** + * @brief get the power mode + * @param[in] *handle points to an as5600 handle structure + * @param[out] *mode points to a power mode buffer + * @return status code + * - 0 success + * - 1 get power mode failed + * - 2 handle is NULL + * - 3 handle is not initialized + * @note none + */ +uint8_t as5600_get_power_mode(as5600_handle_t *handle, as5600_power_mode_t *mode) +{ + uint8_t prev; + + if (handle == NULL) /* check handle */ + { + return 2; /* return error */ + } + if (handle->inited != 1) /* check handle initialization */ + { + return 3; /* return error */ + } + + if (a_as5600_iic_read(handle, AS5600_REG_CONF_L, &prev, 1) != 0) /* read conf */ + { + handle->debug_print("as5600: get conf failed.\n"); /* get conf failed */ + + return 1; /* return error */ + } + *mode = (as5600_power_mode_t)((prev >> 0) & 0x3); /* get the power mode */ + + return 0; /* success return 0 */ +} + +/** + * @brief get the raw angle + * @param[in] *handle points to an as5600 handle structure + * @param[out] *ang points to an ang buffer + * @return status code + * - 0 success + * - 1 get raw angle failed + * - 2 handle is NULL + * - 3 handle is not initialized + * @note none + */ +uint8_t as5600_get_raw_angle(as5600_handle_t *handle, uint16_t *ang) +{ + uint8_t buf[2]; + + if (handle == NULL) /* check handle */ + { + return 2; /* return error */ + } + if (handle->inited != 1) /* check handle initialization */ + { + return 3; /* return error */ + } + + if (a_as5600_iic_read(handle, AS5600_REG_RAW_ANGLE_H, buf, 2) != 0) /* read conf */ + { + handle->debug_print("as5600: get raw angle failed.\n"); /* get raw angle failed */ + + return 1; /* return error */ + } + else + { + *ang = (uint16_t)(((buf[0] >> 0) & 0xF) << 8) | buf[1]; /* set the angle */ + + return 0; /* success return 0 */ + } +} + +/** + * @brief get the angle + * @param[in] *handle points to an as5600 handle structure + * @param[out] *ang points to an ang buffer + * @return status code + * - 0 success + * - 1 get angle failed + * - 2 handle is NULL + * - 3 handle is not initialized + * @note none + */ +uint8_t as5600_get_angle(as5600_handle_t *handle, uint16_t *ang) +{ + uint8_t buf[2]; + + if (handle == NULL) /* check handle */ + { + return 2; /* return error */ + } + if (handle->inited != 1) /* check handle initialization */ + { + return 3; /* return error */ + } + + if (a_as5600_iic_read(handle, AS5600_REG_ANGLE_H, buf, 2) != 0) /* read conf */ + { + handle->debug_print("as5600: get angle failed.\n"); /* get angle failed */ + + return 1; /* return error */ + } + else + { + *ang = (uint16_t)(((buf[0] >> 0) & 0xF) << 8) | buf[1]; /* set the angle */ + + return 0; /* success return 0 */ + } +} + +/** + * @brief get the status + * @param[in] *handle points to an as5600 handle structure + * @param[out] *status points to a status buffer + * @return status code + * - 0 success + * - 1 get status failed + * - 2 handle is NULL + * - 3 handle is not initialized + * @note none + */ +uint8_t as5600_get_status(as5600_handle_t *handle, uint8_t *status) +{ + if (handle == NULL) /* check handle */ + { + return 2; /* return error */ + } + if (handle->inited != 1) /* check handle initialization */ + { + return 3; /* return error */ + } + + if (a_as5600_iic_read(handle, AS5600_REG_STATUS, status, 1) != 0) /* read conf */ + { + handle->debug_print("as5600: get status failed.\n"); /* get status failed */ + + return 1; /* return error */ + } + else + { + return 0; /* success return 0 */ + } +} + +/** + * @brief get the automatic gain control + * @param[in] *handle points to an as5600 handle structure + * @param[out] *agc points to an agc buffer + * @return status code + * - 0 success + * - 1 get agc failed + * - 2 handle is NULL + * - 3 handle is not initialized + * @note none + */ +uint8_t as5600_get_agc(as5600_handle_t *handle, uint8_t *agc) +{ + if (handle == NULL) /* check handle */ + { + return 2; /* return error */ + } + if (handle->inited != 1) /* check handle initialization */ + { + return 3; /* return error */ + } + + if (a_as5600_iic_read(handle, AS5600_REG_AGC, agc, 1) != 0) /* read conf */ + { + handle->debug_print("as5600: get agc failed.\n"); /* get agc failed */ + + return 1; /* return error */ + } + else + { + return 0; /* success return 0 */ + } +} + +/** + * @brief get the magnitude + * @param[in] *handle points to an as5600 handle structure + * @param[out] *magnitude points to a magnitude buffer + * @return status code + * - 0 success + * - 1 get magnitude failed + * - 2 handle is NULL + * - 3 handle is not initialized + * @note none + */ +uint8_t as5600_get_magnitude(as5600_handle_t *handle, uint16_t *magnitude) +{ + uint8_t buf[2]; + + if (handle == NULL) /* check handle */ + { + return 2; /* return error */ + } + if (handle->inited != 1) /* check handle initialization */ + { + return 3; /* return error */ + } + + if (a_as5600_iic_read(handle, AS5600_REG_MAGNITUDE_H, buf, 2) != 0) /* read conf */ + { + handle->debug_print("as5600: get magnitude failed.\n"); /* get magnitude failed */ + + return 1; /* return error */ + } + else + { + *magnitude = (uint16_t)(((buf[0] >> 0) & 0xF) << 8) | buf[1]; /* set the angle */ + + return 0; /* success return 0 */ + } +} + +/** + * @brief set the burn + * @param[in] *handle points to an as5600 handle structure + * @param[in] burn is the set command + * @return status code + * - 0 success + * - 1 set burn failed + * - 2 handle is NULL + * - 3 handle is not initialized + * @note none + */ +uint8_t as5600_set_burn(as5600_handle_t *handle, as5600_burn_t burn) +{ + uint8_t prev; + + if (handle == NULL) /* check handle */ + { + return 2; /* return error */ + } + if (handle->inited != 1) /* check handle initialization */ + { + return 3; /* return error */ + } + + prev = burn; /* set the burn */ + if (a_as5600_iic_write(handle, AS5600_REG_BURN, &prev, 1) != 0) /* write conf */ + { + handle->debug_print("as5600: set burn failed.\n"); /* set burn failed */ + + return 1; /* return error */ + } + + return 0; /* success return 0 */ +} + +/** + * @brief set the chip register + * @param[in] *handle points to an as5600 handle structure + * @param[in] reg is the iic register address + * @param[in] *buf points to a data buffer + * @param[in] len is the data buffer length + * @return status code + * - 0 success + * - 1 write failed + * - 2 handle is NULL + * - 3 handle is not initialized + * @note none + */ +uint8_t as5600_set_reg(as5600_handle_t *handle, uint8_t reg, uint8_t *buf, uint16_t len) +{ + uint8_t res; + + if (handle == NULL) /* check handle */ + { + return 2; /* return error */ + } + if (handle->inited != 1) /* check handle initialization */ + { + return 3; /* return error */ + } + + res = a_as5600_iic_write(handle, reg, buf, len); /* write data */ + if (res != 0) /* check result */ + { + handle->debug_print("as5600: write register failed.\n"); /* write register failed */ + + return 1; /* return error */ + } + + return 0; /* success return 0 */ +} + +/** + * @brief get the chip register + * @param[in] *handle points to an as5600 handle structure + * @param[in] reg is the iic register address + * @param[out] *buf points to a data buffer + * @param[in] len is the data buffer length + * @return status code + * - 0 success + * - 1 read failed + * - 2 handle is NULL + * - 3 handle is not initialized + * @note none + */ +uint8_t as5600_get_reg(as5600_handle_t *handle, uint8_t reg, uint8_t *buf, uint16_t len) +{ + uint8_t res; + + if (handle == NULL) /* check handle */ + { + return 2; /* return error */ + } + if (handle->inited != 1) /* check handle initialization */ + { + return 3; /* return error */ + } + + res = a_as5600_iic_read(handle, reg, buf, len); /* read data */ + if (res != 0) /* check result */ + { + handle->debug_print("as5600: read register failed.\n"); /* read register failed */ + + return 1; /* return error */ + } + + return 0; /* success return 0 */ +} + +/** + * @brief get chip's information + * @param[out] *info points to an as5600 info structure + * @return status code + * - 0 success + * - 2 handle is NULL + * @note none + */ +uint8_t as5600_info(as5600_info_t *info) +{ + if (info == NULL) /* check handle */ + { + return 2; /* return error */ + } + + memset(info, 0, sizeof(as5600_info_t)); /* initialize as5600 info structure */ + strncpy(info->chip_name, CHIP_NAME, 32); /* copy chip name */ + strncpy(info->manufacturer_name, MANUFACTURER_NAME, 32); /* copy manufacturer name */ + strncpy(info->interface, "IIC", 8); /* copy interface name */ + info->supply_voltage_min_v = SUPPLY_VOLTAGE_MIN; /* set minimal supply voltage */ + info->supply_voltage_max_v = SUPPLY_VOLTAGE_MAX; /* set maximum supply voltage */ + info->max_current_ma = MAX_CURRENT; /* set maximum current */ + info->temperature_max = TEMPERATURE_MAX; /* set minimal temperature */ + info->temperature_min = TEMPERATURE_MIN; /* set maximum temperature */ + info->driver_version = DRIVER_VERSION; /* set driver version */ + + return 0; /* success return 0 */ +} \ No newline at end of file diff --git a/firmware/tinyg/encoder_as5600.h b/firmware/tinyg/encoder_as5600.h new file mode 100644 index 000000000..6237dd7a1 --- /dev/null +++ b/firmware/tinyg/encoder_as5600.h @@ -0,0 +1,735 @@ +/** + * Copyright (c) 2015 - present LibDriver All rights reserved + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * @file driver_as5600.h + * @brief driver as5600 header file + * @version 1.0.0 + * @author Shifeng Li + * @date 2022-09-30 + * + *

history

+ * + *
Date Version Author Description + *
2022/09/30 1.0 Shifeng Li first upload + *
+ */ + +#ifndef DRIVER_AS5600_H +#define DRIVER_AS5600_H + +#include +#include +#include + +#ifdef __cplusplus +extern "C"{ +#endif + +/** + * @defgroup as5600_driver as5600 driver function + * @brief as5600 driver modules + * @{ + */ + +/** + * @addtogroup as5600_base_driver + * @{ + */ + +/** + * @brief as5600 bool enumeration definition + */ +typedef enum +{ + AS5600_BOOL_FALSE = 0x00, /**< disable */ + AS5600_BOOL_TRUE = 0x01, /**< enable */ +} as5600_bool_t; + +/** + * @brief as5600 power mode enumeration definition + */ +typedef enum +{ + AS5600_POWER_MODE_NOM = 0x00, /**< normal */ + AS5600_POWER_MODE_LPM1 = 0x01, /**< low power 1 */ + AS5600_POWER_MODE_LPM2 = 0x02, /**< low power 2 */ + AS5600_POWER_MODE_LPM3 = 0x03, /**< low power 3 */ +} as5600_power_mode_t; + +/** + * @brief as5600 hysteresis enumeration definition + */ +typedef enum +{ + AS5600_HYSTERESIS_OFF = 0x00, /**< off */ + AS5600_HYSTERESIS_1LSB = 0x01, /**< 1 lsb */ + AS5600_HYSTERESIS_2LSB = 0x02, /**< 2 lsb */ + AS5600_HYSTERESIS_3LSB = 0x03, /**< 3 lsb */ +} as5600_hysteresis_t; + +/** + * @brief as5600 output stage enumeration definition + */ +typedef enum +{ + AS5600_OUTPUT_STAGE_ANALOG_FULL = 0x00, /**< full range from 0% to 100% between gnd and vdd */ + AS5600_OUTPUT_STAGE_ANALOG_REDUCED = 0x01, /**< reduced range from 10% to 90% between gnd and vdd */ + AS5600_OUTPUT_STAGE_PWM = 0x02, /**< digital pwm */ +} as5600_output_stage_t; + +/** + * @brief as5600 pwm frequency enumeration definition + */ +typedef enum +{ + AS5600_PWM_FREQUENCY_115HZ = 0x00, /**< 115Hz */ + AS5600_PWM_FREQUENCY_230HZ = 0x01, /**< 230Hz */ + AS5600_PWM_FREQUENCY_460HZ = 0x02, /**< 460Hz */ + AS5600_PWM_FREQUENCY_920HZ = 0x03, /**< 920Hz */ +} as5600_pwm_frequency_t; + +/** + * @brief as5600 slow filter enumeration definition + */ +typedef enum +{ + AS5600_SLOW_FILTER_16X = 0x00, /**< 16x */ + AS5600_SLOW_FILTER_8X = 0x01, /**< 8x */ + AS5600_SLOW_FILTER_4X = 0x02, /**< 4x */ + AS5600_SLOW_FILTER_2X = 0x03, /**< 2x */ +} as5600_slow_filter_t; + +/** + * @brief as5600 fast filter threshold enumeration definition + */ +typedef enum +{ + AS5600_FAST_FILTER_THRESHOLD_SLOW_FILTER_ONLY = 0x00, /**< slow filter only */ + AS5600_FAST_FILTER_THRESHOLD_6LSB = 0x01, /**< 6 lsb */ + AS5600_FAST_FILTER_THRESHOLD_7LSB = 0x02, /**< 7 lsb */ + AS5600_FAST_FILTER_THRESHOLD_9LSB = 0x03, /**< 9 lsb */ + AS5600_FAST_FILTER_THRESHOLD_10LSB = 0x07, /**< 10 lsb */ + AS5600_FAST_FILTER_THRESHOLD_18LSB = 0x04, /**< 18 lsb */ + AS5600_FAST_FILTER_THRESHOLD_21LSB = 0x05, /**< 21 lsb */ + AS5600_FAST_FILTER_THRESHOLD_24LSB = 0x06, /**< 24 lsb */ +} as5600_fast_filter_threshold_t; + +/** + * @brief as5600 status enumeration definition + */ +typedef enum +{ + AS5600_STATUS_MD = (1 << 5), /**< agc minimum gain overflow, magnet too strong */ + AS5600_STATUS_ML = (1 << 4), /**< agc maximum gain overflow, magnet too weak */ + AS5600_STATUS_MH = (1 << 3), /**< magnet was detected */ +} as5600_status_t; + +/** + * @brief as5600 burn enumeration definition + */ +typedef enum +{ + AS5600_BURN_CMD1 = 0x01, /**< load the actual otp content command 1 */ + AS5600_BURN_CMD2 = 0x11, /**< load the actual otp content command 2 */ + AS5600_BURN_CMD3 = 0x10, /**< load the actual otp content command 3 */ + AS5600_BURN_ANGLE = 0x80, /**< angle */ + AS5600_BURN_SETTING = 0x40, /**< setting */ +} as5600_burn_t; + +/** + * @brief as5600 handle structure definition + */ +typedef struct as5600_handle_s +{ + uint8_t (*iic_init)(void); /**< point to an iic_init function address */ + uint8_t (*iic_deinit)(void); /**< point to an iic_deinit function address */ + uint8_t (*iic_read)(uint8_t addr, uint8_t reg, uint8_t *buf, uint16_t len); /**< point to an iic_read function address */ + uint8_t (*iic_write)(uint8_t addr, uint8_t reg, uint8_t *buf, uint16_t len); /**< point to an iic_write function address */ + void (*delay_ms)(uint32_t ms); /**< point to a delay_ms function address */ + void (*debug_print)(const char *const fmt, ...); /**< point to a debug_print function address */ + uint8_t inited; /**< inited flag */ +} as5600_handle_t; + +/** + * @brief as5600 information structure definition + */ +typedef struct as5600_info_s +{ + char chip_name[32]; /**< chip name */ + char manufacturer_name[32]; /**< manufacturer name */ + char interface[8]; /**< chip interface name */ + float supply_voltage_min_v; /**< chip min supply voltage */ + float supply_voltage_max_v; /**< chip max supply voltage */ + float max_current_ma; /**< chip max current */ + float temperature_min; /**< chip min operating temperature */ + float temperature_max; /**< chip max operating temperature */ + uint32_t driver_version; /**< driver version */ +} as5600_info_t; + +/** + * @} + */ + +/** + * @defgroup as5600_link_driver as5600 link driver function + * @brief as5600 link driver modules + * @ingroup as5600_driver + * @{ + */ + +/** + * @brief initialize as5600_handle_t structure + * @param[in] HANDLE points to an as5600 handle structure + * @param[in] STRUCTURE is as5600_handle_t + * @note none + */ +#define DRIVER_AS5600_LINK_INIT(HANDLE, STRUCTURE) memset(HANDLE, 0, sizeof(STRUCTURE)) + +/** + * @brief link iic_init function + * @param[in] HANDLE points to an as5600 handle structure + * @param[in] FUC points to an iic_init function address + * @note none + */ +#define DRIVER_AS5600_LINK_IIC_INIT(HANDLE, FUC) (HANDLE)->iic_init = FUC + +/** + * @brief link iic_deinit function + * @param[in] HANDLE points to an as5600 handle structure + * @param[in] FUC points to an iic_deinit function address + * @note none + */ +#define DRIVER_AS5600_LINK_IIC_DEINIT(HANDLE, FUC) (HANDLE)->iic_deinit = FUC + +/** + * @brief link iic_read function + * @param[in] HANDLE points to an as5600 handle structure + * @param[in] FUC points to an iic_read function address + * @note none + */ +#define DRIVER_AS5600_LINK_IIC_READ(HANDLE, FUC) (HANDLE)->iic_read = FUC + +/** + * @brief link iic_write function + * @param[in] HANDLE points to an as5600 handle structure + * @param[in] FUC points to an iic_write function address + * @note none + */ +#define DRIVER_AS5600_LINK_IIC_WRITE(HANDLE, FUC) (HANDLE)->iic_write = FUC + +/** + * @brief link delay_ms function + * @param[in] HANDLE points to an as5600 handle structure + * @param[in] FUC points to a delay_ms function address + * @note none + */ +#define DRIVER_AS5600_LINK_DELAY_MS(HANDLE, FUC) (HANDLE)->delay_ms = FUC + +/** + * @brief link debug_print function + * @param[in] HANDLE points to an as5600 handle structure + * @param[in] FUC points to a debug_print function address + * @note none + */ +#define DRIVER_AS5600_LINK_DEBUG_PRINT(HANDLE, FUC) (HANDLE)->debug_print = FUC + +/** + * @} + */ + +/** + * @defgroup as5600_base_driver as5600 base driver function + * @brief as5600 base driver modules + * @ingroup as5600_driver + * @{ + */ + +/** + * @brief get chip's information + * @param[out] *info points to an as5600 info structure + * @return status code + * - 0 success + * - 2 handle is NULL + * @note none + */ +uint8_t as5600_info(as5600_info_t *info); + +/** + * @brief initialize the chip + * @param[in] *handle points to an as5600 handle structure + * @return status code + * - 0 success + * - 1 iic initialization failed + * - 2 handle is NULL + * - 3 linked functions is NULL + * @note none + */ +uint8_t as5600_init(as5600_handle_t *handle); + +/** + * @brief close the chip + * @param[in] *handle points to an as5600 handle structure + * @return status code + * - 0 success + * - 1 iic deinit failed + * - 2 handle is NULL + * - 3 handle is not initialized + * @note none + */ +uint8_t as5600_deinit(as5600_handle_t *handle); + +/** + * @brief read the magnetic angle + * @param[in] *handle points to an as5600 handle structure + * @param[out] *angle_raw points to a raw angle buffer + * @param[out] *deg points to a converted angle buffer + * @return status code + * - 0 success + * - 1 read failed + * - 2 handle is NULL + * - 3 handle is not initialized + * @note none + */ +uint8_t as5600_read(as5600_handle_t *handle, uint16_t *angle_raw, float *deg); + +/** + * @brief convert the angle to the register raw data + * @param[in] *handle points to an as5600 handle structure + * @param[in] deg is the angle + * @param[out] *reg points to a register raw buffer + * @return status code + * - 0 success + * - 2 handle is NULL + * - 3 handle is not initialized + * @note none + */ +uint8_t as5600_angle_convert_to_register(as5600_handle_t *handle, float deg, uint16_t *reg); + +/** + * @brief convert the register raw data to the angle + * @param[in] *handle points to an as5600 handle structure + * @param[in] reg is the register raw data + * @param[out] *deg points to an angle buffer + * @return status code + * - 0 success + * - 2 handle is NULL + * - 3 handle is not initialized + * @note none + */ +uint8_t as5600_angle_convert_to_data(as5600_handle_t *handle, uint16_t reg, float *deg); + +/** + * @brief set the start position + * @param[in] *handle points to an as5600 handle structure + * @param[in] pos is the start position + * @return status code + * - 0 success + * - 1 set start position failed + * - 2 handle is NULL + * - 3 handle is not initialized + * - 4 pos is over 0xFFF + * @note none + */ +uint8_t as5600_set_start_position(as5600_handle_t *handle, uint16_t pos); + +/** + * @brief get the start position + * @param[in] *handle points to an as5600 handle structure + * @param[out] *pos points to a start position buffer + * @return status code + * - 0 success + * - 1 get start position failed + * - 2 handle is NULL + * - 3 handle is not initialized + * @note none + */ +uint8_t as5600_get_start_position(as5600_handle_t *handle, uint16_t *pos); + +/** + * @brief set the stop position + * @param[in] *handle points to an as5600 handle structure + * @param[in] pos is the stop position + * @return status code + * - 0 success + * - 1 set stop position failed + * - 2 handle is NULL + * - 3 handle is not initialized + * - 4 pos is over 0xFFF + * @note none + */ +uint8_t as5600_set_stop_position(as5600_handle_t *handle, uint16_t pos); + +/** + * @brief get the stop position + * @param[in] *handle points to an as5600 handle structure + * @param[out] *pos points to a stop position buffer + * @return status code + * - 0 success + * - 1 get stop position failed + * - 2 handle is NULL + * - 3 handle is not initialized + * @note none + */ +uint8_t as5600_get_stop_position(as5600_handle_t *handle, uint16_t *pos); + +/** + * @brief set the max angle + * @param[in] *handle points to an as5600 handle structure + * @param[in] ang is the max angle + * @return status code + * - 0 success + * - 1 set max angle failed + * - 2 handle is NULL + * - 3 handle is not initialized + * - 4 ang is over 0xFFF + * @note none + */ +uint8_t as5600_set_max_angle(as5600_handle_t *handle, uint16_t ang); + +/** + * @brief get the max angle + * @param[in] *handle points to an as5600 handle structure + * @param[out] *ang points to a max angle buffer + * @return status code + * - 0 success + * - 1 get max angle failed + * - 2 handle is NULL + * - 3 handle is not initialized + * @note none + */ +uint8_t as5600_get_max_angle(as5600_handle_t *handle, uint16_t *ang); + +/** + * @brief enable or disable the watch dog + * @param[in] *handle points to an as5600 handle structure + * @param[in] enable is a bool value + * @return status code + * - 0 success + * - 1 set watchdog failed + * - 2 handle is NULL + * - 3 handle is not initialized + * @note none + */ +uint8_t as5600_set_watch_dog(as5600_handle_t *handle, as5600_bool_t enable); + +/** + * @brief get the watch dog status + * @param[in] *handle points to an as5600 handle structure + * @param[out] *enable points to a bool value buffer + * @return status code + * - 0 success + * - 1 get watchdog failed + * - 2 handle is NULL + * - 3 handle is not initialized + * @note none + */ +uint8_t as5600_get_watch_dog(as5600_handle_t *handle, as5600_bool_t *enable); + +/** + * @brief set the fast filter threshold + * @param[in] *handle points to an as5600 handle structure + * @param[in] threshold is the fast filter threshold + * @return status code + * - 0 success + * - 1 set fast filter threshold failed + * - 2 handle is NULL + * - 3 handle is not initialized + * @note none + */ +uint8_t as5600_set_fast_filter_threshold(as5600_handle_t *handle, as5600_fast_filter_threshold_t threshold); + +/** + * @brief get the fast filter threshold + * @param[in] *handle points to an as5600 handle structure + * @param[out] *threshold points to a fast filter threshold buffer + * @return status code + * - 0 success + * - 1 get fast filter threshold failed + * - 2 handle is NULL + * - 3 handle is not initialized + * @note none + */ +uint8_t as5600_get_fast_filter_threshold(as5600_handle_t *handle, as5600_fast_filter_threshold_t *threshold); + +/** + * @brief set the slow filter + * @param[in] *handle points to an as5600 handle structure + * @param[in] filter is the slow filter + * @return status code + * - 0 success + * - 1 set slow filter failed + * - 2 handle is NULL + * - 3 handle is not initialized + * @note none + */ +uint8_t as5600_set_slow_filter(as5600_handle_t *handle, as5600_slow_filter_t filter); + +/** + * @brief get the slow filter + * @param[in] *handle points to an as5600 handle structure + * @param[out] *filter points to a slow filter buffer + * @return status code + * - 0 success + * - 1 get slow filter failed + * - 2 handle is NULL + * - 3 handle is not initialized + * @note none + */ +uint8_t as5600_get_slow_filter(as5600_handle_t *handle, as5600_slow_filter_t *filter); + +/** + * @brief set the pwm frequency + * @param[in] *handle points to an as5600 handle structure + * @param[in] freq is the pwm frequency + * @return status code + * - 0 success + * - 1 set pwm frequency failed + * - 2 handle is NULL + * - 3 handle is not initialized + * @note none + */ +uint8_t as5600_set_pwm_frequency(as5600_handle_t *handle, as5600_pwm_frequency_t freq); + +/** + * @brief get the pwm frequency + * @param[in] *handle points to an as5600 handle structure + * @param[out] *freq points to a pwm frequency buffer + * @return status code + * - 0 success + * - 1 get pwm frequency failed + * - 2 handle is NULL + * - 3 handle is not initialized + * @note none + */ +uint8_t as5600_get_pwm_frequency(as5600_handle_t *handle, as5600_pwm_frequency_t *freq); + +/** + * @brief set the output stage + * @param[in] *handle points to an as5600 handle structure + * @param[in] stage is the output stage + * @return status code + * - 0 success + * - 1 set output stage failed + * - 2 handle is NULL + * - 3 handle is not initialized + * @note none + */ +uint8_t as5600_set_output_stage(as5600_handle_t *handle, as5600_output_stage_t stage); + +/** + * @brief get the output stage + * @param[in] *handle points to an as5600 handle structure + * @param[out] *stage points to an output stage buffer + * @return status code + * - 0 success + * - 1 get output stage failed + * - 2 handle is NULL + * - 3 handle is not initialized + * @note none + */ +uint8_t as5600_get_output_stage(as5600_handle_t *handle, as5600_output_stage_t *stage); + +/** + * @brief set the hysteresis + * @param[in] *handle points to an as5600 handle structure + * @param[in] hysteresis is the set hysteresis + * @return status code + * - 0 success + * - 1 set hysteresis failed + * - 2 handle is NULL + * - 3 handle is not initialized + * @note none + */ +uint8_t as5600_set_hysteresis(as5600_handle_t *handle, as5600_hysteresis_t hysteresis); + +/** + * @brief get the hysteresis + * @param[in] *handle points to an as5600 handle structure + * @param[out] *hysteresis points to a hysteresis buffer + * @return status code + * - 0 success + * - 1 get hysteresis failed + * - 2 handle is NULL + * - 3 handle is not initialized + * @note none + */ +uint8_t as5600_get_hysteresis(as5600_handle_t *handle, as5600_hysteresis_t *hysteresis); + +/** + * @brief set the power mode + * @param[in] *handle points to an as5600 handle structure + * @param[in] mode is the power mode + * @return status code + * - 0 success + * - 1 set power mode failed + * - 2 handle is NULL + * - 3 handle is not initialized + * @note none + */ +uint8_t as5600_set_power_mode(as5600_handle_t *handle, as5600_power_mode_t mode); + +/** + * @brief get the power mode + * @param[in] *handle points to an as5600 handle structure + * @param[out] *mode points to a power mode buffer + * @return status code + * - 0 success + * - 1 get power mode failed + * - 2 handle is NULL + * - 3 handle is not initialized + * @note none + */ +uint8_t as5600_get_power_mode(as5600_handle_t *handle, as5600_power_mode_t *mode); + +/** + * @brief get the raw angle + * @param[in] *handle points to an as5600 handle structure + * @param[out] *ang points to an ang buffer + * @return status code + * - 0 success + * - 1 get raw angle failed + * - 2 handle is NULL + * - 3 handle is not initialized + * @note none + */ +uint8_t as5600_get_raw_angle(as5600_handle_t *handle, uint16_t *ang); + +/** + * @brief get the angle + * @param[in] *handle points to an as5600 handle structure + * @param[out] *ang points to an ang buffer + * @return status code + * - 0 success + * - 1 get angle failed + * - 2 handle is NULL + * - 3 handle is not initialized + * @note none + */ +uint8_t as5600_get_angle(as5600_handle_t *handle, uint16_t *ang); + +/** + * @brief get the status + * @param[in] *handle points to an as5600 handle structure + * @param[out] *status points to a status buffer + * @return status code + * - 0 success + * - 1 get status failed + * - 2 handle is NULL + * - 3 handle is not initialized + * @note none + */ +uint8_t as5600_get_status(as5600_handle_t *handle, uint8_t *status); + +/** + * @brief get the automatic gain control + * @param[in] *handle points to an as5600 handle structure + * @param[out] *agc points to an agc buffer + * @return status code + * - 0 success + * - 1 get agc failed + * - 2 handle is NULL + * - 3 handle is not initialized + * @note none + */ +uint8_t as5600_get_agc(as5600_handle_t *handle, uint8_t *agc); + +/** + * @brief get the magnitude + * @param[in] *handle points to an as5600 handle structure + * @param[out] *magnitude points to a magnitude buffer + * @return status code + * - 0 success + * - 1 get magnitude failed + * - 2 handle is NULL + * - 3 handle is not initialized + * @note none + */ +uint8_t as5600_get_magnitude(as5600_handle_t *handle, uint16_t *magnitude); + +/** + * @brief set the burn + * @param[in] *handle points to an as5600 handle structure + * @param[in] burn is the set command + * @return status code + * - 0 success + * - 1 set burn failed + * - 2 handle is NULL + * - 3 handle is not initialized + * @note none + */ +uint8_t as5600_set_burn(as5600_handle_t *handle, as5600_burn_t burn); + +/** + * @} + */ + +/** + * @defgroup as5600_extend_driver as5600 extend driver function + * @brief as5600 extend driver modules + * @ingroup as5600_driver + * @{ + */ + +/** + * @brief set the chip register + * @param[in] *handle points to an as5600 handle structure + * @param[in] reg is the iic register address + * @param[in] *buf points to a data buffer + * @param[in] len is the data buffer length + * @return status code + * - 0 success + * - 1 write failed + * - 2 handle is NULL + * - 3 handle is not initialized + * @note none + */ +uint8_t as5600_set_reg(as5600_handle_t *handle, uint8_t reg, uint8_t *buf, uint16_t len); + +/** + * @brief get the chip register + * @param[in] *handle points to an as5600 handle structure + * @param[in] reg is the iic register address + * @param[out] *buf points to a data buffer + * @param[in] len is the data buffer length + * @return status code + * - 0 success + * - 1 read failed + * - 2 handle is NULL + * - 3 handle is not initialized + * @note none + */ +uint8_t as5600_get_reg(as5600_handle_t *handle, uint8_t reg, uint8_t *buf, uint16_t len); + +/** + * @} + */ + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/firmware/tinyg/main.c b/firmware/tinyg/main.c index 3f2d24278..c68e74b07 100755 --- a/firmware/tinyg/main.c +++ b/firmware/tinyg/main.c @@ -1,9 +1,9 @@ /* * main.c - TinyG - An embedded rs274/ngc CNC controller - * This file is part of the TinyG project. - * - * Copyright (c) 2010 - 2015 Alden S. Hart, Jr. - * Copyright (c) 2013 - 2015 Robert Giseburt + * This file is part of the TinyG project. + * + * Copyright (c) 2010 - 2015 Alden S. Hart, Jr. + * Copyright (c) 2013 - 2015 Robert Giseburt * * This file ("the software") is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2 as published by the @@ -40,83 +40,84 @@ #include #include "xmega/xmega_interrupts.h" #include "xmega/xmega_adc.h" +#include "xmega/xmega_twi.h" #endif // __AVR #ifdef __ARM -#include "MotateTimers.h" -using Motate::delay; +#include "MotateTimers.h" +using Motate::delay; -#ifdef __cplusplus -extern "C"{ -#endif // __cplusplus +#ifdef __cplusplus +extern "C"{ +#endif // __cplusplus -void _init() __attribute__ ((weak)); +void _init() __attribute__ ((weak)); void _init() {;} -void __libc_init_array(void); +void __libc_init_array(void); -#ifdef __cplusplus -} -#endif // __cplusplus +#ifdef __cplusplus +} +#endif // __cplusplus #endif // __ARM -/******************** Application Code ************************/ - -#ifdef __ARM -const Motate::USBSettings_t Motate::USBSettings = { - /*gVendorID = */ 0x1d50, - /*gProductID = */ 0x606d, - /*gProductVersion = */ TINYG_FIRMWARE_VERSION, - /*gAttributes = */ kUSBConfigAttributeSelfPowered, - /*gPowerConsumption = */ 500 -}; - /*gProductVersion = */ //0.1, - -Motate::USBDevice< Motate::USBCDC > usb; -//Motate::USBDevice< Motate::USBCDC, Motate::USBCDC > usb; - -typeof usb._mixin_0_type::Serial &SerialUSB = usb._mixin_0_type::Serial; -//typeof usb._mixin_1_type::Serial &SerialUSB1 = usb._mixin_1_type::Serial; - -MOTATE_SET_USB_VENDOR_STRING( {'S' ,'y', 'n', 't', 'h', 'e', 't', 'o', 's'} ) -MOTATE_SET_USB_PRODUCT_STRING( {'T', 'i', 'n', 'y', 'G', ' ', 'v', '2'} ) -MOTATE_SET_USB_SERIAL_NUMBER_STRING( {'0','0','1'} ) - -Motate::SPI spi; -#endif +/******************** Application Code ************************/ + +#ifdef __ARM +const Motate::USBSettings_t Motate::USBSettings = { + /*gVendorID = */ 0x1d50, + /*gProductID = */ 0x606d, + /*gProductVersion = */ TINYG_FIRMWARE_VERSION, + /*gAttributes = */ kUSBConfigAttributeSelfPowered, + /*gPowerConsumption = */ 500 +}; + /*gProductVersion = */ //0.1, + +Motate::USBDevice< Motate::USBCDC > usb; +//Motate::USBDevice< Motate::USBCDC, Motate::USBCDC > usb; + +typeof usb._mixin_0_type::Serial &SerialUSB = usb._mixin_0_type::Serial; +//typeof usb._mixin_1_type::Serial &SerialUSB1 = usb._mixin_1_type::Serial; + +MOTATE_SET_USB_VENDOR_STRING( {'S' ,'y', 'n', 't', 'h', 'e', 't', 'o', 's'} ) +MOTATE_SET_USB_PRODUCT_STRING( {'T', 'i', 'n', 'y', 'G', ' ', 'v', '2'} ) +MOTATE_SET_USB_SERIAL_NUMBER_STRING( {'0','0','1'} ) + +Motate::SPI spi; +#endif /* * _system_init() */ -void _system_init(void) -{ -#ifdef __ARM - SystemInit(); - - // Disable watchdog - WDT->WDT_MR = WDT_MR_WDDIS; - - // Initialize C library - __libc_init_array(); - - usb.attach(); // USB setup - delay(1000); -#endif -} +void _system_init(void) +{ +#ifdef __ARM + SystemInit(); + + // Disable watchdog + WDT->WDT_MR = WDT_MR_WDDIS; + + // Initialize C library + __libc_init_array(); + + usb.attach(); // USB setup + delay(1000); +#endif +} /* * _application_init() */ -static void _application_init(void) +static void _application_init(void) { // There are a lot of dependencies in the order of these inits. // Don't change the ordering unless you understand this. cli(); - // do these first + // do these first hardware_init(); // system hardware setup - must be first persistence_init(); // set up EEPROM or other NVM - must be second rtc_init(); // real time counter @@ -124,6 +125,7 @@ static void _application_init(void) // do these next stepper_init(); // stepper subsystem - must precede gpio_init() + twi_init(); // I2C bus (a.k.a TWI) encoder_init(); // virtual encoders switch_init(); // switches // gpio_init(); // parallel IO @@ -151,18 +153,18 @@ static void _application_init(void) int main(void) { - // system initialization - _system_init(); + // system initialization + _system_init(); // TinyG application setup - _application_init(); + _application_init(); run_canned_startup(); // run any pre-loaded commands - // main loop - for (;;) { - controller_run( ); // single pass through the controller - } - return 0; + // main loop + for (;;) { + controller_run( ); // single pass through the controller + } + return 0; } diff --git a/firmware/tinyg/xmega/xmega_twi.c b/firmware/tinyg/xmega/xmega_twi.c new file mode 100644 index 000000000..66041c057 --- /dev/null +++ b/firmware/tinyg/xmega/xmega_twi.c @@ -0,0 +1,249 @@ +/* + * File TWI.c + * Author: Tycho J�bsis + * Date: 13-02-2021 + */ + + +/* +* MIT License +* +* Copyright (c) 2021 TychoJ +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ + +#include +#include +#include "xmega_twi.h" + +void set_baud(TWI_t *twi, uint32_t TWI_speed){ + twi->MASTER.BAUD = TWI_BAUD(F_CPU, TWI_speed); +} + +void twi_init(TWI_t *twi, uint32_t TWI_speed, uint8_t timeout){ + set_baud(twi, TWI_speed); + set_acknowledge(twi, ACK); + twi->MASTER.CTRLA = TWI_MASTER_ENABLE_bm; + set_timeout(twi, timeout); +} + +void disable_TWI(TWI_t *twi){ + twi->MASTER.CTRLA = (0 << TWI_MASTER_ENABLE_bp); +} + +void set_timeout(TWI_t *twi, uint8_t time_out){ + switch(time_out){ + case TIMEOUT_DIS: + twi->MASTER.CTRLB = TIMEOUT_DIS; + twi->MASTER.STATUS = TWI_MASTER_BUSSTATE_IDLE_gc; + break; + + case TIMEOUT_50US: + twi->MASTER.CTRLB = TIMEOUT_50US; + twi->MASTER.STATUS = TWI_MASTER_BUSSTATE_UNKNOWN_gc; + break; + + case TIMEOUT_100US: + twi->MASTER.CTRLB = TIMEOUT_100US; + twi->MASTER.STATUS = TWI_MASTER_BUSSTATE_UNKNOWN_gc; + break; + + case TIMEOUT_200US: + twi->MASTER.CTRLB = TIMEOUT_200US; + twi->MASTER.STATUS = TWI_MASTER_BUSSTATE_UNKNOWN_gc; + break; + + default: + twi->MASTER.CTRLB = TIMEOUT_DIS; + twi->MASTER.STATUS = TWI_MASTER_BUSSTATE_IDLE_gc; + break; + } +} + +void set_acknowledge(TWI_t *twi, uint8_t ack){ + (ack == ACK) ? (twi->MASTER.CTRLC = (0 << 2)) : (twi->MASTER.CTRLC = (1 << 2)); +} + +uint8_t bus_state(TWI_t *twi){ + uint8_t ret = twi->MASTER.STATUS; + //ret = (000000 << 2); + if( (ret & TWI_MASTER_BUSSTATE_gm) == TWI_MASTER_BUSSTATE_UNKNOWN_gc ) return UNKNOWN_BUS_STATE; + if( (ret & TWI_MASTER_BUSSTATE_gm) != TWI_MASTER_BUSSTATE_IDLE_gc ) return BUS_NOT_IN_USE; + if( (ret & TWI_MASTER_BUSSTATE_gm) != TWI_MASTER_BUSSTATE_IDLE_gc ) return OWNER_OF_BUS; + if( (ret & TWI_MASTER_BUSSTATE_gm) != TWI_MASTER_BUSSTATE_IDLE_gc ) return BUS_IN_USE; + return 1; +} + +void set_bus_state_TWI(TWI_t *twi, uint8_t state){ + twi->MASTER.STATUS = state; +} + +uint8_t wait_till_send(TWI_t *twi, uint8_t rw){ + uint8_t send_suc = 0; + uint16_t time_passed = 0; + + while ( !send_suc){ + + if(twi->MASTER.STATUS & (TWI_MASTER_WIF_bm << rw)) send_suc = 1; + + if(time_passed > 1000) return DATA_NOT_SEND; + _delay_us(1); + time_passed++; + } + return TWI_STATUS_OK; +} + +uint8_t wait_till_received(TWI_t *twi, uint8_t rw){ + if(wait_till_send(twi, rw) == TWI_STATUS_OK) return TWI_STATUS_OK; + return DATA_NOT_RECEIVED; +} + +uint8_t start_TWI(TWI_t *twi, uint8_t addr, uint8_t rw){ + + if(bus_state(twi) != BUS_NOT_IN_USE) return bus_state(twi); + + if( !( (rw == READ) || (rw == WRITE) ) ) return INVALID_RW; + + twi->MASTER.ADDR = (addr << 1) | rw; //send slave address + if(wait_till_send(twi, rw) == DATA_NOT_SEND) return DATA_NOT_SEND; // wait until sent + + //when RXACK is 0 an ACK has been received + if(twi->MASTER.STATUS & TWI_MASTER_RXACK_bm){ + stop_TWI(twi); + return NACK; + } + + return ACK; +} + +uint8_t repeated_start_TWI(TWI_t *twi, uint8_t addr, uint8_t rw){ + if(bus_state(twi) != OWNER_OF_BUS) return bus_state(twi); + + if( !( (rw == READ) || (rw == WRITE) ) ) return INVALID_RW; + twi->MASTER.ADDR = (addr << 1) | rw; + + wait_till_send(twi, rw); + + + //when RXACK is 0 an ACK has been received + if(twi->MASTER.STATUS & TWI_MASTER_RXACK_bm) return NACK; + + return TWI_STATUS_OK; +} + +void stop_TWI(TWI_t *twi){ + twi->MASTER.CTRLC = TWI_MASTER_CMD_STOP_gc; + +} + +uint8_t send_TWI(TWI_t *twi, uint8_t data){ + twi->MASTER.DATA = data; + + if( wait_till_send(twi, WRITE) == DATA_NOT_SEND) return DATA_NOT_SEND; + + //when RXACK is 0 an ACK has been received + if(twi->MASTER.STATUS & TWI_MASTER_RXACK_bm) return NACK; + + return ACK; +} + +uint8_t read_TWI(TWI_t *twi, uint8_t *data, uint8_t go_on){ + if(wait_till_received(twi, READ) == DATA_NOT_RECEIVED) return DATA_NOT_RECEIVED; + (*data) = twi->MASTER.DATA; + twi->MASTER.CTRLC = ((go_on == ACK) ? TWI_MASTER_CMD_RECVTRANS_gc : // send ack (go on) or + TWI_MASTER_ACKACT_bm|TWI_MASTER_CMD_STOP_gc); // nack (and stop) + return TWI_STATUS_OK; +} + +uint8_t send_8bit_TWI(TWI_t *twi, uint8_t addr, uint8_t data){ + uint8_t err; + + err = start_TWI(twi, addr, WRITE); + + //check for errors + if(err == BUS_IN_USE) return BUS_IN_USE; + if(err == NACK) return NACK; + + err = send_TWI(twi, data); + + //check for errors + if(err == DATA_NOT_SEND) return DATA_NOT_SEND; + if(err == NACK) return NACK; + + stop_TWI(twi); + + return TWI_STATUS_OK; +} + +uint8_t write_8bit_register_TWI(TWI_t *twi, uint8_t addr, uint8_t data, uint8_t reg){ + uint8_t err; + + err = start_TWI(twi, addr, WRITE); + + //check for errors + if(err == BUS_IN_USE) return BUS_IN_USE; + if(err == NACK) return NACK; + + err = send_TWI(twi, reg); + + //check for errors + if(err == DATA_NOT_SEND) return DATA_NOT_SEND; + if(err == NACK) return NACK; + + err = send_TWI(twi, data); + + //check for errors + if(err == DATA_NOT_SEND) return DATA_NOT_SEND; + if(err == NACK) return NACK; + + stop_TWI(twi); + + return TWI_STATUS_OK; +} + +uint8_t read_8bit_register_TWI(TWI_t *twi, uint8_t addr, uint8_t *data, uint8_t reg){ + uint8_t err; + + err = start_TWI(twi, addr, WRITE); + + //check for errors + if(err == BUS_IN_USE) return BUS_IN_USE; + if(err == NACK) return NACK; + + err = send_TWI(twi, reg); + + //check for errors + if(err == DATA_NOT_SEND) return DATA_NOT_SEND; + if(err == NACK) return NACK; + + //err = repeated_start_TWI(twi, addr, READ); + twi->MASTER.ADDR = (addr << 1) | READ; + while( ! (twi->MASTER.STATUS & (TWI_MASTER_WIF_bm << READ)) ); // wait until sent + + //check for errors + if(err == BUS_IN_USE) return BUS_IN_USE; + if(err == NACK) return NACK; + + err = read_TWI(twi, data, NACK); + + if(err == DATA_NOT_RECEIVED) return DATA_NOT_RECEIVED; + + return TWI_STATUS_OK; +} diff --git a/firmware/tinyg/xmega/xmega_twi.h b/firmware/tinyg/xmega/xmega_twi.h new file mode 100644 index 000000000..2309f6b3d --- /dev/null +++ b/firmware/tinyg/xmega/xmega_twi.h @@ -0,0 +1,141 @@ +/* + * File TWI.h + * Author: Tycho J�bsis + * Date: 13-02-2021 + */ + +/* +* MIT License +* +* Copyright (c) 2021 TychoJ +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ + + +#include + +#ifndef F_CPU +#define F_CPU 2000000UL +#endif + +#ifndef TWI_H_ +#define TWI_H_ + +#define BAUD_100K 100000UL +#define BAUD_400K 400000UL + +#define TIMEOUT_DIS TWI_MASTER_TIMEOUT_DISABLED_gc +#define TIMEOUT_50US TWI_MASTER_TIMEOUT_50US_gc +#define TIMEOUT_100US TWI_MASTER_TIMEOUT_100US_gc +#define TIMEOUT_200US TWI_MASTER_TIMEOUT_200US_gc + +#define UNKNOWN_BUS_STATE 0 +#define BUS_NOT_IN_USE 1 +#define OWNER_OF_BUS 2 +#define BUS_IN_USE 3 + +#define UNKNOWN_BUS_STATE_GR TWI_MASTER_BUSSTATE_UNKNOWN_gc +#define BUS_NOT_IN_USE_GR TWI_MASTER_BUSSTATE_IDLE_gc +#define OWNER_OF_BUS_GR TWI_MASTER_BUSSTATE_OWNER_gc +#define BUS_IN_USE_GR TWI_MASTER_BUSSTATE_BUSY_gc + +#define INVALID_RW 4 + +#define WRITE 0 +#define READ 1 + +#define NACK 0 +#define ACK 1 +#define DATA_NOT_SEND 10 +#define DATA_NOT_RECEIVED 7 +#define TWI_STATUS_OK 5 + +//inline function to calculate the baud value +#define TWI_BAUD(F_SYS, F_TWI) ((F_SYS / (2 * F_TWI)) - 5) + +//set the baud rate of a TWI module +void set_baud(TWI_t *twi, uint32_t TWI_speed); + +//enables a TWI module +//TWI_speed is used to calculate the baud rate +//timeout: if you are the only master you should use TIMEOUT_DIS +void enable_TWI(TWI_t *twi, uint32_t TWI_speed, uint8_t timeout); + +//disables a TWI module +void disable_TWI(TWI_t *twi); + +//time in us after which it's assumed the TWI bus is free + +// disabled(normally used for i2c), 50us, 100us, 200us +void set_timeout(TWI_t *twi, uint8_t time_out); + +//selects if ACK or NACK will be used +void set_acknowledge(TWI_t *twi, uint8_t ack); + +//returns in what state the bus is +uint8_t bus_state(TWI_t *twi); + +//function used for setting the bus state +void set_bus_state_TWI(TWI_t *twi, uint8_t state); + +uint8_t wait_till_send(TWI_t *twi, uint8_t rw); + +uint8_t wait_till_received(TWI_t *twi, uint8_t rw); + +//issues an start condition and send an address +//returns 1 if an acknowledge is received +//returns 0 if a not acknowledge is received +//returns 3 if the bus is not free +uint8_t start_TWI(TWI_t *twi, uint8_t addr, uint8_t rw); + +uint8_t repeated_start_TWI(TWI_t *twi, uint8_t addr, uint8_t rw); + +//issues a stop condition +void stop_TWI(TWI_t *twi); + +//internal function used for sending data +uint8_t send_TWI(TWI_t *twi, uint8_t data); + +//internal function used to read data +//twi for what twi module +//data pointer to store read data +//go_on to continue or stop reading data +//returns 7 when data is not received +//returns 5 if data is received and no errors have occurred +//go_on 1 continue 0 stop reading +uint8_t read_TWI(TWI_t *twi, uint8_t *data, uint8_t go_on); + +//send 8bits to the address +uint8_t send_8bit_TWI(TWI_t *twi, uint8_t addr, uint8_t data); + +//twi is the TWI port +//addr is the address of the TWI device were you want to write to a register +//data is the data that you want to write in the register +//reg is the register to which you want to write the data +uint8_t write_8bit_register_TWI(TWI_t *twi, uint8_t addr, uint8_t data, uint8_t reg); + +//twi is the TWI port +//addr is the address of the TWI device were you want to read a register +//data is the variable where you want to store the data +//reg is the register you want to read data from +uint8_t read_8bit_register_TWI(TWI_t *twi, uint8_t addr, uint8_t *data, uint8_t reg); + + +#endif /* TWI_H_ */ \ No newline at end of file From b99e148ef83a45f4795d414de3ad97ffc4fdb136 Mon Sep 17 00:00:00 2001 From: brainstorm Date: Wed, 6 Mar 2024 22:49:15 +1100 Subject: [PATCH 15/18] =?UTF-8?q?Add=20fix=20for=20'cc1:=20error:=20invali?= =?UTF-8?q?d=20parameter=20=E2=80=98min-pagesize=E2=80=99'?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- firmware/tinyg/CMakeLists.txt | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/firmware/tinyg/CMakeLists.txt b/firmware/tinyg/CMakeLists.txt index 025b5d71c..6758005e2 100644 --- a/firmware/tinyg/CMakeLists.txt +++ b/firmware/tinyg/CMakeLists.txt @@ -82,10 +82,12 @@ target_compile_options(tinyg.elf PRIVATE ) if (CMAKE_COMPILER_IS_GNUCC AND CMAKE_C_COMPILER_VERSION VERSION_GREATER 12.0) - message(STATUS "Addding workaround for avr-gcc 12 bug") - target_compile_options(tinyg.elf PRIVATE - --param=min-pagesize=0 - ) + if (CMAKE_C_COMPILER_VERSION VERSION_LESS_EQUAL 13.0) + message(STATUS "Addding workaround for avr-gcc 12 bug") + target_compile_options(tinyg.elf PRIVATE + --param=min-pagesize=0 + ) + endif() endif() target_link_options(tinyg.elf PRIVATE -mmcu=atxmega192a3) From 629a06b4cd25e7fbc4ab8191f8cbfe5b539305cd Mon Sep 17 00:00:00 2001 From: brainstorm Date: Wed, 6 Mar 2024 22:49:39 +1100 Subject: [PATCH 16/18] Support builds from VSCode on Linux --- .vscode/c_cpp_properties.json | 21 +++++++++++++++++++++ .vscode/settings.json | 2 +- .vscode/tasks.json | 2 +- 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json index 03b0c15ec..d26c80ea1 100644 --- a/.vscode/c_cpp_properties.json +++ b/.vscode/c_cpp_properties.json @@ -1,5 +1,26 @@ { "configurations": [ + { + "name": "Linux", + "includePath": [ + "${workspaceFolder}/**", + "/usr/include/**" + ], + "defines": [ + "__AVR", + "__DOXYGEN__", + "__AVR_ATxmega192A3__", + "AS_5600_ENCODER" + ], + "compilerPath": "/usr/bin/avr-gcc", + "cStandard": "c17", + "intelliSenseMode": "linux-gcc-x64", + "compilerArgs": [ + "-mmcu=atmega192a3", // Will ensure MCU defines are set correctly + "-DF_CPU=16000000UL", // Will ensure F_CPU is set correctly + "-Os" // Will avoid optimization warnings re: _delay + ] + }, { "name": "Mac", "includePath": [ diff --git a/.vscode/settings.json b/.vscode/settings.json index a7d176832..82c3dcabe 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -3,5 +3,5 @@ "tinyg.h": "c", "xio.h": "c" }, - "cmake.sourceDirectory": "${workspaceFolder}/firmware/tinyg" + "cmake.sourceDirectory": "/home/rvalls/dev/personal/TinyG-openpnp/firmware/tinyg" } \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json index cfd806a1e..bc61046b5 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -23,7 +23,7 @@ "type": "pickString", "id": "makeTarget", "description": "Select a build target", "options": [ - "make -j $(nprocs)", "sudo make flash", "make disassemble", "make squeaky_clean", "make size", "make debug", + "make -j $(nprocs)" ], "default": "make -j $(nprocs)" } From 97aa819a122198723cc1e817b7459b5b51353ec7 Mon Sep 17 00:00:00 2001 From: brainstorm Date: Wed, 6 Mar 2024 23:25:43 +1100 Subject: [PATCH 17/18] VSCode environment setup, initialise TWI to TWIC with some reasonable defaults for TinyG v8 board (sacrificing RTS/CTS for TWI since XON/XOFF flow control should work well enough without overwhelming the MCU?) --- .vscode/c_cpp_properties.json | 15 ++++++++------- firmware/tinyg/CMakeLists.txt | 5 ----- firmware/tinyg/main.c | 2 +- firmware/tinyg/xmega/xmega_twi.c | 9 ++++++++- firmware/tinyg/xmega/xmega_twi.h | 3 +++ 5 files changed, 20 insertions(+), 14 deletions(-) diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json index d26c80ea1..ce158286e 100644 --- a/.vscode/c_cpp_properties.json +++ b/.vscode/c_cpp_properties.json @@ -4,7 +4,8 @@ "name": "Linux", "includePath": [ "${workspaceFolder}/**", - "/usr/include/**" + "/usr/lib/avr/include/**", + "/usr/lib/gcc/avr/5.4.0/include/**" ], "defines": [ "__AVR", @@ -16,9 +17,9 @@ "cStandard": "c17", "intelliSenseMode": "linux-gcc-x64", "compilerArgs": [ - "-mmcu=atmega192a3", // Will ensure MCU defines are set correctly - "-DF_CPU=16000000UL", // Will ensure F_CPU is set correctly - "-Os" // Will avoid optimization warnings re: _delay + "-mmcu=atmega192a3", + "-DF_CPU=16000000UL", + "-Os" ] }, { @@ -40,9 +41,9 @@ "cStandard": "c17", "intelliSenseMode": "linux-gcc-x64", "compilerArgs": [ - "-mmcu=atmega192a3", // Will ensure MCU defines are set correctly - "-DF_CPU=16000000UL", // Will ensure F_CPU is set correctly - "-Os" // Will avoid optimization warnings re: _delay + "-mmcu=atmega192a3", + "-DF_CPU=16000000UL", + "-Os" ] } ], diff --git a/firmware/tinyg/CMakeLists.txt b/firmware/tinyg/CMakeLists.txt index 6758005e2..e58f8b0fa 100644 --- a/firmware/tinyg/CMakeLists.txt +++ b/firmware/tinyg/CMakeLists.txt @@ -1,14 +1,9 @@ cmake_minimum_required(VERSION 3.10) -# https://stackoverflow.com/questions/54482519/avoid-cmake-to-add-the-flags-search-paths-first-and-headerpad-max-install-name set(HAVE_FLAG_SEARCH_PATHS_FIRST 0) project(tinyg VERSION 0.1.0) -# https://gitlab.kitware.com/cmake/cmake/-/issues/24599 unset(_CMAKE_APPLE_ARCHS_DEFAULT) -#set(CMAKE_C_COMPILER_TARGET atxmega192a3) -#set(CMAKE_SYSTEM_PROCESSOR ARM) -#set(CMAKE_SYSTEM_NAME Linux) set(CMAKE_C_COMPILER avr-gcc) add_executable(tinyg.elf diff --git a/firmware/tinyg/main.c b/firmware/tinyg/main.c index c68e74b07..6f9ba00ef 100755 --- a/firmware/tinyg/main.c +++ b/firmware/tinyg/main.c @@ -126,7 +126,7 @@ static void _application_init(void) // do these next stepper_init(); // stepper subsystem - must precede gpio_init() twi_init(); // I2C bus (a.k.a TWI) - encoder_init(); // virtual encoders + encoder_init(); // encoders (virtual or hardware) switch_init(); // switches // gpio_init(); // parallel IO pwm_init(); // pulse width modulation drivers - must follow gpio_init() diff --git a/firmware/tinyg/xmega/xmega_twi.c b/firmware/tinyg/xmega/xmega_twi.c index 66041c057..71bfbee28 100644 --- a/firmware/tinyg/xmega/xmega_twi.c +++ b/firmware/tinyg/xmega/xmega_twi.c @@ -37,7 +37,14 @@ void set_baud(TWI_t *twi, uint32_t TWI_speed){ twi->MASTER.BAUD = TWI_BAUD(F_CPU, TWI_speed); } -void twi_init(TWI_t *twi, uint32_t TWI_speed, uint8_t timeout){ +/// "Reasonable defaults" here include settings for TinyG v8 board +/// This TWI mod involves physically getting rid of CTS/RTS signals +/// in favor of this I2C compatible (TWI) bus. +void twi_init() { + enable_TWI(&TWIC, BAUD_100K, TIMEOUT_DIS); +} + +void enable_TWI(TWI_t *twi, uint32_t TWI_speed, uint8_t timeout){ set_baud(twi, TWI_speed); set_acknowledge(twi, ACK); twi->MASTER.CTRLA = TWI_MASTER_ENABLE_bm; diff --git a/firmware/tinyg/xmega/xmega_twi.h b/firmware/tinyg/xmega/xmega_twi.h index 2309f6b3d..1978749dc 100644 --- a/firmware/tinyg/xmega/xmega_twi.h +++ b/firmware/tinyg/xmega/xmega_twi.h @@ -73,6 +73,9 @@ //set the baud rate of a TWI module void set_baud(TWI_t *twi, uint32_t TWI_speed); +// initialises TWI module with "reasonable defaults" +void twi_init(); + //enables a TWI module //TWI_speed is used to calculate the baud rate //timeout: if you are the only master you should use TIMEOUT_DIS From 7abf2457c55e935e57a354f4ba83de11685e549c Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Thu, 7 Mar 2024 22:43:56 +1100 Subject: [PATCH 18/18] Next up: translate TWI_t to the higher level as5600 i2c read/write function arguments --- .vscode/settings.json | 3 ++- firmware/tinyg/encoder.c | 15 ++++++++++++++- firmware/tinyg/xmega/xmega_twi.c | 2 ++ 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 82c3dcabe..332509880 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,7 +1,8 @@ { "files.associations": { "tinyg.h": "c", - "xio.h": "c" + "xio.h": "c", + "encoder.h": "c" }, "cmake.sourceDirectory": "/home/rvalls/dev/personal/TinyG-openpnp/firmware/tinyg" } \ No newline at end of file diff --git a/firmware/tinyg/encoder.c b/firmware/tinyg/encoder.c index 58c81c879..539bd1c9c 100755 --- a/firmware/tinyg/encoder.c +++ b/firmware/tinyg/encoder.c @@ -28,6 +28,10 @@ #include "tinyg.h" #include "config.h" #include "encoder.h" +#include "xmega/xmega_twi.h" + +// Hardware encoder support +#include "encoder_as5600.h" #ifdef __cplusplus extern "C"{ @@ -41,15 +45,24 @@ enEncoders_t en; **** CODE ************************************************************************** ************************************************************************************/ +// i2c_init and i2c_deinit are already handled elsewhere +// so we need to do nothing on the as5600 handler for those +// function pointers. +void nop() {} + /* * encoder_init() - initialize encoders */ void encoder_init() { +as5600_handle_t handler = { &nop, &nop, &read_TWI, &send_TWI, 0, printf, 1 }; +// Real hardware sensor(s) #ifdef AS_5600_ENCODER - as5600_init(); + as5600_init(&handler); #endif + +// Fake one memset(&en, 0, sizeof(en)); // clear all values, pointers and status encoder_init_assertions(); } diff --git a/firmware/tinyg/xmega/xmega_twi.c b/firmware/tinyg/xmega/xmega_twi.c index 71bfbee28..7d8290fce 100644 --- a/firmware/tinyg/xmega/xmega_twi.c +++ b/firmware/tinyg/xmega/xmega_twi.c @@ -4,6 +4,8 @@ * Date: 13-02-2021 */ +// Interesting software bit-banging alternative that might not require +// hardware mods to the TinyG v8 board: https://github.com/dvdfreitag/xmega-software-twi/tree/master /* * MIT License