8000 stmhal: Add PWM capability for LED(3) and LED(4) on pyboards. · lispmeister/micropython@a8a4b01 · GitHub
[go: up one dir, main page]

Skip to content 8000

Commit a8a4b01

Browse files
committed
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.
1 parent ea89b80 commit a8a4b01

File tree

7 files changed

+161
-15
lines changed

7 files changed

+161
-15
lines changed

stmhal/boards/PYBLITEV10/mpconfigboard.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,8 @@
7070
#define MICROPY_HW_LED2 (pin_A14) // green
7171
#define MICROPY_HW_LED3 (pin_A15) // yellow
7272
#define MICROPY_HW_LED4 (pin_B4) // blue
73-
#define MICROPY_HW_LED4_PWM (0) // TIM3 is now a user timer
73+
#define MICROPY_HW_LED3_PWM { TIM2, 2, GPIO_AF1_TIM2 }
74+
#define MICROPY_HW_LED4_PWM { TIM3, 3, GPIO_AF2_TIM3 }
7475
#define MICROPY_HW_LED_OTYPE (GPIO_MODE_OUTPUT_PP)
7576
#define MICROPY_HW_LED_ON(pin) (pin->gpio->BSRRL = pin->pin_mask)
7677
#define MICROPY_HW_LED_OFF(pin) (pin->gpio->BSRRH = pin->pin_mask)

stmhal/boards/PYBV10/mpconfigboard.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,8 @@
8181
#define MICROPY_HW_LED2 (pin_A14) // green
8282
#define MICROPY_HW_LED3 (pin_A15) // yellow
8383
#define MICROPY_HW_LED4 (pin_B4) // blue
84-
#define MICROPY_HW_LED4_PWM (1)
84+
#define MICROPY_HW_LED3_PWM { TIM2, 2, GPIO_AF1_TIM2 }
85+
#define MICROPY_HW_LED4_PWM { TIM3, 3, GPIO_AF2_TIM3 }
8586
#define MICROPY_HW_LED_OTYPE (GPIO_MODE_OUTPUT_PP)
8687
#define MICROPY_HW_LED_ON(pin) (pin->gpio->BSRRL = pin->pin_mask)
8788
#define MICROPY_HW_LED_OFF(pin) (pin->gpio->BSRRH = pin->pin_mask)

stmhal/boards/PYBV11/mpconfigboard.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,8 @@
8181
#define MICROPY_HW_LED2 (pin_A14) // green
8282
#define MICROPY_HW_LED3 (pin_A15) // yellow
8383
#define MICROPY_HW_LED4 (pin_B4) // blue
84-
#define MICROPY_HW_LED4_PWM (1)
84+
#define MICROPY_HW_LED3_PWM { TIM2, 2, GPIO_AF1_TIM2 }
85+
#define MICROPY_HW_LED4_PWM { TIM3, 3, GPIO_AF2_TIM3 }
8586
#define MICROPY_HW_LED_OTYPE (GPIO_MODE_OUTPUT_PP)
8687
#define MICROPY_HW_LED_ON(pin) (pin->gpio->BSRRL = pin->pin_mask)
8788
#define MICROPY_HW_LED_OFF(pin) (pin->gpio->BSRRH = pin->pin_mask)

stmhal/boards/PYBV4/mpconfigboard.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,8 @@
7878
#define MICROPY_HW_LED2 (pin_A14) // green
7979
#define MICROPY_HW_LED3 (pin_A15) // yellow
8080
#define MICROPY_HW_LED4 (pin_B4) // blue
81-
#define MICROPY_HW_LED4_PWM (1)
81+
#define MICROPY_HW_LED3_PWM { TIM2, 2, GPIO_AF1_TIM2 }
82+
#define MICROPY_HW_LED4_PWM { TIM3, 3, GPIO_AF2_TIM3 }
8283
#define MICROPY_HW_LED_OTYPE (GPIO_MODE_OUTPUT_PP)
8384
#define MICROPY_HW_LED_ON(pin) (pin->gpio->BSRRL = pin->pin_mask)
8485
#define MICROPY_HW_LED_OFF(pin) (pin->gpio->BSRRH = pin->pin_mask)

stmhal/led.c

Lines changed: 151 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
/*
2-
* This file is part of the Micro Python project, http://micropython.org/
2+
* This file is part of the MicroPython project, http://micropython.org/
33
*
44
* The MIT License (MIT)
55
*
6-
* Copyright (c) 2013, 2014 Damien P. George
6+
* Copyright (c) 2013-2016 Damien P. George
77
*
88
* Permission is hereby granted, free of charge, to any person obtaining a copy
99
* of this software and associated documentation files (the "Software"), to deal
@@ -36,11 +36,6 @@
3636

3737
#if defined(MICROPY_HW_LED1)
3838

39-
// default is to not PWM LED4
40-
#if !defined(MICROPY_HW_LED4_PWM)
41-
#define MICROPY_HW_LED4_PWM (0)
42-
#endif
43-
4439
/// \moduleref pyb
4540
/// \class LED - LED object
4641
///
@@ -85,10 +80,120 @@ void led_init(void) {
8580
}
8681
}
8782

83+
#if defined(MICROPY_HW_LED1_PWM) \
84+
|| defined(MICROPY_HW_LED2_PWM) \
85+
|| defined(MICROPY_HW_LED3_PWM) \
86+
|| defined(MICROPY_HW_LED4_PWM)
87+
88+
// The following is semi-generic code to control LEDs using PWM.
89+
// It currently supports TIM2 and TIM3, channel 1 only.
90+
// Configure by defining the relevant MICROPY_HW_LEDx_PWM macros in mpconfigboard.h.
91+
// If they are not defined then PWM will not be available for that LED.
92+
93+
#define LED_PWM_ENABLED (1)
94+
95+
#ifndef MICROPY_HW_LED1_PWM
96+
#define MICROPY_HW_LED1_PWM { NULL, 0, 0 }
97+
#endif
98+
#ifndef MICROPY_HW_LED2_PWM
99+
#define MICROPY_HW_LED2_PWM { NULL, 0, 0 }
100+
#endif
101+
#ifndef MICROPY_HW_LED3_PWM
102+
#define MICROPY_HW_LED3_PWM { NULL, 0, 0 }
103+
#endif
104+
#ifndef MICROPY_HW_LED4_PWM
105+
#define MICROPY_HW_LED4_PWM { NULL, 0, 0 }
106+
#endif
107+
108+
#define LED_PWM_TIM_PERIOD (10000) // TIM runs at 1MHz and fires every 10ms
109+
110+
typedef struct _led_pwm_config_t {
111+
TIM_TypeDef *tim;
112+
uint8_t tim_id;
113+
uint8_t alt_func;
114+
} led_pwm_config_t;
115+
116+
STATIC const led_pwm_config_t led_pwm_config[] = {
117+
MICROPY_HW_LED1_PWM,
118+
MICROPY_HW_LED2_PWM,
119+
MICROPY_HW_LED3_PWM,
120+
MICROPY_HW_LED4_PWM,
121+
};
122+
123+
STATIC uint8_t led_pwm_state = 0;
124+
125+
static inline bool led_pwm_is_enabled(int led) {
126+
return (led_pwm_state & (1 << led)) != 0;
127+
}
128+
129+
// this function has a large stack so it should not be inlined
130+
STATIC void led_pwm_init(int led) __attribute__((noinline));
131+
STATIC void led_pwm_init(int led) {
132+
const pin_obj_t *led_pin = pyb_led_obj[led - 1].led_pin;
133+
const led_pwm_config_t *pwm_cfg = &led_pwm_config[led - 1];
134+
135+
// GPIO configuration
136+
GPIO_InitTypeDef gpio_init;
137+
gpio_init.Pin = led_pin->pin_mask;
138+
gpio_init.Mode = GPIO_MODE_AF_PP;
139+
gpio_init.Speed = GPIO_SPEED_FAST;
140+
gpio_init.Pull = GPIO_NOPULL;
141+
gpio_init.Alternate = pwm_cfg->alt_func;
142+
HAL_GPIO_Init(led_pin->gpio, &gpio_init);
143+
144+
// TIM configuration
145+
switch (pwm_cfg->tim_id) {
146+
case 2: __TIM2_CLK_ENABLE(); break;
147+
case 3: __TIM3_CLK_ENABLE(); break;
148+
default: assert(0);
149+
}
150+
TIM_HandleTypeDef tim;
151+
tim.Instance = pwm_cfg->tim;
152+
tim.Init.Period = LED_PWM_TIM_PERIOD - 1;
153+
tim.Init.Prescaler = timer_get_source_freq(pwm_cfg->tim_id) / 1000000 - 1; // TIM runs at 1MHz
154+
tim.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
155+
tim.Init.CounterMode = TIM_COUNTERMODE_UP;
156+
HAL_TIM_PWM_Init(&tim);
157+
158+
// PWM configuration (only channel 1 supported at the moment)
159+
TIM_OC_InitTypeDef oc_init;
160+
oc_init.OCMode = TIM_OCMODE_PWM1;
161+
oc_init.Pulse = 0; // off
162+
oc_init.OCPolarity = TIM_OCPOLARITY_HIGH;
163+
oc_init.OCFastMode = TIM_OCFAST_DISABLE;
164+
/* needed only for TIM1 and TIM8
165+
oc_init.OCNPolarity = TIM_OCNPOLARITY_HIGH;
166+
oc_init.OCIdleState = TIM_OCIDLESTATE_SET;
167+
oc_init.OCNIdleState = TIM_OCNIDLESTATE_SET;
168+
*/
169+
HAL_TIM_PWM_ConfigChannel(&tim, &oc_init, TIM_CHANNEL_1);
170+
HAL_TIM_PWM_Start(&tim, TIM_CHANNEL_1);
171+
172+
// indicate that this LED is using PWM
173+
led_pwm_state |= 1 << led;
174+
}
175+
176+
STATIC void led_pwm_deinit(int led) {
177+
// make the LED's pin a standard GPIO output pin
178+
const pin_obj_t *led_pin = pyb_led_obj[led - 1].led_pin;
179+
GPIO_TypeDef *g = led_pin->gpio;
180+
uint32_t pin = led_pin->pin;
181+
static const int mode = 1; // output
182+
static const int alt = 0; // no alt func
183+
g->MODER = (g->MODER & ~(3 << (2 * pin))) | (mode << (2 * pin));
184+
g->AFR[pin >> 3] = (g->AFR[pin >> 3] & ~(15 << (4 * (pin & 7)))) | (alt << (4 * (pin & 7)));
185+
led_pwm_state &= ~(1 << led);
186+
}
187+
188+
#else
189+
#define LED_PWM_ENABLED (0)
190+
#endif
191+
88192
void led_state(pyb_led_t led, int state) {
89193
if (led < 1 || led > NUM_LEDS) {
90194
return;
91195
}
196+
92197
const pin_obj_t *led_pin = pyb_led_obj[led - 1].led_pin;
93198
//printf("led_state(%d,%d)\n", led, state);
94199
if (state == 0) {
@@ -98,13 +203,27 @@ void led_state(pyb_led_t led, int state) {
98203
// turn LED on
99204
MICROPY_HW_LED_ON(led_pin);
100205
}
206+
207+
#if LED_PWM_ENABLED
208+
if (led_pwm_is_enabled(led)) {
209+
led_pwm_deinit(led);
210+
}
211+
#endif
101212
}
102213

103214
void led_toggle(pyb_led_t led) {
104215
if (led < 1 || led > NUM_LEDS) {
105216
return;
106217
}
107218

219+
#if LED_PWM_ENABLED
220+
if (led_pwm_is_enabled(led)) {
221+
// if PWM is enabled then LED has non-zero intensity, so turn it off
222+
led_state(led, 0);
223+
return;
224+
}
225+
#endif
226+
108227
// toggle the output data register to toggle the LED state
109228
const pin_obj_t *led_pin = pyb_led_obj[led - 1].led_pin;
110229
led_pin->gpio->ODR ^= led_pin->pin_mask;
@@ -115,6 +234,17 @@ int led_get_intensity(pyb_led_t led) {
115234
return 0;
116235
}
117236

237+
#if LED_PWM_ENABLED
238+
if (led_pwm_is_enabled(led)) {
239+
TIM_TypeDef *tim = led_pwm_config[led - 1].tim;
240+
mp_uint_t i = (tim->CCR1 * 255 + LED_PWM_TIM_PERIOD - 2) / (LED_PWM_TIM_PERIOD - 1);
241+
if (i > 255) {
242+
i = 255;
243+
}
244+
return i;
245+
}
246+
#endif
247+
118248
const pin_obj_t *led_pin = pyb_led_obj[led - 1].led_pin;
119249
GPIO_TypeDef *gpio = led_pin->gpio;
120250

@@ -129,6 +259,20 @@ int led_get_intensity(pyb_led_t led) {
129259
}
130260

131261
void led_set_intensity(pyb_led_t led, mp_int_t intensity) {
262+
#if LED_PWM_ENABLED
263+
if (intensity > 0 && intensity < 255) {
264+
TIM_TypeDef *tim = led_pwm_config[led - 1].tim;
265+
if (tim != NULL) {
266+
// set intensity using PWM pulse width
267+
if (!led_pwm_is_enabled(led)) {
268+
led_pwm_init(led);
269+
}
270+
tim->CCR1 = intensity * (LED_PWM_TIM_PERIOD - 1) / 255;
271+
return;
272+
}
273+
}
274+
#endif
275+
132276
// intensity not supported for this LED; just turn it on/off
133277
led_state(led, intensity > 0);
134278
}

stmhal/timer.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,6 @@ TIM_HandleTypeDef TIM6_Handle;
147147

148148
#define PYB_TIMER_OBJ_ALL_NUM MP_ARRAY_SIZE(MP_STATE_PORT(pyb_timer_obj_all))
149149

150-
STATIC uint32_t timer_get_source_freq(uint32_t tim_id);
151150
STATIC mp_obj_t pyb_timer_deinit(mp_obj_t self_in);
152151
STATIC mp_obj_t pyb_timer_callback(mp_obj_t self_in, mp_obj_t callback);
153152
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) {
229228
// If the APB prescaler is 1, then the timer clock is equal to its respective
230229
// APB clock. Otherwise (APB prescaler > 1) the timer clock is twice its
231230
// respective APB clock. See DM00031020 Rev 4, page 115.
232-
STATIC uint32_t timer_get_source_freq(uint32_t tim_id) {
231+
uint32_t timer_get_source_freq(uint32_t tim_id) {
233232
uint32_t source;
234233
if (tim_id == 1 || (8 <= tim_id && tim_id <= 11)) {
235234
// TIM{1,8,9,10,11} are on APB2

stmhal/timer.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,8 @@ extern const mp_obj_type_t pyb_timer_type;
3131
void timer_init0(void);
3232
void timer_tim5_init(void);
3333
TIM_HandleTypeDef *timer_tim6_init(uint freq);
34-
3534
void timer_deinit(void);
36-
35+
uint32_t timer_get_source_freq(uint32_t tim_id);
3736
void timer_irq_handler(uint tim_id);
3837

3938
TIM_HandleTypeDef *pyb_timer_get_handle(mp_obj_t timer);

0 commit comments

Comments
 (0)
0