diff options
author | Dave Hylands <dhylands@gmail.com> | 2014-08-20 13:21:11 -0700 |
---|---|---|
committer | Dave Hylands <dhylands@gmail.com> | 2014-09-19 09:26:13 -0700 |
commit | becbc87fd73c98664b472b4a06c2b54558dd5669 (patch) | |
tree | 44b1e1daaab750fa2c10899bc9c3489ab4ec738f /teensy | |
parent | 2842945e760c98793cb0c62a66345ab8850afc1a (diff) | |
download | micropython-becbc87fd73c98664b472b4a06c2b54558dd5669.tar.gz micropython-becbc87fd73c98664b472b4a06c2b54558dd5669.zip |
Add Timer support (PWM, OC, IC) for stmhal and teensy
Diffstat (limited to 'teensy')
-rw-r--r-- | teensy/Makefile | 8 | ||||
-rw-r--r-- | teensy/hal_ftm.c | 201 | ||||
-rw-r--r-- | teensy/hal_ftm.h | 184 | ||||
-rw-r--r-- | teensy/hal_gpio.c | 9 | ||||
-rwxr-xr-x | teensy/make-pins.py | 28 | ||||
-rw-r--r-- | teensy/mk20dx256_af.csv | 2 | ||||
-rw-r--r-- | teensy/modpyb.c | 4 | ||||
-rw-r--r-- | teensy/mpconfigport.h | 6 | ||||
-rw-r--r-- | teensy/pin_defs_teensy.c | 23 | ||||
-rw-r--r-- | teensy/pin_defs_teensy.h | 4 | ||||
-rw-r--r-- | teensy/qstrdefsport.h | 36 | ||||
-rw-r--r-- | teensy/reg.c | 52 | ||||
-rw-r--r-- | teensy/reg.h | 8 | ||||
-rw-r--r-- | teensy/teensy_hal.h | 13 | ||||
-rw-r--r-- | teensy/teensy_pins.csv | 1 | ||||
-rw-r--r-- | teensy/timer.c | 897 | ||||
-rw-r--r-- | teensy/timer.h | 31 |
17 files changed, 1482 insertions, 25 deletions
diff --git a/teensy/Makefile b/teensy/Makefile index 09bee1dbcd..478389e30f 100644 --- a/teensy/Makefile +++ b/teensy/Makefile @@ -42,8 +42,11 @@ CFLAGS += -Og -ggdb else CFLAGS += -Os #-DNDEBUG endif +CFLAGS += -fdata-sections -ffunction-sections +LDFLAGS += -Wl,--gc-sections SRC_C = \ + hal_ftm.c \ hal_gpio.c \ help.c \ import.c \ @@ -54,7 +57,9 @@ SRC_C = \ memzip.c \ modpyb.c \ pin_defs_teensy.c \ + reg.c \ teensy_hal.c \ + timer.c \ uart.c \ usb.c \ @@ -141,6 +146,7 @@ GEN_PINS_SRC = $(BUILD)/pins_gen.c GEN_PINS_HDR = $(HEADER_BUILD)/pins.h GEN_PINS_QSTR = $(BUILD)/pins_qstr.h GEN_PINS_AF_CONST = $(HEADER_BUILD)/pins_af_const.h +GEN_PINS_AF_PY = $(BUILD)/pins_af.py # Making OBJ use an order-only depenedency on the generated pins.h file # has the side effect of making the pins.h file before we actually compile @@ -153,7 +159,7 @@ $(OBJ): | $(HEADER_BUILD)/pins.h # both pins_$(BOARD).c and pins.h $(BUILD)/%_gen.c $(HEADER_BUILD)/%.h $(HEADER_BUILD)/%_af_const.h $(BUILD)/%_qstr.h: teensy_%.csv $(MAKE_PINS) $(AF_FILE) $(PREFIX_FILE) | $(HEADER_BUILD) $(ECHO) "Create $@" - $(Q)$(PYTHON) $(MAKE_PINS) --board $(BOARD_PINS) --af $(AF_FILE) --prefix $(PREFIX_FILE) --hdr $(GEN_PINS_HDR) --qstr $(GEN_PINS_QSTR) --af-const $(GEN_PINS_AF_CONST) > $(GEN_PINS_SRC) + $(Q)$(PYTHON) $(MAKE_PINS) --board $(BOARD_PINS) --af $(AF_FILE) --prefix $(PREFIX_FILE) --hdr $(GEN_PINS_HDR) --qstr $(GEN_PINS_QSTR) --af-const $(GEN_PINS_AF_CONST) --af-py $(GEN_PINS_AF_PY) > $(GEN_PINS_SRC) $(BUILD)/pins_gen.o: $(BUILD)/pins_gen.c $(call compile_c) diff --git a/teensy/hal_ftm.c b/teensy/hal_ftm.c new file mode 100644 index 0000000000..28992881be --- /dev/null +++ b/teensy/hal_ftm.c @@ -0,0 +1,201 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * 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 <stdint.h> +#include <mk20dx128.h> +#include "teensy_hal.h" + +void HAL_FTM_Base_Init(FTM_HandleTypeDef *hftm) { + /* Check the parameters */ + FTM_TypeDef *FTMx = hftm->Instance; + assert_param(IS_FTM_INSTANCE(FTMx)); + assert_param(IS_FTM_PRESCALERSHIFT(hftm->Init.PrescalerShift)); + assert_param(IS_FTM_COUNTERMODE(hftm->Init.CounterMode)); + assert_param(IS_FTM_PERIOD(hftm->Init.Period)); + + hftm->State = HAL_FTM_STATE_BUSY; + + FTMx->MODE = FTM_MODE_WPDIS; + FTMx->SC = 0; + FTMx->MOD = hftm->Init.Period; + uint32_t sc = FTM_SC_PS(hftm->Init.PrescalerShift); + if (hftm->Init.CounterMode == FTM_COUNTERMODE_CENTER) { + sc |= FTM_SC_CPWMS; + } + FTMx->SC = sc; + + hftm->State = HAL_FTM_STATE_READY; +} + +void HAL_FTM_Base_Start(FTM_HandleTypeDef *hftm) { + FTM_TypeDef *FTMx = hftm->Instance; + assert_param(IS_FTM_INSTANCE(FTMx)); + + hftm->State = HAL_FTM_STATE_BUSY; + + FTMx->CNT = 0; + FTMx->SC &= ~FTM_SC_CLKS(3); + FTMx->SC |= FTM_SC_CLKS(1); + + hftm->State = HAL_FTM_STATE_READY; +} + +void HAL_FTM_Base_Start_IT(FTM_HandleTypeDef *hftm) { + FTM_TypeDef *FTMx = hftm->Instance; + assert_param(IS_FTM_INSTANCE(FTMx)); + + hftm->State = HAL_FTM_STATE_BUSY; + + FTMx->CNT = 0; + FTMx->SC |= FTM_SC_CLKS(1) | FTM_SC_TOIE; + + hftm->State = HAL_FTM_STATE_READY; +} + +void HAL_FTM_Base_DeInit(FTM_HandleTypeDef *hftm) { + assert_param(IS_FTM_INSTANCE(hftm->Instance)); + + hftm->State = HAL_FTM_STATE_BUSY; + + __HAL_FTM_DISABLE_TOF_IT(hftm); + + hftm->State = HAL_FTM_STATE_RESET; +} + +void HAL_FTM_OC_Init(FTM_HandleTypeDef *hftm) { + HAL_FTM_Base_Init(hftm); +} + +void HAL_FTM_OC_ConfigChannel(FTM_HandleTypeDef *hftm, FTM_OC_InitTypeDef* sConfig, uint32_t channel) { + FTM_TypeDef *FTMx = hftm->Instance; + assert_param(IS_FTM_INSTANCE(FTMx)); + assert_param(IS_FTM_CHANNEL(channel)); + assert_param(IS_FTM_OC_MODE(sConfig->OCMode)); + assert_param(IS_FTM_OC_PULSE(sConfig->Pulse)); + assert_param(IS_FTM_OC_POLARITY(sConfig->OCPolarity)); + + hftm->State = HAL_FTM_STATE_BUSY; + + FTMx->channel[channel].CSC = sConfig->OCMode; + FTMx->channel[channel].CV = sConfig->Pulse; + if (sConfig->OCPolarity & 1) { + FTMx->POL |= (1 << channel); + } else { + FTMx->POL &= ~(1 << channel); + } + + hftm->State = HAL_FTM_STATE_READY; +} + +void HAL_FTM_OC_Start(FTM_HandleTypeDef *hftm, uint32_t channel) { + // Nothing else to do +} + +void HAL_FTM_OC_Start_IT(FTM_HandleTypeDef *hftm, uint32_t channel) { + FTM_TypeDef *FTMx = hftm->Instance; + assert_param(IS_FTM_INSTANCE(FTMx)); + + FTMx->channel[channel].CSC |= FTM_CSC_CHIE; +} + +void HAL_FTM_OC_DeInit(FTM_HandleTypeDef *hftm) { + HAL_FTM_Base_DeInit(hftm); +} + +void HAL_FTM_PWM_Init(FTM_HandleTypeDef *hftm) { + HAL_FTM_Base_Init(hftm); +} + +void HAL_FTM_PWM_ConfigChannel(FTM_HandleTypeDef *hftm, FTM_OC_InitTypeDef* sConfig, uint32_t channel) { + FTM_TypeDef *FTMx = hftm->Instance; + assert_param(IS_FTM_INSTANCE(FTMx)); + assert_param(IS_FTM_CHANNEL(channel)); + assert_param(IS_FTM_PWM_MODE(sConfig->OCMode)); + assert_param(IS_FTM_OC_PULSE(sConfig->Pulse)); + assert_param(IS_FTM_OC_POLARITY(sConfig->OCPolarity)); + + hftm->State = HAL_FTM_STATE_BUSY; + + FTMx->channel[channel].CSC = sConfig->OCMode; + FTMx->channel[channel].CV = sConfig->Pulse; + if (sConfig->OCPolarity & 1) { + FTMx->POL |= (1 << channel); + } else { + FTMx->POL &= ~(1 << channel); + } + + hftm->State = HAL_FTM_STATE_READY; +} + +void HAL_FTM_PWM_Start(FTM_HandleTypeDef *hftm, uint32_t channel) { + // Nothing else to do +} + +void HAL_FTM_PWM_Start_IT(FTM_HandleTypeDef *hftm, uint32_t channel) { + FTM_TypeDef *FTMx = hftm->Instance; + assert_param(IS_FTM_INSTANCE(FTMx)); + + FTMx->channel[channel].CSC |= FTM_CSC_CHIE; +} + +void HAL_FTM_PWM_DeInit(FTM_HandleTypeDef *hftm) { + HAL_FTM_Base_DeInit(hftm); +} + +void HAL_FTM_IC_Init(FTM_HandleTypeDef *hftm) { + HAL_FTM_Base_Init(hftm); +} + +void HAL_FTM_IC_ConfigChannel(FTM_HandleTypeDef *hftm, FTM_IC_InitTypeDef* sConfig, uint32_t channel) { + FTM_TypeDef *FTMx = hftm->Instance; + assert_param(IS_FTM_INSTANCE(FTMx)); + assert_param(IS_FTM_CHANNEL(channel)); + assert_param(IS_FTM_IC_POLARITY(sConfig->ICPolarity)); + + hftm->State = HAL_FTM_STATE_BUSY; + + FTMx->channel[channel].CSC = sConfig->ICPolarity; + + hftm->State = HAL_FTM_STATE_READY; +} + +void HAL_FTM_IC_Start(FTM_HandleTypeDef *hftm, uint32_t channel) { + //FTM_TypeDef *FTMx = hftm->Instance; + //assert_param(IS_FTM_INSTANCE(FTMx)); + + // Nothing else to do +} + +void HAL_FTM_IC_Start_IT(FTM_HandleTypeDef *hftm, uint32_t channel) { + FTM_TypeDef *FTMx = hftm->Instance; + assert_param(IS_FTM_INSTANCE(FTMx)); + + FTMx->channel[channel].CSC |= FTM_CSC_CHIE; +} + +void HAL_FTM_IC_DeInit(FTM_HandleTypeDef *hftm) { + HAL_FTM_Base_DeInit(hftm); +} diff --git a/teensy/hal_ftm.h b/teensy/hal_ftm.h new file mode 100644 index 0000000000..3dc15300d7 --- /dev/null +++ b/teensy/hal_ftm.h @@ -0,0 +1,184 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * 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. + */ + +#define FTM0 ((FTM_TypeDef *)&FTM0_SC) +#define FTM1 ((FTM_TypeDef *)&FTM1_SC) +#define FTM2 ((FTM_TypeDef *)&FTM2_SC) + +typedef struct { + volatile uint32_t CSC; // Channel x Status And Control + volatile uint32_t CV; // Channel x Value +} FTM_ChannelTypeDef; + +typedef struct { + volatile uint32_t SC; // Status And Control + volatile uint32_t CNT; // Counter + volatile uint32_t MOD; // Modulo + FTM_ChannelTypeDef channel[8]; + volatile uint32_t CNTIN; // Counter Initial Value + volatile uint32_t STATUS; // Capture And Compare Status + volatile uint32_t MODE; // Features Mode Selection + volatile uint32_t SYNC; // Synchronization + volatile uint32_t OUTINIT; // Initial State For Channels Output + volatile uint32_t OUTMASK; // Output Mask + volatile uint32_t COMBINE; // Function For Linked Channels + volatile uint32_t DEADTIME; // Deadtime Insertion Control + volatile uint32_t EXTTRIG; // FTM External Trigger + volatile uint32_t POL; // Channels Polarity + volatile uint32_t FMS; // Fault Mode Status + volatile uint32_t FILTER; // Input Capture Filter Control + volatile uint32_t FLTCTRL; // Fault Control + volatile uint32_t QDCTRL; // Quadrature Decoder Control And Status + volatile uint32_t CONF; // Configuration + volatile uint32_t FLTPOL; // FTM Fault Input Polarity + volatile uint32_t SYNCONF; // Synchronization Configuration + volatile uint32_t INVCTRL; // FTM Inverting Control + volatile uint32_t SWOCTRL; // FTM Software Output Control + volatile uint32_t PWMLOAD; // FTM PWM Load +} FTM_TypeDef; + +typedef struct { + uint32_t PrescalerShift; // Sets the prescaler to 1 << PrescalerShift + uint32_t CounterMode; // One of FTM_COUNTERMODE_xxx + uint32_t Period; // Specifies the Period for determining timer overflow +} FTM_Base_InitTypeDef; + +typedef struct { + uint32_t OCMode; // One of FTM_OCMODE_xxx + uint32_t Pulse; // Specifies initial pulse width (0-0xffff) + uint32_t OCPolarity; // One of FTM_OCPOLRITY_xxx +} FTM_OC_InitTypeDef; + +typedef struct { + uint32_t ICPolarity; // Specifies Rising/Falling/Both +} FTM_IC_InitTypeDef; + +#define IS_FTM_INSTANCE(INSTANCE) (((INSTANCE) == FTM0) || \ + ((INSTANCE) == FTM1) || \ + ((INSTANCE) == FTM2)) + +#define IS_FTM_PRESCALERSHIFT(PRESCALERSHIFT) (((PRESCALERSHIFT) & ~7) == 0) + +#define FTM_COUNTERMODE_UP (0) +#define FTM_COUNTERMODE_CENTER (FTM_SC_CPWMS) + +#define IS_FTM_COUNTERMODE(MODE) (((MODE) == FTM_COUNTERMODE_UP) ||\ + ((MODE) == FTM_COUNTERMODE_CENTER)) + +#define IS_FTM_PERIOD(PERIOD) (((PERIOD) & 0xFFFF0000) == 0) + +#define FTM_CSC_CHF 0x80 +#define FTM_CSC_CHIE 0x40 +#define FTM_CSC_MSB 0x20 +#define FTM_CSC_MSA 0x10 +#define FTM_CSC_ELSB 0x08 +#define FTM_CSC_ELSA 0x04 +#define FTM_CSC_DMA 0x01 + +#define FTM_OCMODE_TIMING (0) +#define FTM_OCMODE_ACTIVE (FTM_CSC_MSA | FTM_CSC_ELSB | FTM_CSC_ELSA) +#define FTM_OCMODE_INACTIVE (FTM_CSC_MSA | FTM_CSC_ELSB) +#define FTM_OCMODE_TOGGLE (FTM_CSC_MSA | FTM_CSC_ELSA) +#define FTM_OCMODE_PWM1 (FTM_CSC_MSB | FTM_CSC_ELSB) +#define FTM_OCMODE_PWM2 (FTM_CSC_MSB | FTM_CSC_ELSA) + +#define IS_FTM_OC_MODE(mode) ((mode) == FTM_OCMODE_TIMING || \ + (mode) == FTM_OCMODE_ACTIVE || \ + (mode) == FTM_OCMODE_INACTIVE || \ + (mode) == FTM_OCMODE_TOGGLE ) + +#define IS_FTM_PWM_MODE(mode) ((mode) == FTM_OCMODE_PWM1 || \ + (mode) == FTM_OCMODE_PWM2) + +#define IS_FTM_CHANNEL(channel) (((channel) & ~7) == 0) + +#define IS_FTM_PULSE(pulse) (((pulse) & ~0xffff) == 0) + +#define FTM_OCPOLARITY_HIGH (0) +#define FTM_OCPOLARITY_LOW (1) + +#define IS_FTM_OC_POLARITY(polarity) ((polarity) == FTM_OCPOLARITY_HIGH || \ + (polarity) == FTM_OCPOLARITY_LOW) + +#define FTM_ICPOLARITY_RISING (FTM_CSC_ELSA) +#define FTM_ICPOLARITY_FALLING (FTM_CSC_ELSB) +#define FTM_ICPOLARITY_BOTH (FTM_CSC_ELSA | FTM_CSC_ELSB) + +#define IS_FTM_IC_POLARITY(polarity) ((polarity) == FTM_ICPOLARITY_RISING || \ + (polarity) == FTM_ICPOLARITY_FALLING || \ + (polarity) == FTM_ICPOLARITY_BOTH) + +typedef enum { + HAL_FTM_STATE_RESET = 0x00, + HAL_FTM_STATE_READY = 0x01, + HAL_FTM_STATE_BUSY = 0x02, +} HAL_FTM_State; + +typedef struct { + FTM_TypeDef *Instance; + FTM_Base_InitTypeDef Init; + HAL_FTM_State State; + +} FTM_HandleTypeDef; + +#define __HAL_FTM_GET_TOF_FLAG(HANDLE) (((HANDLE)->Instance->SC & FTM_SC_TOF) != 0) +#define __HAL_FTM_CLEAR_TOF_FLAG(HANDLE) ((HANDLE)->Instance->SC &= ~FTM_SC_TOF) + +#define __HAL_FTM_GET_TOF_IT(HANDLE) (((HANDLE)->Instance->SC & FTM_SC_TOIE) != 0) +#define __HAL_FTM_ENABLE_TOF_IT(HANDLE) ((HANDLE)->Instance->SC |= FTM_SC_TOIE) +#define __HAL_FTM_DISABLE_TOF_IT(HANDLE) ((HANDLE)->Instance->SC &= ~FTM_SC_TOIE) + +#define __HAL_FTM_GET_CH_FLAG(HANDLE, CH) (((HANDLE)->Instance->channel[CH].CSC & FTM_CSC_CHF) != 0) +#define __HAL_FTM_CLEAR_CH_FLAG(HANDLE, CH) ((HANDLE)->Instance->channel[CH].CSC &= ~FTM_CSC_CHF) + +#define __HAL_FTM_GET_CH_IT(HANDLE, CH) (((HANDLE)->Instance->channel[CH].CSC & FTM_CSC_CHIE) != 0) +#define __HAL_FTM_ENABLE_CH_IT(HANDLE, CH) ((HANDLE)->Instance->channel[CH].CSC |= FTM_CSC_CHIE) +#define __HAL_FTM_DISABLE_CH_IT(HANDLE, CH) ((HANDLE)->Instance->channel[CH].CSC &= ~FTM_CSC_CHIE) + +void HAL_FTM_Base_Init(FTM_HandleTypeDef *hftm); +void HAL_FTM_Base_Start(FTM_HandleTypeDef *hftm); +void HAL_FTM_Base_Start_IT(FTM_HandleTypeDef *hftm); +void HAL_FTM_Base_DeInit(FTM_HandleTypeDef *hftm); + +void HAL_FTM_OC_Init(FTM_HandleTypeDef *hftm); +void HAL_FTM_OC_ConfigChannel(FTM_HandleTypeDef *hftm, FTM_OC_InitTypeDef* sConfig, uint32_t channel); +void HAL_FTM_OC_Start(FTM_HandleTypeDef *hftm, uint32_t channel); +void HAL_FTM_OC_Start_IT(FTM_HandleTypeDef *hftm, uint32_t channel); +void HAL_FTM_OC_DeInit(FTM_HandleTypeDef *hftm); + +void HAL_FTM_PWM_Init(FTM_HandleTypeDef *hftm); +void HAL_FTM_PWM_ConfigChannel(FTM_HandleTypeDef *hftm, FTM_OC_InitTypeDef* sConfig, uint32_t channel); +void HAL_FTM_PWM_Start(FTM_HandleTypeDef *hftm, uint32_t channel); +void HAL_FTM_PWM_Start_IT(FTM_HandleTypeDef *hftm, uint32_t channel); +void HAL_FTM_PWM_DeInit(FTM_HandleTypeDef *hftm); + +void HAL_FTM_IC_Init(FTM_HandleTypeDef *hftm); +void HAL_FTM_IC_ConfigChannel(FTM_HandleTypeDef *hftm, FTM_IC_InitTypeDef* sConfig, uint32_t channel); +void HAL_FTM_IC_Start(FTM_HandleTypeDef *hftm, uint32_t channel); +void HAL_FTM_IC_Start_IT(FTM_HandleTypeDef *hftm, uint32_t channel); +void HAL_FTM_IC_DeInit(FTM_HandleTypeDef *hftm); + + diff --git a/teensy/hal_gpio.c b/teensy/hal_gpio.c index 218560e29b..e65d03410e 100644 --- a/teensy/hal_gpio.c +++ b/teensy/hal_gpio.c @@ -17,7 +17,6 @@ void HAL_GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_Init) if ((GPIO_Init->Pin & bitmask) == 0) { continue; } - volatile uint32_t *port_pcr = GPIO_PIN_TO_PORT_PCR(GPIOx, position); /*--------------------- GPIO Mode Configuration ------------------------*/ @@ -50,6 +49,8 @@ void HAL_GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_Init) /* Check the Speed parameter */ assert_param(IS_GPIO_SPEED(GPIO_Init->Speed)); + *port_pcr |= PORT_PCR_DSE; + /* Configure the IO Speed */ if (GPIO_Init->Speed > GPIO_SPEED_MEDIUM) { *port_pcr &= ~PORT_PCR_SRE; @@ -59,10 +60,12 @@ void HAL_GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_Init) /* Configure the IO Output Type */ if (GPIO_Init->Mode & GPIO_OUTPUT_TYPE) { - *port_pcr |= PORT_PCR_ODE; + *port_pcr |= PORT_PCR_ODE; // OD } else { - *port_pcr &= ~PORT_PCR_ODE; + *port_pcr &= ~PORT_PCR_ODE; // PP } + } else { + *port_pcr &= ~PORT_PCR_DSE; } /* Activate the Pull-up or Pull down resistor for the current IO */ diff --git a/teensy/make-pins.py b/teensy/make-pins.py index 6df1e4162b..f7ba7a04a5 100755 --- a/teensy/make-pins.py +++ b/teensy/make-pins.py @@ -8,7 +8,7 @@ import sys import csv SUPPORTED_FN = { - 'FTM' : ['CH0', 'CH1', 'CH2', 'CH3', + 'FTM' : ['CH0', 'CH1', 'CH2', 'CH3', 'CH4', 'CH5', 'CH6', 'CH7', 'QD_PHA', 'QD_PHB'], 'I2C' : ['SDA', 'SCL'], 'UART' : ['RX', 'TX', 'CTS', 'RTS'], @@ -313,6 +313,17 @@ class Pins(object): print(' { %-*s %s },' % (mux_name_width + 26, key, val), file=af_const_file) + def print_af_py(self, af_py_filename): + with open(af_py_filename, 'wt') as af_py_file: + print('PINS_AF = (', file=af_py_file); + for named_pin in self.board_pins: + print(" ('%s', " % named_pin.name(), end='', file=af_py_file) + for af in named_pin.pin().alt_fn: + if af.is_supported(): + print("(%d, '%s'), " % (af.idx, af.af_str), end='', file=af_py_file) + print('),', file=af_py_file) + print(')', file=af_py_file) + def main(): parser = argparse.ArgumentParser( @@ -324,13 +335,19 @@ def main(): "-a", "--af", dest="af_filename", help="Specifies the alternate function file for the chip", - default="stm32f4xx-af.csv" + default="mk20dx256_af.csv" ) parser.add_argument( "--af-const", dest="af_const_filename", help="Specifies header file for alternate function constants.", - default="build/pins-af-const.h" + default="build/pins_af_const.h" + ) + parser.add_argument( + "--af-py", + dest="af_py_filename", + help="Specifies the filename for the python alternate function mappings.", + default="build/pins_af.py" ) parser.add_argument( "-b", "--board", @@ -341,13 +358,13 @@ def main(): "-p", "--prefix", dest="prefix_filename", help="Specifies beginning portion of generated pins file", - default="stm32f4xx-prefix.c" + default="mk20dx256_prefix.c" ) parser.add_argument( "-q", "--qstr", dest="qstr_filename", help="Specifies name of generated qstr header file", - default="build/pins-qstr.h" + default="build/pins_qstr.h" ) parser.add_argument( "-r", "--hdr", @@ -381,6 +398,7 @@ def main(): pins.print_header(args.hdr_filename) pins.print_qstr(args.qstr_filename) pins.print_af_hdr(args.af_const_filename) + pins.print_af_py(args.af_py_filename) if __name__ == "__main__": diff --git a/teensy/mk20dx256_af.csv b/teensy/mk20dx256_af.csv index 3015c6c7a1..571587de6b 100644 --- a/teensy/mk20dx256_af.csv +++ b/teensy/mk20dx256_af.csv @@ -61,5 +61,5 @@ Pin,Name,Default,ALT0,ALT1,ALT2,ALT3,ALT4,ALT5,ALT6,ALT7,EzPort 60,PTD3,DISABLED,,PTD3,SPI0_SIN,UART2_TX,,FB_AD3,,, 61,PTD4/LLWU_P14,DISABLED,,PTD4/LLWU_P14,SPI0_PCS1,UART0_RTS_b,FTM0_CH4,FB_AD2,EWM_IN,, 62,PTD5,ADC0_SE6b,ADC0_SE6b,PTD5,SPI0_PCS2,UART0_CTS_b/UART0_COL_b,FTM0_CH5,FB_AD1,EWM_OUT_b,, -63,PTD6/LLWU_P15,ADC0_SE7b,ADC0_SE7b,PTD6/LLWU_P15,SPI0_PCS3,UART0_RX,FTM0_CH6,FB_AD0,FTM0_FLT0,, +63,PTD6/LLWU_P15,ADC0_SE7b,ADC0_SE7b,PTD6/LLWU_P15,SPI0_PCS3,UART0_RX,FTM0_CH6,FB_AD0,FTM0_FLT0f,, 64,PTD7,DISABLED,,PTD7,CMT_IRO,UART0_TX,FTM0_CH7,,FTM0_FLT1,, diff --git a/teensy/modpyb.c b/teensy/modpyb.c index 518b93be8f..696554ee4b 100644 --- a/teensy/modpyb.c +++ b/teensy/modpyb.c @@ -43,7 +43,7 @@ #include "pyexec.h" #include "led.h" #include "pin.h" -//#include "timer.h" +#include "timer.h" #include "extint.h" #include "usrsw.h" #include "rng.h" @@ -252,7 +252,7 @@ STATIC const mp_map_elem_t pyb_module_globals_table[] = { { MP_OBJ_NEW_QSTR(MP_QSTR_udelay), (mp_obj_t)&pyb_udelay_obj }, { MP_OBJ_NEW_QSTR(MP_QSTR_sync), (mp_obj_t)&pyb_sync_obj }, -// { MP_OBJ_NEW_QSTR(MP_QSTR_Timer), (mp_obj_t)&pyb_timer_type }, + { MP_OBJ_NEW_QSTR(MP_QSTR_Timer), (mp_obj_t)&pyb_timer_type }, //#if MICROPY_HW_ENABLE_RNG // { MP_OBJ_NEW_QSTR(MP_QSTR_rng), (mp_obj_t)&pyb_rng_get_obj }, diff --git a/teensy/mpconfigport.h b/teensy/mpconfigport.h index 5e4b5129be..3528101b37 100644 --- a/teensy/mpconfigport.h +++ b/teensy/mpconfigport.h @@ -20,6 +20,12 @@ #define MICROPY_PY_SYS_STDFILES (1) #define MICROPY_PY_CMATH (1) +#define MICROPY_TIMER_REG (0) +#define MICROPY_REG (MICROPY_TIMER_REG) + +#define MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF (1) +#define MICROPY_EMERGENCY_EXCEPTION_BUF_SIZE (0) + // extra built in names to add to the global namespace extern const struct _mp_obj_fun_builtin_t mp_builtin_help_obj; extern const struct _mp_obj_fun_builtin_t mp_builtin_input_obj; diff --git a/teensy/pin_defs_teensy.c b/teensy/pin_defs_teensy.c index dd2f0dc21f..35be669970 100644 --- a/teensy/pin_defs_teensy.c +++ b/teensy/pin_defs_teensy.c @@ -14,10 +14,13 @@ // GPIO_MODE_AF_PP, GPIO_MODE_AF_OD, or GPIO_MODE_ANALOG. uint32_t pin_get_mode(const pin_obj_t *pin) { + if (pin->gpio == NULL) { + // Analog only pin + return GPIO_MODE_ANALOG; + } volatile uint32_t *port_pcr = GPIO_PIN_TO_PORT_PCR(pin->gpio, pin->pin); uint32_t pcr = *port_pcr; - uint32_t af = (*port_pcr & PORT_PCR_MUX_MASK) >> 8;; - + uint32_t af = (pcr & PORT_PCR_MUX_MASK) >> 8; if (af == 0) { return GPIO_MODE_ANALOG; } @@ -41,10 +44,18 @@ uint32_t pin_get_mode(const pin_obj_t *pin) { // be one of GPIO_NOPULL, GPIO_PULLUP, or GPIO_PULLDOWN. uint32_t pin_get_pull(const pin_obj_t *pin) { - volatile uint32_t *port_pcr = GPIO_PIN_TO_PORT_PCR(pin->gpio, pin->pin); + if (pin->gpio == NULL) { + // Analog only pin + return GPIO_NOPULL; + } + volatile uint32_t *port_pcr = GPIO_PIN_TO_PORT_PCR(pin->gpio, pin->pin); uint32_t pcr = *port_pcr; - if (pcr & PORT_PCR_PE) { + uint32_t af = (pcr & PORT_PCR_MUX_MASK) >> 8; + + // pull is only valid for digital modes (hence the af > 0 test) + + if (af > 0 && (pcr & PORT_PCR_PE) != 0) { if (pcr & PORT_PCR_PS) { return GPIO_PULLUP; } @@ -56,6 +67,10 @@ uint32_t pin_get_pull(const pin_obj_t *pin) { // Returns the af (alternate function) index currently set for a pin. uint32_t pin_get_af(const pin_obj_t *pin) { + if (pin->gpio == NULL) { + // Analog only pin + return 0; + } volatile uint32_t *port_pcr = GPIO_PIN_TO_PORT_PCR(pin->gpio, pin->pin); return (*port_pcr & PORT_PCR_MUX_MASK) >> 8; } diff --git a/teensy/pin_defs_teensy.h b/teensy/pin_defs_teensy.h index 70fd47d8fd..54a6055f1a 100644 --- a/teensy/pin_defs_teensy.h +++ b/teensy/pin_defs_teensy.h @@ -19,6 +19,10 @@ enum { AF_PIN_TYPE_FTM_CH1, AF_PIN_TYPE_FTM_CH2, AF_PIN_TYPE_FTM_CH3, + AF_PIN_TYPE_FTM_CH4, + AF_PIN_TYPE_FTM_CH5, + AF_PIN_TYPE_FTM_CH6, + AF_PIN_TYPE_FTM_CH7, AF_PIN_TYPE_FTM_QD_PHA, AF_PIN_TYPE_FTM_QD_PHB, diff --git a/teensy/qstrdefsport.h b/teensy/qstrdefsport.h index bdafd14286..44f5d4309d 100644 --- a/teensy/qstrdefsport.h +++ b/teensy/qstrdefsport.h @@ -87,6 +87,42 @@ Q(PULL_NONE) Q(PULL_UP) Q(PULL_DOWN) +// for Timer class +Q(Timer) +Q(init) +Q(deinit) +Q(channel) +Q(counter) +Q(prescaler) +Q(period) +Q(callback) +Q(freq) +Q(mode) +Q(reg) +Q(UP) +Q(CENTER) +Q(IC) +Q(PWM) +Q(PWM_INVERTED) +Q(OC_TIMING) +Q(OC_ACTIVE) +Q(OC_INACTIVE) +Q(OC_TOGGLE) +Q(OC_FORCED_ACTIVE) +Q(OC_FORCED_INACTIVE) +Q(HIGH) +Q(LOW) +Q(RISING) +Q(FALLING) +Q(BOTH) + +// for TimerChannel class +Q(TimerChannel) +Q(pulse_width) +Q(compare) +Q(capture) +Q(polarity) +t // for UART class Q(UART) Q(baudrate) diff --git a/teensy/reg.c b/teensy/reg.c new file mode 100644 index 0000000000..8783a8351f --- /dev/null +++ b/teensy/reg.c @@ -0,0 +1,52 @@ +#include <stdio.h> +#include <string.h> +#include "mpconfig.h" +#include "nlr.h" +#include "misc.h" +#include "qstr.h" +#include "obj.h" +#include "runtime.h" +#include "reg.h" + +#if MICROPY_REG + +mp_obj_t reg_cmd(void *base, reg_t *reg, mp_uint_t num_regs, uint n_args, const mp_obj_t *args) { + if (n_args == 0) { + // dump all regs + + for (mp_uint_t reg_idx = 0; reg_idx < num_regs; reg_idx++, reg++) { + printf(" %-8s @0x%08x = 0x%08lx\n", + reg->name, (mp_uint_t)base + reg->offset, *(uint32_t *)((uint8_t *)base + reg->offset)); + } + return mp_const_none; + } + + mp_uint_t addr = 0; + + if (MP_OBJ_IS_STR(args[0])) { + const char *name = mp_obj_str_get_str(args[0]); + mp_uint_t reg_idx; + for (reg_idx = 0; reg_idx < num_regs; reg_idx++, reg++) { + if (strcmp(name, reg->name) == 0) { + break; + } + } + if (reg_idx >= num_regs) { + printf("Unknown register: '%s'\n", name); + return mp_const_none; + } + addr = (mp_uint_t)base + reg->offset; + } else { + addr = (mp_uint_t)base + mp_obj_get_int(args[0]); + } + + if (n_args < 2) { + // get + printf("0x%08lx\n", *(uint32_t *)addr); + } else { + *(uint32_t *)addr = mp_obj_get_int(args[1]); + } + return mp_const_none; +} + +#endif diff --git a/teensy/reg.h b/teensy/reg.h new file mode 100644 index 0000000000..5d1d27443b --- /dev/null +++ b/teensy/reg.h @@ -0,0 +1,8 @@ +typedef struct { + const char *name; + mp_uint_t offset; +} reg_t; + +#define REG_ENTRY(st, name) { #name, offsetof(st, name) } + +mp_obj_t reg_cmd(void *base, reg_t *reg, mp_uint_t num_reg, uint n_args, const mp_obj_t *args); diff --git a/teensy/teensy_hal.h b/teensy/teensy_hal.h index 39d272f778..d27116d415 100644 --- a/teensy/teensy_hal.h +++ b/teensy/teensy_hal.h @@ -1,4 +1,5 @@ #include <mk20dx128.h> +#include "hal_ftm.h" #ifdef USE_FULL_ASSERT #define assert_param(expr) ((expr) ? (void)0 : assert_failed((uint8_t *)__FILE__, __LINE__)) @@ -7,9 +8,7 @@ #define assert_param(expr) ((void)0) #endif /* USE_FULL_ASSERT */ -#define FTM0 ((FTM_TypeDef *)&FTM0_SC) -#define FTM1 ((FTM_TypeDef *)&FTM1_SC) -#define FTM2 ((FTM_TypeDef *)&FTM2_SC) +#define HAL_NVIC_EnableIRQ(irq) NVIC_ENABLE_IRQ(irq) #define GPIOA ((GPIO_TypeDef *)&GPIOA_PDOR) #define GPIOB ((GPIO_TypeDef *)&GPIOB_PDOR) @@ -31,10 +30,6 @@ typedef struct { uint32_t dummy; -} FTM_TypeDef; - -typedef struct { - uint32_t dummy; } I2C_TypeDef; typedef struct { @@ -93,10 +88,10 @@ typedef struct { } GPIO_InitTypeDef; #define GPIO_PORT_TO_PORT_NUM(GPIOx) \ - ((GPIOx->PDOR - GPIOA_PDOR) / (GPIOB_PDOR - GPIOA_PDOR)) + ((&GPIOx->PDOR - &GPIOA_PDOR) / (&GPIOB_PDOR - &GPIOA_PDOR)) #define GPIO_PIN_TO_PORT_PCR(GPIOx, pin) \ - (&PORTA_PCR0 + GPIO_PORT_TO_PORT_NUM(GPIOx) * 32 + (pin)) + (&PORTA_PCR0 + (GPIO_PORT_TO_PORT_NUM(GPIOx) * 0x400) + (pin)) #define GPIO_AF2_I2C0 2 #define GPIO_AF2_I2C1 2 diff --git a/teensy/teensy_pins.csv b/teensy/teensy_pins.csv index acaef63aad..10887e2120 100644 --- a/teensy/teensy_pins.csv +++ b/teensy/teensy_pins.csv @@ -53,3 +53,4 @@ A17,PTC8 A18,PTC10 A19,PTC11 A20,PTE0 +LED,PTC5 diff --git a/teensy/timer.c b/teensy/timer.c new file mode 100644 index 0000000000..96678b86cc --- /dev/null +++ b/teensy/timer.c @@ -0,0 +1,897 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * 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 <stdio.h> +#include <stdint.h> +#include <string.h> +#include <stddef.h> + +#include "mpconfig.h" +#include "nlr.h" +#include "misc.h" +#include "qstr.h" +#include "obj.h" +#include "runtime.h" +#include MICROPY_HAL_H +#include "gc.h" +#include "pin.h" +#include "reg.h" + +#include "timer.h" + + +typedef enum { + CHANNEL_MODE_PWM_NORMAL, + CHANNEL_MODE_PWM_INVERTED, + CHANNEL_MODE_OC_TIMING, + CHANNEL_MODE_OC_ACTIVE, + CHANNEL_MODE_OC_INACTIVE, + CHANNEL_MODE_OC_TOGGLE, +// CHANNEL_MODE_OC_FORCED_ACTIVE, +// CHANNEL_MODE_OC_FORCED_INACTIVE, + CHANNEL_MODE_IC, +} pyb_channel_mode; + +STATIC const struct { + qstr name; + uint32_t oc_mode; +} gChannelMode[] = { + { MP_QSTR_PWM, FTM_OCMODE_PWM1 }, + { MP_QSTR_PWM_INVERTED, FTM_OCMODE_PWM2 }, + { MP_QSTR_OC_TIMING, FTM_OCMODE_TIMING }, + { MP_QSTR_OC_ACTIVE, FTM_OCMODE_ACTIVE }, + { MP_QSTR_OC_INACTIVE, FTM_OCMODE_INACTIVE }, + { MP_QSTR_OC_TOGGLE, FTM_OCMODE_TOGGLE }, +// { MP_QSTR_OC_FORCED_ACTIVE, FTM_OCMODE_FORCED_ACTIVE }, +// { MP_QSTR_OC_FORCED_INACTIVE, FTM_OCMODE_FORCED_INACTIVE }, + { MP_QSTR_IC, 0 }, +}; + +struct _pyb_timer_obj_t; + +typedef struct _pyb_timer_channel_obj_t { + mp_obj_base_t base; + struct _pyb_timer_obj_t *timer; + uint8_t channel; + uint8_t mode; + mp_obj_t callback; + struct _pyb_timer_channel_obj_t *next; +} pyb_timer_channel_obj_t; + +typedef struct _pyb_timer_obj_t { + mp_obj_base_t base; + uint8_t tim_id; + uint8_t irqn; + mp_obj_t callback; + FTM_HandleTypeDef ftm; + pyb_timer_channel_obj_t *channel; +} pyb_timer_obj_t; + +// Used to do callbacks to Python code on interrupt +STATIC pyb_timer_obj_t *pyb_timer_obj_all[3]; +#define PYB_TIMER_OBJ_ALL_NUM MP_ARRAY_SIZE(pyb_timer_obj_all) + +STATIC mp_obj_t pyb_timer_deinit(mp_obj_t self_in); +STATIC mp_obj_t pyb_timer_callback(mp_obj_t self_in, mp_obj_t callback); +STATIC mp_obj_t pyb_timer_channel_callback(mp_obj_t self_in, mp_obj_t callback); + +void timer_init0(void) { + for (uint i = 0; i < PYB_TIMER_OBJ_ALL_NUM; i++) { + pyb_timer_obj_all[i] = NULL; + } +} + +// unregister all interrupt sources +void timer_deinit(void) { + for (uint i = 0; i < PYB_TIMER_OBJ_ALL_NUM; i++) { + pyb_timer_obj_t *tim = pyb_timer_obj_all[i]; + if (tim != NULL) { + pyb_timer_deinit(tim); + } + } +} + +mp_uint_t get_prescaler_shift(mp_int_t prescaler) { + mp_uint_t prescaler_shift; + for (prescaler_shift = 0; prescaler_shift < 8; prescaler_shift++) { + if (prescaler == (1 << prescaler_shift)) { + return prescaler_shift; + } + } + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError, "prescaler must be a power of 2 between 1 and 128, not %d", prescaler)); +} + +/******************************************************************************/ +/* Micro Python bindings */ + +STATIC void pyb_timer_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t self_in, mp_print_kind_t kind) { + pyb_timer_obj_t *self = self_in; + + if (self->ftm.State == HAL_FTM_STATE_RESET) { + print(env, "Timer(%u)", self->tim_id); + } else { + print(env, "Timer(%u, prescaler=%u, period=%u, mode=%s)", + self->tim_id, + 1 << self->ftm.Init.PrescalerShift, + self->ftm.Init.Period, + self->ftm.Init.CounterMode == FTM_COUNTERMODE_UP ? "tUP" : "CENTER"); + } +} + +/// \method init(*, freq, prescaler, period) +/// Initialise the timer. Initialisation must be either by frequency (in Hz) +/// or by prescaler and period: +/// +/// tim.init(freq=100) # set the timer to trigger at 100Hz +/// tim.init(prescaler=83, period=999) # set the prescaler and period directly +/// +/// Keyword arguments: +/// +/// - `freq` - specifies the periodic frequency of the timer. You migh also +/// view this as the frequency with which the timer goes through +/// one complete cycle. +/// +/// - `prescaler` 1, 2, 4, 8 16 32, 64 or 128 - specifies the value to be loaded into the +/// timer's prescaler. The timer clock source is divided by +/// (`prescaler`) to arrive at the timer clock. +/// +/// - `period` [0-0xffff] - Specifies the value to be loaded into the timer's +/// Modulo Register (MOD). This determines the period of the timer (i.e. +/// when the counter cycles). The timer counter will roll-over after +/// `period + 1` timer clock cycles. +/// +/// - `mode` can be one of: +/// - `Timer.UP` - configures the timer to count from 0 to MOD (default) +/// - `Timer.CENTER` - confgures the timer to count from 0 to MOD and +/// then back down to 0. +/// +/// - `callback` - as per Timer.callback() +/// +/// You must either specify freq or both of period and prescaler. +STATIC const mp_arg_t pyb_timer_init_args[] = { + { MP_QSTR_freq, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0xffffffff} }, + { MP_QSTR_prescaler, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0xffffffff} }, + { MP_QSTR_period, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0xffffffff} }, + { MP_QSTR_mode, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = FTM_COUNTERMODE_UP} }, + { MP_QSTR_callback, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, +}; +#define PYB_TIMER_INIT_NUM_ARGS MP_ARRAY_SIZE(pyb_timer_init_args) + +STATIC mp_obj_t pyb_timer_init_helper(pyb_timer_obj_t *self, uint n_args, const mp_obj_t *args, mp_map_t *kw_args) { + // parse args + mp_arg_val_t vals[PYB_TIMER_INIT_NUM_ARGS]; + mp_arg_parse_all(n_args, args, kw_args, PYB_TIMER_INIT_NUM_ARGS, pyb_timer_init_args, vals); + + FTM_HandleTypeDef *ftm = &self->ftm; + + // set the TIM configuration values + FTM_Base_InitTypeDef *init = &ftm->Init; + + if (vals[0].u_int != 0xffffffff) { + // set prescaler and period from frequency + + if (vals[0].u_int == 0) { + nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "can't have 0 frequency")); + } + + uint32_t period = MAX(1, F_BUS / vals[0].u_int); + uint32_t prescaler_shift = 0; + while (period > 0x10000 && prescaler_shift < 7) { + period >>= 1; + prescaler_shift++; + } + if (period > 0x10000) { + period = 0x10000; + } + init->PrescalerShift = prescaler_shift; + init->Period = period - 1; + } else if (vals[1].u_int != 0xffffffff && vals[2].u_int != 0xffffffff) { + // set prescaler and period directly + init->PrescalerShift = get_prescaler_shift(vals[1].u_int); + init->Period = vals[2].u_int; + if (!IS_FTM_PERIOD(init->Period)) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError, "period must be between 0 and 65535, not %d", init->Period)); + } + } else { + nlr_raise(mp_obj_new_exception_msg(&mp_type_TypeError, "must specify either freq, or prescaler and period")); + } + + init->CounterMode = vals[3].u_int; + if (!IS_FTM_COUNTERMODE(init->CounterMode)) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError, "invalid counter mode: %d", init->CounterMode)); + } + + // Currently core/mk20dx128.c sets SIM_SCGC6_FTM0, SIM_SCGC6_FTM1, SIM_SCGC3_FTM2 + // so we don't need to do it here. + + NVIC_SET_PRIORITY(self->irqn, 0xe); // next-to lowest priority + + HAL_FTM_Base_Init(ftm); + if (vals[4].u_obj == mp_const_none) { + HAL_FTM_Base_Start(ftm); + } else { + pyb_timer_callback(self, vals[4].u_obj); + } + + return mp_const_none; +} + +/// \classmethod \constructor(id, ...) +/// Construct a new timer object of the given id. If additional +/// arguments are given, then the timer is initialised by `init(...)`. +/// `id` can be 1 to 14, excluding 3. +STATIC mp_obj_t pyb_timer_make_new(mp_obj_t type_in, uint n_args, uint n_kw, const mp_obj_t *args) { + // check arguments + mp_arg_check_num(n_args, n_kw, 1, MP_OBJ_FUN_ARGS_MAX, true); + + // create new Timer object + pyb_timer_obj_t *tim = m_new_obj(pyb_timer_obj_t); + memset(tim, 0, sizeof(*tim)); + + tim->base.type = &pyb_timer_type; + tim->callback = mp_const_none; + tim->channel = NULL; + + // get FTM number + tim->tim_id = mp_obj_get_int(args[0]); + + switch (tim->tim_id) { + case 0: tim->ftm.Instance = FTM0; tim->irqn = IRQ_FTM0; break; + case 1: tim->ftm.Instance = FTM1; tim->irqn = IRQ_FTM1; break; + case 2: tim->ftm.Instance = FTM2; tim->irqn = IRQ_FTM2; break; + default: nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "Timer %d does not exist", tim->tim_id)); + } + + if (n_args > 1 || n_kw > 0) { + // start the peripheral + mp_map_t kw_args; + mp_map_init_fixed_table(&kw_args, n_kw, args + n_args); + pyb_timer_init_helper(tim, n_args - 1, args + 1, &kw_args); + } + + // set the global variable for interrupt callbacks + if (tim->tim_id < PYB_TIMER_OBJ_ALL_NUM) { + pyb_timer_obj_all[tim->tim_id] = tim; + } + + return (mp_obj_t)tim; +} + +STATIC mp_obj_t pyb_timer_init(uint n_args, const mp_obj_t *args, mp_map_t *kw_args) { + return pyb_timer_init_helper(args[0], n_args - 1, args + 1, kw_args); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_timer_init_obj, 1, pyb_timer_init); + +/// \method deinit() +/// Deinitialises the timer. +/// +/// Disables the callback (and the associated irq). +/// Stops the timer, and disables the timer peripheral. +STATIC mp_obj_t pyb_timer_deinit(mp_obj_t self_in) { + pyb_timer_obj_t *self = self_in; + + // Disable the interrupt + pyb_timer_callback(self_in, mp_const_none); + + pyb_timer_channel_obj_t *chan = self->channel; + self->channel = NULL; + + // Disable the channel interrupts + while (chan != NULL) { + pyb_timer_channel_callback(chan, mp_const_none); + pyb_timer_channel_obj_t *prev_chan = chan; + chan = chan->next; + prev_chan->next = NULL; + } + + HAL_FTM_Base_DeInit(&self->ftm); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_timer_deinit_obj, pyb_timer_deinit); + +/// \method channel(channel, ...) +/// +/// If only a channel nunber is passed, then a previously initialized channel +/// object is returned. +/// +/// Othwerwise, a TimerChannel object is initialized and returned. +/// +/// Each channel can be configured to perform pwm, output compare, or +/// input capture. All channels share the same underlying timer, which means +/// that they share the same timer clock. +/// +/// Keyword arguments: +/// +/// - `mode` can be one of: +/// - `Timer.PWM` - configure the timer in PWM mode (active high). +/// - `Timer.PWM_INVERTED` - configure the timer in PWM mode (active low). +/// - `Timer.OC_TIMING` - indicates that no pin is driven. +/// - `Timer.OC_ACTIVE` - the pin will be made active when a compare +/// match occurs (active is determined by polarity) +/// - `Timer.OC_INACTIVE` - the pin will be made inactive when a compare +/// match occurs. +/// - `Timer.OC_TOGGLE` - the pin will be toggled when an compare match occurs. +/// - `Timer.IC` - configure the timer in Input Capture mode. +/// +/// - `callback` - as per TimerChannel.callback() +/// +/// - `pin` None (the default) or a Pin object. If specified (and not None) +/// this will cause the alternate function of the the indicated pin +/// to be configured for this timer channel. An error will be raised if +/// the pin doesn't support any alternate functions for this timer channel. +/// +/// Keyword arguments for Timer.PWM modes: +/// +/// - 'pulse_width' - determines the initial pulse width to use. +/// +/// Keyword arguments for Timer.OC modes: +/// +/// - `compare` - determines the initial value of the compare register. +/// +/// - `polarity` can be one of: +/// - `Timer.HIGH` - output is active high +/// - `Timer.LOW` - output is acive low +/// +/// Optional keyword arguments for Timer.IC modes: +/// +/// - `polarity` can be one of: +/// - `Timer.RISING` - captures on rising edge. +/// - `Timer.FALLING` - captures on falling edge. +/// - `Timer.BOTH` - captures on both edges. +/// +/// PWM Example: +/// +/// timer = pyb.Timer(0, prescaler=128, period=37500, counter_mode=pyb.Timer.COUNTER_MODE_CENTER) +/// ch0 = t0.channel(0, pyb.Timer.PWM, pin=pyb.Pin.board.D22, pulse_width=(t0.period() + 1) // 4) +/// ch1 = t0.channel(1, pyb.Timer.PWM, pin=pyb.Pin.board.D23, pulse_width=(t0.period() + 1) // 2) +STATIC const mp_arg_t pyb_timer_channel_args[] = { + { MP_QSTR_callback, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_pin, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_pulse_width, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_compare, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_polarity, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0xffffffff} }, +}; +#define PYB_TIMER_CHANNEL_NUM_ARGS MP_ARRAY_SIZE(pyb_timer_channel_args) + +STATIC mp_obj_t pyb_timer_channel(mp_uint_t n_args, const mp_obj_t *args, mp_map_t *kw_args) { + mp_arg_check_num(n_args, n_args - 2, 2, MP_OBJ_FUN_ARGS_MAX, true); + + pyb_timer_obj_t *self = args[0]; + mp_int_t channel = mp_obj_get_int(args[1]); + + if (channel < 0 || channel > 7) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "Invalid channel (%d)", channel)); + } + + pyb_timer_channel_obj_t *chan = self->channel; + pyb_timer_channel_obj_t *prev_chan = NULL; + + while (chan != NULL) { + if (chan->channel == channel) { + break; + } + prev_chan = chan; + chan = chan->next; + } + if (kw_args->used == 0) { + // Return the previously allocated channel + if (chan) { + return chan; + } + return mp_const_none; + } + + // If there was already a channel, then remove it from the list. Note that + // the order we do things here is important so as to appear atomic to + // the IRQ handler. + if (chan) { + // Turn off any IRQ associated with the channel. + pyb_timer_channel_callback(chan, mp_const_none); + + // Unlink the channel from the list. + if (prev_chan) { + prev_chan->next = chan->next; + } + self->channel = chan->next; + chan->next = NULL; + } + + // Allocate and initialize a new channel + mp_arg_val_t vals[PYB_TIMER_CHANNEL_NUM_ARGS]; + mp_arg_parse_all(n_args - 3, args + 3, kw_args, PYB_TIMER_CHANNEL_NUM_ARGS, pyb_timer_channel_args, vals); + + chan = m_new_obj(pyb_timer_channel_obj_t); + memset(chan, 0, sizeof(*chan)); + chan->base.type = &pyb_timer_channel_type; + chan->timer = self; + chan->channel = channel; + chan->mode = mp_obj_get_int(args[2]); + chan->callback = vals[0].u_obj; + + mp_obj_t pin_obj = vals[1].u_obj; + if (pin_obj != mp_const_none) { + if (!MP_OBJ_IS_TYPE(pin_obj, &pin_type)) { + nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "pin argument needs to be be a Pin type")); + } + const pin_obj_t *pin = pin_obj; + const pin_af_obj_t *af = pin_find_af(pin, AF_FN_FTM, self->tim_id); + if (af == NULL) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "pin %s doesn't have an af for TIM%d", qstr_str(pin->name), self->tim_id)); + } + // pin.init(mode=AF_PP, af=idx) + const mp_obj_t args[6] = { + (mp_obj_t)&pin_init_obj, + pin_obj, + MP_OBJ_NEW_QSTR(MP_QSTR_mode), MP_OBJ_NEW_SMALL_INT(GPIO_MODE_AF_PP), + MP_OBJ_NEW_QSTR(MP_QSTR_af), MP_OBJ_NEW_SMALL_INT(af->idx) + }; + mp_call_method_n_kw(0, 2, args); + } + + // Link the channel to the timer before we turn the channel on. + // Note that this needs to appear atomic to the IRQ handler (the write + // to self->channel is atomic, so we're good, but I thought I'd mention + // in case this was ever changed in the future). + chan->next = self->channel; + self->channel = chan; + + switch (chan->mode) { + + case CHANNEL_MODE_PWM_NORMAL: + case CHANNEL_MODE_PWM_INVERTED: { + FTM_OC_InitTypeDef oc_config; + oc_config.OCMode = gChannelMode[chan->mode].oc_mode; + oc_config.Pulse = vals[2].u_int; + oc_config.OCPolarity = FTM_OCPOLARITY_HIGH; + + HAL_FTM_PWM_ConfigChannel(&self->ftm, &oc_config, channel); + if (chan->callback == mp_const_none) { + HAL_FTM_PWM_Start(&self->ftm, channel); + } else { + HAL_FTM_PWM_Start_IT(&self->ftm, channel); + } + break; + } + + case CHANNEL_MODE_OC_TIMING: + case CHANNEL_MODE_OC_ACTIVE: + case CHANNEL_MODE_OC_INACTIVE: + case CHANNEL_MODE_OC_TOGGLE: { + FTM_OC_InitTypeDef oc_config; + oc_config.OCMode = gChannelMode[chan->mode].oc_mode; + oc_config.Pulse = vals[3].u_int; + oc_config.OCPolarity = vals[4].u_int; + if (oc_config.OCPolarity == 0xffffffff) { + oc_config.OCPolarity = FTM_OCPOLARITY_HIGH; + } + + if (!IS_FTM_OC_POLARITY(oc_config.OCPolarity)) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "Invalid polarity (%d)", oc_config.OCPolarity)); + } + HAL_FTM_OC_ConfigChannel(&self->ftm, &oc_config, channel); + if (chan->callback == mp_const_none) { + HAL_FTM_OC_Start(&self->ftm, channel); + } else { + HAL_FTM_OC_Start_IT(&self->ftm, channel); + } + break; + } + + case CHANNEL_MODE_IC: { + FTM_IC_InitTypeDef ic_config; + + ic_config.ICPolarity = vals[4].u_int; + if (ic_config.ICPolarity == 0xffffffff) { + ic_config.ICPolarity = FTM_ICPOLARITY_RISING; + } + + if (!IS_FTM_IC_POLARITY(ic_config.ICPolarity)) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "Invalid polarity (%d)", ic_config.ICPolarity)); + } + HAL_FTM_IC_ConfigChannel(&self->ftm, &ic_config, chan->channel); + if (chan->callback == mp_const_none) { + HAL_FTM_IC_Start(&self->ftm, channel); + } else { + HAL_FTM_IC_Start_IT(&self->ftm, channel); + } + break; + } + + default: + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "Invalid mode (%d)", chan->mode)); + } + return chan; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_timer_channel_obj, 3, pyb_timer_channel); + +/// \method counter([value]) +/// Get or set the timer counter. +mp_obj_t pyb_timer_counter(uint n_args, const mp_obj_t *args) { + pyb_timer_obj_t *self = args[0]; + if (n_args == 1) { + // get + return mp_obj_new_int(self->ftm.Instance->CNT); + } + // set - In order to write to CNT we need to set CNTIN + self->ftm.Instance->CNTIN = mp_obj_get_int(args[1]); + self->ftm.Instance->CNT = 0; // write any value to load CNTIN into CNT + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pyb_timer_counter_obj, 1, 2, pyb_timer_counter); + +/// \method prescaler([value]) +/// Get or set the prescaler for the timer. +mp_obj_t pyb_timer_prescaler(uint n_args, const mp_obj_t *args) { + pyb_timer_obj_t *self = args[0]; + if (n_args == 1) { + // get + return mp_obj_new_int(1 << (self->ftm.Instance->SC & 7)); + } + + // set + mp_uint_t prescaler_shift = get_prescaler_shift(mp_obj_get_int(args[1])); + + mp_uint_t sc = self->ftm.Instance->SC; + sc &= ~7; + sc |= FTM_SC_PS(prescaler_shift); + self->ftm.Instance->SC = sc; + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pyb_timer_prescaler_obj, 1, 2, pyb_timer_prescaler); + +/// \method period([value]) +/// Get or set the period of the timer. +mp_obj_t pyb_timer_period(uint n_args, const mp_obj_t *args) { + pyb_timer_obj_t *self = args[0]; + if (n_args == 1) { + // get + return mp_obj_new_int(self->ftm.Instance->MOD & 0xffff); + } + + // set + mp_int_t period = mp_obj_get_int(args[1]) & 0xffff; + self->ftm.Instance->CNT = 0; + self->ftm.Instance->MOD = period; + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pyb_timer_period_obj, 1, 2, pyb_timer_period); + +/// \method callback(fun) +/// Set the function to be called when the timer triggers. +/// `fun` is passed 1 argument, the timer object. +/// If `fun` is `None` then the callback will be disabled. +STATIC mp_obj_t pyb_timer_callback(mp_obj_t self_in, mp_obj_t callback) { + pyb_timer_obj_t *self = self_in; + if (callback == mp_const_none) { + // stop interrupt (but not timer) + __HAL_FTM_DISABLE_TOF_IT(&self->ftm); + self->callback = mp_const_none; + } else if (mp_obj_is_callable(callback)) { + self->callback = callback; + HAL_NVIC_EnableIRQ(self->irqn); + // start timer, so that it interrupts on overflow + HAL_FTM_Base_Start_IT(&self->ftm); + } else { + nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "callback must be None or a callable object")); + } + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(pyb_timer_callback_obj, pyb_timer_callback); + +#if MICROPY_TIMER_REG +reg_t timer_reg[] = { + REG_ENTRY(FTM_TypeDef, SC), + REG_ENTRY(FTM_TypeDef, CNT), + REG_ENTRY(FTM_TypeDef, MOD), + REG_ENTRY(FTM_TypeDef, CNTIN), + REG_ENTRY(FTM_TypeDef, STATUS), + REG_ENTRY(FTM_TypeDef, MODE), + REG_ENTRY(FTM_TypeDef, SYNC), + REG_ENTRY(FTM_TypeDef, OUTINIT), + REG_ENTRY(FTM_TypeDef, OUTMASK), + REG_ENTRY(FTM_TypeDef, COMBINE), + REG_ENTRY(FTM_TypeDef, DEADTIME), + REG_ENTRY(FTM_TypeDef, EXTTRIG), + REG_ENTRY(FTM_TypeDef, POL), + REG_ENTRY(FTM_TypeDef, FMS), + REG_ENTRY(FTM_TypeDef, FILTER), + REG_ENTRY(FTM_TypeDef, FLTCTRL), + REG_ENTRY(FTM_TypeDef, QDCTRL), + REG_ENTRY(FTM_TypeDef, CONF), + REG_ENTRY(FTM_TypeDef, FLTPOL), + REG_ENTRY(FTM_TypeDef, SYNCONF), + REG_ENTRY(FTM_TypeDef, INVCTRL), + REG_ENTRY(FTM_TypeDef, SWOCTRL), + REG_ENTRY(FTM_TypeDef, PWMLOAD), +}; + +mp_obj_t pyb_timer_reg(uint n_args, const mp_obj_t *args) { + pyb_timer_obj_t *self = args[0]; + return reg_cmd(self->ftm.Instance, timer_reg, MP_ARRAY_SIZE(timer_reg), n_args - 1, args + 1); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pyb_timer_reg_obj, 1, 3, pyb_timer_reg); +#endif // MICROPY_TIMER_REG + +STATIC const mp_map_elem_t pyb_timer_locals_dict_table[] = { + // instance methods + { MP_OBJ_NEW_QSTR(MP_QSTR_init), (mp_obj_t)&pyb_timer_init_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_deinit), (mp_obj_t)&pyb_timer_deinit_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_channel), (mp_obj_t)&pyb_timer_channel_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_counter), (mp_obj_t)&pyb_timer_counter_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_prescaler), (mp_obj_t)&pyb_timer_prescaler_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_period), (mp_obj_t)&pyb_timer_period_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_callback), (mp_obj_t)&pyb_timer_callback_obj }, +#if MICROPY_TIMER_REG + { MP_OBJ_NEW_QSTR(MP_QSTR_reg), (mp_obj_t)&pyb_timer_reg_obj }, +#endif + { MP_OBJ_NEW_QSTR(MP_QSTR_UP), MP_OBJ_NEW_SMALL_INT(FTM_COUNTERMODE_UP) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_CENTER), MP_OBJ_NEW_SMALL_INT(FTM_COUNTERMODE_CENTER) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_PWM), MP_OBJ_NEW_SMALL_INT(CHANNEL_MODE_PWM_NORMAL) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_PWM_INVERTED), MP_OBJ_NEW_SMALL_INT(CHANNEL_MODE_PWM_INVERTED) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_OC_TIMING), MP_OBJ_NEW_SMALL_INT(CHANNEL_MODE_OC_TIMING) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_OC_ACTIVE), MP_OBJ_NEW_SMALL_INT(CHANNEL_MODE_OC_ACTIVE) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_OC_INACTIVE), MP_OBJ_NEW_SMALL_INT(CHANNEL_MODE_OC_INACTIVE) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_OC_TOGGLE), MP_OBJ_NEW_SMALL_INT(CHANNEL_MODE_OC_TOGGLE) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_IC), MP_OBJ_NEW_SMALL_INT(CHANNEL_MODE_IC) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_HIGH), MP_OBJ_NEW_SMALL_INT(FTM_OCPOLARITY_HIGH) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_LOW), MP_OBJ_NEW_SMALL_INT(FTM_OCPOLARITY_LOW) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_RISING), MP_OBJ_NEW_SMALL_INT(FTM_ICPOLARITY_RISING) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_FALLING), MP_OBJ_NEW_SMALL_INT(FTM_ICPOLARITY_FALLING) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_BOTH), MP_OBJ_NEW_SMALL_INT(FTM_ICPOLARITY_BOTH) }, +}; + +STATIC MP_DEFINE_CONST_DICT(pyb_timer_locals_dict, pyb_timer_locals_dict_table); + +const mp_obj_type_t pyb_timer_type = { + { &mp_type_type }, + .name = MP_QSTR_Timer, + .print = pyb_timer_print, + .make_new = pyb_timer_make_new, + .locals_dict = (mp_obj_t)&pyb_timer_locals_dict, +}; + +/// \moduleref pyb +/// \class TimerChannel - setup a channel for a timer. +/// +/// Timer channels are used to generate/capture a signal using a timer. +/// +/// TimerChannel objects are created using the Timer.channel() method. +STATIC void pyb_timer_channel_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t self_in, mp_print_kind_t kind) { + pyb_timer_channel_obj_t *self = self_in; + + print(env, "TimerChannel(timer=%u, channel=%u mode=%s)", + self->timer->tim_id, + self->channel, + qstr_str(gChannelMode[self->mode].name)); +} + +/// \method capture([value]) +/// Get or set the capture value associated with a channel. +/// capture, compare, and pulse_width are all aliases for the same function. +/// capture is the logical name to use when the channel is in input capture mode. + +/// \method compare([value]) +/// Get or set the compare value associated with a channel. +/// capture, compare, and pulse_width are all aliases for the same function. +/// compare is the logical name to use when the channel is in output compare mode. + +/// \method pulse_width([value]) +/// Get or set the pulse width value associated with a channel. +/// capture, compare, and pulse_width are all aliases for the same function. +/// pulse_width is the logical name to use when the channel is in PWM mode. +STATIC mp_obj_t pyb_timer_channel_capture_compare(uint n_args, const mp_obj_t *args) { + pyb_timer_channel_obj_t *self = args[0]; + if (self->channel == 0xffffffff) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "Timer %d no channel specified", self->timer->tim_id)); + } + FTM_TypeDef *FTMx = self->timer->ftm.Instance; + if (n_args == 1) { + // get + return mp_obj_new_int(FTMx->channel[self->channel].CV); + } + + mp_int_t pw = mp_obj_get_int(args[1]); + + // set + FTMx->channel[self->channel].CV = pw; + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pyb_timer_channel_capture_compare_obj, 1, 2, pyb_timer_channel_capture_compare); + +/// \method callback(fun) +/// Set the function to be called when the timer channel triggers. +/// `fun` is passed 1 argument, the timer object. +/// If `fun` is `None` then the callback will be disabled. +STATIC mp_obj_t pyb_timer_channel_callback(mp_obj_t self_in, mp_obj_t callback) { + pyb_timer_channel_obj_t *self = self_in; + if (callback == mp_const_none) { + // stop interrupt (but not timer) + __HAL_FTM_DISABLE_CH_IT(&self->timer->ftm, self->channel); + self->callback = mp_const_none; + } else if (mp_obj_is_callable(callback)) { + self->callback = callback; + HAL_NVIC_EnableIRQ(self->timer->irqn); + // start timer, so that it interrupts on overflow + switch (self->mode) { + case CHANNEL_MODE_PWM_NORMAL: + case CHANNEL_MODE_PWM_INVERTED: + HAL_FTM_PWM_Start_IT(&self->timer->ftm, self->channel); + break; + case CHANNEL_MODE_OC_TIMING: + case CHANNEL_MODE_OC_ACTIVE: + case CHANNEL_MODE_OC_INACTIVE: + case CHANNEL_MODE_OC_TOGGLE: + HAL_FTM_OC_Start_IT(&self->timer->ftm, self->channel); + break; + case CHANNEL_MODE_IC: + HAL_FTM_IC_Start_IT(&self->timer->ftm, self->channel); + break; + } + } else { + nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "callback must be None or a callable object")); + } + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(pyb_timer_channel_callback_obj, pyb_timer_channel_callback); + +#if MICROPY_TIMER_REG +reg_t timer_channel_reg[] = { + REG_ENTRY(FTM_ChannelTypeDef, CSC), + REG_ENTRY(FTM_ChannelTypeDef, CV), +}; + +mp_obj_t pyb_timer_channel_reg(uint n_args, const mp_obj_t *args) { + pyb_timer_channel_obj_t *self = args[0]; + return reg_cmd(&self->timer->ftm.Instance->channel[self->channel], + timer_channel_reg, MP_ARRAY_SIZE(timer_channel_reg), + n_args - 1, args + 1); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pyb_timer_channel_reg_obj, 1, 3, pyb_timer_channel_reg); +#endif + +STATIC const mp_map_elem_t pyb_timer_channel_locals_dict_table[] = { + // instance methods + { MP_OBJ_NEW_QSTR(MP_QSTR_callback), (mp_obj_t)&pyb_timer_channel_callback_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_pulse_width), (mp_obj_t)&pyb_timer_channel_capture_compare_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_capture), (mp_obj_t)&pyb_timer_channel_capture_compare_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_compare), (mp_obj_t)&pyb_timer_channel_capture_compare_obj }, +#if MICROPY_TIMER_REG + { MP_OBJ_NEW_QSTR(MP_QSTR_reg), (mp_obj_t)&pyb_timer_channel_reg_obj }, +#endif +}; +STATIC MP_DEFINE_CONST_DICT(pyb_timer_channel_locals_dict, pyb_timer_channel_locals_dict_table); + +const mp_obj_type_t pyb_timer_channel_type = { + { &mp_type_type }, + .name = MP_QSTR_TimerChannel, + .print = pyb_timer_channel_print, + .locals_dict = (mp_obj_t)&pyb_timer_channel_locals_dict, +}; + +STATIC bool ftm_handle_irq_callback(pyb_timer_obj_t *self, mp_uint_t channel, mp_obj_t callback) { + // execute callback if it's set + if (callback == mp_const_none) { + return false; + } + bool handled = false; + + // When executing code within a handler we must lock the GC to prevent + // any memory allocations. We must also catch any exceptions. + gc_lock(); + nlr_buf_t nlr; + if (nlr_push(&nlr) == 0) { + mp_call_function_1(callback, self); + nlr_pop(); + handled = true; + } else { + // Uncaught exception; disable the callback so it doesn't run again. + self->callback = mp_const_none; + if (channel == 0xffffffff) { + printf("Uncaught exception in Timer(" UINT_FMT + ") interrupt handler\n", self->tim_id); + } else { + printf("Uncaught exception in Timer(" UINT_FMT ") channel " + UINT_FMT " interrupt handler\n", self->tim_id, channel); + } + mp_obj_print_exception((mp_obj_t)nlr.ret_val); + } + gc_unlock(); + return handled; +} + +STATIC void ftm_irq_handler(uint tim_id) { + if (tim_id >= PYB_TIMER_OBJ_ALL_NUM) { + return; + } + // get the timer object + pyb_timer_obj_t *self = pyb_timer_obj_all[tim_id]; + if (self == NULL) { + // timer object has not been set, so we can't do anything + printf("No timer object for id=%d\n", tim_id); + return; + } + FTM_HandleTypeDef *hftm = &self->ftm; + + bool handled = false; + + // Check for timer (versus timer channel) interrupt. + if (__HAL_FTM_GET_TOF_IT(hftm) && __HAL_FTM_GET_TOF_FLAG(hftm)) { + __HAL_FTM_CLEAR_TOF_FLAG(hftm); + if (ftm_handle_irq_callback(self, 0xffffffff, self->callback)) { + handled = true; + } else { + __HAL_FTM_DISABLE_TOF_IT(&self->ftm); + printf("No callback for Timer %d TOF (now disabled)\n", tim_id); + } + } + + uint32_t processed = 0; + + // Check to see if a timer channel interrupt is pending + pyb_timer_channel_obj_t *chan = self->channel; + while (chan != NULL) { + processed |= (1 << chan->channel); + if (__HAL_FTM_GET_CH_IT(&self->ftm, chan->channel) && __HAL_FTM_GET_CH_FLAG(&self->ftm, chan->channel)) { + __HAL_FTM_CLEAR_CH_FLAG(&self->ftm, chan->channel); + if (ftm_handle_irq_callback(self, chan->channel, chan->callback)) { + handled = true; + } else { + __HAL_FTM_DISABLE_CH_IT(&self->ftm, chan->channel); + printf("No callback for Timer %d channel %u (now disabled)\n", + self->tim_id, chan->channel); + } + } + chan = chan->next; + } + + if (!handled) { + // An interrupt occurred for a channel we didn't process. Find it and + // turn it off. + for (mp_uint_t channel = 0; channel < 8; channel++) { + if ((processed & (1 << channel)) == 0) { + if (__HAL_FTM_GET_CH_FLAG(&self->ftm, channel) != 0) { + __HAL_FTM_CLEAR_CH_FLAG(&self->ftm, channel); + __HAL_FTM_DISABLE_CH_IT(&self->ftm, channel); + printf("Unhandled interrupt Timer %d channel %u (now disabled)\n", + tim_id, channel); + } + } + } + } +} + +void ftm0_isr(void) { + ftm_irq_handler(0); +} + +void ftm1_isr(void) { + ftm_irq_handler(1); +} + +void ftm2_isr(void) { + ftm_irq_handler(2); +} diff --git a/teensy/timer.h b/teensy/timer.h new file mode 100644 index 0000000000..a7b2978564 --- /dev/null +++ b/teensy/timer.h @@ -0,0 +1,31 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * 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. + */ + +extern const mp_obj_type_t pyb_timer_type; +extern const mp_obj_type_t pyb_timer_channel_type; + +void timer_init0(void); +void timer_deinit(void); |