From e3f9ee839afb5a369779a30a96214c929e76911e Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Thu, 26 Jan 2017 18:05:46 -0800 Subject: [PATCH] Add frequency changing support to PWMOut. You can either set it once up front, or set variable_frequency on custruction to indicate that the frequency must be able to change. This informs whether a timer can be shared amongst pins. This also adds persistent clock calibration on atmel-samd. Once the device has synced its clock frequency over USB it will remember that config value until USB is used again. This helps ensure the clock frequency is similar on and off USB. Lastly, this also corrects time.sleep() when on USB by correcting the tick counter. --- .../system/clock/clock_samd21_r21_da/clock.c | 6 +- .../clock/clock_samd21_r21_da/clock_feature.h | 2 +- atmel-samd/asf/sam0/drivers/system/system.c | 11 +- atmel-samd/asf/sam0/drivers/system/system.h | 3 +- .../asf/sam0/drivers/tc/tc_sam_d_r/tc.c | 164 ++++----- atmel-samd/asf/sam0/drivers/tcc/tcc.c | 36 +- .../cmsis/samd21/source/gcc/startup_samd21.c | 3 +- atmel-samd/boards/gemma_m0/mpconfigboard.h | 2 +- .../boards/metro_m0_flash/mpconfigboard.mk | 2 +- .../samd21x18-bootloader-external-flash.ld | 9 +- atmel-samd/boards/samd21x18-bootloader.ld | 14 +- atmel-samd/boards/samd21x18-external-flash.ld | 10 +- atmel-samd/boards/samd21x18.ld | 2 +- atmel-samd/boards/trinket_m0/mpconfigboard.h | 2 +- atmel-samd/common-hal/nativeio/I2C.c | 3 +- atmel-samd/common-hal/nativeio/PWMOut.c | 344 ++++++++++++++---- atmel-samd/common-hal/nativeio/PWMOut.h | 32 ++ atmel-samd/common-hal/nativeio/SPI.c | 3 +- atmel-samd/common-hal/nativeio/UART.c | 3 +- atmel-samd/common-hal/nativeio/types.h | 9 +- atmel-samd/internal_flash.c | 26 +- atmel-samd/main.c | 73 +++- atmel-samd/mphalport.c | 8 +- atmel-samd/samd21_pins.c | 52 +-- atmel-samd/tick.c | 4 +- esp8266/common-hal/nativeio/PWMOut.c | 42 ++- esp8266/common-hal/nativeio/PWMOut.h | 32 ++ esp8266/main.c | 2 + shared-bindings/nativeio/PWMOut.c | 63 +++- shared-bindings/nativeio/PWMOut.h | 7 +- 30 files changed, 713 insertions(+), 256 deletions(-) create mode 100644 atmel-samd/common-hal/nativeio/PWMOut.h create mode 100644 esp8266/common-hal/nativeio/PWMOut.h diff --git a/atmel-samd/asf/sam0/drivers/system/clock/clock_samd21_r21_da/clock.c b/atmel-samd/asf/sam0/drivers/system/clock/clock_samd21_r21_da/clock.c index 7dd6e4df32..777ac5b9b9 100644 --- a/atmel-samd/asf/sam0/drivers/system/clock/clock_samd21_r21_da/clock.c +++ b/atmel-samd/asf/sam0/drivers/system/clock/clock_samd21_r21_da/clock.c @@ -772,7 +772,9 @@ static void _switch_peripheral_gclk(void) * the OSC8M default enable can be disabled after system_clock_init. Make sure the * clock switch successfully before disabling OSC8M. */ -void system_clock_init(void) +// Added dfll_fine_calibration as a parameter so that the user program can save +// and restore it. +void system_clock_init(uint16_t dfll_fine_calibration) { /* Various bits in the INTFLAG register can be set to one at startup. This will ensure that these bits are cleared */ @@ -904,7 +906,7 @@ void system_clock_init(void) if (CONF_CLOCK_DFLL_LOOP_MODE == SYSTEM_CLOCK_DFLL_LOOP_MODE_USB_RECOVERY) { dfll_conf.fine_max_step = 10; - dfll_conf.fine_value = 0x1ff; + dfll_conf.fine_value = dfll_fine_calibration; dfll_conf.quick_lock = SYSTEM_CLOCK_DFLL_QUICK_LOCK_ENABLE; dfll_conf.stable_tracking = SYSTEM_CLOCK_DFLL_STABLE_TRACKING_TRACK_AFTER_LOCK; dfll_conf.wakeup_lock = SYSTEM_CLOCK_DFLL_WAKEUP_LOCK_KEEP; diff --git a/atmel-samd/asf/sam0/drivers/system/clock/clock_samd21_r21_da/clock_feature.h b/atmel-samd/asf/sam0/drivers/system/clock/clock_samd21_r21_da/clock_feature.h index a2c4bb60b0..ee8890a312 100644 --- a/atmel-samd/asf/sam0/drivers/system/clock/clock_samd21_r21_da/clock_feature.h +++ b/atmel-samd/asf/sam0/drivers/system/clock/clock_samd21_r21_da/clock_feature.h @@ -1262,7 +1262,7 @@ void system_clock_source_dpll_set_config( * @{ */ -void system_clock_init(void); +void system_clock_init(uint16_t dfll_fine_calibration); /** * @} diff --git a/atmel-samd/asf/sam0/drivers/system/system.c b/atmel-samd/asf/sam0/drivers/system/system.c index 0121588ddf..62f2d64630 100644 --- a/atmel-samd/asf/sam0/drivers/system/system.c +++ b/atmel-samd/asf/sam0/drivers/system/system.c @@ -59,13 +59,13 @@ void _system_dummy_init(void) #if !defined(__DOXYGEN__) # if defined(__GNUC__) -void system_clock_init(void) WEAK __attribute__((alias("_system_dummy_init"))); +void system_clock_init(uint16_t dfll_fine_calibration) WEAK __attribute__((alias("_system_dummy_init"))); void system_board_init(void) WEAK __attribute__((alias("_system_dummy_init"))); void _system_events_init(void) WEAK __attribute__((alias("_system_dummy_init"))); void _system_extint_init(void) WEAK __attribute__((alias("_system_dummy_init"))); void _system_divas_init(void) WEAK __attribute__((alias("_system_dummy_init"))); # elif defined(__ICCARM__) -void system_clock_init(void); +void system_clock_init(uint16_t dfll_fine_calibration); void system_board_init(void); void _system_events_init(void); void _system_extint_init(void); @@ -91,10 +91,10 @@ void _system_divas_init(void); * - Event system driver initialization (via the EVSYS module) * - External Interrupt driver initialization (via the EXTINT module) */ -void system_init(void) +void system_init(uint16_t dfll_fine_calibration) { /* Configure GCLK and clock sources according to conf_clocks.h */ - system_clock_init(); + system_clock_init(dfll_fine_calibration); /* Initialize board hardware */ system_board_init(); @@ -104,8 +104,7 @@ void system_init(void) /* Initialize External hardware */ _system_extint_init(); - + /* Initialize DIVAS hardware */ _system_divas_init(); } - diff --git a/atmel-samd/asf/sam0/drivers/system/system.h b/atmel-samd/asf/sam0/drivers/system/system.h index c6dd6e8562..a0be5b7051 100644 --- a/atmel-samd/asf/sam0/drivers/system/system.h +++ b/atmel-samd/asf/sam0/drivers/system/system.h @@ -555,7 +555,7 @@ static inline uint32_t system_get_device_id(void) * @{ */ -void system_init(void); +void system_init(uint16_t dfll_fine_calibration); /** * @} @@ -725,4 +725,3 @@ void system_init(void); #endif #endif /* SYSTEM_H_INCLUDED */ - diff --git a/atmel-samd/asf/sam0/drivers/tc/tc_sam_d_r/tc.c b/atmel-samd/asf/sam0/drivers/tc/tc_sam_d_r/tc.c index 546c36cd17..3d4a428b7c 100644 --- a/atmel-samd/asf/sam0/drivers/tc/tc_sam_d_r/tc.c +++ b/atmel-samd/asf/sam0/drivers/tc/tc_sam_d_r/tc.c @@ -140,7 +140,7 @@ enum status_code tc_init( /* Array of PM APBC mask bit position for different TC instances */ uint16_t inst_pm_apbmask[] = TC_INST_PM_APBCMASK; - struct system_pinmux_config pin_config; + //struct system_pinmux_config pin_config; struct system_gclk_chan_config gclk_chan_config; #if TC_ASYNC == true @@ -169,15 +169,15 @@ enum status_code tc_init( return STATUS_ERR_INVALID_ARG; } #else - /* Check if odd numbered TC modules are being configured in 32-bit - * counter size. Only even numbered counters are allowed to be - * configured in 32-bit counter size. - */ - if ((config->counter_size == TC_COUNTER_SIZE_32BIT) && - ((instance + TC_INSTANCE_OFFSET) & 0x01)) { - Assert(false); - return STATUS_ERR_INVALID_ARG; - } + // /* Check if odd numbered TC modules are being configured in 32-bit + // * counter size. Only even numbered counters are allowed to be + // * configured in 32-bit counter size. + // */ + // if ((config->counter_size == TC_COUNTER_SIZE_32BIT) && + // ((instance + TC_INSTANCE_OFFSET) & 0x01)) { + // Assert(false); + // return STATUS_ERR_INVALID_ARG; + // } #endif /* Make the counter size variable in the module_inst struct reflect @@ -200,35 +200,35 @@ enum status_code tc_init( return STATUS_ERR_DENIED; } - /* Set up the TC PWM out pin for channel 0 */ - if (config->pwm_channel[0].enabled) { - system_pinmux_get_config_defaults(&pin_config); - pin_config.mux_position = config->pwm_channel[0].pin_mux; - pin_config.direction = SYSTEM_PINMUX_PIN_DIR_OUTPUT; - system_pinmux_pin_set_config( - config->pwm_channel[0].pin_out, &pin_config); - } - - /* Set up the TC PWM out pin for channel 1 */ - if (config->pwm_channel[1].enabled) { - system_pinmux_get_config_defaults(&pin_config); - pin_config.mux_position = config->pwm_channel[1].pin_mux; - pin_config.direction = SYSTEM_PINMUX_PIN_DIR_OUTPUT; - system_pinmux_pin_set_config( - config->pwm_channel[1].pin_out, &pin_config); - } + // /* Set up the TC PWM out pin for channel 0 */ + // if (config->pwm_channel[0].enabled) { + // system_pinmux_get_config_defaults(&pin_config); + // pin_config.mux_position = config->pwm_channel[0].pin_mux; + // pin_config.direction = SYSTEM_PINMUX_PIN_DIR_OUTPUT; + // system_pinmux_pin_set_config( + // config->pwm_channel[0].pin_out, &pin_config); + // } + // + // /* Set up the TC PWM out pin for channel 1 */ + // if (config->pwm_channel[1].enabled) { + // system_pinmux_get_config_defaults(&pin_config); + // pin_config.mux_position = config->pwm_channel[1].pin_mux; + // pin_config.direction = SYSTEM_PINMUX_PIN_DIR_OUTPUT; + // system_pinmux_pin_set_config( + // config->pwm_channel[1].pin_out, &pin_config); + // } /* Enable the user interface clock in the PM */ system_apb_clock_set_mask(SYSTEM_CLOCK_APB_APBC, inst_pm_apbmask[instance]); - /* Enable the slave counter if counter_size is 32-bit */ - if ((config->counter_size == TC_COUNTER_SIZE_32BIT)) - { - /* Enable the user interface clock in the PM */ - system_apb_clock_set_mask(SYSTEM_CLOCK_APB_APBC, - inst_pm_apbmask[instance + 1]); - } + // /* Enable the slave counter if counter_size is 32-bit */ + // if ((config->counter_size == TC_COUNTER_SIZE_32BIT)) + // { + // /* Enable the user interface clock in the PM */ + // system_apb_clock_set_mask(SYSTEM_CLOCK_APB_APBC, + // inst_pm_apbmask[instance + 1]); + // } /* Setup clock for module */ system_gclk_chan_get_config_defaults(&gclk_chan_config); @@ -299,34 +299,34 @@ enum status_code tc_init( /* Switch for TC counter size */ switch (module_inst->counter_size) { case TC_COUNTER_SIZE_8BIT: - while (tc_is_syncing(module_inst)) { - /* Wait for sync */ - } - - hw->COUNT8.COUNT.reg = - config->counter_8_bit.value; - - - while (tc_is_syncing(module_inst)) { - /* Wait for sync */ - } - - hw->COUNT8.PER.reg = - config->counter_8_bit.period; - - while (tc_is_syncing(module_inst)) { - /* Wait for sync */ - } - - hw->COUNT8.CC[0].reg = - config->counter_8_bit.compare_capture_channel[0]; - - while (tc_is_syncing(module_inst)) { - /* Wait for sync */ - } - - hw->COUNT8.CC[1].reg = - config->counter_8_bit.compare_capture_channel[1]; + // while (tc_is_syncing(module_inst)) { + // /* Wait for sync */ + // } + // + // hw->COUNT8.COUNT.reg = + // config->counter_8_bit.value; + // + // + // while (tc_is_syncing(module_inst)) { + // /* Wait for sync */ + // } + // + // hw->COUNT8.PER.reg = + // config->counter_8_bit.period; + // + // while (tc_is_syncing(module_inst)) { + // /* Wait for sync */ + // } + // + // hw->COUNT8.CC[0].reg = + // config->counter_8_bit.compare_capture_channel[0]; + // + // while (tc_is_syncing(module_inst)) { + // /* Wait for sync */ + // } + // + // hw->COUNT8.CC[1].reg = + // config->counter_8_bit.compare_capture_channel[1]; return STATUS_OK; @@ -355,26 +355,26 @@ enum status_code tc_init( return STATUS_OK; case TC_COUNTER_SIZE_32BIT: - while (tc_is_syncing(module_inst)) { - /* Wait for sync */ - } - - hw->COUNT32.COUNT.reg - = config->counter_32_bit.value; - - while (tc_is_syncing(module_inst)) { - /* Wait for sync */ - } - - hw->COUNT32.CC[0].reg = - config->counter_32_bit.compare_capture_channel[0]; - - while (tc_is_syncing(module_inst)) { - /* Wait for sync */ - } - - hw->COUNT32.CC[1].reg = - config->counter_32_bit.compare_capture_channel[1]; + // while (tc_is_syncing(module_inst)) { + // /* Wait for sync */ + // } + // + // hw->COUNT32.COUNT.reg + // = config->counter_32_bit.value; + // + // while (tc_is_syncing(module_inst)) { + // /* Wait for sync */ + // } + // + // hw->COUNT32.CC[0].reg = + // config->counter_32_bit.compare_capture_channel[0]; + // + // while (tc_is_syncing(module_inst)) { + // /* Wait for sync */ + // } + // + // hw->COUNT32.CC[1].reg = + // config->counter_32_bit.compare_capture_channel[1]; return STATUS_OK; } diff --git a/atmel-samd/asf/sam0/drivers/tcc/tcc.c b/atmel-samd/asf/sam0/drivers/tcc/tcc.c index 6b5f8fb5c7..e46cf287ae 100644 --- a/atmel-samd/asf/sam0/drivers/tcc/tcc.c +++ b/atmel-samd/asf/sam0/drivers/tcc/tcc.c @@ -324,12 +324,12 @@ static inline void _tcc_build_ctrlb( { uint8_t ctrlb = 0; - if (config->counter.oneshot) { - ctrlb |= TCC_CTRLBSET_ONESHOT; - } - if (config->counter.direction == TCC_COUNT_DIRECTION_DOWN) { - ctrlb |= TCC_CTRLBSET_DIR; - } + // if (config->counter.oneshot) { + // ctrlb |= TCC_CTRLBSET_ONESHOT; + // } + // if (config->counter.direction == TCC_COUNT_DIRECTION_DOWN) { + // ctrlb |= TCC_CTRLBSET_DIR; + // } *value_buffer = ctrlb; } @@ -606,18 +606,18 @@ enum status_code tcc_init( system_gclk_chan_enable(_tcc_gclk_ids[module_index]); /* Initialize pins */ - struct system_pinmux_config pin_config; - for (i = 0; i < _tcc_ow_nums[module_index]; i ++) { - if (!config->pins.enable_wave_out_pin[i]) { - continue; - } - - system_pinmux_get_config_defaults(&pin_config); - pin_config.mux_position = config->pins.wave_out_pin_mux[i]; - pin_config.direction = SYSTEM_PINMUX_PIN_DIR_OUTPUT; - system_pinmux_pin_set_config( - config->pins.wave_out_pin[i], &pin_config); - } + // struct system_pinmux_config pin_config; + // for (i = 0; i < _tcc_ow_nums[module_index]; i ++) { + // if (!config->pins.enable_wave_out_pin[i]) { + // continue; + // } + // + // system_pinmux_get_config_defaults(&pin_config); + // pin_config.mux_position = config->pins.wave_out_pin_mux[i]; + // pin_config.direction = SYSTEM_PINMUX_PIN_DIR_OUTPUT; + // system_pinmux_pin_set_config( + // config->pins.wave_out_pin[i], &pin_config); + // } /* Write to registers */ diff --git a/atmel-samd/asf/sam0/utils/cmsis/samd21/source/gcc/startup_samd21.c b/atmel-samd/asf/sam0/utils/cmsis/samd21/source/gcc/startup_samd21.c index 767fe0c254..670fb0d614 100644 --- a/atmel-samd/asf/sam0/utils/cmsis/samd21/source/gcc/startup_samd21.c +++ b/atmel-samd/asf/sam0/utils/cmsis/samd21/source/gcc/startup_samd21.c @@ -49,6 +49,7 @@ extern uint32_t _sfixed; extern uint32_t _efixed; extern uint32_t _etext; +extern uint32_t _sidata; extern uint32_t _srelocate; extern uint32_t _erelocate; extern uint32_t _szero; @@ -233,7 +234,7 @@ void Reset_Handler(void) uint32_t *pSrc, *pDest; /* Initialize the relocate segment */ - pSrc = &_etext; + pSrc = &_sidata; pDest = &_srelocate; if (pSrc != pDest) { diff --git a/atmel-samd/boards/gemma_m0/mpconfigboard.h b/atmel-samd/boards/gemma_m0/mpconfigboard.h index dff412b6e7..165522fab7 100644 --- a/atmel-samd/boards/gemma_m0/mpconfigboard.h +++ b/atmel-samd/boards/gemma_m0/mpconfigboard.h @@ -1,6 +1,6 @@ #define USB_REPL -#define MICROPY_HW_BOARD_NAME "Adafruit Gemma M0 (Experimental)" +#define MICROPY_HW_BOARD_NAME "Adafruit Gemma M0" #define MICROPY_HW_MCU_NAME "samd21e18" #define MICROPY_HW_APA102_MOSI &pin_PA04 diff --git a/atmel-samd/boards/metro_m0_flash/mpconfigboard.mk b/atmel-samd/boards/metro_m0_flash/mpconfigboard.mk index 8b6d61bbb4..4b508957d1 100644 --- a/atmel-samd/boards/metro_m0_flash/mpconfigboard.mk +++ b/atmel-samd/boards/metro_m0_flash/mpconfigboard.mk @@ -1,4 +1,4 @@ -LD_FILE = boards/samd21x18-bootloader-external-flash.ld +LD_FILE = boards/samd21x18-external-flash.ld USB_VID = 0x239A USB_PID = 0x8015 diff --git a/atmel-samd/boards/samd21x18-bootloader-external-flash.ld b/atmel-samd/boards/samd21x18-bootloader-external-flash.ld index 574dbb001b..a6be02f90f 100644 --- a/atmel-samd/boards/samd21x18-bootloader-external-flash.ld +++ b/atmel-samd/boards/samd21x18-bootloader-external-flash.ld @@ -5,7 +5,7 @@ /* Specify the memory areas */ MEMORY { - FLASH (rx) : ORIGIN = 0x00000000 + 0x2000, LENGTH = 0x00040000 - 0x2000 /* Leave 8KiB for the bootloader. */ + FLASH (rx) : ORIGIN = 0x00000000 + 0x2000, LENGTH = 0x00040000 - 0x2000 - 0x100 /* Leave 8KiB for the bootloader and 256b for config. */ RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 0x008000 /* 32 KiB */ } @@ -28,9 +28,14 @@ SECTIONS . = ALIGN(4); _etext = .; /* define a global symbol at end of code */ - _sidata = _etext; /* This is used by the startup in order to initialize the .data secion */ } >FLASH + .ARM.exidx : + { + *(.ARM.exidx*) + *(.gnu.linkonce.armexidx.*) + _sidata = .; /* This is used by the startup in order to initialize the .data section */ + } > FLASH /* This is the initialized data section The program executes knowing that the data is in the RAM diff --git a/atmel-samd/boards/samd21x18-bootloader.ld b/atmel-samd/boards/samd21x18-bootloader.ld index b43c292b16..4e3312bf03 100644 --- a/atmel-samd/boards/samd21x18-bootloader.ld +++ b/atmel-samd/boards/samd21x18-bootloader.ld @@ -5,7 +5,8 @@ /* Specify the memory areas */ MEMORY { - FLASH (rx) : ORIGIN = 0x00000000+0x2000, LENGTH = 0x00040000 - 0x2000 - 0x010000 /* Leave 8KiB for the bootloader and 64k for the flash file system. */ + /* Leave 8KiB for the bootloader, 256b for persistant config (clock), and 64k for the flash file system. */ + FLASH (rx) : ORIGIN = 0x00000000+0x2000, LENGTH = 0x00040000 - 0x2000 - 0x100 - 0x010000 RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 0x008000 /* 32 KiB */ } @@ -28,9 +29,14 @@ SECTIONS . = ALIGN(4); _etext = .; /* define a global symbol at end of code */ - _sidata = _etext; /* This is used by the startup in order to initialize the .data secion */ } >FLASH + .ARM.exidx : + { + *(.ARM.exidx*) + *(.gnu.linkonce.armexidx.*) + _sidata = .; /* This is used by the startup in order to initialize the .data section */ + } > FLASH /* This is the initialized data section The program executes knowing that the data is in the RAM @@ -39,14 +45,14 @@ SECTIONS .data : AT ( _sidata ) { . = ALIGN(4); - _srelocate = .; /* create a global symbol at data start; used by startup code in order to initialise the .data section in RAM */ + _srelocate = .; /* create a global symbol at data start; used by startup code in order to initialize the .data section in RAM */ *(.ramfunc) *(.ramfunc*) *(.data) /* .data sections */ *(.data*) /* .data* sections */ . = ALIGN(4); - _erelocate = .; /* define a global symbol at data end; used by startup code in order to initialise the .data section in RAM */ + _erelocate = .; /* define a global symbol at data end; used by startup code in order to initialize the .data section in RAM */ } >RAM /* Uninitialized data section */ diff --git a/atmel-samd/boards/samd21x18-external-flash.ld b/atmel-samd/boards/samd21x18-external-flash.ld index 0c2e71df15..90cb6160a7 100644 --- a/atmel-samd/boards/samd21x18-external-flash.ld +++ b/atmel-samd/boards/samd21x18-external-flash.ld @@ -5,7 +5,7 @@ /* Specify the memory areas */ MEMORY { - FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 0x00040000 /* 256 KiB */ + FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 0x00040000 - 0x100 /* 256 KiB but leave 256b for config */ RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 0x008000 /* 32 KiB */ } @@ -28,9 +28,15 @@ SECTIONS . = ALIGN(4); _etext = .; /* define a global symbol at end of code */ - _sidata = _etext; /* This is used by the startup in order to initialize the .data section */ } >FLASH + .ARM.exidx : + { + *(.ARM.exidx*) + *(.gnu.linkonce.armexidx.*) + _sidata = .; /* This is used by the startup in order to initialize the .data section */ + } > FLASH + /* This is the initialized data section The program executes knowing that the data is in the RAM but the loader puts the initial values in the FLASH (inidata). diff --git a/atmel-samd/boards/samd21x18.ld b/atmel-samd/boards/samd21x18.ld index 995d2754fa..98a24a8110 100644 --- a/atmel-samd/boards/samd21x18.ld +++ b/atmel-samd/boards/samd21x18.ld @@ -5,7 +5,7 @@ /* Specify the memory areas */ MEMORY { - FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 0x00040000 - 0x010000 /* Leave 64k for the flash file system. */ + FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 0x00040000 - 0x100 - 0x010000 /* Leave 256b for config and 64k for the flash file system. */ RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 0x008000 /* 32 KiB */ } diff --git a/atmel-samd/boards/trinket_m0/mpconfigboard.h b/atmel-samd/boards/trinket_m0/mpconfigboard.h index 35c7cceeb4..e72b0a8eb8 100644 --- a/atmel-samd/boards/trinket_m0/mpconfigboard.h +++ b/atmel-samd/boards/trinket_m0/mpconfigboard.h @@ -1,6 +1,6 @@ #define USB_REPL -#define MICROPY_HW_BOARD_NAME "Adafruit Trinket M0 (Experimental)" +#define MICROPY_HW_BOARD_NAME "Adafruit Trinket M0" #define MICROPY_HW_MCU_NAME "samd21e18" #define MICROPY_HW_APA102_MOSI &pin_PA01 diff --git a/atmel-samd/common-hal/nativeio/I2C.c b/atmel-samd/common-hal/nativeio/I2C.c index 07e367baae..8e36152326 100644 --- a/atmel-samd/common-hal/nativeio/I2C.c +++ b/atmel-samd/common-hal/nativeio/I2C.c @@ -69,8 +69,7 @@ void common_hal_nativeio_i2c_construct(nativeio_i2c_obj_t *self, } } if (sercom == NULL) { - nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, - "No hardware support available with those pins.")); + nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "Invalid pins.")); } config_i2c_master.pinmux_pad0 = sda_pinmux; // SDA diff --git a/atmel-samd/common-hal/nativeio/PWMOut.c b/atmel-samd/common-hal/nativeio/PWMOut.c index 82833fbba5..a122fadb5c 100644 --- a/atmel-samd/common-hal/nativeio/PWMOut.c +++ b/atmel-samd/common-hal/nativeio/PWMOut.c @@ -27,90 +27,308 @@ #include #include "py/runtime.h" +#include "common-hal/nativeio/PWMOut.h" #include "shared-bindings/nativeio/PWMOut.h" #include "samd21_pins.h" -void common_hal_nativeio_pwmout_construct(nativeio_pwmout_obj_t* self, const mcu_pin_obj_t* pin, uint16_t duty) { - self->pin = pin; - self->using_primary_timer = true; +#undef ENABLE - if (pin->primary_timer.tc == 0 && pin->secondary_timer.tc == 0) { - nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, - "PWM not supported on pin %q", self->pin->name)); +uint32_t target_timer_frequencies[TC_INST_NUM + TCC_INST_NUM]; +uint8_t timer_refcount[TC_INST_NUM + TCC_INST_NUM]; +const uint16_t prescaler[8] = {1, 2, 4, 8, 16, 64, 256, 1024}; + +// This bitmask keeps track of which channels of a TCC are currently claimed. +uint8_t tcc_channels[3] = {0xf0, 0xfc, 0xfc}; + +void pwmout_reset(void) { + // Reset all but TC5 + for (int i = 0; i < TC_INST_NUM + TCC_INST_NUM; i++) { + if (i == 5) { + target_timer_frequencies[i] = 1000; + timer_refcount[i] = 1; + } else { + target_timer_frequencies[i] = 0; + timer_refcount[i] = 0; + } } - - // TODO(tannewt): Support output on multiple timer channels at once. - const pin_timer_t* t = &pin->primary_timer; - if (t->is_tc) { - struct tc_config config_tc; - tc_get_config_defaults(&config_tc); - - config_tc.counter_size = TC_COUNTER_SIZE_8BIT; - config_tc.wave_generation = TC_WAVE_GENERATION_NORMAL_PWM; - config_tc.clock_prescaler = TC_CLOCK_PRESCALER_DIV256; - config_tc.counter_8_bit.period = 0xff; - config_tc.counter_8_bit.compare_capture_channel[t->channel] = duty; - - config_tc.pwm_channel[t->wave_output].enabled = true; - config_tc.pwm_channel[t->wave_output].pin_out = pin->pin; - config_tc.pwm_channel[t->wave_output].pin_mux = MUX_E; - - tc_init(&self->tc_instance, t->tc, &config_tc); - - tc_enable(&self->tc_instance); - } else { - struct tcc_config config_tcc; - tcc_get_config_defaults(&config_tcc, t->tcc); - - config_tcc.counter.clock_prescaler = TCC_CLOCK_PRESCALER_DIV256; - config_tcc.counter.period = 0xFF; - config_tcc.compare.wave_generation = TCC_WAVE_GENERATION_SINGLE_SLOPE_PWM; - config_tcc.compare.match[t->channel] = duty; - - config_tcc.pins.enable_wave_out_pin[t->wave_output] = true; - config_tcc.pins.wave_out_pin[t->wave_output] = pin->pin; - config_tcc.pins.wave_out_pin_mux[t->wave_output] = MUX_E; - - tcc_init(&self->tcc_instance, t->tcc, &config_tcc); - - tcc_enable(&self->tcc_instance); + Tcc *tccs[TCC_INST_NUM] = TCC_INSTS; + for (int i = 0; i < TCC_INST_NUM; i++) { + tccs[i]->CTRLA.bit.SWRST = 1; + } + Tc *tcs[TC_INST_NUM] = TC_INSTS; + for (int i = 0; i < TC_INST_NUM; i++) { + if (tcs[i] == TC5) { + continue; + } + tcs[i]->COUNT16.CTRLA.bit.SWRST = 1; } } -extern void common_hal_nativeio_pwmout_deinit(nativeio_pwmout_obj_t* self) { - const pin_timer_t* t = &self->pin->primary_timer; - if (!self->using_primary_timer) { - t = &self->pin->secondary_timer; +bool channel_ok(const pin_timer_t* t, uint8_t index) { + return (!t->is_tc && (tcc_channels[index] & (1 << t->channel)) == 0) || + t->is_tc; +} + +void common_hal_nativeio_pwmout_construct(nativeio_pwmout_obj_t* self, + const mcu_pin_obj_t* pin, + uint16_t duty, + uint32_t frequency, + bool variable_frequency) { + self->pin = pin; + self->variable_frequency = variable_frequency; + + if (pin->primary_timer.tc == 0 && pin->secondary_timer.tc == 0) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, + "Invalid pin.")); } - if (t->is_tc) { - tc_disable(&self->tc_instance); + + if (frequency == 0 || frequency > 6000000) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, + "Invalid PWM frequency.")); + } + + uint16_t primary_timer_index = 0xff; + uint16_t secondary_timer_index = 0xff; + if (pin->primary_timer.tc != NULL) { + primary_timer_index = (((uint32_t) pin->primary_timer.tcc) - ((uint32_t) TCC0)) / 0x400; + } + if (pin->secondary_timer.tc != NULL) { + secondary_timer_index = (((uint32_t) pin->secondary_timer.tcc) - ((uint32_t) TCC0)) / 0x400; + } + + // Figure out which timer we are using. + + // First see if a timer is already going with the frequency we want and our + // channel is unused. + // NOTE(shawcroft): The enable bit is in the same position for TC and TCC so + // we treat them all as TCC for checking ENABLE. + const pin_timer_t* t = NULL; + uint8_t index = 0; + if (!variable_frequency && + primary_timer_index != 0xff && + target_timer_frequencies[primary_timer_index] == frequency && + pin->primary_timer.tcc->CTRLA.bit.ENABLE == 1 && + channel_ok(&pin->primary_timer, primary_timer_index)) { + t = &pin->primary_timer; + index = primary_timer_index; + } else if (!variable_frequency && + secondary_timer_index != 0xff && + target_timer_frequencies[secondary_timer_index] == frequency && + pin->secondary_timer.tcc->CTRLA.bit.ENABLE == 1 && + channel_ok(&pin->secondary_timer, secondary_timer_index)) { + t = &pin->secondary_timer; + index = secondary_timer_index; } else { - tcc_disable(&self->tcc_instance); + // Pick an unused timer if available. + + // Check the secondary timer first since its always a nicer TCC (when it + // exists) + if (pin->secondary_timer.tc != 0 && + timer_refcount[secondary_timer_index] == 0 && + pin->secondary_timer.tcc->CTRLA.bit.ENABLE == 0) { + t = &pin->secondary_timer; + index = secondary_timer_index; + } else if (pin->primary_timer.tc != 0 && + (!pin->primary_timer.is_tc || pin->primary_timer.channel == 1) && + timer_refcount[primary_timer_index] == 0) { + t = &pin->primary_timer; + index = primary_timer_index; + } + if (t == NULL) { + nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "All timers in use.")); + return; + } + uint8_t resolution = 0; + if (t->is_tc) { + resolution = 16; + } else { + resolution = 24; + } + // First determine the divisor that gets us the highest resolution. + uint32_t system_clock = system_cpu_clock_get_hz(); + uint32_t top; + uint8_t divisor; + for (divisor = 0; divisor < 8; divisor++) { + top = (system_clock / prescaler[divisor] / frequency) - 1; + if (top < (1u << resolution)) { + break; + } + } + if (t->is_tc) { + struct tc_config config_tc; + tc_get_config_defaults(&config_tc); + + config_tc.counter_size = TC_COUNTER_SIZE_16BIT; + config_tc.clock_prescaler = TC_CTRLA_PRESCALER(divisor); + config_tc.wave_generation = TC_WAVE_GENERATION_MATCH_PWM; + config_tc.counter_16_bit.compare_capture_channel[0] = top; + + tc_init(&self->tc_instance, t->tc, &config_tc); + tc_enable(&self->tc_instance); + } else { + struct tcc_config config_tcc; + tcc_get_config_defaults(&config_tcc, t->tcc); + + config_tcc.counter.clock_prescaler = divisor; + config_tcc.counter.period = top; + config_tcc.compare.wave_generation = TCC_WAVE_GENERATION_SINGLE_SLOPE_PWM; + + tcc_init(&self->tcc_instance, t->tcc, &config_tcc); + tcc_enable(&self->tcc_instance); + } + + target_timer_frequencies[index] = frequency; + timer_refcount[index]++; + } + + if (!t->is_tc) { + if (variable_frequency) { + // We're changing frequency so claim all of the channels. + tcc_channels[index] = 0xff; + } else { + tcc_channels[index] |= (1 << t->channel); + } + } + + self->timer = t; + + // Connect the wave output to the outside world. + struct system_pinmux_config pin_config; + system_pinmux_get_config_defaults(&pin_config); + pin_config.mux_position = &self->pin->primary_timer == t ? MUX_E : MUX_F; + pin_config.direction = SYSTEM_PINMUX_PIN_DIR_OUTPUT; + system_pinmux_pin_set_config(pin->pin, &pin_config); + + common_hal_nativeio_pwmout_set_duty_cycle(self, duty); +} + +extern void common_hal_nativeio_pwmout_deinit(nativeio_pwmout_obj_t* self) { + const pin_timer_t* t = self->timer; + uint8_t index = (((uint32_t) t->tcc) - ((uint32_t) TCC0)) / 0x400; + timer_refcount[index]--; + if (!t->is_tc) { + tcc_channels[index] &= ~(1 << t->channel); + } + if (timer_refcount[index] == 0) { + target_timer_frequencies[index] = 0; + if (t->is_tc) { + tc_disable(&self->tc_instance); + } else { + if (t->tcc == TCC0) { + tcc_channels[index] = 0xf0; + } else { + tcc_channels[index] = 0xfc; + } + tcc_disable(&self->tcc_instance); + tcc_reset(&self->tcc_instance); + } } reset_pin(self->pin->pin); } extern void common_hal_nativeio_pwmout_set_duty_cycle(nativeio_pwmout_obj_t* self, uint16_t duty) { - const pin_timer_t* t = &self->pin->primary_timer; - if (!self->using_primary_timer) { - t = &self->pin->secondary_timer; - } - if (t->tcc != 0) { - tcc_set_compare_value(&self->tcc_instance, t->channel, duty); + const pin_timer_t* t = self->timer; + if (t->is_tc) { + uint32_t top = ((uint32_t) t->tc->COUNT16.CC[0].reg + 1); + uint16_t adjusted_duty = top * duty / 0xffff; + tc_set_compare_value(&self->tc_instance, t->channel, adjusted_duty); } else { - tc_set_compare_value(&self->tc_instance, t->channel, duty); + uint32_t top = t->tcc->PER.reg + 1; + uint32_t adjusted_duty = ((uint64_t) top) * duty / 0xffff; + tcc_set_compare_value(&self->tcc_instance, t->channel, adjusted_duty); } } uint16_t common_hal_nativeio_pwmout_get_duty_cycle(nativeio_pwmout_obj_t* self) { - const pin_timer_t* t = &self->pin->primary_timer; - if (!self->using_primary_timer) { - t = &self->pin->secondary_timer; - } - if (t->tcc != 0) { - return tcc_get_capture_value(&self->tcc_instance, t->channel); + const pin_timer_t* t = self->timer; + if (t->is_tc) { + uint16_t top = t->tc->COUNT16.CC[0].reg; + while (tc_is_syncing(&self->tc_instance)) { + /* Wait for sync */ + } + uint16_t cv = t->tc->COUNT16.CC[t->channel].reg; + return cv * 0xffff / top; } else { - return tc_get_capture_value(&self->tc_instance, t->channel); + uint32_t top = t->tcc->PER.reg; + uint32_t cv = 0; + if ((t->tcc->STATUS.vec.CCBV & (1 << t->channel)) != 0) { + cv = t->tcc->CCB[t->channel].reg; + } else { + cv = t->tcc->CC[t->channel].reg; + } + return cv * 0xffff / top; } } + + +void common_hal_nativeio_pwmout_set_frequency(nativeio_pwmout_obj_t* self, + uint32_t frequency) { + if (frequency == 0 || frequency > 6000000) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, + "Invalid PWM frequency.")); + } + const pin_timer_t* t = self->timer; + uint8_t resolution; + if (t->is_tc) { + resolution = 16; + } else { + resolution = 24; + } + uint32_t system_clock = system_cpu_clock_get_hz(); + uint32_t new_top; + uint8_t new_divisor; + for (new_divisor = 0; new_divisor < 8; new_divisor++) { + new_top = (system_clock / prescaler[new_divisor] / frequency) - 1; + if (new_top < (1u << resolution)) { + break; + } + } + uint16_t old_duty = common_hal_nativeio_pwmout_get_duty_cycle(self); + uint8_t old_divisor; + if (t->is_tc) { + old_divisor = t->tc->COUNT16.CTRLA.bit.PRESCALER; + } else { + old_divisor = t->tcc->CTRLA.bit.PRESCALER; + } + if (new_divisor != old_divisor) { + if (t->is_tc) { + tc_disable(&self->tc_instance); + t->tc->COUNT16.CTRLA.bit.PRESCALER = new_divisor; + tc_enable(&self->tc_instance); + } else { + tcc_disable(&self->tcc_instance); + t->tcc->CTRLA.bit.PRESCALER = new_divisor; + tcc_enable(&self->tcc_instance); + } + } + if (t->is_tc) { + while (tc_is_syncing(&self->tc_instance)) { + /* Wait for sync */ + } + t->tc->COUNT16.CC[0].reg = new_top; + } else { + tcc_set_top_value(&self->tcc_instance, new_top); + } + + common_hal_nativeio_pwmout_set_duty_cycle(self, old_duty); +} + +uint32_t common_hal_nativeio_pwmout_get_frequency(nativeio_pwmout_obj_t* self) { + uint32_t system_clock = system_cpu_clock_get_hz(); + const pin_timer_t* t = self->timer; + uint32_t top; + uint8_t divisor; + if (t->is_tc) { + top = t->tc->COUNT16.CC[0].reg; + divisor = t->tc->COUNT16.CTRLA.bit.PRESCALER; + } else { + top = t->tcc->PER.reg; + divisor = t->tcc->CTRLA.bit.PRESCALER; + } + return (system_clock / prescaler[divisor]) / (top + 1); +} + +bool common_hal_nativeio_pwmout_get_variable_frequency(nativeio_pwmout_obj_t* self) { + return self->variable_frequency; +} diff --git a/atmel-samd/common-hal/nativeio/PWMOut.h b/atmel-samd/common-hal/nativeio/PWMOut.h new file mode 100644 index 0000000000..56994e519a --- /dev/null +++ b/atmel-samd/common-hal/nativeio/PWMOut.h @@ -0,0 +1,32 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 Scott Shawcroft 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_ATMEL_SAMD_COMMON_HAL_NATIVEIO_PWMOUT_H__ +#define __MICROPY_INCLUDED_ATMEL_SAMD_COMMON_HAL_NATIVEIO_PWMOUT_H__ + +void pwmout_reset(void); + +#endif // __MICROPY_INCLUDED_ATMEL_SAMD_COMMON_HAL_NATIVEIO_PWMOUT_H__ diff --git a/atmel-samd/common-hal/nativeio/SPI.c b/atmel-samd/common-hal/nativeio/SPI.c index 70b0965798..f24cd40a2a 100644 --- a/atmel-samd/common-hal/nativeio/SPI.c +++ b/atmel-samd/common-hal/nativeio/SPI.c @@ -92,8 +92,7 @@ void common_hal_nativeio_spi_construct(nativeio_spi_obj_t *self, } } if (sercom == NULL) { - nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, - "No hardware support available with those pins.")); + nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "Invalid pins.")); } // Depends on where MOSI and CLK are. diff --git a/atmel-samd/common-hal/nativeio/UART.c b/atmel-samd/common-hal/nativeio/UART.c index f827911998..8a66173151 100644 --- a/atmel-samd/common-hal/nativeio/UART.c +++ b/atmel-samd/common-hal/nativeio/UART.c @@ -162,8 +162,7 @@ void common_hal_nativeio_uart_construct(nativeio_uart_obj_t *self, } } if (sercom == NULL) { - nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, - "No hardware support available with those pins.")); + nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "Invalid pins.")); } if (tx == NULL) { tx_pad = 0; diff --git a/atmel-samd/common-hal/nativeio/types.h b/atmel-samd/common-hal/nativeio/types.h index 1f483eced0..ad52053816 100644 --- a/atmel-samd/common-hal/nativeio/types.h +++ b/atmel-samd/common-hal/nativeio/types.h @@ -90,9 +90,12 @@ typedef struct { typedef struct { mp_obj_base_t base; const mcu_pin_obj_t *pin; - bool using_primary_timer; - struct tc_module tc_instance; - struct tcc_module tcc_instance; + const pin_timer_t* timer; + bool variable_frequency; + union { + struct tc_module tc_instance; + struct tcc_module tcc_instance; + }; } nativeio_pwmout_obj_t; typedef struct { diff --git a/atmel-samd/internal_flash.c b/atmel-samd/internal_flash.c index d62b3a8ad1..1ce38f0b0a 100644 --- a/atmel-samd/internal_flash.c +++ b/atmel-samd/internal_flash.c @@ -45,26 +45,16 @@ #define INTERNAL_FLASH_PART1_START_BLOCK (0x1) #define INTERNAL_FLASH_PART1_NUM_BLOCKS (TOTAL_INTERNAL_FLASH_SIZE / INTERNAL_FLASH_BLOCK_SIZE) -static bool internal_flash_is_initialised = false; - void internal_flash_init(void) { - if (!internal_flash_is_initialised) { - struct nvm_config config_nvm; - nvm_get_config_defaults(&config_nvm); - config_nvm.manual_page_write = false; - nvm_set_config(&config_nvm); - internal_flash_is_initialised = true; + // Activity LED for flash writes. + #ifdef MICROPY_HW_LED_MSC + struct port_config pin_conf; + port_get_config_defaults(&pin_conf); - // Activity LED for flash writes. - #ifdef MICROPY_HW_LED_MSC - struct port_config pin_conf; - port_get_config_defaults(&pin_conf); - - pin_conf.direction = PORT_PIN_DIR_OUTPUT; - port_pin_set_config(MICROPY_HW_LED_MSC, &pin_conf); - port_pin_set_output_level(MICROPY_HW_LED_MSC, false); - #endif - } + pin_conf.direction = PORT_PIN_DIR_OUTPUT; + port_pin_set_config(MICROPY_HW_LED_MSC, &pin_conf); + port_pin_set_output_level(MICROPY_HW_LED_MSC, false); + #endif } uint32_t internal_flash_get_block_size(void) { diff --git a/atmel-samd/main.c b/atmel-samd/main.c index 561de8316c..f05195f5bc 100644 --- a/atmel-samd/main.c +++ b/atmel-samd/main.c @@ -17,16 +17,21 @@ #include "asf/common/services/sleepmgr/sleepmgr.h" #include "asf/common/services/usb/udc/udc.h" #include "asf/common2/services/delay/delay.h" +#include "asf/sam0/drivers/nvm/nvm.h" #include "asf/sam0/drivers/port/port.h" #include "asf/sam0/drivers/sercom/usart/usart.h" #include "asf/sam0/drivers/system/system.h" #include #include "common-hal/nativeio/AnalogIn.h" +#include "common-hal/nativeio/PWMOut.h" #ifdef EXPRESS_BOARD #include "common-hal/nativeio/types.h" #include "QTouch/touch_api_ptc.h" +#define INTERNAL_CIRCUITPY_CONFIG_START_ADDR (0x00040000 - 0x100) +#else +#define INTERNAL_CIRCUITPY_CONFIG_START_ADDR (0x00040000 - 0x010000 - 0x100) #endif #include "autoreset.h" @@ -34,6 +39,7 @@ #include "rgb_led_status.h" #include "tick.h" + fs_user_mount_t fs_user_mount_flash; void do_str(const char *src, mp_parse_input_kind_t input_kind) { @@ -133,6 +139,8 @@ void reset_mp(void) { extern nativeio_touchin_obj_t *active_touchin_obj[DEF_SELFCAP_NUM_CHANNELS]; extern touch_selfcap_config_t selfcap_config; #endif +extern volatile bool mp_msc_enabled; + void reset_samd21(void) { // Reset all SERCOMs except the one being used by the SPI flash. Sercom *sercom_instances[SERCOM_INST_NUM] = SERCOM_INSTS; @@ -173,6 +181,53 @@ void reset_samd21(void) { system_pinmux_group_set_config(&(PORT->Group[0]), pin_mask[0] & ~MICROPY_PORT_A, &config); system_pinmux_group_set_config(&(PORT->Group[1]), pin_mask[1] & ~MICROPY_PORT_B, &config); + + pwmout_reset(); + + // If we are on USB lets double check our fine calibration for the clock and + // save the new value if its different enough. + if (mp_msc_enabled) { + SYSCTRL->DFLLSYNC.bit.READREQ = 1; + uint16_t saved_calibration = 0x1ff; + if (strcmp((char*) INTERNAL_CIRCUITPY_CONFIG_START_ADDR, "CIRCUITPYTHON1") == 0) { + saved_calibration = ((uint16_t *) INTERNAL_CIRCUITPY_CONFIG_START_ADDR)[8]; + } + while (SYSCTRL->PCLKSR.bit.DFLLRDY == 0) { + // TODO(tannewt): Run the mass storage stuff if this takes a while. + } + int16_t current_calibration = SYSCTRL->DFLLVAL.bit.FINE; + if (abs(current_calibration - saved_calibration) > 10) { + enum status_code error_code; + uint8_t page_buffer[NVMCTRL_ROW_SIZE]; + for (int i = 0; i < NVMCTRL_ROW_PAGES; i++) { + do + { + error_code = nvm_read_buffer(INTERNAL_CIRCUITPY_CONFIG_START_ADDR + i * NVMCTRL_PAGE_SIZE, + page_buffer + i * NVMCTRL_PAGE_SIZE, + NVMCTRL_PAGE_SIZE); + } while (error_code == STATUS_BUSY); + } + // If this is the first write, include the header. + if (strcmp((char*) page_buffer, "CIRCUITPYTHON1") != 0) { + memcpy(page_buffer, "CIRCUITPYTHON1", 15); + } + // First 16 bytes (0-15) are ID. Little endian! + page_buffer[16] = current_calibration & 0xff; + page_buffer[17] = current_calibration >> 8; + do + { + error_code = nvm_erase_row(INTERNAL_CIRCUITPY_CONFIG_START_ADDR); + } while (error_code == STATUS_BUSY); + for (int i = 0; i < NVMCTRL_ROW_PAGES; i++) { + do + { + error_code = nvm_write_buffer(INTERNAL_CIRCUITPY_CONFIG_START_ADDR + i * NVMCTRL_PAGE_SIZE, + page_buffer + i * NVMCTRL_PAGE_SIZE, + NVMCTRL_PAGE_SIZE); + } while (error_code == STATUS_BUSY); + } + } + } } bool maybe_run(const char* filename, pyexec_result_t* exec_result) { @@ -405,7 +460,17 @@ void samd21_init(void) { // Initialize the sleep manager sleepmgr_init(); - system_init(); + uint16_t dfll_fine_calibration = 0x1ff; + // This is stored in an NVM page after the text and data storage but before + // the optional file system. The first 16 bytes are the identifier for the + // section. + if (strcmp((char*) INTERNAL_CIRCUITPY_CONFIG_START_ADDR, "CIRCUITPYTHON1") == 0) { + dfll_fine_calibration = ((uint16_t *) INTERNAL_CIRCUITPY_CONFIG_START_ADDR)[8]; + } + + // We pass in the DFLL fine calibration because we can't change it once the + // clock is going. + system_init(dfll_fine_calibration); delay_init(); @@ -423,6 +488,12 @@ void samd21_init(void) { // port_pin_set_output_level(MICROPY_HW_LED1, false); rgb_led_status_init(); + + // Init the nvm controller. + struct nvm_config config_nvm; + nvm_get_config_defaults(&config_nvm); + config_nvm.manual_page_write = false; + nvm_set_config(&config_nvm); } int main(int argc, char **argv) { diff --git a/atmel-samd/mphalport.c b/atmel-samd/mphalport.c index a47063994f..7fe06dd808 100644 --- a/atmel-samd/mphalport.c +++ b/atmel-samd/mphalport.c @@ -37,7 +37,8 @@ volatile bool mp_cdc_enabled = false; extern struct usart_module usart_instance; -static volatile bool mp_msc_enabled = false; +// Read by main to know when USB is connected. +volatile bool mp_msc_enabled = false; bool mp_msc_enable() { mp_msc_enabled = true; @@ -220,9 +221,8 @@ void mp_hal_stdout_tx_strn(const char *str, size_t len) { void mp_hal_delay_ms(mp_uint_t delay) { // If mass storage is enabled measure the time ourselves and run any mass // storage transactions in the meantime. - // TODO(tannewt): Break out of this delay on KeyboardInterrupt too. if (mp_msc_enabled) { - uint64_t start_tick = common_hal_time_monotonic(); + uint64_t start_tick = ticks_ms; uint64_t duration = 0; while (duration < delay) { #ifdef MICROPY_VM_HOOK_LOOP @@ -232,7 +232,7 @@ void mp_hal_delay_ms(mp_uint_t delay) { if(MP_STATE_VM(mp_pending_exception) == MP_OBJ_FROM_PTR(&MP_STATE_VM(mp_kbd_exception))) { break; } - duration = (common_hal_time_monotonic() - start_tick); + duration = (ticks_ms - start_tick); } } else { delay_ms(delay); diff --git a/atmel-samd/samd21_pins.c b/atmel-samd/samd21_pins.c index b08f5a8732..754509b969 100644 --- a/atmel-samd/samd21_pins.c +++ b/atmel-samd/samd21_pins.c @@ -69,6 +69,8 @@ void reset_pin(uint8_t pin) { } // Pins in datasheet order. +// NOTE(tannewt): TC wave out 0 is commented out because the first channel is +// used to vary the 16 bit timer's frequency. #ifdef PIN_PA00 PIN(PA00, NO_ADC, NO_TOUCH, TCC(TCC2, 0, 0), @@ -127,7 +129,7 @@ PIN(PB07, TOUCH(ADC_POSITIVE_INPUT_PIN15), TOUCH(13), #endif #ifdef PIN_PB08 PIN(PB08, ADC_INPUT(ADC_POSITIVE_INPUT_PIN2), TOUCH(14), - TC(TC4, 0, 0), + NO_TIMER, // TC(TC4, 0, 0), NO_TIMER, NO_SERCOM, SERCOM(SERCOM4, 0)); @@ -197,7 +199,7 @@ PIN(PA11, ADC_INPUT(ADC_POSITIVE_INPUT_PIN19), NO_TOUCH, #endif #ifdef PIN_PB10 PIN(PB10, NO_ADC, NO_TOUCH, - TC(TC5, 0, 0), + NO_TIMER, // TC(TC5, 0, 0), TCC(TCC0, 0, 4), NO_SERCOM, SERCOM(SERCOM4, 2)); @@ -211,7 +213,7 @@ PIN(PB11, NO_ADC, NO_TOUCH, #endif #ifdef PIN_PB12 PIN(PB12, NO_ADC, NO_TOUCH, - TC(TC4, 0, 0), + NO_TIMER, // TC(TC4, 0, 0), TCC(TCC0, 2, 6), SERCOM(SERCOM4, 0), NO_SERCOM); @@ -225,7 +227,7 @@ PIN(PB13, NO_ADC, NO_TOUCH, #endif #ifdef PIN_PB14 PIN(PB14, NO_ADC, NO_TOUCH, - TC(TC5, 0, 0), + NO_TIMER, // TC(TC5, 0, 0), NO_TIMER, SERCOM(SERCOM4, 2), NO_SERCOM); @@ -255,7 +257,7 @@ PIN(PA13, NO_ADC, NO_TOUCH, #endif #ifdef PIN_PA14 PIN(PA14, NO_ADC, NO_TOUCH, - TC(TC3, 0, 0), + NO_TIMER, // TC(TC3, 0, 0), TCC(TCC0, 0, 4), SERCOM(SERCOM2, 2), #ifdef SERCOM4 @@ -286,29 +288,29 @@ PIN(PA16, NO_ADC, NO_TOUCH, #endif #ifdef PIN_PA17 PIN(PA17, NO_ADC, NO_TOUCH, - TCC(TCC2, 1, 1), - TCC(TCC0, 3, 7), - SERCOM(SERCOM1, 1), - SERCOM(SERCOM3, 1)); + TCC(TCC2, 1, 1), + TCC(TCC0, 3, 7), + SERCOM(SERCOM1, 1), + SERCOM(SERCOM3, 1)); #endif #ifdef PIN_PA18 PIN(PA18, NO_ADC, NO_TOUCH, - TC(TC3, 0, 0), - TCC(TCC0, 2, 2), - SERCOM(SERCOM1, 2), - SERCOM(SERCOM3, 2)); + NO_TIMER, // TC(TC3, 0, 0), + TCC(TCC0, 2, 2), + SERCOM(SERCOM1, 2), + SERCOM(SERCOM3, 2)); #endif #ifdef PIN_PA19 PIN(PA19, NO_ADC, NO_TOUCH, - TC(TC3, 1, 1), - TCC(TCC0, 3, 3), - SERCOM(SERCOM1, 3), - SERCOM(SERCOM3, 3)); + TC(TC3, 1, 1), + TCC(TCC0, 3, 3), + SERCOM(SERCOM1, 3), + SERCOM(SERCOM3, 3)); #endif #ifdef PIN_PB16 PIN(PB16, NO_ADC, NO_TOUCH, #ifdef _SAMD21_TC6_INSTANCE_ - TC(TC6, 0, 0), + NO_TIMER, // TC(TC6, 0, 0), #else NO_TIMER, #endif @@ -319,7 +321,7 @@ PIN(PB16, NO_ADC, NO_TOUCH, #ifdef PIN_PB17 PIN(PB17, NO_ADC, NO_TOUCH, #ifdef _SAMD21_TC6_INSTANCE_ - TC(TC6, 0, 0), + TC(TC6, 1, 1), #else NO_TIMER, #endif @@ -330,7 +332,7 @@ PIN(PB17, NO_ADC, NO_TOUCH, #ifdef PIN_PA20 PIN(PA20, NO_ADC, NO_TOUCH, #ifdef _SAMD21_TC7_INSTANCE_ - TC(TC7, 0, 0), + NO_TIMER, // TC(TC7, 0, 0), #else NO_TIMER, #endif @@ -351,7 +353,7 @@ PIN(PA21, NO_ADC, NO_TOUCH, #endif #ifdef PIN_PA22 PIN(PA22, NO_ADC, NO_TOUCH, - TC(TC4, 0, 0), + NO_TIMER, // TC(TC4, 0, 0), TCC(TCC0, 0, 4), SERCOM(SERCOM3, 0), #ifdef SERCOM5 @@ -375,7 +377,7 @@ PIN(PA23, NO_ADC, NO_TOUCH, #endif #ifdef PIN_PA24 PIN(PA24, NO_ADC, NO_TOUCH, - TC(TC5, 0, 0), + NO_TIMER, // TC(TC5, 0, 0), TCC(TCC0, 2, 2), SERCOM(SERCOM3, 2), #ifdef SERCOM5 @@ -400,7 +402,7 @@ PIN(PA25, NO_ADC, NO_TOUCH, #ifdef PIN_PB22 PIN(PB22, NO_ADC, NO_TOUCH, #ifdef _SAMD21_TC7_INSTANCE_ - TC(TC7, 1, 1), + NO_TIMER, // TC(TC7, 0, 0), #else NO_TIMER, #endif @@ -464,7 +466,7 @@ PIN(PB31, NO_ADC, NO_TOUCH, #ifdef PIN_PB00 PIN(PB00, ADC_INPUT(ADC_POSITIVE_INPUT_PIN8), TOUCH(6), #ifdef _SAMD21_TC7_INSTANCE_ - TC(TC7, 0, 0), + NO_TIMER, // TC(TC7, 0, 0), #else NO_TIMER, #endif @@ -486,7 +488,7 @@ PIN(PB01, ADC_INPUT(ADC_POSITIVE_INPUT_PIN9), TOUCH(7), #ifdef PIN_PB02 PIN(PB02, ADC_INPUT(ADC_POSITIVE_INPUT_PIN10), TOUCH(8), #ifdef _SAMD21_TC6_INSTANCE_ - TC(TC6, 0, 0), + NO_TIMER, // TC(TC6, 0, 0), #else NO_TIMER, #endif diff --git a/atmel-samd/tick.c b/atmel-samd/tick.c index cad69f0807..d779f7fdc3 100644 --- a/atmel-samd/tick.c +++ b/atmel-samd/tick.c @@ -23,9 +23,11 @@ void tick_init() { struct tc_config config_tc; tc_get_config_defaults(&config_tc); config_tc.counter_size = TC_COUNTER_SIZE_16BIT; + config_tc.wave_generation = TC_WAVE_GENERATION_MATCH_FREQ; config_tc.clock_prescaler = TC_CLOCK_PRESCALER_DIV1; - tc_set_top_value(&ms_timer, system_cpu_clock_get_hz() / 1000); tc_init(&ms_timer, TC5, &config_tc); + + tc_set_top_value(&ms_timer, system_cpu_clock_get_hz() / 1000 - 1); tc_enable(&ms_timer); tc_register_callback(&ms_timer, ms_tick, TC_CALLBACK_OVERFLOW); tc_enable_callback(&ms_timer, TC_CALLBACK_OVERFLOW); diff --git a/esp8266/common-hal/nativeio/PWMOut.c b/esp8266/common-hal/nativeio/PWMOut.c index ce40b6c368..234246d15f 100644 --- a/esp8266/common-hal/nativeio/PWMOut.c +++ b/esp8266/common-hal/nativeio/PWMOut.c @@ -36,14 +36,35 @@ #include "c_types.h" #include "gpio.h" +#define PWM_FREQ_MAX 1000 + // Shared with pybpwm extern bool pwm_inited; +bool first_channel_variable; + +void pwmout_reset(void) { + first_channel_variable = false; +} + +void common_hal_nativeio_pwmout_construct(nativeio_pwmout_obj_t* self, const mcu_pin_obj_t* pin, uint16_t duty, uint32_t frequency, + bool variable_frequency) { + if (frequency > PWM_FREQ_MAX) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, + "Maximum PWM frequency is %dhz.", PWM_FREQ_MAX)); + } else if (frequency < 1) { + nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, + "Minimum PWM frequency is 1hz.")); + } -void common_hal_nativeio_pwmout_construct(nativeio_pwmout_obj_t* self, const mcu_pin_obj_t* pin, uint16_t duty) { // start the PWM subsystem if it's not already running if (!pwm_inited) { pwm_init(); pwm_inited = true; + pwm_set_freq(frequency, 0); + first_channel_variable = variable_frequency; + } else if (first_channel_variable || pwm_get_freq(0) != frequency) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, + "Multiple PWM frequencies not supported. PWM already set to %dhz.", pwm_get_freq(0))); } self->channel = pwm_add(pin->gpio_number, @@ -76,3 +97,22 @@ extern void common_hal_nativeio_pwmout_set_duty_cycle(nativeio_pwmout_obj_t* sel uint16_t common_hal_nativeio_pwmout_get_duty_cycle(nativeio_pwmout_obj_t* self) { return pwm_get_duty(self->channel) << 6; } + +void common_hal_nativeio_pwmout_set_frequency(nativeio_pwmout_obj_t* self, uint32_t frequency) { + if (frequency > PWM_FREQ_MAX) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, + "Maximum PWM frequency is %dhz.", PWM_FREQ_MAX)); + } else if (frequency < 1) { + nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, + "Minimum PWM frequency is 1hz.")); + } + pwm_set_freq(frequency, 0); +} + +uint32_t common_hal_nativeio_pwmout_get_frequency(nativeio_pwmout_obj_t* self) { + return pwm_get_freq(0); +} + +bool common_hal_nativeio_pwmout_get_variable_frequency(nativeio_pwmout_obj_t* self) { + return first_channel_variable; +} diff --git a/esp8266/common-hal/nativeio/PWMOut.h b/esp8266/common-hal/nativeio/PWMOut.h new file mode 100644 index 0000000000..9406b49538 --- /dev/null +++ b/esp8266/common-hal/nativeio/PWMOut.h @@ -0,0 +1,32 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 Scott Shawcroft 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_ESP8266_COMMON_HAL_NATIVEIO_PWMOUT_H__ +#define __MICROPY_INCLUDED_ESP8266_COMMON_HAL_NATIVEIO_PWMOUT_H__ + +void pwmout_reset(void); + +#endif // __MICROPY_INCLUDED_ESP8266_COMMON_HAL_NATIVEIO_PWMOUT_H__ diff --git a/esp8266/main.c b/esp8266/main.c index 6c06f0e078..98db93d868 100644 --- a/esp8266/main.c +++ b/esp8266/main.c @@ -39,6 +39,7 @@ #include "gccollect.h" #include "user_interface.h" #include "common-hal/microcontroller/Pin.h" +#include "common-hal/nativeio/PWMOut.h" STATIC char heap[36 * 1024]; @@ -63,6 +64,7 @@ STATIC void mp_reset(void) { pin_init0(); readline_init0(); dupterm_task_init(); + pwmout_reset(); #if MICROPY_MODULE_FROZEN pyexec_frozen_module("_boot.py"); pyexec_file("boot.py", NULL); diff --git a/shared-bindings/nativeio/PWMOut.c b/shared-bindings/nativeio/PWMOut.c index 10c7e86413..7ac274cd80 100644 --- a/shared-bindings/nativeio/PWMOut.c +++ b/shared-bindings/nativeio/PWMOut.c @@ -38,13 +38,24 @@ //| //| PWMOut can be used to output a PWM signal on a given pin. //| -//| .. class:: PWMOut(pin, duty=0) +//| .. class:: PWMOut(pin, duty=0, frequency=500, variable_frequency=False) //| //| Create a PWM object associated with the given pin. This allows you to -//| write PWM signals out on the given pin. Frequency is currently fixed at -//| ~735Hz like Arduino. +//| write PWM signals out on the given pin. Frequency is fixed after init +//| unless `variable_frequency` is True. //| //| :param ~microcontroller.Pin pin: The pin to output to +//| :param int duty: The fraction of each pulse which is high. 16-bit +//| :param int frequency: The target frequency in Hertz (32-bit) +//| :param bool variable_frequency: True if the frequency will change over time +//| +//| Example usage:: +//| +//| import nativeio +//| import board +//| +//| with nativeio.PWMOut(board.D13) as pwm: # output on D13 +//| pwm.duty_cycle = 2 ** 15 # Cycles the pin with 50% duty cycle (half of 2 ** 16) //| STATIC mp_obj_t nativeio_pwmout_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { mp_arg_check_num(n_args, n_kw, 1, MP_OBJ_FUN_ARGS_MAX, true); @@ -59,15 +70,19 @@ STATIC mp_obj_t nativeio_pwmout_make_new(const mp_obj_type_t *type, size_t n_arg mp_map_t kw_args; mp_map_init_fixed_table(&kw_args, n_kw, args + n_args); - enum { ARG_duty }; + enum { ARG_duty, ARG_frequency, ARG_variable_frequency }; static const mp_arg_t allowed_args[] = { { MP_QSTR_duty, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_frequency, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 500} }, + { MP_QSTR_variable_frequency, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} }, }; mp_arg_val_t parsed_args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all(n_args - 1, args + 1, &kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, parsed_args); uint8_t duty = parsed_args[ARG_duty].u_int; + uint32_t frequency = parsed_args[ARG_frequency].u_int; + bool variable_frequency = parsed_args[ARG_variable_frequency].u_int; - common_hal_nativeio_pwmout_construct(self, pin, duty); + common_hal_nativeio_pwmout_construct(self, pin, duty, frequency, variable_frequency); return MP_OBJ_FROM_PTR(self); } @@ -105,7 +120,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(nativeio_pwmout___exit___obj, 4, 4, n //| .. attribute:: duty_cycle //| -//| 8 bit value that dictates how much of one cycle is high (1) versus low +//| 16 bit value that dictates how much of one cycle is high (1) versus low //| (0). 255 will always be high, 0 will always be low and 127 will be half //| high and then half low. STATIC mp_obj_t nativeio_pwmout_obj_get_duty_cycle(mp_obj_t self_in) { @@ -117,9 +132,9 @@ MP_DEFINE_CONST_FUN_OBJ_1(nativeio_pwmout_get_duty_cycle_obj, nativeio_pwmout_ob STATIC mp_obj_t nativeio_pwmout_obj_set_duty_cycle(mp_obj_t self_in, mp_obj_t duty_cycle) { nativeio_pwmout_obj_t *self = MP_OBJ_TO_PTR(self_in); mp_int_t duty = mp_obj_get_int(duty_cycle); - if (duty < 0 || duty > 255) { + if (duty < 0 || duty > 0xffff) { nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, - "PWM duty must be between 0 and 255 (8 bit resolution), not %d", + "PWM duty must be between 0 and 65536 (16 bit resolution), not %d", duty)); } common_hal_nativeio_pwmout_set_duty_cycle(self, duty); @@ -134,6 +149,36 @@ mp_obj_property_t nativeio_pwmout_duty_cycle_obj = { (mp_obj_t)&mp_const_none_obj}, }; +//| .. attribute:: frequency +//| +//| 32 bit value that dictates the PWM frequency in Hertz (cycles per +//| second). Only writeable when constructed with ``variable_frequency=True``. +//| +STATIC mp_obj_t nativeio_pwmout_obj_get_frequency(mp_obj_t self_in) { + nativeio_pwmout_obj_t *self = MP_OBJ_TO_PTR(self_in); + return MP_OBJ_NEW_SMALL_INT(common_hal_nativeio_pwmout_get_frequency(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(nativeio_pwmout_get_frequency_obj, nativeio_pwmout_obj_get_frequency); + +STATIC mp_obj_t nativeio_pwmout_obj_set_frequency(mp_obj_t self_in, mp_obj_t frequency) { + nativeio_pwmout_obj_t *self = MP_OBJ_TO_PTR(self_in); + if (!common_hal_nativeio_pwmout_get_variable_frequency(self)) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_AttributeError, + "PWM frequency not writeable when variable_frequency is False on " + "construction.")); + } + common_hal_nativeio_pwmout_set_frequency(self, mp_obj_get_int(frequency)); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(nativeio_pwmout_set_frequency_obj, nativeio_pwmout_obj_set_frequency); + +mp_obj_property_t nativeio_pwmout_frequency_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&nativeio_pwmout_get_frequency_obj, + (mp_obj_t)&nativeio_pwmout_set_frequency_obj, + (mp_obj_t)&mp_const_none_obj}, +}; + STATIC const mp_rom_map_elem_t nativeio_pwmout_locals_dict_table[] = { // Methods { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&nativeio_pwmout_deinit_obj) }, @@ -142,7 +187,7 @@ STATIC const mp_rom_map_elem_t nativeio_pwmout_locals_dict_table[] = { // Properties { MP_ROM_QSTR(MP_QSTR_duty_cycle), MP_ROM_PTR(&nativeio_pwmout_duty_cycle_obj) }, - // TODO(tannewt): Add frequency. + { MP_ROM_QSTR(MP_QSTR_frequency), MP_ROM_PTR(&nativeio_pwmout_frequency_obj) }, // TODO(tannewt): Add enabled to determine whether the signal is output // without giving up the resources. Useful for IR output. }; diff --git a/shared-bindings/nativeio/PWMOut.h b/shared-bindings/nativeio/PWMOut.h index 0be553315d..588992ceba 100644 --- a/shared-bindings/nativeio/PWMOut.h +++ b/shared-bindings/nativeio/PWMOut.h @@ -32,9 +32,14 @@ extern const mp_obj_type_t nativeio_pwmout_type; -extern void common_hal_nativeio_pwmout_construct(nativeio_pwmout_obj_t* self, const mcu_pin_obj_t* pin, uint16_t duty); +extern void common_hal_nativeio_pwmout_construct(nativeio_pwmout_obj_t* self, + const mcu_pin_obj_t* pin, uint16_t duty, uint32_t frequency, + bool variable_frequency); extern void common_hal_nativeio_pwmout_deinit(nativeio_pwmout_obj_t* self); extern void common_hal_nativeio_pwmout_set_duty_cycle(nativeio_pwmout_obj_t* self, uint16_t duty); extern uint16_t common_hal_nativeio_pwmout_get_duty_cycle(nativeio_pwmout_obj_t* self); +extern void common_hal_nativeio_pwmout_set_frequency(nativeio_pwmout_obj_t* self, uint32_t frequency); +extern uint32_t common_hal_nativeio_pwmout_get_frequency(nativeio_pwmout_obj_t* self); +extern bool common_hal_nativeio_pwmout_get_variable_frequency(nativeio_pwmout_obj_t* self); #endif // __MICROPY_INCLUDED_SHARED_BINDINGS_NATIVEIO_PWMOUT_H__