diff options
| author | Scott Shawcroft <scott@adafruit.com> | 2020-09-22 12:39:52 -0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2020-09-22 12:39:52 -0700 |
| commit | 98185e914b201fc78b334ef04e83e6b9c17c0a1b (patch) | |
| tree | 73f842995832ccbebf209aeb5298d9b20afe0278 | |
| parent | 9f892cdda81e9cb941b3111ead0de5dca6827347 (diff) | |
| parent | 82f37c92520248cef5086ac62a8483ae087e4531 (diff) | |
Merge pull request #3425 from jepler/canbus
canio: Initial implementation for SAM E5x MCUs
40 files changed, 2963 insertions, 40 deletions
diff --git a/locale/circuitpython.pot b/locale/circuitpython.pot index 37212e241..c9114dd5c 100644 --- a/locale/circuitpython.pot +++ b/locale/circuitpython.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-09-16 17:07-0700\n" +"POT-Creation-Date: 2020-09-21 16:45-0500\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" @@ -92,7 +92,11 @@ msgstr "" msgid "%q must be a tuple of length 2" msgstr "" -#: ports/atmel-samd/common-hal/sdioio/SDCard.c +#: shared-bindings/canio/Match.c +msgid "%q out of range" +msgstr "" + +#: ports/atmel-samd/common-hal/microcontroller/Pin.c msgid "%q pin invalid" msgstr "" @@ -280,6 +284,10 @@ msgstr "" msgid "All I2C peripherals are in use" msgstr "" +#: ports/atmel-samd/common-hal/canio/Listener.c +msgid "All RX FIFOs in use" +msgstr "" + #: ports/nrf/common-hal/busio/SPI.c msgid "All SPI peripherals are in use" msgstr "" @@ -316,6 +324,10 @@ msgstr "" msgid "Already advertising." msgstr "" +#: ports/atmel-samd/common-hal/canio/Listener.c +msgid "Already have all-matches listener" +msgstr "" + #: shared-module/memorymonitor/AllocationAlarm.c #: shared-module/memorymonitor/AllocationSize.c msgid "Already running" @@ -752,7 +764,7 @@ msgstr "" msgid "Error in regex" msgstr "" -#: shared-bindings/_bleio/__init__.c shared-bindings/aesio/aes.c +#: py/enum.c shared-bindings/_bleio/__init__.c shared-bindings/aesio/aes.c #: shared-bindings/busio/SPI.c shared-bindings/microcontroller/Pin.c #: shared-bindings/neopixel_write/__init__.c #: shared-bindings/terminalio/Terminal.c @@ -845,6 +857,10 @@ msgstr "" msgid "File exists" msgstr "" +#: ports/atmel-samd/common-hal/canio/Listener.c +msgid "Filters too complex" +msgstr "" + #: ports/cxd56/common-hal/camera/Camera.c msgid "Format not supported" msgstr "" @@ -1127,6 +1143,10 @@ msgstr "" msgid "Maximum x value when mirrored is %d" msgstr "" +#: shared-bindings/canio/Message.c +msgid "Messages limited to 8 bytes" +msgstr "" + #: supervisor/shared/safe_mode.c msgid "MicroPython NLR jump failed. Likely memory corruption." msgstr "" @@ -2310,6 +2330,10 @@ msgstr "" msgid "exceptions must derive from BaseException" msgstr "" +#: shared-bindings/canio/CAN.c shared-bindings/canio/Listener.c +msgid "expected '%q' but got '%q'" +msgstr "" + #: py/objstr.c msgid "expected ':' after format specifier" msgstr "" @@ -3147,6 +3171,10 @@ msgstr "" msgid "source palette too large" msgstr "" +#: shared-bindings/canio/Message.c +msgid "specify size or data, but not both" +msgstr "" + #: py/objstr.c msgid "start/end indices" msgstr "" @@ -3261,7 +3289,7 @@ msgid "tuple/list has wrong length" msgstr "" #: ports/atmel-samd/common-hal/busio/UART.c ports/nrf/common-hal/busio/UART.c -#: shared-bindings/busio/UART.c +#: shared-bindings/busio/UART.c shared-bindings/canio/CAN.c msgid "tx and rx cannot both be None" msgstr "" @@ -81,6 +81,10 @@ #include "supervisor/shared/bluetooth.h" #endif +#if CIRCUITPY_CANIO +#include "common-hal/canio/CAN.h" +#endif + void do_str(const char *src, mp_parse_input_kind_t input_kind) { mp_lexer_t *lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, src, strlen(src), 0); if (lex == NULL) { @@ -226,6 +230,10 @@ void cleanup_after_vm(supervisor_allocation* heap) { free_memory(heap); supervisor_move_memory(); + #if CIRCUITPY_CANIO + common_hal_canio_reset(); + #endif + reset_port(); #if CIRCUITPY_BOARD reset_board_busses(); diff --git a/ports/atmel-samd/Makefile b/ports/atmel-samd/Makefile index aba630dbb..1ee21679d 100644 --- a/ports/atmel-samd/Makefile +++ b/ports/atmel-samd/Makefile @@ -393,6 +393,10 @@ SRC_QSTR += $(HEADER_BUILD)/sdiodata.h $(HEADER_BUILD)/sdiodata.h: tools/mksdiodata.py | $(HEADER_BUILD) $(Q)$(PYTHON3) $< > $@ +SRC_QSTR += $(HEADER_BUILD)/candata.h +$(HEADER_BUILD)/candata.h: tools/mkcandata.py | $(HEADER_BUILD) + $(Q)$(PYTHON3) $< > $@ + SRC_QSTR += $(SRC_C) $(SRC_SUPERVISOR) $(SRC_COMMON_HAL_EXPANDED) $(SRC_SHARED_MODULE_EXPANDED) # Sources that only hold QSTRs after pre-processing. SRC_QSTR_PREPROCESSOR += peripherals/samd/$(PERIPHERALS_CHIP_FAMILY)/clocks.c diff --git a/ports/atmel-samd/asf4_conf/same51/peripheral_clk_config.h b/ports/atmel-samd/asf4_conf/same51/peripheral_clk_config.h index 59fe8730e..51173f6d3 100644 --- a/ports/atmel-samd/asf4_conf/same51/peripheral_clk_config.h +++ b/ports/atmel-samd/asf4_conf/same51/peripheral_clk_config.h @@ -1165,6 +1165,88 @@ #define CONF_SDHC1_SLOW_FREQUENCY 12000000 #endif +// <h> CAN Clock Settings +// <y> CAN Clock source + +// <GCLK_PCHCTRL_GEN_GCLK0_Val"> Generic clock generator 0 + +// <GCLK_PCHCTRL_GEN_GCLK1_Val"> Generic clock generator 1 + +// <GCLK_PCHCTRL_GEN_GCLK2_Val"> Generic clock generator 2 + +// <GCLK_PCHCTRL_GEN_GCLK3_Val"> Generic clock generator 3 + +// <GCLK_PCHCTRL_GEN_GCLK4_Val"> Generic clock generator 4 + +// <GCLK_PCHCTRL_GEN_GCLK5_Val"> Generic clock generator 5 + +// <GCLK_PCHCTRL_GEN_GCLK6_Val"> Generic clock generator 6 + +// <GCLK_PCHCTRL_GEN_GCLK7_Val"> Generic clock generator 7 + +// <GCLK_PCHCTRL_GEN_GCLK8_Val"> Generic clock generator 8 + +// <GCLK_PCHCTRL_GEN_GCLK9_Val"> Generic clock generator 9 + +// <GCLK_PCHCTRL_GEN_GCLK10_Val"> Generic clock generator 10 + +// <GCLK_PCHCTRL_GEN_GCLK11_Val"> Generic clock generator 11 + +// <i> Select the clock source for CAN. +// <id> sdhc_gclk_selection +#ifndef CONF_GCLK_CAN0_SRC +#define CONF_GCLK_CAN0_SRC GCLK_PCHCTRL_GEN_GCLK0_Val +#endif + +/** + * \def CAN FREQUENCY + * \brief CAN's Clock frequency + */ +#ifndef CONF_CAN0_FREQUENCY +#define CONF_CAN0_FREQUENCY 120000000 +#endif + +// <h> CAN Clock Settings +// <y> CAN Clock source + +// <GCLK_PCHCTRL_GEN_GCLK0_Val"> Generic clock generator 0 + +// <GCLK_PCHCTRL_GEN_GCLK1_Val"> Generic clock generator 1 + +// <GCLK_PCHCTRL_GEN_GCLK2_Val"> Generic clock generator 2 + +// <GCLK_PCHCTRL_GEN_GCLK3_Val"> Generic clock generator 3 + +// <GCLK_PCHCTRL_GEN_GCLK4_Val"> Generic clock generator 4 + +// <GCLK_PCHCTRL_GEN_GCLK5_Val"> Generic clock generator 5 + +// <GCLK_PCHCTRL_GEN_GCLK6_Val"> Generic clock generator 6 + +// <GCLK_PCHCTRL_GEN_GCLK7_Val"> Generic clock generator 7 + +// <GCLK_PCHCTRL_GEN_GCLK8_Val"> Generic clock generator 8 + +// <GCLK_PCHCTRL_GEN_GCLK9_Val"> Generic clock generator 9 + +// <GCLK_PCHCTRL_GEN_GCLK10_Val"> Generic clock generator 10 + +// <GCLK_PCHCTRL_GEN_GCLK11_Val"> Generic clock generator 11 + +// <i> Select the clock source for CAN. +// <id> sdhc_gclk_selection +#ifndef CONF_GCLK_CAN1_SRC +#define CONF_GCLK_CAN1_SRC GCLK_PCHCTRL_GEN_GCLK0_Val +#endif + +/** + * \def CAN FREQUENCY + * \brief CAN's Clock frequency + */ +#ifndef CONF_CAN1_FREQUENCY +#define CONF_CAN1_FREQUENCY 120000000 +#endif + // <<< end of configuration section >>> #endif // PERIPHERAL_CLK_CONFIG_H diff --git a/ports/atmel-samd/asf4_conf/same54/peripheral_clk_config.h b/ports/atmel-samd/asf4_conf/same54/peripheral_clk_config.h index 59fe8730e..51173f6d3 100644 --- a/ports/atmel-samd/asf4_conf/same54/peripheral_clk_config.h +++ b/ports/atmel-samd/asf4_conf/same54/peripheral_clk_config.h @@ -1165,6 +1165,88 @@ #define CONF_SDHC1_SLOW_FREQUENCY 12000000 #endif +// <h> CAN Clock Settings +// <y> CAN Clock source + +// <GCLK_PCHCTRL_GEN_GCLK0_Val"> Generic clock generator 0 + +// <GCLK_PCHCTRL_GEN_GCLK1_Val"> Generic clock generator 1 + +// <GCLK_PCHCTRL_GEN_GCLK2_Val"> Generic clock generator 2 + +// <GCLK_PCHCTRL_GEN_GCLK3_Val"> Generic clock generator 3 + +// <GCLK_PCHCTRL_GEN_GCLK4_Val"> Generic clock generator 4 + +// <GCLK_PCHCTRL_GEN_GCLK5_Val"> Generic clock generator 5 + +// <GCLK_PCHCTRL_GEN_GCLK6_Val"> Generic clock generator 6 + +// <GCLK_PCHCTRL_GEN_GCLK7_Val"> Generic clock generator 7 + +// <GCLK_PCHCTRL_GEN_GCLK8_Val"> Generic clock generator 8 + +// <GCLK_PCHCTRL_GEN_GCLK9_Val"> Generic clock generator 9 + +// <GCLK_PCHCTRL_GEN_GCLK10_Val"> Generic clock generator 10 + +// <GCLK_PCHCTRL_GEN_GCLK11_Val"> Generic clock generator 11 + +// <i> Select the clock source for CAN. +// <id> sdhc_gclk_selection +#ifndef CONF_GCLK_CAN0_SRC +#define CONF_GCLK_CAN0_SRC GCLK_PCHCTRL_GEN_GCLK0_Val +#endif + +/** + * \def CAN FREQUENCY + * \brief CAN's Clock frequency + */ +#ifndef CONF_CAN0_FREQUENCY +#define CONF_CAN0_FREQUENCY 120000000 +#endif + +// <h> CAN Clock Settings +// <y> CAN Clock source + +// <GCLK_PCHCTRL_GEN_GCLK0_Val"> Generic clock generator 0 + +// <GCLK_PCHCTRL_GEN_GCLK1_Val"> Generic clock generator 1 + +// <GCLK_PCHCTRL_GEN_GCLK2_Val"> Generic clock generator 2 + +// <GCLK_PCHCTRL_GEN_GCLK3_Val"> Generic clock generator 3 + +// <GCLK_PCHCTRL_GEN_GCLK4_Val"> Generic clock generator 4 + +// <GCLK_PCHCTRL_GEN_GCLK5_Val"> Generic clock generator 5 + +// <GCLK_PCHCTRL_GEN_GCLK6_Val"> Generic clock generator 6 + +// <GCLK_PCHCTRL_GEN_GCLK7_Val"> Generic clock generator 7 + +// <GCLK_PCHCTRL_GEN_GCLK8_Val"> Generic clock generator 8 + +// <GCLK_PCHCTRL_GEN_GCLK9_Val"> Generic clock generator 9 + +// <GCLK_PCHCTRL_GEN_GCLK10_Val"> Generic clock generator 10 + +// <GCLK_PCHCTRL_GEN_GCLK11_Val"> Generic clock generator 11 + +// <i> Select the clock source for CAN. +// <id> sdhc_gclk_selection +#ifndef CONF_GCLK_CAN1_SRC +#define CONF_GCLK_CAN1_SRC GCLK_PCHCTRL_GEN_GCLK0_Val +#endif + +/** + * \def CAN FREQUENCY + * \brief CAN's Clock frequency + */ +#ifndef CONF_CAN1_FREQUENCY +#define CONF_CAN1_FREQUENCY 120000000 +#endif + // <<< end of configuration section >>> #endif // PERIPHERAL_CLK_CONFIG_H diff --git a/ports/atmel-samd/boards/common.template.ld b/ports/atmel-samd/boards/common.template.ld index 1054605c8..53c4cf5ee 100644 --- a/ports/atmel-samd/boards/common.template.ld +++ b/ports/atmel-samd/boards/common.template.ld @@ -43,6 +43,15 @@ SECTIONS _sidata = .; /* start of .data section */ } >FLASH_FIRMWARE + /* Data accessed by the CAN peripheral must be in the first 64kB RAM */ + /* place it at the very start of RAM, before the .data section */ + /* it is zeroed by reset_port */ + .canram (NOLOAD) : + { + . = ALIGN(4); + *(.canram) + } > RAM + /* This is the initialized data section The program executes knowing that the data is in the RAM but the loader puts the initial values in the FLASH_FIRMWARE (inidata). @@ -61,7 +70,7 @@ SECTIONS } >RAM /* Uninitialized data section */ - .bss : + .bss (NOLOAD) : { . = ALIGN(4); _sbss = .; diff --git a/ports/atmel-samd/boards/feather_m4_can/mpconfigboard.mk b/ports/atmel-samd/boards/feather_m4_can/mpconfigboard.mk index 306e7fe42..345ac33c5 100644 --- a/ports/atmel-samd/boards/feather_m4_can/mpconfigboard.mk +++ b/ports/atmel-samd/boards/feather_m4_can/mpconfigboard.mk @@ -12,3 +12,4 @@ EXTERNAL_FLASH_DEVICES = GD25Q16C LONGINT_IMPL = MPZ CIRCUITPY_VECTORIO = 1 +CIRCUITPY_CANIO = 1 diff --git a/ports/atmel-samd/boards/same54_xplained/mpconfigboard.mk b/ports/atmel-samd/boards/same54_xplained/mpconfigboard.mk index 7ac126514..e4472e05d 100644 --- a/ports/atmel-samd/boards/same54_xplained/mpconfigboard.mk +++ b/ports/atmel-samd/boards/same54_xplained/mpconfigboard.mk @@ -12,3 +12,4 @@ EXTERNAL_FLASH_DEVICES = "N25Q256A" LONGINT_IMPL = MPZ CIRCUITPY_SDIOIO = 1 +CIRCUITPY_CANIO = 1 diff --git a/ports/atmel-samd/boards/same54_xplained/pins.c b/ports/atmel-samd/boards/same54_xplained/pins.c index 8a864ab97..1ed431fba 100644 --- a/ports/atmel-samd/boards/same54_xplained/pins.c +++ b/ports/atmel-samd/boards/same54_xplained/pins.c @@ -48,8 +48,9 @@ STATIC const mp_rom_map_elem_t board_global_dict_table[] = { { MP_OBJ_NEW_QSTR(MP_QSTR_QT), MP_ROM_PTR(&pin_PA16) }, - { MP_OBJ_NEW_QSTR(MP_QSTR_CANRX), MP_ROM_PTR(&pin_PB12) }, - { MP_OBJ_NEW_QSTR(MP_QSTR_CANTX), MP_ROM_PTR(&pin_PB13) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_CAN_RX), MP_ROM_PTR(&pin_PB13) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_CAN_TX), MP_ROM_PTR(&pin_PB12) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_CAN_STANDBY), MP_ROM_PTR(&pin_PC13) }, // EXT1 header { MP_OBJ_NEW_QSTR(MP_QSTR_PB04), MP_ROM_PTR(&pin_PB04) }, diff --git a/ports/atmel-samd/common-hal/canio/CAN.c b/ports/atmel-samd/common-hal/canio/CAN.c new file mode 100644 index 000000000..4f28698fb --- /dev/null +++ b/ports/atmel-samd/common-hal/canio/CAN.c @@ -0,0 +1,445 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jeff Epler for Adafruit Industries + * + * 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 <string.h> + +#include "py/runtime.h" +#include "py/mperrno.h" + +#include "peripheral_clk_config.h" + +#include "common-hal/canio/CAN.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/util.h" +#include "supervisor/port.h" + +#include "component/can.h" + +#include "genhdr/candata.h" + +STATIC Can * const can_insts[] = CAN_INSTS; + +STATIC canio_can_obj_t *can_objs[MP_ARRAY_SIZE(can_insts)]; + +// This must be placed in the first 64kB of RAM +STATIC COMPILER_SECTION(".canram") canio_can_state_t can_state[MP_ARRAY_SIZE(can_insts)]; + +void common_hal_canio_can_construct(canio_can_obj_t *self, mcu_pin_obj_t *tx, mcu_pin_obj_t *rx, int baudrate, bool loopback, bool silent) +{ + mcu_pin_function_t *tx_function = mcu_find_pin_function(can_tx, tx, -1, MP_QSTR_tx); + int instance = tx_function->instance; + + mcu_pin_function_t *rx_function = mcu_find_pin_function(can_rx, rx, instance, MP_QSTR_rx); + + const uint32_t can_frequency = CONF_CAN0_FREQUENCY; + +#define DIV_ROUND(a, b) (((a) + (b)/2) / (b)) +#define DIV_ROUND_UP(a, b) (((a) + (b) - 1) / (b)) + + uint32_t clocks_per_bit = DIV_ROUND(can_frequency, baudrate); + uint32_t clocks_to_sample = DIV_ROUND(clocks_per_bit * 7, 8); + uint32_t clocks_after_sample = clocks_per_bit - clocks_to_sample; + uint32_t divisor = MAX(DIV_ROUND_UP(clocks_to_sample, 256), DIV_ROUND_UP(clocks_after_sample, 128)); + if (divisor > 32) { + mp_raise_OSError(MP_EINVAL); // baudrate cannot be attained (16kHz or something is lower bound, should never happen) + } + + gpio_set_pin_direction(tx_function->pin, GPIO_DIRECTION_OUT); + gpio_set_pin_function(tx_function->pin, tx_function->function); + common_hal_never_reset_pin(tx_function->obj); + + gpio_set_pin_direction(rx_function->pin, GPIO_DIRECTION_IN); + gpio_set_pin_function(rx_function->pin, rx_function->function); + common_hal_never_reset_pin(rx_function->obj); + + self->tx_pin_number = tx ? common_hal_mcu_pin_number(tx) : COMMON_HAL_MCU_NO_PIN; + self->rx_pin_number = rx ? common_hal_mcu_pin_number(rx) : COMMON_HAL_MCU_NO_PIN; + self->hw = can_insts[instance]; + self->state = &can_state[instance]; + + self->loopback = loopback; + self->silent = silent; + + // Allow configuration change + hri_can_set_CCCR_INIT_bit(self->hw); + while (hri_can_get_CCCR_INIT_bit(self->hw) == 0) { + } + hri_can_set_CCCR_CCE_bit(self->hw); + + if (instance == 0) { + hri_mclk_set_AHBMASK_CAN0_bit(MCLK); + hri_gclk_write_PCHCTRL_reg(GCLK, CAN0_GCLK_ID, CONF_GCLK_CAN0_SRC | (1 << GCLK_PCHCTRL_CHEN_Pos)); + + NVIC_DisableIRQ(CAN0_IRQn); + NVIC_ClearPendingIRQ(CAN0_IRQn); + NVIC_EnableIRQ(CAN0_IRQn); + hri_can_write_ILE_reg(self->hw, CAN_ILE_EINT0); +#ifdef CAN1_GCLK_ID + } else if (instance == 1) { + hri_mclk_set_AHBMASK_CAN1_bit(MCLK); + hri_gclk_write_PCHCTRL_reg(GCLK, CAN1_GCLK_ID, CONF_GCLK_CAN1_SRC | (1 << GCLK_PCHCTRL_CHEN_Pos)); + + NVIC_DisableIRQ(CAN1_IRQn); + NVIC_ClearPendingIRQ(CAN1_IRQn); + NVIC_EnableIRQ(CAN1_IRQn); + hri_can_write_ILE_reg(self->hw, CAN_ILE_EINT0); +#endif + } + + self->hw->CCCR.bit.FDOE = 0; // neither FD nor Bit Rate Switch enabled + self->hw->CCCR.bit.BRSE = 0; + + hri_can_write_MRCFG_reg(self->hw, CAN_MRCFG_QOS(CAN_MRCFG_QOS_DISABLE_Val)); // QoS disabled (no sensitive operation) + + // A "nominal bit" is a header bit. With dual rate CAN FD, this is a slower rate + { + CAN_NBTP_Type btp = { + // 0 means "1 tq", but 2 is subtracted from NTSEG1 for the + // fixed 1 "SYNC" tq + .bit.NTSEG1 = DIV_ROUND(clocks_to_sample, divisor) - 2, + .bit.NTSEG2 = DIV_ROUND(clocks_after_sample, divisor) - 1, + .bit.NBRP = divisor - 1, + .bit.NSJW = DIV_ROUND(clocks_after_sample, divisor * 4), + }; + hri_can_write_NBTP_reg(self->hw, btp.reg); + } + + // A "data bit" is a data bit :) with dula rate CAN FD, this is a higher + // rate. However, CAN FD is not implemented in CircuitPython, and this is + // the same rate as the "nominal rate". + { + CAN_DBTP_Type btp = { + .bit.DTSEG1 = DIV_ROUND(clocks_to_sample, divisor) - 1, + .bit.DTSEG2 = DIV_ROUND(clocks_after_sample, divisor) - 1, + .bit.DBRP = divisor - 1, + .bit.DSJW = DIV_ROUND(clocks_after_sample, divisor * 4), + }; + hri_can_write_DBTP_reg(self->hw, btp.reg); + } + + { + CAN_RXF0C_Type rxf = { + .bit.F0SA = (uint32_t)self->state->rx0_fifo, + .bit.F0S = COMMON_HAL_CANIO_RX_FIFO_SIZE, + }; + hri_can_write_RXF0C_reg(self->hw, rxf.reg); + } + + { + CAN_RXF1C_Type rxf = { + .bit.F1SA = (uint32_t)self->state->rx1_fifo, + .bit.F1S = COMMON_HAL_CANIO_RX_FIFO_SIZE, + }; + hri_can_write_RXF1C_reg(self->hw, rxf.reg); + } + + // All RX data has an 8 byte payload (max) + { + CAN_RXESC_Type esc = { + .bit.F0DS = CAN_RXESC_F0DS_DATA8_Val, + .bit.F1DS = CAN_RXESC_F1DS_DATA8_Val, + .bit.RBDS = CAN_RXESC_RBDS_DATA8_Val, + }; + hri_can_write_RXESC_reg(self->hw, esc.reg); + } + + // All TX data has an 8 byte payload (max) + { + CAN_TXESC_Type esc = { + .bit.TBDS = CAN_TXESC_TBDS_DATA8_Val, + }; + hri_can_write_TXESC_reg(self->hw, esc.reg); + } + + { + CAN_TXBC_Type bc = { + .bit.TBSA = (uint32_t)self->state->tx_buffer, + .bit.NDTB = COMMON_HAL_CANIO_TX_FIFO_SIZE, + .bit.TFQM = 0, // Messages are transmitted in the order submitted + }; + hri_can_write_TXBC_reg(self->hw, bc.reg); + } + + { + CAN_TXEFC_Type efc = { + .bit.EFS = 0, + }; + hri_can_write_TXEFC_reg(self->hw, efc.reg); + } + + { + CAN_GFC_Type gfc = { + .bit.RRFE = 0, + .bit.ANFS = CAN_GFC_ANFS_REJECT_Val, + .bit.ANFE = CAN_GFC_ANFE_REJECT_Val, + }; + hri_can_write_GFC_reg(self->hw, gfc.reg); + } + + { + CAN_SIDFC_Type dfc = { + .bit.LSS = COMMON_HAL_CANIO_RX_FILTER_SIZE, + .bit.FLSSA = (uint32_t)self->state->standard_rx_filter + }; + hri_can_write_SIDFC_reg(self->hw, dfc.reg); + } + + { + CAN_XIDFC_Type dfc = { + .bit.LSE = COMMON_HAL_CANIO_RX_FILTER_SIZE, + .bit.FLESA = (uint32_t)self->state->extended_rx_filter + }; + hri_can_write_XIDFC_reg(self->hw, dfc.reg); + } + + { + CAN_IE_Type ie = { + .bit.EWE = 1, + .bit.EPE = 1, + .bit.BOE = 1, + }; + hri_can_write_IE_reg(self->hw, ie.reg); + } + + hri_can_write_XIDAM_reg(self->hw, CAN_XIDAM_RESETVALUE); + +// silent: The CAN is set in Bus Monitoring Mode by programming CCCR.MON to '1'. (tx pin unused) +// external loopback: The CAN can be set in External Loop Back Mode by programming TEST.LBCK and CCCR.MON to '1'. (rx pin unused) +// internal loopback (silent loopback): Internal Loop Back Mode is entered by programming bits TEST.LBCK and CCCR.MON to '1'. (tx, rx unused) + self->hw->CCCR.bit.MON = silent; + self->hw->CCCR.bit.TEST = loopback; + self->hw->TEST.bit.LBCK = loopback; + + if (instance == 0) { + NVIC_DisableIRQ(CAN0_IRQn); + NVIC_ClearPendingIRQ(CAN0_IRQn); + NVIC_EnableIRQ(CAN0_IRQn); +#ifdef CAN1_GCLK_ID + } else if (instance == 1) { + NVIC_DisableIRQ(CAN1_IRQn); + NVIC_ClearPendingIRQ(CAN1_IRQn); + NVIC_EnableIRQ(CAN1_IRQn); +#endif + } + + hri_can_write_ILE_reg(self->hw, CAN_ILE_EINT0); + // Prevent configuration change + hri_can_clear_CCCR_CCE_bit(self->hw); + hri_can_clear_CCCR_INIT_bit(self->hw); + while (hri_can_get_CCCR_INIT_bit(self->hw)) { + } + + can_objs[instance] = self; +} + +bool common_hal_canio_can_loopback_get(canio_can_obj_t *self) +{ + return self->loopback; +} + +int common_hal_canio_can_baudrate_get(canio_can_obj_t *self) +{ + return self->baudrate; +} + +int common_hal_canio_can_transmit_error_count_get(canio_can_obj_t *self) +{ + return self->hw->ECR.bit.TEC; +} + +int common_hal_canio_can_receive_error_count_get(canio_can_obj_t *self) +{ + return self->hw->ECR.bit.REC; +} + +int common_hal_canio_can_error_warning_state_count_get(canio_can_obj_t *self) +{ + return self->error_warning_state_count; +} + +int common_hal_canio_can_error_passive_state_count_get(canio_can_obj_t *self) +{ + return self->error_passive_state_count; +} + +int common_hal_canio_can_bus_off_state_count_get(canio_can_obj_t *self) +{ + return self->bus_off_state_count; +} + +canio_bus_state_t common_hal_canio_can_state_get(canio_can_obj_t *self) { + CAN_PSR_Type psr = self->hw->PSR; + if (psr.bit.BO) { + return BUS_STATE_OFF; + } + if (psr.bit.EP) { + return BUS_STATE_ERROR_PASSIVE; + } + if (psr.bit.EW) { + return BUS_STATE_ERROR_WARNING; + } + return BUS_STATE_ERROR_ACTIVE; +} + +void common_hal_canio_can_restart(canio_can_obj_t *self) { + if (!self->hw->PSR.bit.BO) { + return; + } + + hri_can_clear_CCCR_INIT_bit(self->hw); + while (hri_can_get_CCCR_INIT_bit(self->hw)) { + } +} + +bool common_hal_canio_can_auto_restart_get(canio_can_obj_t *self) { + return self->auto_restart; +} + +void common_hal_canio_can_auto_restart_set(canio_can_obj_t *self, bool value) { + self->auto_restart = value; +} + +static void maybe_auto_restart(canio_can_obj_t *self) { + if (self->auto_restart) { + common_hal_canio_can_restart(self); + } +} + +void common_hal_canio_can_send(canio_can_obj_t *self, canio_message_obj_t *message) +{ + maybe_auto_restart(self); + + // We have just one dedicated TX buffer, use it! + canio_can_tx_buffer_t *ent = &self->state->tx_buffer[0]; + + ent->txb0.bit.ESI = false; + ent->txb0.bit.XTD = message->extended; + ent->txb0.bit.RTR = message->rtr; + if (message->extended) { + ent->txb0.bit.ID = message->id; + } else { + ent->txb0.bit.ID = message->id << 18; // short addresses are left-justified + } + + ent->txb1.bit.MM = 0; // "message marker" + ent->txb1.bit.EFC = 0; // don't store fifo events to event queue + ent->txb1.bit.FDF = 0; // Classic CAN format + ent->txb1.bit.BRS = 0; // No bit rate switching + ent->txb1.bit.DLC = message->size; + + if (!message->rtr) { + memcpy(ent->data, message->data, message->size); + } + + // TX buffer add request + self->hw->TXBAR.reg = 1; + + // wait 8ms (hard coded for now) for TX to occur + uint64_t deadline = port_get_raw_ticks(NULL) + 8; + while (port_get_raw_ticks(NULL) < deadline && !(self->hw->TXBTO.reg & 1)) { + RUN_BACKGROUND_TASKS; + } +} + +bool common_hal_canio_can_silent_get(canio_can_obj_t *self) { + return self->silent; +} + +bool common_hal_canio_can_deinited(canio_can_obj_t *self) { + return !self->hw; +} + +void common_hal_canio_can_check_for_deinit(canio_can_obj_t *self) { + if (common_hal_canio_can_deinited(self)) { + raise_deinited_error(); + } +} + +void common_hal_canio_can_deinit(canio_can_obj_t *self) +{ + if (self->hw) { + hri_can_set_CCCR_INIT_bit(self->hw); + self->hw = 0; + } + if (self->rx_pin_number != COMMON_HAL_MCU_NO_PIN) { + reset_pin_number(self->rx_pin_number); + self->rx_pin_number = COMMON_HAL_MCU_NO_PIN; + } + if (self->tx_pin_number != COMMON_HAL_MCU_NO_PIN) { + reset_pin_number(self->tx_pin_number); + self->tx_pin_number = COMMON_HAL_MCU_NO_PIN; + } +} + +void common_hal_canio_reset(void) { + memset(can_state, 0, sizeof(can_state)); + + for (size_t i=0; i<MP_ARRAY_SIZE(can_insts); i++) { + hri_can_set_CCCR_INIT_bit(can_insts[i]); + } + + for (size_t i=0; i<MP_ARRAY_SIZE(can_objs); i++) { + if (can_objs[i]) { + common_hal_canio_can_deinit(can_objs[i]); + can_objs[i] = NULL; + } + } +} + + +STATIC void can_handler(int i) { + canio_can_obj_t *self = can_objs[i]; + (void) self; + + Can *hw = can_insts[i]; + uint32_t ir = hri_can_read_IR_reg(hw); + + /* Count up errors*/ + if (ir & CAN_IE_EWE) { + self->error_warning_state_count += 1; + } + if (ir & CAN_IE_EPE) { + self->error_passive_state_count += 1; + } + if (ir & CAN_IE_BOE) { + self->bus_off_state_count += 1; + } + + /* Acknowledge interrupt */ + hri_can_write_IR_reg(hw, ir); +} + +__attribute__((used)) +void CAN0_Handler(void) { + can_handler(0); +} + +#ifdef CAN1_GCLK_ID +__attribute__((used)) +void CAN1_Handler(void) { + can_handler(1); +} +#endif diff --git a/ports/atmel-samd/common-hal/canio/CAN.h b/ports/atmel-samd/common-hal/canio/CAN.h new file mode 100644 index 000000000..cdea60e7d --- /dev/null +++ b/ports/atmel-samd/common-hal/canio/CAN.h @@ -0,0 +1,55 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jeff Epler for Adafruit Industries + * + * 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. + */ + +#pragma once + +#include "py/obj.h" +#include "shared-bindings/canio/__init__.h" +#include "shared-bindings/canio/CAN.h" +#include "component/can.h" +#include "common-hal/microcontroller/Pin.h" +#include "common-hal/canio/__init__.h" +#include "shared-module/canio/Message.h" + +#define COMMON_HAL_CAN_RX_FIFO_LEN (2) +#define COMMON_HAL_CAN_TX_FIFO_LEN (2) + +typedef struct canio_can_obj { + mp_obj_base_t base; + Can *hw; + canio_can_state_t *state; + volatile uint32_t error_warning_state_count; + volatile uint32_t error_passive_state_count; + volatile uint32_t bus_off_state_count; + int baudrate; + uint8_t rx_pin_number:8; + uint8_t tx_pin_number:8; + bool loopback:1; + bool silent:1; + bool auto_restart:1; + bool fifo0_in_use:1; + bool fifo1_in_use:1; +} canio_can_obj_t; diff --git a/ports/atmel-samd/common-hal/canio/Listener.c b/ports/atmel-samd/common-hal/canio/Listener.c new file mode 100644 index 000000000..02cfde9cc --- /dev/null +++ b/ports/atmel-samd/common-hal/canio/Listener.c @@ -0,0 +1,391 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jeff Epler for Adafruit Industries + * + * 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 <math.h> +#include <string.h> + +#include "py/obj.h" +#include "py/runtime.h" + +#include "common-hal/canio/__init__.h" +#include "common-hal/canio/Listener.h" +#include "shared-bindings/util.h" +#include "supervisor/shared/tick.h" +#include "component/can.h" + +STATIC void allow_config_change(canio_can_obj_t *can) { + can->hw->CCCR.bit.INIT = 1; + while (!can->hw->CCCR.bit.INIT) { + } + can->hw->CCCR.bit.CCE = 1; +} + +STATIC void prevent_config_change(canio_can_obj_t *can) { + can->hw->CCCR.bit.CCE = 0; + can->hw->CCCR.bit.INIT = 0; + while (can->hw->CCCR.bit.INIT) { + } +} + +__attribute__((unused)) +STATIC void static_assertions(void) { + MP_STATIC_ASSERT(CAN_GFC_ANFE_RXF0_Val + 1 == CAN_GFC_ANFE_RXF1_Val); + MP_STATIC_ASSERT(CAN_GFC_ANFS_RXF0_Val + 1 == CAN_GFC_ANFS_RXF1_Val); + MP_STATIC_ASSERT(CAN_SIDFE_0_SFEC_STF0M_Val + 1 == CAN_SIDFE_0_SFEC_STF1M_Val); + MP_STATIC_ASSERT(CAN_XIDFE_0_EFEC_STF0M_Val + 1 == CAN_XIDFE_0_EFEC_STF1M_Val); +} + +STATIC bool single_address_filter(canio_match_obj_t *match) { + return match->mask == 0 || match->mask == match->address; +} + +STATIC bool standard_filter_in_use(CanMramSidfe *filter) { + return filter->SIDFE_0.bit.SFEC != CAN_SIDFE_0_SFEC_DISABLE_Val; +} + +STATIC bool extended_filter_in_use(CanMramXidfe *filter) { + return filter->XIDFE_0.bit.EFEC != CAN_XIDFE_0_EFEC_DISABLE_Val; +} + +STATIC size_t num_filters_needed(size_t nmatch, canio_match_obj_t **matches, bool extended) { + size_t num_half_filters_needed = 1; + for(size_t i=0; i<nmatch; i++) { + if (extended != matches[i]->extended) { + continue; + } + if (single_address_filter(matches[i])) { + num_half_filters_needed += 1; + } else { + num_half_filters_needed += 2; + } + } + return num_half_filters_needed / 2; +} + +STATIC size_t num_filters_available(canio_can_obj_t *can, bool extended) { + size_t available = 0; + if (extended) { + for(size_t i = 0; i < MP_ARRAY_SIZE(can->state->extended_rx_filter); i++) { + if (!extended_filter_in_use(&can->state->extended_rx_filter[i])) { + available++; + } + } + } else { + for(size_t i = 0; i < MP_ARRAY_SIZE(can->state->standard_rx_filter); i++) { + if (!standard_filter_in_use(&can->state->standard_rx_filter[i])) { + available++; + } + } + } + return available; +} + +STATIC void clear_filters(canio_listener_obj_t *self) { + canio_can_obj_t *can = self->can; + int fifo = self->fifo_idx; + + // If it was a global accept, clear it + allow_config_change(can); + if (can->hw->GFC.bit.ANFS == CAN_GFC_ANFS_RXF0 + fifo) { + can->hw->GFC.bit.ANFS = CAN_GFC_ANFS_REJECT_Val; + } + if (can->hw->GFC.bit.ANFE == CAN_GFC_ANFE_RXF0 + fifo) { + can->hw->GFC.bit.ANFE = CAN_GFC_ANFE_REJECT_Val; + } + prevent_config_change(can); + + // For each filter entry, if it pointed at this FIFO set it to DISABLE + for(size_t i = 0; i < MP_ARRAY_SIZE(can->state->extended_rx_filter); i++) { + int val = CAN_XIDFE_0_EFEC_STF0M_Val + fifo; + if (can->state->extended_rx_filter[i].XIDFE_0.bit.EFEC == val) { + can->state->extended_rx_filter[i].XIDFE_0.bit.EFEC = CAN_XIDFE_0_EFEC_DISABLE_Val; + } + } + for(size_t i = 0; i < MP_ARRAY_SIZE(can->state->standard_rx_filter); i++) { + int val = CAN_SIDFE_0_SFEC_STF1M_Val + fifo; + if (can->state->standard_rx_filter[i].SIDFE_0.bit.SFEC == val) { + can->state->standard_rx_filter[i].SIDFE_0.bit.SFEC = CAN_SIDFE_0_SFEC_DISABLE_Val; + } + } +} + +STATIC CanMramXidfe *next_extended_filter(canio_listener_obj_t *self, CanMramXidfe *start) { + CanMramXidfe *end = &self->can->state->extended_rx_filter[MP_ARRAY_SIZE(self->can->state->extended_rx_filter)]; + if (start == NULL) { + start = self->can->state->extended_rx_filter; + } else { + start = start + 1; + } + while (extended_filter_in_use(start)) { + if (start == end) { + return NULL; + } + start = start + 1; + } + return start; +} + +STATIC CanMramSidfe *next_standard_filter(canio_listener_obj_t *self, CanMramSidfe *start) { + CanMramSidfe *end = &self->can->state->standard_rx_filter[MP_ARRAY_SIZE(self->can->state->standard_rx_filter)]; + if (start == NULL) { + start = self->can->state->standard_rx_filter; + } else { + start = start + 1; + } + while (standard_filter_in_use(start)) { + if (start == end) { + return NULL; + } + start = start + 1; + } + return start; +} + +STATIC void install_standard_filter(CanMramSidfe *standard, int id1, int id2, int sfec, int sft) { + assert(standard); + CAN_SIDFE_0_Type val = { + .bit.SFID1 = id1, + .bit.SFID2 = id2, + .bit.SFEC = sfec, + .bit.SFT = sft, + }; + standard->SIDFE_0 = val; +} + +STATIC void install_extended_filter(CanMramXidfe *extended, int id1, int id2, int efec, int eft) { + assert(extended); + CAN_XIDFE_0_Type val0 = { + .bit.EFID1 = id1, + .bit.EFEC = efec, + }; + CAN_XIDFE_1_Type val1 = { + .bit.EFID2 = id2, + .bit.EFT = eft, + }; + // Set entry 0 second, because it has the enable bits (XIDFE_0_EFEC) + extended->XIDFE_1 = val1; + extended->XIDFE_0 = val0; +} + + +#define NO_ADDRESS (-1) +void set_filters(canio_listener_obj_t *self, size_t nmatch, canio_match_obj_t **matches) { + int fifo = self->fifo_idx; + + if (!nmatch) { + allow_config_change(self->can); + self->can->hw->GFC.bit.ANFS = CAN_GFC_ANFS_RXF0_Val + fifo; + self->can->hw->GFC.bit.ANFE = CAN_GFC_ANFE_RXF0_Val + fifo; + self->can->hw->CCCR.bit.CCE = 0; + prevent_config_change(self->can); + return; + } + + CanMramSidfe *standard = next_standard_filter(self, NULL); + CanMramXidfe *extended = next_extended_filter(self, NULL); + + int first_address = NO_ADDRESS; + + // step 1: single address standard matches + // we have to gather up pairs and stuff them in a single filter entry + for(size_t i = 0; i<nmatch; i++) { + canio_match_obj_t *match = matches[i]; + if (match->extended) { + continue; + } + if (!single_address_filter(match)) { + continue; + } + if (first_address != NO_ADDRESS) { + install_standard_filter(standard, first_address, match->address, CAN_SIDFE_0_SFEC_STF0M_Val + fifo, CAN_SIDFE_0_SFT_DUAL_Val); + first_address = NO_ADDRESS; + standard = next_standard_filter(self, standard); + } else { + first_address = match->address; + } + } + // step 1.5. odd single address standard match + if (first_address != NO_ADDRESS) { + install_standard_filter(standard, first_address, first_address, CAN_SIDFE_0_SFEC_STF0M_Val + fifo, CAN_SIDFE_0_SFT_DUAL_Val); + standard = next_standard_filter(self, standard); + first_address = NO_ADDRESS; + } + + // step 2: standard mask filter + for(size_t i = 0; i<nmatch; i++) { + canio_match_obj_t *match = matches[i]; + if (match->extended) { + continue; + } + if (single_address_filter(match)) { + continue; + } + install_standard_filter(standard, match->address, match->mask, CAN_SIDFE_0_SFEC_STF0M_Val + fifo, CAN_SIDFE_0_SFT_CLASSIC_Val); + standard = next_standard_filter(self, standard); + } + + // step 3: single address extended matches + // we have to gather up pairs and stuff them in a single filter entry + for(size_t i = 0; i<nmatch; i++) { + canio_match_obj_t *match = matches[i]; + if (!match->extended) { + continue; + } + if (!single_address_filter(match)) { + continue; + } + if (first_address != NO_ADDRESS) { + install_extended_filter(extended, first_address, match->address, CAN_XIDFE_0_EFEC_STF0M_Val + fifo, CAN_XIDFE_1_EFT_DUAL_Val); + first_address = NO_ADDRESS; + extended = next_extended_filter(self, extended); + } else { + first_address = match->address; + } + } + // step 3.5. odd single address standard match + if (first_address != NO_ADDRESS) { + install_extended_filter(extended, first_address, first_address, CAN_XIDFE_0_EFEC_STF0M_Val + fifo, CAN_XIDFE_1_EFT_DUAL_Val); + extended = next_extended_filter(self, extended); + first_address = NO_ADDRESS; + } + + // step 4: extended mask filters + for(size_t i = 0; i<nmatch; i++) { + canio_match_obj_t *match = matches[i]; + if (!match->extended) { + continue; + } + if (single_address_filter(match)) { + continue; + } + install_extended_filter(extended, match->address, match->mask, CAN_XIDFE_0_EFEC_STF0M_Val + fifo, CAN_XIDFE_1_EFT_CLASSIC_Val); + extended = next_extended_filter(self, extended); + } + + // phew, easy(!) +} + + +void common_hal_canio_listener_construct(canio_listener_obj_t *self, canio_can_obj_t *can, size_t nmatch, canio_match_obj_t **matches, float timeout) { + if (!can->fifo0_in_use) { + self->fifo_idx = 0; + self->fifo = can->state->rx0_fifo; + self->hw = (canio_rxfifo_reg_t*)&can->hw->RXF0C; + can->hw->IR.reg = CAN_IR_RF0N | CAN_IR_RF0W | CAN_IR_RF0F | CAN_IR_RF0L; + can->fifo0_in_use = true; + } else if (!can->fifo1_in_use) { + self->fifo_idx = 1; + self->fifo = can->state->rx1_fifo; + self->hw = (canio_rxfifo_reg_t*)&can->hw->RXF1C; + can->fifo1_in_use = true; + can->hw->IR.reg = CAN_IR_RF1N | CAN_IR_RF1W | CAN_IR_RF1F | CAN_IR_RF1L; + } else { + mp_raise_ValueError(translate("All RX FIFOs in use")); + } + + if (!nmatch) { + if (can->hw->GFC.bit.ANFS == CAN_GFC_ANFS_RXF1_Val - self->fifo_idx) { + mp_raise_ValueError(translate("Already have all-matches listener")); + } + if (can->hw->GFC.bit.ANFE == CAN_GFC_ANFE_RXF1_Val - self->fifo_idx) { + mp_raise_ValueError(translate("Already have all-matches listener")); + } + } + + if (num_filters_needed(nmatch, matches, false) > num_filters_available(can, false)) { + mp_raise_ValueError(translate("Filters too complex")); + } + + if (num_filters_needed(nmatch, matches, true) > num_filters_available(can, true)) { + mp_raise_ValueError(translate("Filters too complex")); + } + + // Nothing can fail now so it's safe to assign self->can + self->can = can; + set_filters(self, nmatch, matches); + common_hal_canio_listener_set_timeout(self, timeout); +} + +void common_hal_canio_listener_set_timeout(canio_listener_obj_t *self, float timeout) { + self->timeout_ms = (int)MICROPY_FLOAT_C_FUN(ceil)(timeout * 1000); +} + +float common_hal_canio_listener_get_timeout(canio_listener_obj_t *self) { + return self->timeout_ms / 1000.0f; +} + +void common_hal_canio_listener_check_for_deinit(canio_listener_obj_t *self) { + if (!self->can) { + raise_deinited_error(); + } + common_hal_canio_can_check_for_deinit(self->can); +} + +int common_hal_canio_listener_in_waiting(canio_listener_obj_t *self) { + return self->hw->RXFS.bit.F0FL; +} + +bool common_hal_canio_listener_readinto(canio_listener_obj_t *self, canio_message_obj_t *message) { + if (!common_hal_canio_listener_in_waiting(self)) { + uint64_t deadline = supervisor_ticks_ms64() + self->timeout_ms; + do { + if (supervisor_ticks_ms64() > deadline) { + return false; + } + } while (!common_hal_canio_listener_in_waiting(self)); + } + int index = self->hw->RXFS.bit.F0GI; + canio_can_rx_fifo_t *hw_message = &self->fifo[index]; + message->extended = hw_message->rxf0.bit.XTD; + if (message->extended) { + message->id = hw_message->rxf0.bit.ID; + } else { + message->id = hw_message->rxf0.bit.ID >> 18; // short addresses are left-justified + } + message->rtr = hw_message->rxf0.bit.RTR; + message->size = hw_message->rxf1.bit.DLC; + if (!message->rtr) { + memcpy(message->data, hw_message->data, message->size); + } + self->hw->RXFA.bit.F0AI = index; + return true; +} + +void common_hal_canio_listener_deinit(canio_listener_obj_t *self) { + if (self->can) { + clear_filters(self); + if (self->fifo_idx == 0) { + self->can->fifo0_in_use = false; + } + if (self->fifo_idx == 1) { + self->can->fifo1_in_use = false; + } + } + self->fifo_idx = -1; + self->fifo = NULL; + self->can = NULL; + self->hw = NULL; +} diff --git a/ports/atmel-samd/common-hal/canio/Listener.h b/ports/atmel-samd/common-hal/canio/Listener.h new file mode 100644 index 000000000..1b81d82aa --- /dev/null +++ b/ports/atmel-samd/common-hal/canio/Listener.h @@ -0,0 +1,53 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jeff Epler for Adafruit Industries + * + * 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. + */ + +#pragma once + +#include "common-hal/canio/CAN.h" +#include "shared-module/canio/Match.h" + +typedef struct { + __IO CAN_RXF0C_Type RXFC; /**< \brief (R/W 32) Rx FIFO n Configuration */ + __I CAN_RXF0S_Type RXFS; /**< \brief (R/ 32) Rx FIFO n Status */ + __IO CAN_RXF0A_Type RXFA; /**< \brief (R/W 32) Rx FIFO n Acknowledge */ +} canio_rxfifo_reg_t; + +typedef struct { + mp_obj_base_t base; + canio_can_obj_t *can; + canio_can_rx_fifo_t *fifo; + canio_rxfifo_reg_t *hw; + uint32_t timeout_ms; + uint8_t fifo_idx; +} canio_listener_obj_t; + +void common_hal_canio_listener_construct(canio_listener_obj_t *self, canio_can_obj_t *can, size_t nmatch, canio_match_obj_t **matches, float timeout); +void common_hal_canio_listener_check_for_deinit(canio_listener_obj_t *self); +void common_hal_canio_listener_deinit(canio_listener_obj_t *self); +bool common_hal_canio_listener_readinto(canio_listener_obj_t *self, canio_message_obj_t *message); +int common_hal_canio_listener_in_waiting(canio_listener_obj_t *self); +float common_hal_canio_listener_get_timeout(canio_listener_obj_t *self); +void common_hal_canio_listener_set_timeout(canio_listener_obj_t *self, float timeout); diff --git a/ports/atmel-samd/common-hal/canio/__init__.c b/ports/atmel-samd/common-hal/canio/__init__.c new file mode 100644 index 000000000..7932bfc2d --- /dev/null +++ b/ports/atmel-samd/common-hal/canio/__init__.c @@ -0,0 +1,25 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jeff Epler for Adafruit Industries + * + * 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. + */ diff --git a/ports/atmel-samd/common-hal/canio/__init__.h b/ports/atmel-samd/common-hal/canio/__init__.h new file mode 100644 index 000000000..32adc5bf9 --- /dev/null +++ b/ports/atmel-samd/common-hal/canio/__init__.h @@ -0,0 +1,66 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jeff Epler for Adafruit Industries + * + * 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. + */ + +#pragma once + +#include "hal/utils/include/utils.h" +#include "component/can.h" + +#define COMMON_HAL_CANIO_MAX_MESSAGE_LENGTH (8) +#define COMMON_HAL_CANIO_RX_FIFO_SIZE (3) +#define COMMON_HAL_CANIO_RX_FILTER_SIZE (4) +#define COMMON_HAL_CANIO_TX_FIFO_SIZE (1) + +// This appears to be a typo (transposition error) in the ASF4 headers +// It's called the "Extended ID Filter Entry" +typedef CanMramXifde CanMramXidfe; + +typedef struct canio_listener canio_listener_t; +typedef struct canio_can canio_can_t; + +typedef struct { + CAN_TXBE_0_Type txb0; + CAN_TXBE_1_Type txb1; + COMPILER_ALIGNED(4) + uint8_t data[COMMON_HAL_CANIO_MAX_MESSAGE_LENGTH]; +} canio_can_tx_buffer_t; + +typedef struct { + CAN_RXF0E_0_Type rxf0; + CAN_RXF0E_1_Type rxf1; + COMPILER_ALIGNED(4) + uint8_t data[COMMON_HAL_CANIO_MAX_MESSAGE_LENGTH]; +} canio_can_rx_fifo_t; + +typedef uint32_t canio_can_filter_t; + +typedef struct { + canio_can_tx_buffer_t tx_buffer[COMMON_HAL_CANIO_TX_FIFO_SIZE]; + canio_can_rx_fifo_t rx0_fifo[COMMON_HAL_CANIO_RX_FIFO_SIZE]; + canio_can_rx_fifo_t rx1_fifo[COMMON_HAL_CANIO_RX_FIFO_SIZE]; + CanMramSidfe standard_rx_filter[COMMON_HAL_CANIO_RX_FILTER_SIZE]; + CanMramXifde extended_rx_filter[COMMON_HAL_CANIO_RX_FILTER_SIZE]; +} canio_can_state_t; diff --git a/ports/atmel-samd/common-hal/microcontroller/Pin.c b/ports/atmel-samd/common-hal/microcontroller/Pin.c index ef1d8fffa..bec9dc1e2 100644 --- a/ports/atmel-samd/common-hal/microcontroller/Pin.c +++ b/ports/atmel-samd/common-hal/microcontroller/Pin.c @@ -24,6 +24,8 @@ * THE SOFTWARE. */ +#include "py/runtime.h" + #include "shared-bindings/microcontroller/Pin.h" #include "atmel_start_pins.h" @@ -256,3 +258,19 @@ void common_hal_mcu_pin_claim(const mcu_pin_obj_t* pin) { void common_hal_mcu_pin_reset_number(uint8_t pin_no) { reset_pin_number(pin_no); } + +mcu_pin_function_t *mcu_find_pin_function(mcu_pin_function_t *table, const mcu_pin_obj_t *pin, int instance, uint16_t name) { + if (!pin) { + return NULL; + } + + for(; table->obj; table++) { + if (instance != -1 && instance != table->instance) { + continue; + } + if (pin == table->obj) { + return table; + } + } + mp_raise_ValueError_varg(translate("%q pin invalid"), name); +} diff --git a/ports/atmel-samd/common-hal/microcontroller/Pin.h b/ports/atmel-samd/common-hal/microcontroller/Pin.h index 14887207a..59302713a 100644 --- a/ports/atmel-samd/common-hal/microcontroller/Pin.h +++ b/ports/atmel-samd/common-hal/microcontroller/Pin.h @@ -47,4 +47,13 @@ void never_reset_pin_number(uint8_t pin_number); void claim_pin(const mcu_pin_obj_t* pin); bool pin_number_is_free(uint8_t pin_number); +typedef struct { + const mcu_pin_obj_t *obj; + uint8_t instance; + uint8_t pin; + uint16_t function; +} mcu_pin_function_t; + +mcu_pin_function_t *mcu_find_pin_function(mcu_pin_function_t *table, const mcu_pin_obj_t *pin, int instance, uint16_t name); + #endif // MICROPY_INCLUDED_ATMEL_SAMD_COMMON_HAL_MICROCONTROLLER_PIN_H diff --git a/ports/atmel-samd/common-hal/sdioio/SDCard.c b/ports/atmel-samd/common-hal/sdioio/SDCard.c index 1f555b647..4d2539aa5 100644 --- a/ports/atmel-samd/common-hal/sdioio/SDCard.c +++ b/ports/atmel-samd/common-hal/sdioio/SDCard.c @@ -58,24 +58,6 @@ static Sdhc *sdhc_insts[] = SDHC_INSTS; -STATIC pin_function_t *find_pin_function(pin_function_t *table, const mcu_pin_obj_t *pin, int instance, uint16_t name) { - DEBUG_PRINT("\n\n[inst=% 2d] %q: ", instance, name); - DEBUG_PRINT_OBJ_NL(pin); - - for(; table->obj; table++) { - DEBUG_PRINT("[inst=% 2d] considering table @%p: "); - DEBUG_PRINT_OBJ(table->obj); - DEBUG_PRINT(" %d %d\n", table->instance, table->pin); - if (instance != -1 && instance != table->instance) { - continue; - } - if (pin == table->obj) { - return table; - } - } - mp_raise_ValueError_varg(translate("%q pin invalid"), name); -} - void common_hal_sdioio_sdcard_construct(sdioio_sdcard_obj_t *self, const mcu_pin_obj_t * clock, const mcu_pin_obj_t * command, uint8_t num_data, mcu_pin_obj_t ** data, uint32_t frequency) { @@ -98,15 +80,15 @@ CLK PA21 PCC_D? (D32) BROWN */ - pin_function_t *functions[6] = {}; - functions[0] = find_pin_function(sdio_cmd, command, -1, MP_QSTR_command); + mcu_pin_function_t *functions[6] = {}; + functions[0] = mcu_find_pin_function(sdio_cmd, command, -1, MP_QSTR_command); int instance = functions[0]->instance; - functions[1] = find_pin_function(sdio_ck, clock, instance, MP_QSTR_clock); - functions[2] = find_pin_function(sdio_dat0, data[0], instance, MP_QSTR_data0); + functions[1] = mcu_find_pin_function(sdio_ck, clock, instance, MP_QSTR_clock); + functions[2] = mcu_find_pin_function(sdio_dat0, data[0], instance, MP_QSTR_data0); if(num_data == 4) { - functions[3] = find_pin_function(sdio_dat1, data[1], instance, MP_QSTR_data1); - functions[4] = find_pin_function(sdio_dat2, data[2], instance, MP_QSTR_data2); - functions[5] = find_pin_function(sdio_dat3, data[3], instance, MP_QSTR_data3); + functions[3] = mcu_find_pin_function(sdio_dat1, data[1], instance, MP_QSTR_data1); + functions[4] = mcu_find_pin_function(sdio_dat2, data[2], instance, MP_QSTR_data2); + functions[5] = mcu_find_pin_function(sdio_dat3, data[3], instance, MP_QSTR_data3); } // We've verified all pins, now set their special functions @@ -114,7 +96,7 @@ CLK PA21 PCC_D? (D32) BROWN self->clock_pin = common_hal_mcu_pin_number(functions[1]->obj); for(int i=0; i<num_data; i++) { - pin_function_t *function = functions[2+i]; + mcu_pin_function_t *function = functions[2+i]; if (function) { self->data_pins[i] = common_hal_mcu_pin_number(function->obj); } else { diff --git a/ports/atmel-samd/tools/mkcandata.py b/ports/atmel-samd/tools/mkcandata.py new file mode 100755 index 000000000..9668d2208 --- /dev/null +++ b/ports/atmel-samd/tools/mkcandata.py @@ -0,0 +1,31 @@ +#!/usr/bin/python3 + +def defines(name, suffix): + print(f'mcu_pin_function_t {name} [] = {{') + for instance in (0, 1): + for function in 'HI': + for port in 'ABCD': + for idx in range(32): + pin = f'P{port}{idx:02d}' + pinmux = f'PINMUX_{pin}{function}_CAN{instance}_{suffix}' + print(f'''\ +#if defined({pinmux}) && ! defined(IGNORE_PIN_{pin}) + {{&pin_{pin}, {instance}, PIN_{pin}, {pinmux} & 0xffff}}, +#endif''') + print(f'{{NULL, 0, 0}}') + print(f'}};') + print() + +print('''\ +#include <stdint.h> +#include "py/obj.h" +#include "sam.h" +#include "samd/pins.h" +#include "mpconfigport.h" +#include "atmel_start_pins.h" +#include "hal/include/hal_gpio.h" +#include "common-hal/microcontroller/Pin.h" +''') + +defines('can_rx', 'RX') +defines('can_tx', 'TX') diff --git a/ports/atmel-samd/tools/mksdiodata.py b/ports/atmel-samd/tools/mksdiodata.py index 0cce3819f..48c4a085f 100755 --- a/ports/atmel-samd/tools/mksdiodata.py +++ b/ports/atmel-samd/tools/mksdiodata.py @@ -1,7 +1,7 @@ #!/usr/bin/python3 def defines(name, function): - print(f'pin_function_t {name} [] = {{') + print(f'mcu_pin_function_t {name} [] = {{') for instance in (0, 1): for port in 'ABCD': for idx in range(32): @@ -23,13 +23,8 @@ print('''\ #include "mpconfigport.h" #include "atmel_start_pins.h" #include "hal/include/hal_gpio.h" +#include "common-hal/microcontroller/Pin.h" -typedef struct { - const mcu_pin_obj_t *obj; - uint8_t instance; - uint8_t pin; - uint16_t function; -} pin_function_t; ''') defines('sdio_ck', 'SDCK') diff --git a/py/circuitpy_defns.mk b/py/circuitpy_defns.mk index 6e98af868..98fa7a5f2 100644 --- a/py/circuitpy_defns.mk +++ b/py/circuitpy_defns.mk @@ -142,6 +142,9 @@ endif ifeq ($(CIRCUITPY_CAMERA),1) SRC_PATTERNS += camera/% endif +ifeq ($(CIRCUITPY_CANIO),1) +SRC_PATTERNS += canio/% +endif ifeq ($(CIRCUITPY_COUNTIO),1) SRC_PATTERNS += countio/% endif @@ -315,6 +318,9 @@ SRC_COMMON_HAL_ALL = \ busio/__init__.c \ camera/__init__.c \ camera/Camera.c \ + canio/CAN.c \ + canio/Listener.c \ + canio/__init__.c \ countio/Counter.c \ countio/__init__.c \ digitalio/DigitalInOut.c \ @@ -384,6 +390,7 @@ $(filter $(SRC_PATTERNS), \ _bleio/Address.c \ _bleio/Attribute.c \ _bleio/ScanEntry.c \ + canio/Match.c \ _eve/__init__.c \ camera/ImageFormat.c \ digitalio/Direction.c \ @@ -402,6 +409,8 @@ SRC_SHARED_MODULE_ALL = \ _bleio/Attribute.c \ _bleio/ScanEntry.c \ _bleio/ScanResults.c \ + canio/Match.c \ + canio/Message.c \ _eve/__init__.c \ _pixelbuf/PixelBuf.c \ _pixelbuf/__init__.c \ diff --git a/py/circuitpy_mpconfig.h b/py/circuitpy_mpconfig.h index 4272de2ec..0583ae1c9 100644 --- a/py/circuitpy_mpconfig.h +++ b/py/circuitpy_mpconfig.h @@ -336,6 +336,13 @@ extern const struct _mp_obj_module_t camera_module; #define CAMERA_MODULE #endif +#if CIRCUITPY_CANIO +extern const struct _mp_obj_module_t canio_module; +#define CANIO_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR_canio), (mp_obj_t)&canio_module }, +#else +#define CANIO_MODULE +#endif + #if CIRCUITPY_COUNTIO extern const struct _mp_obj_module_t countio_module; #define COUNTIO_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR_countio), (mp_obj_t)&countio_module }, @@ -766,6 +773,7 @@ extern const struct _mp_obj_module_t wifi_module; BOARD_MODULE \ BUSIO_MODULE \ CAMERA_MODULE \ + CANIO_MODULE \ COUNTIO_MODULE \ DIGITALIO_MODULE \ DISPLAYIO_MODULE \ diff --git a/py/circuitpy_mpconfig.mk b/py/circuitpy_mpconfig.mk index 54bdefc6d..9b9bd83b4 100644 --- a/py/circuitpy_mpconfig.mk +++ b/py/circuitpy_mpconfig.mk @@ -93,6 +93,9 @@ CFLAGS += -DCIRCUITPY_BUSIO=$(CIRCUITPY_BUSIO) CIRCUITPY_CAMERA ?= 0 CFLAGS += -DCIRCUITPY_CAMERA=$(CIRCUITPY_CAMERA) +CIRCUITPY_CANIO ?= 0 +CFLAGS += -DCIRCUITPY_CANIO=$(CIRCUITPY_CANIO) + CIRCUITPY_DIGITALIO ?= 1 CFLAGS += -DCIRCUITPY_DIGITALIO=$(CIRCUITPY_DIGITALIO) diff --git a/py/enum.c b/py/enum.c new file mode 100644 index 000000000..02a85a168 --- /dev/null +++ b/py/enum.c @@ -0,0 +1,52 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * SPDX-FileCopyrightText: Copyright (c) 2020 Jeff Epler for Adafruit Industries + * + * 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 "py/enum.h" +#include "py/runtime.h" + +mp_obj_t cp_enum_find(const mp_obj_type_t *type, int value) { + const mp_obj_dict_t *dict = type->locals_dict; + for (size_t i=0; i<dict->map.used; i++) { + const cp_enum_obj_t *v = dict->map.table[i].value; + if (v->value == value) { + return (mp_obj_t)v; + } + } + return mp_const_none; +} + +int cp_enum_value(const mp_obj_type_t *type, mp_obj_t *obj) { + if (!MP_OBJ_IS_TYPE(obj, type)) { + mp_raise_TypeError_varg(translate("Expected a %q"), type->name); + } + return ((cp_enum_obj_t*)MP_OBJ_TO_PTR(obj))->value; +} + +void cp_enum_obj_print_helper(uint16_t module, const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + (void) kind; + cp_enum_obj_t *self = self_in; + mp_printf(print, "%q.%q.%q", module, self->base.type->name, self->name); +} diff --git a/py/enum.h b/py/enum.h new file mode 100644 index 000000000..1c38ae5ae --- /dev/null +++ b/py/enum.h @@ -0,0 +1,65 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * SPDX-FileCopyrightText: Copyright (c) 2020 Jeff Epler for Adafruit Industries + * + * 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. + */ + +#pragma once + +#include "py/obj.h" + +typedef struct { + mp_obj_base_t base; + int16_t value; + int16_t name; +} cp_enum_obj_t; + +#define MAKE_ENUM_VALUE(type, prefix, name, value) \ + STATIC const cp_enum_obj_t prefix ## _ ## name ## _obj = { \ + { &type }, value, MP_QSTR_ ## name, \ + } + +#define MAKE_ENUM_MAP(name) \ + STATIC const mp_rom_map_elem_t name ## _locals_table[] = + +#define MAKE_ENUM_MAP_ENTRY(prefix, name) \ + { MP_ROM_QSTR(MP_QSTR_ ## name), MP_ROM_PTR(&prefix ## _ ## name ## _obj) } + +#define MAKE_PRINTER(module, typename) \ + STATIC void typename ## _ ## print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { \ + cp_enum_obj_print_helper(MP_QSTR_ ## module, print, self_in, kind); \ + } + +#define MAKE_ENUM_TYPE(module, type, typename) \ + const mp_obj_type_t typename ## _type = { \ + { &mp_type_type }, \ + .name = MP_QSTR_ ## type, \ + .print = typename ## _print, \ + .locals_dict = (mp_obj_t)&typename ## _locals_dict, \ + } + + + +mp_obj_t cp_enum_find(const mp_obj_type_t *type, int value); +int cp_enum_value(const mp_obj_type_t *type, mp_obj_t *obj); +void cp_enum_obj_print_helper(uint16_t module, const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind); @@ -181,6 +181,7 @@ PY_CORE_O_BASENAME = $(addprefix py/,\ argcheck.o \ warning.o \ map.o \ + enum.o \ obj.o \ objarray.o \ objattrtuple.o \ diff --git a/shared-bindings/canio/CAN.c b/shared-bindings/canio/CAN.c new file mode 100644 index 000000000..ff27bb204 --- /dev/null +++ b/shared-bindings/canio/CAN.c @@ -0,0 +1,422 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jeff Epler for Adafruit Industries + * + * 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 "py/enum.h" +#include "common-hal/canio/CAN.h" +#include "common-hal/canio/Listener.h" +#include "shared-bindings/canio/__init__.h" +#include "shared-bindings/canio/CAN.h" +#include "shared-bindings/canio/Listener.h" +#include "shared-bindings/canio/Match.h" +#include "shared-bindings/canio/Message.h" +#include "shared-bindings/microcontroller/Pin.h" + +#include "py/objproperty.h" +#include "py/runtime.h" + +//| +//| class CAN: +//| """CAN bus protocol""" +//| +//| def __init__(self, +//| tx: microcontroller.Pin, +//| rx: microcontroller.Pin, +//| *, +//| baudrate: int = 250000, +//| loopback: bool = False, +//| silent: bool = False, +//| auto_restart: bool = False, +//| ): +//| """A common shared-bus protocol. The rx and tx pins are generally +//| connected to a transceiver which controls the H and L pins on a +//| shared bus. +//| +//| :param ~microcontroller.Pin rx: the pin to receive with +//| :param ~microcontroller.Pin tx: the pin to transmit with +//| :param int baudrate: The bit rate of the bus in Hz. All devices on the bus must agree on this value. +//| :param bool loopback: When True the ``rx`` pin's value is ignored, and the device receives the packets it sends. +//| :param bool silent: When True the ``tx`` pin is always driven to the high logic level. This mode can be used to "sniff" a CAN bus without interfering. +//| :param bool auto_restart: If True, will restart communications after entering bus-off state +//| """ +//| ... +//| +STATIC mp_obj_t canio_can_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_tx, ARG_rx, ARG_baudrate, ARG_loopback, ARG_silent, ARG_auto_restart, NUM_ARGS }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_tx, MP_ARG_OBJ | MP_ARG_REQUIRED }, + { MP_QSTR_rx, MP_ARG_OBJ | MP_ARG_REQUIRED }, + { MP_QSTR_baudrate, MP_ARG_INT, {.u_int = 250000} }, + { MP_QSTR_loopback, MP_ARG_BOOL, {.u_bool = false} }, + { MP_QSTR_silent, MP_ARG_BOOL, {.u_bool = false} }, + { MP_QSTR_auto_restart, MP_ARG_BOOL, {.u_bool = false} }, + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + MP_STATIC_ASSERT( MP_ARRAY_SIZE(allowed_args) == NUM_ARGS ); + + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + mcu_pin_obj_t *rx_pin = validate_obj_is_free_pin_or_none(args[ARG_rx].u_obj); + mcu_pin_obj_t *tx_pin = validate_obj_is_free_pin_or_none(args[ARG_tx].u_obj); + if (!rx_pin && !tx_pin) { + mp_raise_ValueError(translate("tx and rx cannot both be None")); + } + + canio_can_obj_t *self = m_new_obj(canio_can_obj_t); + self->base.type = &canio_can_type; + common_hal_canio_can_construct(self, tx_pin, rx_pin, args[ARG_baudrate].u_int, args[ARG_loopback].u_bool, args[ARG_silent].u_bool); + + common_hal_canio_can_auto_restart_set(self, args[ARG_auto_restart].u_bool); + + return MP_OBJ_FROM_PTR(self); +} + + +//| auto_restart: bool +//| """If True, will restart communications after entering bus-off state""" +//| +STATIC mp_obj_t canio_can_auto_restart_get(mp_obj_t self_in) { + canio_can_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_canio_can_check_for_deinit(self); + return mp_obj_new_bool(common_hal_canio_can_auto_restart_get(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(canio_can_auto_restart_get_obj, canio_can_auto_restart_get); + +STATIC mp_obj_t canio_can_auto_restart_set(mp_obj_t self_in, mp_obj_t flag_in) { + canio_can_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_canio_can_check_for_deinit(self); + common_hal_canio_can_auto_restart_set(self, mp_obj_is_true(flag_in)); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(canio_can_auto_restart_set_obj, canio_can_auto_restart_set); + +STATIC const mp_obj_property_t canio_can_auto_restart_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&canio_can_auto_restart_get_obj, + (mp_obj_t)&canio_can_auto_restart_set_obj, + (mp_obj_t)mp_const_none}, +}; + + +//| baudrate: int +//| """The baud rate (read-only)""" +//| +STATIC mp_obj_t canio_can_baudrate_get(mp_obj_t self_in) { + canio_can_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_canio_can_check_for_deinit(self); + return MP_OBJ_NEW_SMALL_INT(common_hal_canio_can_baudrate_get(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(canio_can_baudrate_get_obj, canio_can_baudrate_get); + +STATIC const mp_obj_property_t canio_can_baudrate_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&canio_can_baudrate_get_obj, + (mp_obj_t)mp_const_none, + (mp_obj_t)mp_const_none}, +}; + +//| transmit_error_count: int +//| """The number of transmit errors (read-only). Increased for a detected transmission error, decreased for successful transmission. Limited to the range from 0 to 255 inclusive. Also called TEC.""" +//| +STATIC mp_obj_t canio_can_transmit_error_count_get(mp_obj_t self_in) { + canio_can_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_canio_can_check_for_deinit(self); + return MP_OBJ_NEW_SMALL_INT(common_hal_canio_can_transmit_error_count_get(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(canio_can_transmit_error_count_get_obj, canio_can_transmit_error_count_get); + +STATIC const mp_obj_property_t canio_can_transmit_error_count_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&canio_can_transmit_error_count_get_obj, + (mp_obj_t)mp_const_none, + (mp_obj_t)mp_const_none}, +}; + +//| receive_error_count: int +//| """The number of receive errors (read-only). Increased for a detected reception error, decreased for successful reception. Limited to the range from 0 to 255 inclusive. Also called REC.""" +//| +STATIC mp_obj_t canio_can_receive_error_count_get(mp_obj_t self_in) { + canio_can_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_canio_can_check_for_deinit(self); + return MP_OBJ_NEW_SMALL_INT(common_hal_canio_can_receive_error_count_get(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(canio_can_receive_error_count_get_obj, canio_can_receive_error_count_get); + +STATIC const mp_obj_property_t canio_can_receive_error_count_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&canio_can_receive_error_count_get_obj, + (mp_obj_t)mp_const_none, + (mp_obj_t)mp_const_none}, +}; + +//| error_warning_state_count: int +//| """The number of times the controller enterted the Error Warning state (read-only). This number wraps around to 0 after an implementation-defined number of errors.""" +//| +STATIC mp_obj_t canio_can_error_warning_state_count_get(mp_obj_t self_in) { + canio_can_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_canio_can_check_for_deinit(self); + return MP_OBJ_NEW_SMALL_INT(common_hal_canio_can_error_warning_state_count_get(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(canio_can_error_warning_state_count_get_obj, canio_can_error_warning_state_count_get); + +STATIC const mp_obj_property_t canio_can_error_warning_state_count_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&canio_can_error_warning_state_count_get_obj, + (mp_obj_t)mp_const_none, + (mp_obj_t)mp_const_none}, +}; + +//| error_passive_state_count: int +//| """The number of times the controller enterted the Error Passive state (read-only). This number wraps around to 0 after an implementation-defined number of errors.""" +//| +STATIC mp_obj_t canio_can_error_passive_state_count_get(mp_obj_t self_in) { + canio_can_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_canio_can_check_for_deinit(self); + return MP_OBJ_NEW_SMALL_INT(common_hal_canio_can_error_passive_state_count_get(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(canio_can_error_passive_state_count_get_obj, canio_can_error_passive_state_count_get); + +STATIC const mp_obj_property_t canio_can_error_passive_state_count_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&canio_can_error_passive_state_count_get_obj, + (mp_obj_t)mp_const_none, + (mp_obj_t)mp_const_none}, +}; + +//| bus_off_state_count: int +//| """The number of times the controller enterted the Bus Off state (read-only). This number wraps around to 0 after an implementation-defined number of errors.""" +//| +STATIC mp_obj_t canio_can_bus_off_state_count_get(mp_obj_t self_in) { + canio_can_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_canio_can_check_for_deinit(self); + return MP_OBJ_NEW_SMALL_INT(common_hal_canio_can_bus_off_state_count_get(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(canio_can_bus_off_state_count_get_obj, canio_can_bus_off_state_count_get); + +STATIC const mp_obj_property_t canio_can_bus_off_state_count_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&canio_can_bus_off_state_count_get_obj, + (mp_obj_t)mp_const_none, + (mp_obj_t)mp_const_none}, +}; + +//| state: State +//| """The current state of the bus.""" +STATIC mp_obj_t canio_can_state_get(mp_obj_t self_in) { + canio_can_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_canio_can_check_for_deinit(self); + return cp_enum_find(&canio_bus_state_type, common_hal_canio_can_state_get(self)); +} + +MP_DEFINE_CONST_FUN_OBJ_1(canio_can_state_get_obj, canio_can_state_get); + +STATIC const mp_obj_property_t canio_can_state_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&canio_can_state_get_obj, + (mp_obj_t)mp_const_none, + (mp_obj_t)mp_const_none}, +}; + + +//| def restart(self) -> None: +//| """If the device is in the bus off state, restart it.""" +//| ... +//| +STATIC mp_obj_t canio_can_restart(mp_obj_t self_in) { + canio_can_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_canio_can_check_for_deinit(self); + common_hal_canio_can_restart(self); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(canio_can_restart_obj, canio_can_restart); + +//| def listen(self, match: Optional[Sequence[Match]]=None, *, timeout: float=10) -> Listener: +//| """Start receiving messages that match any one of the filters. +//| +//| Creating a listener is an expensive operation and can interfere with reception of messages by other listeners. +//| +//| There is an implementation-defined maximum number of listeners and limit to the complexity of the filters. +//| +//| If the hardware cannot support all the requested matches, a ValueError is raised. Note that generally there are some number of hardware filters shared among all fifos. +//| +//| A message can be received by at most one Listener. If more than one listener matches a message, it is undefined which one actually receives it. +//| +//| An empty filter list causes all messages to be accepted. +//| +//| Timeout dictates how long readinto, read and next() will block.""" +//| ... +//| +STATIC mp_obj_t canio_can_listen(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + canio_can_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + common_hal_canio_can_check_for_deinit(self); + + enum { ARG_match, ARG_timeout, NUM_ARGS }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_match, MP_ARG_OBJ, {.u_obj = 0} }, + { MP_QSTR_timeout, MP_ARG_OBJ, {.u_obj = 0} }, + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + MP_STATIC_ASSERT( MP_ARRAY_SIZE(allowed_args) == NUM_ARGS ); + + mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + size_t nmatch = 0; + mp_obj_t *match_objects = NULL; + + if (args[ARG_match].u_obj) { + mp_obj_get_array(args[ARG_match].u_obj, &nmatch, &match_objects); + } + + canio_match_obj_t *matches[nmatch]; + for (size_t i=0; i<nmatch; i++) { + mp_obj_type_t *type = mp_obj_get_type(match_objects[i]); + if (type != &canio_match_type) { + mp_raise_TypeError_varg(translate("expected '%q' but got '%q'"), MP_QSTR_Match, type->name); + } + matches[i] = MP_OBJ_TO_PTR(match_objects[i]); + } + + float timeout = args[ARG_timeout].u_obj ? mp_obj_get_float(args[ARG_timeout].u_obj) : 10.0f; + canio_listener_obj_t *listener = m_new_obj(canio_listener_obj_t); + listener->base.type = &canio_listener_type; + common_hal_canio_listener_construct(listener, self, nmatch, matches, timeout); + return listener; +} +MP_DEFINE_CONST_FUN_OBJ_KW(canio_can_listen_obj, 1, canio_can_listen); + +//| loopback: bool +//| """True if the device was created in loopback mode, False otherwise""" +//| +STATIC mp_obj_t canio_can_loopback_get(mp_obj_t self_in) { + canio_can_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_canio_can_check_for_deinit(self); + return mp_obj_new_bool(common_hal_canio_can_loopback_get(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(canio_can_loopback_get_obj, canio_can_loopback_get); + +STATIC const mp_obj_property_t canio_can_loopback_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&canio_can_loopback_get_obj, + (mp_obj_t)mp_const_none, + (mp_obj_t)mp_const_none}, +}; + + +//| def send(message: Message) -> None: +//| """Send a message on the bus with the given data and id. +//| If the message could not be sent due to a full fifo or a bus error condition, RuntimeError is raised. +//| """ +//| ... +//| +STATIC mp_obj_t canio_can_send(mp_obj_t self_in, mp_obj_t message_in) { + canio_can_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_canio_can_check_for_deinit(self); + mp_obj_type_t *message_type = mp_obj_get_type(message_in); + if (message_type != &canio_message_type) { + mp_raise_TypeError_varg(translate("expected '%q' but got '%q'"), MP_QSTR_Message, message_type->name); + } + + canio_message_obj_t *message = message_in; + common_hal_canio_can_send(self, message); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(canio_can_send_obj, canio_can_send); + +//| silent: bool +//| """True if the device was created in silent mode, False otherwise""" +//| +STATIC mp_obj_t canio_can_silent_get(mp_obj_t self_in) { + canio_can_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_canio_can_check_for_deinit(self); + return mp_obj_new_bool(common_hal_canio_can_silent_get(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(canio_can_silent_get_obj, canio_can_silent_get); + +STATIC const mp_obj_property_t canio_can_silent_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&canio_can_silent_get_obj, + (mp_obj_t)mp_const_none, + (mp_obj_t)mp_const_none}, +}; + + +//| def deinit(self) -> None: +//| """Deinitialize this object, freeing its hardware resources""" +//| ... +//| +STATIC mp_obj_t canio_can_deinit(mp_obj_t self_in) { + canio_can_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_canio_can_deinit(self); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(canio_can_deinit_obj, canio_can_deinit); + +//| def __enter__(self) -> CAN: +//| """Returns self, to allow the object to be used in a `with` statement for resource control""" +//| ... +//| +STATIC mp_obj_t canio_can_enter(mp_obj_t self_in) { + canio_can_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_canio_can_check_for_deinit(self); + return self_in; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(canio_can_enter_obj, canio_can_enter); + +//| def __exit__(self, unused1, unused2, unused3) -> None: +//| """Calls deinit()""" +//| ... +STATIC mp_obj_t canio_can_exit(size_t num_args, const mp_obj_t args[]) { + canio_can_obj_t *self = MP_OBJ_TO_PTR(args[0]); + common_hal_canio_can_deinit(self); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(canio_can_exit_obj, 4, 4, canio_can_exit); + +STATIC const mp_rom_map_elem_t canio_can_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&canio_can_enter_obj) }, + { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&canio_can_exit_obj) }, + { MP_ROM_QSTR(MP_QSTR_auto_restart), MP_ROM_PTR(&canio_can_auto_restart_obj) }, + { MP_ROM_QSTR(MP_QSTR_baudrate), MP_ROM_PTR(&canio_can_baudrate_obj) }, + { MP_ROM_QSTR(MP_QSTR_bus_off_state_count), MP_ROM_PTR(&canio_can_bus_off_state_count_obj) }, + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&canio_can_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR_error_passive_state_count), MP_ROM_PTR(&canio_can_error_passive_state_count_obj) }, + { MP_ROM_QSTR(MP_QSTR_error_warning_state_count), MP_ROM_PTR(&canio_can_error_warning_state_count_obj) }, + { MP_ROM_QSTR(MP_QSTR_listen), MP_ROM_PTR(&canio_can_listen_obj) }, + { MP_ROM_QSTR(MP_QSTR_loopback), MP_ROM_PTR(&canio_can_loopback_obj) }, + { MP_ROM_QSTR(MP_QSTR_receive_error_count), MP_ROM_PTR(&canio_can_receive_error_count_obj) }, + { MP_ROM_QSTR(MP_QSTR_restart), MP_ROM_PTR(&canio_can_restart_obj) }, + { MP_ROM_QSTR(MP_QSTR_send), MP_ROM_PTR(&canio_can_send_obj) }, + { MP_ROM_QSTR(MP_QSTR_silent), MP_ROM_PTR(&canio_can_silent_obj) }, + { MP_ROM_QSTR(MP_QSTR_state), MP_ROM_PTR(&canio_can_state_obj) }, + { MP_ROM_QSTR(MP_QSTR_transmit_error_count), MP_ROM_PTR(&canio_can_transmit_error_count_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(canio_can_locals_dict, canio_can_locals_dict_table); + +const mp_obj_type_t canio_can_type = { + { &mp_type_type }, + .name = MP_QSTR_CAN, + .make_new = canio_can_make_new, + .locals_dict = (mp_obj_t)&canio_can_locals_dict, +}; diff --git a/shared-bindings/canio/CAN.h b/shared-bindings/canio/CAN.h new file mode 100644 index 000000000..66c972c28 --- /dev/null +++ b/shared-bindings/canio/CAN.h @@ -0,0 +1,54 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jeff Epler for Adafruit Industries + * + * 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. + */ + +#pragma once + +#include "py/obj.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/canio/Message.h" + +extern const mp_obj_type_t canio_can_type; + +typedef struct canio_can_obj canio_can_obj_t; + +void common_hal_canio_can_construct(canio_can_obj_t *self, mcu_pin_obj_t *tx, mcu_pin_obj_t *rx, int baudrate, bool loopback, bool silent); +bool common_hal_canio_can_auto_restart_get(canio_can_obj_t *self); +bool common_hal_canio_can_deinited(canio_can_obj_t *self); +int common_hal_canio_can_baudrate_get(canio_can_obj_t *self); +int common_hal_canio_can_bus_off_state_count_get(canio_can_obj_t *self); +int common_hal_canio_can_error_passive_state_count_get(canio_can_obj_t *self); +int common_hal_canio_can_error_warning_state_count_get(canio_can_obj_t *self); +bool common_hal_canio_can_loopback_get(canio_can_obj_t *self); +int common_hal_canio_can_receive_error_count_get(canio_can_obj_t *self); +canio_bus_state_t common_hal_canio_can_state_get(canio_can_obj_t *self); +bool common_hal_canio_can_silent_get(canio_can_obj_t *self); +int common_hal_canio_can_transmit_error_count_get(canio_can_obj_t *self); +void common_hal_canio_can_auto_restart_set(canio_can_obj_t *self, bool auto_restart); +void common_hal_canio_can_check_for_deinit(canio_can_obj_t *self); +void common_hal_canio_can_deinit(canio_can_obj_t *self); +void common_hal_canio_can_restart(canio_can_obj_t *self); +void common_hal_canio_can_send(canio_can_obj_t *self, canio_message_obj_t *message); +void common_hal_canio_reset(void); diff --git a/shared-bindings/canio/Listener.c b/shared-bindings/canio/Listener.c new file mode 100644 index 000000000..1f4176f0c --- /dev/null +++ b/shared-bindings/canio/Listener.c @@ -0,0 +1,185 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jeff Epler for Adafruit Industries + * + * 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 "shared-bindings/canio/Listener.h" +#include "shared-bindings/canio/Message.h" +#include "common-hal/canio/Listener.h" + +#include "py/runtime.h" +#include "py/objproperty.h" + +//| class Listener: +//| """Listens for CAN message +//| +//| canio.Listener is not constructed directly, but instead by calling the +//| Listen method of a canio.CAN object.""" +//| + +//| def read(self) -> Optional[Message]: +//| """Reads a message, after waiting up to self.timeout seconds +//| +//| If no message is received in time, None is returned. Otherwise, +//| a Message is returned.""" +//| ... +//| +STATIC mp_obj_t canio_listener_read(mp_obj_t self_in) { + canio_listener_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_canio_listener_check_for_deinit(self); + + canio_message_obj_t *message = m_new_obj(canio_message_obj_t); + message->base.type = &canio_message_type; + + if (common_hal_canio_listener_readinto(self, message)) { + return message; + } else { + m_free(message); // message did not escape into vm + } + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(canio_listener_read_obj, canio_listener_read); + +//| def readinto(self, message: Message) -> bool: +//| """Returns True (and modifies message) if a message was received +//| before ``timeout`` seconds elapsed, False otherwise.""" +//| ... +//| +STATIC mp_obj_t canio_listener_readinto(mp_obj_t self_in, mp_obj_t message) { + canio_listener_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_obj_type_t *type = mp_obj_get_type(message); + if (type != &canio_message_type) { + mp_raise_TypeError_varg(translate("expected '%q' but got '%q'"), MP_QSTR_Message, type->name); + } + common_hal_canio_listener_check_for_deinit(self); + return mp_obj_new_bool(common_hal_canio_listener_readinto(self, message)); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(canio_listener_readinto_obj, canio_listener_readinto); + +//| def in_waiting(self) -> int: +//| """Returns the number of messages waiting""" +//| ... +//| +STATIC mp_obj_t canio_listener_in_waiting(mp_obj_t self_in) { + canio_listener_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_canio_listener_check_for_deinit(self); + return MP_OBJ_NEW_SMALL_INT(common_hal_canio_listener_in_waiting(self)); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(canio_listener_in_waiting_obj, canio_listener_in_waiting); + +//| def __iter__(self): +//| """Returns self, unless the object is deinitialized""" +//| ... +//| +//| def __next__(self): +//| """Reads a message, after waiting up to self.timeout seconds +//| +//| If no message is received in time, raises StopIteration. Otherwise, +//| a Message is returned.""" +//| ... +//| +STATIC mp_obj_t canio_iternext(mp_obj_t self_in) { + mp_obj_t result = canio_listener_read(self_in); + if (result == mp_const_none) { + return MP_OBJ_STOP_ITERATION; + } + return result; +} + +//| def deinit(self) -> None: +//| """Deinitialize this object, freeing its hardware resources""" +//| ... +//| +STATIC mp_obj_t canio_listener_deinit(mp_obj_t self_in) { + canio_listener_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_canio_listener_deinit(self); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(canio_listener_deinit_obj, canio_listener_deinit); + +//| def __enter__(self) -> CAN: +//| """Returns self, to allow the object to be used in a `with` statement for resource control""" +//| ... +//| +STATIC mp_obj_t canio_listener_enter(mp_obj_t self_in) { + canio_listener_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_canio_listener_check_for_deinit(self); + return self_in; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(canio_listener_enter_obj, canio_listener_enter); + +//| def __exit__(self, unused1, unused2, unused3) -> None: +//| """Calls deinit()""" +//| ... +STATIC mp_obj_t canio_listener_exit(size_t num_args, const mp_obj_t args[]) { + canio_listener_obj_t *self = MP_OBJ_TO_PTR(args[0]); + common_hal_canio_listener_deinit(self); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(canio_listener_exit_obj, 4, 4, canio_listener_exit); + + +//| timeout : float +STATIC mp_obj_t canio_listener_timeout_get(mp_obj_t self_in) { + canio_listener_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_canio_listener_check_for_deinit(self); + return mp_obj_new_float(common_hal_canio_listener_get_timeout(self)); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(canio_listener_timeout_get_obj, canio_listener_timeout_get); + +STATIC mp_obj_t canio_listener_timeout_set(mp_obj_t self_in, mp_obj_t timeout_in) { + canio_listener_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_canio_listener_check_for_deinit(self); + common_hal_canio_listener_set_timeout(self, mp_obj_get_float(timeout_in)); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(canio_listener_timeout_set_obj, canio_listener_timeout_set); + +STATIC const mp_obj_property_t canio_listener_timeout_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&canio_listener_timeout_get_obj, + (mp_obj_t)&canio_listener_timeout_set_obj, + (mp_obj_t)mp_const_none}, +}; + + + +STATIC const mp_rom_map_elem_t canio_listener_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&canio_listener_enter_obj) }, + { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&canio_listener_exit_obj) }, + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&canio_listener_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR_in_waiting), MP_ROM_PTR(&canio_listener_in_waiting_obj) }, + { MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&canio_listener_read_obj) }, + { MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&canio_listener_readinto_obj) }, + { MP_ROM_QSTR(MP_QSTR_timeout), MP_ROM_PTR(&canio_listener_timeout_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(canio_listener_locals_dict, canio_listener_locals_dict_table); + +const mp_obj_type_t canio_listener_type = { + { &mp_type_type }, + .name = MP_QSTR_Listener, + .getiter = mp_identity_getiter, + .iternext = canio_iternext, + .locals_dict = (mp_obj_dict_t*)&canio_listener_locals_dict, +}; diff --git a/shared-bindings/canio/Listener.h b/shared-bindings/canio/Listener.h new file mode 100644 index 000000000..eaa3490df --- /dev/null +++ b/shared-bindings/canio/Listener.h @@ -0,0 +1,31 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jeff Epler for Adafruit Industries + * + * 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. + */ + +#pragma once + +#include "py/obj.h" + +extern const mp_obj_type_t canio_listener_type; diff --git a/shared-bindings/canio/Match.c b/shared-bindings/canio/Match.c new file mode 100644 index 000000000..ff5d028fa --- /dev/null +++ b/shared-bindings/canio/Match.c @@ -0,0 +1,139 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jeff Epler for Adafruit Industries + * + * 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 "shared-bindings/canio/Match.h" + +#include "py/objproperty.h" +#include "py/runtime.h" + +//| class Match: +//| """Describe CAN bus messages to match""" +//| +//| +//| def __init__(self, address: int, *, mask: int = 0, extended: bool = False): +//| """Construct a Match with the given properties. +//| +//| If mask is nonzero, then the filter is for any sender which matches all +//| the nonzero bits in mask. Otherwise, it matches exactly the given address. +//| If extended is true then only extended addresses are matched, otherwise +//| only standard addresses are matched.""" +//| + +STATIC mp_obj_t canio_match_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_address, ARG_mask, ARG_extended, NUM_ARGS }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_address, MP_ARG_INT | MP_ARG_REQUIRED }, + { MP_QSTR_mask, MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_extended, MP_ARG_BOOL, {.u_bool = false} }, + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + MP_STATIC_ASSERT( MP_ARRAY_SIZE(allowed_args) == NUM_ARGS ); + + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + int address_bits = args[ARG_extended].u_bool ? 0x1fffffff : 0x7ff; + int address = args[ARG_address].u_int; + int mask = args[ARG_mask].u_int; + + if (address & ~address_bits) { + mp_raise_ValueError_varg(translate("%q out of range"), MP_QSTR_address); + } + + if (mask & ~address_bits) { + mp_raise_ValueError_varg(translate("%q out of range"), MP_QSTR_mask); + } + + canio_match_obj_t *self = m_new_obj(canio_match_obj_t); + self->base.type = &canio_match_type; + common_hal_canio_match_construct(self, args[ARG_address].u_int, args[ARG_mask].u_int, args[ARG_extended].u_bool); + return self; +} + +//| address: int +//| """The address to match""" +//| + +STATIC mp_obj_t canio_match_address_get(mp_obj_t self_in) { + canio_match_obj_t *self = self_in; + return MP_OBJ_NEW_SMALL_INT(common_hal_canio_match_get_address(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(canio_match_address_get_obj, canio_match_address_get); + +const mp_obj_property_t canio_match_address_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&canio_match_address_get_obj, + (mp_obj_t)&mp_const_none_obj, + (mp_obj_t)&mp_const_none_obj}, +}; + +//| +//| mask: int +//| """The optional mask of addresses to match""" +//| + +STATIC mp_obj_t canio_match_mask_get(mp_obj_t self_in) { + canio_match_obj_t *self = self_in; + return MP_OBJ_NEW_SMALL_INT(common_hal_canio_match_get_mask(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(canio_match_mask_get_obj, canio_match_mask_get); + +const mp_obj_property_t canio_match_mask_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&canio_match_mask_get_obj, + (mp_obj_t)&mp_const_none_obj, + (mp_obj_t)&mp_const_none_obj}, +}; + +//| extended: bool +//| """True to match extended addresses, False to match standard addresses""" +//| + +STATIC mp_obj_t canio_match_extended_get(mp_obj_t self_in) { + canio_match_obj_t *self = self_in; + return mp_obj_new_bool(common_hal_canio_match_get_extended(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(canio_match_extended_get_obj, canio_match_extended_get); + +const mp_obj_property_t canio_match_extended_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&canio_match_extended_get_obj, + (mp_obj_t)&mp_const_none_obj, + (mp_obj_t)&mp_const_none_obj}, +}; + +STATIC const mp_rom_map_elem_t canio_match_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_address), MP_ROM_PTR(&canio_match_address_obj) }, + { MP_ROM_QSTR(MP_QSTR_mask), MP_ROM_PTR(&canio_match_mask_obj) }, + { MP_ROM_QSTR(MP_QSTR_extended), MP_ROM_PTR(&canio_match_extended_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(canio_match_locals_dict, canio_match_locals_dict_table); + +const mp_obj_type_t canio_match_type = { + { &mp_type_type }, + .name = MP_QSTR_Match, + .make_new = canio_match_make_new, + .locals_dict = (mp_obj_dict_t*)&canio_match_locals_dict, +}; diff --git a/shared-bindings/canio/Match.h b/shared-bindings/canio/Match.h new file mode 100644 index 000000000..88996d730 --- /dev/null +++ b/shared-bindings/canio/Match.h @@ -0,0 +1,37 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jeff Epler for Adafruit Industries + * + * 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. + */ + +#pragma once + +#include "py/obj.h" +#include "shared-module/canio/Match.h" + +extern const mp_obj_type_t canio_match_type; + +void common_hal_canio_match_construct(canio_match_obj_t *self, int address, int mask, bool extended); +int common_hal_canio_match_get_address(const canio_match_obj_t *self); +int common_hal_canio_match_get_mask(const canio_match_obj_t *self); +bool common_hal_canio_match_get_extended(const canio_match_obj_t *self); diff --git a/shared-bindings/canio/Message.c b/shared-bindings/canio/Message.c new file mode 100644 index 000000000..23645d2e5 --- /dev/null +++ b/shared-bindings/canio/Message.c @@ -0,0 +1,212 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jeff Epler for Adafruit Industries + * + * 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 "shared-bindings/canio/Message.h" + +#include "py/obj.h" +#include "py/objproperty.h" +#include "py/runtime.h" + +//| class Message: +//| def __init__(self, id: int=0, data: Optional[bytes] = None, *, size: Optional[int] = None, rtr: bool = False, extended: bool = False): +//| """Construct a Message to use with a CAN bus. Provide arguments to create a message to send. Otherwise, use Listener.readinto() to read a message. +//| +//| :param int id: The numeric ID of the message +//| :param bytes data: The content of the message +//| :param int size: The amount of data requested, for an rtr +//| :param bool rtr: True if the message represents an rtr (Remote Transmission Request) +//| :param bool extended: True if the message has an extended identifier, False if it has a standard identifier +//| +//| In CAN, messages can have a size from 0 to 8 bytes. +//| +//| For a non-rtr message, specify ``data``. For an rtr-message, specify either ``data`` (a dummy buffer of the requested size) or ``size``. +//| """ +//| ... +//| +STATIC mp_obj_t canio_message_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_id, ARG_data, ARG_size, ARG_rtr, ARG_extended, NUM_ARGS }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_id, MP_ARG_INT, {.u_obj = 0} }, + { MP_QSTR_data, MP_ARG_OBJ, {.u_obj = 0} }, + { MP_QSTR_size, MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_rtr, MP_ARG_BOOL, {.u_bool = false} }, + { MP_QSTR_extended, MP_ARG_BOOL, {.u_bool = false} }, + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + MP_STATIC_ASSERT( MP_ARRAY_SIZE(allowed_args) == NUM_ARGS ); + + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + bool rtr = args[ARG_rtr].u_bool; + bool extended = args[ARG_extended].u_bool; + size_t size = (size_t)args[ARG_size].u_int; + bool specified_size = (size != (size_t)-1); + bool specified_data = (args[ARG_data].u_obj != NULL); + + if (specified_size && specified_data) { + mp_raise_TypeError(translate("specify size or data, but not both")); + } + + mp_buffer_info_t data; + if (specified_data) { + mp_get_buffer_raise(args[ARG_data].u_obj, &data, MP_BUFFER_READ); + } else if (specified_size) { + data.buf = 0; + data.len = size; + } else { + data.buf = 0; + data.len = 0; + } + + if (data.len > 8) { + mp_raise_ValueError(translate("Messages limited to 8 bytes")); + } + + canio_message_obj_t *self = m_new_obj(canio_message_obj_t); + self->base.type = &canio_message_type; + common_hal_canio_message_construct(self, args[ARG_id].u_int, data.buf, data.len, rtr, extended); + return self; +} + +//| id: int +//| """The numeric ID of the message""" +//| +STATIC mp_obj_t canio_message_id_get(const mp_obj_t self_in) { + canio_message_obj_t *self = self_in; + return MP_OBJ_NEW_SMALL_INT(common_hal_canio_message_get_id(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(canio_message_id_get_obj, canio_message_id_get); + +STATIC mp_obj_t canio_message_id_set(const mp_obj_t self_in, const mp_obj_t id) { + canio_message_obj_t *self = self_in; + common_hal_canio_message_set_id(self, mp_obj_get_int(id)); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(canio_message_id_set_obj, canio_message_id_set); + +STATIC const mp_obj_property_t canio_message_id_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&canio_message_id_get_obj, + (mp_obj_t)&canio_message_id_set_obj, + (mp_obj_t)&mp_const_none_obj}, +}; + +//| data: bytes +//| """The content of the message, or dummy content in the case of an rtr. +//| +//| Assigning to data also sets the length and clears the rtr flag.""" +//| +STATIC mp_obj_t canio_message_data_get(const mp_obj_t self_in) { + canio_message_obj_t *self = self_in; + return mp_obj_new_bytes((const byte*)common_hal_canio_message_get_data(self), common_hal_canio_message_get_size(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(canio_message_data_get_obj, canio_message_data_get); + +STATIC mp_obj_t canio_message_data_set(const mp_obj_t self_in, const mp_obj_t data_in) { + canio_message_obj_t *self = self_in; + mp_buffer_info_t data; + mp_get_buffer_raise(data_in, &data, MP_BUFFER_READ); + if (data.len > 8) { + mp_raise_ValueError(translate("Messages limited to 8 bytes")); + } + common_hal_canio_message_set_data(self, data.buf, data.len); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(canio_message_data_set_obj, canio_message_data_set); + + +STATIC const mp_obj_property_t canio_message_data_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&canio_message_data_get_obj, + (mp_obj_t)&canio_message_data_set_obj, + (mp_obj_t)&mp_const_none_obj}, +}; + + +//| extended: bool +//| """True if the message represents a remote transmission request (RTR)""" +//| +STATIC mp_obj_t canio_message_extended_get(const mp_obj_t self_in) { + canio_message_obj_t *self = self_in; + return mp_obj_new_bool(common_hal_canio_message_get_extended(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(canio_message_extended_get_obj, canio_message_extended_get); + +STATIC mp_obj_t canio_message_extended_set(const mp_obj_t self_in, const mp_obj_t extended) { + canio_message_obj_t *self = self_in; + common_hal_canio_message_set_extended(self, mp_obj_is_true(extended)); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(canio_message_extended_set_obj, canio_message_extended_set); + + +STATIC const mp_obj_property_t canio_message_extended_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&canio_message_extended_get_obj, + (mp_obj_t)&canio_message_extended_set_obj, + (mp_obj_t)&mp_const_none_obj}, +}; + + +//| rtr: bool +//| """True if the message represents a remote transmission request (RTR). Setting rtr to true zeros out data""" +//| +STATIC mp_obj_t canio_message_rtr_get(const mp_obj_t self_in) { + canio_message_obj_t *self = self_in; + return mp_obj_new_bool(common_hal_canio_message_get_rtr(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(canio_message_rtr_get_obj, canio_message_rtr_get); + +STATIC mp_obj_t canio_message_rtr_set(const mp_obj_t self_in, const mp_obj_t rtr) { + canio_message_obj_t *self = self_in; + common_hal_canio_message_set_rtr(self, mp_obj_is_true(rtr)); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(canio_message_rtr_set_obj, canio_message_rtr_set); + + +STATIC const mp_obj_property_t canio_message_rtr_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&canio_message_rtr_get_obj, + (mp_obj_t)&canio_message_rtr_set_obj, + (mp_obj_t)&mp_const_none_obj}, +}; + + +STATIC const mp_rom_map_elem_t canio_message_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_id), MP_ROM_PTR(&canio_message_id_obj) }, + { MP_ROM_QSTR(MP_QSTR_data), MP_ROM_PTR(&canio_message_data_obj) }, + { MP_ROM_QSTR(MP_QSTR_rtr), MP_ROM_PTR(&canio_message_rtr_obj) }, + { MP_ROM_QSTR(MP_QSTR_extended), MP_ROM_PTR(&canio_message_extended_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(canio_message_locals_dict, canio_message_locals_dict_table); + +const mp_obj_type_t canio_message_type = { + { &mp_type_type }, + .name = MP_QSTR_Message, + .make_new = canio_message_make_new, + .locals_dict = (mp_obj_t)&canio_message_locals_dict, +}; diff --git a/shared-bindings/canio/Message.h b/shared-bindings/canio/Message.h new file mode 100644 index 000000000..34b632e84 --- /dev/null +++ b/shared-bindings/canio/Message.h @@ -0,0 +1,44 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jeff Epler for Adafruit Industries + * + * 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. + */ + +#pragma once + +#include "py/obj.h" +#include "shared-module/canio/Message.h" + +extern const mp_obj_type_t canio_message_type; + +void common_hal_canio_message_construct(canio_message_obj_t *self, int id, void *data, size_t size, bool rtr, bool extended); +const void *common_hal_canio_message_get_data(const canio_message_obj_t *self); +void common_hal_canio_message_set_data(canio_message_obj_t *self, const void *data, size_t size); +bool common_hal_canio_message_get_extended(const canio_message_obj_t *self); +void common_hal_canio_message_set_extended(canio_message_obj_t *self, bool extended); +int common_hal_canio_message_get_id(const canio_message_obj_t *self); +void common_hal_canio_message_set_id(canio_message_obj_t *self, int id); +bool common_hal_canio_message_get_rtr(const canio_message_obj_t *self); +void common_hal_canio_message_set_rtr(canio_message_obj_t *self, bool rtr); +size_t common_hal_canio_message_get_size(const canio_message_obj_t *self); +void common_hal_canio_message_set_size(canio_message_obj_t *self, size_t size); diff --git a/shared-bindings/canio/__init__.c b/shared-bindings/canio/__init__.c new file mode 100644 index 000000000..b0b982c95 --- /dev/null +++ b/shared-bindings/canio/__init__.c @@ -0,0 +1,124 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jeff Epler for Adafruit Industries + * + * 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. + */ + +//| """CAN bus access +//| +//| The `canio` module contains low level classes to support the CAN bus +//| protocol. +//| +//| CAN and Listener classes change hardware state and should be deinitialized when they +//| are no longer needed if the program continues after use. To do so, either +//| call :py:meth:`!deinit` or use a context manager. See +//| :ref:`lifetime-and-contextmanagers` for more info. +//| +//| For example:: +//| +//| import canio +//| from board import * +//| +//| can = canio.CAN(board.CAN_RX, board.CAN_TX, baudrate=1000000) +//| message = canio.Message(id=0x0408, data="adafruit" +//| can.write(message)) +//| can.deinit() +//| +//| This example will write the data 'adafruit' onto the CAN bus to any +//| device listening for message id 0x0408. +//| +//| A CAN bus involves a transceiver, which is often a separate chip with a "standby" pin. +//| If your board has a CAN_STANDBY pin, ensure to set it to an output with the value False +//| to enable the transceiver. +//| +//| Other implementations of the CAN device may exist (for instance, attached +//| via an SPI bus). If so their constructor arguments may differ, but +//| otherwise we encourage implementors to follow the API that the core uses. +//| """ +//| + +#include "py/obj.h" +#include "py/enum.h" + +#include "shared-bindings/canio/__init__.h" +#include "shared-bindings/canio/CAN.h" +#include "shared-bindings/canio/Match.h" +#include "shared-bindings/canio/Message.h" +#include "shared-bindings/canio/Listener.h" + +MAKE_ENUM_VALUE(canio_bus_state_type, bus_state, ERROR_ACTIVE, BUS_STATE_ERROR_ACTIVE); +MAKE_ENUM_VALUE(canio_bus_state_type, bus_state, ERROR_PASSIVE, BUS_STATE_ERROR_PASSIVE); +MAKE_ENUM_VALUE(canio_bus_state_type, bus_state, ERROR_WARNING, BUS_STATE_ERROR_WARNING); +MAKE_ENUM_VALUE(canio_bus_state_type, bus_state, BUS_OFF, BUS_STATE_OFF); + +//| class BusState: +//| """The state of the CAN bus""" +//| +//| ERROR_ACTIVE: object +//| """The bus is in the normal (active) state""" +//| +//| ERROR_WARNING: object +//| """The bus is in the normal (active) state, but a moderate number of errors have occurred recently. +//| +//| NOTE: Not all implementations may use ERROR_WARNING. Do not rely on seeing ERROR_WARNING before ERROR_PASSIVE.""" +//| +//| ERROR_PASSIVE: object +//| """The bus is in the passive state due to the number of errors that have occurred recently. +//| +//| This device will acknowledge packets it receives, but cannot transmit messages. +//| If additional errors occur, this device may progress to BUS_OFF. +//| If it successfully acknowledges other packets on the bus, it can return to ERROR_WARNING or ERROR_ACTIVE and transmit packets. +//| """ +//| +//| BUS_OFF: object +//| """The bus has turned off due to the number of errors that have +//| occurred recently. It must be restarted before it will send or receive +//| packets. This device will neither send or acknowledge packets on the bus.""" +//| +MAKE_ENUM_MAP(canio_bus_state) { + MAKE_ENUM_MAP_ENTRY(bus_state, ERROR_ACTIVE), + MAKE_ENUM_MAP_ENTRY(bus_state, ERROR_PASSIVE), + MAKE_ENUM_MAP_ENTRY(bus_state, ERROR_WARNING), + MAKE_ENUM_MAP_ENTRY(bus_state, BUS_OFF), +}; +STATIC MP_DEFINE_CONST_DICT(canio_bus_state_locals_dict, canio_bus_state_locals_table); + +MAKE_PRINTER(canio, canio_bus_state); + +MAKE_ENUM_TYPE(canio, BusState, canio_bus_state); + +STATIC const mp_rom_map_elem_t canio_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR_BusState), MP_ROM_PTR(&canio_bus_state_type) }, + { MP_ROM_QSTR(MP_QSTR_CAN), MP_ROM_PTR(&canio_can_type) }, + { MP_ROM_QSTR(MP_QSTR_Listener), MP_ROM_PTR(&canio_listener_type) }, + { MP_ROM_QSTR(MP_QSTR_Match), MP_ROM_PTR(&canio_match_type) }, + { MP_ROM_QSTR(MP_QSTR_Message), MP_ROM_PTR(&canio_message_type) }, + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR__canio) }, +}; + +STATIC MP_DEFINE_CONST_DICT(canio_module_globals, canio_module_globals_table); + +const mp_obj_module_t canio_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t*)&canio_module_globals, +}; diff --git a/shared-bindings/canio/__init__.h b/shared-bindings/canio/__init__.h new file mode 100644 index 000000000..e24eba92c --- /dev/null +++ b/shared-bindings/canio/__init__.h @@ -0,0 +1,33 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jeff Epler for Adafruit Industries + * + * 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. + */ + +#pragma once + +typedef enum { + BUS_STATE_ERROR_ACTIVE, BUS_STATE_ERROR_PASSIVE, BUS_STATE_ERROR_WARNING, BUS_STATE_OFF +} canio_bus_state_t; + +extern const mp_obj_type_t canio_bus_state_type; diff --git a/shared-module/canio/Match.c b/shared-module/canio/Match.c new file mode 100644 index 000000000..9e33b956f --- /dev/null +++ b/shared-module/canio/Match.c @@ -0,0 +1,43 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jeff Epler for Adafruit Industries + * + * 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 "shared-module/canio/Match.h" + +void common_hal_canio_match_construct(canio_match_obj_t *self, int address, int mask, bool extended) { + self->address = address; + self->mask = mask; + self->extended = extended; +} + +int common_hal_canio_match_get_address(const canio_match_obj_t *self) { + return self->address; +} +int common_hal_canio_match_get_mask(const canio_match_obj_t *self) { + return self->mask; +} +bool common_hal_canio_match_get_extended(const canio_match_obj_t *self) { + return self->extended; +} diff --git a/shared-module/canio/Match.h b/shared-module/canio/Match.h new file mode 100644 index 000000000..25f047f37 --- /dev/null +++ b/shared-module/canio/Match.h @@ -0,0 +1,36 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jeff Epler for Adafruit Industries + * + * 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. + */ + +#pragma once + +#include "py/obj.h" + +typedef struct { + mp_obj_base_t base; + int address; + int mask; + bool extended; +} canio_match_obj_t; diff --git a/shared-module/canio/Message.c b/shared-module/canio/Message.c new file mode 100644 index 000000000..b8ebb7859 --- /dev/null +++ b/shared-module/canio/Message.c @@ -0,0 +1,101 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jeff Epler for Adafruit Industries + * + * 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 "shared-module/canio/Message.h" + +#include <string.h> + +void common_hal_canio_message_construct(canio_message_obj_t *self, int id, void *data, size_t size, bool rtr, bool extended) +{ + self->id = id; + self->size = size; + self->rtr = rtr; + self->extended = extended; + if (data) { + memcpy(self->data, data, size); + } else { + memset(self->data, 0, size); + } +} + +int common_hal_canio_message_get_id(const canio_message_obj_t *self) +{ + return self->id; +} + +void common_hal_canio_message_set_id(canio_message_obj_t *self, int id) +{ + self->id = id; +} + + +const void *common_hal_canio_message_get_data(const canio_message_obj_t *self) +{ + return self->data; +} + +const void common_hal_canio_message_set_data(canio_message_obj_t *self, const void *data, size_t size) +{ + self->rtr = false; + self->size = size; + memcpy(self->data, data, size); +} + + +size_t common_hal_canio_message_get_size(const canio_message_obj_t *self) +{ + return self->size; +} + +void common_hal_canio_message_set_size(canio_message_obj_t *self, size_t size) +{ + memset(self->data, 0, size); + self->size = size; +} + + +bool common_hal_canio_message_get_rtr(const canio_message_obj_t *self) +{ + return self->rtr; +} + +void common_hal_canio_message_set_rtr(canio_message_obj_t *self, bool rtr) +{ + self->rtr = rtr; + if (rtr) { + memset(self->data, 0, self->size); + } +} + +bool common_hal_canio_message_get_extended(const canio_message_obj_t *self) +{ + return self->extended; +} + +void common_hal_canio_message_set_extended(canio_message_obj_t *self, bool extended) +{ + self->extended = extended; +} diff --git a/shared-module/canio/Message.h b/shared-module/canio/Message.h new file mode 100644 index 000000000..b99c5c48e --- /dev/null +++ b/shared-module/canio/Message.h @@ -0,0 +1,38 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jeff Epler for Adafruit Industries + * + * 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. + */ + +#pragma once + +#include "py/obj.h" + +typedef struct { + mp_obj_base_t base; + int id; + uint8_t data[8]; + size_t size:4; + bool rtr:1; + bool extended:1; +} canio_message_obj_t; |
