From 058c16e98c4036c8a53547062f513a0d74eb90c4 Mon Sep 17 00:00:00 2001 From: DavePutz Date: Wed, 10 Jun 2020 17:11:28 -0500 Subject: Issue #3014 - refresh after changing to transparent Adding self->needs_refresh = true; in common_hal_displayio_palette_make_opaque() and common_hal_displayio_palette_make_transparent() --- shared-module/displayio/Palette.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'shared-module') diff --git a/shared-module/displayio/Palette.c b/shared-module/displayio/Palette.c index 1ef03aab4..facb1fa73 100644 --- a/shared-module/displayio/Palette.c +++ b/shared-module/displayio/Palette.c @@ -35,10 +35,12 @@ void common_hal_displayio_palette_construct(displayio_palette_t* self, uint16_t void common_hal_displayio_palette_make_opaque(displayio_palette_t* self, uint32_t palette_index) { self->colors[palette_index].transparent = false; + self->needs_refresh = true; } void common_hal_displayio_palette_make_transparent(displayio_palette_t* self, uint32_t palette_index) { self->colors[palette_index].transparent = true; + self->needs_refresh = true; } uint32_t common_hal_displayio_palette_get_len(displayio_palette_t* self) { -- cgit v1.2.3 From 81896930c7a91447c8830003e8cf92dce28db3d4 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Mon, 15 Jun 2020 16:35:51 -0400 Subject: Fix ScanEntry prefix matching for all --- shared-module/_bleio/ScanEntry.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) (limited to 'shared-module') diff --git a/shared-module/_bleio/ScanEntry.c b/shared-module/_bleio/ScanEntry.c index 785209c4a..91712baad 100644 --- a/shared-module/_bleio/ScanEntry.c +++ b/shared-module/_bleio/ScanEntry.c @@ -56,12 +56,19 @@ bool bleio_scanentry_data_matches(const uint8_t* data, size_t len, const uint8_t if (prefixes_length == 0) { return true; } + if (len == 0) { + // Prefixes exist, but no data. + return false; + } size_t i = 0; while(i < prefixes_length) { uint8_t prefix_length = prefixes[i]; i += 1; size_t j = 0; + bool prefix_matched = false; + mp_printf(&mp_plat_print,"i %d\n", i); // XXX while (j < len) { + mp_printf(&mp_plat_print," j %d\n", j); // XXX uint8_t structure_length = data[j]; j += 1; if (structure_length == 0) { @@ -71,13 +78,21 @@ bool bleio_scanentry_data_matches(const uint8_t* data, size_t len, const uint8_t if (any) { return true; } - } else if (!any) { - return false; + prefix_matched = true; + mp_printf(&mp_plat_print," match\n"); // XXX + break; } j += structure_length; } + // If all (!any), the current prefix must have matched at least one field. + if (!prefix_matched && !any) { + mp_printf(&mp_plat_print," Date: Mon, 15 Jun 2020 18:10:34 -0400 Subject: remove debugging prints --- shared-module/_bleio/ScanEntry.c | 5 ----- 1 file changed, 5 deletions(-) (limited to 'shared-module') diff --git a/shared-module/_bleio/ScanEntry.c b/shared-module/_bleio/ScanEntry.c index 91712baad..af9e4b347 100644 --- a/shared-module/_bleio/ScanEntry.c +++ b/shared-module/_bleio/ScanEntry.c @@ -66,9 +66,7 @@ bool bleio_scanentry_data_matches(const uint8_t* data, size_t len, const uint8_t i += 1; size_t j = 0; bool prefix_matched = false; - mp_printf(&mp_plat_print,"i %d\n", i); // XXX while (j < len) { - mp_printf(&mp_plat_print," j %d\n", j); // XXX uint8_t structure_length = data[j]; j += 1; if (structure_length == 0) { @@ -79,20 +77,17 @@ bool bleio_scanentry_data_matches(const uint8_t* data, size_t len, const uint8_t return true; } prefix_matched = true; - mp_printf(&mp_plat_print," match\n"); // XXX break; } j += structure_length; } // If all (!any), the current prefix must have matched at least one field. if (!prefix_matched && !any) { - mp_printf(&mp_plat_print," Date: Sun, 21 Jun 2020 08:31:26 -0500 Subject: _pew: move to common-hal I noticed that this code was referring to samd-specific functionality, and isn't enabled except in one samd board (pewpew10). Move it. There is incomplte support for _pew in mimxrt10xx which then caused build errors; adding a #if guard to check for _pew being enabled fixes it. The _pew module is not likely to be important on mimxrt but I'll leave the choice to remove it to someone else. --- ports/atmel-samd/common-hal/_pew/PewPew.c | 125 ++++++++++++++++++++++++++++ ports/atmel-samd/common-hal/_pew/PewPew.h | 48 +++++++++++ ports/atmel-samd/common-hal/_pew/__init__.c | 87 +++++++++++++++++++ ports/atmel-samd/common-hal/_pew/__init__.h | 32 +++++++ ports/atmel-samd/supervisor/port.c | 4 +- ports/atmel-samd/timer_handler.c | 2 +- ports/mimxrt10xx/supervisor/port.c | 2 + py/circuitpy_defns.mk | 4 +- shared-bindings/_pew/PewPew.c | 2 +- shared-bindings/_pew/__init__.c | 2 +- shared-module/_pew/PewPew.c | 125 ---------------------------- shared-module/_pew/PewPew.h | 48 ----------- shared-module/_pew/__init__.c | 87 ------------------- shared-module/_pew/__init__.h | 32 ------- 14 files changed, 302 insertions(+), 298 deletions(-) create mode 100644 ports/atmel-samd/common-hal/_pew/PewPew.c create mode 100644 ports/atmel-samd/common-hal/_pew/PewPew.h create mode 100644 ports/atmel-samd/common-hal/_pew/__init__.c create mode 100644 ports/atmel-samd/common-hal/_pew/__init__.h delete mode 100644 shared-module/_pew/PewPew.c delete mode 100644 shared-module/_pew/PewPew.h delete mode 100644 shared-module/_pew/__init__.c delete mode 100644 shared-module/_pew/__init__.h (limited to 'shared-module') diff --git a/ports/atmel-samd/common-hal/_pew/PewPew.c b/ports/atmel-samd/common-hal/_pew/PewPew.c new file mode 100644 index 000000000..568dc6425 --- /dev/null +++ b/ports/atmel-samd/common-hal/_pew/PewPew.c @@ -0,0 +1,125 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Radomir Dopieralski + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include + +#include "py/mpstate.h" +#include "py/runtime.h" +#include "__init__.h" +#include "PewPew.h" + +#include "shared-bindings/digitalio/Pull.h" +#include "shared-bindings/digitalio/DigitalInOut.h" +#include "shared-bindings/util.h" +#include "samd/timers.h" +#include "supervisor/shared/translate.h" +#include "timer_handler.h" + + +static uint8_t pewpew_tc_index = 0xff; + + +void pewpew_interrupt_handler(uint8_t index) { + if (index != pewpew_tc_index) { + return; + } + Tc* tc = tc_insts[index]; + if (!tc->COUNT16.INTFLAG.bit.MC0) { + return; + } + + pew_tick(); + + // Clear the interrupt bit. + tc->COUNT16.INTFLAG.reg = TC_INTFLAG_MC0; +} + +void pew_init() { + pew_obj_t* pew = MP_STATE_VM(pew_singleton); + + common_hal_digitalio_digitalinout_switch_to_input(pew->buttons, PULL_UP); + + for (size_t i = 0; i < pew->rows_size; ++i) { + digitalio_digitalinout_obj_t *pin = MP_OBJ_TO_PTR(pew->rows[i]); + common_hal_digitalio_digitalinout_switch_to_output(pin, false, + DRIVE_MODE_PUSH_PULL); + } + for (size_t i = 0; i < pew->cols_size; ++i) { + digitalio_digitalinout_obj_t *pin = MP_OBJ_TO_PTR(pew->cols[i]); + common_hal_digitalio_digitalinout_switch_to_output(pin, true, + DRIVE_MODE_OPEN_DRAIN); + } + if (pewpew_tc_index == 0xff) { + // Find a spare timer. + uint8_t index = find_free_timer(); + if (index == 0xff) { + mp_raise_RuntimeError(translate("All timers in use")); + } + Tc *tc = tc_insts[index]; + + pewpew_tc_index = index; + set_timer_handler(true, index, TC_HANDLER_PEW); + + // We use GCLK0 for SAMD21 and GCLK1 for SAMD51 because they both run + // at 48mhz making our math the same across the boards. + #ifdef SAMD21 + turn_on_clocks(true, index, 0); + #endif + #ifdef SAMD51 + turn_on_clocks(true, index, 1); + #endif + + + #ifdef SAMD21 + tc->COUNT16.CTRLA.reg = TC_CTRLA_MODE_COUNT16 | + TC_CTRLA_PRESCALER_DIV64 | + TC_CTRLA_WAVEGEN_MFRQ; + #endif + #ifdef SAMD51 + tc_reset(tc); + tc_set_enable(tc, false); + tc->COUNT16.CTRLA.reg = TC_CTRLA_MODE_COUNT16 + | TC_CTRLA_PRESCALER_DIV64; + tc->COUNT16.WAVE.reg = TC_WAVE_WAVEGEN_MFRQ; + #endif + + tc_set_enable(tc, true); + tc->COUNT16.CC[0].reg = 64; + + // Clear our interrupt in case it was set earlier + tc->COUNT16.INTFLAG.reg = TC_INTFLAG_MC0; + tc->COUNT16.INTENSET.reg = TC_INTENSET_MC0; + tc_enable_interrupts(pewpew_tc_index); + } +} + +void pew_reset(void) { + if (pewpew_tc_index != 0xff) { + tc_reset(tc_insts[pewpew_tc_index]); + pewpew_tc_index = 0xff; + } + MP_STATE_VM(pew_singleton) = NULL; +} diff --git a/ports/atmel-samd/common-hal/_pew/PewPew.h b/ports/atmel-samd/common-hal/_pew/PewPew.h new file mode 100644 index 000000000..da1cae0a2 --- /dev/null +++ b/ports/atmel-samd/common-hal/_pew/PewPew.h @@ -0,0 +1,48 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Radomir Dopieralski + * + * 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. + */ + +#ifndef MICROPY_INCLUDED_PEW_PEWPEW_H +#define MICROPY_INCLUDED_PEW_PEWPEW_H + +#include +#include "shared-bindings/digitalio/DigitalInOut.h" + +typedef struct { + mp_obj_base_t base; + uint8_t* buffer; + mp_obj_t* rows; + mp_obj_t* cols; + digitalio_digitalinout_obj_t *buttons; + uint8_t rows_size; + uint8_t cols_size; + uint8_t pressed; +} pew_obj_t; + +void pew_init(void); +void pewpew_interrupt_handler(uint8_t index); +void pew_reset(void); + +#endif // MICROPY_INCLUDED_PEW_PEWPEW_H diff --git a/ports/atmel-samd/common-hal/_pew/__init__.c b/ports/atmel-samd/common-hal/_pew/__init__.c new file mode 100644 index 000000000..f3b23c606 --- /dev/null +++ b/ports/atmel-samd/common-hal/_pew/__init__.c @@ -0,0 +1,87 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Radomir Dopieralski + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include + +#include "py/mpstate.h" +#include "__init__.h" +#include "PewPew.h" + +#include "shared-bindings/digitalio/DigitalInOut.h" + + +void pew_tick(void) { + static uint8_t col = 0; + static uint8_t turn = 0; + static uint8_t pressed = 0; + static uint8_t last_pressed = 0; + digitalio_digitalinout_obj_t *pin; + + pew_obj_t* pew = MP_STATE_VM(pew_singleton); + if (!pew) { return; } + + pin = MP_OBJ_TO_PTR(pew->cols[col]); + ++col; + if (col >= pew->cols_size) { + pew->pressed |= last_pressed & pressed; + last_pressed = pressed; + pressed = 0; + col = 0; + ++turn; + if (turn > 11) { + turn = 0; + } + } + if (!common_hal_digitalio_digitalinout_get_value(pew->buttons)) { + pressed |= 1 << col; + } + common_hal_digitalio_digitalinout_set_value(pin, true); + for (size_t x = 0; x < pew->rows_size; ++x) { + pin = MP_OBJ_TO_PTR(pew->rows[x]); + uint8_t color = pew->buffer[col * (pew->rows_size) + x]; + bool value = false; + switch (color & 0x03) { + case 3: + value = true; + break; + case 2: + if (turn == 2 || turn == 5 || turn == 8 || turn == 11) { + value = true; + } + break; + case 1: + if (turn == 0) { + value = true; + } + break; + case 0: + break; + } + common_hal_digitalio_digitalinout_set_value(pin, value); + } + pin = MP_OBJ_TO_PTR(pew->cols[col]); + common_hal_digitalio_digitalinout_set_value(pin, false); +} diff --git a/ports/atmel-samd/common-hal/_pew/__init__.h b/ports/atmel-samd/common-hal/_pew/__init__.h new file mode 100644 index 000000000..4f953c610 --- /dev/null +++ b/ports/atmel-samd/common-hal/_pew/__init__.h @@ -0,0 +1,32 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Radomir Dopieralski + * + * 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. + */ + +#ifndef MICROPY_INCLUDED_PEW_H +#define MICROPY_INCLUDED_PEW_H + +void pew_tick(void); + +#endif // MICROPY_INCLUDED_PEW_H diff --git a/ports/atmel-samd/supervisor/port.c b/ports/atmel-samd/supervisor/port.c index 2b3e9826f..74c984997 100644 --- a/ports/atmel-samd/supervisor/port.c +++ b/ports/atmel-samd/supervisor/port.c @@ -85,7 +85,9 @@ #if CIRCUITPY_GAMEPADSHIFT #include "shared-module/gamepadshift/__init__.h" #endif -#include "shared-module/_pew/PewPew.h" +#if CIRCUITPY_PEW +#include "common-hal/_pew/PewPew.h" +#endif extern volatile bool mp_msc_enabled; diff --git a/ports/atmel-samd/timer_handler.c b/ports/atmel-samd/timer_handler.c index 34d819e16..16d65334e 100644 --- a/ports/atmel-samd/timer_handler.c +++ b/ports/atmel-samd/timer_handler.c @@ -31,7 +31,7 @@ #include "common-hal/pulseio/PulseIn.h" #include "common-hal/pulseio/PulseOut.h" -#include "shared-module/_pew/PewPew.h" +#include "common-hal/_pew/PewPew.h" #include "common-hal/frequencyio/FrequencyIn.h" extern void _PM_IRQ_HANDLER(void); diff --git a/ports/mimxrt10xx/supervisor/port.c b/ports/mimxrt10xx/supervisor/port.c index ed5824732..0b53a79f0 100644 --- a/ports/mimxrt10xx/supervisor/port.c +++ b/ports/mimxrt10xx/supervisor/port.c @@ -53,7 +53,9 @@ #if CIRCUITPY_GAMEPADSHIFT #include "shared-module/gamepadshift/__init__.h" #endif +#if CIRCUITPY_PEW #include "shared-module/_pew/PewPew.h" +#endif #include "supervisor/shared/tick.h" #include "clocks.h" diff --git a/py/circuitpy_defns.mk b/py/circuitpy_defns.mk index 67fbde6e4..cdfb4c6b6 100644 --- a/py/circuitpy_defns.mk +++ b/py/circuitpy_defns.mk @@ -259,6 +259,8 @@ SRC_COMMON_HAL_ALL = \ _bleio/PacketBuffer.c \ _bleio/Service.c \ _bleio/UUID.c \ + _pew/__init__.c \ + _pew/PewPew.c \ analogio/AnalogIn.c \ analogio/AnalogOut.c \ analogio/__init__.c \ @@ -396,8 +398,6 @@ SRC_SHARED_MODULE_ALL = \ terminalio/__init__.c \ uheap/__init__.c \ ustack/__init__.c \ - _pew/__init__.c \ - _pew/PewPew.c \ _eve/__init__.c # All possible sources are listed here, and are filtered by SRC_PATTERNS. diff --git a/shared-bindings/_pew/PewPew.c b/shared-bindings/_pew/PewPew.c index 82bf04a61..352ef38f2 100644 --- a/shared-bindings/_pew/PewPew.c +++ b/shared-bindings/_pew/PewPew.c @@ -31,7 +31,7 @@ #include "shared-bindings/digitalio/DigitalInOut.h" #include "shared-bindings/util.h" #include "PewPew.h" -#include "shared-module/_pew/PewPew.h" +#include "common-hal/_pew/PewPew.h" #include "supervisor/shared/translate.h" //| class PewPew: diff --git a/shared-bindings/_pew/__init__.c b/shared-bindings/_pew/__init__.c index 6e0070d8e..498beaf1c 100644 --- a/shared-bindings/_pew/__init__.c +++ b/shared-bindings/_pew/__init__.c @@ -27,7 +27,7 @@ #include "py/runtime.h" #include "py/mphal.h" #include "PewPew.h" -#include "shared-module/_pew/PewPew.h" +#include "common-hal/_pew/PewPew.h" STATIC mp_obj_t get_pressed(void) { pew_obj_t *pew = MP_STATE_VM(pew_singleton); diff --git a/shared-module/_pew/PewPew.c b/shared-module/_pew/PewPew.c deleted file mode 100644 index 568dc6425..000000000 --- a/shared-module/_pew/PewPew.c +++ /dev/null @@ -1,125 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2019 Radomir Dopieralski - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include - -#include "py/mpstate.h" -#include "py/runtime.h" -#include "__init__.h" -#include "PewPew.h" - -#include "shared-bindings/digitalio/Pull.h" -#include "shared-bindings/digitalio/DigitalInOut.h" -#include "shared-bindings/util.h" -#include "samd/timers.h" -#include "supervisor/shared/translate.h" -#include "timer_handler.h" - - -static uint8_t pewpew_tc_index = 0xff; - - -void pewpew_interrupt_handler(uint8_t index) { - if (index != pewpew_tc_index) { - return; - } - Tc* tc = tc_insts[index]; - if (!tc->COUNT16.INTFLAG.bit.MC0) { - return; - } - - pew_tick(); - - // Clear the interrupt bit. - tc->COUNT16.INTFLAG.reg = TC_INTFLAG_MC0; -} - -void pew_init() { - pew_obj_t* pew = MP_STATE_VM(pew_singleton); - - common_hal_digitalio_digitalinout_switch_to_input(pew->buttons, PULL_UP); - - for (size_t i = 0; i < pew->rows_size; ++i) { - digitalio_digitalinout_obj_t *pin = MP_OBJ_TO_PTR(pew->rows[i]); - common_hal_digitalio_digitalinout_switch_to_output(pin, false, - DRIVE_MODE_PUSH_PULL); - } - for (size_t i = 0; i < pew->cols_size; ++i) { - digitalio_digitalinout_obj_t *pin = MP_OBJ_TO_PTR(pew->cols[i]); - common_hal_digitalio_digitalinout_switch_to_output(pin, true, - DRIVE_MODE_OPEN_DRAIN); - } - if (pewpew_tc_index == 0xff) { - // Find a spare timer. - uint8_t index = find_free_timer(); - if (index == 0xff) { - mp_raise_RuntimeError(translate("All timers in use")); - } - Tc *tc = tc_insts[index]; - - pewpew_tc_index = index; - set_timer_handler(true, index, TC_HANDLER_PEW); - - // We use GCLK0 for SAMD21 and GCLK1 for SAMD51 because they both run - // at 48mhz making our math the same across the boards. - #ifdef SAMD21 - turn_on_clocks(true, index, 0); - #endif - #ifdef SAMD51 - turn_on_clocks(true, index, 1); - #endif - - - #ifdef SAMD21 - tc->COUNT16.CTRLA.reg = TC_CTRLA_MODE_COUNT16 | - TC_CTRLA_PRESCALER_DIV64 | - TC_CTRLA_WAVEGEN_MFRQ; - #endif - #ifdef SAMD51 - tc_reset(tc); - tc_set_enable(tc, false); - tc->COUNT16.CTRLA.reg = TC_CTRLA_MODE_COUNT16 - | TC_CTRLA_PRESCALER_DIV64; - tc->COUNT16.WAVE.reg = TC_WAVE_WAVEGEN_MFRQ; - #endif - - tc_set_enable(tc, true); - tc->COUNT16.CC[0].reg = 64; - - // Clear our interrupt in case it was set earlier - tc->COUNT16.INTFLAG.reg = TC_INTFLAG_MC0; - tc->COUNT16.INTENSET.reg = TC_INTENSET_MC0; - tc_enable_interrupts(pewpew_tc_index); - } -} - -void pew_reset(void) { - if (pewpew_tc_index != 0xff) { - tc_reset(tc_insts[pewpew_tc_index]); - pewpew_tc_index = 0xff; - } - MP_STATE_VM(pew_singleton) = NULL; -} diff --git a/shared-module/_pew/PewPew.h b/shared-module/_pew/PewPew.h deleted file mode 100644 index da1cae0a2..000000000 --- a/shared-module/_pew/PewPew.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2019 Radomir Dopieralski - * - * 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. - */ - -#ifndef MICROPY_INCLUDED_PEW_PEWPEW_H -#define MICROPY_INCLUDED_PEW_PEWPEW_H - -#include -#include "shared-bindings/digitalio/DigitalInOut.h" - -typedef struct { - mp_obj_base_t base; - uint8_t* buffer; - mp_obj_t* rows; - mp_obj_t* cols; - digitalio_digitalinout_obj_t *buttons; - uint8_t rows_size; - uint8_t cols_size; - uint8_t pressed; -} pew_obj_t; - -void pew_init(void); -void pewpew_interrupt_handler(uint8_t index); -void pew_reset(void); - -#endif // MICROPY_INCLUDED_PEW_PEWPEW_H diff --git a/shared-module/_pew/__init__.c b/shared-module/_pew/__init__.c deleted file mode 100644 index f3b23c606..000000000 --- a/shared-module/_pew/__init__.c +++ /dev/null @@ -1,87 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2019 Radomir Dopieralski - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include - -#include "py/mpstate.h" -#include "__init__.h" -#include "PewPew.h" - -#include "shared-bindings/digitalio/DigitalInOut.h" - - -void pew_tick(void) { - static uint8_t col = 0; - static uint8_t turn = 0; - static uint8_t pressed = 0; - static uint8_t last_pressed = 0; - digitalio_digitalinout_obj_t *pin; - - pew_obj_t* pew = MP_STATE_VM(pew_singleton); - if (!pew) { return; } - - pin = MP_OBJ_TO_PTR(pew->cols[col]); - ++col; - if (col >= pew->cols_size) { - pew->pressed |= last_pressed & pressed; - last_pressed = pressed; - pressed = 0; - col = 0; - ++turn; - if (turn > 11) { - turn = 0; - } - } - if (!common_hal_digitalio_digitalinout_get_value(pew->buttons)) { - pressed |= 1 << col; - } - common_hal_digitalio_digitalinout_set_value(pin, true); - for (size_t x = 0; x < pew->rows_size; ++x) { - pin = MP_OBJ_TO_PTR(pew->rows[x]); - uint8_t color = pew->buffer[col * (pew->rows_size) + x]; - bool value = false; - switch (color & 0x03) { - case 3: - value = true; - break; - case 2: - if (turn == 2 || turn == 5 || turn == 8 || turn == 11) { - value = true; - } - break; - case 1: - if (turn == 0) { - value = true; - } - break; - case 0: - break; - } - common_hal_digitalio_digitalinout_set_value(pin, value); - } - pin = MP_OBJ_TO_PTR(pew->cols[col]); - common_hal_digitalio_digitalinout_set_value(pin, false); -} diff --git a/shared-module/_pew/__init__.h b/shared-module/_pew/__init__.h deleted file mode 100644 index 4f953c610..000000000 --- a/shared-module/_pew/__init__.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2019 Radomir Dopieralski - * - * 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. - */ - -#ifndef MICROPY_INCLUDED_PEW_H -#define MICROPY_INCLUDED_PEW_H - -void pew_tick(void); - -#endif // MICROPY_INCLUDED_PEW_H -- cgit v1.2.3 From a26102607e3fe5b6f888fcd246e20ac82a70af7b Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Tue, 9 Jun 2020 18:28:02 -0700 Subject: Add UART support --- Makefile | 9 + ports/atmel-samd/common-hal/busio/UART.c | 6 +- ports/cxd56/common-hal/busio/UART.c | 4 +- ports/esp32s2/common-hal/busio/UART.c | 502 ++++++++++++++----------------- ports/esp32s2/common-hal/busio/UART.h | 7 +- ports/esp32s2/esp-idf | 2 +- ports/mimxrt10xx/common-hal/busio/UART.c | 4 +- ports/nrf/common-hal/busio/UART.c | 6 +- ports/stm/common-hal/busio/UART.c | 6 +- shared-bindings/busio/UART.c | 14 +- shared-bindings/busio/UART.h | 10 +- shared-module/board/__init__.c | 2 +- 12 files changed, 261 insertions(+), 311 deletions(-) (limited to 'shared-module') diff --git a/Makefile b/Makefile index 55f64b17d..0df195093 100644 --- a/Makefile +++ b/Makefile @@ -243,3 +243,12 @@ stubs: update-frozen-libraries: @echo "Updating all frozen libraries to latest tagged version." cd frozen; for library in *; do cd $$library; ../../tools/git-checkout-latest-tag.sh; cd ..; done + +one-of-each: all-source + make -C ports/atmel-samd BOARD=trinket_m0 + make -C ports/atmel-samd BOARD=feather_m4_express + make -C ports/esp32s2 BOARD=espressif_saola_1_wroom + make -C ports/litex BOARD=fomu + make -C ports/mimxrt10xx BOARD=feather_mimxrt1011 + make -C ports/nrf BOARD=feather_nrf52840_express + make -C ports/stm BOARD=feather_stm32f405_express diff --git a/ports/atmel-samd/common-hal/busio/UART.c b/ports/atmel-samd/common-hal/busio/UART.c index 9f740dc8a..9e213e061 100644 --- a/ports/atmel-samd/common-hal/busio/UART.c +++ b/ports/atmel-samd/common-hal/busio/UART.c @@ -58,7 +58,7 @@ void common_hal_busio_uart_construct(busio_uart_obj_t *self, const mcu_pin_obj_t * tx, const mcu_pin_obj_t * rx, const mcu_pin_obj_t * rts, const mcu_pin_obj_t * cts, const mcu_pin_obj_t * rs485_dir, bool rs485_invert, - uint32_t baudrate, uint8_t bits, uart_parity_t parity, uint8_t stop, + uint32_t baudrate, uint8_t bits, busio_uart_parity_t parity, uint8_t stop, mp_float_t timeout, uint16_t receiver_buffer_size, byte* receiver_buffer, bool sigint_enabled) { @@ -195,7 +195,7 @@ void common_hal_busio_uart_construct(busio_uart_obj_t *self, SERCOM_USART_CTRLA_FORM_Msk); sercom->USART.CTRLA.reg |= SERCOM_USART_CTRLA_TXPO(tx_pad / 2) | SERCOM_USART_CTRLA_RXPO(rx_pad) | - (parity == PARITY_NONE ? 0 : SERCOM_USART_CTRLA_FORM(1)); + (parity == BUSIO_UART_PARITY_NONE ? 0 : SERCOM_USART_CTRLA_FORM(1)); // Enable tx and/or rx based on whether the pins were specified. // CHSIZE is 0 for 8 bits, 5, 6, 7 for 5, 6, 7 bits. 1 for 9 bits, but we don't support that. @@ -206,7 +206,7 @@ void common_hal_busio_uart_construct(busio_uart_obj_t *self, SERCOM_USART_CTRLB_CHSIZE_Msk); sercom->USART.CTRLB.reg |= (have_tx ? SERCOM_USART_CTRLB_TXEN : 0) | (have_rx ? SERCOM_USART_CTRLB_RXEN : 0) | - (parity == PARITY_ODD ? SERCOM_USART_CTRLB_PMODE : 0) | + (parity == BUSIO_UART_PARITY_ODD ? SERCOM_USART_CTRLB_PMODE : 0) | (stop > 1 ? SERCOM_USART_CTRLB_SBMODE : 0) | SERCOM_USART_CTRLB_CHSIZE(bits % 8); diff --git a/ports/cxd56/common-hal/busio/UART.c b/ports/cxd56/common-hal/busio/UART.c index e455b0568..52d2afc0c 100644 --- a/ports/cxd56/common-hal/busio/UART.c +++ b/ports/cxd56/common-hal/busio/UART.c @@ -56,7 +56,7 @@ void common_hal_busio_uart_construct(busio_uart_obj_t *self, const mcu_pin_obj_t * tx, const mcu_pin_obj_t * rx, const mcu_pin_obj_t * rts, const mcu_pin_obj_t * cts, const mcu_pin_obj_t * rs485_dir, bool rs485_invert, - uint32_t baudrate, uint8_t bits, uart_parity_t parity, uint8_t stop, + uint32_t baudrate, uint8_t bits, busio_uart_parity_t parity, uint8_t stop, mp_float_t timeout, uint16_t receiver_buffer_size, byte* receiver_buffer, bool sigint_enabled) { struct termios tio; @@ -69,7 +69,7 @@ void common_hal_busio_uart_construct(busio_uart_obj_t *self, mp_raise_ValueError(translate("Could not initialize UART")); } - if (parity != PARITY_NONE) { + if (parity != BUSIO_UART_PARITY_NONE) { mp_raise_ValueError(translate("Could not initialize UART")); } diff --git a/ports/esp32s2/common-hal/busio/UART.c b/ports/esp32s2/common-hal/busio/UART.c index 4a016ad1a..d52e50cad 100644 --- a/ports/esp32s2/common-hal/busio/UART.c +++ b/ports/esp32s2/common-hal/busio/UART.c @@ -3,7 +3,7 @@ * * The MIT License (MIT) * - * Copyright (c) 2016 Damien P. George + * Copyright (c) 2020 Scott Shawcroft for Adafruit Industries LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -27,6 +27,8 @@ #include "shared-bindings/microcontroller/__init__.h" #include "shared-bindings/busio/UART.h" +#include "driver/uart.h" + #include "mpconfigport.h" #include "lib/utils/interrupt_char.h" #include "py/gc.h" @@ -36,208 +38,178 @@ #include "supervisor/shared/translate.h" #include "supervisor/shared/tick.h" -#define UART_DEBUG(...) (void)0 -// #define UART_DEBUG(...) mp_printf(&mp_plat_print __VA_OPT__(,) __VA_ARGS__) - -// Do-nothing callback needed so that usart_async code will enable rx interrupts. -// See comment below re usart_async_register_callback() -// static void usart_async_rxc_callback(const struct usart_async_descriptor *const descr) { -// // Nothing needs to be done by us. -// } - void uart_reset(void) { - + for (uart_port_t num = 0; num < UART_NUM_MAX; num++) { + // Ignore the UART used by the IDF. + #ifdef CONFIG_CONSOLE_UART_NUM + if (num == CONFIG_CONSOLE_UART_NUM) { + continue; + } + #endif + if (uart_is_driver_installed(num)) { + uart_driver_delete(num); + } + } } void common_hal_busio_uart_construct(busio_uart_obj_t *self, const mcu_pin_obj_t * tx, const mcu_pin_obj_t * rx, const mcu_pin_obj_t * rts, const mcu_pin_obj_t * cts, const mcu_pin_obj_t * rs485_dir, bool rs485_invert, - uint32_t baudrate, uint8_t bits, uart_parity_t parity, uint8_t stop, + uint32_t baudrate, uint8_t bits, busio_uart_parity_t parity, uint8_t stop, mp_float_t timeout, uint16_t receiver_buffer_size, byte* receiver_buffer, bool sigint_enabled) { - // uint8_t sercom_index = 255; // Unset index - // uint32_t rx_pinmux = 0; - // uint8_t rx_pad = 255; // Unset pad - // uint32_t tx_pinmux = 0; - // uint8_t tx_pad = 255; // Unset pad - - // if ((rts != NULL) || (cts != NULL) || (rs485_dir != NULL) || (rs485_invert)) { - // mp_raise_ValueError(translate("RTS/CTS/RS485 Not yet supported on this device")); - // } - - // if (bits > 8) { - // mp_raise_NotImplementedError(translate("bytes > 8 bits not supported")); - // } + if (bits > 8) { + mp_raise_NotImplementedError(translate("bytes > 8 bits not supported")); + } bool have_tx = tx != NULL; bool have_rx = rx != NULL; + bool have_rts = rts != NULL; + bool have_cts = cts != NULL; + bool have_rs485_dir = rs485_dir != NULL; if (!have_tx && !have_rx) { mp_raise_ValueError(translate("tx and rx cannot both be None")); } - self->baudrate = baudrate; - self->character_bits = bits; + // Filter for sane settings for RS485 + if (have_rs485_dir) { + if (have_rts || have_cts) { + mp_raise_ValueError(translate("Cannot specify RTS or CTS in RS485 mode")); + } + } else if (rs485_invert) { + mp_raise_ValueError(translate("RS485 inversion specified when not in RS485 mode")); + } + self->timeout_ms = timeout * 1000; - // This assignment is only here because the usart_async routines take a *const argument. -// struct usart_async_descriptor * const usart_desc_p = (struct usart_async_descriptor * const) &self->usart_desc; - -// for (int i = 0; i < NUM_SERCOMS_PER_PIN; i++) { -// Sercom* potential_sercom = NULL; -// if (have_tx) { -// sercom_index = tx->sercom[i].index; -// if (sercom_index >= SERCOM_INST_NUM) { -// continue; -// } -// potential_sercom = sercom_insts[sercom_index]; -// #ifdef SAMD21 -// if (potential_sercom->USART.CTRLA.bit.ENABLE != 0 || -// !(tx->sercom[i].pad == 0 || -// tx->sercom[i].pad == 2)) { -// continue; -// } -// #endif -// #ifdef SAMD51 -// if (potential_sercom->USART.CTRLA.bit.ENABLE != 0 || -// !(tx->sercom[i].pad == 0)) { -// continue; -// } -// #endif -// tx_pinmux = PINMUX(tx->number, (i == 0) ? MUX_C : MUX_D); -// tx_pad = tx->sercom[i].pad; -// if (rx == NULL) { -// sercom = potential_sercom; -// break; -// } -// } -// for (int j = 0; j < NUM_SERCOMS_PER_PIN; j++) { -// if (((!have_tx && rx->sercom[j].index < SERCOM_INST_NUM && -// sercom_insts[rx->sercom[j].index]->USART.CTRLA.bit.ENABLE == 0) || -// sercom_index == rx->sercom[j].index) && -// rx->sercom[j].pad != tx_pad) { -// rx_pinmux = PINMUX(rx->number, (j == 0) ? MUX_C : MUX_D); -// rx_pad = rx->sercom[j].pad; -// sercom = sercom_insts[rx->sercom[j].index]; -// sercom_index = rx->sercom[j].index; -// break; -// } -// } -// if (sercom != NULL) { -// break; -// } -// } - // if (sercom == NULL) { - // mp_raise_ValueError(translate("Invalid pins")); - // } - // if (!have_tx) { - // tx_pad = 0; - // if (rx_pad == 0) { - // tx_pad = 2; - // } - // } - // if (!have_rx) { - // rx_pad = (tx_pad + 1) % 4; - // } - - // // Set up clocks on SERCOM. - // samd_peripherals_sercom_clock_init(sercom, sercom_index); - - // if (rx && receiver_buffer_size > 0) { - // self->buffer_length = receiver_buffer_size; - // // Initially allocate the UART's buffer in the long-lived part of the - // // heap. UARTs are generally long-lived objects, but the "make long- - // // lived" machinery is incapable of moving internal pointers like - // // self->buffer, so do it manually. (However, as long as internal - // // pointers like this are NOT moved, allocating the buffer - // // in the long-lived pool is not strictly necessary) - // self->buffer = (uint8_t *) gc_alloc(self->buffer_length * sizeof(uint8_t), false, true); - // if (self->buffer == NULL) { - // common_hal_busio_uart_deinit(self); - // mp_raise_msg_varg(&mp_type_MemoryError, translate("Failed to allocate RX buffer of %d bytes"), self->buffer_length * sizeof(uint8_t)); - // } - // } else { - // self->buffer_length = 0; - // self->buffer = NULL; - // } - - // if (usart_async_init(usart_desc_p, sercom, self->buffer, self->buffer_length, NULL) != ERR_NONE) { - // mp_raise_ValueError(translate("Could not initialize UART")); - // } - - // usart_async_init() sets a number of defaults based on a prototypical SERCOM - // which don't necessarily match what we need. After calling it, set the values - // specific to this instantiation of UART. - - // Set pads computed for this SERCOM. - // TXPO: - // 0x0: TX pad 0; no RTS/CTS - // 0x1: TX pad 2; no RTS/CTS - // 0x2: TX pad 0; RTS: pad 2, CTS: pad 3 (not used by us right now) - // So divide by 2 to map pad to value. - // RXPO: - // 0x0: RX pad 0 - // 0x1: RX pad 1 - // 0x2: RX pad 2 - // 0x3: RX pad 3 - - // Doing a group mask and set of the registers saves 60 bytes over setting the bitfields individually. - - // sercom->USART.CTRLA.reg &= ~(SERCOM_USART_CTRLA_TXPO_Msk | - // SERCOM_USART_CTRLA_RXPO_Msk | - // SERCOM_USART_CTRLA_FORM_Msk); - // sercom->USART.CTRLA.reg |= SERCOM_USART_CTRLA_TXPO(tx_pad / 2) | - // SERCOM_USART_CTRLA_RXPO(rx_pad) | - // (parity == PARITY_NONE ? 0 : SERCOM_USART_CTRLA_FORM(1)); - - // Enable tx and/or rx based on whether the pins were specified. - // CHSIZE is 0 for 8 bits, 5, 6, 7 for 5, 6, 7 bits. 1 for 9 bits, but we don't support that. - // sercom->USART.CTRLB.reg &= ~(SERCOM_USART_CTRLB_TXEN | - // SERCOM_USART_CTRLB_RXEN | - // SERCOM_USART_CTRLB_PMODE | - // SERCOM_USART_CTRLB_SBMODE | - // SERCOM_USART_CTRLB_CHSIZE_Msk); - // sercom->USART.CTRLB.reg |= (have_tx ? SERCOM_USART_CTRLB_TXEN : 0) | - // (have_rx ? SERCOM_USART_CTRLB_RXEN : 0) | - // (parity == PARITY_ODD ? SERCOM_USART_CTRLB_PMODE : 0) | - // (stop > 1 ? SERCOM_USART_CTRLB_SBMODE : 0) | - // SERCOM_USART_CTRLB_CHSIZE(bits % 8); + self->uart_num = UART_NUM_MAX; + for (uart_port_t num = 0; num < UART_NUM_MAX; num++) { + if (!uart_is_driver_installed(num)) { + self->uart_num = num; + } + } + if (self->uart_num == UART_NUM_MAX) { + mp_raise_ValueError(translate("All UART peripherals are in use")); + } + + uart_mode_t mode = UART_MODE_UART; + uart_hw_flowcontrol_t flow_control = UART_HW_FLOWCTRL_DISABLE; + if (have_rs485_dir) { + mode = UART_MODE_RS485_HALF_DUPLEX; + if (!rs485_invert) { + uart_set_line_inverse(self->uart_num, UART_SIGNAL_DTR_INV); + } + } else if (have_rts && have_cts) { + flow_control = UART_HW_FLOWCTRL_CTS_RTS; + } else if (have_rts) { + flow_control = UART_HW_FLOWCTRL_RTS; + } else if (have_rts) { + flow_control = UART_HW_FLOWCTRL_CTS; + } + + if (receiver_buffer_size <= UART_FIFO_LEN) { + receiver_buffer_size = UART_FIFO_LEN + 8; + } + + uint8_t rx_threshold = UART_FIFO_LEN - 8; + // Install the driver before we change the settings. + if (uart_driver_install(self->uart_num, receiver_buffer_size, 0, 0, NULL, 0) != ESP_OK || + uart_set_mode(self->uart_num, mode) != ESP_OK) { + mp_raise_ValueError(translate("Could not initialize UART")); + } + uart_set_hw_flow_ctrl(self->uart_num, flow_control, rx_threshold); // Set baud rate - // common_hal_busio_uart_set_baudrate(self, baudrate); - - // Turn on rx interrupt handling. The UART async driver has its own set of internal callbacks, - // which are set up by uart_async_init(). These in turn can call user-specified callbacks. - // In fact, the actual interrupts are not enabled unless we set up a user-specified callback. - // This is confusing. It's explained in the Atmel START User Guide -> Implementation Description -> - // Different read function behavior in some asynchronous drivers. As of this writing: - // http://start.atmel.com/static/help/index.html?GUID-79201A5A-226F-4FBB-B0B8-AB0BE0554836 - // Look at the ASFv4 code example for async USART. - // usart_async_register_callback(usart_desc_p, USART_ASYNC_RXC_CB, usart_async_rxc_callback); - - - // if (have_tx) { - // gpio_set_pin_direction(tx->number, GPIO_DIRECTION_OUT); - // gpio_set_pin_pull_mode(tx->number, GPIO_PULL_OFF); - // gpio_set_pin_function(tx->number, tx_pinmux); - // self->tx_pin = tx->number; - // claim_pin(tx); - // } else { - // self->tx_pin = NO_PIN; - // } - - // if (have_rx) { - // gpio_set_pin_direction(rx->number, GPIO_DIRECTION_IN); - // gpio_set_pin_pull_mode(rx->number, GPIO_PULL_OFF); - // gpio_set_pin_function(rx->number, rx_pinmux); - // self->rx_pin = rx->number; - // claim_pin(rx); - // } else { - // self->rx_pin = NO_PIN; - // } - - // usart_async_enable(usart_desc_p); + common_hal_busio_uart_set_baudrate(self, baudrate); + + uart_word_length_t word_length = UART_DATA_8_BITS; + switch (bits) { + // Shared bindings prevents data < 7 bits. + // case 5: + // word_length = UART_DATA_5_BITS; + // break; + // case 6: + // word_length = UART_DATA_6_BITS; + // break; + case 7: + word_length = UART_DATA_7_BITS; + break; + case 8: + word_length = UART_DATA_8_BITS; + break; + default: + // Won't hit this because shared-bindings limits to 7-9 bits. We error on 9 above. + break; + } + uart_set_word_length(self->uart_num, word_length); + + uart_parity_t parity_mode = UART_PARITY_DISABLE; + switch (parity) { + case BUSIO_UART_PARITY_NONE: + parity_mode = UART_PARITY_DISABLE; + break; + case BUSIO_UART_PARITY_EVEN: + parity_mode = UART_PARITY_EVEN; + break; + case BUSIO_UART_PARITY_ODD: + parity_mode = UART_PARITY_ODD; + break; + default: + // Won't reach here because the input is an enum that is completely handled. + break; + } + uart_set_parity(self->uart_num, parity_mode); + + // Stop is 1 or 2 always. + uart_stop_bits_t stop_bits= UART_STOP_BITS_1; + if (stop == 2) { + stop_bits = UART_STOP_BITS_2; + } + uart_set_stop_bits(self->uart_num, stop_bits); + + self->tx_pin = NULL; + self->rx_pin = NULL; + self->rts_pin = NULL; + self->cts_pin = NULL; + int tx_num = -1; + int rx_num = -1; + int rts_num = -1; + int cts_num = -1; + if (have_tx) { + claim_pin(tx); + self->tx_pin = tx; + tx_num = tx->number; + } + + if (have_rx) { + claim_pin(rx); + self->rx_pin = rx; + rx_num = rx->number; + } + + if (have_rts) { + claim_pin(rts); + self->rts_pin = rts; + rts_num = rts->number; + } + + if (have_cts) { + claim_pin(cts); + self->cts_pin = cts; + cts_num = cts->number; + } + + if (have_rs485_dir) { + claim_pin(rs485_dir); + // RTS is used for RS485 direction. + self->rts_pin = rs485_dir; + rts_num = rs485_dir->number; + } + if (uart_set_pin(self->uart_num, tx_num, rx_num, rts_num, cts_num) != ESP_OK) { + mp_raise_ValueError(translate("Invalid pins")); + } } bool common_hal_busio_uart_deinited(busio_uart_obj_t *self) { @@ -248,14 +220,16 @@ void common_hal_busio_uart_deinit(busio_uart_obj_t *self) { if (common_hal_busio_uart_deinited(self)) { return; } - // // This assignment is only here because the usart_async routines take a *const argument. - // struct usart_async_descriptor * const usart_desc_p = (struct usart_async_descriptor * const) &self->usart_desc; - // usart_async_disable(usart_desc_p); - // usart_async_deinit(usart_desc_p); + uart_driver_delete(self->uart_num); + reset_pin(self->rx_pin); reset_pin(self->tx_pin); + reset_pin(self->rts_pin); + reset_pin(self->cts_pin); self->rx_pin = NULL; self->tx_pin = NULL; + self->cts_pin = NULL; + self->rts_pin = NULL; } // Read characters. @@ -263,57 +237,49 @@ size_t common_hal_busio_uart_read(busio_uart_obj_t *self, uint8_t *data, size_t if (self->rx_pin == NULL) { mp_raise_ValueError(translate("No RX pin")); } - - // This assignment is only here because the usart_async routines take a *const argument. - // struct usart_async_descriptor * const usart_desc_p = (struct usart_async_descriptor * const) &self->usart_desc; - if (len == 0) { // Nothing to read. return 0; } - // struct io_descriptor *io; - // usart_async_get_io_descriptor(usart_desc_p, &io); - - // size_t total_read = 0; - // uint64_t start_ticks = supervisor_ticks_ms64(); + size_t total_read = 0; + uint64_t start_ticks = supervisor_ticks_ms64(); // Busy-wait until timeout or until we've read enough chars. - // while (supervisor_ticks_ms64() - start_ticks <= self->timeout_ms) { - // // Read as many chars as we can right now, up to len. - // size_t num_read = io_read(io, data, len); - - // // Advance pointer in data buffer, and decrease how many chars left to read. - // data += num_read; - // len -= num_read; - // total_read += num_read; - // if (len == 0) { - // // Don't need to read any more: data buf is full. - // break; - // } - // if (num_read > 0) { - // // Reset the timeout on every character read. - // start_ticks = supervisor_ticks_ms64(); - // } - // RUN_BACKGROUND_TASKS; - // // Allow user to break out of a timeout with a KeyboardInterrupt. - // if (mp_hal_is_interrupted()) { - // break; - // } - // // If we are zero timeout, make sure we don't loop again (in the event - // // we read in under 1ms) - // if (self->timeout_ms == 0) { - // break; - // } - // } - - // if (total_read == 0) { - // *errcode = EAGAIN; - // return MP_STREAM_ERROR; - // } - - // return total_read; - return 0; + while (supervisor_ticks_ms64() - start_ticks <= self->timeout_ms) { + // Read as many chars as we can right now, up to len. + size_t num_read = uart_read_bytes(self->uart_num, data, len, 0); + + // Advance pointer in data buffer, and decrease how many chars left to read. + data += num_read; + len -= num_read; + total_read += num_read; + if (len == 0) { + // Don't need to read any more: data buf is full. + break; + } + if (num_read > 0) { + // Reset the timeout on every character read. + start_ticks = supervisor_ticks_ms64(); + } + RUN_BACKGROUND_TASKS; + // Allow user to break out of a timeout with a KeyboardInterrupt. + if (mp_hal_is_interrupted()) { + break; + } + // If we are zero timeout, make sure we don't loop again (in the event + // we read in under 1ms) + if (self->timeout_ms == 0) { + break; + } + } + + if (total_read == 0) { + *errcode = EAGAIN; + return MP_STREAM_ERROR; + } + + return total_read; } // Write characters. @@ -322,49 +288,34 @@ size_t common_hal_busio_uart_write(busio_uart_obj_t *self, const uint8_t *data, mp_raise_ValueError(translate("No TX pin")); } - // This assignment is only here because the usart_async routines take a *const argument. - // struct usart_async_descriptor * const usart_desc_p = (struct usart_async_descriptor * const) &self->usart_desc; - - // struct io_descriptor *io; - // usart_async_get_io_descriptor(usart_desc_p, &io); - - // // Start writing characters. This is non-blocking and will - // // return immediately after setting up the write. - // if (io_write(io, data, len) < 0) { - // *errcode = MP_EAGAIN; - // return MP_STREAM_ERROR; - // } - - // // Busy-wait until all characters transmitted. - // struct usart_async_status async_status; - // while (true) { - // usart_async_get_status(usart_desc_p, &async_status); - // if (async_status.txcnt >= len) { - // break; - // } - // RUN_BACKGROUND_TASKS; - // } + while (len > 0) { + int count = uart_tx_chars(self->uart_num, (const char*) data, len); + if (count < 0) { + *errcode = MP_EAGAIN; + return MP_STREAM_ERROR; + } + len -= count; + data += count; + RUN_BACKGROUND_TASKS; + } + while (uart_wait_tx_done(self->uart_num, 0) == ESP_ERR_TIMEOUT) { + RUN_BACKGROUND_TASKS; + } return len; } uint32_t common_hal_busio_uart_get_baudrate(busio_uart_obj_t *self) { - return self->baudrate; + uint32_t baudrate; + uart_get_baudrate(self->uart_num, &baudrate); + return baudrate; } void common_hal_busio_uart_set_baudrate(busio_uart_obj_t *self, uint32_t baudrate) { - // This assignment is only here because the usart_async routines take a *const argument. - // struct usart_async_descriptor * const usart_desc_p = (struct usart_async_descriptor * const) &self->usart_desc; - // usart_async_set_baud_rate(usart_desc_p, - // // Samples and ARITHMETIC vs FRACTIONAL must correspond to USART_SAMPR in - // // hpl_sercom_config.h. - // _usart_async_calculate_baud_rate(baudrate, // e.g. 9600 baud - // PROTOTYPE_SERCOM_USART_ASYNC_CLOCK_FREQUENCY, - // 16, // samples - // USART_BAUDRATE_ASYNCH_ARITHMETIC, - // 0 // fraction - not used for ARITHMETIC - // )); - self->baudrate = baudrate; + if (baudrate > UART_BITRATE_MAX || + uart_set_baudrate(self->uart_num, baudrate) != ESP_OK) { + mp_raise_ValueError(translate("Unsupported baudrate")); + } } mp_float_t common_hal_busio_uart_get_timeout(busio_uart_obj_t *self) { @@ -376,19 +327,13 @@ void common_hal_busio_uart_set_timeout(busio_uart_obj_t *self, mp_float_t timeou } uint32_t common_hal_busio_uart_rx_characters_available(busio_uart_obj_t *self) { - // This assignment is only here because the usart_async routines take a *const argument. - // struct usart_async_descriptor * const usart_desc_p = (struct usart_async_descriptor * const) &self->usart_desc; - // struct usart_async_status async_status; - // usart_async_get_status(usart_desc_p, &async_status); - // return async_status.rxcnt; - return 0; + size_t count; + uart_get_buffered_data_len(self->uart_num, &count); + return count; } void common_hal_busio_uart_clear_rx_buffer(busio_uart_obj_t *self) { - // This assignment is only here because the usart_async routines take a *const argument. - // struct usart_async_descriptor * const usart_desc_p = (struct usart_async_descriptor * const) &self->usart_desc; - // usart_async_flush_rx_buffer(usart_desc_p); - + uart_flush(self->uart_num); } // True if there are no characters still to be written. @@ -396,10 +341,5 @@ bool common_hal_busio_uart_ready_to_tx(busio_uart_obj_t *self) { if (self->tx_pin == NULL) { return false; } - // This assignment is only here because the usart_async routines take a *const argument. - // struct usart_async_descriptor * const usart_desc_p = (struct usart_async_descriptor * const) &self->usart_desc; - // struct usart_async_status async_status; - // usart_async_get_status(usart_desc_p, &async_status); - // return !(async_status.flags & USART_ASYNC_STATUS_BUSY); - return false; + return uart_wait_tx_done(self->uart_num, 0) != ESP_ERR_TIMEOUT; } diff --git a/ports/esp32s2/common-hal/busio/UART.h b/ports/esp32s2/common-hal/busio/UART.h index 3c2bd1dac..b3cc92966 100644 --- a/ports/esp32s2/common-hal/busio/UART.h +++ b/ports/esp32s2/common-hal/busio/UART.h @@ -29,18 +29,19 @@ #include "common-hal/microcontroller/Pin.h" +#include "esp-idf/components/soc/include/hal/uart_types.h" #include "py/obj.h" typedef struct { mp_obj_base_t base; const mcu_pin_obj_t* rx_pin; const mcu_pin_obj_t* tx_pin; + const mcu_pin_obj_t* rts_pin; + const mcu_pin_obj_t* cts_pin; + uart_port_t uart_num; uint8_t character_bits; bool rx_error; - uint32_t baudrate; uint32_t timeout_ms; - uint32_t buffer_length; - uint8_t* buffer; } busio_uart_obj_t; void uart_reset(void); diff --git a/ports/esp32s2/esp-idf b/ports/esp32s2/esp-idf index 7aae7f034..648f95903 160000 --- a/ports/esp32s2/esp-idf +++ b/ports/esp32s2/esp-idf @@ -1 +1 @@ -Subproject commit 7aae7f034bab68d2dd6aaa763924c91eb697d87e +Subproject commit 648f959037896cff887a05b67105748790bfe63a diff --git a/ports/mimxrt10xx/common-hal/busio/UART.c b/ports/mimxrt10xx/common-hal/busio/UART.c index 5ef347e59..4ca2f8e49 100644 --- a/ports/mimxrt10xx/common-hal/busio/UART.c +++ b/ports/mimxrt10xx/common-hal/busio/UART.c @@ -85,7 +85,7 @@ void common_hal_busio_uart_construct(busio_uart_obj_t *self, const mcu_pin_obj_t * tx, const mcu_pin_obj_t * rx, const mcu_pin_obj_t * rts, const mcu_pin_obj_t * cts, const mcu_pin_obj_t * rs485_dir, bool rs485_invert, - uint32_t baudrate, uint8_t bits, uart_parity_t parity, uint8_t stop, + uint32_t baudrate, uint8_t bits, busio_uart_parity_t parity, uint8_t stop, mp_float_t timeout, uint16_t receiver_buffer_size, byte* receiver_buffer, bool sigint_enabled) { @@ -94,7 +94,7 @@ void common_hal_busio_uart_construct(busio_uart_obj_t *self, self->timeout_ms = timeout * 1000; // We are transmitting one direction if one pin is NULL and the other isn't. - bool is_onedirection = (rx != NULL) != (tx != NULL); + bool is_onedirection = (rx == NULL) != (tx == NULL); bool uart_taken = false; const uint32_t rx_count = MP_ARRAY_SIZE(mcu_uart_rx_list); diff --git a/ports/nrf/common-hal/busio/UART.c b/ports/nrf/common-hal/busio/UART.c index 89f3c9f32..012ebc3b5 100644 --- a/ports/nrf/common-hal/busio/UART.c +++ b/ports/nrf/common-hal/busio/UART.c @@ -133,7 +133,7 @@ void common_hal_busio_uart_construct(busio_uart_obj_t *self, const mcu_pin_obj_t * tx, const mcu_pin_obj_t * rx, const mcu_pin_obj_t * rts, const mcu_pin_obj_t * cts, const mcu_pin_obj_t * rs485_dir, bool rs485_invert, - uint32_t baudrate, uint8_t bits, uart_parity_t parity, uint8_t stop, + uint32_t baudrate, uint8_t bits, busio_uart_parity_t parity, uint8_t stop, mp_float_t timeout, uint16_t receiver_buffer_size, byte* receiver_buffer, bool sigint_enabled) { @@ -162,7 +162,7 @@ void common_hal_busio_uart_construct(busio_uart_obj_t *self, mp_raise_ValueError(translate("Invalid buffer size")); } - if ( parity == PARITY_ODD ) { + if ( parity == BUSIO_UART_PARITY_ODD ) { mp_raise_ValueError(translate("Odd parity is not supported")); } @@ -176,7 +176,7 @@ void common_hal_busio_uart_construct(busio_uart_obj_t *self, .interrupt_priority = 7, .hal_cfg = { .hwfc = NRF_UARTE_HWFC_DISABLED, - .parity = (parity == PARITY_NONE) ? NRF_UARTE_PARITY_EXCLUDED : NRF_UARTE_PARITY_INCLUDED + .parity = (parity == BUSIO_UART_PARITY_NONE) ? NRF_UARTE_PARITY_EXCLUDED : NRF_UARTE_PARITY_INCLUDED } }; diff --git a/ports/stm/common-hal/busio/UART.c b/ports/stm/common-hal/busio/UART.c index 86a932290..7450f9897 100644 --- a/ports/stm/common-hal/busio/UART.c +++ b/ports/stm/common-hal/busio/UART.c @@ -79,7 +79,7 @@ void common_hal_busio_uart_construct(busio_uart_obj_t *self, const mcu_pin_obj_t * tx, const mcu_pin_obj_t * rx, const mcu_pin_obj_t * rts, const mcu_pin_obj_t * cts, const mcu_pin_obj_t * rs485_dir, bool rs485_invert, - uint32_t baudrate, uint8_t bits, uart_parity_t parity, uint8_t stop, + uint32_t baudrate, uint8_t bits, busio_uart_parity_t parity, uint8_t stop, mp_float_t timeout, uint16_t receiver_buffer_size, byte* receiver_buffer, bool sigint_enabled) { @@ -201,8 +201,8 @@ void common_hal_busio_uart_construct(busio_uart_obj_t *self, self->handle.Init.BaudRate = baudrate; self->handle.Init.WordLength = (bits == 9) ? UART_WORDLENGTH_9B : UART_WORDLENGTH_8B; self->handle.Init.StopBits = (stop > 1) ? UART_STOPBITS_2 : UART_STOPBITS_1; - self->handle.Init.Parity = (parity == PARITY_ODD) ? UART_PARITY_ODD : - (parity == PARITY_EVEN) ? UART_PARITY_EVEN : + self->handle.Init.Parity = (parity == BUSIO_UART_PARITY_ODD) ? UART_PARITY_ODD : + (parity == BUSIO_UART_PARITY_EVEN) ? UART_PARITY_EVEN : UART_PARITY_NONE; self->handle.Init.Mode = (self->tx != NULL && self->rx != NULL) ? UART_MODE_TX_RX : (self->tx != NULL) ? UART_MODE_TX : diff --git a/shared-bindings/busio/UART.c b/shared-bindings/busio/UART.c index 8c0811266..018ded182 100644 --- a/shared-bindings/busio/UART.c +++ b/shared-bindings/busio/UART.c @@ -52,8 +52,8 @@ //| :param ~microcontroller.Pin rx: the pin to receive on, or ``None`` if this ``UART`` is transmit-only. //| :param ~microcontroller.Pin rts: the pin for rts, or ``None`` if rts not in use. //| :param ~microcontroller.Pin cts: the pin for cts, or ``None`` if cts not in use. -//| :param ~microcontroller.Pin rs485_dir: the pin for rs485 direction setting, or ``None`` if rs485 not in use. -//| :param bool rs485_invert: set to invert the sense of the rs485_dir pin. +//| :param ~microcontroller.Pin rs485_dir: the output pin for rs485 direction setting, or ``None`` if rs485 not in use. +//| :param bool rs485_invert: rs485_dir pin active high when set. Active low otherwise. //| :param int baudrate: the transmit and receive speed. //| :param int bits: the number of bits per byte, 7, 8 or 9. //| :param Parity parity: the parity used for error checking. @@ -87,8 +87,8 @@ STATIC mp_obj_t busio_uart_make_new(const mp_obj_type_t *type, size_t n_args, co enum { ARG_tx, ARG_rx, ARG_baudrate, ARG_bits, ARG_parity, ARG_stop, ARG_timeout, ARG_receiver_buffer_size, ARG_rts, ARG_cts, ARG_rs485_dir,ARG_rs485_invert}; static const mp_arg_t allowed_args[] = { - { MP_QSTR_tx, MP_ARG_REQUIRED | MP_ARG_OBJ }, - { MP_QSTR_rx, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_tx, MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_rx, MP_ARG_OBJ, {.u_obj = mp_const_none} }, { MP_QSTR_baudrate, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 9600} }, { MP_QSTR_bits, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 8} }, { MP_QSTR_parity, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, @@ -115,11 +115,11 @@ STATIC mp_obj_t busio_uart_make_new(const mp_obj_type_t *type, size_t n_args, co mp_raise_ValueError(translate("bits must be 7, 8 or 9")); } - uart_parity_t parity = PARITY_NONE; + busio_uart_parity_t parity = BUSIO_UART_PARITY_NONE; if (args[ARG_parity].u_obj == &busio_uart_parity_even_obj) { - parity = PARITY_EVEN; + parity = BUSIO_UART_PARITY_EVEN; } else if (args[ARG_parity].u_obj == &busio_uart_parity_odd_obj) { - parity = PARITY_ODD; + parity = BUSIO_UART_PARITY_ODD; } uint8_t stop = args[ARG_stop].u_int; diff --git a/shared-bindings/busio/UART.h b/shared-bindings/busio/UART.h index 3aed4e534..ce8da1445 100644 --- a/shared-bindings/busio/UART.h +++ b/shared-bindings/busio/UART.h @@ -34,17 +34,17 @@ extern const mp_obj_type_t busio_uart_type; typedef enum { - PARITY_NONE, - PARITY_EVEN, - PARITY_ODD -} uart_parity_t; + BUSIO_UART_PARITY_NONE, + BUSIO_UART_PARITY_EVEN, + BUSIO_UART_PARITY_ODD +} busio_uart_parity_t; // Construct an underlying UART object. extern void common_hal_busio_uart_construct(busio_uart_obj_t *self, const mcu_pin_obj_t * tx, const mcu_pin_obj_t * rx, const mcu_pin_obj_t * rts, const mcu_pin_obj_t * cts, const mcu_pin_obj_t * rs485_dir, bool rs485_invert, - uint32_t baudrate, uint8_t bits, uart_parity_t parity, uint8_t stop, + uint32_t baudrate, uint8_t bits, busio_uart_parity_t parity, uint8_t stop, mp_float_t timeout, uint16_t receiver_buffer_size, byte* receiver_buffer, bool sigint_enabled); diff --git a/shared-module/board/__init__.c b/shared-module/board/__init__.c index 0fbf916cd..2c9139d8a 100644 --- a/shared-module/board/__init__.c +++ b/shared-module/board/__init__.c @@ -123,7 +123,7 @@ mp_obj_t common_hal_board_create_uart(void) { #endif common_hal_busio_uart_construct(self, tx, rx, rts, cts, rs485_dir, rs485_invert, - 9600, 8, PARITY_NONE, 1, 1.0f, 64, NULL, false); + 9600, 8, BUSIO_UART_PARITY_NONE, 1, 1.0f, 64, NULL, false); MP_STATE_VM(shared_uart_bus) = MP_OBJ_FROM_PTR(self); return MP_STATE_VM(shared_uart_bus); } -- cgit v1.2.3 From ed6e81d688469138b555f3579b1083f521485382 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Wed, 24 Jun 2020 12:46:36 -0700 Subject: Switch SPI to polling DMA and enable displayio --- ports/esp32s2/background.c | 6 +- .../boards/espressif_saola_1_wrover/board.c | 8 +-- ports/esp32s2/common-hal/busio/I2C.c | 5 +- ports/esp32s2/common-hal/busio/SPI.c | 82 +++++++++++++--------- ports/esp32s2/common-hal/busio/SPI.h | 4 ++ ports/esp32s2/common-hal/displayio/ParallelBus.c | 66 +++++++++++++++++ ports/esp32s2/common-hal/displayio/ParallelBus.h | 36 ++++++++++ ports/esp32s2/common-hal/microcontroller/Pin.c | 2 +- ports/esp32s2/common-hal/microcontroller/Pin.h | 1 - ports/esp32s2/modules/wroom.c | 12 ++-- ports/esp32s2/modules/wrover.c | 14 ++-- ports/esp32s2/mpconfigport.mk | 2 +- shared-module/displayio/Display.h | 4 ++ shared-module/displayio/EPaperDisplay.h | 1 - 14 files changed, 183 insertions(+), 60 deletions(-) create mode 100644 ports/esp32s2/common-hal/displayio/ParallelBus.c create mode 100644 ports/esp32s2/common-hal/displayio/ParallelBus.h (limited to 'shared-module') diff --git a/ports/esp32s2/background.c b/ports/esp32s2/background.c index e22cf4aac..e979d7831 100644 --- a/ports/esp32s2/background.c +++ b/ports/esp32s2/background.c @@ -54,9 +54,9 @@ void run_background_tasks(void) { running_background_tasks = true; filesystem_background(); - // #if CIRCUITPY_DISPLAYIO - // displayio_background(); - // #endif + #if CIRCUITPY_DISPLAYIO + displayio_background(); + #endif running_background_tasks = false; assert_heap_ok(); diff --git a/ports/esp32s2/boards/espressif_saola_1_wrover/board.c b/ports/esp32s2/boards/espressif_saola_1_wrover/board.c index b7b2c4ef5..9f708874b 100644 --- a/ports/esp32s2/boards/espressif_saola_1_wrover/board.c +++ b/ports/esp32s2/boards/espressif_saola_1_wrover/board.c @@ -30,12 +30,12 @@ void board_init(void) { // USB - never_reset_pin(&pin_GPIO19); - never_reset_pin(&pin_GPIO20); + common_hal_never_reset_pin(&pin_GPIO19); + common_hal_never_reset_pin(&pin_GPIO20); // Debug UART - never_reset_pin(&pin_GPIO43); - never_reset_pin(&pin_GPIO44); + common_hal_never_reset_pin(&pin_GPIO43); + common_hal_never_reset_pin(&pin_GPIO44); } bool board_requests_safe_mode(void) { diff --git a/ports/esp32s2/common-hal/busio/I2C.c b/ports/esp32s2/common-hal/busio/I2C.c index 51817c95e..391d7323c 100644 --- a/ports/esp32s2/common-hal/busio/I2C.c +++ b/ports/esp32s2/common-hal/busio/I2C.c @@ -31,6 +31,7 @@ #include "driver/i2c.h" #include "shared-bindings/microcontroller/__init__.h" +#include "shared-bindings/microcontroller/Pin.h" #include "supervisor/shared/translate.h" typedef enum { @@ -217,6 +218,6 @@ uint8_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t addr, void common_hal_busio_i2c_never_reset(busio_i2c_obj_t *self) { never_reset_i2c(self->i2c_num); - never_reset_pin(self->scl_pin); - never_reset_pin(self->sda_pin); + common_hal_never_reset_pin(self->scl_pin); + common_hal_never_reset_pin(self->sda_pin); } diff --git a/ports/esp32s2/common-hal/busio/SPI.c b/ports/esp32s2/common-hal/busio/SPI.c index 9790c516f..686f44cbc 100644 --- a/ports/esp32s2/common-hal/busio/SPI.c +++ b/ports/esp32s2/common-hal/busio/SPI.c @@ -29,7 +29,7 @@ #include "py/runtime.h" #include "boards/board.h" -#include "common-hal/microcontroller/Pin.h" +#include "shared-bindings/microcontroller/Pin.h" #include "supervisor/shared/rgb_led_status.h" #include "esp_log.h" @@ -101,7 +101,8 @@ static bool spi_bus_is_free(spi_host_device_t host_id) { } static void spi_interrupt_handler(void *arg) { - // busio_spi_obj_t *self = arg; + busio_spi_obj_t *self = arg; + ESP_LOGE(TAG, "SPI interrupt %p", self); } // The interrupt may get invoked by the bus lock. @@ -148,7 +149,7 @@ void common_hal_busio_spi_construct(busio_spi_obj_t *self, mp_raise_ValueError(translate("All SPI peripherals are in use")); } - esp_err_t result = spi_bus_initialize(host_id, &bus_config, 0 /* dma channel */); + esp_err_t result = spi_bus_initialize(host_id, &bus_config, host_id /* dma channel */); if (result == ESP_ERR_NO_MEM) { mp_raise_msg(&mp_type_MemoryError, translate("ESP-IDF memory allocation failed")); } else if (result == ESP_ERR_INVALID_ARG) { @@ -183,34 +184,35 @@ void common_hal_busio_spi_construct(busio_spi_obj_t *self, spi_bus_lock_set_bg_control(spi_bus_get_attr(host_id)->lock, spi_bus_intr_enable, spi_bus_intr_disable, self); spi_hal_context_t* hal = &self->hal_context; - hal->hw = NULL; // Set by spi_hal_init - hal->dmadesc_tx = NULL; - hal->dmadesc_rx = NULL; - hal->dmadesc_n = 0; + + // spi_hal_init clears the given hal context so set everything after. + spi_hal_init(hal, host_id); + hal->dmadesc_tx = &self->tx_dma; + hal->dmadesc_rx = &self->rx_dma; + hal->dmadesc_n = 1; // We don't use native CS. - hal->cs_setup = 0; - hal->cs_hold = 0; - hal->cs_pin_id = 0; + // hal->cs_setup = 0; + // hal->cs_hold = 0; + // hal->cs_pin_id = 0; hal->sio = 1; - hal->half_duplex = 0; - hal->tx_lsbfirst = 0; - hal->rx_lsbfirst = 0; - hal->dma_enabled = 0; + // hal->half_duplex = 0; + // hal->tx_lsbfirst = 0; + // hal->rx_lsbfirst = 0; + hal->dma_enabled = 1; hal->no_compensate = 1; // Ignore CS bits // We don't use cmd, addr or dummy bits. - hal->cmd = 0; - hal->cmd_bits = 0; - hal->addr_bits = 0; - hal->dummy_bits = 0; - hal->addr = 0; + // hal->cmd = 0; + // hal->cmd_bits = 0; + // hal->addr_bits = 0; + // hal->dummy_bits = 0; + // hal->addr = 0; hal->io_mode = SPI_LL_IO_MODE_NORMAL; - spi_hal_init(hal, host_id); // This must be set after spi_hal_init. hal->timing_conf = &self->timing_conf; if (hal->hw == NULL) { @@ -223,9 +225,9 @@ void common_hal_busio_spi_construct(busio_spi_obj_t *self, void common_hal_busio_spi_never_reset(busio_spi_obj_t *self) { spi_never_reset[self->host_id] = true; - never_reset_pin(self->clock_pin); - never_reset_pin(self->MOSI_pin); - never_reset_pin(self->MISO_pin); + common_hal_never_reset_pin(self->clock_pin); + common_hal_never_reset_pin(self->MOSI_pin); + common_hal_never_reset_pin(self->MISO_pin); } bool common_hal_busio_spi_deinited(busio_spi_obj_t *self) { @@ -322,17 +324,29 @@ bool common_hal_busio_spi_transfer(busio_spi_obj_t *self, const uint8_t *data_ou } spi_hal_context_t* hal = &self->hal_context; - hal->tx_bitlen = len * self->bits; - hal->rx_bitlen = len * self->bits; - hal->send_buffer = (uint8_t*) data_out; - hal->rcv_buffer = data_in; - - spi_hal_setup_trans(hal); - spi_hal_prepare_data(hal); - spi_hal_user_start(hal); - if (len >= SOC_SPI_MAXIMUM_BUFFER_SIZE && false) { - // Set up the interrupt and wait on the lock. - } else { + hal->send_buffer = NULL; + hal->rcv_buffer = NULL; + // This rounds up. + size_t dma_count = (len + LLDESC_MAX_NUM_PER_DESC - 1) / LLDESC_MAX_NUM_PER_DESC; + for (size_t i = 0; i < dma_count; i++) { + size_t offset = LLDESC_MAX_NUM_PER_DESC * i; + size_t dma_len = len - offset; + if (dma_len > LLDESC_MAX_NUM_PER_DESC) { + dma_len = LLDESC_MAX_NUM_PER_DESC; + } + hal->tx_bitlen = dma_len * self->bits; + hal->rx_bitlen = dma_len * self->bits; + if (data_out != NULL) { + hal->send_buffer = (uint8_t*) data_out + offset; + } + if (data_in != NULL) { + hal->rcv_buffer = data_in + offset; + } + + spi_hal_setup_trans(hal); + spi_hal_prepare_data(hal); + spi_hal_user_start(hal); + // TODO: Switch to waiting on a lock that is given by an interrupt. while (!spi_hal_usr_is_done(hal)) { RUN_BACKGROUND_TASKS; } diff --git a/ports/esp32s2/common-hal/busio/SPI.h b/ports/esp32s2/common-hal/busio/SPI.h index 38fbe42ff..6d8203831 100644 --- a/ports/esp32s2/common-hal/busio/SPI.h +++ b/ports/esp32s2/common-hal/busio/SPI.h @@ -44,6 +44,10 @@ typedef struct { spi_hal_context_t hal_context; spi_hal_timing_conf_t timing_conf; intr_handle_t interrupt; + // IDF allocates these in DMA accessible memory so they may need to move when + // we use external RAM for CircuitPython. + lldesc_t tx_dma; + lldesc_t rx_dma; uint32_t target_frequency; int32_t real_frequency; uint8_t polarity; diff --git a/ports/esp32s2/common-hal/displayio/ParallelBus.c b/ports/esp32s2/common-hal/displayio/ParallelBus.c new file mode 100644 index 000000000..314b72ff7 --- /dev/null +++ b/ports/esp32s2/common-hal/displayio/ParallelBus.c @@ -0,0 +1,66 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Lucian Copeland 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/displayio/ParallelBus.h" + +#include + +#include "common-hal/microcontroller/Pin.h" +#include "py/runtime.h" +#include "shared-bindings/digitalio/DigitalInOut.h" +#include "shared-bindings/microcontroller/__init__.h" + +void common_hal_displayio_parallelbus_construct(displayio_parallelbus_obj_t* self, + const mcu_pin_obj_t* data0, const mcu_pin_obj_t* command, const mcu_pin_obj_t* chip_select, + const mcu_pin_obj_t* write, const mcu_pin_obj_t* read, const mcu_pin_obj_t* reset) { + + mp_raise_NotImplementedError(translate("ParallelBus not yet supported")); +} + +void common_hal_displayio_parallelbus_deinit(displayio_parallelbus_obj_t* self) { + +} + +bool common_hal_displayio_parallelbus_reset(mp_obj_t obj) { + return false; +} + +bool common_hal_displayio_parallelbus_bus_free(mp_obj_t obj) { + return false; +} + +bool common_hal_displayio_parallelbus_begin_transaction(mp_obj_t obj) { + + return false; +} + +void common_hal_displayio_parallelbus_send(mp_obj_t obj, display_byte_type_t byte_type, display_chip_select_behavior_t chip_select, uint8_t *data, uint32_t data_length) { + +} + +void common_hal_displayio_parallelbus_end_transaction(mp_obj_t obj) { + +} diff --git a/ports/esp32s2/common-hal/displayio/ParallelBus.h b/ports/esp32s2/common-hal/displayio/ParallelBus.h new file mode 100644 index 000000000..cd636921d --- /dev/null +++ b/ports/esp32s2/common-hal/displayio/ParallelBus.h @@ -0,0 +1,36 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Lucian Copeland 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. + */ + +#ifndef MICROPY_INCLUDED_ESP32S2_COMMON_HAL_DISPLAYIO_PARALLELBUS_H +#define MICROPY_INCLUDED_ESP32S2_COMMON_HAL_DISPLAYIO_PARALLELBUS_H + +#include "common-hal/digitalio/DigitalInOut.h" + +typedef struct { + mp_obj_base_t base; +} displayio_parallelbus_obj_t; + +#endif // MICROPY_INCLUDED_ESP32S2_COMMON_HAL_DISPLAYIO_PARALLELBUS_H diff --git a/ports/esp32s2/common-hal/microcontroller/Pin.c b/ports/esp32s2/common-hal/microcontroller/Pin.c index 075f80abc..5a059f0bb 100644 --- a/ports/esp32s2/common-hal/microcontroller/Pin.c +++ b/ports/esp32s2/common-hal/microcontroller/Pin.c @@ -42,7 +42,7 @@ void never_reset_pin_number(gpio_num_t pin_number) { never_reset_pins[pin_number / 32] |= 1 << pin_number % 32; } -void never_reset_pin(const mcu_pin_obj_t* pin) { +void common_hal_never_reset_pin(const mcu_pin_obj_t* pin) { never_reset_pin_number(pin->number); } diff --git a/ports/esp32s2/common-hal/microcontroller/Pin.h b/ports/esp32s2/common-hal/microcontroller/Pin.h index ab06b388f..19985bda6 100644 --- a/ports/esp32s2/common-hal/microcontroller/Pin.h +++ b/ports/esp32s2/common-hal/microcontroller/Pin.h @@ -42,6 +42,5 @@ void common_hal_reset_pin(const mcu_pin_obj_t* pin); void claim_pin(const mcu_pin_obj_t* pin); bool pin_number_is_free(gpio_num_t pin_number); void never_reset_pin_number(gpio_num_t pin_number); -void never_reset_pin(const mcu_pin_obj_t* pin); #endif // MICROPY_INCLUDED_ESP32S2_COMMON_HAL_MICROCONTROLLER_PIN_H diff --git a/ports/esp32s2/modules/wroom.c b/ports/esp32s2/modules/wroom.c index 16c586118..5e530701b 100644 --- a/ports/esp32s2/modules/wroom.c +++ b/ports/esp32s2/modules/wroom.c @@ -28,10 +28,10 @@ void never_reset_module_internal_pins(void) { // SPI Flash - never_reset_pin(&pin_GPIO27); - never_reset_pin(&pin_GPIO28); - never_reset_pin(&pin_GPIO29); - never_reset_pin(&pin_GPIO30); - never_reset_pin(&pin_GPIO31); - never_reset_pin(&pin_GPIO32); + common_hal_never_reset_pin(&pin_GPIO27); + common_hal_never_reset_pin(&pin_GPIO28); + common_hal_never_reset_pin(&pin_GPIO29); + common_hal_never_reset_pin(&pin_GPIO30); + common_hal_never_reset_pin(&pin_GPIO31); + common_hal_never_reset_pin(&pin_GPIO32); } diff --git a/ports/esp32s2/modules/wrover.c b/ports/esp32s2/modules/wrover.c index d589a8fd4..23fa7ee5c 100644 --- a/ports/esp32s2/modules/wrover.c +++ b/ports/esp32s2/modules/wrover.c @@ -28,11 +28,11 @@ void never_reset_module_internal_pins(void) { // SPI Flash and RAM - never_reset_pin(&pin_GPIO26); - never_reset_pin(&pin_GPIO27); - never_reset_pin(&pin_GPIO28); - never_reset_pin(&pin_GPIO29); - never_reset_pin(&pin_GPIO30); - never_reset_pin(&pin_GPIO31); - never_reset_pin(&pin_GPIO32); + common_hal_never_reset_pin(&pin_GPIO26); + common_hal_never_reset_pin(&pin_GPIO27); + common_hal_never_reset_pin(&pin_GPIO28); + common_hal_never_reset_pin(&pin_GPIO29); + common_hal_never_reset_pin(&pin_GPIO30); + common_hal_never_reset_pin(&pin_GPIO31); + common_hal_never_reset_pin(&pin_GPIO32); } diff --git a/ports/esp32s2/mpconfigport.mk b/ports/esp32s2/mpconfigport.mk index 9f59c1b46..8e2745a8c 100644 --- a/ports/esp32s2/mpconfigport.mk +++ b/ports/esp32s2/mpconfigport.mk @@ -20,7 +20,7 @@ CIRCUITPY_BITBANGIO = 1 CIRCUITPY_BOARD = 1 CIRCUITPY_DIGITALIO = 1 CIRCUITPY_BUSIO = 1 -CIRCUITPY_DISPLAYIO = 0 +CIRCUITPY_DISPLAYIO = 1 CIRCUITPY_FREQUENCYIO = 0 CIRCUITPY_I2CSLAVE = 0 CIRCUITPY_MICROCONTROLLER = 1 diff --git a/shared-module/displayio/Display.h b/shared-module/displayio/Display.h index 8adaf597f..861a38fd7 100644 --- a/shared-module/displayio/Display.h +++ b/shared-module/displayio/Display.h @@ -29,7 +29,9 @@ #include "shared-bindings/digitalio/DigitalInOut.h" #include "shared-bindings/displayio/Group.h" +#if CIRCUITPY_PULSEIO #include "shared-bindings/pulseio/PWMOut.h" +#endif #include "shared-module/displayio/area.h" #include "shared-module/displayio/display_core.h" @@ -39,7 +41,9 @@ typedef struct { displayio_display_core_t core; union { digitalio_digitalinout_obj_t backlight_inout; + #if CIRCUITPY_PULSEIO pulseio_pwmout_obj_t backlight_pwm; + #endif }; uint64_t last_backlight_refresh; uint64_t last_refresh_call; diff --git a/shared-module/displayio/EPaperDisplay.h b/shared-module/displayio/EPaperDisplay.h index d08bed546..3b9f6e368 100644 --- a/shared-module/displayio/EPaperDisplay.h +++ b/shared-module/displayio/EPaperDisplay.h @@ -29,7 +29,6 @@ #include "shared-bindings/digitalio/DigitalInOut.h" #include "shared-bindings/displayio/Group.h" -#include "shared-bindings/pulseio/PWMOut.h" #include "shared-module/displayio/area.h" #include "shared-module/displayio/display_core.h" -- cgit v1.2.3 From 57fde2e07bb2ee4dcff342deed08e4e7799da799 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Wed, 6 May 2020 11:04:53 -0500 Subject: sdcardio: implement new library for SD card I/O Testing performed: That a card is successfully mounted on Pygamer with the built in SD card slot This module is enabled for most FULL_BUILD boards, but is disabled for samd21 ("M0"), litex, and pca10100 for various reasons. --- locale/circuitpython.pot | 37 ++- ports/atmel-samd/mpconfigport.mk | 2 + ports/litex/mpconfigport.mk | 3 +- ports/nrf/boards/pca10100/mpconfigboard.mk | 1 + py/circuitpy_defns.mk | 5 + py/circuitpy_mpconfig.h | 8 + py/circuitpy_mpconfig.mk | 3 + shared-bindings/busio/SPI.c | 7 + shared-bindings/busio/SPI.h | 2 + shared-bindings/sdcardio/SDCard.c | 184 ++++++++++++ shared-bindings/sdcardio/SDCard.h | 30 ++ shared-bindings/sdcardio/__init__.c | 47 +++ shared-bindings/sdcardio/__init__.h | 0 shared-module/sdcardio/SDCard.c | 466 +++++++++++++++++++++++++++++ shared-module/sdcardio/SDCard.h | 51 ++++ shared-module/sdcardio/__init__.c | 0 shared-module/sdcardio/__init__.h | 0 17 files changed, 843 insertions(+), 3 deletions(-) create mode 100644 shared-bindings/sdcardio/SDCard.c create mode 100644 shared-bindings/sdcardio/SDCard.h create mode 100644 shared-bindings/sdcardio/__init__.c create mode 100644 shared-bindings/sdcardio/__init__.h create mode 100644 shared-module/sdcardio/SDCard.c create mode 100644 shared-module/sdcardio/SDCard.h create mode 100644 shared-module/sdcardio/__init__.c create mode 100644 shared-module/sdcardio/__init__.h (limited to 'shared-module') diff --git a/locale/circuitpython.pot b/locale/circuitpython.pot index 1582b746c..55147dc03 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-06-25 11:44-0500\n" +"POT-Creation-Date: 2020-06-26 11:50-0500\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -417,6 +417,10 @@ msgstr "" msgid "Buffer length %d too big. It must be less than %d" msgstr "" +#: shared-module/sdcardio/SDCard.c +msgid "Buffer length must be a multiple of 512" +msgstr "" + #: shared-bindings/bitbangio/I2C.c shared-bindings/busio/I2C.c msgid "Buffer must be at least length 1" msgstr "" @@ -698,7 +702,8 @@ msgstr "" msgid "Error in regex" msgstr "" -#: shared-bindings/aesio/aes.c shared-bindings/microcontroller/Pin.c +#: shared-bindings/aesio/aes.c shared-bindings/busio/SPI.c +#: shared-bindings/microcontroller/Pin.c #: shared-bindings/neopixel_write/__init__.c shared-bindings/pulseio/PulseOut.c #: shared-bindings/terminalio/Terminal.c msgid "Expected a %q" @@ -1358,6 +1363,10 @@ msgstr "" msgid "Running in safe mode! Not running saved code.\n" msgstr "" +#: shared-module/sdcardio/SDCard.c +msgid "SD card CSD format not supported" +msgstr "" + #: ports/atmel-samd/common-hal/busio/I2C.c #: ports/mimxrt10xx/common-hal/busio/I2C.c ports/nrf/common-hal/busio/I2C.c msgid "SDA or SCL needs a pull up" @@ -1979,6 +1988,10 @@ msgstr "" msgid "can't send non-None value to a just-started generator" msgstr "" +#: shared-module/sdcardio/SDCard.c +msgid "can't set 512 block size" +msgstr "" + #: py/objnamedtuple.c msgid "can't set attribute" msgstr "" @@ -2105,6 +2118,10 @@ msgstr "" msgid "could not invert Vandermonde matrix" msgstr "" +#: shared-module/sdcardio/SDCard.c +msgid "couldn't determine SD card version" +msgstr "" + #: extmod/ulab/code/approx.c msgid "data must be iterable" msgstr "" @@ -2662,6 +2679,10 @@ msgstr "" msgid "negative shift count" msgstr "" +#: shared-module/sdcardio/SDCard.c +msgid "no SD card" +msgstr "" + #: py/vm.c msgid "no active exception to reraise" msgstr "" @@ -2683,6 +2704,10 @@ msgstr "" msgid "no reset pin available" msgstr "" +#: shared-module/sdcardio/SDCard.c +msgid "no response from SD card" +msgstr "" + #: py/runtime.c msgid "no such attribute" msgstr "" @@ -3073,6 +3098,14 @@ msgstr "" msgid "timeout must be >= 0.0" msgstr "" +#: shared-module/sdcardio/SDCard.c +msgid "timeout waiting for v1 card" +msgstr "" + +#: shared-module/sdcardio/SDCard.c +msgid "timeout waiting for v2 card" +msgstr "" + #: shared-bindings/time/__init__.c msgid "timestamp out of range for platform time_t" msgstr "" diff --git a/ports/atmel-samd/mpconfigport.mk b/ports/atmel-samd/mpconfigport.mk index 92d47b186..face79fad 100644 --- a/ports/atmel-samd/mpconfigport.mk +++ b/ports/atmel-samd/mpconfigport.mk @@ -37,6 +37,8 @@ ifndef CIRCUITPY_TOUCHIO_USE_NATIVE CIRCUITPY_TOUCHIO_USE_NATIVE = 1 endif +CIRCUITPY_SDCARDIO ?= 0 + # SAMD21 needs separate endpoint pairs for MSC BULK IN and BULK OUT, otherwise it's erratic. USB_MSC_EP_NUM_OUT = 1 diff --git a/ports/litex/mpconfigport.mk b/ports/litex/mpconfigport.mk index 311c87b5d..427e9ea84 100644 --- a/ports/litex/mpconfigport.mk +++ b/ports/litex/mpconfigport.mk @@ -18,6 +18,7 @@ CIRCUITPY_AUDIOIO = 0 CIRCUITPY_BITBANGIO = 0 CIRCUITPY_BOARD = 0 CIRCUITPY_BUSIO = 0 +CIRCUITPY_COUNTIO = 0 CIRCUITPY_DISPLAYIO = 0 CIRCUITPY_FREQUENCYIO = 0 CIRCUITPY_I2CPERIPHERAL = 0 @@ -25,7 +26,7 @@ CIRCUITPY_NVM = 0 CIRCUITPY_PULSEIO = 0 CIRCUITPY_ROTARYIO = 0 CIRCUITPY_RTC = 0 -CIRCUITPY_COUNTIO = 0 +CIRCUITPY_SDCARDIO = 0 # Enable USB support CIRCUITPY_USB_HID = 1 CIRCUITPY_USB_MIDI = 1 diff --git a/ports/nrf/boards/pca10100/mpconfigboard.mk b/ports/nrf/boards/pca10100/mpconfigboard.mk index 385870a65..318bc0206 100644 --- a/ports/nrf/boards/pca10100/mpconfigboard.mk +++ b/ports/nrf/boards/pca10100/mpconfigboard.mk @@ -21,6 +21,7 @@ CIRCUITPY_PIXELBUF = 0 CIRCUITPY_RGBMATRIX = 0 CIRCUITPY_ROTARYIO = 0 CIRCUITPY_RTC = 1 +CIRCUITPY_SDCARDIO = 0 CIRCUITPY_TOUCHIO = 0 CIRCUITPY_ULAB = 0 diff --git a/py/circuitpy_defns.mk b/py/circuitpy_defns.mk index 74d8f548d..f40f506c2 100644 --- a/py/circuitpy_defns.mk +++ b/py/circuitpy_defns.mk @@ -213,6 +213,9 @@ endif ifeq ($(CIRCUITPY_SAMD),1) SRC_PATTERNS += samd/% endif +ifeq ($(CIRCUITPY_SDCARDIO),1) +SRC_PATTERNS += sdcardio/% +endif ifeq ($(CIRCUITPY_STAGE),1) SRC_PATTERNS += _stage/% endif @@ -384,6 +387,8 @@ SRC_SHARED_MODULE_ALL = \ fontio/__init__.c \ framebufferio/FramebufferDisplay.c \ framebufferio/__init__.c \ + sdcardio/SDCard.c \ + sdcardio/__init__.c \ gamepad/GamePad.c \ gamepad/__init__.c \ gamepadshift/GamePadShift.c \ diff --git a/py/circuitpy_mpconfig.h b/py/circuitpy_mpconfig.h index c92cb1b66..10f91167b 100644 --- a/py/circuitpy_mpconfig.h +++ b/py/circuitpy_mpconfig.h @@ -537,6 +537,13 @@ extern const struct _mp_obj_module_t samd_module; #define SAMD_MODULE #endif +#if CIRCUITPY_SDCARDIO +extern const struct _mp_obj_module_t sdcardio_module; +#define SDCARDIO_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR_sdcardio), (mp_obj_t)&sdcardio_module }, +#else +#define SDCARDIO_MODULE +#endif + #if CIRCUITPY_STAGE extern const struct _mp_obj_module_t stage_module; #define STAGE_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR__stage), (mp_obj_t)&stage_module }, @@ -709,6 +716,7 @@ extern const struct _mp_obj_module_t watchdog_module; ROTARYIO_MODULE \ RTC_MODULE \ SAMD_MODULE \ + SDCARDIO_MODULE \ STAGE_MODULE \ STORAGE_MODULE \ STRUCT_MODULE \ diff --git a/py/circuitpy_mpconfig.mk b/py/circuitpy_mpconfig.mk index 3459bff6d..0850c195a 100644 --- a/py/circuitpy_mpconfig.mk +++ b/py/circuitpy_mpconfig.mk @@ -166,6 +166,9 @@ CFLAGS += -DCIRCUITPY_RTC=$(CIRCUITPY_RTC) CIRCUITPY_SAMD ?= 0 CFLAGS += -DCIRCUITPY_SAMD=$(CIRCUITPY_SAMD) +CIRCUITPY_SDCARDIO ?= $(CIRCUITPY_FULL_BUILD) +CFLAGS += -DCIRCUITPY_SDCARDIO=$(CIRCUITPY_SDCARDIO) + # Currently always off. CIRCUITPY_STAGE ?= 0 CFLAGS += -DCIRCUITPY_STAGE=$(CIRCUITPY_STAGE) diff --git a/shared-bindings/busio/SPI.c b/shared-bindings/busio/SPI.c index 22f001d2d..793ab4f67 100644 --- a/shared-bindings/busio/SPI.c +++ b/shared-bindings/busio/SPI.c @@ -418,3 +418,10 @@ const mp_obj_type_t busio_spi_type = { .make_new = busio_spi_make_new, .locals_dict = (mp_obj_dict_t*)&busio_spi_locals_dict, }; + +busio_spi_obj_t *validate_obj_is_spi_bus(mp_obj_t obj) { + if (!MP_OBJ_IS_TYPE(obj, &busio_spi_type)) { + mp_raise_TypeError_varg(translate("Expected a %q"), busio_spi_type.name); + } + return MP_OBJ_TO_PTR(obj); +} diff --git a/shared-bindings/busio/SPI.h b/shared-bindings/busio/SPI.h index b7b0715d1..aa7d715b9 100644 --- a/shared-bindings/busio/SPI.h +++ b/shared-bindings/busio/SPI.h @@ -70,4 +70,6 @@ uint8_t common_hal_busio_spi_get_polarity(busio_spi_obj_t* self); // This is used by the supervisor to claim SPI devices indefinitely. extern void common_hal_busio_spi_never_reset(busio_spi_obj_t *self); +extern busio_spi_obj_t *validate_obj_is_spi_bus(mp_obj_t obj_in); + #endif // MICROPY_INCLUDED_SHARED_BINDINGS_BUSIO_SPI_H diff --git a/shared-bindings/sdcardio/SDCard.c b/shared-bindings/sdcardio/SDCard.c new file mode 100644 index 000000000..54ca39f80 --- /dev/null +++ b/shared-bindings/sdcardio/SDCard.c @@ -0,0 +1,184 @@ +/* + * This file is part of the Micro Python 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/obj.h" +#include "py/objproperty.h" +#include "py/runtime.h" +#include "py/objarray.h" + +#include "shared-bindings/sdcardio/SDCard.h" +#include "shared-module/sdcardio/SDCard.h" +#include "common-hal/busio/SPI.h" +#include "shared-bindings/busio/SPI.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "supervisor/flash.h" + +//| class SDCard: +//| """SD Card Block Interface +//| +//| Controls an SD card over SPI. This built-in module has higher read +//| performance than the library adafruit_sdcard, but it is only compatible with +//| `busio.SPI`, not `bitbangio.SPI`. Usually an SDCard object is used +//| with ``storage.VfsFat`` to allow file I/O to an SD card.""" +//| +//| def __init__(bus:busio.SPI, cs=digitalio.DigitalInOut, baudrate=8000000): +//| """Construct an SPI SD Card object with the given properties +//| +//| :param busio.SPI spi: The SPI bus +//| :param microcontroller.Pin cs: The chip select connected to the card +//| :param int baudrate: The SPI data rate to use after card setup +//| :param busio.SDIO sdio: The SDIO bus. Mutually exclusive with spi and cs. +//| +//| Note that during detection and configuration, a hard-coded low baudrate is used. +//| Data transfers use the specified baurate (rounded down to one that is supported by +//| the microcontroller) +//| +//| Example usage: +//| +//| .. code-block:: python +//| +//| import os +//| +//| import board +//| import sdcardio +//| import storage +//| +//| sd = sdcardio.SDCard(board.SPI(), board.SD_CS) +//| vfs = storage.VfsFat(sd) +//| storage.mount(vfs, '/sd') +//| os.listdir('/sd')""" + +STATIC mp_obj_t sdcardio_sdcard_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_spi, ARG_cs, ARG_baudrate, ARG_sdio, NUM_ARGS }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_spi, MP_ARG_OBJ, {.u_obj = mp_const_none } }, + { MP_QSTR_cs, MP_ARG_OBJ, {.u_obj = mp_const_none } }, + { MP_QSTR_baudrate, MP_ARG_INT, {.u_int = 8000000} }, + { MP_QSTR_sdio, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_int = 8000000} }, + }; + MP_STATIC_ASSERT( MP_ARRAY_SIZE(allowed_args) == NUM_ARGS ); + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + busio_spi_obj_t *spi = validate_obj_is_spi_bus(args[ARG_spi].u_obj); + mcu_pin_obj_t *cs = validate_obj_is_free_pin(args[ARG_cs].u_obj); + + sdcardio_sdcard_obj_t *self = m_new_obj(sdcardio_sdcard_obj_t); + self->base.type = &sdcardio_SDCard_type; + + common_hal_sdcardio_sdcard_construct(self, spi, cs, args[ARG_baudrate].u_int); + + return self; +} + + +//| def count() -> int: +//| """Returns the total number of sectors +//| +//| Due to technical limitations, this is a function and not a property. +//| +//| :return: The number of 512-byte blocks, as a number""" +//| +mp_obj_t sdcardio_sdcard_count(mp_obj_t self_in) { + sdcardio_sdcard_obj_t *self = (sdcardio_sdcard_obj_t*)self_in; + return mp_obj_new_int_from_ull(common_hal_sdcardio_sdcard_get_blockcount(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(sdcardio_sdcard_count_obj, sdcardio_sdcard_count); + +//| def deinit() -> None: +//| """Disable permanently. +//| +//| :return: None""" +//| +mp_obj_t sdcardio_sdcard_deinit(mp_obj_t self_in) { + sdcardio_sdcard_obj_t *self = (sdcardio_sdcard_obj_t*)self_in; + common_hal_sdcardio_sdcard_deinit(self); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(sdcardio_sdcard_deinit_obj, sdcardio_sdcard_deinit); + + +//| def readblocks(start_block: int, buf: bytearray) -> None: +//| +//| """Read one or more blocks from the card +//| +//| :param int start_block: The block to start reading from +//| :param bytearray buf: The buffer to write into. Length must be multiple of 512. +//| +//| :return: None""" +//| + +mp_obj_t sdcardio_sdcard_readblocks(mp_obj_t self_in, mp_obj_t start_block_in, mp_obj_t buf_in) { + uint32_t start_block = mp_obj_get_int(start_block_in); + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(buf_in, &bufinfo, MP_BUFFER_WRITE); + sdcardio_sdcard_obj_t *self = (sdcardio_sdcard_obj_t*)self_in; + int result = common_hal_sdcardio_sdcard_readblocks(self, start_block, &bufinfo); + if (result < 0) { + mp_raise_OSError(-result); + } + return mp_const_none; +} + +MP_DEFINE_CONST_FUN_OBJ_3(sdcardio_sdcard_readblocks_obj, sdcardio_sdcard_readblocks); + +//| def writeblocks(start_block: int, buf: bytearray) -> None: +//| +//| """Write one or more blocks to the card +//| +//| :param int start_block: The block to start writing from +//| :param bytearray buf: The buffer to read from. Length must be multiple of 512. +//| +//| :return: None""" +//| + +mp_obj_t sdcardio_sdcard_writeblocks(mp_obj_t self_in, mp_obj_t start_block_in, mp_obj_t buf_in) { + uint32_t start_block = mp_obj_get_int(start_block_in); + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(buf_in, &bufinfo, MP_BUFFER_READ); + sdcardio_sdcard_obj_t *self = (sdcardio_sdcard_obj_t*)self_in; + int result = common_hal_sdcardio_sdcard_writeblocks(self, start_block, &bufinfo); + if (result < 0) { + mp_raise_OSError(-result); + } + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_3(sdcardio_sdcard_writeblocks_obj, sdcardio_sdcard_writeblocks); + +STATIC const mp_rom_map_elem_t sdcardio_sdcard_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_count), MP_ROM_PTR(&sdcardio_sdcard_count_obj) }, + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&sdcardio_sdcard_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR_readblocks), MP_ROM_PTR(&sdcardio_sdcard_readblocks_obj) }, + { MP_ROM_QSTR(MP_QSTR_writeblocks), MP_ROM_PTR(&sdcardio_sdcard_writeblocks_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(sdcardio_sdcard_locals_dict, sdcardio_sdcard_locals_dict_table); + +const mp_obj_type_t sdcardio_SDCard_type = { + { &mp_type_type }, + .name = MP_QSTR_SDCard, + .make_new = sdcardio_sdcard_make_new, + .locals_dict = (mp_obj_dict_t*)&sdcardio_sdcard_locals_dict, +}; diff --git a/shared-bindings/sdcardio/SDCard.h b/shared-bindings/sdcardio/SDCard.h new file mode 100644 index 000000000..5986d5b81 --- /dev/null +++ b/shared-bindings/sdcardio/SDCard.h @@ -0,0 +1,30 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017, 2018 Scott Shawcroft for Adafruit Industries + * 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 + +extern const mp_obj_type_t sdcardio_SDCard_type; diff --git a/shared-bindings/sdcardio/__init__.c b/shared-bindings/sdcardio/__init__.c new file mode 100644 index 000000000..746aa5588 --- /dev/null +++ b/shared-bindings/sdcardio/__init__.c @@ -0,0 +1,47 @@ +/* + * 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 + +#include "py/obj.h" +#include "py/runtime.h" + +#include "shared-bindings/sdcardio/SDCard.h" + +//| """Interface to an SD card via the SPI bus""" + +STATIC const mp_rom_map_elem_t sdcardio_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_sdcardio) }, + { MP_ROM_QSTR(MP_QSTR_SDCard), MP_ROM_PTR(&sdcardio_SDCard_type) }, +}; + +STATIC MP_DEFINE_CONST_DICT(sdcardio_module_globals, sdcardio_module_globals_table); + +const mp_obj_module_t sdcardio_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t*)&sdcardio_module_globals, +}; diff --git a/shared-bindings/sdcardio/__init__.h b/shared-bindings/sdcardio/__init__.h new file mode 100644 index 000000000..e69de29bb diff --git a/shared-module/sdcardio/SDCard.c b/shared-module/sdcardio/SDCard.c new file mode 100644 index 000000000..9e861279d --- /dev/null +++ b/shared-module/sdcardio/SDCard.c @@ -0,0 +1,466 @@ +/* + * This file is part of the Micro Python 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. + */ + +// This implementation largely follows the structure of adafruit_sdcard.py + +#include "shared-bindings/busio/SPI.h" +#include "shared-bindings/digitalio/DigitalInOut.h" +#include "shared-bindings/time/__init__.h" +#include "shared-bindings/util.h" +#include "shared-module/sdcardio/SDCard.h" + +#include "py/mperrno.h" + +#if 0 +#define DEBUG_PRINT(...) ((void)mp_printf(&mp_plat_print, ## __VA_ARGS__)) +#else +#define DEBUG_PRINT(...) ((void)0) +#endif + +#define CMD_TIMEOUT (200) + +#define R1_IDLE_STATE (1<<0) +#define R1_ILLEGAL_COMMAND (1<<2) + +#define TOKEN_CMD25 (0xFC) +#define TOKEN_STOP_TRAN (0xFD) +#define TOKEN_DATA (0xFE) + +STATIC bool lock_and_configure_bus(sdcardio_sdcard_obj_t *self) { + if (!common_hal_busio_spi_try_lock(self->bus)) { + return false; + } + common_hal_busio_spi_configure(self->bus, self->baudrate, 0, 0, 8); + common_hal_digitalio_digitalinout_set_value(&self->cs, false); + return true; +} + +STATIC void lock_bus_or_throw(sdcardio_sdcard_obj_t *self) { + if (!lock_and_configure_bus(self)) { + mp_raise_OSError(EAGAIN); + } +} + +STATIC void clock_card(sdcardio_sdcard_obj_t *self, int bytes) { + uint8_t buf[] = {0xff}; + common_hal_digitalio_digitalinout_set_value(&self->cs, true); + for (int i=0; ibus, buf, 1); + } +} + +STATIC void extraclock_and_unlock_bus(sdcardio_sdcard_obj_t *self) { + clock_card(self, 1); + common_hal_busio_spi_unlock(self->bus); +} + +static uint8_t CRC7(const uint8_t* data, uint8_t n) { + uint8_t crc = 0; + for (uint8_t i = 0; i < n; i++) { + uint8_t d = data[i]; + for (uint8_t j = 0; j < 8; j++) { + crc <<= 1; + if ((d & 0x80) ^ (crc & 0x80)) { + crc ^= 0x09; + } + d <<= 1; + } + } + return (crc << 1) | 1; +} + +#define READY_TIMEOUT_NS (300 * 1000 * 1000) // 300ms +STATIC void wait_for_ready(sdcardio_sdcard_obj_t *self) { + uint64_t deadline = common_hal_time_monotonic_ns() + READY_TIMEOUT_NS; + while (common_hal_time_monotonic_ns() < deadline) { + uint8_t b; + common_hal_busio_spi_read(self->bus, &b, 1, 0xff); + if (b == 0xff) { + break; + } + } +} + +// In Python API, defaults are response=None, data_block=True, wait=True +STATIC int cmd(sdcardio_sdcard_obj_t *self, int cmd, int arg, void *response_buf, size_t response_len, bool data_block, bool wait) { + DEBUG_PRINT("cmd % 3d [%02x] arg=% 11d [%08x] len=%d%s%s\n", cmd, cmd, arg, arg, response_len, data_block ? " data" : "", wait ? " wait" : ""); + uint8_t cmdbuf[6]; + cmdbuf[0] = cmd | 0x40; + cmdbuf[1] = (arg >> 24) & 0xff; + cmdbuf[2] = (arg >> 16) & 0xff; + cmdbuf[3] = (arg >> 8) & 0xff; + cmdbuf[4] = arg & 0xff; + cmdbuf[5] = CRC7(cmdbuf, 5); + + if (wait) { + wait_for_ready(self); + } + + common_hal_busio_spi_write(self->bus, cmdbuf, sizeof(cmdbuf)); + + // Wait for the response (response[7] == 0) + bool response_received = false; + for (int i=0; ibus, cmdbuf, 1, 0xff); + if ((cmdbuf[0] & 0x80) == 0) { + response_received = true; + break; + } + } + + if (!response_received) { + return -EIO; + } + + if (response_buf) { + + if (data_block) { + cmdbuf[1] = 0xff; + do { + // Wait for the start block byte + common_hal_busio_spi_read(self->bus, cmdbuf+1, 1, 0xff); + } while (cmdbuf[1] != 0xfe); + } + + common_hal_busio_spi_read(self->bus, response_buf, response_len, 0xff); + + if (data_block) { + // Read and discard the CRC-CCITT checksum + common_hal_busio_spi_read(self->bus, cmdbuf+1, 2, 0xff); + } + + } + + return cmdbuf[0]; +} + +STATIC int block_cmd(sdcardio_sdcard_obj_t *self, int cmd_, int block, void *response_buf, size_t response_len, bool data_block, bool wait) { + return cmd(self, cmd_, block * self->cdv, response_buf, response_len, true, true); +} + +STATIC bool cmd_nodata(sdcardio_sdcard_obj_t* self, int cmd, int response) { + uint8_t cmdbuf[2] = {cmd, 0xff}; + + common_hal_busio_spi_write(self->bus, cmdbuf, sizeof(cmdbuf)); + + // Wait for the response (response[7] == response) + for (int i=0; ibus, cmdbuf, 1, 0xff); + if (cmdbuf[0] == response) { + return 0; + } + } + return -EIO; +} + +STATIC const compressed_string_t *init_card_v1(sdcardio_sdcard_obj_t *self) { + for (int i=0; icdv = 1; + } + return NULL; + } + } + return translate("timeout waiting for v2 card"); +} + +STATIC const compressed_string_t *init_card(sdcardio_sdcard_obj_t *self) { + clock_card(self, 10); + + common_hal_digitalio_digitalinout_set_value(&self->cs, false); + + // CMD0: init card: should return _R1_IDLE_STATE (allow 5 attempts) + { + bool reached_idle_state = false; + for (int i=0; i<5; i++) { + if (cmd(self, 0, 0, NULL, 0, true, true) == R1_IDLE_STATE) { + reached_idle_state = true; + break; + } + } + if (!reached_idle_state) { + return translate("no SD card"); + } + } + + // CMD8: determine card version + { + uint8_t rb7[4]; + int response = cmd(self, 8, 0x1AA, rb7, sizeof(rb7), false, true); + if (response == R1_IDLE_STATE) { + const compressed_string_t *result =init_card_v2(self); + if (result != NULL) { + return result; + } + } else if (response == (R1_IDLE_STATE | R1_ILLEGAL_COMMAND)) { + const compressed_string_t *result =init_card_v1(self); + if (result != NULL) { + return result; + } + } else { + return translate("couldn't determine SD card version"); + } + } + + // CMD9: get number of sectors + { + uint8_t csd[16]; + int response = cmd(self, 9, 0, csd, sizeof(csd), true, true); + if (response != 0) { + return translate("no response from SD card"); + } + int csd_version = (csd[0] & 0xC0) >> 6; + if (csd_version >= 2) { + return translate("SD card CSD format not supported"); + } + + if (csd_version == 1) { + self->sectors = ((csd[8] << 8 | csd[9]) + 1) * 1024; + } else { + uint32_t block_length = 1 << (csd[5] & 0xF); + uint32_t c_size = ((csd[6] & 0x3) << 10) | (csd[7] << 2) | ((csd[8] & 0xC) >> 6); + uint32_t mult = 1 << (((csd[9] & 0x3) << 1 | (csd[10] & 0x80) >> 7) + 2); + self->sectors = block_length / 512 * mult * (c_size + 1); + } + } + + // CMD16: set block length to 512 bytes + { + int response = cmd(self, 16, 512, NULL, 0, true, true); + if (response != 0) { + return translate("can't set 512 block size"); + } + } + + return NULL; +} + +void common_hal_sdcardio_sdcard_construct(sdcardio_sdcard_obj_t *self, busio_spi_obj_t *bus, mcu_pin_obj_t *cs, int baudrate) { + self->bus = bus; + common_hal_digitalio_digitalinout_construct(&self->cs, cs); + common_hal_digitalio_digitalinout_switch_to_output(&self->cs, true, DRIVE_MODE_PUSH_PULL); + + self->cdv = 512; + self->sectors = 0; + self->baudrate = 250000; + + lock_bus_or_throw(self); + const compressed_string_t *result = init_card(self); + extraclock_and_unlock_bus(self); + + if (result != NULL) { + common_hal_digitalio_digitalinout_deinit(&self->cs); + mp_raise_OSError_msg(result); + } + + self->baudrate = baudrate; +} + +void common_hal_sdcardio_sdcard_deinit(sdcardio_sdcard_obj_t *self) { + if (!self->bus) { + return; + } + self->bus = 0; + common_hal_digitalio_digitalinout_deinit(&self->cs); +} + +void common_hal_sdcardio_check_for_deinit(sdcardio_sdcard_obj_t *self) { + if (!self->bus) { + raise_deinited_error(); + } +} + +int common_hal_sdcardio_sdcard_get_blockcount(sdcardio_sdcard_obj_t *self) { + common_hal_sdcardio_check_for_deinit(self); + return self->sectors; +} + +int readinto(sdcardio_sdcard_obj_t *self, void *buf, size_t size) { + uint8_t aux[2] = {0, 0}; + while (aux[0] != 0xfe) { + common_hal_busio_spi_read(self->bus, aux, 1, 0xff); + } + + common_hal_busio_spi_read(self->bus, buf, size, 0xff); + + // Read checksum and throw it away + common_hal_busio_spi_read(self->bus, aux, sizeof(aux), 0xff); + return 0; +} + +int readblocks(sdcardio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *buf) { + uint32_t nblocks = buf->len / 512; + if (nblocks == 1) { + // Use CMD17 to read a single block + return block_cmd(self, 17, start_block, buf->buf, buf->len, true, true); + } else { + // Use CMD18 to read multiple blocks + int r = block_cmd(self, 18, start_block, NULL, 0, true, true); + if (r < 0) { + return r; + } + + uint8_t *ptr = buf->buf; + while (nblocks--) { + r = readinto(self, ptr, 512); + if (r < 0) { + return r; + } + ptr += 512; + } + + // End the multi-block read + r = cmd(self, 12, 0, NULL, 0, true, false); + + // Return first status 0 or last before card ready (0xff) + while (r != 0) { + uint8_t single_byte; + common_hal_busio_spi_read(self->bus, &single_byte, 1, 0xff); + if (single_byte & 0x80) { + return r; + } + r = single_byte; + } + } + return 0; +} + +int common_hal_sdcardio_sdcard_readblocks(sdcardio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *buf) { + common_hal_sdcardio_check_for_deinit(self); + if (buf->len % 512 != 0) { + mp_raise_ValueError(translate("Buffer length must be a multiple of 512")); + } + + lock_and_configure_bus(self); + int r = readblocks(self, start_block, buf); + extraclock_and_unlock_bus(self); + return r; +} + +int _write(sdcardio_sdcard_obj_t *self, uint8_t token, void *buf, size_t size) { + wait_for_ready(self); + + uint8_t cmd[2]; + cmd[0] = token; + + common_hal_busio_spi_write(self->bus, cmd, 1); + common_hal_busio_spi_write(self->bus, buf, size); + + cmd[0] = cmd[1] = 0xff; + common_hal_busio_spi_write(self->bus, cmd, 2); + + // Check the response + // This differs from the traditional adafruit_sdcard handling, + // but adafruit_sdcard also ignored the return value of SDCard._write(!) + // so nobody noticed + // + // + // Response is as follows: + // x x x 0 STAT 1 + // 7 6 5 4 3..1 0 + // with STATUS 010 indicating "data accepted", and other status bit + // combinations indicating failure. + // In practice, I was seeing cmd[0] as 0xe5, indicating success + for (int i=0; ibus, cmd, 1, 0xff); + DEBUG_PRINT("i=%02d cmd[0] = 0x%02x\n", i, cmd[0]); + if ((cmd[0] & 0b00010001) == 0b00000001) { + if ((cmd[0] & 0x1f) != 0x5) { + return -EIO; + } else { + break; + } + } + } + + // Wait for the write to finish + do { + common_hal_busio_spi_read(self->bus, cmd, 1, 0xff); + } while (cmd[0] == 0); + + // Success + return 0; +} + +int writeblocks(sdcardio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *buf) { + common_hal_sdcardio_check_for_deinit(self); + uint32_t nblocks = buf->len / 512; + if (nblocks == 1) { + // Use CMD24 to write a single block + int r = block_cmd(self, 24, start_block, NULL, 0, true, true); + if (r < 0) { + return r; + } + r = _write(self, TOKEN_DATA, buf->buf, buf->len); + if (r < 0) { + return r; + } + } else { + // Use CMD25 to write multiple block + int r = block_cmd(self, 25, start_block, NULL, 0, true, true); + if (r < 0) { + return r; + } + + uint8_t *ptr = buf->buf; + while (nblocks--) { + r = _write(self, TOKEN_CMD25, ptr, 512); + if (r < 0) { + return r; + } + ptr += 512; + } + + cmd_nodata(self, TOKEN_STOP_TRAN, 0); + } + return 0; +} + +int common_hal_sdcardio_sdcard_writeblocks(sdcardio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *buf) { + common_hal_sdcardio_check_for_deinit(self); + if (buf->len % 512 != 0) { + mp_raise_ValueError(translate("Buffer length must be a multiple of 512")); + } + lock_and_configure_bus(self); + int r = writeblocks(self, start_block, buf); + extraclock_and_unlock_bus(self); + return r; +} diff --git a/shared-module/sdcardio/SDCard.h b/shared-module/sdcardio/SDCard.h new file mode 100644 index 000000000..76c906029 --- /dev/null +++ b/shared-module/sdcardio/SDCard.h @@ -0,0 +1,51 @@ +/* + * This file is part of the Micro Python 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 "py/objproperty.h" +#include "py/runtime.h" +#include "py/objarray.h" + +#include "common-hal/busio/SPI.h" +#include "common-hal/digitalio/DigitalInOut.h" + +typedef struct { + mp_obj_base_t base; + busio_spi_obj_t *bus; + digitalio_digitalinout_obj_t cs; + int cdv; + int baudrate; + uint32_t sectors; +} sdcardio_sdcard_obj_t; + +void common_hal_sdcardio_sdcard_construct(sdcardio_sdcard_obj_t *self, busio_spi_obj_t *spi, mcu_pin_obj_t *cs, int baudrate); +void common_hal_sdcardio_sdcard_deinit(sdcardio_sdcard_obj_t *self); +void common_hal_sdcardio_sdcard_check_for_deinit(sdcardio_sdcard_obj_t *self); +int common_hal_sdcardio_sdcard_get_blockcount(sdcardio_sdcard_obj_t *self); +int common_hal_sdcardio_sdcard_readblocks(sdcardio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *buf); +int common_hal_sdcardio_sdcard_writeblocks(sdcardio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *buf); diff --git a/shared-module/sdcardio/__init__.c b/shared-module/sdcardio/__init__.c new file mode 100644 index 000000000..e69de29bb diff --git a/shared-module/sdcardio/__init__.h b/shared-module/sdcardio/__init__.h new file mode 100644 index 000000000..e69de29bb -- cgit v1.2.3