Commit 1dc2862a authored by Damien George's avatar Damien George
Browse files

stmhal/led: Allow LEDs to be in PWM mode with TIM1 and channels 1-4.

This allows PYBv3 to use PWM for LED(1) and LED(2).
parent 68a7a92c
......@@ -71,8 +71,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_LED3_PWM { TIM2, 2, GPIO_AF1_TIM2 }
#define MICROPY_HW_LED4_PWM { TIM3, 3, GPIO_AF2_TIM3 }
#define MICROPY_HW_LED3_PWM { TIM2, 2, TIM_CHANNEL_1, GPIO_AF1_TIM2 }
#define MICROPY_HW_LED4_PWM { TIM3, 3, TIM_CHANNEL_1, 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)
......
......@@ -82,8 +82,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_LED3_PWM { TIM2, 2, GPIO_AF1_TIM2 }
#define MICROPY_HW_LED4_PWM { TIM3, 3, GPIO_AF2_TIM3 }
#define MICROPY_HW_LED3_PWM { TIM2, 2, TIM_CHANNEL_1, GPIO_AF1_TIM2 }
#define MICROPY_HW_LED4_PWM { TIM3, 3, TIM_CHANNEL_1, 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)
......
......@@ -82,8 +82,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_LED3_PWM { TIM2, 2, GPIO_AF1_TIM2 }
#define MICROPY_HW_LED4_PWM { TIM3, 3, GPIO_AF2_TIM3 }
#define MICROPY_HW_LED3_PWM { TIM2, 2, TIM_CHANNEL_1, GPIO_AF1_TIM2 }
#define MICROPY_HW_LED4_PWM { TIM3, 3, TIM_CHANNEL_1, 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)
......
......@@ -66,10 +66,13 @@
#define MICROPY_HW_USRSW_PRESSED (0)
// LEDs
#define MICROPY_HW_LED_INVERTED (1) // LEDs are on when pin is driven low
#define MICROPY_HW_LED1 (pin_A8) // R1 - red
#define MICROPY_HW_LED2 (pin_A10) // R2 - red
#define MICROPY_HW_LED3 (pin_C4) // G1 - green
#define MICROPY_HW_LED4 (pin_C5) // G2 - green
#define MICROPY_HW_LED1_PWM { TIM1, 1, TIM_CHANNEL_1, GPIO_AF1_TIM1 }
#define MICROPY_HW_LED2_PWM { TIM1, 1, TIM_CHANNEL_3, GPIO_AF1_TIM1 }
#define MICROPY_HW_LED_OTYPE (GPIO_MODE_OUTPUT_PP)
#define MICROPY_HW_LED_ON(pin) (pin->gpio->BSRRH = pin->pin_mask)
#define MICROPY_HW_LED_OFF(pin) (pin->gpio->BSRRL = pin->pin_mask)
......
......@@ -79,8 +79,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_LED3_PWM { TIM2, 2, GPIO_AF1_TIM2 }
#define MICROPY_HW_LED4_PWM { TIM3, 3, GPIO_AF2_TIM3 }
#define MICROPY_HW_LED3_PWM { TIM2, 2, TIM_CHANNEL_1, GPIO_AF1_TIM2 }
#define MICROPY_HW_LED4_PWM { TIM3, 3, TIM_CHANNEL_1, 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)
......
......@@ -41,6 +41,11 @@
///
/// The LED object controls an individual LED (Light Emitting Diode).
// the default is that LEDs are not inverted, and pin driven high turns them on
#ifndef MICROPY_HW_LED_INVERTED
#define MICROPY_HW_LED_INVERTED (0)
#endif
typedef struct _pyb_led_obj_t {
mp_obj_base_t base;
mp_uint_t led_id;
......@@ -86,30 +91,34 @@ void led_init(void) {
|| 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.
// It currently supports TIM1, TIM2 and TIM3, channels 1-4.
// 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 }
#define MICROPY_HW_LED1_PWM { NULL, 0, 0, 0 }
#endif
#ifndef MICROPY_HW_LED2_PWM
#define MICROPY_HW_LED2_PWM { NULL, 0, 0 }
#define MICROPY_HW_LED2_PWM { NULL, 0, 0, 0 }
#endif
#ifndef MICROPY_HW_LED3_PWM
#define MICROPY_HW_LED3_PWM { NULL, 0, 0 }
#define MICROPY_HW_LED3_PWM { NULL, 0, 0, 0 }
#endif
#ifndef MICROPY_HW_LED4_PWM
#define MICROPY_HW_LED4_PWM { NULL, 0, 0 }
#define MICROPY_HW_LED4_PWM { NULL, 0, 0, 0 }
#endif
#define LED_PWM_TIM_PERIOD (10000) // TIM runs at 1MHz and fires every 10ms
// this gives the address of the CCR register for channels 1-4
#define LED_PWM_CCR(pwm_cfg) ((volatile uint32_t*)&(pwm_cfg)->tim->CCR1 + ((pwm_cfg)->tim_channel >> 2))
typedef struct _led_pwm_config_t {
TIM_TypeDef *tim;
uint8_t tim_id;
uint8_t tim_channel;
uint8_t alt_func;
} led_pwm_config_t;
......@@ -143,6 +152,7 @@ STATIC void led_pwm_init(int led) {
// TIM configuration
switch (pwm_cfg->tim_id) {
case 1: __TIM1_CLK_ENABLE(); break;
case 2: __TIM2_CLK_ENABLE(); break;
case 3: __TIM3_CLK_ENABLE(); break;
default: assert(0);
......@@ -153,21 +163,20 @@ STATIC void led_pwm_init(int led) {
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;
tim.Init.RepetitionCounter = 0;
HAL_TIM_PWM_Init(&tim);
// PWM configuration (only channel 1 supported at the moment)
// PWM configuration
TIM_OC_InitTypeDef oc_init;
oc_init.OCMode = TIM_OCMODE_PWM1;
oc_init.Pulse = 0; // off
oc_init.OCPolarity = TIM_OCPOLARITY_HIGH;
oc_init.OCPolarity = MICROPY_HW_LED_INVERTED ? TIM_OCPOLARITY_LOW : 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);
oc_init.OCNPolarity = TIM_OCNPOLARITY_HIGH; // needed for TIM1 and TIM8
oc_init.OCIdleState = TIM_OCIDLESTATE_SET; // needed for TIM1 and TIM8
oc_init.OCNIdleState = TIM_OCNIDLESTATE_SET; // needed for TIM1 and TIM8
HAL_TIM_PWM_ConfigChannel(&tim, &oc_init, pwm_cfg->tim_channel);
HAL_TIM_PWM_Start(&tim, pwm_cfg->tim_channel);
// indicate that this LED is using PWM
led_pwm_state |= 1 << led;
......@@ -236,8 +245,8 @@ int led_get_intensity(pyb_led_t led) {
#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);
const led_pwm_config_t *pwm_cfg = &led_pwm_config[led - 1];
mp_uint_t i = (*LED_PWM_CCR(pwm_cfg) * 255 + LED_PWM_TIM_PERIOD - 2) / (LED_PWM_TIM_PERIOD - 1);
if (i > 255) {
i = 255;
}
......@@ -248,26 +257,25 @@ int led_get_intensity(pyb_led_t led) {
const pin_obj_t *led_pin = pyb_led_obj[led - 1].led_pin;
GPIO_TypeDef *gpio = led_pin->gpio;
// TODO convert high/low to on/off depending on board
if (gpio->ODR & led_pin->pin_mask) {
// pin is high
return 255;
return MICROPY_HW_LED_INVERTED ? 0 : 255;
} else {
// pin is low
return 0;
return MICROPY_HW_LED_INVERTED ? 255 : 0;
}
}
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) {
const led_pwm_config_t *pwm_cfg = &led_pwm_config[led - 1];
if (pwm_cfg->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;
*LED_PWM_CCR(pwm_cfg) = intensity * (LED_PWM_TIM_PERIOD - 1) / 255;
return;
}
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment