WLED/usermods/MY9291/MY92xx.h
Spectre 46eae410c3
add My9291 light bulb driver (#2599)
Co-authored-by: Christian Schwinne <dev.aircoookie@gmail.com>
2022-03-31 20:49:00 +02:00

321 lines
7.8 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
MY92XX LED Driver for Arduino
Based on the C driver by MaiKe Labs
Copyright (c) 2016 - 2026 MaiKe Labs
Copyright (C) 2017 - 2018 Xose Pérez for the Arduino compatible library
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _my92xx_h
#define _my92xx_h
#include <Arduino.h>
#ifdef DEBUG_MY92XX
#if ARDUINO_ARCH_ESP8266
#define DEBUG_MSG_MY92XX(...) DEBUG_MY92XX.printf( __VA_ARGS__ )
#elif ARDUINO_ARCH_AVR
#define DEBUG_MSG_MY92XX(...) { char buffer[80]; snprintf(buffer, sizeof(buffer), __VA_ARGS__ ); DEBUG_MY92XX.print(buffer); }
#endif
#else
#define DEBUG_MSG_MY92XX(...)
#endif
typedef enum my92xx_model_t {
MY92XX_MODEL_MY9291 = 0X00,
MY92XX_MODEL_MY9231 = 0X01,
} my92xx_model_t;
typedef enum my92xx_cmd_one_shot_t {
MY92XX_CMD_ONE_SHOT_DISABLE = 0X00,
MY92XX_CMD_ONE_SHOT_ENFORCE = 0X01,
} my92xx_cmd_one_shot_t;
typedef enum my92xx_cmd_reaction_t {
MY92XX_CMD_REACTION_FAST = 0X00,
MY92XX_CMD_REACTION_SLOW = 0X01,
} my92xx_cmd_reaction_t;
typedef enum my92xx_cmd_bit_width_t {
MY92XX_CMD_BIT_WIDTH_16 = 0X00,
MY92XX_CMD_BIT_WIDTH_14 = 0X01,
MY92XX_CMD_BIT_WIDTH_12 = 0X02,
MY92XX_CMD_BIT_WIDTH_8 = 0X03,
} my92xx_cmd_bit_width_t;
typedef enum my92xx_cmd_frequency_t {
MY92XX_CMD_FREQUENCY_DIVIDE_1 = 0X00,
MY92XX_CMD_FREQUENCY_DIVIDE_4 = 0X01,
MY92XX_CMD_FREQUENCY_DIVIDE_16 = 0X02,
MY92XX_CMD_FREQUENCY_DIVIDE_64 = 0X03,
} my92xx_cmd_frequency_t;
typedef enum my92xx_cmd_scatter_t {
MY92XX_CMD_SCATTER_APDM = 0X00,
MY92XX_CMD_SCATTER_PWM = 0X01,
} my92xx_cmd_scatter_t;
typedef struct {
my92xx_cmd_scatter_t scatter : 1;
my92xx_cmd_frequency_t frequency : 2;
my92xx_cmd_bit_width_t bit_width : 2;
my92xx_cmd_reaction_t reaction : 1;
my92xx_cmd_one_shot_t one_shot : 1;
unsigned char resv : 1;
} __attribute__((aligned(1), packed)) my92xx_cmd_t;
#define MY92XX_COMMAND_DEFAULT { \
.scatter = MY92XX_CMD_SCATTER_APDM, \
.frequency = MY92XX_CMD_FREQUENCY_DIVIDE_1, \
.bit_width = MY92XX_CMD_BIT_WIDTH_8, \
.reaction = MY92XX_CMD_REACTION_FAST, \
.one_shot = MY92XX_CMD_ONE_SHOT_DISABLE, \
.resv = 0 \
}
class my92xx {
public:
my92xx(my92xx_model_t model, unsigned char chips, unsigned char di, unsigned char dcki, my92xx_cmd_t command);
unsigned char getChannels();
void setChannel(unsigned char channel, unsigned int value);
unsigned int getChannel(unsigned char channel);
void setState(bool state);
bool getState();
void update();
private:
void _di_pulse(unsigned int times);
void _dcki_pulse(unsigned int times);
void _set_cmd(my92xx_cmd_t command);
void _send();
void _write(unsigned int data, unsigned char bit_length);
my92xx_cmd_t _command;
my92xx_model_t _model = MY92XX_MODEL_MY9291;
unsigned char _chips = 1;
unsigned char _channels;
uint16_t* _value;
bool _state = false;
unsigned char _pin_di;
unsigned char _pin_dcki;
};
#if ARDUINO_ARCH_ESP8266
extern "C" {
void os_delay_us(unsigned int);
}
#elif ARDUINO_ARCH_AVR
#define os_delay_us delayMicroseconds
#endif
void my92xx::_di_pulse(unsigned int times) {
for (unsigned int i = 0; i < times; i++) {
digitalWrite(_pin_di, HIGH);
digitalWrite(_pin_di, LOW);
}
}
void my92xx::_dcki_pulse(unsigned int times) {
for (unsigned int i = 0; i < times; i++) {
digitalWrite(_pin_dcki, HIGH);
digitalWrite(_pin_dcki, LOW);
}
}
void my92xx::_write(unsigned int data, unsigned char bit_length) {
unsigned int mask = (0x01 << (bit_length - 1));
for (unsigned int i = 0; i < bit_length / 2; i++) {
digitalWrite(_pin_dcki, LOW);
digitalWrite(_pin_di, (data & mask) ? HIGH : LOW);
digitalWrite(_pin_dcki, HIGH);
data = data << 1;
digitalWrite(_pin_di, (data & mask) ? HIGH : LOW);
digitalWrite(_pin_dcki, LOW);
digitalWrite(_pin_di, LOW);
data = data << 1;
}
}
void my92xx::_set_cmd(my92xx_cmd_t command) {
// ets_intr_lock();
// TStop > 12us.
os_delay_us(12);
// Send 12 DI pulse, after 6 pulse's falling edge store duty data, and 12
// pulse's rising edge convert to command mode.
_di_pulse(12);
// Delay >12us, begin send CMD data
os_delay_us(12);
// Send CMD data
unsigned char command_data = *(unsigned char*)(&command);
for (unsigned char i = 0; i < _chips; i++) {
_write(command_data, 8);
}
// TStart > 12us. Delay 12 us.
os_delay_us(12);
// Send 16 DI pulseat 14 pulse's falling edge store CMD data, and
// at 16 pulse's falling edge convert to duty mode.
_di_pulse(16);
// TStop > 12us.
os_delay_us(12);
// ets_intr_unlock();
}
void my92xx::_send() {
#ifdef DEBUG_MY92XX
DEBUG_MSG_MY92XX("[MY92XX] Refresh: %s (", _state ? "ON" : "OFF");
for (unsigned char channel = 0; channel < _channels; channel++) {
DEBUG_MSG_MY92XX(" %d", _value[channel]);
}
DEBUG_MSG_MY92XX(" )\n");
#endif
unsigned char bit_length = 8;
switch (_command.bit_width) {
case MY92XX_CMD_BIT_WIDTH_16:
bit_length = 16;
break;
case MY92XX_CMD_BIT_WIDTH_14:
bit_length = 14;
break;
case MY92XX_CMD_BIT_WIDTH_12:
bit_length = 12;
break;
case MY92XX_CMD_BIT_WIDTH_8:
bit_length = 8;
break;
default:
bit_length = 8;
break;
}
// ets_intr_lock();
// TStop > 12us.
os_delay_us(12);
// Send color data
for (unsigned char channel = 0; channel < _channels; channel++) {
_write(_state ? _value[channel] : 0, bit_length);
}
// TStart > 12us. Ready for send DI pulse.
os_delay_us(12);
// Send 8 DI pulse. After 8 pulse falling edge, store old data.
_di_pulse(8);
// TStop > 12us.
os_delay_us(12);
// ets_intr_unlock();
}
// -----------------------------------------------------------------------------
unsigned char my92xx::getChannels() {
return _channels;
}
void my92xx::setChannel(unsigned char channel, unsigned int value) {
if (channel < _channels) {
_value[channel] = value;
}
}
unsigned int my92xx::getChannel(unsigned char channel) {
if (channel < _channels) {
return _value[channel];
}
return 0;
}
bool my92xx::getState() {
return _state;
}
void my92xx::setState(bool state) {
_state = state;
}
void my92xx::update() {
_send();
}
// -----------------------------------------------------------------------------
my92xx::my92xx(my92xx_model_t model, unsigned char chips, unsigned char di, unsigned char dcki, my92xx_cmd_t command) : _command(command) {
_model = model;
_chips = chips;
_pin_di = di;
_pin_dcki = dcki;
// Init channels
if (_model == MY92XX_MODEL_MY9291) {
_channels = 4 * _chips;
}
else if (_model == MY92XX_MODEL_MY9231) {
_channels = 3 * _chips;
}
_value = new uint16_t[_channels];
for (unsigned char i = 0; i < _channels; i++) {
_value[i] = 0;
}
// Init GPIO
pinMode(_pin_di, OUTPUT);
pinMode(_pin_dcki, OUTPUT);
digitalWrite(_pin_di, LOW);
digitalWrite(_pin_dcki, LOW);
// Clear all duty register
_dcki_pulse(32 * _chips);
// Send init command
_set_cmd(command);
DEBUG_MSG_MY92XX("[MY92XX] Initialized\n");
}
#endif