/*
* 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 <stdio.h>
# include <string.h>
# include <stm32f4xx_hal.h>
# include "usbd_cdc_msc_hid.h"
# include "usbd_cdc_interface.h"
# include "py/nlr.h"
# include "py/runtime.h"
# include "py/gc.h"
# include "py/pfenv.h"
# include "timer.h"
# include "servo.h"
# include "pin.h"
/// \moduleref pyb
/// \class Timer - periodically call a function
///
/// Timers can be used for a great variety of tasks. At the moment, only
/// the simplest case is implemented: that of calling a function periodically.
///
/// Each timer consists of a counter that counts up at a certain rate. The rate
/// at which it counts is the peripheral clock frequency (in Hz) divided by the
/// timer prescaler. When the counter reaches the timer period it triggers an
/// event, and the counter resets back to zero. By using the callback method,
/// the timer event can call a Python function.
///
/// Example usage to toggle an LED at a fixed frequency:
///
/// tim = pyb.Timer(4) # create a timer object using timer 4
/// tim.init(freq=2) # trigger at 2Hz
/// tim.callback(lambda t:pyb.LED(1).toggle())
///
/// Further examples:
///
/// tim = pyb.Timer(4, freq=100) # freq in Hz
/// tim = pyb.Timer(4, prescaler=0, period=99)
/// tim.counter() # get counter (can also set)
/// tim.prescaler(2) # set prescaler (can also get)
/// tim.period(199) # set period (can also get)
/// tim.callback(lambda t: ...) # set callback for update interrupt (t=tim instance)
/// tim.callback(None) # clear callback
///
/// *Note:* Timer 3 is reserved for internal use. Timer 5 controls
/// the servo driver, and Timer 6 is used for timed ADC/DAC reading/writing.
/// It is recommended to use the other timers in your programs.
// The timers can be used by multiple drivers, and need a common point for
// the interrupts to be dispatched, so they are all collected here.
//
// TIM3:
// - flash storage controller, to flush the cache
// - USB CDC interface, interval, to check for new data
// - LED 4, PWM to set the LED intensity
//
// TIM5:
// - servo controller, PWM
//
// TIM6:
// - ADC, DAC for read_timed and write_timed
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 ;
} channel_mode_info [ ] = {
{ MP_QSTR_PWM , TIM_OCMODE_PWM1 } ,
{ MP_QSTR_PWM_INVERTED , TIM_OCMODE_PWM2 } ,
{ MP_QSTR_OC_TIMING , TIM_OCMODE_TIMING } ,
{ MP_QSTR_OC_ACTIVE , TIM_OCMODE_ACTIVE } ,
{ MP_QSTR_OC_INACTIVE , TIM_OCMODE_INACTIVE } ,
{ MP_QSTR_OC_TOGGLE , TIM_OCMODE_TOGGLE } ,
{ MP_QSTR_OC_FORCED_ACTIVE , TIM_OCMODE_FORCED_ACTIVE } ,
{ MP_QSTR_OC_FORCED_INACTIVE , TIM_OCMODE_FORCED_INACTIVE } ,
{ MP_QSTR_IC , 0 } ,
} ;
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 is_32bit ;
mp_obj_t callback ;
TIM_HandleTypeDef tim ;
IRQn_Type irqn ;
pyb_timer_channel_obj_t * channel ;
} pyb_timer_obj_t ;
// The following yields TIM_IT_UPDATE when channel is zero and
// TIM_IT_CC1..TIM_IT_CC4 when channel is 1..4
# define TIMER_IRQ_MASK(channel) (1 << (channel))
# define TIMER_CNT_MASK(self) ((self)->is_32bit ? 0xffffffff : 0xffff)
# define TIMER_CHANNEL(self) ((((self)->channel) - 1) << 2)
TIM_HandleTypeDef TIM3_Handle ;
TIM_HandleTypeDef TIM5_Handle ;
TIM_HandleTypeDef TIM6_Handle ;
// Used to divide down TIM3 and periodically call the flash storage IRQ
STATIC uint32_t tim3_counter = 0 ;
# define PYB_TIMER_OBJ_ALL_NUM MP_ARRAY_SIZE(MP_STATE_PORT(pyb_timer_obj_all))
STATIC uint32_t timer_get_source_freq ( uint32_t tim_id ) ;
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 ) {
tim3_counter = 0 ;
for ( uint i = 0 ; i < PYB_TIMER_OBJ_ALL_NUM ; i + + ) {
MP_STATE_PORT ( 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 = MP_STATE_PORT ( pyb_timer_obj_all ) [ i ] ;
if ( tim ! = NULL ) {
pyb_timer_deinit ( tim ) ;
}
}
}
// TIM3 is set-up for the USB CDC interface
void timer_tim3_init ( void ) {
// set up the timer for USBD CDC
__TIM3_CLK_ENABLE ( ) ;
TIM3_Handle . Instance = TIM3 ;
TIM3_Handle . Init . Period = ( USBD_CDC_POLLING_INTERVAL * 1000 ) - 1 ; // TIM3 fires every USBD_CDC_POLLING_INTERVAL ms
TIM3_Handle . Init . Prescaler = timer_get_source_freq ( 3 ) / 1000000 - 1 ; // TIM3 runs at 1MHz
TIM3_Handle . Init . ClockDivision = TIM_CLOCKDIVISION_DIV1 ;
TIM3_Handle . Init . CounterMode = TIM_COUNTERMODE_UP ;
HAL_TIM_Base_Init ( & TIM3_Handle ) ;
HAL_NVIC_SetPriority ( TIM3_IRQn , 6 , 0 ) ;
HAL_NVIC_EnableIRQ ( TIM3_IRQn ) ;
if ( HAL_TIM_Base_Start ( & TIM3_Handle ) ! = HAL_OK ) {
/* Starting Error */
}
}
/* unused
void timer_tim3_deinit ( void ) {
// reset TIM3 timer
__TIM3_FORCE_RESET ( ) ;
__TIM3_RELEASE_RESET ( ) ;
}
*/
// TIM5 is set-up for the servo controller
// This function inits but does not start the timer
void timer_tim5_init ( void ) {
// TIM5 clock enable
__TIM5_CLK_ENABLE ( ) ;
// set up and enable interrupt
HAL_NVIC_SetPriority ( TIM5_IRQn , 6 , 0 ) ;
HAL_NVIC_EnableIRQ ( TIM5_IRQn ) ;
// PWM clock configuration
TIM5_Handle . Instance = TIM5 ;
TIM5_Handle . Init . Period = 2000 - 1 ; // timer cycles at 50Hz
TIM5_Handle . Init . Prescaler = ( timer_get_source_freq ( 5 ) / 100000 ) - 1 ; // timer runs at 100kHz
TIM5_Handle . Init . ClockDivision = TIM_CLOCKDIVISION_DIV1 ;
TIM5_Handle . Init . CounterMode = TIM_COUNTERMODE_UP ;
HAL_TIM_PWM_Init ( & TIM5_Handle ) ;
}
// Init TIM6 with a counter-overflow at the given frequency (given in Hz)
// TIM6 is used by the DAC and ADC for auto sampling at a given frequency
// This function inits but does not start the timer
void timer_tim6_init ( uint freq ) {
// TIM6 clock enable
__TIM6_CLK_ENABLE ( ) ;
// Timer runs at SystemCoreClock / 2
// Compute the prescaler value so TIM6 triggers at freq-Hz
uint32_t period = MAX ( 1 , timer_get_source_freq ( 6 ) / freq ) ;
uint32_t prescaler = 1 ;
while ( period > 0xffff ) {
period > > = 1 ;
prescaler < < = 1 ;
}
// Time base clock configuration
TIM6_Handle . Instance = TIM6 ;
TIM6_Handle . Init . Period = period - 1 ;
TIM6_Handle . Init . Prescaler = prescaler - 1 ;
TIM6_Handle . Init . ClockDivision = TIM_CLOCKDIVISION_DIV1 ; // unused for TIM6
TIM6_Handle . Init . CounterMode = TIM_COUNTERMODE_UP ; // unused for TIM6
HAL_TIM_Base_Init ( & TIM6_Handle ) ;
}
// Interrupt dispatch
void HAL_TIM_PeriodElapsedCallback ( TIM_HandleTypeDef * htim ) {
if ( htim = = & TIM3_Handle ) {
USBD_CDC_HAL_TIM_PeriodElapsedCallback ( ) ;
// Periodically raise a flash IRQ for the flash storage controller
if ( tim3_counter + + > = 500 / USBD_CDC_POLLING_INTERVAL ) {
tim3_counter = 0 ;
NVIC - > STIR = FLASH_IRQn ;
}
} else if ( htim = = & TIM5_Handle ) {
servo_timer_irq_callback ( ) ;
}
}
// Get the frequency (in Hz) of the source clock for the given timer.
// On STM32F405/407/415/417 there are 2 cases for how the clock freq is set.
// If the APB prescaler is 1, then the timer clock is equal to its respective
// APB clock. Otherwise (APB prescaler > 1) the timer clock is twice its
// respective APB clock. See DM00031020 Rev 4, page 115.
STATIC uint32_t timer_get_source_freq ( uint32_t tim_id ) {
uint32_t source ;
if ( tim_id = = 1 | | ( 8 < = tim_id & & tim_id < = 11 ) ) {
// TIM{1,8,9,10,11} are on APB2
source = HAL_RCC_GetPCLK2Freq ( ) ;
if ( ( uint32_t ) ( ( RCC - > CFGR & RCC_CFGR_PPRE2 ) > > 3 ) ! = RCC_HCLK_DIV1 ) {
source * = 2 ;
}
} else {
// TIM{2,3,4,5,6,7,12,13,14} are on APB1
source = HAL_RCC_GetPCLK1Freq ( ) ;
if ( ( uint32_t ) ( RCC - > CFGR & RCC_CFGR_PPRE1 ) ! = RCC_HCLK_DIV1 ) {
source * = 2 ;
}
}
return source ;
}
/******************************************************************************/
/* Micro Python bindings */
STATIC const mp_obj_type_t pyb_timer_channel_type ;
// This is the largest value that we can multiply by 100 and have the result
// fit in a uint32_t.
# define MAX_PERIOD_DIV_100 42949672
// computes prescaler and period so TIM triggers at freq-Hz
STATIC uint32_t compute_prescaler_period_from_freq ( pyb_timer_obj_t * self , mp_obj_t freq_in , uint32_t * period_out ) {
uint32_t source_freq = timer_get_source_freq ( self - > tim_id ) ;
uint32_t prescaler = 1 ;
uint32_t period ;
if ( 0 ) {
# if MICROPY_PY_BUILTINS_FLOAT
} else if ( MP_OBJ_IS_TYPE ( freq_in , & mp_type_float ) ) {
float freq = mp_obj_get_float ( freq_in ) ;
if ( freq < = 0 ) {
goto bad_freq ;
}
while ( freq < 1 & & prescaler < 6553 ) {
prescaler * = 10 ;
freq * = 10 ;
}
period = ( float ) source_freq / freq ;
# endif
} else {
mp_int_t freq = mp_obj_get_int ( freq_in ) ;
if ( freq < = 0 ) {
goto bad_freq ;
bad_freq :
nlr_raise ( mp_obj_new_exception_msg ( & mp_type_ValueError , " must have positive freq " ) ) ;
}
period = source_freq / freq ;
}
period = MAX ( 1 , period ) ;
while ( period > TIMER_CNT_MASK ( self ) ) {
// if we can divide exactly, do that first
if ( period % 5 = = 0 ) {
prescaler * = 5 ;
period / = 5 ;
} else if ( period % 3 = = 0 ) {
prescaler * = 3 ;
period / = 3 ;
} else {
// may not divide exactly, but loses minimal precision
prescaler < < = 1 ;
period > > = 1 ;
}
}
* period_out = ( period - 1 ) & TIMER_CNT_MASK ( self ) ;
return ( prescaler - 1 ) & 0xffff ;
}
// Helper function for determining the period used for calculating percent
STATIC uint32_t compute_period ( pyb_timer_obj_t * self ) {
// In center mode, compare == period corresponds to 100%
// In edge mode, compare == (period + 1) corresponds to 100%
uint32_t period = ( __HAL_TIM_GetAutoreload ( & self - > tim ) & TIMER_CNT_MASK ( self ) ) ;
if ( period ! = 0xffffffff ) {
if ( self - > tim . Init . CounterMode = = TIM_COUNTERMODE_UP | |
self - > tim . Init . CounterMode = = TIM_COUNTERMODE_DOWN ) {
// Edge mode
period + + ;
}
}
return period ;
}
// Helper function to compute PWM value from timer period and percent value.
// 'percent_in' can be an int or a float between 0 and 100 (out of range
// values are clamped).
STATIC uint32_t compute_pwm_value_from_percent ( uint32_t period , mp_obj_t percent_in ) {
uint32_t cmp ;
if ( 0 ) {
# if MICROPY_PY_BUILTINS_FLOAT
} else if ( MP_OBJ_IS_TYPE ( percent_in , & mp_type_float ) ) {
float percent = mp_obj_get_float ( percent_in ) ;
if ( percent < = 0.0 ) {
cmp = 0 ;
} else if ( percent > = 100.0 ) {
cmp = period ;
} else {
cmp = percent / 100.0 * ( ( float ) period ) ;
}
# endif
} else {
// For integer arithmetic, if period is large and 100*period will
// overflow, then divide period before multiplying by cmp. Otherwise
// do it the other way round to retain precision.
mp_int_t percent = mp_obj_get_int ( percent_in ) ;
if ( percent < = 0 ) {
cmp = 0 ;
} else if ( percent > = 100 ) {
cmp = period ;
} else if ( period > MAX_PERIOD_DIV_100 ) {
cmp = ( uint32_t ) percent * ( period / 100 ) ;
} else {
cmp = ( ( uint32_t ) percent * period ) / 100 ;
}
}
return cmp ;
}
// Helper function to compute percentage from timer perion and PWM value.
STATIC mp_obj_t compute_percent_from_pwm_value ( uint32_t period , uint32_t cmp ) {
# if MICROPY_PY_BUILTINS_FLOAT
float percent ;
if ( cmp > = period ) {
percent = 100.0 ;
} else {
percent = ( float ) cmp * 100.0 / ( ( float ) period ) ;
}
return mp_obj_new_float ( percent ) ;
# else
mp_int_t percent ;
if ( cmp > = period ) {
percent = 100 ;
} else if ( cmp > MAX_PERIOD_DIV_100 ) {
percent = cmp / ( period / 100 ) ;
} else {
percent = cmp * 100 / period ;
}
return mp_obj_new_int ( percent ) ;
# endif
}
// Computes the 8-bit value for the DTG field in the BDTR register.
//
// 1 tick = 1 count of the timer's clock (source_freq) divided by div.
// 0-128 ticks in inrements of 1
// 128-256 ticks in increments of 2
// 256-512 ticks in increments of 8
// 512-1008 ticks in increments of 16
STATIC uint32_t compute_dtg_from_ticks ( mp_int_t ticks ) {
if ( ticks < = 0 ) {
return 0 ;
}
if ( ticks < 128 ) {
return ticks ;
}
if ( ticks < 256 ) {
return 0x80 | ( ( ticks - 128 ) / 2 ) ;
}
if ( ticks < 512 ) {
return 0xC0 | ( ( ticks - 256 ) / 8 ) ;
}
if ( ticks < 1008 ) {
return 0xE0 | ( ( ticks - 512 ) / 16 ) ;
}
return 0xFF ;
}
// Given the 8-bit value stored in the DTG field of the BDTR register, compute
// the number of ticks.
STATIC mp_int_t compute_ticks_from_dtg ( uint32_t dtg ) {
if ( ( dtg & 0x80 ) = = 0 ) {
return dtg & 0x7F ;
}
if ( ( dtg & 0xC0 ) = = 0x80 ) {
return 128 + ( ( dtg & 0x3F ) * 2 ) ;
}
if ( ( dtg & 0xE0 ) = = 0xC0 ) {
return 256 + ( ( dtg & 0x1F ) * 8 ) ;
}
return 512 + ( ( dtg & 0x1F ) * 16 ) ;
}
STATIC void config_deadtime ( pyb_timer_obj_t * self , mp_int_t ticks ) {
TIM_BreakDeadTimeConfigTypeDef deadTimeConfig ;
deadTimeConfig . OffStateRunMode = TIM_OSSR_DISABLE ;
deadTimeConfig . OffStateIDLEMode = TIM_OSSI_DISABLE ;
deadTimeConfig . LockLevel = TIM_LOCKLEVEL_OFF ;
deadTimeConfig . DeadTime = compute_dtg_from_ticks ( ticks ) ;
deadTimeConfig . BreakState = TIM_BREAK_DISABLE ;
deadTimeConfig . BreakPolarity = TIM_BREAKPOLARITY_LOW ;
deadTimeConfig . AutomaticOutput = TIM_AUTOMATICOUTPUT_DISABLE ;
HAL_TIMEx_ConfigBreakDeadTime ( & self - > tim , & deadTimeConfig ) ;
}
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 - > tim . State = = HAL_TIM_STATE_RESET ) {
print ( env , " Timer(%u) " , self - > tim_id ) ;
} else {
uint32_t prescaler = self - > tim . Instance - > PSC & 0xffff ;
uint32_t period = __HAL_TIM_GetAutoreload ( & self - > tim ) & TIMER_CNT_MASK ( self ) ;
// for efficiency, we compute and print freq as an int (not a float)
uint32_t freq = timer_get_source_freq ( self - > tim_id ) / ( ( prescaler + 1 ) * ( period + 1 ) ) ;
print ( env , " Timer(%u, freq=%u, prescaler=%u, period=%u, mode=%s, div=%u " ,
self - > tim_id ,
freq ,
prescaler ,
period ,
self - > tim . Init . CounterMode = = TIM_COUNTERMODE_UP ? " UP " :
self - > tim . Init . CounterMode = = TIM_COUNTERMODE_DOWN ? " DOWN " : " CENTER " ,
self - > tim . Init . ClockDivision = = TIM_CLOCKDIVISION_DIV4 ? 4 :
self - > tim . Init . ClockDivision = = TIM_CLOCKDIVISION_DIV2 ? 2 : 1 ) ;
if ( IS_TIM_ADVANCED_INSTANCE ( self - > tim . Instance ) ) {
print ( env , " , deadtime=%u " , compute_ticks_from_dtg ( self - > tim . Instance - > BDTR & TIM_BDTR_DTG ) ) ;
}
print ( env , " ) " ) ;
}
}
/// \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` [0-0xffff] - specifies the value to be loaded into the
/// timer's Prescaler Register (PSC). The timer clock source is divided by
/// (`prescaler + 1`) to arrive at the timer clock. Timers 2-7 and 12-14
/// have a clock source of 84 MHz (pyb.freq()[2] * 2), and Timers 1, and 8-11
/// have a clock source of 168 MHz (pyb.freq()[3] * 2).
///
/// - `period` [0-0xffff] for timers 1, 3, 4, and 6-15. [0-0x3fffffff] for timers 2 & 5.
/// Specifies the value to be loaded into the timer's AutoReload
/// Register (ARR). 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 ARR (default)
/// - `Timer.DOWN` - configures the timer to count from ARR down to 0.
/// - `Timer.CENTER` - confgures the timer to count from 0 to ARR and
/// then back down to 0.
///
/// - `div` can be one of 1, 2, or 4. Divides the timer clock to determine
/// the sampling clock used by the digital filters.
///
/// - `callback` - as per Timer.callback()
///
/// - `deadtime` - specifies the amount of "dead" or inactive time between
/// transitions on complimentary channels (both channels will be inactive)
/// for this time). `deadtime` may be an integer between 0 and 1008, with
/// the following restrictions: 0-128 in steps of 1. 128-256 in steps of
/// 2, 256-512 in steps of 8, and 512-1008 in steps of 16. `deadime`
/// measures ticks of `source_freq` divided by `div` clock ticks.
/// `deadtime` is only available on timers 1 and 8.
///
/// You must either specify freq or both of period and prescaler.
STATIC mp_obj_t pyb_timer_init_helper ( pyb_timer_obj_t * self , mp_uint_t n_args , const mp_obj_t * pos_args , mp_map_t * kw_args ) {
static const mp_arg_t allowed_args [ ] = {
{ MP_QSTR_freq , MP_ARG_KW_ONLY | MP_ARG_OBJ , { . u_obj = mp_const_none } } ,
{ 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 = TIM_COUNTERMODE_UP } } ,
{ MP_QSTR_div , MP_ARG_KW_ONLY | MP_ARG_INT , { . u_int = 1 } } ,
{ MP_QSTR_callback , MP_ARG_KW_ONLY | MP_ARG_OBJ , { . u_obj = mp_const_none } } ,
{ MP_QSTR_deadtime , MP_ARG_KW_ONLY | MP_ARG_INT , { . u_int = 0 } } ,
} ;
// parse args
mp_arg_val_t args [ MP_ARRAY_SIZE ( allowed_args ) ] ;
mp_arg_parse_all ( n_args , pos_args , kw_args , MP_ARRAY_SIZE ( allowed_args ) , allowed_args , args ) ;
// set the TIM configuration values
TIM_Base_InitTypeDef * init = & self - > tim . Init ;
if ( args [ 0 ] . u_obj ! = mp_const_none ) {
// set prescaler and period from desired frequency
init - > Prescaler = compute_prescaler_period_from_freq ( self , args [ 0 ] . u_obj , & init - > Period ) ;
} else if ( args [ 1 ] . u_int ! = 0xffffffff & & args [ 2 ] . u_int ! = 0xffffffff ) {
// set prescaler and period directly
init - > Prescaler = args [ 1 ] . u_int ;
init - > Period = args [ 2 ] . u_int ;
} else {
nlr_raise ( mp_obj_new_exception_msg ( & mp_type_TypeError , " must specify either freq, or prescaler and period " ) ) ;
}
init - > CounterMode = args [ 3 ] . u_int ;
if ( ! IS_TIM_COUNTER_MODE ( init - > CounterMode ) ) {
nlr_raise ( mp_obj_new_exception_msg_varg ( & mp_type_ValueError , " invalid mode (%d) " , init - > CounterMode ) ) ;
}
init - > ClockDivision = args [ 4 ] . u_int = = 2 ? TIM_CLOCKDIVISION_DIV2 :
args [ 4 ] . u_int = = 4 ? TIM_CLOCKDIVISION_DIV4 :
TIM_CLOCKDIVISION_DIV1 ;
init - > RepetitionCounter = 0 ;
// enable TIM clock
switch ( self - > tim_id ) {
case 1 : __TIM1_CLK_ENABLE ( ) ; break ;
case 2 : __TIM2_CLK_ENABLE ( ) ; break ;
case 3 : __TIM3_CLK_ENABLE ( ) ; break ;
case 4 : __TIM4_CLK_ENABLE ( ) ; break ;
case 5 : __TIM5_CLK_ENABLE ( ) ; break ;
case 6 : __TIM6_CLK_ENABLE ( ) ; break ;
case 7 : __TIM7_CLK_ENABLE ( ) ; break ;
case 8 : __TIM8_CLK_ENABLE ( ) ; break ;
case 9 : __TIM9_CLK_ENABLE ( ) ; break ;
case 10 : __TIM10_CLK_ENABLE ( ) ; break ;
case 11 : __TIM11_CLK_ENABLE ( ) ; break ;
case 12 : __TIM12_CLK_ENABLE ( ) ; break ;
case 13 : __TIM13_CLK_ENABLE ( ) ; break ;
case 14 : __TIM14_CLK_ENABLE ( ) ; break ;
}
// set IRQ priority (if not a special timer)
if ( self - > tim_id ! = 3 & & self - > tim_id ! = 5 ) {
HAL_NVIC_SetPriority ( self - > irqn , 0xe , 0xe ) ; // next-to lowest priority
}
// init TIM
HAL_TIM_Base_Init ( & self - > tim ) ;
if ( IS_TIM_ADVANCED_INSTANCE ( self - > tim . Instance ) ) {
config_deadtime ( self , args [ 6 ] . u_int ) ;
}
if ( args [ 5 ] . u_obj = = mp_const_none ) {
HAL_TIM_Base_Start ( & self - > tim ) ;
} else {
pyb_timer_callback ( self , args [ 5 ] . 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 , mp_uint_t n_args , mp_uint_t 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 TIM number
tim - > tim_id = mp_obj_get_int ( args [ 0 ] ) ;
tim - > is_32bit = false ;
switch ( tim - > tim_id ) {
case 1 : tim - > tim . Instance = TIM1 ; tim - > irqn = TIM1_UP_TIM10_IRQn ; break ;
case 2 : tim - > tim . Instance = TIM2 ; tim - > irqn = TIM2_IRQn ; tim - > is_32bit = true ; break ;
case 3 : nlr_raise ( mp_obj_new_exception_msg ( & mp_type_ValueError , " Timer 3 is for internal use only " ) ) ; // TIM3 used for low-level stuff; go via regs if necessary
case 4 : tim - > tim . Instance = TIM4 ; tim - > irqn = TIM4_IRQn ; break ;
case 5 : tim - > tim . Instance = TIM5 ; tim - > irqn = TIM5_IRQn ; tim - > is_32bit = true ; break ;
case 6 : tim - > tim . Instance = TIM6 ; tim - > irqn = TIM6_DAC_IRQn ; break ;
case 7 : tim - > tim . Instance = TIM7 ; tim - > irqn = TIM7_IRQn ; break ;
case 8 : tim - > tim . Instance = TIM8 ; tim - > irqn = TIM8_UP_TIM13_IRQn ; break ;
case 9 : tim - > tim . Instance = TIM9 ; tim - > irqn = TIM1_BRK_TIM9_IRQn ; break ;
case 10 : tim - > tim . Instance = TIM10 ; tim - > irqn = TIM1_UP_TIM10_IRQn ; break ;
case 11 : tim - > tim . Instance = TIM11 ; tim - > irqn = TIM1_TRG_COM_TIM11_IRQn ; break ;
case 12 : tim - > tim . Instance = TIM12 ; tim - > irqn = TIM8_BRK_TIM12_IRQn ; break ;
case 13 : tim - > tim . Instance = TIM13 ; tim - > irqn = TIM8_UP_TIM13_IRQn ; break ;
case 14 : tim - > tim . Instance = TIM14 ; tim - > irqn = TIM8_TRG_COM_TIM14_IRQn ; 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 - 1 < PYB_TIMER_OBJ_ALL_NUM ) {
MP_STATE_PORT ( pyb_timer_obj_all ) [ tim - > tim_id - 1 ] = tim ;
}
return ( mp_obj_t ) tim ;
}
STATIC mp_obj_t pyb_timer_init ( mp_uint_t 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).
/// Disables any channel callbacks (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 base 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_TIM_Base_DeInit ( & self - > tim ) ;
return mp_const_none ;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1 ( pyb_timer_deinit_obj , pyb_timer_deinit ) ;
/// \method channel(channel, mode, ...)
///
/// If only a channel number is passed, then a previously initialized channel
/// object is returned (or `None` if there is no previous channel).
///
/// 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.OC_FORCED_ACTIVE` - the pin is forced active (compare match is ignored).
/// - `Timer.OC_FORCED_INACTIVE` - the pin is forced inactive (compare match is ignored).
/// - `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 value to use.
/// - `pulse_width_percent` - determines the initial pulse width percentage 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.
///
/// Note that capture only works on the primary channel, and not on the
/// complimentary channels.
///
/// PWM Example:
///
/// timer = pyb.Timer(2, freq=1000)
/// ch2 = timer.channel(2, pyb.Timer.PWM, pin=pyb.Pin.board.X2, pulse_width=210000)
/// ch3 = timer.channel(3, pyb.Timer.PWM, pin=pyb.Pin.board.X3, pulse_width=420000)
STATIC mp_obj_t pyb_timer_channel ( mp_uint_t n_args , const mp_obj_t * pos_args , mp_map_t * kw_args ) {
static const mp_arg_t allowed_args [ ] = {
{ MP_QSTR_mode , MP_ARG_REQUIRED | MP_ARG_INT , { . u_int = 0 } } ,
{ 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_pulse_width_percent , MP_ARG_KW_ONLY | MP_ARG_OBJ , { . u_obj = mp_const_none } } ,
{ 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 } } ,
} ;
pyb_timer_obj_t * self = pos_args [ 0 ] ;
mp_int_t channel = mp_obj_get_int ( pos_args [ 1 ] ) ;
if ( channel < 1 | | channel > 4 ) {
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 only the channel number is given return the previously allocated
// channel (or None if no previous channel).
if ( n_args = = 2 & & kw_args - > used = = 0 ) {
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 args [ MP_ARRAY_SIZE ( allowed_args ) ] ;
mp_arg_parse_all ( n_args - 2 , pos_args + 2 , kw_args , MP_ARRAY_SIZE ( allowed_args ) , allowed_args , args ) ;
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 = args [ 0 ] . u_int ;
chan - > callback = args [ 1 ] . u_obj ;
mp_obj_t pin_obj = args [ 2 ] . 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_TIM , 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 args2 [ 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 , args2 ) ;
}
// 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 : {
TIM_OC_InitTypeDef oc_config ;
oc_config . OCMode = channel_mode_info [ chan - > mode ] . oc_mode ;
if ( args [ 4 ] . u_obj ! = mp_const_none ) {
// pulse width percent given
uint32_t period = compute_period ( self ) ;
oc_config . Pulse = compute_pwm_value_from_percent ( period , args [ 4 ] . u_obj ) ;
} else {
// use absolute pulse width value (defaults to 0 if nothing given)
oc_config . Pulse = args [ 3 ] . u_int ;
}
oc_config . OCPolarity = TIM_OCPOLARITY_HIGH ;
oc_config . OCNPolarity = TIM_OCNPOLARITY_HIGH ;
oc_config . OCFastMode = TIM_OCFAST_DISABLE ;
oc_config . OCIdleState = TIM_OCIDLESTATE_SET ;
oc_config . OCNIdleState = TIM_OCNIDLESTATE_SET ;
HAL_TIM_PWM_ConfigChannel ( & self - > tim , & oc_config , TIMER_CHANNEL ( chan ) ) ;
if ( chan - > callback = = mp_const_none ) {
HAL_TIM_PWM_Start ( & self - > tim , TIMER_CHANNEL ( chan ) ) ;
} else {
HAL_TIM_PWM_Start_IT ( & self - > tim , TIMER_CHANNEL ( chan ) ) ;
}
// Start the complimentary channel too (if its supported)
if ( IS_TIM_CCXN_INSTANCE ( self - > tim . Instance , TIMER_CHANNEL ( chan ) ) ) {
HAL_TIMEx_PWMN_Start ( & self - > tim , TIMER_CHANNEL ( chan ) ) ;
}
break ;
}
case CHANNEL_MODE_OC_TIMING :
case CHANNEL_MODE_OC_ACTIVE :
case CHANNEL_MODE_OC_INACTIVE :
case CHANNEL_MODE_OC_TOGGLE :
case CHANNEL_MODE_OC_FORCED_ACTIVE :
case CHANNEL_MODE_OC_FORCED_INACTIVE : {
TIM_OC_InitTypeDef oc_config ;
oc_config . OCMode = channel_mode_info [ chan - > mode ] . oc_mode ;
oc_config . Pulse = args [ 5 ] . u_int ;
oc_config . OCPolarity = args [ 6 ] . u_int ;
if ( oc_config . OCPolarity = = 0xffffffff ) {
oc_config . OCPolarity = TIM_OCPOLARITY_HIGH ;
}
if ( oc_config . OCPolarity = = TIM_OCPOLARITY_HIGH ) {
oc_config . OCNPolarity = TIM_OCNPOLARITY_HIGH ;
} else {
oc_config . OCNPolarity = TIM_OCNPOLARITY_LOW ;
}
oc_config . OCFastMode = TIM_OCFAST_DISABLE ;
oc_config . OCIdleState = TIM_OCIDLESTATE_SET ;
oc_config . OCNIdleState = TIM_OCNIDLESTATE_SET ;
if ( ! IS_TIM_OC_POLARITY ( oc_config . OCPolarity ) ) {
nlr_raise ( mp_obj_new_exception_msg_varg ( & mp_type_ValueError , " invalid polarity (%d) " , oc_config . OCPolarity ) ) ;
}
HAL_TIM_OC_ConfigChannel ( & self - > tim , & oc_config , TIMER_CHANNEL ( chan ) ) ;
if ( chan - > callback = = mp_const_none ) {
HAL_TIM_OC_Start ( & self - > tim , TIMER_CHANNEL ( chan ) ) ;
} else {
HAL_TIM_OC_Start_IT ( & self - > tim , TIMER_CHANNEL ( chan ) ) ;
}
// Start the complimentary channel too (if its supported)
if ( IS_TIM_CCXN_INSTANCE ( self - > tim . Instance , TIMER_CHANNEL ( chan ) ) ) {
HAL_TIMEx_OCN_Start ( & self - > tim , TIMER_CHANNEL ( chan ) ) ;
}
break ;
}
case CHANNEL_MODE_IC : {
TIM_IC_InitTypeDef ic_config ;
ic_config . ICPolarity = args [ 6 ] . u_int ;
if ( ic_config . ICPolarity = = 0xffffffff ) {
ic_config . ICPolarity = TIM_ICPOLARITY_RISING ;
}
ic_config . ICSelection = TIM_ICSELECTION_DIRECTTI ;
ic_config . ICPrescaler = TIM_ICPSC_DIV1 ;
ic_config . ICFilter = 0 ;
if ( ! IS_TIM_IC_POLARITY ( ic_config . ICPolarity ) ) {
nlr_raise ( mp_obj_new_exception_msg_varg ( & mp_type_ValueError , " invalid polarity (%d) " , ic_config . ICPolarity ) ) ;
}
HAL_TIM_IC_ConfigChannel ( & self - > tim , & ic_config , TIMER_CHANNEL ( chan ) ) ;
if ( chan - > callback = = mp_const_none ) {
HAL_TIM_IC_Start ( & self - > tim , TIMER_CHANNEL ( chan ) ) ;
} else {
HAL_TIM_IC_Start_IT ( & self - > tim , TIMER_CHANNEL ( chan ) ) ;
}
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 , 2 , pyb_timer_channel ) ;
/// \method counter([value])
/// Get or set the timer counter.
STATIC mp_obj_t pyb_timer_counter ( mp_uint_t 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 - > tim . Instance - > CNT ) ;
} else {
// set
__HAL_TIM_SetCounter ( & self - > tim , mp_obj_get_int ( args [ 1 ] ) ) ;
return mp_const_none ;
}
}
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN ( pyb_timer_counter_obj , 1 , 2 , pyb_timer_counter ) ;
/// \method source_freq()
/// Get the frequency of the source of the timer.
STATIC mp_obj_t pyb_timer_source_freq ( mp_obj_t self_in ) {
pyb_timer_obj_t * self = self_in ;
uint32_t source_freq = timer_get_source_freq ( self - > tim_id ) ;
return mp_obj_new_int ( source_freq ) ;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1 ( pyb_timer_source_freq_obj , pyb_timer_source_freq ) ;
/// \method freq([value])
/// Get or set the frequency for the timer (changes prescaler and period if set).
STATIC mp_obj_t pyb_timer_freq ( mp_uint_t n_args , const mp_obj_t * args ) {
pyb_timer_obj_t * self = args [ 0 ] ;
if ( n_args = = 1 ) {
// get
uint32_t prescaler = self - > tim . Instance - > PSC & 0xffff ;
uint32_t period = __HAL_TIM_GetAutoreload ( & self - > tim ) & TIMER_CNT_MASK ( self ) ;
uint32_t source_freq = timer_get_source_freq ( self - > tim_id ) ;
uint32_t divide = ( ( prescaler + 1 ) * ( period + 1 ) ) ;
# if MICROPY_PY_BUILTINS_FLOAT
if ( source_freq % divide ! = 0 ) {
return mp_obj_new_float ( ( float ) source_freq / ( float ) divide ) ;
} else
# endif
{
return mp_obj_new_int ( source_freq / divide ) ;
}
} else {
// set
uint32_t period ;
uint32_t prescaler = compute_prescaler_period_from_freq ( self , args [ 1 ] , & period ) ;
self - > tim . Instance - > PSC = prescaler ;
__HAL_TIM_SetAutoreload ( & self - > tim , period ) ;
return mp_const_none ;
}
}
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN ( pyb_timer_freq_obj , 1 , 2 , pyb_timer_freq ) ;
/// \method prescaler([value])
/// Get or set the prescaler for the timer.
STATIC mp_obj_t pyb_timer_prescaler ( mp_uint_t 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 - > tim . Instance - > PSC & 0xffff ) ;
} else {
// set
self - > tim . Instance - > PSC = mp_obj_get_int ( args [ 1 ] ) & 0xffff ;
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.
STATIC mp_obj_t pyb_timer_period ( mp_uint_t n_args , const mp_obj_t * args ) {
pyb_timer_obj_t * self = args [ 0 ] ;
if ( n_args = = 1 ) {
// get
return mp_obj_new_int ( __HAL_TIM_GetAutoreload ( & self - > tim ) & TIMER_CNT_MASK ( self ) ) ;
} else {
// set
__HAL_TIM_SetAutoreload ( & self - > tim , mp_obj_get_int ( args [ 1 ] ) & TIMER_CNT_MASK ( self ) ) ;
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_TIM_DISABLE_IT ( & self - > tim , TIM_IT_UPDATE ) ;
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_TIM_Base_Start_IT ( & self - > tim ) ;
} 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 ) ;
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_source_freq ) , ( mp_obj_t ) & pyb_timer_source_freq_obj } ,
{ MP_OBJ_NEW_QSTR ( MP_QSTR_freq ) , ( mp_obj_t ) & pyb_timer_freq_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 } ,
{ MP_OBJ_NEW_QSTR ( MP_QSTR_UP ) , MP_OBJ_NEW_SMALL_INT ( TIM_COUNTERMODE_UP ) } ,
{ MP_OBJ_NEW_QSTR ( MP_QSTR_DOWN ) , MP_OBJ_NEW_SMALL_INT ( TIM_COUNTERMODE_DOWN ) } ,
{ MP_OBJ_NEW_QSTR ( MP_QSTR_CENTER ) , MP_OBJ_NEW_SMALL_INT ( TIM_COUNTERMODE_CENTERALIGNED1 ) } ,
{ 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_OC_FORCED_ACTIVE ) , MP_OBJ_NEW_SMALL_INT ( CHANNEL_MODE_OC_FORCED_ACTIVE ) } ,
{ MP_OBJ_NEW_QSTR ( MP_QSTR_OC_FORCED_INACTIVE ) , MP_OBJ_NEW_SMALL_INT ( CHANNEL_MODE_OC_FORCED_INACTIVE ) } ,
{ 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 ( TIM_OCPOLARITY_HIGH ) } ,
{ MP_OBJ_NEW_QSTR ( MP_QSTR_LOW ) , MP_OBJ_NEW_SMALL_INT ( TIM_OCPOLARITY_LOW ) } ,
{ MP_OBJ_NEW_QSTR ( MP_QSTR_RISING ) , MP_OBJ_NEW_SMALL_INT ( TIM_ICPOLARITY_RISING ) } ,
{ MP_OBJ_NEW_QSTR ( MP_QSTR_FALLING ) , MP_OBJ_NEW_SMALL_INT ( TIM_ICPOLARITY_FALLING ) } ,
{ MP_OBJ_NEW_QSTR ( MP_QSTR_BOTH ) , MP_OBJ_NEW_SMALL_INT ( TIM_ICPOLARITY_BOTHEDGE ) } ,
} ;
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 ( channel_mode_info [ 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.
///
/// In edge aligned mode, a pulse_width of `period + 1` corresponds to a duty cycle of 100%
/// In center aligned mode, a pulse width of `period` corresponds to a duty cycle of 100%
STATIC mp_obj_t pyb_timer_channel_capture_compare ( mp_uint_t n_args , const mp_obj_t * args ) {
pyb_timer_channel_obj_t * self = args [ 0 ] ;
if ( n_args = = 1 ) {
// get
return mp_obj_new_int ( __HAL_TIM_GetCompare ( & self - > timer - > tim , TIMER_CHANNEL ( self ) ) & TIMER_CNT_MASK ( self - > timer ) ) ;
} else {
// set
__HAL_TIM_SetCompare ( & self - > timer - > tim , TIMER_CHANNEL ( self ) , mp_obj_get_int ( args [ 1 ] ) & TIMER_CNT_MASK ( self - > timer ) ) ;
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 pulse_width_percent([value])
/// Get or set the pulse width percentage associated with a channel. The value
/// is a number between 0 and 100 and sets the percentage of the timer period
/// for which the pulse is active. The value can be an integer or
/// floating-point number for more accuracy. For example, a value of 25 gives
/// a duty cycle of 25%.
STATIC mp_obj_t pyb_timer_channel_pulse_width_percent ( mp_uint_t n_args , const mp_obj_t * args ) {
pyb_timer_channel_obj_t * self = args [ 0 ] ;
uint32_t period = compute_period ( self - > timer ) ;
if ( n_args = = 1 ) {
// get
uint32_t cmp = __HAL_TIM_GetCompare ( & self - > timer - > tim , TIMER_CHANNEL ( self ) ) & TIMER_CNT_MASK ( self - > timer ) ;
return compute_percent_from_pwm_value ( period , cmp ) ;
} else {
// set
uint32_t cmp = compute_pwm_value_from_percent ( period , args [ 1 ] ) ;
__HAL_TIM_SetCompare ( & self - > timer - > tim , TIMER_CHANNEL ( self ) , cmp & TIMER_CNT_MASK ( self - > timer ) ) ;
return mp_const_none ;
}
}
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN ( pyb_timer_channel_pulse_width_percent_obj , 1 , 2 , pyb_timer_channel_pulse_width_percent ) ;
/// \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_TIM_DISABLE_IT ( & self - > timer - > tim , TIMER_IRQ_MASK ( 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_TIM_PWM_Start_IT ( & self - > timer - > tim , TIMER_CHANNEL ( self ) ) ;
break ;
case CHANNEL_MODE_OC_TIMING :
case CHANNEL_MODE_OC_ACTIVE :
case CHANNEL_MODE_OC_INACTIVE :
case CHANNEL_MODE_OC_TOGGLE :
case CHANNEL_MODE_OC_FORCED_ACTIVE :
case CHANNEL_MODE_OC_FORCED_INACTIVE :
HAL_TIM_OC_Start_IT ( & self - > timer - > tim , TIMER_CHANNEL ( self ) ) ;
break ;
case CHANNEL_MODE_IC :
HAL_TIM_IC_Start_IT ( & self - > timer - > tim , TIMER_CHANNEL ( self ) ) ;
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 ) ;
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_pulse_width_percent ) , ( mp_obj_t ) & pyb_timer_channel_pulse_width_percent_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 } ,
} ;
STATIC MP_DEFINE_CONST_DICT ( pyb_timer_channel_locals_dict , pyb_timer_channel_locals_dict_table ) ;
STATIC 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 void timer_handle_irq_channel ( pyb_timer_obj_t * tim , uint8_t channel , mp_obj_t callback ) {
uint32_t irq_mask = TIMER_IRQ_MASK ( channel ) ;
if ( __HAL_TIM_GET_FLAG ( & tim - > tim , irq_mask ) ! = RESET ) {
if ( __HAL_TIM_GET_ITSTATUS ( & tim - > tim , irq_mask ) ! = RESET ) {
// clear the interrupt
__HAL_TIM_CLEAR_IT ( & tim - > tim , irq_mask ) ;
// execute callback if it's set
if ( callback ! = mp_const_none ) {
// 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 , tim ) ;
nlr_pop ( ) ;
} else {
// Uncaught exception; disable the callback so it doesn't run again.
tim - > callback = mp_const_none ;
__HAL_TIM_DISABLE_IT ( & tim - > tim , irq_mask ) ;
if ( channel = = 0 ) {
printf ( " uncaught exception in Timer(%u) interrupt handler \n " , tim - > tim_id ) ;
} else {
printf ( " uncaught exception in Timer(%u) channel %u interrupt handler \n " , tim - > tim_id , channel ) ;
}
mp_obj_print_exception ( printf_wrapper , NULL , ( mp_obj_t ) nlr . ret_val ) ;
}
gc_unlock ( ) ;
}
}
}
}
void timer_irq_handler ( uint tim_id ) {
if ( tim_id - 1 < PYB_TIMER_OBJ_ALL_NUM ) {
// get the timer object
pyb_timer_obj_t * tim = MP_STATE_PORT ( pyb_timer_obj_all ) [ tim_id - 1 ] ;
if ( tim = = NULL ) {
// Timer object has not been set, so we can't do anything.
// This can happen under normal circumstances for timers like
// 1 & 10 which use the same IRQ.
return ;
}
// Check for timer (versus timer channel) interrupt.
timer_handle_irq_channel ( tim , 0 , tim - > callback ) ;
uint32_t handled = TIMER_IRQ_MASK ( 0 ) ;
// Check to see if a timer channel interrupt was pending
pyb_timer_channel_obj_t * chan = tim - > channel ;
while ( chan ! = NULL ) {
timer_handle_irq_channel ( tim , chan - > channel , chan - > callback ) ;
handled | = TIMER_IRQ_MASK ( chan - > channel ) ;
chan = chan - > next ;
}
// Finally, clear any remaining interrupt sources. Otherwise we'll
// just get called continuously.
uint32_t unhandled = __HAL_TIM_GET_ITSTATUS ( & tim - > tim , 0xff & ~ handled ) ;
if ( unhandled ! = 0 ) {
__HAL_TIM_CLEAR_IT ( & tim - > tim , unhandled ) ;
printf ( " Unhandled interrupt SR=0x%02lx (now disabled) \n " , unhandled ) ;
}
}
}