From b23b7088cbba364bd37a7ec5e6572f1ecf234e7a Mon Sep 17 00:00:00 2001 From: Amaury Pouly Date: Wed, 11 Jan 2017 16:58:30 +0100 Subject: imx233: add small framework for LED It handles GPIO and PWM based LEDs, possibly with several channels (red-green LED for example). The debug allows one to play with the setting. Currently the code supports the ZEN, ZEN X-Fi, and ZEN Mozaic. Change-Id: I8c3b66e6ba21778acdb123daabb724280a7d1a4f --- firmware/target/arm/imx233/debug-imx233.c | 134 ++++++++++++++++++++++++++++- firmware/target/arm/imx233/led-imx233.c | 100 +++++++++++++++++++++ firmware/target/arm/imx233/led-imx233.h | 69 +++++++++++++++ firmware/target/arm/imx233/pwm-imx233.c | 11 ++- firmware/target/arm/imx233/system-imx233.c | 2 + 5 files changed, 311 insertions(+), 5 deletions(-) create mode 100644 firmware/target/arm/imx233/led-imx233.c create mode 100644 firmware/target/arm/imx233/led-imx233.h (limited to 'firmware/target/arm/imx233') diff --git a/firmware/target/arm/imx233/debug-imx233.c b/firmware/target/arm/imx233/debug-imx233.c index 808716916c..c5646a1c91 100644 --- a/firmware/target/arm/imx233/debug-imx233.c +++ b/firmware/target/arm/imx233/debug-imx233.c @@ -45,6 +45,7 @@ #include "button.h" #include "button-imx233.h" #include "sdmmc-imx233.h" +#include "led-imx233.h" #include "storage.h" #include "regs/usbphy.h" @@ -792,6 +793,13 @@ bool dbg_hw_info_ocotp(void) } } +static void get_pwm_freq_duty(int chan, int *freq, int *duty) +{ + struct imx233_pwm_info_t info = imx233_pwm_get_info(chan); + *freq = imx233_clkctrl_get_freq(CLK_XTAL) * 1000 / info.cdiv / info.period; + *duty = (info.inactive - info.active) * 100 / info.period; +} + bool dbg_hw_info_pwm(void) { lcd_setfont(FONT_SYSFIXED); @@ -833,14 +841,14 @@ bool dbg_hw_info_pwm(void) } else { - char *prefix = ""; - int freq = imx233_clkctrl_get_freq(CLK_XTAL) * 1000 / info.cdiv / info.period; + int freq, duty; + get_pwm_freq_duty(i, &freq, &duty); + const char *prefix = ""; if(freq > 1000) { prefix = "K"; freq /= 1000; } - int duty = (info.inactive - info.active) * 100 / info.period; lcd_putsf(0, line++, "%d @%d %sHz, %d%% %c/%c", i, freq, prefix, duty, info.active_state, info.inactive_state); } @@ -1261,6 +1269,125 @@ bool dbg_hw_info_sdmmc(void) } } +static const char *get_led_col(enum imx233_led_color_t col) +{ + switch(col) + { + case LED_RED: return "red"; + case LED_GREEN: return "green"; + case LED_BLUE: return "blue"; + default: return "unknown"; + } +} + +bool dbg_hw_info_led(void) +{ + lcd_setfont(FONT_SYSFIXED); + int cur_led = 0, cur_chan = 0; + bool nr_leds = imx233_led_get_count(); + struct imx233_led_t *leds = imx233_led_get_info(); + bool prev_pending = false; + bool next_pending = false; + bool editing = false; + + while(1) + { + int button = my_get_action(HZ / 10); + switch(button) + { + case ACT_NEXT: + if(nr_leds > 0 && !editing) + { + cur_chan++; + if(cur_chan == leds[cur_led].nr_chan) + { + cur_chan = 0; + cur_led = (cur_led + 1) % nr_leds; + } + } + else + next_pending = true; + break; + case ACT_PREV: + if(nr_leds > 0 && !editing) + { + cur_chan--; + if(cur_chan == -1) + { + cur_led = (cur_led + nr_leds - 1) % nr_leds; + cur_chan = leds[cur_led].nr_chan - 1; + } + } + else + prev_pending = true; + break; + case ACT_OK: + editing = !editing; + break; + case ACT_CANCEL: + lcd_setfont(FONT_UI); + return false; + } + + lcd_clear_display(); + int line = 0; + if(nr_leds == 0) + lcd_putsf(0, line++, "This device has no LED!"); + for(int led = 0; led < imx233_led_get_count(); led++) + { + lcd_putsf(0, line++, "LED %d:", led); + for(int chan = 0; chan < leds[led].nr_chan; chan++) + { + /* read current configuration */ + char buffer[64]; + bool use_pwm = false; + int duty = 0, freq = 1; + bool on = false; + if(leds[led].chan[chan].has_pwm && + imx233_pwm_is_enabled(leds[led].chan[chan].pwm_chan)) + { + get_pwm_freq_duty(leds[led].chan[chan].pwm_chan, &freq, &duty); + /* assume active is high and inactive is low */ + snprintf(buffer, sizeof(buffer), "%d Hz, %d%%", freq, duty); + use_pwm = true; + } + else + { + on = imx233_pinctrl_get_gpio(leds[led].chan[chan].gpio_bank, + leds[led].chan[chan].gpio_pin); + snprintf(buffer, sizeof(buffer), "%s", on ? "on" : "off"); + } + if(cur_led == led && cur_chan == chan) + lcd_set_foreground(editing ? LCD_RGBPACK(255, 0, 0) : LCD_RGBPACK(0, 0, 255)); + lcd_putsf(0, line++, " %s: %s", + get_led_col(leds[led].chan[chan].color), buffer); + lcd_set_foreground(LCD_WHITE); + /* do edit */ + if(cur_led != led || cur_chan != chan || !editing) + continue; + if(!next_pending && !prev_pending) + continue; + bool inc = next_pending; + next_pending = false; + prev_pending = false; + if(use_pwm) + { + duty += inc ? 10 : -10; + if(duty < 0) + duty = 0; + if(duty > 100) + duty = 100; + imx233_led_set_pwm(cur_led, cur_chan, freq, duty); + } + else + imx233_led_set(cur_led, cur_chan, !on); + } + } + lcd_update(); + yield(); + } +} + #ifdef HAVE_DUALBOOT_STUB bool dbg_hw_info_dualboot(void) { @@ -1341,6 +1468,7 @@ static struct {"timrot", dbg_hw_info_timrot}, {"button", dbg_hw_info_button}, {"sdmmc", dbg_hw_info_sdmmc}, + {"led", dbg_hw_info_led}, #ifdef HAVE_DUALBOOT_STUB {"dualboot", dbg_hw_info_dualboot}, #endif diff --git a/firmware/target/arm/imx233/led-imx233.c b/firmware/target/arm/imx233/led-imx233.c new file mode 100644 index 0000000000..88c2b7e054 --- /dev/null +++ b/firmware/target/arm/imx233/led-imx233.c @@ -0,0 +1,100 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (c) 2017 by Amaury Pouly + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#include "led-imx233.h" +#include "pwm-imx233.h" +#include "pinctrl-imx233.h" + +/** Target specific tables */ +#if defined(CREATIVE_ZENXFI) || defined(CREATIVE_ZENMOZAIC) +/* ZEN X-Fi/Mozaic have a Red-Green LED */ +static struct imx233_led_chan_t zenxfi_led_chans[] = +{ + { true, LED_RED, 2, 2, 2 }, /* red channel on B20P2 (pwm 2) */ + { true, LED_GREEN, 2, 4, 4 }, /* green channel on B20P4 (pwm 4) */ +}; +static struct imx233_led_t leds[] = {{2, zenxfi_led_chans}}; +#elif defined(CREATIVE_ZEN) +/* ZEN has a blue LED */ +static struct imx233_led_chan_t zen_led_chans[] = +{ + { true, LED_BLUE, 2, 3, 3 }, /* blue channel on B20P3 (pwm 3) */ +}; +static struct imx233_led_t leds[] = {{1, zen_led_chans}}; +#else +#define NO_LEDS +#endif + +#ifndef NO_LEDS +int imx233_led_get_count(void) +{ + return sizeof(leds) / sizeof(leds[0]); +} + +struct imx233_led_t *imx233_led_get_info(void) +{ + return leds; +} +#else +int imx233_led_get_count(void) +{ + return 0; +} + +struct imx233_led_t * imx233_led_get_info(void) +{ + return NULL; +} +#endif + +void imx233_led_init(void) +{ + struct imx233_led_t *leds = imx233_led_get_info(); + /* turn off all channels */ + for(int led = 0; led < imx233_led_get_count(); led++) + for(int chan = 0; chan < leds[led].nr_chan; chan++) + imx233_led_set(led, chan, false); +} + +void imx233_led_set(int led, int chan, bool on) +{ + struct imx233_led_chan_t *c = &imx233_led_get_info()[led].chan[chan]; + /* if LED has a PWM, handle it using the PWM */ + if(c->has_pwm) + { + /* toogle at 1KHz */ + return imx233_led_set_pwm(led, chan, 1000, on ? 100 : 0); + } + /* make sure pin is configured as a GPIO */ + imx233_pinctrl_setup_vpin( + VPIN_PACK(c->gpio_bank, c->gpio_pin, GPIO), "led", + PINCTRL_DRIVE_4mA, false); + imx233_pinctrl_enable_gpio(c->gpio_bank, c->gpio_pin, true); + imx233_pinctrl_set_gpio(c->gpio_bank, c->gpio_pin, on); +} + +void imx233_led_set_pwm(int led, int chan, int freq, int duty) +{ + struct imx233_led_chan_t *c = &imx233_led_get_info()[led].chan[chan]; + if(!c->has_pwm) + panicf("led %d chan %d cannot do pwm", led, chan); + imx233_pwm_setup_simple(c->pwm_chan, freq, duty); + imx233_pwm_enable(c->pwm_chan, true); +} diff --git a/firmware/target/arm/imx233/led-imx233.h b/firmware/target/arm/imx233/led-imx233.h new file mode 100644 index 0000000000..658135c94a --- /dev/null +++ b/firmware/target/arm/imx233/led-imx233.h @@ -0,0 +1,69 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (c) 2017 by Amaury Pouly + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#ifndef __LED_IMX233_H__ +#define __LED_IMX233_H__ + +#include "system.h" + +/** LED API + * + * The following assumes each single LED is controllable by one + * or more channels. Each channel is either controlled by a GPIO (on/off) or a + * PWM. Each channel also has a color, so it is possible to describe + * a red-green LED for example. */ + +/* LED channel color */ +enum imx233_led_color_t +{ + LED_RED, + LED_GREEN, + LED_BLUE, +}; + +/* LED channel */ +struct imx233_led_chan_t +{ + bool has_pwm; /* GPIO or PWM */ + enum imx233_led_color_t color; /* color */ + int gpio_bank, gpio_pin; /* GPIO bank and pin (also valid for PWM) */ + int pwm_chan; /* for PWM: harware channel*/ +}; + +/* LED */ +struct imx233_led_t +{ + int nr_chan; /* number of channels */ + struct imx233_led_chan_t *chan; /* array of channels */ +}; + +/* init: all LEDs are turned off on init */ +void imx233_led_init(void); +/* get number of LEDs */ +int imx233_led_get_count(void); +/* return an array of LEDs */ +struct imx233_led_t *imx233_led_get_info(void); +/* set LED channel on/off, it also works for PWM channel by setting the PWM to + * constantly off or constantly on */ +void imx233_led_set(int led, int chan, bool on); +/* set LED PWM: control the frequency and the duty cycle percentage (0-100) */ +void imx233_led_set_pwm(int led, int chan, int freq, int duty); + +#endif /* __LED_IMX233_H__ */ diff --git a/firmware/target/arm/imx233/pwm-imx233.c b/firmware/target/arm/imx233/pwm-imx233.c index 5e1cc1daa5..3704b60ff2 100644 --- a/firmware/target/arm/imx233/pwm-imx233.c +++ b/firmware/target/arm/imx233/pwm-imx233.c @@ -54,9 +54,18 @@ bool imx233_pwm_is_enabled(int channel) void imx233_pwm_enable(int channel, bool enable) { if(enable) + { + /* claim pin */ + imx233_pinctrl_setup_vpin(VPIN_PWM(channel), "pwm", PINCTRL_DRIVE_4mA, false); BF_SET(PWM_CTRL, PWMx_ENABLE(channel)); + } else + { BF_CLR(PWM_CTRL, PWMx_ENABLE(channel)); + /* stop claiming the pin */ + imx233_pinctrl_release(VPIN_UNPACK_BANK(VPIN_PWM(channel)), + VPIN_UNPACK_PIN(VPIN_PWM(channel)), "pwm"); + } } void imx233_pwm_setup(int channel, int period, int cdiv, int active, @@ -66,8 +75,6 @@ void imx233_pwm_setup(int channel, int period, int cdiv, int active, bool enable = imx233_pwm_is_enabled(channel); if(enable) imx233_pwm_enable(channel, false); - /* setup pin */ - imx233_pinctrl_setup_vpin(VPIN_PWM(channel), "pwm", PINCTRL_DRIVE_4mA, false); /* watch the order ! active THEN period * NOTE: the register value is period-1 */ BF_WR_ALL(PWM_ACTIVEn(channel), ACTIVE(active), INACTIVE(inactive)); diff --git a/firmware/target/arm/imx233/system-imx233.c b/firmware/target/arm/imx233/system-imx233.c index c6f974b108..860039b85f 100644 --- a/firmware/target/arm/imx233/system-imx233.c +++ b/firmware/target/arm/imx233/system-imx233.c @@ -44,6 +44,7 @@ #include "button.h" #include "fmradio_i2c.h" #include "powermgmt-imx233.h" +#include "led-imx233.h" #include "regs/digctl.h" #include "regs/usbphy.h" @@ -202,6 +203,7 @@ void system_init(void) imx233_power_init(); imx233_i2c_init(); imx233_powermgmt_init(); + imx233_led_init(); /* setup watchdog */ watchdog_init(); -- cgit v1.2.3