mirror of
https://github.com/warmcat/libwebsockets.git
synced 2025-03-09 00:00:04 +01:00
lws_pwm
This commit is contained in:
parent
366333adba
commit
33f1003305
12 changed files with 843 additions and 98 deletions
|
@ -616,6 +616,7 @@ struct lws;
|
|||
#include <libwebsockets/lws-ssd1306-i2c.h>
|
||||
#include <libwebsockets/lws-button.h>
|
||||
#include <libwebsockets/lws-led.h>
|
||||
#include <libwebsockets/lws-pwm.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
@ -28,30 +28,39 @@
|
|||
#if !defined(__LWS_LED_H__)
|
||||
#define __LWS_LED_H__
|
||||
|
||||
/* 0 is always OFF, for gpio, anything else is ON */
|
||||
/* only b15 significant for GPIO */
|
||||
typedef uint16_t lws_led_intensity_t;
|
||||
typedef uint16_t lws_led_seq_phase_t;
|
||||
#define LWS_LED_MAX_INTENSITY (0xffff)
|
||||
#define LWS_LED_FRAME_RATE 20
|
||||
#define LWS_LED_FUNC_PHASE 1024
|
||||
|
||||
/* the normalized max intensity */
|
||||
#define LWS_LED_MAX_INTENSITY (0xffff)
|
||||
|
||||
/* the normalized 360 degree phase count for intensity functions */
|
||||
#define LWS_LED_FUNC_PHASE 65536
|
||||
/* used when the sequence doesn't stop by itself and goes around forever */
|
||||
#define LWS_SEQ_LEDPHASE_TOTAL_ENDLESS (-1)
|
||||
|
||||
#define LWS_LED_SEQUENCER_UPDATE_INTERVAL_MS 33
|
||||
|
||||
struct lws_led_state; /* opaque */
|
||||
struct lws_pwm_ops; /* forward ref */
|
||||
|
||||
typedef lws_led_intensity_t (*lws_led_lookup_t)(int idx);
|
||||
typedef lws_led_intensity_t (*lws_led_lookup_t)(lws_led_seq_phase_t ph);
|
||||
|
||||
typedef struct lws_led_sequence_def_t {
|
||||
lws_led_lookup_t func;
|
||||
lws_led_seq_phase_t ledphase_offset;
|
||||
int ledphase_total;
|
||||
int ms_full_phase; /* to compute rate */
|
||||
lws_led_lookup_t func;
|
||||
lws_led_seq_phase_t ledphase_offset;
|
||||
int ledphase_total; /* 65536= one cycle */
|
||||
uint16_t ms;
|
||||
uint8_t flags;
|
||||
} lws_led_sequence_def_t;
|
||||
|
||||
/* this should always be first in the subclassed implementation types */
|
||||
|
||||
typedef struct lws_led_ops {
|
||||
void (*intensity)(const struct lws_led_ops *lo, int index,
|
||||
void (*intensity)(const struct lws_led_ops *lo, const char *name,
|
||||
lws_led_intensity_t inten);
|
||||
int (*lookup)(const struct lws_led_ops *lo, const char *name);
|
||||
/**< for BOOL led control like GPIO, only inten b15 is significant */
|
||||
struct lws_led_state * (*create)(const struct lws_led_ops *led_ops);
|
||||
void (*destroy)(struct lws_led_state *);
|
||||
} lws_led_ops_t;
|
||||
|
@ -59,6 +68,15 @@ typedef struct lws_led_ops {
|
|||
typedef struct lws_led_gpio_map {
|
||||
const char *name;
|
||||
_lws_plat_gpio_t gpio;
|
||||
lws_led_lookup_t intensity_correction;
|
||||
/**< May be NULL. If GPIO-based LED, ignored. If pwm_ops provided,
|
||||
* NULL means use default CIE 100% correction function. If non-NULL,
|
||||
* use the pointed-to correction function. This is useful to provide
|
||||
* LED-specific intensity correction / scaling so different types of
|
||||
* LED can "look the same". */
|
||||
const struct lws_pwm_ops *pwm_ops;
|
||||
/**< if NULL, gpio controls the led directly. If set to a pwm_ops,
|
||||
* the led control is outsourced to the pwm controller. */
|
||||
uint8_t active_level;
|
||||
} lws_led_gpio_map_t;
|
||||
|
||||
|
@ -78,19 +96,32 @@ lws_led_gpio_create(const lws_led_ops_t *led_ops);
|
|||
LWS_VISIBLE LWS_EXTERN void
|
||||
lws_led_gpio_destroy(struct lws_led_state *lcs);
|
||||
|
||||
/**
|
||||
* lws_led_gpio_intensity() - set the static intensity of an led
|
||||
*
|
||||
* \param lo: the base class of the led controller
|
||||
* \param index: which led in the controller set
|
||||
* \param inten: 16-bit unsigned intensity
|
||||
*
|
||||
* For LEDs controlled by a BOOL like GPIO, only inten b15 is significant.
|
||||
* For PWM type LED control, as many bits as the hardware can support from b15
|
||||
* down are significant.
|
||||
*/
|
||||
LWS_VISIBLE LWS_EXTERN void
|
||||
lws_led_gpio_intensity(const struct lws_led_ops *lo, int index, lws_led_intensity_t inten);
|
||||
lws_led_gpio_intensity(const struct lws_led_ops *lo, const char *name,
|
||||
lws_led_intensity_t inten);
|
||||
|
||||
LWS_VISIBLE LWS_EXTERN int
|
||||
lws_led_gpio_lookup(const struct lws_led_ops *lo, const char *name);
|
||||
lws_led_transition(struct lws_led_state *lcs, const char *name,
|
||||
const lws_led_sequence_def_t *next,
|
||||
const lws_led_sequence_def_t *trans);
|
||||
|
||||
|
||||
#define lws_led_gpio_ops \
|
||||
{ \
|
||||
.create = lws_led_gpio_create, \
|
||||
.destroy = lws_led_gpio_destroy, \
|
||||
.intensity = lws_led_gpio_intensity, \
|
||||
.lookup = lws_led_gpio_lookup, \
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
|
64
include/libwebsockets/lws-pwm.h
Normal file
64
include/libwebsockets/lws-pwm.h
Normal file
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* Generic PWM controller ops
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
typedef struct lws_pwm_map {
|
||||
_lws_plat_gpio_t gpio;
|
||||
uint8_t index;
|
||||
} lws_pwm_map_t;
|
||||
|
||||
typedef struct lws_pwm_ops {
|
||||
int (*init)(const struct lws_pwm_ops *lo);
|
||||
void (*intensity)(const struct lws_pwm_ops *lo, _lws_plat_gpio_t gpio,
|
||||
lws_led_intensity_t inten);
|
||||
const lws_pwm_map_t *pwm_map;
|
||||
uint8_t count_pwm_map;
|
||||
} lws_pwm_ops_t;
|
||||
|
||||
LWS_VISIBLE LWS_EXTERN int
|
||||
lws_pwm_plat_init(const struct lws_pwm_ops *lo);
|
||||
|
||||
LWS_VISIBLE LWS_EXTERN void
|
||||
lws_pwm_plat_intensity(const struct lws_pwm_ops *lo, _lws_plat_gpio_t gpio,
|
||||
lws_led_intensity_t inten);
|
||||
|
||||
#define lws_pwm_plat_ops \
|
||||
.init = lws_pwm_plat_init, \
|
||||
.intensity = lws_pwm_plat_intensity
|
||||
|
||||
/*
|
||||
* May be useful for making your own transitions or sequences
|
||||
*/
|
||||
|
||||
LWS_VISIBLE LWS_EXTERN lws_led_intensity_t
|
||||
lws_led_func_linear(lws_led_seq_phase_t n);
|
||||
LWS_VISIBLE LWS_EXTERN lws_led_intensity_t
|
||||
lws_led_func_sine(lws_led_seq_phase_t n);
|
||||
|
||||
/* canned sequences that can work out of the box */
|
||||
|
||||
extern const lws_led_sequence_def_t lws_pwmseq_sine_endless_slow,
|
||||
lws_pwmseq_sine_endless_fast,
|
||||
lws_pwmseq_linear_wipe,
|
||||
lws_pwmseq_sine_up, lws_pwmseq_sine_down,
|
||||
lws_pwmseq_static_on, lws_pwmseq_static_off;
|
|
@ -5,11 +5,15 @@ list(APPEND SOURCES
|
|||
drivers/i2c/bitbang/lws-bb-i2c.c
|
||||
drivers/button/lws-button.c
|
||||
drivers/led/led-gpio.c
|
||||
drivers/led/led-seq.c
|
||||
drivers/pwm/pwm.c
|
||||
)
|
||||
|
||||
if (LWS_ESP_PLATFORM)
|
||||
list(APPEND SOURCES
|
||||
plat/freertos/esp32/drivers/gpio-esp32.c)
|
||||
plat/freertos/esp32/drivers/gpio-esp32.c
|
||||
plat/freertos/esp32/drivers/pwm-esp32.c
|
||||
)
|
||||
endif()
|
||||
|
||||
exports_to_parent_scope()
|
||||
|
|
154
lib/drivers/led/README.md
Normal file
154
lib/drivers/led/README.md
Normal file
|
@ -0,0 +1,154 @@
|
|||
# lws_led gpio and pwm class drivers
|
||||
|
||||
Lws provides an abstract led controller class that can bind an array of LEDs
|
||||
to gpio and pwm controllers, and automatically handled pwm sequencers.
|
||||
|
||||
Lumience intensity is corrected for IEC curves to match perceptual intensity,
|
||||
and the correction can be overridden per led for curve adaptation matching.
|
||||
|
||||
Intensity is normalized to a 16-bit scale, when controlled by a GPIO b15 is
|
||||
significant and the rest ignored. When controlled by PWM, as many bits from
|
||||
b15 down are significant as the PWM arrangements can represent.
|
||||
|
||||
The PWM sequencers use arbitrary function generation callbacks on a normalized
|
||||
16-bit phase space, they can choose how much to interpolate and how much to put
|
||||
in a table, a 64-sample, 16-bit sine function is provided along with 16-bit
|
||||
linear sawtooth.
|
||||
|
||||
Changing the sequencer is subject to a third transition function sequencer, this
|
||||
can for example mix the transition linearly over, eg, 500ms so the leds look
|
||||
very smooth.
|
||||
|
||||
## Defining an led controller
|
||||
|
||||
An array of inidividual LED information is provided first, and referenced by
|
||||
the LED controller definintion. Leds are named so code does not introduce
|
||||
dependencies on specific implementations.
|
||||
|
||||
```
|
||||
static const lws_led_gpio_map_t lgm[] = {
|
||||
{
|
||||
.name = "alert",
|
||||
.gpio = GPIO_NUM_25,
|
||||
.pwm_ops = &pwm_ops,
|
||||
.active_level = 1,
|
||||
},
|
||||
};
|
||||
|
||||
static const lws_led_gpio_controller_t lgc = {
|
||||
.led_ops = lws_led_gpio_ops,
|
||||
.gpio_ops = &lws_gpio_plat,
|
||||
.led_map = &lgm[0],
|
||||
.count_leds = LWS_ARRAY_SIZE(lgm)
|
||||
};
|
||||
|
||||
struct lws_led_state *lls;
|
||||
|
||||
lls = lgc.led_ops.create(&lgc.led_ops);
|
||||
if (!lls) {
|
||||
lwsl_err("%s: could not create led\n", __func__);
|
||||
goto spin;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
For GPIO control, the active level of the GPIO to light the LED may be set.
|
||||
|
||||
Each LED may bind to a pwm controller, in which case setting the intensity
|
||||
programs the pwm controller corresponding to the GPIO.
|
||||
|
||||
## Setting the intensity directly
|
||||
|
||||
```
|
||||
lgc.led_ops.intensity(&lgc.led_ops, "alert", 0);
|
||||
```
|
||||
|
||||
## Defining Sequencer
|
||||
|
||||
Some common sequencers are provided out of the box, you can also define your
|
||||
own arbitrary ones.
|
||||
|
||||
The main point is sequencers have a function that returns an intensity for each
|
||||
of 65536 phase steps in its cycle. For example, this is the linear function
|
||||
that is included
|
||||
|
||||
```
|
||||
lws_led_intensity_t
|
||||
lws_led_func_linear(lws_led_seq_phase_t n)
|
||||
{
|
||||
return (lws_led_intensity_t)n;
|
||||
}
|
||||
```
|
||||
|
||||
It simply returns an intensity between 0 - 65535 matching the phase angle of
|
||||
0 - 65535 that it was given, so it's a sawtooth ramp.
|
||||
|
||||
An interpolated sine function is also provided that returns an intensity
|
||||
between 0 - 65535 reflecting one cycle of sine wave for the phase angle of 0 -
|
||||
65535.
|
||||
|
||||
These functions are packaged into sequencer structures like this
|
||||
|
||||
```
|
||||
const lws_led_sequence_def_t lws_pwmseq_sine_endless_fast = {
|
||||
.func = lws_led_func_sine,
|
||||
.ledphase_offset = 0, /* already at 0 amp at 0 phase */
|
||||
.ledphase_total = LWS_SEQ_LEDPHASE_TOTAL_ENDLESS,
|
||||
.ms = 750
|
||||
};
|
||||
```
|
||||
|
||||
This "endless" sequencer cycles through the sine function at 750ms per cycle.
|
||||
Non-endless sequencers have a specific start and end in the phase space, eg
|
||||
|
||||
```
|
||||
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 */
|
||||
.ledphase_total = LWS_LED_FUNC_PHASE / 2, /* 180 degree ./^ */
|
||||
.ms = 300
|
||||
};
|
||||
```
|
||||
|
||||
... this one traverses 180 degrees of the sine wave starting from 0 and ending
|
||||
at full intensity, over 300ms.
|
||||
|
||||
A commonly-used, provided one is like this, as used in the next section
|
||||
|
||||
```
|
||||
const lws_led_sequence_def_t lws_pwmseq_linear_wipe = {
|
||||
.func = lws_led_func_linear,
|
||||
.ledphase_offset = 0,
|
||||
.ledphase_total = LWS_LED_FUNC_PHASE - 1,
|
||||
.ms = 300
|
||||
};
|
||||
```
|
||||
|
||||
## Setting the intensity using sequencer transitions
|
||||
|
||||
The main api for high level sequenced control is
|
||||
|
||||
```
|
||||
int
|
||||
lws_led_transition(struct lws_led_state *lcs, const char *name,
|
||||
const lws_led_sequence_def_t *next,
|
||||
const lws_led_sequence_def_t *trans);
|
||||
```
|
||||
|
||||
This fades from the current sequence to a new sequence, using `trans` sequencer
|
||||
intensity as the mix factor. `trans` is typically `lws_pwmseq_linear_wipe`,
|
||||
fading between the current and new linearly over 300ms. At the end of the
|
||||
`trans` sequence, the new sequence simply replaces the current one and the
|
||||
transition is completed.
|
||||
|
||||
Sequencers use a single 30Hz OS timer while any sequence is active.
|
||||
|
||||
exported sequencer symbol|description
|
||||
---|---
|
||||
lws_pwmseq_sine_endless_slow|continuous 100% sine, 1.5s cycle
|
||||
lws_pwmseq_sine_endless_fast|continuous 100% sine, 0.75s cycle
|
||||
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_off|0% static
|
|
@ -22,28 +22,30 @@
|
|||
* IN THE SOFTWARE.
|
||||
*/
|
||||
#include "private-lib-core.h"
|
||||
#include "drivers/led/private-lib-drivers-led.h"
|
||||
|
||||
typedef struct lws_led_state
|
||||
#if defined(LWS_PLAT_TIMER_CB)
|
||||
static LWS_PLAT_TIMER_CB(lws_led_timer_cb, th)
|
||||
{
|
||||
#if defined(LWS_PLAT_FREERTOS)
|
||||
TimerHandle_t timer;
|
||||
#endif
|
||||
lws_led_gpio_controller_t *controller;
|
||||
} lws_led_state_t;
|
||||
lws_led_state_t *lcs = LWS_PLAT_TIMER_CB_GET_OPAQUE(th);
|
||||
|
||||
#if defined(LWS_PLAT_FREERTOS)
|
||||
static void
|
||||
lws_led_timer_cb(TimerHandle_t th)
|
||||
{
|
||||
// lws_led_state_t *lcs = pvTimerGetTimerID(th);
|
||||
lws_seq_timer_handle(lcs);
|
||||
}
|
||||
#endif
|
||||
|
||||
struct lws_led_state *
|
||||
lws_led_gpio_create(const lws_led_ops_t *led_ops)
|
||||
{
|
||||
lws_led_state_t *lcs = lws_zalloc(sizeof(lws_led_state_t), __func__);
|
||||
lws_led_gpio_controller_t *lgc = (lws_led_gpio_controller_t *)led_ops;
|
||||
/*
|
||||
* We allocate the main state object, and a 3 x seq dynamic footprint
|
||||
* for each led, since it may be sequencing the transition between two
|
||||
* other sequences.
|
||||
*/
|
||||
|
||||
lws_led_state_t *lcs = lws_zalloc(sizeof(lws_led_state_t) +
|
||||
(lgc->count_leds * sizeof(lws_led_state_chs_t)),
|
||||
__func__);
|
||||
int n;
|
||||
|
||||
if (!lcs)
|
||||
|
@ -51,15 +53,25 @@ lws_led_gpio_create(const lws_led_ops_t *led_ops)
|
|||
|
||||
lcs->controller = lgc;
|
||||
|
||||
#if defined(LWS_PLAT_FREERTOS)
|
||||
lcs->timer = xTimerCreate("leds", 1, 0, lcs,
|
||||
#if defined(LWS_PLAT_TIMER_CREATE)
|
||||
lcs->timer = LWS_PLAT_TIMER_CREATE("leds",
|
||||
LWS_LED_SEQUENCER_UPDATE_INTERVAL_MS, 1, lcs,
|
||||
(TimerCallbackFunction_t)lws_led_timer_cb);
|
||||
if (!lcs->timer)
|
||||
return NULL;
|
||||
#endif
|
||||
|
||||
for (n = 0; n < lgc->count_leds; n++) {
|
||||
lgc->gpio_ops->mode(lgc->led_map[n].gpio, LWSGGPIO_FL_WRITE);
|
||||
lgc->gpio_ops->set(lgc->led_map[n].gpio,
|
||||
!lgc->led_map[n].active_level);
|
||||
const lws_led_gpio_map_t *map = &lgc->led_map[n];
|
||||
|
||||
if (map->pwm_ops) {
|
||||
lgc->gpio_ops->mode(map->gpio, LWSGGPIO_FL_READ);
|
||||
lgc->gpio_ops->set(map->gpio, 0);
|
||||
} else {
|
||||
lgc->gpio_ops->mode(map->gpio, LWSGGPIO_FL_WRITE);
|
||||
lgc->gpio_ops->set(map->gpio,
|
||||
!lgc->led_map[n].active_level);
|
||||
}
|
||||
}
|
||||
|
||||
return lcs;
|
||||
|
@ -68,25 +80,12 @@ lws_led_gpio_create(const lws_led_ops_t *led_ops)
|
|||
void
|
||||
lws_led_gpio_destroy(struct lws_led_state *lcs)
|
||||
{
|
||||
#if defined(LWS_PLAT_FREERTOS)
|
||||
xTimerDelete(&lcs->timer, 0);
|
||||
#if defined(LWS_PLAT_TIMER_DELETE)
|
||||
LWS_PLAT_TIMER_DELETE(&lcs->timer);
|
||||
#endif
|
||||
lws_free(lcs);
|
||||
}
|
||||
|
||||
void
|
||||
lws_led_gpio_intensity(const struct lws_led_ops *lo, int idx, lws_led_intensity_t inten)
|
||||
{
|
||||
const lws_led_gpio_controller_t *lgc = (lws_led_gpio_controller_t *)lo;
|
||||
const lws_led_gpio_map_t *map = &lgc->led_map[idx];
|
||||
|
||||
lgc->gpio_ops->set(map->gpio, (!!map->active_level) ^ !inten);
|
||||
|
||||
// ledc_set_duty(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_0, inten);
|
||||
// ledc_update_duty(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_0);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
lws_led_gpio_lookup(const struct lws_led_ops *lo, const char *name)
|
||||
{
|
||||
|
@ -100,51 +99,22 @@ lws_led_gpio_lookup(const struct lws_led_ops *lo, const char *name)
|
|||
return -1;
|
||||
}
|
||||
|
||||
static const lws_led_intensity_t sineq16[] = {
|
||||
0x0000, 0x0191, 0x031e, 0x04a4, 0x061e, 0x0789, 0x08e2, 0x0a24,
|
||||
0x0b4e, 0x0c5c, 0x0d4b, 0x0e1a, 0x0ec6, 0x0f4d, 0x0faf, 0x0fea,
|
||||
};
|
||||
|
||||
static lws_led_intensity_t sine_lu(int n)
|
||||
void
|
||||
lws_led_gpio_intensity(const struct lws_led_ops *lo, const char *name,
|
||||
lws_led_intensity_t inten)
|
||||
{
|
||||
switch ((n >> 4) & 3) {
|
||||
case 1:
|
||||
return 4096 + sineq16[n & 15];
|
||||
case 2:
|
||||
return 4096 + sineq16[15 - (n & 15)];
|
||||
case 3:
|
||||
return 4096 - sineq16[n & 15];
|
||||
default:
|
||||
return 4096 - sineq16[15 - (n & 15)];
|
||||
}
|
||||
const lws_led_gpio_controller_t *lgc = (lws_led_gpio_controller_t *)lo;
|
||||
int idx = lws_led_gpio_lookup(lo, name);
|
||||
const lws_led_gpio_map_t *map;
|
||||
|
||||
if (idx < 0)
|
||||
return;
|
||||
|
||||
map = &lgc->led_map[idx];
|
||||
|
||||
if (map->pwm_ops)
|
||||
map->pwm_ops->intensity(map->pwm_ops, map->gpio, inten);
|
||||
else
|
||||
lgc->gpio_ops->set(map->gpio,
|
||||
(!!map->active_level) ^ !(inten & 0x8000));
|
||||
}
|
||||
|
||||
/* useful for sine led fade patterns */
|
||||
|
||||
lws_led_intensity_t lws_led_func_sine(int n)
|
||||
{
|
||||
/*
|
||||
* 2: quadrant
|
||||
* 4: table entry in quadrant
|
||||
* 4: interp (LSB)
|
||||
*
|
||||
* total 10 bits / 1024 steps per cycle
|
||||
*
|
||||
* + 0: 0
|
||||
* + 256: 4096
|
||||
* + 512: 8192
|
||||
* + 768: 4096
|
||||
* +1023: 0
|
||||
*/
|
||||
|
||||
return (sine_lu(n >> 4) * (15 - (n & 15)) +
|
||||
sine_lu((n >> 4) + 1) * (n & 15)) / 15;
|
||||
}
|
||||
|
||||
const lws_led_sequence_def_t lws_ledseq_sine_wipe = {
|
||||
.func = lws_led_func_sine,
|
||||
.ledphase_offset = 0, /* already at 0 amp at 0 phase */
|
||||
.ledphase_total = 512, /* 180 degree phase ./^ */
|
||||
.ms_full_phase = 1000, /* ie, 500ms for 180 degree */
|
||||
};
|
||||
|
||||
|
|
198
lib/drivers/led/led-seq.c
Normal file
198
lib/drivers/led/led-seq.c
Normal file
|
@ -0,0 +1,198 @@
|
|||
/*
|
||||
* Generic GPIO led
|
||||
*
|
||||
* 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/led/private-lib-drivers-led.h"
|
||||
|
||||
/*
|
||||
* 64 entry interpolated CIE correction
|
||||
* https://en.wikipedia.org/wiki/Lightness
|
||||
*/
|
||||
|
||||
uint16_t cie[] = {
|
||||
0, 113, 227, 340, 454, 568, 688, 824, 976, 1146,
|
||||
1335, 1543, 1772, 2023, 2296, 2592, 2914, 3260, 3633, 4034,
|
||||
4463, 4921, 5409, 5929, 6482, 7067, 7687, 8341, 9032, 9761,
|
||||
10527, 11332, 12178, 13064, 13993, 14964, 15980, 17040, 18146, 19299,
|
||||
20500, 21750, 23049, 24400, 25802, 27256, 28765, 30328, 31946, 33622,
|
||||
35354, 37146, 38996, 40908, 42881, 44916, 47014, 49177, 51406, 53700,
|
||||
56062, 58492, 60992, 63561,
|
||||
65535 /* for interpolation */
|
||||
};
|
||||
|
||||
/*
|
||||
* This is the default intensity correction function, it can be overridden
|
||||
* per-led to eg, normalize intensity of different leds
|
||||
*/
|
||||
|
||||
static lws_led_intensity_t
|
||||
cie_antilog(lws_led_intensity_t lin)
|
||||
{
|
||||
return (cie[lin >> 10] * (0x3ff - (lin & 0x3ff)) +
|
||||
cie[(lin >> 10) + 1] * (lin & 0x3ff)) / 0x3ff;
|
||||
}
|
||||
|
||||
static void
|
||||
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) {
|
||||
|
||||
/* we are done */
|
||||
|
||||
ch->seq = NULL;
|
||||
if (!(--lcs->timer_refcount)) {
|
||||
#if defined(LWS_PLAT_TIMER_STOP)
|
||||
LWS_PLAT_TIMER_STOP(lcs->timer);
|
||||
#endif
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
ch->ph += ch->step;
|
||||
if (ch->phase_budget != -1)
|
||||
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;
|
||||
|
||||
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[LWS_LED_SEQ_IDX_TRANSITION].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);
|
||||
}
|
||||
|
||||
void
|
||||
lws_seq_timer_handle(lws_led_state_t *lcs)
|
||||
{
|
||||
lws_led_gpio_controller_t *lgc = lcs->controller;
|
||||
lws_led_state_chs_t *chs = (lws_led_state_chs_t *)&lcs[1];
|
||||
const lws_led_gpio_map_t *map = &lgc->led_map[0];
|
||||
unsigned int n;
|
||||
|
||||
for (n = 0; n < lgc->count_leds; n++) {
|
||||
|
||||
lws_seq_advance(lcs, &chs->seqs[LWS_LED_SEQ_IDX_CURR]);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
lgc->led_ops.intensity(&lgc->led_ops, map->name,
|
||||
lws_seq_sample(map, chs));
|
||||
|
||||
map++;
|
||||
chs++;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
lws_led_set_chs_seq(struct lws_led_state *lcs, lws_led_state_ch_t *dest,
|
||||
const lws_led_sequence_def_t *def)
|
||||
{
|
||||
int steps;
|
||||
|
||||
dest->seq = def;
|
||||
dest->ph = def->ledphase_offset;
|
||||
dest->phase_budget = def->ledphase_total;
|
||||
|
||||
/*
|
||||
* We need to compute the incremental phase angle step to cover the
|
||||
* total number of phases in the indicated ms, incrementing at the
|
||||
* timer rate of LWS_LED_SEQUENCER_UPDATE_RATE_HZ. Eg,
|
||||
*
|
||||
* 65536 phase steps (one cycle) in 2000ms at 30Hz timer rate means we
|
||||
* will update 2000ms / 33ms = 60 times, so we must step at at
|
||||
* 65536 / 60 = 1092 phase angle resolution
|
||||
*/
|
||||
|
||||
steps = def->ms / LWS_LED_SEQUENCER_UPDATE_INTERVAL_MS;
|
||||
dest->step = (def->ledphase_total != -1 ?
|
||||
def->ledphase_total : LWS_LED_FUNC_PHASE) / (steps ? steps : 1);
|
||||
|
||||
if (steps && !lcs->timer_refcount++) {
|
||||
#if defined(LWS_PLAT_TIMER_START)
|
||||
LWS_PLAT_TIMER_START(lcs->timer);
|
||||
#endif
|
||||
}
|
||||
|
||||
return steps;
|
||||
}
|
||||
|
||||
int
|
||||
lws_led_transition(struct lws_led_state *lcs, const char *name,
|
||||
const lws_led_sequence_def_t *next,
|
||||
const lws_led_sequence_def_t *trans)
|
||||
{
|
||||
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));
|
||||
|
||||
return 0;
|
||||
}
|
58
lib/drivers/led/private-lib-drivers-led.h
Normal file
58
lib/drivers/led/private-lib-drivers-led.h
Normal file
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* Generic GPIO led
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
typedef struct lws_led_state
|
||||
{
|
||||
#if defined(LWS_PLAT_TIMER_TYPE)
|
||||
LWS_PLAT_TIMER_TYPE timer;
|
||||
#endif
|
||||
|
||||
lws_led_gpio_controller_t *controller;
|
||||
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);
|
||||
|
||||
int
|
||||
lws_led_gpio_lookup(const struct lws_led_ops *lo, const char *name);
|
149
lib/drivers/pwm/pwm.c
Normal file
149
lib/drivers/pwm/pwm.c
Normal file
|
@ -0,0 +1,149 @@
|
|||
/*
|
||||
* Generic GPIO led
|
||||
*
|
||||
* 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"
|
||||
|
||||
static const lws_led_intensity_t sineq16[] = {
|
||||
|
||||
/*
|
||||
* Quadrant at sin(270) in 16 samples, normalized so
|
||||
* -1 == 0 and 0 == 32767
|
||||
*/
|
||||
|
||||
0, 158, 630, 1411, 2494, 3869, 5522, 7437,
|
||||
9597, 11980, 14562, 17321, 20228, 23225, 26374, 29555,
|
||||
32767 /* to interpolate against */
|
||||
};
|
||||
|
||||
/*
|
||||
* Elaborate the 90 degree phase table to 360 degrees and offset to +32768,
|
||||
* notice for the last sample we have to interpolate against a 17th sample
|
||||
* reflecting full scale to avoid clipping due to interpolation against the
|
||||
* 16th sample again
|
||||
*/
|
||||
|
||||
static lws_led_intensity_t
|
||||
sine_lu(int n, int next)
|
||||
{
|
||||
switch ((n >> 4) & 3) {
|
||||
case 1:
|
||||
/* forwards */
|
||||
return 32768 + sineq16[(n & 15) + next];
|
||||
case 2:
|
||||
/* scan it backwards */
|
||||
return 32768 + sineq16[15 - (n & 15) + (!next)];
|
||||
case 3:
|
||||
/* forwards */
|
||||
return 32768 - sineq16[(n & 15) + next];
|
||||
default:
|
||||
/* scan it backwards */
|
||||
return 32768 - sineq16[15 - (n & 15) + (!next)];
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* The normalized phase resolution is 16-bit, however much table you decide to
|
||||
* have needs interpolating or indexing in a reduced number of significant
|
||||
* phase bits if it doesn't have the same phase resolution.
|
||||
*
|
||||
* In this sine table we have a 16 x 15-bit sample quadrant reflected 4 times
|
||||
* to make 360 degrees, so 64 accurate sample points, with the rest of the
|
||||
* intermediate phases generated by linear interpolation. That probably would
|
||||
* sound a bit funky, but for modulating light dynamically it's more than
|
||||
* enough.
|
||||
*/
|
||||
|
||||
lws_led_intensity_t
|
||||
lws_led_func_sine(lws_led_seq_phase_t n)
|
||||
{
|
||||
/*
|
||||
* 2: quadrant
|
||||
* 4: table entry in quadrant
|
||||
* 10: interp (LSB)
|
||||
*/
|
||||
|
||||
return (sine_lu(n >> 10, 0) * (0x3ff - (n & 0x3ff)) +
|
||||
sine_lu(n >> 10, 1) * (n & 0x3ff)) / 0x3ff;
|
||||
}
|
||||
|
||||
lws_led_intensity_t
|
||||
lws_led_func_linear(lws_led_seq_phase_t n)
|
||||
{
|
||||
return (lws_led_intensity_t)n;
|
||||
}
|
||||
|
||||
|
||||
static lws_led_intensity_t
|
||||
lws_led_func_static(lws_led_seq_phase_t n)
|
||||
{
|
||||
return n ? LWS_LED_MAX_INTENSITY : 0;
|
||||
}
|
||||
|
||||
const lws_led_sequence_def_t lws_pwmseq_static_off = {
|
||||
.func = lws_led_func_static,
|
||||
.ledphase_offset = 0,
|
||||
.ledphase_total = 0,
|
||||
.ms = 0
|
||||
};
|
||||
|
||||
const lws_led_sequence_def_t lws_pwmseq_static_on = {
|
||||
.func = lws_led_func_static,
|
||||
.ledphase_offset = 1,
|
||||
.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 */
|
||||
.ledphase_total = LWS_LED_FUNC_PHASE / 2, /* 180 degree ./^ */
|
||||
.ms = 300
|
||||
};
|
||||
|
||||
const lws_led_sequence_def_t lws_pwmseq_sine_down = {
|
||||
.func = lws_led_func_sine,
|
||||
.ledphase_offset = LWS_LED_FUNC_PHASE / 2, /* start at peak */
|
||||
.ledphase_total = LWS_LED_FUNC_PHASE / 2, /* 180 degree ./^ */
|
||||
.ms = 300
|
||||
};
|
||||
|
||||
const lws_led_sequence_def_t lws_pwmseq_linear_wipe = {
|
||||
.func = lws_led_func_linear,
|
||||
.ledphase_offset = 0,
|
||||
.ledphase_total = LWS_LED_FUNC_PHASE - 1,
|
||||
.ms = 300
|
||||
};
|
||||
|
||||
const lws_led_sequence_def_t lws_pwmseq_sine_endless_slow = {
|
||||
.func = lws_led_func_sine,
|
||||
.ledphase_offset = 0, /* already at 0 amp at 0 phase */
|
||||
.ledphase_total = LWS_SEQ_LEDPHASE_TOTAL_ENDLESS,
|
||||
.ms = 1500
|
||||
};
|
||||
|
||||
const lws_led_sequence_def_t lws_pwmseq_sine_endless_fast = {
|
||||
.func = lws_led_func_sine,
|
||||
.ledphase_offset = 0, /* already at 0 amp at 0 phase */
|
||||
.ledphase_total = LWS_SEQ_LEDPHASE_TOTAL_ENDLESS,
|
||||
.ms = 750
|
||||
};
|
78
lib/plat/freertos/esp32/drivers/pwm-esp32.c
Normal file
78
lib/plat/freertos/esp32/drivers/pwm-esp32.c
Normal file
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* esp32 / esp-idf pwm
|
||||
*
|
||||
* 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 "soc/ledc_reg.h"
|
||||
#include "driver/ledc.h"
|
||||
|
||||
static const ledc_timer_config_t tc = {
|
||||
.speed_mode = LEDC_HIGH_SPEED_MODE,
|
||||
.duty_resolution = LEDC_TIMER_13_BIT,
|
||||
.timer_num = LEDC_TIMER_0,
|
||||
.freq_hz = 5000,
|
||||
.clk_cfg = LEDC_AUTO_CLK
|
||||
};
|
||||
|
||||
int
|
||||
lws_pwm_plat_init(const struct lws_pwm_ops *lo)
|
||||
{
|
||||
ledc_channel_config_t lc = {
|
||||
.duty = 8191,
|
||||
.intr_type = LEDC_INTR_FADE_END,
|
||||
.speed_mode = LEDC_HIGH_SPEED_MODE,
|
||||
.timer_sel = LEDC_TIMER_0,
|
||||
};
|
||||
size_t n;
|
||||
|
||||
ledc_timer_config(&tc);
|
||||
|
||||
for (n = 0; n < lo->count_pwm_map; n++) {
|
||||
lc.channel = LEDC_CHANNEL_0 + lo->pwm_map[n].index;
|
||||
lc.gpio_num = lo->pwm_map[n].gpio;
|
||||
ledc_channel_config(&lc);
|
||||
ledc_set_duty(LEDC_HIGH_SPEED_MODE, lc.channel, 0);
|
||||
ledc_update_duty(LEDC_HIGH_SPEED_MODE, lc.channel);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
lws_pwm_plat_intensity(const struct lws_pwm_ops *lo, _lws_plat_gpio_t gpio,
|
||||
lws_led_intensity_t inten)
|
||||
{
|
||||
size_t n;
|
||||
|
||||
for (n = 0; n < lo->count_pwm_map; n++) {
|
||||
if (lo->pwm_map[n].gpio == gpio) {
|
||||
ledc_set_duty(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_0 +
|
||||
lo->pwm_map[n].index, inten >> 3);
|
||||
ledc_update_duty(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_0 +
|
||||
lo->pwm_map[n].index);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
lwsl_err("%s: unknown gpio for pwm\n", __func__);
|
||||
}
|
|
@ -118,3 +118,14 @@ insert_wsi(const struct lws_context *context, struct lws *wsi);
|
|||
|
||||
#define delete_from_fd(A,B) A->lws_lookup[B - lws_plat_socket_offset()] = 0
|
||||
|
||||
#define LWS_PLAT_TIMER_TYPE TimerHandle_t
|
||||
#define LWS_PLAT_TIMER_CB(name, var) void name(TimerHandle_t var)
|
||||
#define LWS_PLAT_TIMER_CB_GET_OPAQUE(x) pvTimerGetTimerID(x)
|
||||
#define LWS_PLAT_TIMER_CREATE(name, interval, repeat, opaque, cb) \
|
||||
xTimerCreate(name, pdMS_TO_TICKS(interval), repeat ? pdTRUE : 0, \
|
||||
opaque, cb)
|
||||
#define LWS_PLAT_TIMER_DELETE(ptr) xTimerDelete(ptr, 0)
|
||||
#define LWS_PLAT_TIMER_START(ptr) xTimerStart(ptr, 0)
|
||||
#define LWS_PLAT_TIMER_STOP(ptr) xTimerStop(ptr, 0)
|
||||
|
||||
|
||||
|
|
|
@ -27,8 +27,8 @@
|
|||
|
||||
struct lws_context *context;
|
||||
lws_sorted_usec_list_t sul;
|
||||
lws_display_state_t lds;
|
||||
struct lws_led_state *lls;
|
||||
lws_display_state_t lds;
|
||||
int interrupted;
|
||||
|
||||
/*
|
||||
|
@ -82,6 +82,20 @@ static const lws_button_controller_t bc = {
|
|||
.count_buttons = LWS_ARRAY_SIZE(bcm)
|
||||
};
|
||||
|
||||
/*
|
||||
* pwm controller
|
||||
*/
|
||||
|
||||
static const lws_pwm_map_t pwm_map[] = {
|
||||
{ .gpio = GPIO_NUM_25, .index = 0 }
|
||||
};
|
||||
|
||||
static const lws_pwm_ops_t pwm_ops = {
|
||||
lws_pwm_plat_ops,
|
||||
.pwm_map = &pwm_map[0],
|
||||
.count_pwm_map = LWS_ARRAY_SIZE(pwm_map)
|
||||
};
|
||||
|
||||
/*
|
||||
* led controller
|
||||
*/
|
||||
|
@ -90,6 +104,7 @@ static const lws_led_gpio_map_t lgm[] = {
|
|||
{
|
||||
.name = "alert",
|
||||
.gpio = GPIO_NUM_25,
|
||||
.pwm_ops = &pwm_ops, /* managed by pwm */
|
||||
.active_level = 1,
|
||||
},
|
||||
};
|
||||
|
@ -107,13 +122,19 @@ static const uint8_t img[] = {
|
|||
|
||||
static uint8_t flip;
|
||||
|
||||
static const lws_led_sequence_def_t *seqs[] = {
|
||||
&lws_pwmseq_static_on,
|
||||
&lws_pwmseq_static_off,
|
||||
&lws_pwmseq_sine_endless_slow,
|
||||
&lws_pwmseq_sine_endless_fast,
|
||||
};
|
||||
|
||||
static int
|
||||
smd_cb(void *opaque, lws_smd_class_t _class, lws_usec_t timestamp, void *buf,
|
||||
size_t len)
|
||||
{
|
||||
flip = flip ^ 1;
|
||||
lgc.led_ops.intensity(&lgc.led_ops,
|
||||
lgc.led_ops.lookup(&lgc.led_ops, "alert"), flip);
|
||||
lws_led_transition(lls, 0, seqs[flip & 3], &lws_pwmseq_linear_wipe);
|
||||
flip++;
|
||||
|
||||
lwsl_hexdump_notice(buf, len);
|
||||
|
||||
|
@ -171,6 +192,12 @@ app_main(void)
|
|||
goto spin;
|
||||
}
|
||||
|
||||
/* pwm init must go after the led controller init */
|
||||
|
||||
pwm_ops.init(&pwm_ops);
|
||||
lgc.led_ops.intensity(&lgc.led_ops, "alert", 0);
|
||||
// lws_led_transition(lls, 0, &lws_pwmseq_sine_endless, NULL);
|
||||
|
||||
bcs = lws_button_controller_create(context, &bc);
|
||||
if (!bcs) {
|
||||
lwsl_err("%s: could not create buttons\n", __func__);
|
||||
|
|
Loading…
Add table
Reference in a new issue