stmhal: Add PWM capability for LED(3) and LED(4) on pyboards.
USB CDC no longer needs TIM3 (which was originally used for LED(4) PWM) and so TIM3 has been freed for general purpose use by the user. Hence LED(4) lost its PWM capabilities. This patch reinstates the PWM capabilities using a semi-generic piece of code which allows to configure a timer and PWM channel to use for any LED. But the PWM capability is only configured if the LED is set to an intensity between 1 and 254 (ie only when needed). In that case the relevant timer is configured for PWM. It's up to the user to make sure the timers are not used if PWM is active. This patch also makes sure that PWM LEDs are turned off using standard GPIO when calling led.off() or led.intensity(0), instead of just setting the PWM counter to zero.
This commit is contained in:
parent
ea89b80ff4
commit
a8a4b01af6
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
156
stmhal/led.c
156
stmhal/led.c
@ -3,7 +3,7 @@
|
||||
*
|
||||
* 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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user