aboutsummaryrefslogtreecommitdiff
path: root/lib/drivers
diff options
context:
space:
mode:
authorAndy Green <andy@warmcat.com>2020-06-25 14:44:05 +0100
committerAndy Green <andy@warmcat.com>2020-06-30 19:35:41 +0100
commit63c1e8ba0007d920cec68a88cce83d268f6fec8a (patch)
tree2f900cf0ac9f17cf3da7b5bbf7d38d7d6089af78 /lib/drivers
parent57bfaa9ccb030b4defa0f5c7ddbc1b56291f57cb (diff)
downloadlibwebsockets-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.txt5
-rw-r--r--lib/drivers/button/README.md5
-rw-r--r--lib/drivers/devices/display/ili9341.h95
-rw-r--r--lib/drivers/display/README.md36
-rw-r--r--lib/drivers/display/ili9341-spi.c187
-rw-r--r--lib/drivers/display/lws-display.c124
-rw-r--r--lib/drivers/display/ssd1306-i2c.c17
-rw-r--r--lib/drivers/led/README.md1
-rw-r--r--lib/drivers/led/led-seq.c90
-rw-r--r--lib/drivers/led/private-lib-drivers-led.h19
-rw-r--r--lib/drivers/pwm/pwm.c11
-rw-r--r--lib/drivers/spi/bitbang/lws-bb-spi.c126
-rw-r--r--lib/drivers/spi/lws-spi.c26
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>
+