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.
This commit is contained in:
Scott Shawcroft 2017-01-26 18:05:46 -08:00
parent 749d22b5e6
commit e3f9ee839a
30 changed files with 713 additions and 256 deletions

View File

@ -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;

View File

@ -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);
/**
* @}

View File

@ -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();
}

View File

@ -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 */

View File

@ -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;
}

View File

@ -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 */

View File

@ -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) {

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 */

View File

@ -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).

View File

@ -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 */
}

View File

@ -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

View File

@ -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

View File

@ -27,90 +27,308 @@
#include <stdint.h>
#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;
}

View File

@ -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__

View File

@ -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.

View File

@ -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;

View File

@ -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 {

View File

@ -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) {

View File

@ -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 <board.h>
#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) {

View File

@ -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);

View File

@ -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

View File

@ -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);

View File

@ -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;
}

View File

@ -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__

View File

@ -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);

View File

@ -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.
};

View File

@ -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__