Der RGB-Zweig von nl_sm2135eh_set() schrieb Stromregister, Kanalbyte und
die drei RGB-Werte, liess SM_ADDR_WY (0xC5) aber unangetastet. Der Chip
behielt die zuletzt gesetzte Weiss-Mischung, und beim direkten Wechsel
Weiss -> Farbe (ohne Zwischenschritt ueber dunkel) schaelte sich der
Weissanteil beim Ausblenden der Farbe wieder frei - an einer migrierten
GU10 RGB gemessen.
Das ist dieselbe Klasse Fehler wie in 00d5a4c fuer die Gegenrichtung
behoben (WY-Zweig schrieb nie die RGB-Register), nur bislang unentdeckt
in dieser Richtung. Der RGB-Zweig schreibt jetzt zusaetzlich WY mit
0/0 - ein zweiter Bus-Transfer pro Aktualisierung, symmetrisch zum
WY-Zweig, der ebenfalls zwei Transfers braucht.
112 lines
4.1 KiB
C
112 lines
4.1 KiB
C
#include <string.h>
|
|
|
|
#include "wm_include.h"
|
|
|
|
#include "drivers/nl_led.h"
|
|
|
|
#define SM_ADDR_CURRENT 0xC0u
|
|
#define SM_ADDR_WY 0xC5u
|
|
#define SM_CHANNEL_WY 0x80u /* channel select bit 7: 0 = RGB, 1 = WY */
|
|
#define SM_CHANNEL_STBY 0x20u /* standby bit 5: powers the outputs down */
|
|
|
|
extern void delay_us(unsigned int us); /* provided by libwlan.a */
|
|
|
|
static uint8_t s_clk, s_dat, s_current;
|
|
static uint8_t s_last[NL_CH_COUNT];
|
|
static bool s_have_last;
|
|
|
|
static void pin(uint8_t p, int v)
|
|
{
|
|
tls_gpio_write((enum tls_io_name)p, (u8)v);
|
|
}
|
|
|
|
static void bus_start(void)
|
|
{
|
|
pin(s_clk, 1); pin(s_dat, 1); delay_us(1);
|
|
pin(s_dat, 0); delay_us(1);
|
|
pin(s_clk, 0); delay_us(1);
|
|
}
|
|
|
|
static void bus_stop(void)
|
|
{
|
|
pin(s_clk, 0); pin(s_dat, 0); delay_us(1);
|
|
pin(s_clk, 1); delay_us(1);
|
|
pin(s_dat, 1); delay_us(1);
|
|
}
|
|
|
|
static void write_byte(uint8_t b)
|
|
{
|
|
for (uint8_t m = 0x80; m; m >>= 1) {
|
|
pin(s_dat, (b & m) ? 1 : 0); delay_us(1);
|
|
pin(s_clk, 1); delay_us(1);
|
|
pin(s_clk, 0); delay_us(1);
|
|
}
|
|
/* ack clock; the SM2135EH does not drive an ack */
|
|
pin(s_dat, 0); delay_us(1);
|
|
pin(s_clk, 1); delay_us(1);
|
|
pin(s_clk, 0); pin(s_dat, 0);
|
|
}
|
|
|
|
static void sm_send(const uint8_t *d, int n)
|
|
{
|
|
bus_start();
|
|
for (int i = 0; i < n; i++)
|
|
write_byte(d[i]);
|
|
bus_stop();
|
|
}
|
|
|
|
void nl_sm2135eh_init(const nl_board_t *board)
|
|
{
|
|
uint8_t off[NL_CH_COUNT] = {0};
|
|
s_clk = board->sm_clk_pin;
|
|
s_dat = board->sm_dat_pin;
|
|
s_current = board->sm_current;
|
|
s_have_last = false;
|
|
tls_gpio_cfg((enum tls_io_name)s_clk, WM_GPIO_DIR_OUTPUT, WM_GPIO_ATTR_PULLHIGH);
|
|
tls_gpio_cfg((enum tls_io_name)s_dat, WM_GPIO_DIR_OUTPUT, WM_GPIO_ATTR_PULLHIGH);
|
|
nl_sm2135eh_set(off);
|
|
}
|
|
|
|
void nl_sm2135eh_set(const uint8_t ch[NL_CH_COUNT])
|
|
{
|
|
bool white = ch[NL_CH_CW] || ch[NL_CH_WW];
|
|
|
|
if (s_have_last && memcmp(ch, s_last, NL_CH_COUNT) == 0)
|
|
return;
|
|
memcpy(s_last, ch, NL_CH_COUNT);
|
|
s_have_last = true;
|
|
|
|
if (!white) {
|
|
/* All channels dark: put the driver into standby instead of just writing zero duty.
|
|
Without bit 5 the chip stays active and leaks enough current to keep the lamp
|
|
faintly glowing - measured on a migrated GU10 RGB, 2026-09-13. The old QConnex
|
|
firmware had this logic but left it commented out (qcl_lamp.c), which is why the
|
|
behaviour was inherited. Lamps driven straight from PWM pins (G95, the CW variants)
|
|
are unaffected. Turning back on writes 0x00 or SM_CHANNEL_WY here and clears it. */
|
|
bool dark = !ch[NL_CH_R] && !ch[NL_CH_G] && !ch[NL_CH_B];
|
|
uint8_t chan = dark ? SM_CHANNEL_STBY : 0x00u;
|
|
uint8_t d[6] = {SM_ADDR_CURRENT, s_current, chan, ch[NL_CH_B], ch[NL_CH_G], ch[NL_CH_R]};
|
|
sm_send(d, 6);
|
|
/* The WY data registers are cleared here too, even though this is the RGB branch:
|
|
the chip keeps whatever white value was last written, and a direct white -> colour
|
|
change never passes through the all-dark frame above. The stale white then bleeds
|
|
through as soon as the colour dims - the same defect as the colour -> white case
|
|
below (fixed in 00d5a4c, 2026-09-13), just mirrored: that fix made the WY branch
|
|
also write the (always-zero) RGB registers, this makes the RGB branch also write
|
|
the (always-zero) WY registers. Both directions had it; only one was caught first. */
|
|
uint8_t w[3] = {SM_ADDR_WY, 0, 0};
|
|
sm_send(w, 3);
|
|
} else {
|
|
/* The RGB data registers are written along with the channel select, even though they
|
|
are always zero here: the chip keeps whatever colour was last written, and a direct
|
|
colour -> white change never passes through the all-dark frame above. The stale
|
|
colour then bleeds through as soon as the white dims - visible as a coloured
|
|
fade-out on a migrated GU10 RGB, 2026-09-13. */
|
|
uint8_t h[6] = {SM_ADDR_CURRENT, s_current, SM_CHANNEL_WY,
|
|
ch[NL_CH_B], ch[NL_CH_G], ch[NL_CH_R]};
|
|
uint8_t d[3] = {SM_ADDR_WY, ch[NL_CH_WW], ch[NL_CH_CW]};
|
|
sm_send(h, 6);
|
|
sm_send(d, 3);
|
|
}
|
|
}
|