diff --git a/stmhal/boards/PYBLITEV10/mpconfigboard.h b/stmhal/boards/PYBLITEV10/mpconfigboard.h index fb839888cb..452d3061ad 100644 --- a/stmhal/boards/PYBLITEV10/mpconfigboard.h +++ b/stmhal/boards/PYBLITEV10/mpconfigboard.h @@ -70,7 +70,8 @@ #define MICROPY_HW_LED2 (pin_A14) // green #define MICROPY_HW_LED3 (pin_A15) // yellow #define MICROPY_HW_LED4 (pin_B4) // blue -#define MICROPY_HW_LED4_PWM (0) // TIM3 is now a user timer +#define MICROPY_HW_LED3_PWM { TIM2, 2, GPIO_AF1_TIM2 } +#define MICROPY_HW_LED4_PWM { TIM3, 3, GPIO_AF2_TIM3 } #define MICROPY_HW_LED_OTYPE (GPIO_MODE_OUTPUT_PP) #define MICROPY_HW_LED_ON(pin) (pin->gpio->BSRRL = pin->pin_mask) #define MICROPY_HW_LED_OFF(pin) (pin->gpio->BSRRH = pin->pin_mask) diff --git a/stmhal/boards/PYBV10/mpconfigboard.h b/stmhal/boards/PYBV10/mpconfigboard.h index 34207be560..f8cb14b518 100644 --- a/stmhal/boards/PYBV10/mpconfigboard.h +++ b/stmhal/boards/PYBV10/mpconfigboard.h @@ -81,7 +81,8 @@ #define MICROPY_HW_LED2 (pin_A14) // green #define MICROPY_HW_LED3 (pin_A15) // yellow #define MICROPY_HW_LED4 (pin_B4) // blue -#define MICROPY_HW_LED4_PWM (1) +#define MICROPY_HW_LED3_PWM { TIM2, 2, GPIO_AF1_TIM2 } +#define MICROPY_HW_LED4_PWM { TIM3, 3, GPIO_AF2_TIM3 } #define MICROPY_HW_LED_OTYPE (GPIO_MODE_OUTPUT_PP) #define MICROPY_HW_LED_ON(pin) (pin->gpio->BSRRL = pin->pin_mask) #define MICROPY_HW_LED_OFF(pin) (pin->gpio->BSRRH = pin->pin_mask) diff --git a/stmhal/boards/PYBV11/mpconfigboard.h b/stmhal/boards/PYBV11/mpconfigboard.h index 7bc48e8b8a..30a14badec 100644 --- a/stmhal/boards/PYBV11/mpconfigboard.h +++ b/stmhal/boards/PYBV11/mpconfigboard.h @@ -81,7 +81,8 @@ #define MICROPY_HW_LED2 (pin_A14) // green #define MICROPY_HW_LED3 (pin_A15) // yellow #define MICROPY_HW_LED4 (pin_B4) // blue -#define MICROPY_HW_LED4_PWM (1) +#define MICROPY_HW_LED3_PWM { TIM2, 2, GPIO_AF1_TIM2 } +#define MICROPY_HW_LED4_PWM { TIM3, 3, GPIO_AF2_TIM3 } #define MICROPY_HW_LED_OTYPE (GPIO_MODE_OUTPUT_PP) #define MICROPY_HW_LED_ON(pin) (pin->gpio->BSRRL = pin->pin_mask) #define MICROPY_HW_LED_OFF(pin) (pin->gpio->BSRRH = pin->pin_mask) diff --git a/stmhal/boards/PYBV4/mpconfigboard.h b/stmhal/boards/PYBV4/mpconfigboard.h index 48e328604d..663564eb98 100644 --- a/stmhal/boards/PYBV4/mpconfigboard.h +++ b/stmhal/boards/PYBV4/mpconfigboard.h @@ -78,7 +78,8 @@ #define MICROPY_HW_LED2 (pin_A14) // green #define MICROPY_HW_LED3 (pin_A15) // yellow #define MICROPY_HW_LED4 (pin_B4) // blue -#define MICROPY_HW_LED4_PWM (1) +#define MICROPY_HW_LED3_PWM { TIM2, 2, GPIO_AF1_TIM2 } +#define MICROPY_HW_LED4_PWM { TIM3, 3, GPIO_AF2_TIM3 } #define MICROPY_HW_LED_OTYPE (GPIO_MODE_OUTPUT_PP) #define MICROPY_HW_LED_ON(pin) (pin->gpio->BSRRL = pin->pin_mask) #define MICROPY_HW_LED_OFF(pin) (pin->gpio->BSRRH = pin->pin_mask) diff --git a/stmhal/led.c b/stmhal/led.c index ee03d3fd00..fcdb88c9c3 100644 --- a/stmhal/led.c +++ b/stmhal/led.c @@ -1,9 +1,9 @@ /* - * This file is part of the Micro Python project, http://micropython.org/ + * This file is part of the MicroPython project, http://micropython.org/ * * The MIT License (MIT) * - * Copyright (c) 2013, 2014 Damien P. George + * Copyright (c) 2013-2016 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 @@ -36,11 +36,6 @@ #if defined(MICROPY_HW_LED1) -// default is to not PWM LED4 -#if !defined(MICROPY_HW_LED4_PWM) -#define MICROPY_HW_LED4_PWM (0) -#endif - /// \moduleref pyb /// \class LED - LED object /// @@ -85,10 +80,120 @@ void led_init(void) { } } +#if defined(MICROPY_HW_LED1_PWM) \ + || defined(MICROPY_HW_LED2_PWM) \ + || defined(MICROPY_HW_LED3_PWM) \ + || defined(MICROPY_HW_LED4_PWM) + +// The following is semi-generic code to control LEDs using PWM. +// It currently supports TIM2 and TIM3, channel 1 only. +// Configure by defining the relevant MICROPY_HW_LEDx_PWM macros in mpconfigboard.h. +// If they are not defined then PWM will not be available for that LED. + +#define LED_PWM_ENABLED (1) + +#ifndef MICROPY_HW_LED1_PWM +#define MICROPY_HW_LED1_PWM { NULL, 0, 0 } +#endif +#ifndef MICROPY_HW_LED2_PWM +#define MICROPY_HW_LED2_PWM { NULL, 0, 0 } +#endif +#ifndef MICROPY_HW_LED3_PWM +#define MICROPY_HW_LED3_PWM { NULL, 0, 0 } +#endif +#ifndef MICROPY_HW_LED4_PWM +#define MICROPY_HW_LED4_PWM { NULL, 0, 0 } +#endif + +#define LED_PWM_TIM_PERIOD (10000) // TIM runs at 1MHz and fires every 10ms + +typedef struct _led_pwm_config_t { + TIM_TypeDef *tim; + uint8_t tim_id; + uint8_t alt_func; +} led_pwm_config_t; + +STATIC const led_pwm_config_t led_pwm_config[] = { + MICROPY_HW_LED1_PWM, + MICROPY_HW_LED2_PWM, + MICROPY_HW_LED3_PWM, + MICROPY_HW_LED4_PWM, +}; + +STATIC uint8_t led_pwm_state = 0; + +static inline bool led_pwm_is_enabled(int led) { + return (led_pwm_state & (1 << led)) != 0; +} + +// this function has a large stack so it should not be inlined +STATIC void led_pwm_init(int led) __attribute__((noinline)); +STATIC void led_pwm_init(int led) { + const pin_obj_t *led_pin = pyb_led_obj[led - 1].led_pin; + const led_pwm_config_t *pwm_cfg = &led_pwm_config[led - 1]; + + // GPIO configuration + GPIO_InitTypeDef gpio_init; + gpio_init.Pin = led_pin->pin_mask; + gpio_init.Mode = GPIO_MODE_AF_PP; + gpio_init.Speed = GPIO_SPEED_FAST; + gpio_init.Pull = GPIO_NOPULL; + gpio_init.Alternate = pwm_cfg->alt_func; + HAL_GPIO_Init(led_pin->gpio, &gpio_init); + + // TIM configuration + switch (pwm_cfg->tim_id) { + case 2: __TIM2_CLK_ENABLE(); break; + case 3: __TIM3_CLK_ENABLE(); break; + default: assert(0); + } + TIM_HandleTypeDef tim; + tim.Instance = pwm_cfg->tim; + tim.Init.Period = LED_PWM_TIM_PERIOD - 1; + tim.Init.Prescaler = timer_get_source_freq(pwm_cfg->tim_id) / 1000000 - 1; // TIM runs at 1MHz + tim.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; + tim.Init.CounterMode = TIM_COUNTERMODE_UP; + HAL_TIM_PWM_Init(&tim); + + // PWM configuration (only channel 1 supported at the moment) + TIM_OC_InitTypeDef oc_init; + oc_init.OCMode = TIM_OCMODE_PWM1; + oc_init.Pulse = 0; // off + oc_init.OCPolarity = TIM_OCPOLARITY_HIGH; + oc_init.OCFastMode = TIM_OCFAST_DISABLE; + /* needed only for TIM1 and TIM8 + oc_init.OCNPolarity = TIM_OCNPOLARITY_HIGH; + oc_init.OCIdleState = TIM_OCIDLESTATE_SET; + oc_init.OCNIdleState = TIM_OCNIDLESTATE_SET; + */ + HAL_TIM_PWM_ConfigChannel(&tim, &oc_init, TIM_CHANNEL_1); + HAL_TIM_PWM_Start(&tim, TIM_CHANNEL_1); + + // indicate that this LED is using PWM + led_pwm_state |= 1 << led; +} + +STATIC void led_pwm_deinit(int led) { + // make the LED's pin a standard GPIO output pin + const pin_obj_t *led_pin = pyb_led_obj[led - 1].led_pin; + GPIO_TypeDef *g = led_pin->gpio; + uint32_t pin = led_pin->pin; + static const int mode = 1; // output + static const int alt = 0; // no alt func + g->MODER = (g->MODER & ~(3 << (2 * pin))) | (mode << (2 * pin)); + g->AFR[pin >> 3] = (g->AFR[pin >> 3] & ~(15 << (4 * (pin & 7)))) | (alt << (4 * (pin & 7))); + led_pwm_state &= ~(1 << led); +} + +#else +#define LED_PWM_ENABLED (0) +#endif + void led_state(pyb_led_t led, int state) { if (led < 1 || led > NUM_LEDS) { return; } + const pin_obj_t *led_pin = pyb_led_obj[led - 1].led_pin; //printf("led_state(%d,%d)\n", led, state); if (state == 0) { @@ -98,6 +203,12 @@ void led_state(pyb_led_t led, int state) { // turn LED on MICROPY_HW_LED_ON(led_pin); } + + #if LED_PWM_ENABLED + if (led_pwm_is_enabled(led)) { + led_pwm_deinit(led); + } + #endif } void led_toggle(pyb_led_t led) { @@ -105,6 +216,14 @@ void led_toggle(pyb_led_t led) { return; } + #if LED_PWM_ENABLED + if (led_pwm_is_enabled(led)) { + // if PWM is enabled then LED has non-zero intensity, so turn it off + led_state(led, 0); + return; + } + #endif + // toggle the output data register to toggle the LED state const pin_obj_t *led_pin = pyb_led_obj[led - 1].led_pin; led_pin->gpio->ODR ^= led_pin->pin_mask; @@ -115,6 +234,17 @@ int led_get_intensity(pyb_led_t led) { return 0; } + #if LED_PWM_ENABLED + if (led_pwm_is_enabled(led)) { + TIM_TypeDef *tim = led_pwm_config[led - 1].tim; + mp_uint_t i = (tim->CCR1 * 255 + LED_PWM_TIM_PERIOD - 2) / (LED_PWM_TIM_PERIOD - 1); + if (i > 255) { + i = 255; + } + return i; + } + #endif + const pin_obj_t *led_pin = pyb_led_obj[led - 1].led_pin; GPIO_TypeDef *gpio = led_pin->gpio; @@ -129,6 +259,20 @@ int led_get_intensity(pyb_led_t led) { } void led_set_intensity(pyb_led_t led, mp_int_t intensity) { + #if LED_PWM_ENABLED + if (intensity > 0 && intensity < 255) { + TIM_TypeDef *tim = led_pwm_config[led - 1].tim; + if (tim != NULL) { + // set intensity using PWM pulse width + if (!led_pwm_is_enabled(led)) { + led_pwm_init(led); + } + tim->CCR1 = intensity * (LED_PWM_TIM_PERIOD - 1) / 255; + return; + } + } + #endif + // intensity not supported for this LED; just turn it on/off led_state(led, intensity > 0); } diff --git a/stmhal/timer.c b/stmhal/timer.c index 9caa056a2f..8d462975e1 100644 --- a/stmhal/timer.c +++ b/stmhal/timer.c @@ -147,7 +147,6 @@ TIM_HandleTypeDef TIM6_Handle; #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); @@ -229,7 +228,7 @@ void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { // 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 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 diff --git a/stmhal/timer.h b/stmhal/timer.h index 89b3c79f4f..a18d7cf104 100644 --- a/stmhal/timer.h +++ b/stmhal/timer.h @@ -31,9 +31,8 @@ extern const mp_obj_type_t pyb_timer_type; void timer_init0(void); void timer_tim5_init(void); TIM_HandleTypeDef *timer_tim6_init(uint freq); - void timer_deinit(void); - +uint32_t timer_get_source_freq(uint32_t tim_id); void timer_irq_handler(uint tim_id); TIM_HandleTypeDef *pyb_timer_get_handle(mp_obj_t timer);