Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add tm1829 module to dev branch #984

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions app/include/user_modules.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
#define LUA_USE_MODULES_WIFI
//#define LUA_USE_MODULES_WS2801
#define LUA_USE_MODULES_WS2812
//#define LUA_USE_MODULES_TM1829


#endif /* LUA_CROSS_COMPILER */
Expand Down
155 changes: 155 additions & 0 deletions app/modules/tm1829.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
#include "module.h"
#include "lauxlib.h"
#include "platform.h"
#include "c_stdlib.h"
#include "c_string.h"
#include "user_interface.h"

static inline uint32_t _getCycleCount(void) {
uint32_t cycles;
__asm__ __volatile__("rsr %0,ccount":"=a" (cycles));
return cycles;
}

// This algorithm reads the cpu clock cycles to calculate the correct
// pulse widths. It works in both 80 and 160 MHz mode.
// The values for t0l, t1l, ttot are for low-speed mode (and not optimized).
// The TM1829 also has a high-speed mode. Timings are:
// T0L: typical: 170 ns (50 - 250 ns)
// T1L: typical: 450 ns (300 - 550 ns)
// TTOT: min 600 ns
// TresetL: typical: 500 us (min 140 us) (for both timings)
static void ICACHE_RAM_ATTR tm1829_write(uint8_t pin, uint8_t *pixels, uint32_t length) {
uint8_t *p, *end, pixel, mask;
uint32_t t, t0l, t1l, ttot, c, start_time, pin_mask;

pin_mask = 1 << pin;
p = pixels;
end = p + length;
pixel = *p++;
mask = 0x80;
start_time = 0;
t0l = (1000 * system_get_cpu_freq()) / 3333; // 0.30us (spec=0.30 +- 0.15)
t1l = (1000 * system_get_cpu_freq()) / 1250; // 0.80us (spec=0.80 +- 0.20)
ttot = (1000 * system_get_cpu_freq()) / 833; // 1.20us (spec=must be >= 1.20)

while (true) {
if (pixel & mask) {
t = t1l;
} else {
t = t0l;
}
while (((c = _getCycleCount()) - start_time) < ttot); // Wait for the previous bit to finish
GPIO_REG_WRITE(GPIO_OUT_W1TC_ADDRESS, pin_mask); // Set pin low
start_time = c; // Save the start time
while (((c = _getCycleCount()) - start_time) < t); // Wait for low time to finish
GPIO_REG_WRITE(GPIO_OUT_W1TS_ADDRESS, pin_mask); // Set pin high
if (!(mask >>= 1)) { // Next bit/byte
if (p >= end) {
break;
}
pixel= *p++;
mask = 0x80;
}
}
}

// Reset the TM1829
// restart with first pixel
static void ICACHE_RAM_ATTR tm1829_reset(uint8_t pin) {
GPIO_REG_WRITE(GPIO_OUT_W1TC_ADDRESS, 1 << pin); // Set pin low
os_delay_us(250); // wait reset time
GPIO_REG_WRITE(GPIO_OUT_W1TS_ADDRESS, 1 << pin); // Set pin high
}

// Lua: tm1829.writergb(pin, "string")
// Byte triples in the string are interpreted as R G B values and sent to the hardware as B R G.
// WARNING: this function scrambles the input buffer :
// a = string.char(255,0,128)
// tm1829.writergb(3,a)
// =a.byte()
// (0,255,128)

// tm1829.writergb(4, string.char(255, 0, 0)) uses GPIO2 and sets the first LED red.
// tm1829.writergb(3, string.char(0, 0, 255):rep(10)) uses GPIO0 and sets ten LEDs blue.
// tm1829.writergb(4, string.char(0, 255, 0, 255, 255, 255)) first LED green, second LED white.
static int ICACHE_FLASH_ATTR tm1829_writergb(lua_State* L)
{
const uint8_t pin = luaL_checkinteger(L, 1);
size_t length;
const char *rgb = luaL_checklstring(L, 2, &length);

// dont modify lua-internal lstring - make a copy instead
char *buffer = (char *)c_malloc(length);
c_memcpy(buffer, rgb, length);

// Ignore incomplete Byte triples at the end of buffer:
length -= length % 3;

// Rearrange R G B values to B R G order needed by TM1829 LEDs:
size_t i;
for (i = 0; i < length; i += 3) {
const char r = buffer[i];
const char g = buffer[i + 1];
const char b = buffer[i + 2];
buffer[i] = b;
buffer[i + 1] = r;
buffer[i + 2] = g;
// TM1829 does interpret blue == 255 as different packet type (set constant current)
// but we need pwm packet type, so make sure blue can't be 255
if (buffer[i] == 0xFF) buffer[i] = 0xFE;
}

// Initialize the output pin and wait a bit
platform_gpio_mode(pin, PLATFORM_GPIO_OUTPUT, PLATFORM_GPIO_FLOAT);
platform_gpio_write(pin, 1); // TM1829: high level on default

// Send the buffer
ets_intr_lock();
tm1829_write(pin_num[pin], (uint8_t*) buffer, length);
ets_intr_unlock();

c_free(buffer);

return 0;
}

// Lua: tm1829.write(pin, "string")
// Byte triples in the string are interpreted as B R G values.
// This function does not corrupt your buffer.
// WARNING: Make sure the blue values are not 255. Otherwise TM1829 will interpret
// the following 2 bytes as a different packet type (set constant current)
//
// tm1829.write(4, string.char(0, 255, 0)) uses GPIO2 and sets the first LED red.
// tm1829.write(3, string.char(255, 0, 0):rep(10)) uses GPIO0 and sets ten LEDs blue.
// tm1829.write(4, string.char(0, 0, 255, 255, 255, 255)) first LED green, second LED white.
static int ICACHE_FLASH_ATTR tm1829_writebrg(lua_State* L) {
const uint8_t pin = luaL_checkinteger(L, 1);
size_t length;
const char *buffer = luaL_checklstring(L, 2, &length);

// Initialize the output pin
platform_gpio_mode(pin, PLATFORM_GPIO_OUTPUT, PLATFORM_GPIO_FLOAT);
platform_gpio_write(pin, 1); // TM1829: high level on default

// Send the buffer
ets_intr_lock();
tm1829_write(pin_num[pin], (uint8_t*) buffer, length);
ets_intr_unlock();

return 0;
}

static const LUA_REG_TYPE tm1829_map[] =
{
{ LSTRKEY( "writergb" ), LFUNCVAL( tm1829_writergb )},
{ LSTRKEY( "write" ), LFUNCVAL( tm1829_writebrg )},
{ LNILKEY, LNILVAL}
};

int luaopen_tm1829(lua_State *L) {
// TODO: Make sure that the GPIO system is initialized
return 0;
}

NODEMCU_MODULE(TM1829, "tm1829", tm1829_map, luaopen_tm1829);
40 changes: 40 additions & 0 deletions docs/en/modules/tm1829.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# TM1829 Module

## tm1829.writergb()
Send RGB data in 8 bits to a TM1829 LED chain.

#### Syntax
`tm1829.writegrb(pin, string)`

#### Parameters
- `pin` any GPIO pin 0, 1, 2, ...
- `string` payload to be sent to one or more TM1829 LEDs.
It should be composed from a RGB triplet per element.
- `R1` the first pixel's red channel (0-255)
- `G1` the first pixel's green channel (0-255)
- `B1` the first pixel's blue channel (0-255)<br />
... You can connect a lot of TM1829 ...
- `R2`, `G2`, `B2` are the next TM1829's red, green and blue channel parameters

#### Returns
`nil`

#### Example
```lua
r = 0
g = 255
b = 0
leds_rgb = string.char(r,g,b, r,g,b)
tm1829.writergb(2, leds_rgb) -- turn two TM1829s to green, connected to pin 2
```

#### Example 2
```lua
tm1829.writergb(3, string.char(0, 0, 255):rep(20)) -- uses GPIO0 and sets twenty LEDs to blue
```

## tm1829.writebrg()
Send BRG data in 8bits to a TM1829 chain.
In contrast to writergb(), use TM1829s internal BRG order

Usage: see tm1829.writergb()