m0 pulseout works. Factored out shared timer code.
This commit is contained in:
parent
8479eef578
commit
6a7d889dd4
@ -188,7 +188,6 @@ SRC_ASF := \
|
|||||||
hpl/rtc/hpl_rtc.c \
|
hpl/rtc/hpl_rtc.c \
|
||||||
hpl/sercom/hpl_sercom.c \
|
hpl/sercom/hpl_sercom.c \
|
||||||
hpl/systick/hpl_systick.c \
|
hpl/systick/hpl_systick.c \
|
||||||
hpl/tc/hpl_tc.c \
|
|
||||||
hpl/usb/hpl_usb.c \
|
hpl/usb/hpl_usb.c \
|
||||||
usb/class/cdc/device/cdcdf_acm.c \
|
usb/class/cdc/device/cdcdf_acm.c \
|
||||||
usb/class/msc/device/mscdf.c \
|
usb/class/msc/device/mscdf.c \
|
||||||
@ -227,6 +226,7 @@ SRC_C = \
|
|||||||
peripherals.c \
|
peripherals.c \
|
||||||
$(CHIP_FAMILY)_pins.c \
|
$(CHIP_FAMILY)_pins.c \
|
||||||
tick.c \
|
tick.c \
|
||||||
|
timers.c \
|
||||||
usb.c \
|
usb.c \
|
||||||
usb_mass_storage.c \
|
usb_mass_storage.c \
|
||||||
boards/$(BOARD)/board.c \
|
boards/$(BOARD)/board.c \
|
||||||
|
@ -34,9 +34,7 @@
|
|||||||
|
|
||||||
#include "atmel_start_pins.h"
|
#include "atmel_start_pins.h"
|
||||||
#include "hal/utils/include/utils_repeat_macro.h"
|
#include "hal/utils/include/utils_repeat_macro.h"
|
||||||
#ifdef SAMD21
|
#include "timers.h"
|
||||||
#include "hpl/gclk/hpl_gclk_base.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "samd21_pins.h"
|
#include "samd21_pins.h"
|
||||||
|
|
||||||
@ -55,44 +53,10 @@ 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.
|
// This bitmask keeps track of which channels of a TCC are currently claimed.
|
||||||
#ifdef SAMD21
|
#ifdef SAMD21
|
||||||
uint8_t tcc_channels[3] = {0xf0, 0xfc, 0xfc};
|
uint8_t tcc_channels[3] = {0xf0, 0xfc, 0xfc};
|
||||||
const uint8_t tcc_cc_num[3] = {4, 2, 2};
|
|
||||||
const uint8_t tc_gclk_ids[TC_INST_NUM] = {TC3_GCLK_ID,
|
|
||||||
TC4_GCLK_ID,
|
|
||||||
TC5_GCLK_ID,
|
|
||||||
#ifdef TC6_GCLK_ID
|
|
||||||
, TC6_GCLK_ID
|
|
||||||
#endif
|
|
||||||
#ifdef TC7_GCLK_ID
|
|
||||||
, TC7_GCLK_ID
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
const uint8_t tcc_gclk_ids[3] = {TCC0_GCLK_ID, TCC1_GCLK_ID, TCC2_GCLK_ID};
|
|
||||||
#endif
|
#endif
|
||||||
#ifdef SAMD51
|
#ifdef SAMD51
|
||||||
uint8_t tcc_channels[5] = {0xc0, 0xf0, 0xf8, 0xfc, 0xfc};
|
uint8_t tcc_channels[5] = {0xc0, 0xf0, 0xf8, 0xfc, 0xfc};
|
||||||
static const uint8_t tcc_cc_num[5] = {6, 4, 3, 2, 2};
|
|
||||||
const uint8_t tc_gclk_ids[TC_INST_NUM] = {TC0_GCLK_ID,
|
|
||||||
TC1_GCLK_ID,
|
|
||||||
TC2_GCLK_ID,
|
|
||||||
TC3_GCLK_ID
|
|
||||||
#ifdef TC4_GCLK_ID
|
|
||||||
, TC4_GCLK_ID
|
|
||||||
#endif
|
#endif
|
||||||
#ifdef TC5_GCLK_ID
|
|
||||||
, TC5_GCLK_ID
|
|
||||||
#endif
|
|
||||||
#ifdef TC6_GCLK_ID
|
|
||||||
, TC6_GCLK_ID
|
|
||||||
#endif
|
|
||||||
#ifdef TC7_GCLK_ID
|
|
||||||
, TC7_GCLK_ID
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
const uint8_t tcc_gclk_ids[5] = {TCC0_GCLK_ID, TCC1_GCLK_ID, TCC2_GCLK_ID, TCC3_GCLK_ID,
|
|
||||||
TCC4_GCLK_ID};
|
|
||||||
#endif
|
|
||||||
static Tc* const tc_insts[TC_INST_NUM] = TC_INSTS;
|
|
||||||
static Tcc* const tcc_insts[TCC_INST_NUM] = TCC_INSTS;
|
|
||||||
|
|
||||||
void pwmout_reset(void) {
|
void pwmout_reset(void) {
|
||||||
// Reset all timers
|
// Reset all timers
|
||||||
@ -129,120 +93,12 @@ static uint8_t tcc_channel(const pin_timer_t* t) {
|
|||||||
return t->wave_output % tcc_cc_num[t->index];
|
return t->wave_output % tcc_cc_num[t->index];
|
||||||
}
|
}
|
||||||
|
|
||||||
static void tc_set_enable(Tc* tc, bool enable) {
|
|
||||||
tc->COUNT16.CTRLA.bit.ENABLE = enable;
|
|
||||||
#ifdef SAMD21
|
|
||||||
while (tc->COUNT16.STATUS.bit.SYNCBUSY != 0) {
|
|
||||||
/* Wait for sync */
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#ifdef SAMD51
|
|
||||||
while (tc->COUNT16.SYNCBUSY.bit.ENABLE != 0) {
|
|
||||||
/* Wait for sync */
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
static void tcc_set_enable(Tcc* tcc, bool enable) {
|
|
||||||
tcc->CTRLA.bit.ENABLE = enable;
|
|
||||||
while (tcc->SYNCBUSY.bit.ENABLE != 0) {
|
|
||||||
/* Wait for sync */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void tc_wait_for_sync(Tc* tc) {
|
|
||||||
#ifdef SAMD21
|
|
||||||
while (tc->COUNT16.STATUS.bit.SYNCBUSY != 0) {}
|
|
||||||
#endif
|
|
||||||
#ifdef SAMD51
|
|
||||||
while (tc->COUNT16.SYNCBUSY.reg != 0) {}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
bool channel_ok(const pin_timer_t* t) {
|
bool channel_ok(const pin_timer_t* t) {
|
||||||
uint8_t channel_bit = 1 << tcc_channel(t);
|
uint8_t channel_bit = 1 << tcc_channel(t);
|
||||||
return (!t->is_tc && ((tcc_channels[t->index] & channel_bit) == 0)) ||
|
return (!t->is_tc && ((tcc_channels[t->index] & channel_bit) == 0)) ||
|
||||||
t->is_tc;
|
t->is_tc;
|
||||||
}
|
}
|
||||||
|
|
||||||
void turn_on_clocks(const pin_timer_t* timer) {
|
|
||||||
uint8_t gclk_id;
|
|
||||||
if (timer->is_tc) {
|
|
||||||
gclk_id = tc_gclk_ids[timer->index];
|
|
||||||
} else {
|
|
||||||
gclk_id = tcc_gclk_ids[timer->index];
|
|
||||||
}
|
|
||||||
// Turn on the clocks for the peripherals.
|
|
||||||
#ifdef SAMD51
|
|
||||||
if (timer->is_tc) {
|
|
||||||
switch (timer->index) {
|
|
||||||
case 0:
|
|
||||||
MCLK->APBAMASK.reg |= MCLK_APBAMASK_TC0;
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
MCLK->APBAMASK.reg |= MCLK_APBAMASK_TC1;
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
MCLK->APBBMASK.reg |= MCLK_APBBMASK_TC2;
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
MCLK->APBBMASK.reg |= MCLK_APBBMASK_TC3;
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
MCLK->APBCMASK.reg |= MCLK_APBCMASK_TC4;
|
|
||||||
break;
|
|
||||||
case 5:
|
|
||||||
MCLK->APBCMASK.reg |= MCLK_APBCMASK_TC5;
|
|
||||||
break;
|
|
||||||
case 6:
|
|
||||||
MCLK->APBDMASK.reg |= MCLK_APBDMASK_TC6;
|
|
||||||
break;
|
|
||||||
case 7:
|
|
||||||
MCLK->APBDMASK.reg |= MCLK_APBDMASK_TC7;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
switch (timer->index) {
|
|
||||||
case 0:
|
|
||||||
MCLK->APBBMASK.reg |= MCLK_APBBMASK_TCC0;
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
MCLK->APBBMASK.reg |= MCLK_APBBMASK_TCC1;
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
MCLK->APBCMASK.reg |= MCLK_APBCMASK_TCC2;
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
MCLK->APBCMASK.reg |= MCLK_APBCMASK_TCC3;
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
MCLK->APBDMASK.reg |= MCLK_APBDMASK_TCC4;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME(tannewt): TC4-TC7 can only have 100mhz inputs.
|
|
||||||
|
|
||||||
hri_gclk_write_PCHCTRL_reg(GCLK, gclk_id,
|
|
||||||
GCLK_PCHCTRL_GEN_GCLK0_Val | (1 << GCLK_PCHCTRL_CHEN_Pos));
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef SAMD21
|
|
||||||
// Determine the clock slot on the APBC bus. TCC0 is the first and 8 slots in.
|
|
||||||
uint8_t clock_slot = 8 + timer->index;
|
|
||||||
// We index TCs starting at zero but in memory they begin at three so we have to add three.
|
|
||||||
if (timer->is_tc) {
|
|
||||||
clock_slot += 3;
|
|
||||||
}
|
|
||||||
PM->APBCMASK.reg |= 1 << clock_slot;
|
|
||||||
_gclk_enable_channel(gclk_id, GCLK_CLKCTRL_GEN_GCLK0_Val);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void common_hal_pulseio_pwmout_construct(pulseio_pwmout_obj_t* self,
|
void common_hal_pulseio_pwmout_construct(pulseio_pwmout_obj_t* self,
|
||||||
const mcu_pin_obj_t* pin,
|
const mcu_pin_obj_t* pin,
|
||||||
uint16_t duty,
|
uint16_t duty,
|
||||||
@ -345,7 +201,8 @@ void common_hal_pulseio_pwmout_construct(pulseio_pwmout_obj_t* self,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
turn_on_clocks(timer);
|
// We use the zeroeth clock on either port to go full speed.
|
||||||
|
turn_on_clocks(timer->is_tc, timer->index, 0);
|
||||||
|
|
||||||
if (timer->is_tc) {
|
if (timer->is_tc) {
|
||||||
tc_periods[timer->index] = top;
|
tc_periods[timer->index] = top;
|
||||||
|
@ -35,12 +35,13 @@
|
|||||||
#include "py/runtime.h"
|
#include "py/runtime.h"
|
||||||
#include "samd21_pins.h"
|
#include "samd21_pins.h"
|
||||||
#include "shared-bindings/pulseio/PulseOut.h"
|
#include "shared-bindings/pulseio/PulseOut.h"
|
||||||
|
#include "timers.h"
|
||||||
|
|
||||||
// This timer is shared amongst all PulseOut objects under the assumption that
|
// This timer is shared amongst all PulseOut objects under the assumption that
|
||||||
// the code is single threaded.
|
// the code is single threaded.
|
||||||
static uint8_t refcount = 0;
|
static uint8_t refcount = 0;
|
||||||
|
|
||||||
static Tc* pulseout_tc_instance = NULL;
|
static uint8_t pulseout_tc_index = 0xff;
|
||||||
|
|
||||||
static __IO PORT_PINCFG_Type *active_pincfg = NULL;
|
static __IO PORT_PINCFG_Type *active_pincfg = NULL;
|
||||||
static uint16_t *pulse_buffer = NULL;
|
static uint16_t *pulse_buffer = NULL;
|
||||||
@ -56,7 +57,7 @@ static void turn_off(__IO PORT_PINCFG_Type * pincfg) {
|
|||||||
pincfg->reg = PORT_PINCFG_RESETVALUE;
|
pincfg->reg = PORT_PINCFG_RESETVALUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
void pulse_finish(struct tc_module *const module) {
|
void pulse_finish(void) {
|
||||||
pulse_index++;
|
pulse_index++;
|
||||||
|
|
||||||
if (active_pincfg == NULL) {
|
if (active_pincfg == NULL) {
|
||||||
@ -68,15 +69,27 @@ void pulse_finish(struct tc_module *const module) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
current_compare = (current_compare + pulse_buffer[pulse_index] * 3 / 4) & 0xffff;
|
current_compare = (current_compare + pulse_buffer[pulse_index] * 3 / 4) & 0xffff;
|
||||||
//tc_set_compare_value(MP_STATE_VM(pulseout_tc_instance), TC_COMPARE_CAPTURE_CHANNEL_0, current_compare);
|
Tc* tc = tc_insts[pulseout_tc_index];
|
||||||
|
tc->COUNT16.CC[0].reg = current_compare;
|
||||||
if (pulse_index % 2 == 0) {
|
if (pulse_index % 2 == 0) {
|
||||||
turn_on(active_pincfg);
|
turn_on(active_pincfg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void pulseout_interrupt_handler(uint8_t index) {
|
||||||
|
if (index != pulseout_tc_index) return;
|
||||||
|
Tc* tc = tc_insts[index];
|
||||||
|
if (!tc->COUNT16.INTFLAG.bit.MC0) return;
|
||||||
|
|
||||||
|
pulse_finish();
|
||||||
|
|
||||||
|
// Clear the interrupt bit.
|
||||||
|
tc->COUNT16.INTFLAG.reg = TC_INTFLAG_MC0;
|
||||||
|
}
|
||||||
|
|
||||||
void pulseout_reset() {
|
void pulseout_reset() {
|
||||||
refcount = 0;
|
refcount = 0;
|
||||||
pulseout_tc_instance = NULL;
|
pulseout_tc_index = 0xff;
|
||||||
active_pincfg = NULL;
|
active_pincfg = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,29 +97,44 @@ void common_hal_pulseio_pulseout_construct(pulseio_pulseout_obj_t* self,
|
|||||||
const pulseio_pwmout_obj_t* carrier) {
|
const pulseio_pwmout_obj_t* carrier) {
|
||||||
if (refcount == 0) {
|
if (refcount == 0) {
|
||||||
// Find a spare timer.
|
// Find a spare timer.
|
||||||
Tc *t = NULL;
|
Tc *tc = NULL;
|
||||||
Tc *tcs[TC_INST_NUM] = TC_INSTS;
|
uint8_t index = TC_INST_NUM - 1;
|
||||||
for (uint8_t i = TC_INST_NUM; i > 0; i--) {
|
for (; index >= 0; index--) {
|
||||||
if (tcs[i - 1]->COUNT16.CTRLA.bit.ENABLE == 0) {
|
if (tc_insts[index]->COUNT16.CTRLA.bit.ENABLE == 0) {
|
||||||
t = tcs[i - 1];
|
tc = tc_insts[index];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (t == NULL) {
|
if (tc == NULL) {
|
||||||
mp_raise_RuntimeError("All timers in use");
|
mp_raise_RuntimeError("All timers in use");
|
||||||
}
|
}
|
||||||
|
|
||||||
// struct tc_config config_tc;
|
pulseout_tc_index = index;
|
||||||
// tc_get_config_defaults(&config_tc);
|
|
||||||
//
|
|
||||||
// config_tc.counter_size = TC_COUNTER_SIZE_16BIT;
|
|
||||||
// config_tc.clock_prescaler = TC_CTRLA_PRESCALER_DIV64;
|
|
||||||
// config_tc.wave_generation = TC_WAVE_GENERATION_NORMAL_FREQ;
|
|
||||||
|
|
||||||
// tc_init(MP_STATE_VM(pulseout_tc_instance), t, &config_tc);
|
// We use GCLK0 for SAMD21 and GCLK1 for SAMD51 because they both run at 48mhz making our
|
||||||
// tc_register_callback(MP_STATE_VM(pulseout_tc_instance), pulse_finish, TC_CALLBACK_CC_CHANNEL0);
|
// math the same across the boards.
|
||||||
// tc_enable(MP_STATE_VM(pulseout_tc_instance));
|
#ifdef SAMD21
|
||||||
// tc_stop_counter(MP_STATE_VM(pulseout_tc_instance));
|
turn_on_clocks(true, index, 0);
|
||||||
|
#endif
|
||||||
|
#ifdef SAMD51
|
||||||
|
turn_on_clocks(true, index, 1);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef SAMD21
|
||||||
|
tc->COUNT16.CTRLA.reg = TC_CTRLA_MODE_COUNT16 |
|
||||||
|
TC_CTRLA_PRESCALER_DIV64 |
|
||||||
|
TC_CTRLA_WAVEGEN_NFRQ;
|
||||||
|
#endif
|
||||||
|
#ifdef SAMD51
|
||||||
|
tc_reset(tc);
|
||||||
|
tc_set_enable(tc, false);
|
||||||
|
tc->COUNT16.CTRLA.reg = TC_CTRLA_MODE_COUNT16 | TC_CTRLA_PRESCALER_DIV64;
|
||||||
|
tc->COUNT16.WAVE.reg = TC_WAVE_WAVEGEN_NFRQ;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
tc_set_enable(tc, true);
|
||||||
|
tc->COUNT16.CTRLBSET.reg = TC_CTRLBSET_CMD_STOP;
|
||||||
}
|
}
|
||||||
refcount++;
|
refcount++;
|
||||||
|
|
||||||
@ -138,8 +166,8 @@ void common_hal_pulseio_pulseout_deinit(pulseio_pulseout_obj_t* self) {
|
|||||||
|
|
||||||
refcount--;
|
refcount--;
|
||||||
if (refcount == 0) {
|
if (refcount == 0) {
|
||||||
//tc_reset(MP_STATE_VM(pulseout_tc_instance));
|
tc_reset(tc_insts[pulseout_tc_index]);
|
||||||
pulseout_tc_instance = NULL;
|
pulseout_tc_index = 0xff;
|
||||||
}
|
}
|
||||||
self->pin = NO_PIN;
|
self->pin = NO_PIN;
|
||||||
}
|
}
|
||||||
@ -154,11 +182,13 @@ void common_hal_pulseio_pulseout_send(pulseio_pulseout_obj_t* self, uint16_t* pu
|
|||||||
pulse_length = length;
|
pulse_length = length;
|
||||||
|
|
||||||
current_compare = pulses[0] * 3 / 4;
|
current_compare = pulses[0] * 3 / 4;
|
||||||
//tc_set_compare_value(MP_STATE_VM(pulseout_tc_instance), TC_COMPARE_CAPTURE_CHANNEL_0, current_compare);
|
Tc* tc = tc_insts[pulseout_tc_index];
|
||||||
|
tc->COUNT16.CC[0].reg = current_compare;
|
||||||
|
|
||||||
//tc_enable_callback(MP_STATE_VM(pulseout_tc_instance), TC_CALLBACK_CC_CHANNEL0);
|
tc->COUNT16.INTENSET.reg = TC_INTENSET_MC0;
|
||||||
|
tc_enable_interrupts(pulseout_tc_index);
|
||||||
turn_on(active_pincfg);
|
turn_on(active_pincfg);
|
||||||
//tc_start_counter(MP_STATE_VM(pulseout_tc_instance));
|
tc->COUNT16.CTRLBSET.reg = TC_CTRLBSET_CMD_RETRIGGER;
|
||||||
|
|
||||||
while(pulse_index < length) {
|
while(pulse_index < length) {
|
||||||
// Do other things while we wait. The interrupts will handle sending the
|
// Do other things while we wait. The interrupts will handle sending the
|
||||||
@ -168,7 +198,8 @@ void common_hal_pulseio_pulseout_send(pulseio_pulseout_obj_t* self, uint16_t* pu
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
//tc_stop_counter(MP_STATE_VM(pulseout_tc_instance));
|
tc->COUNT16.CTRLBSET.reg = TC_CTRLBSET_CMD_STOP;
|
||||||
//tc_disable_callback(MP_STATE_VM(pulseout_tc_instance), TC_CALLBACK_CC_CHANNEL0);
|
tc->COUNT16.INTENCLR.reg = TC_INTENCLR_MC0;
|
||||||
|
tc_disable_interrupts(pulseout_tc_index);
|
||||||
active_pincfg = NULL;
|
active_pincfg = NULL;
|
||||||
}
|
}
|
||||||
|
@ -38,5 +38,6 @@ typedef struct {
|
|||||||
} pulseio_pulseout_obj_t;
|
} pulseio_pulseout_obj_t;
|
||||||
|
|
||||||
void pulseout_reset(void);
|
void pulseout_reset(void);
|
||||||
|
void pulseout_interrupt_handler(uint8_t index);
|
||||||
|
|
||||||
#endif // MICROPY_INCLUDED_ATMEL_SAMD_COMMON_HAL_PULSEIO_PULSEOUT_H
|
#endif // MICROPY_INCLUDED_ATMEL_SAMD_COMMON_HAL_PULSEIO_PULSEOUT_H
|
||||||
|
@ -46,6 +46,7 @@
|
|||||||
#include "common-hal/analogio/AnalogIn.h"
|
#include "common-hal/analogio/AnalogIn.h"
|
||||||
#include "common-hal/analogio/AnalogOut.h"
|
#include "common-hal/analogio/AnalogOut.h"
|
||||||
#include "common-hal/microcontroller/Pin.h"
|
#include "common-hal/microcontroller/Pin.h"
|
||||||
|
#include "common-hal/pulseio/PulseOut.h"
|
||||||
#include "common-hal/pulseio/PWMOut.h"
|
#include "common-hal/pulseio/PWMOut.h"
|
||||||
#include "tick.h"
|
#include "tick.h"
|
||||||
|
|
||||||
@ -206,8 +207,8 @@ void reset_port(void) {
|
|||||||
// touchin_reset();
|
// touchin_reset();
|
||||||
// pdmin_reset();
|
// pdmin_reset();
|
||||||
// pulsein_reset();
|
// pulsein_reset();
|
||||||
// pulseout_reset();
|
|
||||||
// #endif
|
// #endif
|
||||||
|
pulseout_reset();
|
||||||
pwmout_reset();
|
pwmout_reset();
|
||||||
|
|
||||||
analogin_reset();
|
analogin_reset();
|
||||||
|
258
ports/atmel-samd/timers.c
Normal file
258
ports/atmel-samd/timers.c
Normal file
@ -0,0 +1,258 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "timers.h"
|
||||||
|
|
||||||
|
#include "common-hal/pulseio/PulseOut.h"
|
||||||
|
|
||||||
|
#ifdef SAMD21
|
||||||
|
#include "hpl/gclk/hpl_gclk_base.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// This bitmask keeps track of which channels of a TCC are currently claimed.
|
||||||
|
#ifdef SAMD21
|
||||||
|
const uint8_t tcc_cc_num[3] = {4, 2, 2};
|
||||||
|
const uint8_t tc_gclk_ids[TC_INST_NUM] = {TC3_GCLK_ID,
|
||||||
|
TC4_GCLK_ID,
|
||||||
|
TC5_GCLK_ID,
|
||||||
|
#ifdef TC6_GCLK_ID
|
||||||
|
, TC6_GCLK_ID
|
||||||
|
#endif
|
||||||
|
#ifdef TC7_GCLK_ID
|
||||||
|
, TC7_GCLK_ID
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
const uint8_t tcc_gclk_ids[3] = {TCC0_GCLK_ID, TCC1_GCLK_ID, TCC2_GCLK_ID};
|
||||||
|
#endif
|
||||||
|
#ifdef SAMD51
|
||||||
|
static const uint8_t tcc_cc_num[5] = {6, 4, 3, 2, 2};
|
||||||
|
const uint8_t tc_gclk_ids[TC_INST_NUM] = {TC0_GCLK_ID,
|
||||||
|
TC1_GCLK_ID,
|
||||||
|
TC2_GCLK_ID,
|
||||||
|
TC3_GCLK_ID
|
||||||
|
#ifdef TC4_GCLK_ID
|
||||||
|
, TC4_GCLK_ID
|
||||||
|
#endif
|
||||||
|
#ifdef TC5_GCLK_ID
|
||||||
|
, TC5_GCLK_ID
|
||||||
|
#endif
|
||||||
|
#ifdef TC6_GCLK_ID
|
||||||
|
, TC6_GCLK_ID
|
||||||
|
#endif
|
||||||
|
#ifdef TC7_GCLK_ID
|
||||||
|
, TC7_GCLK_ID
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
const uint8_t tcc_gclk_ids[5] = {TCC0_GCLK_ID, TCC1_GCLK_ID, TCC2_GCLK_ID, TCC3_GCLK_ID,
|
||||||
|
TCC4_GCLK_ID};
|
||||||
|
#endif
|
||||||
|
Tc* const tc_insts[TC_INST_NUM] = TC_INSTS;
|
||||||
|
Tcc* const tcc_insts[TCC_INST_NUM] = TCC_INSTS;
|
||||||
|
|
||||||
|
IRQn_Type const tc_irq[TC_INST_NUM] = {
|
||||||
|
#ifdef TC0
|
||||||
|
TC0_IRQn,
|
||||||
|
#endif
|
||||||
|
#ifdef TC1
|
||||||
|
TC1_IRQn,
|
||||||
|
#endif
|
||||||
|
#ifdef TC2
|
||||||
|
TC2_IRQn,
|
||||||
|
#endif
|
||||||
|
TC3_IRQn,
|
||||||
|
TC4_IRQn,
|
||||||
|
TC5_IRQn
|
||||||
|
#ifdef TC6
|
||||||
|
, TC6_IRQn
|
||||||
|
#endif
|
||||||
|
#ifdef TC7
|
||||||
|
, TC7_IRQn
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
void turn_on_clocks(bool is_tc, uint8_t index, uint32_t gclk_index) {
|
||||||
|
uint8_t gclk_id;
|
||||||
|
if (is_tc) {
|
||||||
|
gclk_id = tc_gclk_ids[index];
|
||||||
|
} else {
|
||||||
|
gclk_id = tcc_gclk_ids[index];
|
||||||
|
}
|
||||||
|
// Turn on the clocks for the peripherals.
|
||||||
|
#ifdef SAMD51
|
||||||
|
if (is_tc) {
|
||||||
|
switch (index) {
|
||||||
|
case 0:
|
||||||
|
MCLK->APBAMASK.reg |= MCLK_APBAMASK_TC0;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
MCLK->APBAMASK.reg |= MCLK_APBAMASK_TC1;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
MCLK->APBBMASK.reg |= MCLK_APBBMASK_TC2;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
MCLK->APBBMASK.reg |= MCLK_APBBMASK_TC3;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
MCLK->APBCMASK.reg |= MCLK_APBCMASK_TC4;
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
MCLK->APBCMASK.reg |= MCLK_APBCMASK_TC5;
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
MCLK->APBDMASK.reg |= MCLK_APBDMASK_TC6;
|
||||||
|
break;
|
||||||
|
case 7:
|
||||||
|
MCLK->APBDMASK.reg |= MCLK_APBDMASK_TC7;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
switch (index) {
|
||||||
|
case 0:
|
||||||
|
MCLK->APBBMASK.reg |= MCLK_APBBMASK_TCC0;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
MCLK->APBBMASK.reg |= MCLK_APBBMASK_TCC1;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
MCLK->APBCMASK.reg |= MCLK_APBCMASK_TCC2;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
MCLK->APBCMASK.reg |= MCLK_APBCMASK_TCC3;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
MCLK->APBDMASK.reg |= MCLK_APBDMASK_TCC4;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME(tannewt): TC4-TC7 can only have 100mhz inputs.
|
||||||
|
|
||||||
|
hri_gclk_write_PCHCTRL_reg(GCLK, gclk_id,
|
||||||
|
gclk_index | (1 << GCLK_PCHCTRL_CHEN_Pos));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef SAMD21
|
||||||
|
// Determine the clock slot on the APBC bus. TCC0 is the first and 8 slots in.
|
||||||
|
uint8_t clock_slot = 8 + index;
|
||||||
|
// We index TCs starting at zero but in memory they begin at three so we have to add three.
|
||||||
|
if (is_tc) {
|
||||||
|
clock_slot += 3;
|
||||||
|
}
|
||||||
|
PM->APBCMASK.reg |= 1 << clock_slot;
|
||||||
|
_gclk_enable_channel(gclk_id, gclk_index);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void tc_set_enable(Tc* tc, bool enable) {
|
||||||
|
tc->COUNT16.CTRLA.bit.ENABLE = enable;
|
||||||
|
#ifdef SAMD21
|
||||||
|
while (tc->COUNT16.STATUS.bit.SYNCBUSY != 0) {
|
||||||
|
/* Wait for sync */
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef SAMD51
|
||||||
|
while (tc->COUNT16.SYNCBUSY.bit.ENABLE != 0) {
|
||||||
|
/* Wait for sync */
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void tc_enable_interrupts(uint8_t tc_index) {
|
||||||
|
NVIC_DisableIRQ(tc_irq[tc_index]);
|
||||||
|
NVIC_ClearPendingIRQ(tc_irq[tc_index]);
|
||||||
|
NVIC_EnableIRQ(tc_irq[tc_index]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tc_disable_interrupts(uint8_t tc_index) {
|
||||||
|
NVIC_DisableIRQ(tc_irq[tc_index]);
|
||||||
|
NVIC_ClearPendingIRQ(tc_irq[tc_index]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tcc_set_enable(Tcc* tcc, bool enable) {
|
||||||
|
tcc->CTRLA.bit.ENABLE = enable;
|
||||||
|
while (tcc->SYNCBUSY.bit.ENABLE != 0) {
|
||||||
|
/* Wait for sync */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void tc_wait_for_sync(Tc* tc) {
|
||||||
|
#ifdef SAMD21
|
||||||
|
while (tc->COUNT16.STATUS.bit.SYNCBUSY != 0) {}
|
||||||
|
#endif
|
||||||
|
#ifdef SAMD51
|
||||||
|
while (tc->COUNT16.SYNCBUSY.reg != 0) {}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void tc_reset(Tc* tc) {
|
||||||
|
tc->COUNT16.CTRLA.bit.SWRST = 1;
|
||||||
|
while (tc->COUNT16.CTRLA.bit.SWRST == 1) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void shared_timer_handler(bool is_tc, uint8_t index) {
|
||||||
|
// Add calls to interrupt handlers for specific functionality here.
|
||||||
|
if (is_tc) {
|
||||||
|
pulseout_interrupt_handler(index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TCC0_Handler(void) {
|
||||||
|
shared_timer_handler(false, 0);
|
||||||
|
}
|
||||||
|
void TCC1_Handler(void) {
|
||||||
|
shared_timer_handler(false, 1);
|
||||||
|
}
|
||||||
|
void TCC2_Handler(void) {
|
||||||
|
shared_timer_handler(false, 2);
|
||||||
|
}
|
||||||
|
void TC3_Handler(void) {
|
||||||
|
shared_timer_handler(true, 0);
|
||||||
|
}
|
||||||
|
void TC4_Handler(void) {
|
||||||
|
shared_timer_handler(true, 1);
|
||||||
|
}
|
||||||
|
void TC5_Handler(void) {
|
||||||
|
shared_timer_handler(true, 2);
|
||||||
|
}
|
||||||
|
#ifdef TC6
|
||||||
|
void TC6_Handler(void) {
|
||||||
|
shared_timer_handler(true, 3);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef TC7
|
||||||
|
void TC7_Handler(void) {
|
||||||
|
shared_timer_handler(true, 4);
|
||||||
|
}
|
||||||
|
#endif
|
67
ports/atmel-samd/timers.h
Normal file
67
ports/atmel-samd/timers.h
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the MicroPython project, http://micropython.org/
|
||||||
|
*
|
||||||
|
* The MIT License (MIT)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 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_TIMERS_H
|
||||||
|
#define MICROPY_INCLUDED_ATMEL_SAMD_TIMERS_H
|
||||||
|
|
||||||
|
#include "include/sam.h"
|
||||||
|
|
||||||
|
#ifdef SAMD21
|
||||||
|
const uint8_t tcc_cc_num[3];
|
||||||
|
const uint8_t tc_gclk_ids[TC_INST_NUM];
|
||||||
|
const uint8_t tcc_gclk_ids[3];
|
||||||
|
#endif
|
||||||
|
#ifdef SAMD51
|
||||||
|
static const uint8_t tcc_cc_num[5];
|
||||||
|
const uint8_t tc_gclk_ids[TC_INST_NUM];
|
||||||
|
const uint8_t tcc_gclk_ids[5];
|
||||||
|
#endif
|
||||||
|
Tc* const tc_insts[TC_INST_NUM];
|
||||||
|
Tcc* const tcc_insts[TCC_INST_NUM];
|
||||||
|
|
||||||
|
void turn_on_clocks(bool is_tc, uint8_t index, uint32_t gclk_index);
|
||||||
|
void tc_set_enable(Tc* tc, bool enable);
|
||||||
|
void tcc_set_enable(Tcc* tcc, bool enable);
|
||||||
|
void tc_wait_for_sync(Tc* tc);
|
||||||
|
void tc_reset(Tc* tc);
|
||||||
|
|
||||||
|
void tc_enable_interrupts(uint8_t tc_index);
|
||||||
|
void tc_disable_interrupts(uint8_t tc_index);
|
||||||
|
|
||||||
|
// Handlers
|
||||||
|
void TCC0_Handler(void);
|
||||||
|
void TCC1_Handler(void);
|
||||||
|
void TCC2_Handler(void);
|
||||||
|
void TC3_Handler(void);
|
||||||
|
void TC4_Handler(void);
|
||||||
|
void TC5_Handler(void);
|
||||||
|
#ifdef TC6
|
||||||
|
void TC6_Handler(void);
|
||||||
|
#endif
|
||||||
|
#ifdef TC7
|
||||||
|
void TC7_Handler(void);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // MICROPY_INCLUDED_ATMEL_SAMD_TIMERS_H
|
Loading…
x
Reference in New Issue
Block a user