ESP32: Ledc driver module (#2027)

* Inital commit for supporting ledc driver

* Added documentation. More fade functions and better naming of constants

* Better field checking during setup. Updated documentation

* Reworked LEDC module to be used with an object model to decrease repetition of parameters
This commit is contained in:
larsstenberg 2017-08-11 19:11:12 +02:00 committed by Arnim Läuger
parent 4375e09c0b
commit 40e0be29ee
4 changed files with 627 additions and 1 deletions

View File

@ -104,6 +104,12 @@ config LUA_MODULE_I2C
help
Includes the I2C module (recommended).
config LUA_MODULE_LEDC
bool "LEDC module"
default "n"
help
Includes the LEDC module.
config LUA_MODULE_NET
bool "Net module"
default "y"

333
components/modules/ledc.c Normal file
View File

@ -0,0 +1,333 @@
// Module for working with the ledc driver
#include "module.h"
#include "lauxlib.h"
#include "platform.h"
#include "driver/ledc.h"
typedef struct {
int timer;
int channel;
int mode;
} ledc_channel;
static int lledc_new_channel( lua_State *L )
{
int t=1;
luaL_checkanytable (L, t);
/* Setup timer */
ledc_timer_config_t ledc_timer;
lua_getfield(L, t, "bits");
ledc_timer.bit_num = luaL_optint (L, -1, LEDC_TIMER_13_BIT);
if(ledc_timer.bit_num < LEDC_TIMER_10_BIT || ledc_timer.bit_num > LEDC_TIMER_15_BIT)
return luaL_error (L, "bits field out of range");
lua_getfield(L, t, "frequency");
if (lua_type (L, -1) == LUA_TNUMBER) {
ledc_timer.freq_hz = luaL_checkinteger(L, -1);
} else {
return luaL_error(L, "missing or invalid 'frequency' field");
}
lua_getfield(L, t, "mode");
if (lua_type (L, -1) == LUA_TNUMBER) {
ledc_timer.speed_mode = luaL_checkinteger(L, -1);
if(ledc_timer.speed_mode != LEDC_HIGH_SPEED_MODE && ledc_timer.speed_mode != LEDC_LOW_SPEED_MODE)
return luaL_error (L, "Invalid mode");
} else {
return luaL_error(L, "missing or invalid 'mode' field");
}
lua_getfield(L, t, "timer");
if (lua_type (L, -1) == LUA_TNUMBER) {
ledc_timer.timer_num = luaL_checkinteger(L, -1);
if(ledc_timer.timer_num < LEDC_TIMER_0 || ledc_timer.timer_num > LEDC_TIMER_3)
return luaL_error (L, "Invalid timer");
} else {
return luaL_error(L, "missing or invalid 'timer' field");
}
/* Setup channel */
ledc_channel_config_t channel_config = {
.speed_mode = ledc_timer.speed_mode,
.timer_sel = ledc_timer.timer_num,
.intr_type = LEDC_INTR_DISABLE
};
lua_getfield(L, t, "channel");
if (lua_type (L, -1) == LUA_TNUMBER) {
channel_config.channel = luaL_checkinteger(L, -1);
if(channel_config.channel < LEDC_CHANNEL_0 || channel_config.channel > LEDC_CHANNEL_7)
return luaL_error (L, "Invalid channel");
} else {
return luaL_error(L, "missing or invalid 'channel' field");
}
lua_getfield(L, t, "duty");
if (lua_type (L, -1) == LUA_TNUMBER) {
channel_config.duty = luaL_checkinteger(L, -1);
} else {
return luaL_error(L, "missing or invalid 'duty' field");
}
lua_getfield(L, t, "gpio");
if (lua_type (L, -1) == LUA_TNUMBER) {
channel_config.gpio_num = luaL_checkinteger(L, -1);
if(!GPIO_IS_VALID_GPIO(channel_config.gpio_num))
return luaL_error (L, "Invalid gpio");
} else {
return luaL_error(L, "missing or invalid 'gpio' field");
}
esp_err_t timerErr = ledc_timer_config(&ledc_timer);
if(timerErr != ESP_OK)
return luaL_error (L, "timer configuration failed code %d", timerErr);
esp_err_t channelErr = ledc_channel_config(&channel_config);
if(channelErr != ESP_OK)
return luaL_error (L, "channel configuration failed code %d", channelErr);
ledc_channel * channel = (ledc_channel*)lua_newuserdata(L, sizeof(ledc_channel));
luaL_getmetatable(L, "ledc.channel");
lua_setmetatable(L, -2);
channel->mode = ledc_timer.speed_mode;
channel->channel = channel_config.channel;
channel->timer = ledc_timer.timer_num;
return 1;
}
static int lledc_stop( lua_State *L ) {
ledc_channel * channel = (ledc_channel*)luaL_checkudata(L, 1, "ledc.channel");
int idleLevel = luaL_checkint (L, 2);
luaL_argcheck(L, idleLevel >= 0 && idleLevel <= 1, 1, "Invalid idle level");
esp_err_t err = ledc_stop(channel->mode, channel->channel, idleLevel);
if(err != ESP_OK)
return luaL_error (L, "stop failed, code %d", err);
return 1;
}
static int lledc_set_freq( lua_State *L ) {
ledc_channel * channel = (ledc_channel*)luaL_checkudata(L, 1, "ledc.channel");
int frequency = luaL_checkint (L, 2);
esp_err_t err = ledc_set_freq(channel->mode, channel->timer, frequency);
if(err != ESP_OK)
return luaL_error (L, "set freq failed, code %d", err);
return 1;
}
static int lledc_get_freq( lua_State *L ) {
ledc_channel * channel = (ledc_channel*)luaL_checkudata(L, 1, "ledc.channel");
int frequency = ledc_get_freq(channel->mode, channel->timer);
lua_pushinteger (L, frequency);
return 1;
}
static int lledc_set_duty( lua_State *L ) {
ledc_channel * channel = (ledc_channel*)luaL_checkudata(L, 1, "ledc.channel");
int duty = luaL_checkint (L, 2);
esp_err_t dutyErr = ledc_set_duty(channel->mode, channel->channel, duty);
if(dutyErr != ESP_OK)
return luaL_error (L, "set duty failed, code %d", dutyErr);
esp_err_t updateErr = ledc_update_duty(channel->mode, channel->channel);
if(updateErr != ESP_OK)
return luaL_error (L, "update duty failed, code %d", updateErr);
return 1;
}
static int lledc_get_duty( lua_State *L ) {
ledc_channel * channel = (ledc_channel*)luaL_checkudata(L, 1, "ledc.channel");
int duty = ledc_get_duty(channel->mode, channel->channel);
lua_pushinteger (L, duty);
return 1;
}
static int lledc_timer_rst( lua_State *L ) {
ledc_channel * channel = (ledc_channel*)luaL_checkudata(L, 1, "ledc.channel");
esp_err_t err = ledc_timer_rst(channel->mode, channel->timer);
if(err != ESP_OK)
return luaL_error (L, "reset failed, code %d", err);
return 1;
}
static int lledc_timer_pause( lua_State *L ) {
ledc_channel * channel = (ledc_channel*)luaL_checkudata(L, 1, "ledc.channel");
esp_err_t err = ledc_timer_pause(channel->mode, channel->timer);
if(err != ESP_OK)
return luaL_error (L, "pause failed, code %d", err);
return 1;
}
static int lledc_timer_resume( lua_State *L ) {
ledc_channel * channel = (ledc_channel*)luaL_checkudata(L, 1, "ledc.channel");
esp_err_t err = ledc_timer_resume(channel->mode, channel->timer);
if(err != ESP_OK)
return luaL_error (L, "resume failed, code %d", err);
return 1;
}
static int lledc_set_fade_with_time( lua_State *L ) {
ledc_channel * channel = (ledc_channel*)luaL_checkudata(L, 1, "ledc.channel");
int stack = 1;
int targetDuty = luaL_checkint (L, ++stack);
int maxFadeTime = luaL_checkint (L, ++stack);
int wait = luaL_optint (L, ++stack, LEDC_FADE_NO_WAIT);
luaL_argcheck(L, wait == LEDC_FADE_NO_WAIT || wait == LEDC_FADE_WAIT_DONE, stack, "Invalid wait");
ledc_fade_func_install(0);
esp_err_t fadeErr = ledc_set_fade_with_time(channel->mode, channel->channel, targetDuty, maxFadeTime);
if(fadeErr != ESP_OK)
return luaL_error (L, "set fade failed, code %d", fadeErr);
esp_err_t startErr = ledc_fade_start(channel->mode, channel->channel, wait);
if(startErr != ESP_OK)
return luaL_error (L, "start fade failed, code %d", startErr);
return 1;
}
static int lledc_set_fade_with_step( lua_State *L ) {
ledc_channel * channel = (ledc_channel*)luaL_checkudata(L, 1, "ledc.channel");
int stack = 1;
int targetDuty = luaL_checkint (L, ++stack);
int scale = luaL_checkint (L, ++stack);
int cycleNum = luaL_checkint (L, ++stack);
int wait = luaL_optint (L, ++stack, LEDC_FADE_NO_WAIT);
luaL_argcheck(L, wait == LEDC_FADE_NO_WAIT || wait == LEDC_FADE_WAIT_DONE, stack, "Invalid wait");
ledc_fade_func_install(0);
esp_err_t fadeErr = ledc_set_fade_with_step(channel->mode, channel->channel, targetDuty, scale, cycleNum);
if(fadeErr != ESP_OK)
return luaL_error (L, "set fade failed, code %d", fadeErr);
esp_err_t startErr = ledc_fade_start(channel->mode, channel->channel, wait);
if(startErr != ESP_OK)
return luaL_error (L, "start fade failed, code %d", startErr);
return 1;
}
static int lledc_set_fade( lua_State *L ) {
ledc_channel * channel = (ledc_channel*)luaL_checkudata(L, 1, "ledc.channel");
int stack = 1;
int duty = luaL_checkint (L, ++stack);
int direction = luaL_checkint (L, ++stack);
luaL_argcheck(L, direction == LEDC_DUTY_DIR_DECREASE || direction == LEDC_DUTY_DIR_INCREASE, stack, "Invalid direction");
int scale = luaL_checkint (L, ++stack);
int cycleNum = luaL_checkint (L, ++stack);
int stepNum = luaL_checkint (L, ++stack);;
int wait = luaL_optint (L, ++stack, LEDC_FADE_NO_WAIT);
luaL_argcheck(L, wait == LEDC_FADE_NO_WAIT || wait == LEDC_FADE_WAIT_DONE, stack, "Invalid wait");
ledc_fade_func_install(0);
esp_err_t fadeErr = ledc_set_fade(channel->mode, channel->channel, duty, direction, stepNum, cycleNum, scale);
if(fadeErr != ESP_OK)
return luaL_error (L, "set fade failed, code %d", fadeErr);
esp_err_t startErr = ledc_fade_start(channel->mode, channel->channel, wait);
if(startErr != ESP_OK)
return luaL_error (L, "start fade failed, code %d", startErr);
return 1;
}
// Module function map
static const LUA_REG_TYPE ledc_channel_map[] =
{
{ LSTRKEY( "getduty" ), LFUNCVAL( lledc_get_duty ) },
{ LSTRKEY( "setduty" ), LFUNCVAL( lledc_set_duty ) },
{ LSTRKEY( "getfreq" ), LFUNCVAL( lledc_get_freq ) },
{ LSTRKEY( "setfreq" ), LFUNCVAL( lledc_set_freq ) },
{ LSTRKEY( "stop" ), LFUNCVAL( lledc_stop ) },
{ LSTRKEY( "reset" ), LFUNCVAL( lledc_timer_rst ) },
{ LSTRKEY( "pause" ), LFUNCVAL( lledc_timer_pause ) },
{ LSTRKEY( "resume" ), LFUNCVAL( lledc_timer_resume ) },
{ LSTRKEY( "fadewithtime" ), LFUNCVAL( lledc_set_fade_with_time ) },
{ LSTRKEY( "fadewithstep" ), LFUNCVAL( lledc_set_fade_with_step ) },
{ LSTRKEY( "fade" ), LFUNCVAL( lledc_set_fade ) },
{ LSTRKEY( "__index" ), LROVAL( ledc_channel_map )},
{ LNILKEY, LNILVAL }
};
static const LUA_REG_TYPE ledc_map[] =
{
{ LSTRKEY( "newChannel" ), LFUNCVAL( lledc_new_channel ) },
{ LSTRKEY( "HIGH_SPEED"), LNUMVAL( LEDC_HIGH_SPEED_MODE ) },
{ LSTRKEY( "LOW_SPEED"), LNUMVAL( LEDC_LOW_SPEED_MODE ) },
{ LSTRKEY( "TIMER_0"), LNUMVAL( LEDC_TIMER_0 ) },
{ LSTRKEY( "TIMER_1"), LNUMVAL( LEDC_TIMER_1 ) },
{ LSTRKEY( "TIMER_2"), LNUMVAL( LEDC_TIMER_2 ) },
{ LSTRKEY( "TIMER_10_BIT"), LNUMVAL( LEDC_TIMER_10_BIT ) },
{ LSTRKEY( "TIMER_11_BIT"), LNUMVAL( LEDC_TIMER_11_BIT ) },
{ LSTRKEY( "TIMER_12_BIT"), LNUMVAL( LEDC_TIMER_12_BIT ) },
{ LSTRKEY( "TIMER_13_BIT"), LNUMVAL( LEDC_TIMER_13_BIT ) },
{ LSTRKEY( "TIMER_14_BIT"), LNUMVAL( LEDC_TIMER_14_BIT ) },
{ LSTRKEY( "TIMER_15_BIT"), LNUMVAL( LEDC_TIMER_15_BIT ) },
{ LSTRKEY( "CHANNEL_0"), LNUMVAL( LEDC_CHANNEL_0 ) },
{ LSTRKEY( "CHANNEL_1"), LNUMVAL( LEDC_CHANNEL_1 ) },
{ LSTRKEY( "CHANNEL_2"), LNUMVAL( LEDC_CHANNEL_2 ) },
{ LSTRKEY( "CHANNEL_3"), LNUMVAL( LEDC_CHANNEL_3 ) },
{ LSTRKEY( "CHANNEL_4"), LNUMVAL( LEDC_CHANNEL_4 ) },
{ LSTRKEY( "CHANNEL_5"), LNUMVAL( LEDC_CHANNEL_5 ) },
{ LSTRKEY( "CHANNEL_6"), LNUMVAL( LEDC_CHANNEL_6 ) },
{ LSTRKEY( "CHANNEL_7"), LNUMVAL( LEDC_CHANNEL_7 ) },
{ LSTRKEY( "IDLE_LOW"), LNUMVAL( 0 ) },
{ LSTRKEY( "IDLE_HIGH"), LNUMVAL( 1 ) },
{ LSTRKEY( "FADE_NO_WAIT"), LNUMVAL( LEDC_FADE_NO_WAIT ) },
{ LSTRKEY( "FADE_WAIT_DONE"), LNUMVAL( LEDC_FADE_WAIT_DONE ) },
{ LSTRKEY( "FADE_DECREASE"), LNUMVAL( LEDC_DUTY_DIR_DECREASE ) },
{ LSTRKEY( "FADE_INCREASE"), LNUMVAL( LEDC_DUTY_DIR_INCREASE ) },
{ LNILKEY, LNILVAL }
};
int luaopen_ledc(lua_State *L) {
luaL_rometatable(L, "ledc.channel", (void *)ledc_channel_map); // create metatable for ledc.channel
return 0;
}
NODEMCU_MODULE(LEDC, "ledc", ledc_map, luaopen_ledc);

287
docs/en/modules/ledc.md Normal file
View File

@ -0,0 +1,287 @@
# LEDC Module
| Since | Origin / Contributor | Maintainer | Source |
| :----- | :-------------------- | :---------- | :------ |
| 2017-07-05 | [Lars Stenberg](https://github.com/larsstenberg) | [Lars Stenberg](https://github.com/larsstenberg) | [ledc.c](../../../components/modules/ledc.c)|
This module provides access to the [LEDC](http://esp-idf.readthedocs.io/en/latest/api-reference/peripherals/ledc.html) PWM driver.
# LEDC Overview
The LED control module is primarily designed to control the intensity of LEDs, although it can be used to generate PWM signals for other purposes as well. It has 16 channels which can generate independent waveforms that can be used to drive e.g. RGB LED devices. For maximum flexibility, the high-speed as well as the low-speed channels can be driven from one of four high-speed/low-speed timers. The PWM controller also has the ability to automatically increase or decrease the duty cycle gradually, allowing for fades without any processor interference.
## ledc.newChannel()
Configures a PIN to be controlled by the LEDC system.
#### Syntax
```lua
ledc.setup({
gpio=x,
bits=ledc.TIMER_10_BIT || ledc.TIMER_11_BIT || ledc.TIMER_12_BIT || ledc.TIMER_13_BIT || ledc.TIMER_14_BIT || ledc.TIMER_15_BIT,
mode=ledc.HIGH_SPEED || ledc.LOW_SPEED,
timer=ledc.TIMER_0 || ledc.TIMER_1 || ledc.TIMER_2 || ledc.TIMER_3,
channel=ledc.CHANNEL_0 || ledc.CHANNEL_1 || ledc.CHANNEL_2 || ledc.CHANNEL_3 || ledc.CHANNEL_4 || ledc.CHANNEL_5 || ledc.CHANNEL_6 || ledc.CHANNEL_7,
frequency=x,
duty=x
});
```
#### Parameters
List of configuration tables:
- `gpio` one or more (given as list) pins, see [GPIO Overview](../gpio/#gpio-overview)
- `bits` Channel duty depth. Defaults to ledc.TIMER_13_BIT. Otherwise one of
- `ledc.TIMER_10_BIT`
- ...
- `ledc.TIMER_15_BIT`
- `mode` High-speed mode or low-speed mode
- `ledc.HIGH_SPEED`
- `ledc.LOW_SPEED`
- `timer` The timer source of channel
- `ledc.TIMER_0`
- ...
- `ledc.TIMER_3`
- `channel` The timer source of channel
- `ledc.CHANNEL_0`
- ...
- `ledc.CHANNEL_7`
- `frequency` Timer frequency(Hz)
- `duty` Channel duty, the duty range is [0, (2**bit_num) - 1]. Example: if ledc.TIMER_13_BIT is used maximum value is 4096 x 2 -1 = 8091
#### Returns
`ledc.channel`
#### Example
```lua
ledc.setup({
gpio=19,
bits=ledc.TIMER_13_BIT,
mode=ledc.HIGH_SPEED,
timer=ledc.TIMER_0,
channel=ledc.CHANNEL_0,
frequency=1000,
duty=4096
});
```
# LEDC channel module
All interactions with the LEDC subsystem is done on the channel object created using the newChannel method.
## ledc.channel:stop()
Disable LEDC output, and set idle level.
#### Syntax
`channel:stop(idleLevel)`
#### Parameters
- `idleLevel` Set output idle level after LEDC stops.
- `ledc.IDLE_LOW`
- `ledc.IDLE_HIGH`
#### Returns
nil
#### Example
```lua
channel:stop(ledc.IDLE_LOW)
```
## ledc.channel:setfreq()
Set channel frequency (Hz)
#### Syntax
`channel:setfreq(frequency)`
#### Parameters
- `frequency` What frequency should be set
#### Returns
nil
#### Example
```lua
channel:setfreq(2000)
```
## ledc.channel:getfreq()
Get channel frequency (Hz)
#### Syntax
`channel:getfreq()`
#### Parameters
None
#### Returns
- 0 error
- Others current LEDC frequency
#### Example
```lua
channel:getfreq()
```
## ledc.channel:setduty()
Set channel duty
#### Syntax
`channel:setdutyduty)`
#### Parameters
- `duty` What duty should be set
#### Returns
nil
#### Example
```lua
channel:setduty(4096)
```
## ledc.channel:getduty()
Get channel duty
#### Syntax
`channel:getduty()`
#### Parameters
None
#### Returns
- (-1) parameter error
- Others current LEDC duty
#### Example
```lua
channel:getduty()
```
## ledc.channel:reset()
Resets the timer
#### Syntax
`channel:reset()`
#### Parameters
None
#### Returns
nil
#### Example
```lua
channel:reset();
```
## ledc.channel:pause()
Pauses the timer
#### Syntax
`channel:pause()`
#### Parameters
None
#### Returns
nil
#### Example
```lua
channel:pause();
```
## ledc.channel:resume()
Resumes a paused timer
#### Syntax
`channel:resume()`
#### Parameters
None
#### Returns
nil
#### Example
```lua
channel:resume()
```
## ledc.channel:fadewithtime()
Set LEDC fade function, with a limited time.
#### Syntax
`channel:fadewithtime(targetDuty, maxFadeTime[, wait])`
#### Parameters
- `targetDuty` Target duty of fading.
- `maxFadeTime` The maximum time of the fading ( ms ).
- `wait` Whether to block until fading done.
- `ledc.FADE_NO_WAIT` (default)
- `ledc.FADE_WAIT_DONE`
#### Returns
nil
#### Example
```lua
channel:fadewithtime(4096, 1000);
```
## ledc.channel:fadewithstep()
Set LEDC fade function, with step.
#### Syntax
`channel:fadewithstep(targetDuty, scale, cycleNum[, wait])`
#### Parameters
- `targetDuty` Target duty of fading.
- `scale` Controls the increase or decrease step scale.
- `cycleNum` Increase or decrease the duty every cycle_num cycles
- `wait` Whether to block until fading done.
- `ledc.FADE_NO_WAIT` (default)
- `ledc.FADE_WAIT_DONE`
#### Returns
nil
#### Example
```lua
channel:fadewithstep(1000, 10, 10);
```
## ledc.channel:fade()
Set LEDC fade function.
#### Syntax
`channel:fadewithstep(duty, direction, scale, cycleNum, stepNum [, wait])`
#### Parameters
- `duty` Set the start of the gradient duty.
- `direction` Set the direction of the gradient.
- `ledc.FADE_DECREASE`
- `ledc.FADE_INCREASE`
- `scale` Set gradient change amplitude.
- `cycleNum` Set how many LEDC tick each time the gradient lasts.
- `stepNum` Set the number of the gradient.
- `wait` Whether to block until fading done.
- `ledc.FADE_NO_WAIT` (default)
- `ledc.FADE_WAIT_DONE`
#### Returns
nil
#### Example
```lua
channel:fade(0, ledc.FADE_INCREASE, 100, 100, 1000);
```

View File

@ -40,6 +40,7 @@ pages:
- 'file': 'en/modules/file.md'
- 'gpio': 'en/modules/gpio.md'
- 'i2c': 'en/modules/i2c.md'
- 'ledc': 'en/modules/ledc.md'
- 'net': 'en/modules/net.md'
- 'node': 'en/modules/node.md'
- 'ow (1-Wire)': 'en/modules/ow.md'
@ -53,4 +54,3 @@ pages:
- 'uart': 'en/modules/uart.md'
- 'wifi': 'en/modules/wifi.md'
- 'ws2812': 'en/modules/ws2812.md'