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
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+
88192void 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
103214void 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
131261void 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}
0 commit comments