Files
ninlight-w600/src/drivers/nl_sm2135eh.c
T
Michael SauerandClaude Opus 5 f32216d17f fix: SM2135EH beim Ausschalten in den Standby legen
Bei allen Kanaelen auf null schrieb der Treiber nur Nullen ins
Kanal-Register, liess den Chip aber aktiv - er laesst dann genug
Reststrom durch, dass die Lampe schwach weiterglimmt. An einer
migrierten GU10 RGB gemessen. Jetzt wird Bit 5 (Standby) gesetzt.

Die alte QConnex-Firmware hatte diese Logik, aber auskommentiert
(qcl_lamp.c) - das Verhalten ist von dort geerbt. Lampen an direkten
PWM-Pins (G95, CW-Varianten) sind nicht betroffen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-13 02:32:31 +02:00

97 lines
2.9 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);
} else {
uint8_t h[3] = {SM_ADDR_CURRENT, s_current, SM_CHANNEL_WY};
uint8_t d[3] = {SM_ADDR_WY, ch[NL_CH_WW], ch[NL_CH_CW]};
sm_send(h, 3);
sm_send(d, 3);
}
}