diff options
author | Andy Green <andy@warmcat.com> | 2020-06-25 14:44:05 +0100 |
---|---|---|
committer | Andy Green <andy@warmcat.com> | 2020-06-30 19:35:41 +0100 |
commit | 63c1e8ba0007d920cec68a88cce83d268f6fec8a (patch) | |
tree | 2f900cf0ac9f17cf3da7b5bbf7d38d7d6089af78 /lib/drivers | |
parent | 57bfaa9ccb030b4defa0f5c7ddbc1b56291f57cb (diff) | |
download | libwebsockets-63c1e8ba0007d920cec68a88cce83d268f6fec8a.tar.gz |
esp32-wrover-kit
Add lws_display and minimal example support for esp32-wrover to match wsp32-heltec-wb32
Since no usable buttons that don't affect something else on wrover kit, assumes
a button to 0V on GPIO14.
Diffstat (limited to 'lib/drivers')
-rw-r--r-- | lib/drivers/CMakeLists.txt | 5 | ||||
-rw-r--r-- | lib/drivers/button/README.md | 5 | ||||
-rw-r--r-- | lib/drivers/devices/display/ili9341.h | 95 | ||||
-rw-r--r-- | lib/drivers/display/README.md | 36 | ||||
-rw-r--r-- | lib/drivers/display/ili9341-spi.c | 187 | ||||
-rw-r--r-- | lib/drivers/display/lws-display.c | 124 | ||||
-rw-r--r-- | lib/drivers/display/ssd1306-i2c.c | 17 | ||||
-rw-r--r-- | lib/drivers/led/README.md | 1 | ||||
-rw-r--r-- | lib/drivers/led/led-seq.c | 90 | ||||
-rw-r--r-- | lib/drivers/led/private-lib-drivers-led.h | 19 | ||||
-rw-r--r-- | lib/drivers/pwm/pwm.c | 11 | ||||
-rw-r--r-- | lib/drivers/spi/bitbang/lws-bb-spi.c | 126 | ||||
-rw-r--r-- | lib/drivers/spi/lws-spi.c | 26 |
13 files changed, 604 insertions, 138 deletions
diff --git a/lib/drivers/CMakeLists.txt b/lib/drivers/CMakeLists.txt index b15a7c38..1320d885 100644 --- a/lib/drivers/CMakeLists.txt +++ b/lib/drivers/CMakeLists.txt @@ -1,8 +1,11 @@ list(APPEND SOURCES drivers/display/lws-display.c drivers/display/ssd1306-i2c.c - drivers/i2c/lws-i2c.c + drivers/display/ili9341-spi.c + drivers/i2c/lws-i2c.c drivers/i2c/bitbang/lws-bb-i2c.c + drivers/spi/lws-spi.c + drivers/spi/bitbang/lws-bb-spi.c drivers/button/lws-button.c drivers/led/led-gpio.c drivers/led/led-seq.c diff --git a/lib/drivers/button/README.md b/lib/drivers/button/README.md index 6c7b9ce8..758823df 100644 --- a/lib/drivers/button/README.md +++ b/lib/drivers/button/README.md @@ -85,6 +85,7 @@ ms_min_down|20ms|Down events shorter than this are ignored ms_min_down_longpress|300ms|Down events longer than this are reported as a long-click ms_up_settle|20ms|After the first indication a button is no longer down, the button is ignored for this interval ms_doubleclick_grace|120ms|The time allowed after a click to see if a second, double-click, is forthcoming +ms_repeat_down|0 / disabled|If held down, interval at which to issue `stilldown` events flags|LWSBTNRGMFLAG_CLASSIFY_DOUBLECLICK|Control which classifications can apply ### lws_smd System Message Distribution Events @@ -117,6 +118,7 @@ the interaction, eg, that it can be understood as a single "double-click" event. Event name|Meaning ---|--- down|The button passes a filter for being down, useful for duration-based response +stilldown|The regime can be configured to issue "repeat" notifications at intervals up|The button has come up, useful for duration-based response click|The button activity resulted in a classification as a single-click longclick|The button activity resulted in a classification as a long-click @@ -149,3 +151,6 @@ Classification|Sequencing click|down-up-click (it's classified when it went up and cannot be a longclick) longclick|down-longclick-up (it's classified while still down) doubleclick|down-up-down-doubleclick-up (classified as soon as second click down long enough) + +If the regime is configured for it, any "down" may be followed by one or more +"stilldown" at intervals if the button is down long enough diff --git a/lib/drivers/devices/display/ili9341.h b/lib/drivers/devices/display/ili9341.h new file mode 100644 index 00000000..cb4bc249 --- /dev/null +++ b/lib/drivers/devices/display/ili9341.h @@ -0,0 +1,95 @@ +/* + * Private register map for ILI9341 + * + * Copyright (C) 2019 - 2020 Andy Green <andy@warmcat.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + */ + +#if !defined(__LWS_ILI9341_H__) +#define __LWS_ILI9341_H__ + +enum { + + ILI9341_NOP = 0x00, + ILI9341_SWRESET = 0x01, + ILI9341_RDDID = 0x04, + ILI9341_RDDST = 0x09, + + ILI9341_SLPIN = 0x10, + ILI9341_SLPOUT = 0x11, + ILI9341_PTLON = 0x12, + ILI9341_NORON = 0x13, + + ILI9341_RDMODE = 0x0a, + ILI9341_RDMADCTL = 0x0b, + ILI9341_RDPIXFMT = 0x0c, + ILI9341_RDIMGFMT = 0x0d, + ILI9341_RDSELFDIAG = 0x0f, + + ILI9341_INVOFF = 0x20, + ILI9341_INVON = 0x21, + ILI9341_GAMMASET = 0x26, + ILI9341_DISPOFF = 0x28, + ILI9341_DISPON = 0x29, + ILI9341_CASET = 0x2a, + ILI9341_PASET = 0x2b, + ILI9341_RAMWR = 0x2c, + ILI9341_RAMRD = 0x2e, + + ILI9341_PTLAR = 0x30, + ILI9341_VSCRDEF = 0x33, + ILI9341_MADCTL = 0x36, + ILI9341_VSCRSADD = 0x37, + ILI9341_PIXFMT = 0x3a, + + ILI9341_FRMCTR1 = 0xb1, + ILI9341_FRMCTR2 = 0xb2, + ILI9341_FRMCTR3 = 0xb3, + ILI9341_INVCTR = 0xb4, + ILI9341_DFUNCTR = 0xb6, + + ILI9341_PWCTR1 = 0xc0, + ILI9341_PWCTR2 = 0xc1, + ILI9341_PWCTR3 = 0xc2, + ILI9341_PWCTR4 = 0xc3, + ILI9341_PWCTR5 = 0xc4, + ILI9341_VMCTR1 = 0xc5, + ILI9341_VMCTR2 = 0xc7, + ILI9341_FACPUMPRAT = 0xcb, + ILI9341_FACPWCTRB = 0xcf, + + ILI9341_RDID1 = 0xda, + ILI9341_RDID2 = 0xdb, + ILI9341_RDID3 = 0xdc, + ILI9341_RDID4 = 0xdd, + + ILI9341_GMCTRP1 = 0xe0, + ILI9341_GMCTRN1 = 0xe1, + ILI9341_FACPWCTRA = 0xe8, + ILI9341_FACPWCTR1 = 0xea, + ILI9341_FACDRTIMCTRA = 0xed, + + ILI9341_FACSETGAMMACRV = 0xf2, + ILI9341_FACDRTIMCTR = 0xf7, +}; + +#endif + diff --git a/lib/drivers/display/README.md b/lib/drivers/display/README.md new file mode 100644 index 00000000..f3c8b0d9 --- /dev/null +++ b/lib/drivers/display/README.md @@ -0,0 +1,36 @@ +# lws_display + +lws provides a generic "display" object that is independent of the connection +to the display, i2c and spi implementations are provided. + +Its purpose is to provide basic blit, backlight binding to lws_pwm, backlight / +power management and display info like pixels wide and high in a generic way. + +The generic display object `lws_display_t` can be included at the top of a +specific display implementation object, eg, binding it to additional members +to define the actual IO operations to be used, eg, i2c or spi. + +When the display is instantiated, it allocates an additional structure on heap +that contains dynamic information about display state, `lws_display_state_t`. + +## Power state machine + +lws_display objects have convenient power state management using a single lws +sul event loop timer that is managed automatically. + +State|Meaning +---|--- +OFF|The display is in sleep and not showing anything +BECOMING_ACTIVE|The display was asked to come out of sleep and is waiting for .latency_wake_ms befor proceeding to ACTIVE. The backlight if any is off. After the delay, the backlight is sequenced up to `.bl_active` using `.bl_transition` sequencer +ACTIVE|The backlight is ON and the dim timer is running +AUTODIMMED|The dim timer was not told the display was active for `.autodim_ms`, we are at `.bl_dim` brightness. After `.off_ms` we will transition to OFF + +The lws_pwm sequencers are used to provide customizable, smooth transitions for +the backlight, which may be nonlinear. + +## Active notification + +Calling `lws_display_state_active(&lds)` on eg, user interaction causes the +display state to transition to ACTIVE smoothly, taking care of waking the display +and waiting out a display-specific wake period, and sequencing the backlight +transition to active level as specified in the display structure. diff --git a/lib/drivers/display/ili9341-spi.c b/lib/drivers/display/ili9341-spi.c new file mode 100644 index 00000000..1d01be32 --- /dev/null +++ b/lib/drivers/display/ili9341-spi.c @@ -0,0 +1,187 @@ +/* + * lws abstract display implementation for ili9341 on spi + * + * Copyright (C) 2019 - 2020 Andy Green <andy@warmcat.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include <private-lib-core.h> +#include <drivers/devices/display/ili9341.h> + + +static uint8_t ili9341_320x240_init[] = { + /* + * This provides 70Hz 320x240 at RGB565, we assume im[3:0] is 1110 + * which is 4-bit SPI + */ + + 3, ILI9341_FACPWCTRB, 0x00, 0x83, 0x30, + 4, ILI9341_FACDRTIMCTRA, 0x64, 0x03, 0x12, 0x81, + 3, ILI9341_FACPWCTRA, 0x85, 0x01, 0x79, + 5, ILI9341_FACPUMPRAT, 0x39, 0x2c, 0x00, 0x34, 0x02, + 1, ILI9341_FACDRTIMCTR, 0x20, + 2, ILI9341_FACPWCTR1, 0x00, 0x00, + + 1, ILI9341_PWCTR1, 0x26, + 1, ILI9341_PWCTR2, 0x11, + 2, ILI9341_VMCTR1, 0x35, 0x3e, + 1, ILI9341_VMCTR2, 0xbe, + 1, ILI9341_MADCTL, 0x28, + 1, ILI9341_VSCRSADD, 0x00, + 1, ILI9341_PIXFMT, 0x55, + 2, ILI9341_FRMCTR1, 0x00, 0x1b, + 1, ILI9341_FACSETGAMMACRV, 0x00, + 1, ILI9341_GAMMASET, 0x01, + 15, ILI9341_GMCTRP1, 0x0f, 0x31, 0x2b, 0x0c, 0x0e, 0x08, 0x4e, + 0xf1, 0x37, 0x07, 0x10, 0x03, 0x0e, 0x09, + 0x00, + 15, ILI9341_GMCTRN1, 0x00, 0x0e, 0x14, 0x03, 0x11, 0x07, 0x31, + 0xc1, 0x48, 0x08, 0x0f, 0x0c, 0x31, 0x36, + 0x0f, + 4, ILI9341_DFUNCTR, 0x0a, 0x82, 0x27, 0x00, +}; + +int +lws_display_ili9341_spi_init(const struct lws_display *disp) +{ + const lws_display_ili9341_t *ili = (const lws_display_ili9341_t *)disp; + lws_spi_desc_t desc; + size_t pos = 0; + uint8_t u[8]; + + lwsl_user("%s\n", __func__); + + /* hardware nRESET */ + + if (ili->gpio) { + ili->gpio->mode(ili->reset_gpio, LWSGGPIO_FL_WRITE | + LWSGGPIO_FL_PULLUP); + ili->gpio->set(ili->reset_gpio, 0); + + lws_msleep(1); + ili->gpio->set(ili->reset_gpio, 1); + lws_msleep(1); + } + + /* + * We cut the init table up into transactions... atm we just go with + * the fact that bb spi is synchronous, using async / dma we can't use + * a single desc on the stack like this + */ + + memset(&desc, 0, sizeof(desc)); + desc.count_cmd = 1; + + while (pos < LWS_ARRAY_SIZE(ili9341_320x240_init)) { + desc.count_write = ili9341_320x240_init[pos++]; + desc.src = &ili9341_320x240_init[pos++]; + desc.data = &ili9341_320x240_init[pos]; + pos += desc.count_write; + + ili->spi->queue(ili->spi, &desc); + } + + u[0] = ILI9341_SLPOUT; + desc.src = &u[0]; + desc.count_write = 0; + ili->spi->queue(ili->spi, &desc); + + lws_msleep(5); + + u[0] = ILI9341_DISPON; + ili->spi->queue(ili->spi, &desc); + + return 0; +} + +/* backlight handled by PWM */ + +int +lws_display_ili9341_spi_brightness(const struct lws_display *disp, uint8_t b) +{ + return 0; +} + +int +lws_display_ili9341_spi_blit(const struct lws_display *disp, const uint8_t *src, + lws_display_scalar x, lws_display_scalar y, + lws_display_scalar w, lws_display_scalar h) +{ + const lws_display_ili9341_t *ili = (const lws_display_ili9341_t *)disp; + lws_spi_desc_t desc; + uint8_t u[5]; + + memset(&desc, 0, sizeof(desc)); + desc.count_cmd = 1; + desc.src = &u[0]; + desc.count_write = 0; + + /* + * Blit a line at a time + */ + + while (h--) { + + u[0] = ILI9341_CASET; + desc.data = &u[1]; + u[1] = x; + u[2] = x; + u[3] = w >> 8; + u[4] = w & 0xff; + desc.count_write = 4; + ili->spi->queue(ili->spi, &desc); + + u[0] = ILI9341_PASET; + u[1] = y >> 8; + u[2] = y & 0xff; + u[3] = (y + 1) >> 8; + u[4] = (y + 1) & 0xff; + desc.count_write = 4; + ili->spi->queue(ili->spi, &desc); + + u[0] = ILI9341_RAMWR; + desc.data = src; + desc.count_write = w * 2; + ili->spi->queue(ili->spi, &desc); + src += w * 2; + y++; + } + + return 0; +} + +int +lws_display_ili9341_spi_power(const struct lws_display *disp, int state) +{ + + const lws_display_ili9341_t *ili = (const lws_display_ili9341_t *)disp; + lws_spi_desc_t desc; + uint8_t u[1]; + + memset(&desc, 0, sizeof(desc)); + desc.count_cmd = 1; + desc.data = desc.src = &u[0]; + u[0] = state ? ILI9341_SLPOUT : ILI9341_SLPIN; + ili->spi->queue(ili->spi, &desc); + + /* we're not going to do anything useful for 5ms after this */ + + return 0; +} diff --git a/lib/drivers/display/lws-display.c b/lib/drivers/display/lws-display.c index 32e85368..28d0c97f 100644 --- a/lib/drivers/display/lws-display.c +++ b/lib/drivers/display/lws-display.c @@ -25,102 +25,108 @@ #include <libwebsockets.h> static void -sul_cb(lws_sorted_usec_list_t *sul) -{ - lws_display_state_t *lds = lws_container_of(sul, lws_display_state_t, - sul); - - if (lds->bl_target > lds->bl_current) { - if (lds->bl_target - lds->bl_current < lds->bl_step) - lds->bl_current = lds->bl_target; - else - lds->bl_current += lds->bl_step; - } else { - if (lds->bl_current - lds->bl_target < lds->bl_step) - lds->bl_current = lds->bl_target; - else - lds->bl_current -= lds->bl_step; - } - - lds->disp->brightness(lds->disp, lds->bl_current); - - if (lds->bl_current != lds->bl_target) - /* - * Come back and move towards the target again in 50ms - */ - lws_sul_schedule(lds->ctx, 0, &lds->sul, - sul_cb, 50 * LWS_US_PER_MS); -} - -static void sul_autodim_cb(lws_sorted_usec_list_t *sul) { lws_display_state_t *lds = lws_container_of(sul, lws_display_state_t, - sul_autodim); + sul_autodim); + int next_ms = -1; /* we fire both to dim and to blank... if already in dim state, blank */ - if (lds->state == LWSDISPS_AUTODIMMED) { + switch (lds->state) { + case LWSDISPS_BECOMING_ACTIVE: + lws_display_state_set_brightness(lds, lds->disp->bl_active); + lds->state = LWSDISPS_ACTIVE; + next_ms = lds->autodim_ms; + break; + + case LWSDISPS_ACTIVE: + /* active -> autodimmed */ + lds->state = LWSDISPS_AUTODIMMED; + next_ms = lds->off_ms; + lws_display_state_set_brightness(lds, lds->disp->bl_dim); + break; + + case LWSDISPS_AUTODIMMED: + /* dimmed -> OFF */ + lws_display_state_set_brightness(lds, &lws_pwmseq_static_off); + lds->state = LWSDISPS_GOING_OFF; + next_ms = 600; + break; + + case LWSDISPS_GOING_OFF: + /* off dimming completed, actual display OFF */ lws_display_state_off(lds); return; - } - lds->state = LWSDISPS_AUTODIMMED; - lws_display_state_set_brightness(lds, lds->bl_dim, lds->bl_step); + default: + return; + } - if (lds->off_ms >= 0) + if (next_ms >= 0) lws_sul_schedule(lds->ctx, 0, &lds->sul_autodim, sul_autodim_cb, - lds->off_ms * LWS_US_PER_MS); + next_ms * LWS_US_PER_MS); } void -lws_display_state_init(lws_display_state_t *ds, struct lws_context *ctx, - int dim_ms, int off_ms, lws_display_brightness active, - lws_display_brightness dim, const lws_display_t *disp) +lws_display_state_init(lws_display_state_t *lds, struct lws_context *ctx, + int dim_ms, int off_ms, struct lws_led_state *bl_lcs, + const lws_display_t *disp) { - memset(ds, 0, sizeof(*ds)); - ds->disp = disp; - ds->ctx = ctx; - ds->autodim_ms = dim_ms; - ds->off_ms = off_ms; - ds->bl_active = active; - ds->bl_dim = dim; + memset(lds, 0, sizeof(*lds)); + + lds->disp = disp; + lds->ctx = ctx; + lds->autodim_ms = dim_ms; + lds->off_ms = off_ms; + lds->bl_lcs = bl_lcs; + lds->state = LWSDISPS_OFF; + + lws_led_transition(lds->bl_lcs, "backlight", &lws_pwmseq_static_off, + &lws_pwmseq_static_on); + + disp->init(disp); } void lws_display_state_set_brightness(lws_display_state_t *lds, - lws_display_brightness target, - lws_display_brightness step) + const lws_led_sequence_def_t *pwmseq) { - lds->bl_target = target; - lds->bl_step = step; - - lws_sul_schedule(lds->ctx, 0, &lds->sul, sul_cb, 1); + lws_led_transition(lds->bl_lcs, "backlight", pwmseq, + lds->disp->bl_transition); } void lws_display_state_active(lws_display_state_t *lds) { - if (lds->state == LWSDISPS_OFF) + int waiting_ms; + + if (lds->state == LWSDISPS_OFF) { + /* power us up */ lds->disp->power(lds->disp, 1); + lds->state = LWSDISPS_BECOMING_ACTIVE; + waiting_ms = lds->disp->latency_wake_ms; + } else { - if (lds->bl_current != lds->bl_active) - lws_display_state_set_brightness(lds, lds->bl_active, 2); + if (lds->state != LWSDISPS_ACTIVE) + lws_display_state_set_brightness(lds, + lds->disp->bl_active); + + lds->state = LWSDISPS_ACTIVE; + waiting_ms = lds->autodim_ms; + } /* reset the autodim timer */ - if (lds->autodim_ms >= 0) + if (waiting_ms >= 0) lws_sul_schedule(lds->ctx, 0, &lds->sul_autodim, sul_autodim_cb, - lds->autodim_ms * LWS_US_PER_MS); + waiting_ms * LWS_US_PER_MS); - lds->state = LWSDISPS_ACTIVE; } void lws_display_state_off(lws_display_state_t *lds) { lds->disp->power(lds->disp, 0); - lws_sul_cancel(&lds->sul); lws_sul_cancel(&lds->sul_autodim); - lds->bl_current = 0; lds->state = LWSDISPS_OFF; } diff --git a/lib/drivers/display/ssd1306-i2c.c b/lib/drivers/display/ssd1306-i2c.c index a050271f..bfcb7558 100644 --- a/lib/drivers/display/ssd1306-i2c.c +++ b/lib/drivers/display/ssd1306-i2c.c @@ -22,7 +22,7 @@ * IN THE SOFTWARE. */ -#include <libwebsockets.h> +#include <private-lib-core.h> #include <drivers/devices/display/ssd1306.h> @@ -56,18 +56,9 @@ lws_display_ssd1306_i2c_init(const struct lws_display *disp) si->gpio->mode(si->reset_gpio, LWSGGPIO_FL_WRITE | LWSGGPIO_FL_PULLUP); si->gpio->set(si->reset_gpio, 0); - -#if defined(LWS_PLAT_FREERTOS) - vTaskDelay(10); -#else - usleep(10000); -#endif + lws_msleep(1); si->gpio->set(si->reset_gpio, 1); -#if defined(LWS_PLAT_FREERTOS) - vTaskDelay(1); -#else - usleep(1000); -#endif + lws_msleep(1); } if (lws_i2c_command_list(si->i2c, si->i2c7_address, @@ -81,7 +72,7 @@ lws_display_ssd1306_i2c_init(const struct lws_display *disp) } int -lws_display_ssd1306_i2c_brightness(const struct lws_display *disp, uint8_t b) +lws_display_ssd1306_i2c_contrast(const struct lws_display *disp, uint8_t b) { const lws_display_ssd1306_t *si = (const lws_display_ssd1306_t *)disp; uint8_t ba[2]; diff --git a/lib/drivers/led/README.md b/lib/drivers/led/README.md index 1fef8558..794b10ff 100644 --- a/lib/drivers/led/README.md +++ b/lib/drivers/led/README.md @@ -151,4 +151,5 @@ lws_pwmseq_linear_wipe|single 0 - 100% ramp over 0.3s lws_pwmseq_sine_up|single 0 - 100% using sine curve over 0.3s lws_pwmseq_sine_down|single 100% - 0 using sine curve over 0.3s lws_pwmseq_static_on|100% static +lws_pwmseq_static_half|50% static lws_pwmseq_static_off|0% static diff --git a/lib/drivers/led/led-seq.c b/lib/drivers/led/led-seq.c index 49381d39..2fd2f62b 100644 --- a/lib/drivers/led/led-seq.c +++ b/lib/drivers/led/led-seq.c @@ -59,8 +59,8 @@ lws_seq_advance(lws_led_state_t *lcs, lws_led_state_ch_t *ch) if (!ch->seq) return; - if (ch->phase_budget != -1 && - ch->phase_budget < ch->step) { + if (ch->phase_budget != LWS_SEQ_LEDPHASE_TOTAL_ENDLESS && + (ch->phase_budget < ch->step || !ch->phase_budget)) { /* we are done */ @@ -75,40 +75,42 @@ lws_seq_advance(lws_led_state_t *lcs, lws_led_state_ch_t *ch) } ch->ph += ch->step; - if (ch->phase_budget != -1) + if (ch->phase_budget != LWS_SEQ_LEDPHASE_TOTAL_ENDLESS) ch->phase_budget -= ch->step; } static lws_led_intensity_t lws_seq_sample(const lws_led_gpio_map_t *map, lws_led_state_chs_t *chs) { - unsigned int i = 0, mix, nx; + unsigned int i; - if (chs->seqs[LWS_LED_SEQ_IDX_CURR].seq) - i = chs->seqs[LWS_LED_SEQ_IDX_CURR].seq-> - func(chs->seqs[LWS_LED_SEQ_IDX_CURR].ph); + if (chs->seqs[LLSI_CURR].seq) + chs->seqs[LLSI_CURR].last = chs->seqs[LLSI_CURR].seq-> + func(chs->seqs[LLSI_CURR].ph); - if (chs->seqs[LWS_LED_SEQ_IDX_TRANSITION].seq) { + if (chs->seqs[LLSI_TRANS].seq) { /* * If a transition is ongoing, we need to use the transition * intensity as the mixing factor between the still-live current * and newly-live next sequences */ - mix = chs->seqs[LWS_LED_SEQ_IDX_TRANSITION].seq-> - func(chs->seqs[LWS_LED_SEQ_IDX_TRANSITION].ph); - nx = 0; - if (chs->seqs[LWS_LED_SEQ_IDX_NEXT].seq) - nx = chs->seqs[LWS_LED_SEQ_IDX_NEXT].seq-> - func(chs->seqs[LWS_LED_SEQ_IDX_NEXT].ph); - - i = (lws_led_intensity_t)( - ((i * (65535 - mix) / 65536) + - ((nx * mix) / 65536))); - } - - return map->intensity_correction ? - map->intensity_correction(i) : - cie_antilog((lws_led_intensity_t)i); + chs->seqs[LLSI_TRANS].last = chs->seqs[LLSI_TRANS].seq-> + func(chs->seqs[LLSI_TRANS].ph); + + if (chs->seqs[LLSI_NEXT].seq) + chs->seqs[LLSI_NEXT].last = chs->seqs[LLSI_NEXT].seq-> + func(chs->seqs[LLSI_NEXT].ph); + + i = (lws_led_intensity_t)((( + (unsigned int)chs->seqs[LLSI_CURR].last * + (65535 - chs->seqs[LLSI_TRANS].last) >> 16) + + (((unsigned int)chs->seqs[LLSI_NEXT].last * + (unsigned int)chs->seqs[LLSI_TRANS].last) >> 16))); + } else + i = chs->seqs[LLSI_CURR].last; + + return map->intensity_correction ? map->intensity_correction(i) : + cie_antilog((lws_led_intensity_t)i); } void @@ -121,21 +123,27 @@ lws_seq_timer_handle(lws_led_state_t *lcs) for (n = 0; n < lgc->count_leds; n++) { - lws_seq_advance(lcs, &chs->seqs[LWS_LED_SEQ_IDX_CURR]); + lgc->led_ops.intensity(&lgc->led_ops, map->name, + lws_seq_sample(map, chs)); + + lws_seq_advance(lcs, &chs->seqs[LLSI_CURR]); + + if (chs->seqs[LLSI_TRANS].seq) { + lws_seq_advance(lcs, &chs->seqs[LLSI_NEXT]); + lws_seq_advance(lcs, &chs->seqs[LLSI_TRANS]); - if (chs->seqs[LWS_LED_SEQ_IDX_TRANSITION].seq) { - lws_seq_advance(lcs, &chs->seqs[LWS_LED_SEQ_IDX_NEXT]); - lws_seq_advance(lcs, &chs->seqs[LWS_LED_SEQ_IDX_TRANSITION]); - if (!chs->seqs[LWS_LED_SEQ_IDX_TRANSITION].seq) { - chs->seqs[LWS_LED_SEQ_IDX_CURR] = - chs->seqs[LWS_LED_SEQ_IDX_NEXT]; - chs->seqs[LWS_LED_SEQ_IDX_NEXT].seq = NULL; + /* + * When we finished the transition, we can make the + * "next" sequence the current sequence and no need for + * a "next" or a transition any more. + */ + + if (!chs->seqs[LLSI_TRANS].seq) { + chs->seqs[LLSI_CURR] = chs->seqs[LLSI_NEXT]; + chs->seqs[LLSI_NEXT].seq = NULL; } } - lgc->led_ops.intensity(&lgc->led_ops, map->name, - lws_seq_sample(map, chs)); - map++; chs++; } @@ -162,10 +170,10 @@ lws_led_set_chs_seq(struct lws_led_state *lcs, lws_led_state_ch_t *dest, */ steps = def->ms / LWS_LED_SEQUENCER_UPDATE_INTERVAL_MS; - dest->step = (def->ledphase_total != -1 ? + dest->step = (def->ledphase_total != LWS_SEQ_LEDPHASE_TOTAL_ENDLESS ? def->ledphase_total : LWS_LED_FUNC_PHASE) / (steps ? steps : 1); - if (steps && !lcs->timer_refcount++) { + if (!lcs->timer_refcount++) { #if defined(LWS_PLAT_TIMER_START) LWS_PLAT_TIMER_START(lcs->timer); #endif @@ -181,18 +189,12 @@ lws_led_transition(struct lws_led_state *lcs, const char *name, { lws_led_state_chs_t *chs = (lws_led_state_chs_t *)&lcs[1]; int index = lws_led_gpio_lookup(&lcs->controller->led_ops, name); - const lws_led_gpio_map_t *map; if (index < 0) return 1; - map = &lcs->controller->led_map[index]; - - lws_led_set_chs_seq(lcs, &chs[index].seqs[LWS_LED_SEQ_IDX_TRANSITION], trans); - lws_led_set_chs_seq(lcs, &chs[index].seqs[LWS_LED_SEQ_IDX_NEXT], next); - - lcs->controller->led_ops.intensity(&lcs->controller->led_ops, map->name, - lws_seq_sample(map, chs)); + lws_led_set_chs_seq(lcs, &chs[index].seqs[LLSI_TRANS], trans); + lws_led_set_chs_seq(lcs, &chs[index].seqs[LLSI_NEXT], next); return 0; } diff --git a/lib/drivers/led/private-lib-drivers-led.h b/lib/drivers/led/private-lib-drivers-led.h index 43cfd738..deefe099 100644 --- a/lib/drivers/led/private-lib-drivers-led.h +++ b/lib/drivers/led/private-lib-drivers-led.h @@ -32,25 +32,6 @@ typedef struct lws_led_state int timer_refcount; } lws_led_state_t; -enum { - LWS_LED_SEQ_IDX_CURR, - LWS_LED_SEQ_IDX_NEXT, - LWS_LED_SEQ_IDX_TRANSITION -}; - -typedef struct lws_led_state_ch -{ - const lws_led_sequence_def_t *seq; /* NULL = inactive */ - lws_led_seq_phase_t ph; - lws_led_seq_phase_t step; - int phase_budget; -} lws_led_state_ch_t; - -typedef struct lws_led_state_chs -{ - lws_led_state_ch_t seqs[3]; -} lws_led_state_chs_t; - void lws_seq_timer_handle(lws_led_state_t *lcs); diff --git a/lib/drivers/pwm/pwm.c b/lib/drivers/pwm/pwm.c index 20f95e15..b7f578d4 100644 --- a/lib/drivers/pwm/pwm.c +++ b/lib/drivers/pwm/pwm.c @@ -96,7 +96,7 @@ lws_led_func_linear(lws_led_seq_phase_t n) static lws_led_intensity_t lws_led_func_static(lws_led_seq_phase_t n) { - return n ? LWS_LED_MAX_INTENSITY : 0; + return ((int)n * LWS_LED_MAX_INTENSITY) / 2; } const lws_led_sequence_def_t lws_pwmseq_static_off = { @@ -106,13 +106,20 @@ const lws_led_sequence_def_t lws_pwmseq_static_off = { .ms = 0 }; -const lws_led_sequence_def_t lws_pwmseq_static_on = { +const lws_led_sequence_def_t lws_pwmseq_static_half = { .func = lws_led_func_static, .ledphase_offset = 1, .ledphase_total = 0, .ms = 0 }; +const lws_led_sequence_def_t lws_pwmseq_static_on = { + .func = lws_led_func_static, + .ledphase_offset = 2, + .ledphase_total = 0, + .ms = 0 +}; + const lws_led_sequence_def_t lws_pwmseq_sine_up = { .func = lws_led_func_sine, .ledphase_offset = 0, /* already at 0 amp at 0 phase */ diff --git a/lib/drivers/spi/bitbang/lws-bb-spi.c b/lib/drivers/spi/bitbang/lws-bb-spi.c new file mode 100644 index 00000000..285e53ac --- /dev/null +++ b/lib/drivers/spi/bitbang/lws-bb-spi.c @@ -0,0 +1,126 @@ +/* + * SPI bitbang implementation using generic gpio + * + * Copyright (C) 2019 - 2020 Andy Green <andy@warmcat.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#include <libwebsockets.h> + +int +lws_bb_spi_init(const lws_spi_ops_t *octx) +{ + lws_bb_spi_t *ctx = (lws_bb_spi_t *)octx; + int n; + + for (n = 0; n < LWS_SPI_BB_MAX_CH; n++) { + if (ctx->flags & (1 << n)) + ctx->gpio->mode(ctx->ncs[n], LWSGGPIO_FL_WRITE); + if (ctx->flags & (1 << (n + 4))) + ctx->gpio->mode(ctx->ncmd[n], LWSGGPIO_FL_WRITE); + } + + ctx->gpio->mode(ctx->clk, LWSGGPIO_FL_WRITE | + ((octx->bus_mode & LWSSPIMODE_CPOL) ? + 0 : LWSGGPIO_FL_START_LOW)); + ctx->gpio->mode(ctx->mosi, LWSGGPIO_FL_WRITE | LWSGGPIO_FL_START_LOW); + ctx->gpio->mode(ctx->miso, LWSGGPIO_FL_READ | LWSGGPIO_FL_PULLUP); + + return 0; +} + +/* if active, prepare DnC before this and call separately for Cmd / Data */ + +static void +lws_bb_spi_write(lws_bb_spi_t *ctx, const uint8_t *buf, size_t len) +{ + uint8_t u, inv = !!(ctx->bb_ops.bus_mode & LWSSPIMODE_CPOL); + + while (len--) { + int n; + + u = *buf++; + + for (n = 0; n < 4; n++) { + ctx->gpio->set(ctx->clk, inv); + ctx->gpio->set(ctx->mosi, !!(u & 0x80)); + ctx->gpio->set(ctx->clk, !inv); + ctx->gpio->set(ctx->clk, inv); + ctx->gpio->set(ctx->mosi, !!(u & 0x40)); + ctx->gpio->set(ctx->clk, !inv); + u <<= 2; + } + } + + ctx->gpio->set(ctx->clk, 0 ^ inv); +} + +static void +lws_bb_spi_read(lws_bb_spi_t *ctx, uint8_t *buf, size_t len) +{ + uint8_t u, inv = !!(ctx->bb_ops.bus_mode & LWSSPIMODE_CPOL); + + while (len--) { + int n; + + for (n = 0; n < 8; n++) { + ctx->gpio->set(ctx->clk, inv); + u = (u << 1) | !!ctx->gpio->read(ctx->miso); + ctx->gpio->set(ctx->mosi, !!(u & 0x80)); + ctx->gpio->set(ctx->clk, !inv); + } + *buf++ = u; + } + + ctx->gpio->set(ctx->clk, 0 ^ inv); +} + +int +lws_bb_spi_queue(const lws_spi_ops_t *octx, const lws_spi_desc_t *desc) +{ + lws_bb_spi_t *ctx = (lws_bb_spi_t *)octx; + const uint8_t *src = desc->src; + + /* clock to idle */ + ctx->gpio->set(ctx->clk, 0 ^ !!(octx->bus_mode & LWSSPIMODE_CPOL)); + /* enable nCS */ + ctx->gpio->set(ctx->ncs[desc->channel], 0); + + if (desc->count_cmd) { + ctx->gpio->set(ctx->ncmd[desc->channel], 0); + lws_bb_spi_write(ctx, src, desc->count_cmd); + ctx->gpio->set(ctx->ncmd[desc->channel], 1); + + src += desc->count_cmd; + } + + if (desc->count_write) + lws_bb_spi_write(ctx, desc->data, desc->count_write); + + if (desc->count_read) + lws_bb_spi_read(ctx, desc->dest, desc->count_read); + + /* disable nCS */ + ctx->gpio->set(ctx->ncs[desc->channel], 1); + + /* clock to idle */ + ctx->gpio->set(ctx->clk, 0 ^ !!(octx->bus_mode & LWSSPIMODE_CPOL)); + + return 0; +} diff --git a/lib/drivers/spi/lws-spi.c b/lib/drivers/spi/lws-spi.c new file mode 100644 index 00000000..2d41ecf2 --- /dev/null +++ b/lib/drivers/spi/lws-spi.c @@ -0,0 +1,26 @@ +/* + * Generic SPI + * + * Copyright (C) 2019 - 2020 Andy Green <andy@warmcat.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include <libwebsockets.h> + |