Skip to content

Initial support for RGB led as Status indicator, fixes #1382 #1925

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jul 9, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions ports/nrf/boards/particle_xenon/mpconfigboard.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@

#define MICROPY_HW_LED_STATUS (&pin_P1_12)

#define MICROPY_HW_RGB_LED_RED (&pin_P0_13)
#define MICROPY_HW_RGB_LED_GREEN (&pin_P0_14)
#define MICROPY_HW_RGB_LED_BLUE (&pin_P0_15)
#define CP_RGB_STATUS_R (&pin_P0_13)
#define CP_RGB_STATUS_G (&pin_P0_14)
#define CP_RGB_STATUS_B (&pin_P0_15)

#if QSPI_FLASH_FILESYSTEM
#define MICROPY_QSPI_DATA0 NRF_GPIO_PIN_MAP(0, 20)
Expand Down
55 changes: 30 additions & 25 deletions ports/nrf/common-hal/pulseio/PWMOut.c
Original file line number Diff line number Diff line change
Expand Up @@ -77,33 +77,37 @@ void common_hal_pulseio_pwmout_reset_ok(pulseio_pwmout_obj_t *self) {
}
}

void reset_single_pwmout(uint8_t i) {
NRF_PWM_Type* pwm = pwms[i];

pwm->ENABLE = 0;
pwm->MODE = PWM_MODE_UPDOWN_Up;
pwm->DECODER = PWM_DECODER_LOAD_Individual;
pwm->LOOP = 0;
pwm->PRESCALER = PWM_PRESCALER_PRESCALER_DIV_1; // default is 500 hz
pwm->COUNTERTOP = (PWM_MAX_FREQ/500); // default is 500 hz

pwm->SEQ[0].PTR = (uint32_t) pwm_seq[i];
pwm->SEQ[0].CNT = CHANNELS_PER_PWM; // default mode is Individual --> count must be 4
pwm->SEQ[0].REFRESH = 0;
pwm->SEQ[0].ENDDELAY = 0;

pwm->SEQ[1].PTR = 0;
pwm->SEQ[1].CNT = 0;
pwm->SEQ[1].REFRESH = 0;
pwm->SEQ[1].ENDDELAY = 0;

for(int ch =0; ch < CHANNELS_PER_PWM; ch++) {
pwm_seq[i][ch] = (1 << 15); // polarity = 0
}
}

void pwmout_reset(void) {
for(size_t i=0; i < MP_ARRAY_SIZE(pwms); i++) {
if (never_reset_pwm[i] > 0) {
continue;
}
NRF_PWM_Type* pwm = pwms[i];

pwm->ENABLE = 0;
pwm->MODE = PWM_MODE_UPDOWN_Up;
pwm->DECODER = PWM_DECODER_LOAD_Individual;
pwm->LOOP = 0;
pwm->PRESCALER = PWM_PRESCALER_PRESCALER_DIV_1; // default is 500 hz
pwm->COUNTERTOP = (PWM_MAX_FREQ/500); // default is 500 hz

pwm->SEQ[0].PTR = (uint32_t) pwm_seq[i];
pwm->SEQ[0].CNT = CHANNELS_PER_PWM; // default mode is Individual --> count must be 4
pwm->SEQ[0].REFRESH = 0;
pwm->SEQ[0].ENDDELAY = 0;

pwm->SEQ[1].PTR = 0;
pwm->SEQ[1].CNT = 0;
pwm->SEQ[1].REFRESH = 0;
pwm->SEQ[1].ENDDELAY = 0;

for(int ch =0; ch < CHANNELS_PER_PWM; ch++) {
pwm_seq[i][ch] = (1 << 15); // polarity = 0
}
reset_single_pwmout(i);
}
}

Expand Down Expand Up @@ -148,9 +152,9 @@ pwmout_result_t common_hal_pulseio_pwmout_construct(pulseio_pwmout_obj_t* self,
self->channel = CHANNELS_PER_PWM; // out-of-range value.
bool pwm_already_in_use;
NRF_PWM_Type* pwm;

for (size_t i = 0 ; i < MP_ARRAY_SIZE(pwms); i++) {
pwm = pwms[i];
size_t pwm_index = 0;
for (; pwm_index < MP_ARRAY_SIZE(pwms); pwm_index++) {
pwm = pwms[pwm_index];
pwm_already_in_use = pwm->ENABLE & SPIM_ENABLE_ENABLE_Msk;
if (pwm_already_in_use) {
if (variable_frequency) {
Expand Down Expand Up @@ -199,6 +203,7 @@ pwmout_result_t common_hal_pulseio_pwmout_construct(pulseio_pwmout_obj_t* self,
nrf_pwm_disable(pwm);

if (!pwm_already_in_use) {
reset_single_pwmout(pwm_index);
nrf_pwm_configure(pwm, base_clock, NRF_PWM_MODE_UP, countertop);
}

Expand Down
125 changes: 117 additions & 8 deletions supervisor/shared/rgb_led_status.c
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,28 @@ busio_spi_obj_t status_apa102;
#endif
#endif

#if defined(MICROPY_HW_NEOPIXEL) || (defined(MICROPY_HW_APA102_MOSI) && defined(MICROPY_HW_APA102_SCK))
#if defined(CP_RGB_STATUS_R) || defined(CP_RGB_STATUS_G) || defined(CP_RGB_STATUS_B)
#define CP_RGB_STATUS_LED

#include "shared-bindings/pulseio/PWMOut.h"
#include "shared-bindings/microcontroller/Pin.h"

pulseio_pwmout_obj_t rgb_status_r;
pulseio_pwmout_obj_t rgb_status_g;
pulseio_pwmout_obj_t rgb_status_b;

uint8_t rgb_status_brightness = 0xFF;

uint16_t status_rgb_color[3] = {
0 /* red */, 0 /* green */, 0 /* blue */
};
#endif

#if defined(MICROPY_HW_NEOPIXEL) || (defined(MICROPY_HW_APA102_MOSI) && defined(MICROPY_HW_APA102_SCK)) || (defined(CP_RGB_STATUS_LED))
static uint32_t current_status_color = 0;
#endif


void rgb_led_status_init() {
#ifdef MICROPY_HW_NEOPIXEL
common_hal_digitalio_digitalinout_construct(&status_neopixel, MICROPY_HW_NEOPIXEL);
Expand Down Expand Up @@ -93,7 +111,34 @@ void rgb_led_status_init() {
#endif
#endif

#if defined(MICROPY_HW_NEOPIXEL) || (defined(MICROPY_HW_APA102_MOSI) && defined(MICROPY_HW_APA102_SCK))

#if defined(CP_RGB_STATUS_LED)
if (common_hal_mcu_pin_is_free(CP_RGB_STATUS_R)) {
pwmout_result_t red_result = common_hal_pulseio_pwmout_construct(&rgb_status_r, CP_RGB_STATUS_R, 0, 50000, false);

if (PWMOUT_OK == red_result) {
common_hal_pulseio_pwmout_never_reset(&rgb_status_r);
}
}

if (common_hal_mcu_pin_is_free(CP_RGB_STATUS_G)) {
pwmout_result_t green_result = common_hal_pulseio_pwmout_construct(&rgb_status_g, CP_RGB_STATUS_G, 0, 50000, false);

if (PWMOUT_OK == green_result) {
common_hal_pulseio_pwmout_never_reset(&rgb_status_g);
}
}

if (common_hal_mcu_pin_is_free(CP_RGB_STATUS_B)) {
pwmout_result_t blue_result = common_hal_pulseio_pwmout_construct(&rgb_status_b, CP_RGB_STATUS_B, 0, 50000, false);

if (PWMOUT_OK == blue_result) {
common_hal_pulseio_pwmout_never_reset(&rgb_status_b);
}
}
#endif

#if defined(MICROPY_HW_NEOPIXEL) || (defined(MICROPY_HW_APA102_MOSI) && defined(MICROPY_HW_APA102_SCK)) || (defined(CP_RGB_STATUS_LED))
// Force a write of the current status color.
uint32_t rgb = current_status_color;
current_status_color = 0x1000000; // Not a valid color
Expand All @@ -109,10 +154,13 @@ void reset_status_led() {
reset_pin_number(MICROPY_HW_APA102_MOSI->number);
reset_pin_number(MICROPY_HW_APA102_SCK->number);
#endif
#if defined(CP_RGB_STATUS_LED)
// TODO: Support sharing status LED with user.
#endif
}

void new_status_color(uint32_t rgb) {
#if defined(MICROPY_HW_NEOPIXEL) || (defined(MICROPY_HW_APA102_MOSI) && defined(MICROPY_HW_APA102_SCK))
#if defined(MICROPY_HW_NEOPIXEL) || (defined(MICROPY_HW_APA102_MOSI) && defined(MICROPY_HW_APA102_SCK)) || (defined(CP_RGB_STATUS_LED))
if (current_status_color == rgb) {
return;
}
Expand Down Expand Up @@ -143,10 +191,30 @@ void new_status_color(uint32_t rgb) {
common_hal_busio_spi_write(&status_apa102, status_apa102_color, 8);
#endif
#endif

#if defined(CP_RGB_STATUS_LED)
uint8_t red_u8 = (rgb_adjusted >> 16) & 0xFF;
uint8_t green_u8 = (rgb_adjusted >> 8) & 0xFF;
uint8_t blue_u8 = rgb_adjusted & 0xFF;

#if defined(CP_RGB_STATUS_INVERTED_PWM)
status_rgb_color[0] = (1 << 16) - 1 - ((uint16_t) (red_u8 << 8) + red_u8);
status_rgb_color[1] = (1 << 16) - 1 - ((uint16_t) (green_u8 << 8) + green_u8);
status_rgb_color[2] = (1 << 16) - 1 - ((uint16_t) (blue_u8 << 8) + blue_u8);
#else
status_rgb_color[0] = (uint16_t) (red_u8 << 8) + red_u8;
status_rgb_color[1] = (uint16_t) (green_u8 << 8) + green_u8;
status_rgb_color[2] = (uint16_t) (blue_u8 << 8) + blue_u8;
#endif

common_hal_pulseio_pwmout_set_duty_cycle(&rgb_status_r, status_rgb_color[0]);
common_hal_pulseio_pwmout_set_duty_cycle(&rgb_status_g, status_rgb_color[1]);
common_hal_pulseio_pwmout_set_duty_cycle(&rgb_status_b, status_rgb_color[2]);
#endif
}

void temp_status_color(uint32_t rgb) {
#if defined(MICROPY_HW_NEOPIXEL) || (defined(MICROPY_HW_APA102_MOSI) && defined(MICROPY_HW_APA102_SCK))
#if defined(MICROPY_HW_NEOPIXEL) || (defined(MICROPY_HW_APA102_MOSI) && defined(MICROPY_HW_APA102_SCK)) || (defined(CP_RGB_STATUS_LED))
uint32_t rgb_adjusted = rgb;
rgb_adjusted = color_brightness(rgb, rgb_status_brightness);
#endif
Expand All @@ -168,6 +236,27 @@ void temp_status_color(uint32_t rgb) {
common_hal_busio_spi_write(&status_apa102, colors, 12);
#endif
#endif
#if defined(CP_RGB_STATUS_LED)
uint8_t red_u8 = (rgb_adjusted >> 16) & 0xFF;
uint8_t green_u8 = (rgb_adjusted >> 8) & 0xFF;
uint8_t blue_u8 = rgb_adjusted & 0xFF;

uint16_t temp_status_color_rgb[3] = {0};

#if defined(CP_RGB_STATUS_INVERTED_PWM)
temp_status_color_rgb[0] = (1 << 16) - 1 - ((uint16_t) (red_u8 << 8) + red_u8);
temp_status_color_rgb[1] = (1 << 16) - 1 - ((uint16_t) (green_u8 << 8) + green_u8);
temp_status_color_rgb[2] = (1 << 16) - 1 - ((uint16_t) (blue_u8 << 8) + blue_u8);
#else
temp_status_color_rgb[0] = (uint16_t) (red_u8 << 8) + red_u8;
temp_status_color_rgb[1] = (uint16_t) (green_u8 << 8) + green_u8;
temp_status_color_rgb[2] = (uint16_t) (blue_u8 << 8) + blue_u8;
#endif

common_hal_pulseio_pwmout_set_duty_cycle(&rgb_status_r, temp_status_color_rgb[0]);
common_hal_pulseio_pwmout_set_duty_cycle(&rgb_status_g, temp_status_color_rgb[1]);
common_hal_pulseio_pwmout_set_duty_cycle(&rgb_status_b, temp_status_color_rgb[2]);
#endif
}

void clear_temp_status() {
Expand All @@ -181,10 +270,30 @@ void clear_temp_status() {
common_hal_busio_spi_write(&status_apa102, status_apa102_color, 8);
#endif
#endif
#if defined(CP_RGB_STATUS_LED)

uint16_t red = 0;
uint16_t green = 0;
uint16_t blue = 0;

#if defined(CP_RGB_STATUS_INVERTED_PWM)
red = (1 << 16) - 1 - status_rgb_color[0];
green = (1 << 16) - 1 - status_rgb_color[1];
blue = (1 << 16) - 1 - status_rgb_color[2];
#else
red = status_rgb_color[0];
green = status_rgb_color[1];
blue = status_rgb_color[2];
#endif

common_hal_pulseio_pwmout_set_duty_cycle(&rgb_status_r, red);
common_hal_pulseio_pwmout_set_duty_cycle(&rgb_status_g, green);
common_hal_pulseio_pwmout_set_duty_cycle(&rgb_status_b, blue);
#endif
}

uint32_t color_brightness(uint32_t color, uint8_t brightness) {
#if defined(MICROPY_HW_NEOPIXEL) || (defined(MICROPY_HW_APA102_MOSI) && defined(MICROPY_HW_APA102_SCK))
#if defined(MICROPY_HW_NEOPIXEL) || (defined(MICROPY_HW_APA102_MOSI) && defined(MICROPY_HW_APA102_SCK)) || (defined(CP_RGB_STATUS_LED))
uint32_t result = ((color & 0xff0000) * brightness / 255) & 0xff0000;
result += ((color & 0xff00) * brightness / 255) & 0xff00;
result += ((color & 0xff) * brightness / 255) & 0xff;
Expand All @@ -195,7 +304,7 @@ uint32_t color_brightness(uint32_t color, uint8_t brightness) {
}

void set_rgb_status_brightness(uint8_t level){
#if defined(MICROPY_HW_NEOPIXEL) || (defined(MICROPY_HW_APA102_MOSI) && defined(MICROPY_HW_APA102_SCK))
#if defined(MICROPY_HW_NEOPIXEL) || (defined(MICROPY_HW_APA102_MOSI) && defined(MICROPY_HW_APA102_SCK)) || (defined(CP_RGB_STATUS_LED))
rgb_status_brightness = level;
uint32_t current_color = current_status_color;
// Temporarily change the current color global to force the new_status_color call to update the
Expand All @@ -210,7 +319,7 @@ void prep_rgb_status_animation(const pyexec_result_t* result,
bool found_main,
safe_mode_t safe_mode,
rgb_status_animation_t* status) {
#if defined(MICROPY_HW_NEOPIXEL) || (defined(MICROPY_HW_APA102_MOSI) && defined(MICROPY_HW_APA102_SCK))
#if defined(MICROPY_HW_NEOPIXEL) || (defined(MICROPY_HW_APA102_MOSI) && defined(MICROPY_HW_APA102_SCK)) || (defined(CP_RGB_STATUS_LED))
new_status_color(ALL_DONE);
status->pattern_start = ticks_ms;
status->safe_mode = safe_mode;
Expand Down Expand Up @@ -256,7 +365,7 @@ void prep_rgb_status_animation(const pyexec_result_t* result,
}

void tick_rgb_status_animation(rgb_status_animation_t* status) {
#if defined(MICROPY_HW_NEOPIXEL) || (defined(MICROPY_HW_APA102_MOSI) && defined(MICROPY_HW_APA102_SCK))
#if defined(MICROPY_HW_NEOPIXEL) || (defined(MICROPY_HW_APA102_MOSI) && defined(MICROPY_HW_APA102_SCK)) || (defined(CP_RGB_STATUS_LED))
uint32_t tick_diff = ticks_ms - status->pattern_start;
if (status->ok) {
// All is good. Ramp ALL_DONE up and down.
Expand Down