diff options
Diffstat (limited to 'drivers/led')
65 files changed, 12500 insertions, 6955 deletions
diff --git a/drivers/led/apa102.c b/drivers/led/apa102.c index 527519eb8a..d6d4327495 100644 --- a/drivers/led/apa102.c +++ b/drivers/led/apa102.c @@ -43,72 +43,37 @@ } \ } while (0) -#define APA102_SEND_BIT(byte, bit) \ - do { \ - writePin(APA102_DI_PIN, (byte >> bit) & 1); \ - io_wait; \ - writePinHigh(APA102_CI_PIN); \ - io_wait; \ - writePinLow(APA102_CI_PIN); \ - io_wait; \ +#define APA102_SEND_BIT(byte, bit) \ + do { \ + gpio_write_pin(APA102_DI_PIN, (byte >> bit) & 1); \ + io_wait; \ + gpio_write_pin_high(APA102_CI_PIN); \ + io_wait; \ + gpio_write_pin_low(APA102_CI_PIN); \ + io_wait; \ } while (0) uint8_t apa102_led_brightness = APA102_DEFAULT_BRIGHTNESS; -void static apa102_start_frame(void); -void static apa102_end_frame(uint16_t num_leds); - -void static apa102_send_frame(uint8_t red, uint8_t green, uint8_t blue, uint8_t brightness); -void static apa102_send_byte(uint8_t byte); - -void apa102_setleds(rgb_led_t *start_led, uint16_t num_leds) { - rgb_led_t *end = start_led + num_leds; - - apa102_start_frame(); - for (rgb_led_t *led = start_led; led < end; led++) { - apa102_send_frame(led->r, led->g, led->b, apa102_led_brightness); - } - apa102_end_frame(num_leds); -} - -// Overwrite the default rgblight_call_driver to use apa102 driver -void rgblight_call_driver(rgb_led_t *start_led, uint8_t num_leds) { - apa102_setleds(start_led, num_leds); -} - -void static apa102_init(void) { - setPinOutput(APA102_DI_PIN); - setPinOutput(APA102_CI_PIN); - - writePinLow(APA102_DI_PIN); - writePinLow(APA102_CI_PIN); -} - -void apa102_set_brightness(uint8_t brightness) { - if (brightness > APA102_MAX_BRIGHTNESS) { - apa102_led_brightness = APA102_MAX_BRIGHTNESS; - } else if (brightness < 0) { - apa102_led_brightness = 0; - } else { - apa102_led_brightness = brightness; - } -} - -void static apa102_send_frame(uint8_t red, uint8_t green, uint8_t blue, uint8_t brightness) { - apa102_send_byte(0b11100000 | brightness); - apa102_send_byte(blue); - apa102_send_byte(green); - apa102_send_byte(red); +static void apa102_send_byte(uint8_t byte) { + APA102_SEND_BIT(byte, 7); + APA102_SEND_BIT(byte, 6); + APA102_SEND_BIT(byte, 5); + APA102_SEND_BIT(byte, 4); + APA102_SEND_BIT(byte, 3); + APA102_SEND_BIT(byte, 2); + APA102_SEND_BIT(byte, 1); + APA102_SEND_BIT(byte, 0); } -void static apa102_start_frame(void) { +static void apa102_start_frame(void) { apa102_init(); for (uint16_t i = 0; i < 4; i++) { apa102_send_byte(0); } } -void static apa102_end_frame(uint16_t num_leds) { +static void apa102_end_frame(uint16_t num_leds) { // This function has been taken from: https://github.com/pololu/apa102-arduino/blob/master/APA102.h // and adapted. The code is MIT licensed. I think thats compatible? // @@ -141,13 +106,37 @@ void static apa102_end_frame(uint16_t num_leds) { apa102_init(); } -void static apa102_send_byte(uint8_t byte) { - APA102_SEND_BIT(byte, 7); - APA102_SEND_BIT(byte, 6); - APA102_SEND_BIT(byte, 5); - APA102_SEND_BIT(byte, 4); - APA102_SEND_BIT(byte, 3); - APA102_SEND_BIT(byte, 2); - APA102_SEND_BIT(byte, 1); - APA102_SEND_BIT(byte, 0); +static void apa102_send_frame(uint8_t red, uint8_t green, uint8_t blue, uint8_t brightness) { + apa102_send_byte(0b11100000 | brightness); + apa102_send_byte(blue); + apa102_send_byte(green); + apa102_send_byte(red); +} + +void apa102_init(void) { + gpio_set_pin_output(APA102_DI_PIN); + gpio_set_pin_output(APA102_CI_PIN); + + gpio_write_pin_low(APA102_DI_PIN); + gpio_write_pin_low(APA102_CI_PIN); +} + +void apa102_setleds(rgb_led_t *start_led, uint16_t num_leds) { + rgb_led_t *end = start_led + num_leds; + + apa102_start_frame(); + for (rgb_led_t *led = start_led; led < end; led++) { + apa102_send_frame(led->r, led->g, led->b, apa102_led_brightness); + } + apa102_end_frame(num_leds); +} + +void apa102_set_brightness(uint8_t brightness) { + if (brightness > APA102_MAX_BRIGHTNESS) { + apa102_led_brightness = APA102_MAX_BRIGHTNESS; + } else if (brightness < 0) { + apa102_led_brightness = 0; + } else { + apa102_led_brightness = brightness; + } } diff --git a/drivers/led/apa102.h b/drivers/led/apa102.h index cd0a19d445..5e2f78658b 100644 --- a/drivers/led/apa102.h +++ b/drivers/led/apa102.h @@ -19,13 +19,19 @@ #include "color.h" +#if defined(RGBLIGHT_APA102) +# define APA102_LED_COUNT RGBLIGHT_LED_COUNT +#elif defined(RGB_MATRIX_APA102) +# define APA102_LED_COUNT RGB_MATRIX_LED_COUNT +#endif + #ifndef APA102_DEFAULT_BRIGHTNESS # define APA102_DEFAULT_BRIGHTNESS 31 #endif #define APA102_MAX_BRIGHTNESS 31 -extern uint8_t apa102_led_brightness; +void apa102_init(void); /* User Interface * @@ -38,4 +44,5 @@ extern uint8_t apa102_led_brightness; * - Send out the LED data */ void apa102_setleds(rgb_led_t *start_led, uint16_t num_leds); + void apa102_set_brightness(uint8_t brightness); diff --git a/drivers/led/aw20216s.c b/drivers/led/aw20216s.c index ab7f3ccb42..704794f5b5 100644 --- a/drivers/led/aw20216s.c +++ b/drivers/led/aw20216s.c @@ -45,8 +45,15 @@ # define AW20216S_SPI_DIVISOR 4 #endif -uint8_t g_pwm_buffer[AW20216S_DRIVER_COUNT][AW20216S_PWM_REGISTER_COUNT]; -bool g_pwm_buffer_update_required[AW20216S_DRIVER_COUNT] = {false}; +typedef struct aw20216s_driver_t { + uint8_t pwm_buffer[AW20216S_PWM_REGISTER_COUNT]; + bool pwm_buffer_dirty; +} PACKED aw20216s_driver_t; + +aw20216s_driver_t driver_buffers[AW20216S_DRIVER_COUNT] = {{ + .pwm_buffer = {0}, + .pwm_buffer_dirty = false, +}}; bool aw20216s_write(pin_t cs_pin, uint8_t page, uint8_t reg, uint8_t* data, uint8_t len) { static uint8_t s_spi_transfer_buffer[2] = {0}; @@ -106,16 +113,18 @@ static inline void aw20216s_auto_lowpower(pin_t cs_pin) { void aw20216s_init_drivers(void) { spi_init(); - aw20216s_init(AW20216S_CS_PIN_1, AW20216S_EN_PIN_1); +#if defined(AW20216S_EN_PIN) + gpio_set_pin_output(AW20216S_EN_PIN); + gpio_write_pin_high(AW20216S_EN_PIN); +#endif + + aw20216s_init(AW20216S_CS_PIN_1); #if defined(AW20216S_CS_PIN_2) - aw20216s_init(AW20216S_CS_PIN_2, AW20216S_EN_PIN_2); + aw20216s_init(AW20216S_CS_PIN_2); #endif } -void aw20216s_init(pin_t cs_pin, pin_t en_pin) { - setPinOutput(en_pin); - writePinHigh(en_pin); - +void aw20216s_init(pin_t cs_pin) { aw20216s_soft_reset(cs_pin); wait_ms(2); @@ -131,13 +140,14 @@ void aw20216s_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) { aw20216s_led_t led; memcpy_P(&led, (&g_aw20216s_leds[index]), sizeof(led)); - if (g_pwm_buffer[led.driver][led.r] == red && g_pwm_buffer[led.driver][led.g] == green && g_pwm_buffer[led.driver][led.b] == blue) { + if (driver_buffers[led.driver].pwm_buffer[led.r] == red && driver_buffers[led.driver].pwm_buffer[led.g] == green && driver_buffers[led.driver].pwm_buffer[led.b] == blue) { return; } - g_pwm_buffer[led.driver][led.r] = red; - g_pwm_buffer[led.driver][led.g] = green; - g_pwm_buffer[led.driver][led.b] = blue; - g_pwm_buffer_update_required[led.driver] = true; + + driver_buffers[led.driver].pwm_buffer[led.r] = red; + driver_buffers[led.driver].pwm_buffer[led.g] = green; + driver_buffers[led.driver].pwm_buffer[led.b] = blue; + driver_buffers[led.driver].pwm_buffer_dirty = true; } void aw20216s_set_color_all(uint8_t red, uint8_t green, uint8_t blue) { @@ -147,10 +157,10 @@ void aw20216s_set_color_all(uint8_t red, uint8_t green, uint8_t blue) { } void aw20216s_update_pwm_buffers(pin_t cs_pin, uint8_t index) { - if (g_pwm_buffer_update_required[index]) { - aw20216s_write(cs_pin, AW20216S_PAGE_PWM, 0, g_pwm_buffer[index], AW20216S_PWM_REGISTER_COUNT); + if (driver_buffers[index].pwm_buffer_dirty) { + aw20216s_write(cs_pin, AW20216S_PAGE_PWM, 0, driver_buffers[index].pwm_buffer, AW20216S_PWM_REGISTER_COUNT); + driver_buffers[index].pwm_buffer_dirty = false; } - g_pwm_buffer_update_required[index] = false; } void aw20216s_flush(void) { diff --git a/drivers/led/aw20216s.h b/drivers/led/aw20216s.h index 38a0c92b2f..b2c097125f 100644 --- a/drivers/led/aw20216s.h +++ b/drivers/led/aw20216s.h @@ -42,10 +42,10 @@ # define AW20216S_CS_PIN_2 DRIVER_2_CS #endif #ifdef DRIVER_1_EN -# define AW20216S_EN_PIN_1 DRIVER_1_EN +# define AW20216S_EN_PIN DRIVER_1_EN #endif -#ifdef DRIVER_2_EN -# define AW20216S_EN_PIN_2 DRIVER_2_EN +#ifdef AW20216S_EN_PIN_1 +# define AW20216S_EN_PIN AW20216S_EN_PIN_1 #endif #define aw_led aw20216s_led_t @@ -94,226 +94,467 @@ typedef struct aw20216s_led_t { extern const aw20216s_led_t PROGMEM g_aw20216s_leds[AW20216S_LED_COUNT]; void aw20216s_init_drivers(void); -void aw20216s_init(pin_t cs_pin, pin_t en_pin); +void aw20216s_init(pin_t cs_pin); void aw20216s_set_color(int index, uint8_t red, uint8_t green, uint8_t blue); void aw20216s_set_color_all(uint8_t red, uint8_t green, uint8_t blue); void aw20216s_update_pwm_buffers(pin_t cs_pin, uint8_t index); void aw20216s_flush(void); -#define CS1_SW1 0x00 -#define CS2_SW1 0x01 -#define CS3_SW1 0x02 -#define CS4_SW1 0x03 -#define CS5_SW1 0x04 -#define CS6_SW1 0x05 -#define CS7_SW1 0x06 -#define CS8_SW1 0x07 -#define CS9_SW1 0x08 -#define CS10_SW1 0x09 -#define CS11_SW1 0x0A -#define CS12_SW1 0x0B -#define CS13_SW1 0x0C -#define CS14_SW1 0x0D -#define CS15_SW1 0x0E -#define CS16_SW1 0x0F -#define CS17_SW1 0x10 -#define CS18_SW1 0x11 -#define CS1_SW2 0x12 -#define CS2_SW2 0x13 -#define CS3_SW2 0x14 -#define CS4_SW2 0x15 -#define CS5_SW2 0x16 -#define CS6_SW2 0x17 -#define CS7_SW2 0x18 -#define CS8_SW2 0x19 -#define CS9_SW2 0x1A -#define CS10_SW2 0x1B -#define CS11_SW2 0x1C -#define CS12_SW2 0x1D -#define CS13_SW2 0x1E -#define CS14_SW2 0x1F -#define CS15_SW2 0x20 -#define CS16_SW2 0x21 -#define CS17_SW2 0x22 -#define CS18_SW2 0x23 -#define CS1_SW3 0x24 -#define CS2_SW3 0x25 -#define CS3_SW3 0x26 -#define CS4_SW3 0x27 -#define CS5_SW3 0x28 -#define CS6_SW3 0x29 -#define CS7_SW3 0x2A -#define CS8_SW3 0x2B -#define CS9_SW3 0x2C -#define CS10_SW3 0x2D -#define CS11_SW3 0x2E -#define CS12_SW3 0x2F -#define CS13_SW3 0x30 -#define CS14_SW3 0x31 -#define CS15_SW3 0x32 -#define CS16_SW3 0x33 -#define CS17_SW3 0x34 -#define CS18_SW3 0x35 -#define CS1_SW4 0x36 -#define CS2_SW4 0x37 -#define CS3_SW4 0x38 -#define CS4_SW4 0x39 -#define CS5_SW4 0x3A -#define CS6_SW4 0x3B -#define CS7_SW4 0x3C -#define CS8_SW4 0x3D -#define CS9_SW4 0x3E -#define CS10_SW4 0x3F -#define CS11_SW4 0x40 -#define CS12_SW4 0x41 -#define CS13_SW4 0x42 -#define CS14_SW4 0x43 -#define CS15_SW4 0x44 -#define CS16_SW4 0x45 -#define CS17_SW4 0x46 -#define CS18_SW4 0x47 -#define CS1_SW5 0x48 -#define CS2_SW5 0x49 -#define CS3_SW5 0x4A -#define CS4_SW5 0x4B -#define CS5_SW5 0x4C -#define CS6_SW5 0x4D -#define CS7_SW5 0x4E -#define CS8_SW5 0x4F -#define CS9_SW5 0x50 -#define CS10_SW5 0x51 -#define CS11_SW5 0x52 -#define CS12_SW5 0x53 -#define CS13_SW5 0x54 -#define CS14_SW5 0x55 -#define CS15_SW5 0x56 -#define CS16_SW5 0x57 -#define CS17_SW5 0x58 -#define CS18_SW5 0x59 -#define CS1_SW6 0x5A -#define CS2_SW6 0x5B -#define CS3_SW6 0x5C -#define CS4_SW6 0x5D -#define CS5_SW6 0x5E -#define CS6_SW6 0x5F -#define CS7_SW6 0x60 -#define CS8_SW6 0x61 -#define CS9_SW6 0x62 -#define CS10_SW6 0x63 -#define CS11_SW6 0x64 -#define CS12_SW6 0x65 -#define CS13_SW6 0x66 -#define CS14_SW6 0x67 -#define CS15_SW6 0x68 -#define CS16_SW6 0x69 -#define CS17_SW6 0x6A -#define CS18_SW6 0x6B -#define CS1_SW7 0x6C -#define CS2_SW7 0x6D -#define CS3_SW7 0x6E -#define CS4_SW7 0x6F -#define CS5_SW7 0x70 -#define CS6_SW7 0x71 -#define CS7_SW7 0x72 -#define CS8_SW7 0x73 -#define CS9_SW7 0x74 -#define CS10_SW7 0x75 -#define CS11_SW7 0x76 -#define CS12_SW7 0x77 -#define CS13_SW7 0x78 -#define CS14_SW7 0x79 -#define CS15_SW7 0x7A -#define CS16_SW7 0x7B -#define CS17_SW7 0x7C -#define CS18_SW7 0x7D -#define CS1_SW8 0x7E -#define CS2_SW8 0x7F -#define CS3_SW8 0x80 -#define CS4_SW8 0x81 -#define CS5_SW8 0x82 -#define CS6_SW8 0x83 -#define CS7_SW8 0x84 -#define CS8_SW8 0x85 -#define CS9_SW8 0x86 -#define CS10_SW8 0x87 -#define CS11_SW8 0x88 -#define CS12_SW8 0x89 -#define CS13_SW8 0x8A -#define CS14_SW8 0x8B -#define CS15_SW8 0x8C -#define CS16_SW8 0x8D -#define CS17_SW8 0x8E -#define CS18_SW8 0x8F -#define CS1_SW9 0x90 -#define CS2_SW9 0x91 -#define CS3_SW9 0x92 -#define CS4_SW9 0x93 -#define CS5_SW9 0x94 -#define CS6_SW9 0x95 -#define CS7_SW9 0x96 -#define CS8_SW9 0x97 -#define CS9_SW9 0x98 -#define CS10_SW9 0x99 -#define CS11_SW9 0x9A -#define CS12_SW9 0x9B -#define CS13_SW9 0x9C -#define CS14_SW9 0x9D -#define CS15_SW9 0x9E -#define CS16_SW9 0x9F -#define CS17_SW9 0xA0 -#define CS18_SW9 0xA1 -#define CS1_SW10 0xA2 -#define CS2_SW10 0xA3 -#define CS3_SW10 0xA4 -#define CS4_SW10 0xA5 -#define CS5_SW10 0xA6 -#define CS6_SW10 0xA7 -#define CS7_SW10 0xA8 -#define CS8_SW10 0xA9 -#define CS9_SW10 0xAA -#define CS10_SW10 0xAB -#define CS11_SW10 0xAC -#define CS12_SW10 0xAD -#define CS13_SW10 0xAE -#define CS14_SW10 0xAF -#define CS15_SW10 0xB0 -#define CS16_SW10 0xB1 -#define CS17_SW10 0xB2 -#define CS18_SW10 0xB3 -#define CS1_SW11 0xB4 -#define CS2_SW11 0xB5 -#define CS3_SW11 0xB6 -#define CS4_SW11 0xB7 -#define CS5_SW11 0xB8 -#define CS6_SW11 0xB9 -#define CS7_SW11 0xBA -#define CS8_SW11 0xBB -#define CS9_SW11 0xBC -#define CS10_SW11 0xBD -#define CS11_SW11 0xBE -#define CS12_SW11 0xBF -#define CS13_SW11 0xC0 -#define CS14_SW11 0xC1 -#define CS15_SW11 0xC2 -#define CS16_SW11 0xC3 -#define CS17_SW11 0xC4 -#define CS18_SW11 0xC5 -#define CS1_SW12 0xC6 -#define CS2_SW12 0xC7 -#define CS3_SW12 0xC8 -#define CS4_SW12 0xC9 -#define CS5_SW12 0xCA -#define CS6_SW12 0xCB -#define CS7_SW12 0xCC -#define CS8_SW12 0xCD -#define CS9_SW12 0xCE -#define CS10_SW12 0xCF -#define CS11_SW12 0xD0 -#define CS12_SW12 0xD1 -#define CS13_SW12 0xD2 -#define CS14_SW12 0xD3 -#define CS15_SW12 0xD4 -#define CS16_SW12 0xD5 -#define CS17_SW12 0xD6 -#define CS18_SW12 0xD7 +#define SW1_CS1 0x00 +#define SW1_CS2 0x01 +#define SW1_CS3 0x02 +#define SW1_CS4 0x03 +#define SW1_CS5 0x04 +#define SW1_CS6 0x05 +#define SW1_CS7 0x06 +#define SW1_CS8 0x07 +#define SW1_CS9 0x08 +#define SW1_CS10 0x09 +#define SW1_CS11 0x0A +#define SW1_CS12 0x0B +#define SW1_CS13 0x0C +#define SW1_CS14 0x0D +#define SW1_CS15 0x0E +#define SW1_CS16 0x0F +#define SW1_CS17 0x10 +#define SW1_CS18 0x11 + +#define SW2_CS1 0x12 +#define SW2_CS2 0x13 +#define SW2_CS3 0x14 +#define SW2_CS4 0x15 +#define SW2_CS5 0x16 +#define SW2_CS6 0x17 +#define SW2_CS7 0x18 +#define SW2_CS8 0x19 +#define SW2_CS9 0x1A +#define SW2_CS10 0x1B +#define SW2_CS11 0x1C +#define SW2_CS12 0x1D +#define SW2_CS13 0x1E +#define SW2_CS14 0x1F +#define SW2_CS15 0x20 +#define SW2_CS16 0x21 +#define SW2_CS17 0x22 +#define SW2_CS18 0x23 + +#define SW3_CS1 0x24 +#define SW3_CS2 0x25 +#define SW3_CS3 0x26 +#define SW3_CS4 0x27 +#define SW3_CS5 0x28 +#define SW3_CS6 0x29 +#define SW3_CS7 0x2A +#define SW3_CS8 0x2B +#define SW3_CS9 0x2C +#define SW3_CS10 0x2D +#define SW3_CS11 0x2E +#define SW3_CS12 0x2F +#define SW3_CS13 0x30 +#define SW3_CS14 0x31 +#define SW3_CS15 0x32 +#define SW3_CS16 0x33 +#define SW3_CS17 0x34 +#define SW3_CS18 0x35 + +#define SW4_CS1 0x36 +#define SW4_CS2 0x37 +#define SW4_CS3 0x38 +#define SW4_CS4 0x39 +#define SW4_CS5 0x3A +#define SW4_CS6 0x3B +#define SW4_CS7 0x3C +#define SW4_CS8 0x3D +#define SW4_CS9 0x3E +#define SW4_CS10 0x3F +#define SW4_CS11 0x40 +#define SW4_CS12 0x41 +#define SW4_CS13 0x42 +#define SW4_CS14 0x43 +#define SW4_CS15 0x44 +#define SW4_CS16 0x45 +#define SW4_CS17 0x46 +#define SW4_CS18 0x47 + +#define SW5_CS1 0x48 +#define SW5_CS2 0x49 +#define SW5_CS3 0x4A +#define SW5_CS4 0x4B +#define SW5_CS5 0x4C +#define SW5_CS6 0x4D +#define SW5_CS7 0x4E +#define SW5_CS8 0x4F +#define SW5_CS9 0x50 +#define SW5_CS10 0x51 +#define SW5_CS11 0x52 +#define SW5_CS12 0x53 +#define SW5_CS13 0x54 +#define SW5_CS14 0x55 +#define SW5_CS15 0x56 +#define SW5_CS16 0x57 +#define SW5_CS17 0x58 +#define SW5_CS18 0x59 + +#define SW6_CS1 0x5A +#define SW6_CS2 0x5B +#define SW6_CS3 0x5C +#define SW6_CS4 0x5D +#define SW6_CS5 0x5E +#define SW6_CS6 0x5F +#define SW6_CS7 0x60 +#define SW6_CS8 0x61 +#define SW6_CS9 0x62 +#define SW6_CS10 0x63 +#define SW6_CS11 0x64 +#define SW6_CS12 0x65 +#define SW6_CS13 0x66 +#define SW6_CS14 0x67 +#define SW6_CS15 0x68 +#define SW6_CS16 0x69 +#define SW6_CS17 0x6A +#define SW6_CS18 0x6B + +#define SW7_CS1 0x6C +#define SW7_CS2 0x6D +#define SW7_CS3 0x6E +#define SW7_CS4 0x6F +#define SW7_CS5 0x70 +#define SW7_CS6 0x71 +#define SW7_CS7 0x72 +#define SW7_CS8 0x73 +#define SW7_CS9 0x74 +#define SW7_CS10 0x75 +#define SW7_CS11 0x76 +#define SW7_CS12 0x77 +#define SW7_CS13 0x78 +#define SW7_CS14 0x79 +#define SW7_CS15 0x7A +#define SW7_CS16 0x7B +#define SW7_CS17 0x7C +#define SW7_CS18 0x7D + +#define SW8_CS1 0x7E +#define SW8_CS2 0x7F +#define SW8_CS3 0x80 +#define SW8_CS4 0x81 +#define SW8_CS5 0x82 +#define SW8_CS6 0x83 +#define SW8_CS7 0x84 +#define SW8_CS8 0x85 +#define SW8_CS9 0x86 +#define SW8_CS10 0x87 +#define SW8_CS11 0x88 +#define SW8_CS12 0x89 +#define SW8_CS13 0x8A +#define SW8_CS14 0x8B +#define SW8_CS15 0x8C +#define SW8_CS16 0x8D +#define SW8_CS17 0x8E +#define SW8_CS18 0x8F + +#define SW9_CS1 0x90 +#define SW9_CS2 0x91 +#define SW9_CS3 0x92 +#define SW9_CS4 0x93 +#define SW9_CS5 0x94 +#define SW9_CS6 0x95 +#define SW9_CS7 0x96 +#define SW9_CS8 0x97 +#define SW9_CS9 0x98 +#define SW9_CS10 0x99 +#define SW9_CS11 0x9A +#define SW9_CS12 0x9B +#define SW9_CS13 0x9C +#define SW9_CS14 0x9D +#define SW9_CS15 0x9E +#define SW9_CS16 0x9F +#define SW9_CS17 0xA0 +#define SW9_CS18 0xA1 + +#define SW10_CS1 0xA2 +#define SW10_CS2 0xA3 +#define SW10_CS3 0xA4 +#define SW10_CS4 0xA5 +#define SW10_CS5 0xA6 +#define SW10_CS6 0xA7 +#define SW10_CS7 0xA8 +#define SW10_CS8 0xA9 +#define SW10_CS9 0xAA +#define SW10_CS10 0xAB +#define SW10_CS11 0xAC +#define SW10_CS12 0xAD +#define SW10_CS13 0xAE +#define SW10_CS14 0xAF +#define SW10_CS15 0xB0 +#define SW10_CS16 0xB1 +#define SW10_CS17 0xB2 +#define SW10_CS18 0xB3 + +#define SW11_CS1 0xB4 +#define SW11_CS2 0xB5 +#define SW11_CS3 0xB6 +#define SW11_CS4 0xB7 +#define SW11_CS5 0xB8 +#define SW11_CS6 0xB9 +#define SW11_CS7 0xBA +#define SW11_CS8 0xBB +#define SW11_CS9 0xBC +#define SW11_CS10 0xBD +#define SW11_CS11 0xBE +#define SW11_CS12 0xBF +#define SW11_CS13 0xC0 +#define SW11_CS14 0xC1 +#define SW11_CS15 0xC2 +#define SW11_CS16 0xC3 +#define SW11_CS17 0xC4 +#define SW11_CS18 0xC5 + +#define SW12_CS1 0xC6 +#define SW12_CS2 0xC7 +#define SW12_CS3 0xC8 +#define SW12_CS4 0xC9 +#define SW12_CS5 0xCA +#define SW12_CS6 0xCB +#define SW12_CS7 0xCC +#define SW12_CS8 0xCD +#define SW12_CS9 0xCE +#define SW12_CS10 0xCF +#define SW12_CS11 0xD0 +#define SW12_CS12 0xD1 +#define SW12_CS13 0xD2 +#define SW12_CS14 0xD3 +#define SW12_CS15 0xD4 +#define SW12_CS16 0xD5 +#define SW12_CS17 0xD6 +#define SW12_CS18 0xD7 + +// DEPRECATED - DO NOT USE + +#define CS1_SW1 SW1_CS1 +#define CS2_SW1 SW1_CS2 +#define CS3_SW1 SW1_CS3 +#define CS4_SW1 SW1_CS4 +#define CS5_SW1 SW1_CS5 +#define CS6_SW1 SW1_CS6 +#define CS7_SW1 SW1_CS7 +#define CS8_SW1 SW1_CS8 +#define CS9_SW1 SW1_CS9 +#define CS10_SW1 SW1_CS10 +#define CS11_SW1 SW1_CS11 +#define CS12_SW1 SW1_CS12 +#define CS13_SW1 SW1_CS13 +#define CS14_SW1 SW1_CS14 +#define CS15_SW1 SW1_CS15 +#define CS16_SW1 SW1_CS16 +#define CS17_SW1 SW1_CS17 +#define CS18_SW1 SW1_CS18 + +#define CS1_SW2 SW2_CS1 +#define CS2_SW2 SW2_CS2 +#define CS3_SW2 SW2_CS3 +#define CS4_SW2 SW2_CS4 +#define CS5_SW2 SW2_CS5 +#define CS6_SW2 SW2_CS6 +#define CS7_SW2 SW2_CS7 +#define CS8_SW2 SW2_CS8 +#define CS9_SW2 SW2_CS9 +#define CS10_SW2 SW2_CS10 +#define CS11_SW2 SW2_CS11 +#define CS12_SW2 SW2_CS12 +#define CS13_SW2 SW2_CS13 +#define CS14_SW2 SW2_CS14 +#define CS15_SW2 SW2_CS15 +#define CS16_SW2 SW2_CS16 +#define CS17_SW2 SW2_CS17 +#define CS18_SW2 SW2_CS18 + +#define CS1_SW3 SW3_CS1 +#define CS2_SW3 SW3_CS2 +#define CS3_SW3 SW3_CS3 +#define CS4_SW3 SW3_CS4 +#define CS5_SW3 SW3_CS5 +#define CS6_SW3 SW3_CS6 +#define CS7_SW3 SW3_CS7 +#define CS8_SW3 SW3_CS8 +#define CS9_SW3 SW3_CS9 +#define CS10_SW3 SW3_CS10 +#define CS11_SW3 SW3_CS11 +#define CS12_SW3 SW3_CS12 +#define CS13_SW3 SW3_CS13 +#define CS14_SW3 SW3_CS14 +#define CS15_SW3 SW3_CS15 +#define CS16_SW3 SW3_CS16 +#define CS17_SW3 SW3_CS17 +#define CS18_SW3 SW3_CS18 + +#define CS1_SW4 SW4_CS1 +#define CS2_SW4 SW4_CS2 +#define CS3_SW4 SW4_CS3 +#define CS4_SW4 SW4_CS4 +#define CS5_SW4 SW4_CS5 +#define CS6_SW4 SW4_CS6 +#define CS7_SW4 SW4_CS7 +#define CS8_SW4 SW4_CS8 +#define CS9_SW4 SW4_CS9 +#define CS10_SW4 SW4_CS10 +#define CS11_SW4 SW4_CS11 +#define CS12_SW4 SW4_CS12 +#define CS13_SW4 SW4_CS13 +#define CS14_SW4 SW4_CS14 +#define CS15_SW4 SW4_CS15 +#define CS16_SW4 SW4_CS16 +#define CS17_SW4 SW4_CS17 +#define CS18_SW4 SW4_CS18 + +#define CS1_SW5 SW5_CS1 +#define CS2_SW5 SW5_CS2 +#define CS3_SW5 SW5_CS3 +#define CS4_SW5 SW5_CS4 +#define CS5_SW5 SW5_CS5 +#define CS6_SW5 SW5_CS6 +#define CS7_SW5 SW5_CS7 +#define CS8_SW5 SW5_CS8 +#define CS9_SW5 SW5_CS9 +#define CS10_SW5 SW5_CS10 +#define CS11_SW5 SW5_CS11 +#define CS12_SW5 SW5_CS12 +#define CS13_SW5 SW5_CS13 +#define CS14_SW5 SW5_CS14 +#define CS15_SW5 SW5_CS15 +#define CS16_SW5 SW5_CS16 +#define CS17_SW5 SW5_CS17 +#define CS18_SW5 SW5_CS18 + +#define CS1_SW6 SW6_CS1 +#define CS2_SW6 SW6_CS2 +#define CS3_SW6 SW6_CS3 +#define CS4_SW6 SW6_CS4 +#define CS5_SW6 SW6_CS5 +#define CS6_SW6 SW6_CS6 +#define CS7_SW6 SW6_CS7 +#define CS8_SW6 SW6_CS8 +#define CS9_SW6 SW6_CS9 +#define CS10_SW6 SW6_CS10 +#define CS11_SW6 SW6_CS11 +#define CS12_SW6 SW6_CS12 +#define CS13_SW6 SW6_CS13 +#define CS14_SW6 SW6_CS14 +#define CS15_SW6 SW6_CS15 +#define CS16_SW6 SW6_CS16 +#define CS17_SW6 SW6_CS17 +#define CS18_SW6 SW6_CS18 + +#define CS1_SW7 SW7_CS1 +#define CS2_SW7 SW7_CS2 +#define CS3_SW7 SW7_CS3 +#define CS4_SW7 SW7_CS4 +#define CS5_SW7 SW7_CS5 +#define CS6_SW7 SW7_CS6 +#define CS7_SW7 SW7_CS7 +#define CS8_SW7 SW7_CS8 +#define CS9_SW7 SW7_CS9 +#define CS10_SW7 SW7_CS10 +#define CS11_SW7 SW7_CS11 +#define CS12_SW7 SW7_CS12 +#define CS13_SW7 SW7_CS13 +#define CS14_SW7 SW7_CS14 +#define CS15_SW7 SW7_CS15 +#define CS16_SW7 SW7_CS16 +#define CS17_SW7 SW7_CS17 +#define CS18_SW7 SW7_CS18 + +#define CS1_SW8 SW8_CS1 +#define CS2_SW8 SW8_CS2 +#define CS3_SW8 SW8_CS3 +#define CS4_SW8 SW8_CS4 +#define CS5_SW8 SW8_CS5 +#define CS6_SW8 SW8_CS6 +#define CS7_SW8 SW8_CS7 +#define CS8_SW8 SW8_CS8 +#define CS9_SW8 SW8_CS9 +#define CS10_SW8 SW8_CS10 +#define CS11_SW8 SW8_CS11 +#define CS12_SW8 SW8_CS12 +#define CS13_SW8 SW8_CS13 +#define CS14_SW8 SW8_CS14 +#define CS15_SW8 SW8_CS15 +#define CS16_SW8 SW8_CS16 +#define CS17_SW8 SW8_CS17 +#define CS18_SW8 SW8_CS18 + +#define CS1_SW9 SW9_CS1 +#define CS2_SW9 SW9_CS2 +#define CS3_SW9 SW9_CS3 +#define CS4_SW9 SW9_CS4 +#define CS5_SW9 SW9_CS5 +#define CS6_SW9 SW9_CS6 +#define CS7_SW9 SW9_CS7 +#define CS8_SW9 SW9_CS8 +#define CS9_SW9 SW9_CS9 +#define CS10_SW9 SW9_CS10 +#define CS11_SW9 SW9_CS11 +#define CS12_SW9 SW9_CS12 +#define CS13_SW9 SW9_CS13 +#define CS14_SW9 SW9_CS14 +#define CS15_SW9 SW9_CS15 +#define CS16_SW9 SW9_CS16 +#define CS17_SW9 SW9_CS17 +#define CS18_SW9 SW9_CS18 + +#define CS1_SW10 SW10_CS1 +#define CS2_SW10 SW10_CS2 +#define CS3_SW10 SW10_CS3 +#define CS4_SW10 SW10_CS4 +#define CS5_SW10 SW10_CS5 +#define CS6_SW10 SW10_CS6 +#define CS7_SW10 SW10_CS7 +#define CS8_SW10 SW10_CS8 +#define CS9_SW10 SW10_CS9 +#define CS10_SW10 SW10_CS10 +#define CS11_SW10 SW10_CS11 +#define CS12_SW10 SW10_CS12 +#define CS13_SW10 SW10_CS13 +#define CS14_SW10 SW10_CS14 +#define CS15_SW10 SW10_CS15 +#define CS16_SW10 SW10_CS16 +#define CS17_SW10 SW10_CS17 +#define CS18_SW10 SW10_CS18 + +#define CS1_SW11 SW11_CS1 +#define CS2_SW11 SW11_CS2 +#define CS3_SW11 SW11_CS3 +#define CS4_SW11 SW11_CS4 +#define CS5_SW11 SW11_CS5 +#define CS6_SW11 SW11_CS6 +#define CS7_SW11 SW11_CS7 +#define CS8_SW11 SW11_CS8 +#define CS9_SW11 SW11_CS9 +#define CS10_SW11 SW11_CS10 +#define CS11_SW11 SW11_CS11 +#define CS12_SW11 SW11_CS12 +#define CS13_SW11 SW11_CS13 +#define CS14_SW11 SW11_CS14 +#define CS15_SW11 SW11_CS15 +#define CS16_SW11 SW11_CS16 +#define CS17_SW11 SW11_CS17 +#define CS18_SW11 SW11_CS18 + +#define CS1_SW12 SW12_CS1 +#define CS2_SW12 SW12_CS2 +#define CS3_SW12 SW12_CS3 +#define CS4_SW12 SW12_CS4 +#define CS5_SW12 SW12_CS5 +#define CS6_SW12 SW12_CS6 +#define CS7_SW12 SW12_CS7 +#define CS8_SW12 SW12_CS8 +#define CS9_SW12 SW12_CS9 +#define CS10_SW12 SW12_CS10 +#define CS11_SW12 SW12_CS11 +#define CS12_SW12 SW12_CS12 +#define CS13_SW12 SW12_CS13 +#define CS14_SW12 SW12_CS14 +#define CS15_SW12 SW12_CS15 +#define CS16_SW12 SW12_CS16 +#define CS17_SW12 SW12_CS17 +#define CS18_SW12 SW12_CS18 diff --git a/drivers/led/issi/is31fl3218-simple.c b/drivers/led/issi/is31fl3218-mono.c index ce28c51d18..0174da7ab3 100644 --- a/drivers/led/issi/is31fl3218-simple.c +++ b/drivers/led/issi/is31fl3218-mono.c @@ -13,9 +13,10 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include "is31fl3218.h" -#include <string.h> + +#include "is31fl3218-mono.h" #include "i2c_master.h" +#include "gpio.h" #define IS31FL3218_PWM_REGISTER_COUNT 18 #define IS31FL3218_LED_CONTROL_REGISTER_COUNT 3 @@ -28,44 +29,49 @@ # define IS31FL3218_I2C_PERSISTENCE 0 #endif -// Reusable buffer for transfers -uint8_t g_twi_transfer_buffer[20]; +typedef struct is31fl3218_driver_t { + uint8_t pwm_buffer[IS31FL3218_PWM_REGISTER_COUNT]; + bool pwm_buffer_dirty; + uint8_t led_control_buffer[IS31FL3218_LED_CONTROL_REGISTER_COUNT]; + bool led_control_buffer_dirty; +} PACKED is31fl3218_driver_t; // IS31FL3218 has 18 PWM outputs and a fixed I2C address, so no chaining. -uint8_t g_pwm_buffer[IS31FL3218_PWM_REGISTER_COUNT]; -bool g_pwm_buffer_update_required = false; - -uint8_t g_led_control_registers[IS31FL3218_LED_CONTROL_REGISTER_COUNT] = {0}; -bool g_led_control_registers_update_required = false; +is31fl3218_driver_t driver_buffers = { + .pwm_buffer = {0}, + .pwm_buffer_dirty = false, + .led_control_buffer = {0}, + .led_control_buffer_dirty = false, +}; void is31fl3218_write_register(uint8_t reg, uint8_t data) { - g_twi_transfer_buffer[0] = reg; - g_twi_transfer_buffer[1] = data; #if IS31FL3218_I2C_PERSISTENCE > 0 for (uint8_t i = 0; i < IS31FL3218_I2C_PERSISTENCE; i++) { - if (i2c_transmit(IS31FL3218_I2C_ADDRESS << 1, g_twi_transfer_buffer, 2, IS31FL3218_I2C_TIMEOUT) == 0) break; + if (i2c_write_register(IS31FL3218_I2C_ADDRESS << 1, reg, &data, 1, IS31FL3218_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break; } #else - i2c_transmit(IS31FL3218_I2C_ADDRESS << 1, g_twi_transfer_buffer, 2, IS31FL3218_I2C_TIMEOUT); + i2c_write_register(IS31FL3218_I2C_ADDRESS << 1, reg, &data, 1, IS31FL3218_I2C_TIMEOUT); #endif } -void is31fl3218_write_pwm_buffer(uint8_t *pwm_buffer) { - g_twi_transfer_buffer[0] = IS31FL3218_REG_PWM; - memcpy(g_twi_transfer_buffer + 1, pwm_buffer, 18); - +void is31fl3218_write_pwm_buffer(void) { #if IS31FL3218_I2C_PERSISTENCE > 0 for (uint8_t i = 0; i < IS31FL3218_I2C_PERSISTENCE; i++) { - i2c_transmit(IS31FL3218_I2C_ADDRESS << 1, g_twi_transfer_buffer, 19, IS31FL3218_I2C_TIMEOUT); + if (i2c_write_register(IS31FL3218_I2C_ADDRESS << 1, IS31FL3218_REG_PWM, driver_buffers.pwm_buffer, 18, IS31FL3218_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break; } #else - i2c_transmit(IS31FL3218_I2C_ADDRESS << 1, g_twi_transfer_buffer, 19, IS31FL3218_I2C_TIMEOUT); + i2c_write_register(IS31FL3218_I2C_ADDRESS << 1, IS31FL3218_REG_PWM, driver_buffers.pwm_buffer, 18, IS31FL3218_I2C_TIMEOUT); #endif } void is31fl3218_init(void) { i2c_init(); +#if defined(IS31FL3218_SDB_PIN) + gpio_set_pin_output(IS31FL3218_SDB_PIN); + gpio_write_pin_high(IS31FL3218_SDB_PIN); +#endif + // In case we ever want to reinitialize (?) is31fl3218_write_register(IS31FL3218_REG_RESET, 0x00); @@ -94,14 +100,17 @@ void is31fl3218_init(void) { void is31fl3218_set_value(int index, uint8_t value) { is31fl3218_led_t led; + if (index >= 0 && index < IS31FL3218_LED_COUNT) { memcpy_P(&led, (&g_is31fl3218_leds[index]), sizeof(led)); + + if (driver_buffers.pwm_buffer[led.v] == value) { + return; + } + + driver_buffers.pwm_buffer[led.v] = value; + driver_buffers.pwm_buffer_dirty = true; } - if (g_pwm_buffer[led.v - IS31FL3218_REG_PWM] == value) { - return; - } - g_pwm_buffer[led.v - IS31FL3218_REG_PWM] = value; - g_pwm_buffer_update_required = true; } void is31fl3218_set_value_all(uint8_t value) { @@ -114,34 +123,34 @@ void is31fl3218_set_led_control_register(uint8_t index, bool value) { is31fl3218_led_t led; memcpy_P(&led, (&g_is31fl3218_leds[index]), sizeof(led)); - uint8_t control_register = (led.v - IS31FL3218_REG_PWM) / 6; - uint8_t bit_value = (led.v - IS31FL3218_REG_PWM) % 6; + uint8_t control_register = led.v / 6; + uint8_t bit_value = led.v % 6; if (value) { - g_led_control_registers[control_register] |= (1 << bit_value); + driver_buffers.led_control_buffer[control_register] |= (1 << bit_value); } else { - g_led_control_registers[control_register] &= ~(1 << bit_value); + driver_buffers.led_control_buffer[control_register] &= ~(1 << bit_value); } - g_led_control_registers_update_required = true; + driver_buffers.led_control_buffer_dirty = true; } void is31fl3218_update_pwm_buffers(void) { - if (g_pwm_buffer_update_required) { - is31fl3218_write_pwm_buffer(g_pwm_buffer); + if (driver_buffers.pwm_buffer_dirty) { + is31fl3218_write_pwm_buffer(); // Load PWM registers and LED Control register data is31fl3218_write_register(IS31FL3218_REG_UPDATE, 0x01); - g_pwm_buffer_update_required = false; + driver_buffers.pwm_buffer_dirty = false; } } void is31fl3218_update_led_control_registers(void) { - if (g_led_control_registers_update_required) { - for (int i = 0; i < IS31FL3218_LED_CONTROL_REGISTER_COUNT; i++) { - is31fl3218_write_register(IS31FL3218_REG_LED_CONTROL_1 + i, g_led_control_registers[i]); + if (driver_buffers.led_control_buffer_dirty) { + for (uint8_t i = 0; i < IS31FL3218_LED_CONTROL_REGISTER_COUNT; i++) { + is31fl3218_write_register(IS31FL3218_REG_LED_CONTROL_1 + i, driver_buffers.led_control_buffer[i]); } - g_led_control_registers_update_required = false; + driver_buffers.led_control_buffer_dirty = false; } } diff --git a/drivers/led/issi/is31fl3218-simple.h b/drivers/led/issi/is31fl3218-mono.h index 9492817809..38beed0569 100644 --- a/drivers/led/issi/is31fl3218-simple.h +++ b/drivers/led/issi/is31fl3218-mono.h @@ -43,6 +43,8 @@ extern const is31fl3218_led_t PROGMEM g_is31fl3218_leds[IS31FL3218_LED_COUNT]; void is31fl3218_init(void); +void is31fl3218_write_register(uint8_t reg, uint8_t data); + void is31fl3218_set_value(int index, uint8_t value); void is31fl3218_set_value_all(uint8_t value); @@ -53,21 +55,21 @@ void is31fl3218_update_pwm_buffers(void); void is31fl3218_update_led_control_registers(void); -#define OUT1 0x01 -#define OUT2 0x02 -#define OUT3 0x03 -#define OUT4 0x04 -#define OUT5 0x05 -#define OUT6 0x06 -#define OUT7 0x07 -#define OUT8 0x08 -#define OUT9 0x09 -#define OUT10 0x0A -#define OUT11 0x0B -#define OUT12 0x0C -#define OUT13 0x0D -#define OUT14 0x0E -#define OUT15 0x0F -#define OUT16 0x10 -#define OUT17 0x11 -#define OUT18 0x12 +#define OUT1 0x00 +#define OUT2 0x01 +#define OUT3 0x02 +#define OUT4 0x03 +#define OUT5 0x04 +#define OUT6 0x05 +#define OUT7 0x06 +#define OUT8 0x07 +#define OUT9 0x08 +#define OUT10 0x09 +#define OUT11 0x0A +#define OUT12 0x0B +#define OUT13 0x0C +#define OUT14 0x0D +#define OUT15 0x0E +#define OUT16 0x0F +#define OUT17 0x10 +#define OUT18 0x11 diff --git a/drivers/led/issi/is31fl3218.c b/drivers/led/issi/is31fl3218.c index 39db09d518..dd97d236f7 100644 --- a/drivers/led/issi/is31fl3218.c +++ b/drivers/led/issi/is31fl3218.c @@ -13,9 +13,10 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ + #include "is31fl3218.h" -#include <string.h> #include "i2c_master.h" +#include "gpio.h" #define IS31FL3218_PWM_REGISTER_COUNT 18 #define IS31FL3218_LED_CONTROL_REGISTER_COUNT 3 @@ -28,44 +29,49 @@ # define IS31FL3218_I2C_PERSISTENCE 0 #endif -// Reusable buffer for transfers -uint8_t g_twi_transfer_buffer[20]; +typedef struct is31fl3218_driver_t { + uint8_t pwm_buffer[IS31FL3218_PWM_REGISTER_COUNT]; + bool pwm_buffer_dirty; + uint8_t led_control_buffer[IS31FL3218_LED_CONTROL_REGISTER_COUNT]; + bool led_control_buffer_dirty; +} PACKED is31fl3218_driver_t; // IS31FL3218 has 18 PWM outputs and a fixed I2C address, so no chaining. -uint8_t g_pwm_buffer[IS31FL3218_PWM_REGISTER_COUNT]; -bool g_pwm_buffer_update_required = false; - -uint8_t g_led_control_registers[IS31FL3218_LED_CONTROL_REGISTER_COUNT] = {0}; -bool g_led_control_registers_update_required = false; +is31fl3218_driver_t driver_buffers = { + .pwm_buffer = {0}, + .pwm_buffer_dirty = false, + .led_control_buffer = {0}, + .led_control_buffer_dirty = false, +}; void is31fl3218_write_register(uint8_t reg, uint8_t data) { - g_twi_transfer_buffer[0] = reg; - g_twi_transfer_buffer[1] = data; #if IS31FL3218_I2C_PERSISTENCE > 0 for (uint8_t i = 0; i < IS31FL3218_I2C_PERSISTENCE; i++) { - if (i2c_transmit(IS31FL3218_I2C_ADDRESS << 1, g_twi_transfer_buffer, 2, IS31FL3218_I2C_TIMEOUT) == 0) break; + if (i2c_write_register(IS31FL3218_I2C_ADDRESS << 1, reg, &data, 1, IS31FL3218_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break; } #else - i2c_transmit(IS31FL3218_I2C_ADDRESS << 1, g_twi_transfer_buffer, 2, IS31FL3218_I2C_TIMEOUT); + i2c_write_register(IS31FL3218_I2C_ADDRESS << 1, reg, &data, 1, IS31FL3218_I2C_TIMEOUT); #endif } -void is31fl3218_write_pwm_buffer(uint8_t *pwm_buffer) { - g_twi_transfer_buffer[0] = IS31FL3218_REG_PWM; - memcpy(g_twi_transfer_buffer + 1, pwm_buffer, 18); - +void is31fl3218_write_pwm_buffer(void) { #if IS31FL3218_I2C_PERSISTENCE > 0 for (uint8_t i = 0; i < IS31FL3218_I2C_PERSISTENCE; i++) { - i2c_transmit(IS31FL3218_I2C_ADDRESS << 1, g_twi_transfer_buffer, 19, IS31FL3218_I2C_TIMEOUT); + if (i2c_write_register(IS31FL3218_I2C_ADDRESS << 1, IS31FL3218_REG_PWM, driver_buffers.pwm_buffer, 18, IS31FL3218_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break; } #else - i2c_transmit(IS31FL3218_I2C_ADDRESS << 1, g_twi_transfer_buffer, 19, IS31FL3218_I2C_TIMEOUT); + i2c_write_register(IS31FL3218_I2C_ADDRESS << 1, IS31FL3218_REG_PWM, driver_buffers.pwm_buffer, 18, IS31FL3218_I2C_TIMEOUT); #endif } void is31fl3218_init(void) { i2c_init(); +#if defined(IS31FL3218_SDB_PIN) + gpio_set_pin_output(IS31FL3218_SDB_PIN); + gpio_write_pin_high(IS31FL3218_SDB_PIN); +#endif + // In case we ever want to reinitialize (?) is31fl3218_write_register(IS31FL3218_REG_RESET, 0x00); @@ -94,16 +100,19 @@ void is31fl3218_init(void) { void is31fl3218_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) { is31fl3218_led_t led; + if (index >= 0 && index < IS31FL3218_LED_COUNT) { memcpy_P(&led, (&g_is31fl3218_leds[index]), sizeof(led)); + + if (driver_buffers.pwm_buffer[led.r] == red && driver_buffers.pwm_buffer[led.g] == green && driver_buffers.pwm_buffer[led.b] == blue) { + return; + } + + driver_buffers.pwm_buffer[led.r] = red; + driver_buffers.pwm_buffer[led.g] = green; + driver_buffers.pwm_buffer[led.b] = blue; + driver_buffers.pwm_buffer_dirty = true; } - if (g_pwm_buffer[led.r - IS31FL3218_REG_PWM] == red && g_pwm_buffer[led.g - IS31FL3218_REG_PWM] == green && g_pwm_buffer[led.b - IS31FL3218_REG_PWM] == blue) { - return; - } - g_pwm_buffer[led.r - IS31FL3218_REG_PWM] = red; - g_pwm_buffer[led.g - IS31FL3218_REG_PWM] = green; - g_pwm_buffer[led.b - IS31FL3218_REG_PWM] = blue; - g_pwm_buffer_update_required = true; } void is31fl3218_set_color_all(uint8_t red, uint8_t green, uint8_t blue) { @@ -116,48 +125,48 @@ void is31fl3218_set_led_control_register(uint8_t index, bool red, bool green, bo is31fl3218_led_t led; memcpy_P(&led, (&g_is31fl3218_leds[index]), sizeof(led)); - uint8_t control_register_r = (led.r - IS31FL3218_REG_PWM) / 6; - uint8_t control_register_g = (led.g - IS31FL3218_REG_PWM) / 6; - uint8_t control_register_b = (led.b - IS31FL3218_REG_PWM) / 6; - uint8_t bit_r = (led.r - IS31FL3218_REG_PWM) % 6; - uint8_t bit_g = (led.g - IS31FL3218_REG_PWM) % 6; - uint8_t bit_b = (led.b - IS31FL3218_REG_PWM) % 6; + uint8_t control_register_r = led.r / 6; + uint8_t control_register_g = led.g / 6; + uint8_t control_register_b = led.b / 6; + uint8_t bit_r = led.r % 6; + uint8_t bit_g = led.g % 6; + uint8_t bit_b = led.b % 6; if (red) { - g_led_control_registers[control_register_r] |= (1 << bit_r); + driver_buffers.led_control_buffer[control_register_r] |= (1 << bit_r); } else { - g_led_control_registers[control_register_r] &= ~(1 << bit_r); + driver_buffers.led_control_buffer[control_register_r] &= ~(1 << bit_r); } if (green) { - g_led_control_registers[control_register_g] |= (1 << bit_g); + driver_buffers.led_control_buffer[control_register_g] |= (1 << bit_g); } else { - g_led_control_registers[control_register_g] &= ~(1 << bit_g); + driver_buffers.led_control_buffer[control_register_g] &= ~(1 << bit_g); } if (blue) { - g_led_control_registers[control_register_b] |= (1 << bit_b); + driver_buffers.led_control_buffer[control_register_b] |= (1 << bit_b); } else { - g_led_control_registers[control_register_b] &= ~(1 << bit_b); + driver_buffers.led_control_buffer[control_register_b] &= ~(1 << bit_b); } - g_led_control_registers_update_required = true; + driver_buffers.led_control_buffer_dirty = true; } void is31fl3218_update_pwm_buffers(void) { - if (g_pwm_buffer_update_required) { - is31fl3218_write_pwm_buffer(g_pwm_buffer); + if (driver_buffers.pwm_buffer_dirty) { + is31fl3218_write_pwm_buffer(); // Load PWM registers and LED Control register data is31fl3218_write_register(IS31FL3218_REG_UPDATE, 0x01); - g_pwm_buffer_update_required = false; + driver_buffers.pwm_buffer_dirty = false; } } void is31fl3218_update_led_control_registers(void) { - if (g_led_control_registers_update_required) { - for (int i = 0; i < IS31FL3218_LED_CONTROL_REGISTER_COUNT; i++) { - is31fl3218_write_register(IS31FL3218_REG_LED_CONTROL_1 + i, g_led_control_registers[i]); + if (driver_buffers.led_control_buffer_dirty) { + for (uint8_t i = 0; i < IS31FL3218_LED_CONTROL_REGISTER_COUNT; i++) { + is31fl3218_write_register(IS31FL3218_REG_LED_CONTROL_1 + i, driver_buffers.led_control_buffer[i]); } - g_led_control_registers_update_required = false; + driver_buffers.led_control_buffer_dirty = false; } } diff --git a/drivers/led/issi/is31fl3218.h b/drivers/led/issi/is31fl3218.h index ffa7f36d61..2228343329 100644 --- a/drivers/led/issi/is31fl3218.h +++ b/drivers/led/issi/is31fl3218.h @@ -45,6 +45,8 @@ extern const is31fl3218_led_t PROGMEM g_is31fl3218_leds[IS31FL3218_LED_COUNT]; void is31fl3218_init(void); +void is31fl3218_write_register(uint8_t reg, uint8_t data); + void is31fl3218_set_color(int index, uint8_t red, uint8_t green, uint8_t blue); void is31fl3218_set_color_all(uint8_t red, uint8_t green, uint8_t blue); @@ -55,21 +57,21 @@ void is31fl3218_update_pwm_buffers(void); void is31fl3218_update_led_control_registers(void); -#define OUT1 0x01 -#define OUT2 0x02 -#define OUT3 0x03 -#define OUT4 0x04 -#define OUT5 0x05 -#define OUT6 0x06 -#define OUT7 0x07 -#define OUT8 0x08 -#define OUT9 0x09 -#define OUT10 0x0A -#define OUT11 0x0B -#define OUT12 0x0C -#define OUT13 0x0D -#define OUT14 0x0E -#define OUT15 0x0F -#define OUT16 0x10 -#define OUT17 0x11 -#define OUT18 0x12 +#define OUT1 0x00 +#define OUT2 0x01 +#define OUT3 0x02 +#define OUT4 0x03 +#define OUT5 0x04 +#define OUT6 0x05 +#define OUT7 0x06 +#define OUT8 0x07 +#define OUT9 0x08 +#define OUT10 0x09 +#define OUT11 0x0A +#define OUT12 0x0B +#define OUT13 0x0C +#define OUT14 0x0D +#define OUT15 0x0E +#define OUT16 0x0F +#define OUT17 0x10 +#define OUT18 0x11 diff --git a/drivers/led/issi/is31fl3729-mono.c b/drivers/led/issi/is31fl3729-mono.c new file mode 100644 index 0000000000..13d5146877 --- /dev/null +++ b/drivers/led/issi/is31fl3729-mono.c @@ -0,0 +1,213 @@ +/* Copyright 2024 HorrorTroll <https://github.com/HorrorTroll> + * Copyright 2024 Harrison Chan (Xelus) + * Copyright 2024 Dimitris Mantzouranis <d3xter93@gmail.com> + * + * 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 2 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/>. + */ + +#include "is31fl3729-mono.h" +#include "i2c_master.h" +#include "gpio.h" +#include "wait.h" + +#define IS31FL3729_PWM_REGISTER_COUNT 143 +#define IS31FL3729_SCALING_REGISTER_COUNT 16 + +#ifndef IS31FL3729_I2C_TIMEOUT +# define IS31FL3729_I2C_TIMEOUT 100 +#endif + +#ifndef IS31FL3729_I2C_PERSISTENCE +# define IS31FL3729_I2C_PERSISTENCE 0 +#endif + +#ifndef IS31FL3729_CONFIGURATION +# define IS31FL3729_CONFIGURATION IS31FL3729_CONFIG_SWS_15_9 +#endif + +#ifndef IS31FL3729_GLOBAL_CURRENT +# define IS31FL3729_GLOBAL_CURRENT 0x40 +#endif + +#ifndef IS31FL3729_SW_PULLDOWN +# define IS31FL3729_SW_PULLDOWN IS31FL3729_SW_PULLDOWN_2K_OHM_SW_OFF +#endif + +#ifndef IS31FL3729_CS_PULLUP +# define IS31FL3729_CS_PULLUP IS31FL3729_CS_PULLUP_2K_OHM_CS_OFF +#endif + +#ifndef IS31FL3729_SPREAD_SPECTRUM +# define IS31FL3729_SPREAD_SPECTRUM IS31FL3729_SPREAD_SPECTRUM_DISABLE +#endif + +#ifndef IS31FL3729_SPREAD_SPECTRUM_RANGE +# define IS31FL3729_SPREAD_SPECTRUM_RANGE IS31FL3729_SPREAD_SPECTRUM_RANGE_5_PERCENT +#endif + +#ifndef IS31FL3729_SPREAD_SPECTRUM_CYCLE_TIME +# define IS31FL3729_SPREAD_SPECTRUM_CYCLE_TIME IS31FL3729_SPREAD_SPECTRUM_CYCLE_TIME_1980_US +#endif + +#ifndef IS31FL3729_PWM_FREQUENCY +# define IS31FL3729_PWM_FREQUENCY IS31FL3729_PWM_FREQUENCY_32K_HZ +#endif + +const uint8_t i2c_addresses[IS31FL3729_DRIVER_COUNT] = { + IS31FL3729_I2C_ADDRESS_1, +#ifdef IS31FL3729_I2C_ADDRESS_2 + IS31FL3729_I2C_ADDRESS_2, +# ifdef IS31FL3729_I2C_ADDRESS_3 + IS31FL3729_I2C_ADDRESS_3, +# ifdef IS31FL3729_I2C_ADDRESS_4 + IS31FL3729_I2C_ADDRESS_4, +# endif +# endif +#endif +}; + +// These buffers match the PWM & scaling registers. +// Storing them like this is optimal for I2C transfers to the registers. +typedef struct is31fl3729_driver_t { + uint8_t pwm_buffer[IS31FL3729_PWM_REGISTER_COUNT]; + bool pwm_buffer_dirty; + uint8_t scaling_buffer[IS31FL3729_SCALING_REGISTER_COUNT]; + bool scaling_buffer_dirty; +} PACKED is31fl3729_driver_t; + +is31fl3729_driver_t driver_buffers[IS31FL3729_DRIVER_COUNT] = {{ + .pwm_buffer = {0}, + .pwm_buffer_dirty = false, + .scaling_buffer = {0}, + .scaling_buffer_dirty = false, +}}; + +void is31fl3729_write_register(uint8_t index, uint8_t reg, uint8_t data) { +#if IS31FL3729_I2C_PERSISTENCE > 0 + for (uint8_t i = 0; i < IS31FL3729_I2C_PERSISTENCE; i++) { + if (i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3729_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break; + } +#else + i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3729_I2C_TIMEOUT); +#endif +} + +void is31fl3729_write_pwm_buffer(uint8_t index) { + // Transmit PWM registers in 11 transfers of 13 bytes. + + // Iterate over the pwm_buffer contents at 13 byte intervals. + for (uint8_t i = 0; i <= IS31FL3729_PWM_REGISTER_COUNT; i += 13) { +#if IS31FL3729_I2C_PERSISTENCE > 0 + for (uint8_t j = 0; j < IS31FL3729_I2C_PERSISTENCE; j++) { + if (i2c_write_register(i2c_addresses[index] << 1, IS31FL3729_REG_PWM + i, driver_buffers[index].pwm_buffer + i, 13, IS31FL3729_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break; + } +#else + i2c_write_register(i2c_addresses[index] << 1, IS31FL3729_REG_PWM + i, driver_buffers[index].pwm_buffer + i, 13, IS31FL3729_I2C_TIMEOUT); +#endif + } +} + +void is31fl3729_init_drivers(void) { + i2c_init(); + +#if defined(IS31FL3729_SDB_PIN) + gpio_set_pin_output(IS31FL3729_SDB_PIN); + gpio_write_pin_high(IS31FL3729_SDB_PIN); +#endif + + for (uint8_t i = 0; i < IS31FL3729_DRIVER_COUNT; i++) { + is31fl3729_init(i); + } + + for (int i = 0; i < IS31FL3729_LED_COUNT; i++) { + is31fl3729_set_scaling_register(i, 0xFF); + } + + for (uint8_t i = 0; i < IS31FL3729_DRIVER_COUNT; i++) { + is31fl3729_update_scaling_registers(i); + } +} + +void is31fl3729_init(uint8_t index) { + // In order to avoid the LEDs being driven with garbage data + // in the LED driver's PWM registers, shutdown is enabled last. + // Set up the mode and other settings, clear the PWM registers, + // then disable software shutdown. + + is31fl3729_write_register(index, IS31FL3729_REG_PULLDOWNUP, ((IS31FL3729_SW_PULLDOWN & 0b111) << 4) | (IS31FL3729_CS_PULLUP & 0b111)); + is31fl3729_write_register(index, IS31FL3729_REG_SPREAD_SPECTRUM, ((IS31FL3729_SPREAD_SPECTRUM & 0b1) << 4) | ((IS31FL3729_SPREAD_SPECTRUM_RANGE & 0b11) << 2) | (IS31FL3729_SPREAD_SPECTRUM_CYCLE_TIME & 0b11)); + is31fl3729_write_register(index, IS31FL3729_REG_PWM_FREQUENCY, IS31FL3729_PWM_FREQUENCY); + is31fl3729_write_register(index, IS31FL3729_REG_GLOBAL_CURRENT, IS31FL3729_GLOBAL_CURRENT); + is31fl3729_write_register(index, IS31FL3729_REG_CONFIGURATION, IS31FL3729_CONFIGURATION); + + // Wait 10ms to ensure the device has woken up. + wait_ms(10); +} + +void is31fl3729_set_value(int index, uint8_t value) { + is31fl3729_led_t led; + if (index >= 0 && index < IS31FL3729_LED_COUNT) { + memcpy_P(&led, (&g_is31fl3729_leds[index]), sizeof(led)); + + if (driver_buffers[led.driver].pwm_buffer[led.v] == value) { + return; + } + + driver_buffers[led.driver].pwm_buffer[led.v] = value; + driver_buffers[led.driver].pwm_buffer_dirty = true; + } +} + +void is31fl3729_set_value_all(uint8_t value) { + for (int i = 0; i < IS31FL3729_LED_COUNT; i++) { + is31fl3729_set_value(i, value); + } +} + +void is31fl3729_set_scaling_register(uint8_t index, uint8_t value) { + is31fl3729_led_t led; + memcpy_P(&led, (&g_is31fl3729_leds[index]), sizeof(led)); + + // need to do a bit of checking here since 3729 scaling is per CS pin. + // not the usual per single LED key as per other ISSI drivers + // only enable them, since they should be default disabled + int cs_value = (led.v & 0x0F) - 1; + + driver_buffers[led.driver].scaling_buffer[cs_value] = value; + driver_buffers[led.driver].scaling_buffer_dirty = true; +} + +void is31fl3729_update_pwm_buffers(uint8_t index) { + if (driver_buffers[index].pwm_buffer_dirty) { + is31fl3729_write_pwm_buffer(index); + + driver_buffers[index].pwm_buffer_dirty = false; + } +} + +void is31fl3729_update_scaling_registers(uint8_t index) { + if (driver_buffers[index].scaling_buffer_dirty) { + for (uint8_t i = 0; i < IS31FL3729_SCALING_REGISTER_COUNT; i++) { + is31fl3729_write_register(index, IS31FL3729_REG_SCALING + i, driver_buffers[index].scaling_buffer[i]); + } + + driver_buffers[index].scaling_buffer_dirty = false; + } +} + +void is31fl3729_flush(void) { + for (uint8_t i = 0; i < IS31FL3729_DRIVER_COUNT; i++) { + is31fl3729_update_pwm_buffers(i); + } +} diff --git a/drivers/led/issi/is31fl3729-mono.h b/drivers/led/issi/is31fl3729-mono.h new file mode 100644 index 0000000000..9afcde84b3 --- /dev/null +++ b/drivers/led/issi/is31fl3729-mono.h @@ -0,0 +1,278 @@ +/* Copyright 2024 HorrorTroll <https://github.com/HorrorTroll> + * Copyright 2024 Harrison Chan (Xelus) + * Copyright 2024 Dimitris Mantzouranis <d3xter93@gmail.com> + * + * 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 2 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/>. + */ + +#pragma once + +#include <stdint.h> +#include <stdbool.h> +#include "progmem.h" +#include "util.h" + +#define IS31FL3729_REG_PWM 0x01 +#define IS31FL3729_REG_SCALING 0x90 +#define IS31FL3729_REG_CONFIGURATION 0xA0 +#define IS31FL3729_REG_GLOBAL_CURRENT 0xA1 +#define IS31FL3729_REG_PULLDOWNUP 0xB0 +#define IS31FL3729_REG_SPREAD_SPECTRUM 0xB1 +#define IS31FL3729_REG_PWM_FREQUENCY 0xB2 +#define IS31FL3729_REG_RESET 0xCF + +#define IS31FL3729_I2C_ADDRESS_GND 0x34 +#define IS31FL3729_I2C_ADDRESS_SCL 0x35 +#define IS31FL3729_I2C_ADDRESS_SDA 0x36 +#define IS31FL3729_I2C_ADDRESS_VCC 0x37 + +#if defined(LED_MATRIX_IS31FL3729) +# define IS31FL3729_LED_COUNT LED_MATRIX_LED_COUNT +#endif + +#if defined(IS31FL3729_I2C_ADDRESS_4) +# define IS31FL3729_DRIVER_COUNT 4 +#elif defined(IS31FL3729_I2C_ADDRESS_3) +# define IS31FL3729_DRIVER_COUNT 3 +#elif defined(IS31FL3729_I2C_ADDRESS_2) +# define IS31FL3729_DRIVER_COUNT 2 +#elif defined(IS31FL3729_I2C_ADDRESS_1) +# define IS31FL3729_DRIVER_COUNT 1 +#endif + +typedef struct is31fl3729_led_t { + uint8_t driver : 2; + uint8_t v; +} PACKED is31fl3729_led_t; + +extern const is31fl3729_led_t PROGMEM g_is31fl3729_leds[IS31FL3729_LED_COUNT]; + +void is31fl3729_init_drivers(void); +void is31fl3729_init(uint8_t index); +void is31fl3729_write_register(uint8_t index, uint8_t reg, uint8_t data); + +void is31fl3729_set_value(int index, uint8_t value); +void is31fl3729_set_value_all(uint8_t value); + +void is31fl3729_set_scaling_register(uint8_t index, uint8_t value); + +// This should not be called from an interrupt +// (eg. from a timer interrupt). +// Call this while idle (in between matrix scans). +// If the buffer is dirty, it will update the driver with the buffer. +void is31fl3729_update_pwm_buffers(uint8_t index); +void is31fl3729_update_scaling_registers(uint8_t index); + +void is31fl3729_flush(void); + +#define IS31FL3729_SW_PULLDOWN_0_OHM 0b000 +#define IS31FL3729_SW_PULLDOWN_0K5_OHM_SW_OFF 0b001 +#define IS31FL3729_SW_PULLDOWN_1K_OHM_SW_OFF 0b010 +#define IS31FL3729_SW_PULLDOWN_2K_OHM_SW_OFF 0b011 +#define IS31FL3729_SW_PULLDOWN_1K_OHM 0b100 +#define IS31FL3729_SW_PULLDOWN_2K_OHM 0b101 +#define IS31FL3729_SW_PULLDOWN_4K_OHM 0b110 +#define IS31FL3729_SW_PULLDOWN_8K_OHM 0b111 + +#define IS31FL3729_CS_PULLUP_0_OHM 0b000 +#define IS31FL3729_CS_PULLUP_0K5_OHM_CS_OFF 0b001 +#define IS31FL3729_CS_PULLUP_1K_OHM_CS_OFF 0b010 +#define IS31FL3729_CS_PULLUP_2K_OHM_CS_OFF 0b011 +#define IS31FL3729_CS_PULLUP_1K_OHM 0b100 +#define IS31FL3729_CS_PULLUP_2K_OHM 0b101 +#define IS31FL3729_CS_PULLUP_4K_OHM 0b110 +#define IS31FL3729_CS_PULLUP_8K_OHM 0b111 + +#define IS31FL3729_SPREAD_SPECTRUM_DISABLE 0b0 +#define IS31FL3729_SPREAD_SPECTRUM_ENABLE 0b1 + +#define IS31FL3729_SPREAD_SPECTRUM_RANGE_5_PERCENT 0b00 +#define IS31FL3729_SPREAD_SPECTRUM_RANGE_15_PERCENT 0b01 +#define IS31FL3729_SPREAD_SPECTRUM_RANGE_24_PERCENT 0b10 +#define IS31FL3729_SPREAD_SPECTRUM_RANGE_34_PERCENT 0b11 + +#define IS31FL3729_SPREAD_SPECTRUM_CYCLE_TIME_1980_US 0b00 +#define IS31FL3729_SPREAD_SPECTRUM_CYCLE_TIME_1200_US 0b01 +#define IS31FL3729_SPREAD_SPECTRUM_CYCLE_TIME_820_US 0b10 +#define IS31FL3729_SPREAD_SPECTRUM_CYCLE_TIME_660_US 0b11 + +#define IS31FL3729_PWM_FREQUENCY_55K_HZ 0b000 +#define IS31FL3729_PWM_FREQUENCY_32K_HZ 0b001 +#define IS31FL3729_PWM_FREQUENCY_4K_HZ 0b010 +#define IS31FL3729_PWM_FREQUENCY_2K_HZ 0b011 +#define IS31FL3729_PWM_FREQUENCY_1K_HZ 0b100 +#define IS31FL3729_PWM_FREQUENCY_500_HZ 0b101 +#define IS31FL3729_PWM_FREQUENCY_250_HZ 0b110 +#define IS31FL3729_PWM_FREQUENCY_80K_HZ 0b111 + +#define IS31FL3729_CONFIG_SWS_15_9 0x01 // 15 CS x 9 SW matrix +#define IS31FL3729_CONFIG_SWS_16_8 0x11 // 16 CS x 8 SW matrix +#define IS31FL3729_CONFIG_SWS_16_7 0x21 // 16 CS x 7 SW matrix +#define IS31FL3729_CONFIG_SWS_16_6 0x31 // 16 CS x 6 SW matrix +#define IS31FL3729_CONFIG_SWS_16_5 0x41 // 16 CS x 5 SW matrix +#define IS31FL3729_CONFIG_SWS_16_4 0x51 // 16 CS x 4 SW matrix +#define IS31FL3729_CONFIG_SWS_16_3 0x61 // 16 CS x 3 SW matrix +#define IS31FL3729_CONFIG_SWS_16_2 0x71 // 16 CS x 2 SW matrix + +#define SW1_CS1 0x00 +#define SW1_CS2 0x01 +#define SW1_CS3 0x02 +#define SW1_CS4 0x03 +#define SW1_CS5 0x04 +#define SW1_CS6 0x05 +#define SW1_CS7 0x06 +#define SW1_CS8 0x07 +#define SW1_CS9 0x08 +#define SW1_CS10 0x09 +#define SW1_CS11 0x0A +#define SW1_CS12 0x0B +#define SW1_CS13 0x0C +#define SW1_CS14 0x0D +#define SW1_CS15 0x0E +#define SW1_CS16 0x0F + +#define SW2_CS1 0x10 +#define SW2_CS2 0x11 +#define SW2_CS3 0x12 +#define SW2_CS4 0x13 +#define SW2_CS5 0x14 +#define SW2_CS6 0x15 +#define SW2_CS7 0x16 +#define SW2_CS8 0x17 +#define SW2_CS9 0x18 +#define SW2_CS10 0x19 +#define SW2_CS11 0x1A +#define SW2_CS12 0x1B +#define SW2_CS13 0x1C +#define SW2_CS14 0x1D +#define SW2_CS15 0x1E +#define SW2_CS16 0x1F + +#define SW3_CS1 0x20 +#define SW3_CS2 0x21 +#define SW3_CS3 0x22 +#define SW3_CS4 0x23 +#define SW3_CS5 0x24 +#define SW3_CS6 0x25 +#define SW3_CS7 0x26 +#define SW3_CS8 0x27 +#define SW3_CS9 0x28 +#define SW3_CS10 0x29 +#define SW3_CS11 0x2A +#define SW3_CS12 0x2B +#define SW3_CS13 0x2C +#define SW3_CS14 0x2D +#define SW3_CS15 0x2E +#define SW3_CS16 0x2F + +#define SW4_CS1 0x30 +#define SW4_CS2 0x31 +#define SW4_CS3 0x32 +#define SW4_CS4 0x33 +#define SW4_CS5 0x34 +#define SW4_CS6 0x35 +#define SW4_CS7 0x36 +#define SW4_CS8 0x37 +#define SW4_CS9 0x38 +#define SW4_CS10 0x39 +#define SW4_CS11 0x3A +#define SW4_CS12 0x3B +#define SW4_CS13 0x3C +#define SW4_CS14 0x3D +#define SW4_CS15 0x3E +#define SW4_CS16 0x3F + +#define SW5_CS1 0x40 +#define SW5_CS2 0x41 +#define SW5_CS3 0x42 +#define SW5_CS4 0x43 +#define SW5_CS5 0x44 +#define SW5_CS6 0x45 +#define SW5_CS7 0x46 +#define SW5_CS8 0x47 +#define SW5_CS9 0x48 +#define SW5_CS10 0x49 +#define SW5_CS11 0x4A +#define SW5_CS12 0x4B +#define SW5_CS13 0x4C +#define SW5_CS14 0x4D +#define SW5_CS15 0x4E +#define SW5_CS16 0x4F + +#define SW6_CS1 0x50 +#define SW6_CS2 0x51 +#define SW6_CS3 0x52 +#define SW6_CS4 0x53 +#define SW6_CS5 0x54 +#define SW6_CS6 0x55 +#define SW6_CS7 0x56 +#define SW6_CS8 0x57 +#define SW6_CS9 0x58 +#define SW6_CS10 0x59 +#define SW6_CS11 0x5A +#define SW6_CS12 0x5B +#define SW6_CS13 0x5C +#define SW6_CS14 0x5D +#define SW6_CS15 0x5E +#define SW6_CS16 0x5F + +#define SW7_CS1 0x60 +#define SW7_CS2 0x61 +#define SW7_CS3 0x62 +#define SW7_CS4 0x63 +#define SW7_CS5 0x64 +#define SW7_CS6 0x65 +#define SW7_CS7 0x66 +#define SW7_CS8 0x67 +#define SW7_CS9 0x68 +#define SW7_CS10 0x69 +#define SW7_CS11 0x6A +#define SW7_CS12 0x6B +#define SW7_CS13 0x6C +#define SW7_CS14 0x6D +#define SW7_CS15 0x6E +#define SW7_CS16 0x6F + +#define SW8_CS1 0x70 +#define SW8_CS2 0x71 +#define SW8_CS3 0x72 +#define SW8_CS4 0x73 +#define SW8_CS5 0x74 +#define SW8_CS6 0x75 +#define SW8_CS7 0x76 +#define SW8_CS8 0x77 +#define SW8_CS9 0x78 +#define SW8_CS10 0x79 +#define SW8_CS11 0x7A +#define SW8_CS12 0x7B +#define SW8_CS13 0x7C +#define SW8_CS14 0x7D +#define SW8_CS15 0x7E +#define SW8_CS16 0x7F + +#define SW9_CS1 0x80 +#define SW9_CS2 0x81 +#define SW9_CS3 0x82 +#define SW9_CS4 0x83 +#define SW9_CS5 0x84 +#define SW9_CS6 0x85 +#define SW9_CS7 0x86 +#define SW9_CS8 0x87 +#define SW9_CS9 0x88 +#define SW9_CS10 0x89 +#define SW9_CS11 0x8A +#define SW9_CS12 0x8B +#define SW9_CS13 0x8C +#define SW9_CS14 0x8D +#define SW9_CS15 0x8E diff --git a/drivers/led/issi/is31fl3729.c b/drivers/led/issi/is31fl3729.c new file mode 100644 index 0000000000..80acb22fa2 --- /dev/null +++ b/drivers/led/issi/is31fl3729.c @@ -0,0 +1,219 @@ +/* Copyright 2024 HorrorTroll <https://github.com/HorrorTroll> + * Copyright 2024 Harrison Chan (Xelus) + * Copyright 2024 Dimitris Mantzouranis <d3xter93@gmail.com> + * + * 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 2 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/>. + */ + +#include "is31fl3729.h" +#include "i2c_master.h" +#include "gpio.h" +#include "wait.h" + +#define IS31FL3729_PWM_REGISTER_COUNT 143 +#define IS31FL3729_SCALING_REGISTER_COUNT 16 + +#ifndef IS31FL3729_I2C_TIMEOUT +# define IS31FL3729_I2C_TIMEOUT 100 +#endif + +#ifndef IS31FL3729_I2C_PERSISTENCE +# define IS31FL3729_I2C_PERSISTENCE 0 +#endif + +#ifndef IS31FL3729_CONFIGURATION +# define IS31FL3729_CONFIGURATION IS31FL3729_CONFIG_SWS_15_9 +#endif + +#ifndef IS31FL3729_GLOBAL_CURRENT +# define IS31FL3729_GLOBAL_CURRENT 0x40 +#endif + +#ifndef IS31FL3729_SW_PULLDOWN +# define IS31FL3729_SW_PULLDOWN IS31FL3729_SW_PULLDOWN_2K_OHM_SW_OFF +#endif + +#ifndef IS31FL3729_CS_PULLUP +# define IS31FL3729_CS_PULLUP IS31FL3729_CS_PULLUP_2K_OHM_CS_OFF +#endif + +#ifndef IS31FL3729_SPREAD_SPECTRUM +# define IS31FL3729_SPREAD_SPECTRUM IS31FL3729_SPREAD_SPECTRUM_DISABLE +#endif + +#ifndef IS31FL3729_SPREAD_SPECTRUM_RANGE +# define IS31FL3729_SPREAD_SPECTRUM_RANGE IS31FL3729_SPREAD_SPECTRUM_RANGE_5_PERCENT +#endif + +#ifndef IS31FL3729_SPREAD_SPECTRUM_CYCLE_TIME +# define IS31FL3729_SPREAD_SPECTRUM_CYCLE_TIME IS31FL3729_SPREAD_SPECTRUM_CYCLE_TIME_1980_US +#endif + +#ifndef IS31FL3729_PWM_FREQUENCY +# define IS31FL3729_PWM_FREQUENCY IS31FL3729_PWM_FREQUENCY_32K_HZ +#endif + +const uint8_t i2c_addresses[IS31FL3729_DRIVER_COUNT] = { + IS31FL3729_I2C_ADDRESS_1, +#ifdef IS31FL3729_I2C_ADDRESS_2 + IS31FL3729_I2C_ADDRESS_2, +# ifdef IS31FL3729_I2C_ADDRESS_3 + IS31FL3729_I2C_ADDRESS_3, +# ifdef IS31FL3729_I2C_ADDRESS_4 + IS31FL3729_I2C_ADDRESS_4, +# endif +# endif +#endif +}; + +// These buffers match the PWM & scaling registers. +// Storing them like this is optimal for I2C transfers to the registers. +typedef struct is31fl3729_driver_t { + uint8_t pwm_buffer[IS31FL3729_PWM_REGISTER_COUNT]; + bool pwm_buffer_dirty; + uint8_t scaling_buffer[IS31FL3729_SCALING_REGISTER_COUNT]; + bool scaling_buffer_dirty; +} PACKED is31fl3729_driver_t; + +is31fl3729_driver_t driver_buffers[IS31FL3729_DRIVER_COUNT] = {{ + .pwm_buffer = {0}, + .pwm_buffer_dirty = false, + .scaling_buffer = {0}, + .scaling_buffer_dirty = false, +}}; + +void is31fl3729_write_register(uint8_t index, uint8_t reg, uint8_t data) { +#if IS31FL3729_I2C_PERSISTENCE > 0 + for (uint8_t i = 0; i < IS31FL3729_I2C_PERSISTENCE; i++) { + if (i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3729_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break; + } +#else + i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3729_I2C_TIMEOUT); +#endif +} + +void is31fl3729_write_pwm_buffer(uint8_t index) { + // Transmit PWM registers in 11 transfers of 13 bytes. + + // Iterate over the pwm_buffer contents at 13 byte intervals. + for (uint8_t i = 0; i <= IS31FL3729_PWM_REGISTER_COUNT; i += 13) { +#if IS31FL3729_I2C_PERSISTENCE > 0 + for (uint8_t j = 0; j < IS31FL3729_I2C_PERSISTENCE; j++) { + if (i2c_write_register(i2c_addresses[index] << 1, IS31FL3729_REG_PWM + i, driver_buffers[index].pwm_buffer + i, 13, IS31FL3729_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break; + } +#else + i2c_write_register(i2c_addresses[index] << 1, IS31FL3729_REG_PWM + i, driver_buffers[index].pwm_buffer + i, 13, IS31FL3729_I2C_TIMEOUT); +#endif + } +} + +void is31fl3729_init_drivers(void) { + i2c_init(); + +#if defined(IS31FL3729_SDB_PIN) + gpio_set_pin_output(IS31FL3729_SDB_PIN); + gpio_write_pin_high(IS31FL3729_SDB_PIN); +#endif + + for (uint8_t i = 0; i < IS31FL3729_DRIVER_COUNT; i++) { + is31fl3729_init(i); + } + + for (int i = 0; i < IS31FL3729_LED_COUNT; i++) { + is31fl3729_set_scaling_register(i, 0xFF, 0xFF, 0xFF); + } + + for (uint8_t i = 0; i < IS31FL3729_DRIVER_COUNT; i++) { + is31fl3729_update_scaling_registers(i); + } +} + +void is31fl3729_init(uint8_t index) { + // In order to avoid the LEDs being driven with garbage data + // in the LED driver's PWM registers, shutdown is enabled last. + // Set up the mode and other settings, clear the PWM registers, + // then disable software shutdown. + + is31fl3729_write_register(index, IS31FL3729_REG_PULLDOWNUP, ((IS31FL3729_SW_PULLDOWN & 0b111) << 4) | (IS31FL3729_CS_PULLUP & 0b111)); + is31fl3729_write_register(index, IS31FL3729_REG_SPREAD_SPECTRUM, ((IS31FL3729_SPREAD_SPECTRUM & 0b1) << 4) | ((IS31FL3729_SPREAD_SPECTRUM_RANGE & 0b11) << 2) | (IS31FL3729_SPREAD_SPECTRUM_CYCLE_TIME & 0b11)); + is31fl3729_write_register(index, IS31FL3729_REG_PWM_FREQUENCY, IS31FL3729_PWM_FREQUENCY); + is31fl3729_write_register(index, IS31FL3729_REG_GLOBAL_CURRENT, IS31FL3729_GLOBAL_CURRENT); + is31fl3729_write_register(index, IS31FL3729_REG_CONFIGURATION, IS31FL3729_CONFIGURATION); + + // Wait 10ms to ensure the device has woken up. + wait_ms(10); +} + +void is31fl3729_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) { + is31fl3729_led_t led; + if (index >= 0 && index < IS31FL3729_LED_COUNT) { + memcpy_P(&led, (&g_is31fl3729_leds[index]), sizeof(led)); + + if (driver_buffers[led.driver].pwm_buffer[led.r] == red && driver_buffers[led.driver].pwm_buffer[led.g] == green && driver_buffers[led.driver].pwm_buffer[led.b] == blue) { + return; + } + + driver_buffers[led.driver].pwm_buffer[led.r] = red; + driver_buffers[led.driver].pwm_buffer[led.g] = green; + driver_buffers[led.driver].pwm_buffer[led.b] = blue; + driver_buffers[led.driver].pwm_buffer_dirty = true; + } +} + +void is31fl3729_set_color_all(uint8_t red, uint8_t green, uint8_t blue) { + for (int i = 0; i < IS31FL3729_LED_COUNT; i++) { + is31fl3729_set_color(i, red, green, blue); + } +} + +void is31fl3729_set_scaling_register(uint8_t index, uint8_t red, uint8_t green, uint8_t blue) { + is31fl3729_led_t led; + memcpy_P(&led, (&g_is31fl3729_leds[index]), sizeof(led)); + + // need to do a bit of checking here since 3729 scaling is per CS pin. + // not the usual per RGB key as per other ISSI drivers + // only enable them, since they should be default disabled + int cs_red = (led.r & 0x0F) - 1; + int cs_green = (led.g & 0x0F) - 1; + int cs_blue = (led.b & 0x0F) - 1; + + driver_buffers[led.driver].scaling_buffer[cs_red] = red; + driver_buffers[led.driver].scaling_buffer[cs_green] = green; + driver_buffers[led.driver].scaling_buffer[cs_blue] = blue; + driver_buffers[led.driver].scaling_buffer_dirty = true; +} + +void is31fl3729_update_pwm_buffers(uint8_t index) { + if (driver_buffers[index].pwm_buffer_dirty) { + is31fl3729_write_pwm_buffer(index); + + driver_buffers[index].pwm_buffer_dirty = false; + } +} + +void is31fl3729_update_scaling_registers(uint8_t index) { + if (driver_buffers[index].scaling_buffer_dirty) { + for (uint8_t i = 0; i < IS31FL3729_SCALING_REGISTER_COUNT; i++) { + is31fl3729_write_register(index, IS31FL3729_REG_SCALING + i, driver_buffers[index].scaling_buffer[i]); + } + + driver_buffers[index].scaling_buffer_dirty = false; + } +} + +void is31fl3729_flush(void) { + for (uint8_t i = 0; i < IS31FL3729_DRIVER_COUNT; i++) { + is31fl3729_update_pwm_buffers(i); + } +} diff --git a/drivers/led/issi/is31fl3729.h b/drivers/led/issi/is31fl3729.h new file mode 100644 index 0000000000..865c462f05 --- /dev/null +++ b/drivers/led/issi/is31fl3729.h @@ -0,0 +1,280 @@ +/* Copyright 2024 HorrorTroll <https://github.com/HorrorTroll> + * Copyright 2024 Harrison Chan (Xelus) + * Copyright 2024 Dimitris Mantzouranis <d3xter93@gmail.com> + * + * 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 2 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/>. + */ + +#pragma once + +#include <stdint.h> +#include <stdbool.h> +#include "progmem.h" +#include "util.h" + +#define IS31FL3729_REG_PWM 0x01 +#define IS31FL3729_REG_SCALING 0x90 +#define IS31FL3729_REG_CONFIGURATION 0xA0 +#define IS31FL3729_REG_GLOBAL_CURRENT 0xA1 +#define IS31FL3729_REG_PULLDOWNUP 0xB0 +#define IS31FL3729_REG_SPREAD_SPECTRUM 0xB1 +#define IS31FL3729_REG_PWM_FREQUENCY 0xB2 +#define IS31FL3729_REG_RESET 0xCF + +#define IS31FL3729_I2C_ADDRESS_GND 0x34 +#define IS31FL3729_I2C_ADDRESS_SCL 0x35 +#define IS31FL3729_I2C_ADDRESS_SDA 0x36 +#define IS31FL3729_I2C_ADDRESS_VCC 0x37 + +#if defined(RGB_MATRIX_IS31FL3729) +# define IS31FL3729_LED_COUNT RGB_MATRIX_LED_COUNT +#endif + +#if defined(IS31FL3729_I2C_ADDRESS_4) +# define IS31FL3729_DRIVER_COUNT 4 +#elif defined(IS31FL3729_I2C_ADDRESS_3) +# define IS31FL3729_DRIVER_COUNT 3 +#elif defined(IS31FL3729_I2C_ADDRESS_2) +# define IS31FL3729_DRIVER_COUNT 2 +#elif defined(IS31FL3729_I2C_ADDRESS_1) +# define IS31FL3729_DRIVER_COUNT 1 +#endif + +typedef struct is31fl3729_led_t { + uint8_t driver : 2; + uint8_t r; + uint8_t g; + uint8_t b; +} PACKED is31fl3729_led_t; + +extern const is31fl3729_led_t PROGMEM g_is31fl3729_leds[IS31FL3729_LED_COUNT]; + +void is31fl3729_init_drivers(void); +void is31fl3729_init(uint8_t index); +void is31fl3729_write_register(uint8_t index, uint8_t reg, uint8_t data); + +void is31fl3729_set_color(int index, uint8_t red, uint8_t green, uint8_t blue); +void is31fl3729_set_color_all(uint8_t red, uint8_t green, uint8_t blue); + +void is31fl3729_set_scaling_register(uint8_t index, uint8_t red, uint8_t green, uint8_t blue); + +// This should not be called from an interrupt +// (eg. from a timer interrupt). +// Call this while idle (in between matrix scans). +// If the buffer is dirty, it will update the driver with the buffer. +void is31fl3729_update_pwm_buffers(uint8_t index); +void is31fl3729_update_scaling_registers(uint8_t index); + +void is31fl3729_flush(void); + +#define IS31FL3729_SW_PULLDOWN_0_OHM 0b000 +#define IS31FL3729_SW_PULLDOWN_0K5_OHM_SW_OFF 0b001 +#define IS31FL3729_SW_PULLDOWN_1K_OHM_SW_OFF 0b010 +#define IS31FL3729_SW_PULLDOWN_2K_OHM_SW_OFF 0b011 +#define IS31FL3729_SW_PULLDOWN_1K_OHM 0b100 +#define IS31FL3729_SW_PULLDOWN_2K_OHM 0b101 +#define IS31FL3729_SW_PULLDOWN_4K_OHM 0b110 +#define IS31FL3729_SW_PULLDOWN_8K_OHM 0b111 + +#define IS31FL3729_CS_PULLUP_0_OHM 0b000 +#define IS31FL3729_CS_PULLUP_0K5_OHM_CS_OFF 0b001 +#define IS31FL3729_CS_PULLUP_1K_OHM_CS_OFF 0b010 +#define IS31FL3729_CS_PULLUP_2K_OHM_CS_OFF 0b011 +#define IS31FL3729_CS_PULLUP_1K_OHM 0b100 +#define IS31FL3729_CS_PULLUP_2K_OHM 0b101 +#define IS31FL3729_CS_PULLUP_4K_OHM 0b110 +#define IS31FL3729_CS_PULLUP_8K_OHM 0b111 + +#define IS31FL3729_SPREAD_SPECTRUM_DISABLE 0b0 +#define IS31FL3729_SPREAD_SPECTRUM_ENABLE 0b1 + +#define IS31FL3729_SPREAD_SPECTRUM_RANGE_5_PERCENT 0b00 +#define IS31FL3729_SPREAD_SPECTRUM_RANGE_15_PERCENT 0b01 +#define IS31FL3729_SPREAD_SPECTRUM_RANGE_24_PERCENT 0b10 +#define IS31FL3729_SPREAD_SPECTRUM_RANGE_34_PERCENT 0b11 + +#define IS31FL3729_SPREAD_SPECTRUM_CYCLE_TIME_1980_US 0b00 +#define IS31FL3729_SPREAD_SPECTRUM_CYCLE_TIME_1200_US 0b01 +#define IS31FL3729_SPREAD_SPECTRUM_CYCLE_TIME_820_US 0b10 +#define IS31FL3729_SPREAD_SPECTRUM_CYCLE_TIME_660_US 0b11 + +#define IS31FL3729_PWM_FREQUENCY_55K_HZ 0b000 +#define IS31FL3729_PWM_FREQUENCY_32K_HZ 0b001 +#define IS31FL3729_PWM_FREQUENCY_4K_HZ 0b010 +#define IS31FL3729_PWM_FREQUENCY_2K_HZ 0b011 +#define IS31FL3729_PWM_FREQUENCY_1K_HZ 0b100 +#define IS31FL3729_PWM_FREQUENCY_500_HZ 0b101 +#define IS31FL3729_PWM_FREQUENCY_250_HZ 0b110 +#define IS31FL3729_PWM_FREQUENCY_80K_HZ 0b111 + +#define IS31FL3729_CONFIG_SWS_15_9 0x01 // 15 CS x 9 SW matrix +#define IS31FL3729_CONFIG_SWS_16_8 0x11 // 16 CS x 8 SW matrix +#define IS31FL3729_CONFIG_SWS_16_7 0x21 // 16 CS x 7 SW matrix +#define IS31FL3729_CONFIG_SWS_16_6 0x31 // 16 CS x 6 SW matrix +#define IS31FL3729_CONFIG_SWS_16_5 0x41 // 16 CS x 5 SW matrix +#define IS31FL3729_CONFIG_SWS_16_4 0x51 // 16 CS x 4 SW matrix +#define IS31FL3729_CONFIG_SWS_16_3 0x61 // 16 CS x 3 SW matrix +#define IS31FL3729_CONFIG_SWS_16_2 0x71 // 16 CS x 2 SW matrix + +#define SW1_CS1 0x00 +#define SW1_CS2 0x01 +#define SW1_CS3 0x02 +#define SW1_CS4 0x03 +#define SW1_CS5 0x04 +#define SW1_CS6 0x05 +#define SW1_CS7 0x06 +#define SW1_CS8 0x07 +#define SW1_CS9 0x08 +#define SW1_CS10 0x09 +#define SW1_CS11 0x0A +#define SW1_CS12 0x0B +#define SW1_CS13 0x0C +#define SW1_CS14 0x0D +#define SW1_CS15 0x0E +#define SW1_CS16 0x0F + +#define SW2_CS1 0x10 +#define SW2_CS2 0x11 +#define SW2_CS3 0x12 +#define SW2_CS4 0x13 +#define SW2_CS5 0x14 +#define SW2_CS6 0x15 +#define SW2_CS7 0x16 +#define SW2_CS8 0x17 +#define SW2_CS9 0x18 +#define SW2_CS10 0x19 +#define SW2_CS11 0x1A +#define SW2_CS12 0x1B +#define SW2_CS13 0x1C +#define SW2_CS14 0x1D +#define SW2_CS15 0x1E +#define SW2_CS16 0x1F + +#define SW3_CS1 0x20 +#define SW3_CS2 0x21 +#define SW3_CS3 0x22 +#define SW3_CS4 0x23 +#define SW3_CS5 0x24 +#define SW3_CS6 0x25 +#define SW3_CS7 0x26 +#define SW3_CS8 0x27 +#define SW3_CS9 0x28 +#define SW3_CS10 0x29 +#define SW3_CS11 0x2A +#define SW3_CS12 0x2B +#define SW3_CS13 0x2C +#define SW3_CS14 0x2D +#define SW3_CS15 0x2E +#define SW3_CS16 0x2F + +#define SW4_CS1 0x30 +#define SW4_CS2 0x31 +#define SW4_CS3 0x32 +#define SW4_CS4 0x33 +#define SW4_CS5 0x34 +#define SW4_CS6 0x35 +#define SW4_CS7 0x36 +#define SW4_CS8 0x37 +#define SW4_CS9 0x38 +#define SW4_CS10 0x39 +#define SW4_CS11 0x3A +#define SW4_CS12 0x3B +#define SW4_CS13 0x3C +#define SW4_CS14 0x3D +#define SW4_CS15 0x3E +#define SW4_CS16 0x3F + +#define SW5_CS1 0x40 +#define SW5_CS2 0x41 +#define SW5_CS3 0x42 +#define SW5_CS4 0x43 +#define SW5_CS5 0x44 +#define SW5_CS6 0x45 +#define SW5_CS7 0x46 +#define SW5_CS8 0x47 +#define SW5_CS9 0x48 +#define SW5_CS10 0x49 +#define SW5_CS11 0x4A +#define SW5_CS12 0x4B +#define SW5_CS13 0x4C +#define SW5_CS14 0x4D +#define SW5_CS15 0x4E +#define SW5_CS16 0x4F + +#define SW6_CS1 0x50 +#define SW6_CS2 0x51 +#define SW6_CS3 0x52 +#define SW6_CS4 0x53 +#define SW6_CS5 0x54 +#define SW6_CS6 0x55 +#define SW6_CS7 0x56 +#define SW6_CS8 0x57 +#define SW6_CS9 0x58 +#define SW6_CS10 0x59 +#define SW6_CS11 0x5A +#define SW6_CS12 0x5B +#define SW6_CS13 0x5C +#define SW6_CS14 0x5D +#define SW6_CS15 0x5E +#define SW6_CS16 0x5F + +#define SW7_CS1 0x60 +#define SW7_CS2 0x61 +#define SW7_CS3 0x62 +#define SW7_CS4 0x63 +#define SW7_CS5 0x64 +#define SW7_CS6 0x65 +#define SW7_CS7 0x66 +#define SW7_CS8 0x67 +#define SW7_CS9 0x68 +#define SW7_CS10 0x69 +#define SW7_CS11 0x6A +#define SW7_CS12 0x6B +#define SW7_CS13 0x6C +#define SW7_CS14 0x6D +#define SW7_CS15 0x6E +#define SW7_CS16 0x6F + +#define SW8_CS1 0x70 +#define SW8_CS2 0x71 +#define SW8_CS3 0x72 +#define SW8_CS4 0x73 +#define SW8_CS5 0x74 +#define SW8_CS6 0x75 +#define SW8_CS7 0x76 +#define SW8_CS8 0x77 +#define SW8_CS9 0x78 +#define SW8_CS10 0x79 +#define SW8_CS11 0x7A +#define SW8_CS12 0x7B +#define SW8_CS13 0x7C +#define SW8_CS14 0x7D +#define SW8_CS15 0x7E +#define SW8_CS16 0x7F + +#define SW9_CS1 0x80 +#define SW9_CS2 0x81 +#define SW9_CS3 0x82 +#define SW9_CS4 0x83 +#define SW9_CS5 0x84 +#define SW9_CS6 0x85 +#define SW9_CS7 0x86 +#define SW9_CS8 0x87 +#define SW9_CS9 0x88 +#define SW9_CS10 0x89 +#define SW9_CS11 0x8A +#define SW9_CS12 0x8B +#define SW9_CS13 0x8C +#define SW9_CS14 0x8D +#define SW9_CS15 0x8E diff --git a/drivers/led/issi/is31fl3731-mono.c b/drivers/led/issi/is31fl3731-mono.c new file mode 100644 index 0000000000..33a863b982 --- /dev/null +++ b/drivers/led/issi/is31fl3731-mono.c @@ -0,0 +1,230 @@ +/* Copyright 2017 Jason Williams + * Copyright 2018 Jack Humbert + * Copyright 2019 Clueboard + * Copyright 2021 Doni Crosby + * + * 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 2 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/>. + */ + +#include "is31fl3731-mono.h" +#include "i2c_master.h" +#include "gpio.h" +#include "wait.h" + +#define IS31FL3731_PWM_REGISTER_COUNT 144 +#define IS31FL3731_LED_CONTROL_REGISTER_COUNT 18 + +#ifndef IS31FL3731_I2C_TIMEOUT +# define IS31FL3731_I2C_TIMEOUT 100 +#endif + +#ifndef IS31FL3731_I2C_PERSISTENCE +# define IS31FL3731_I2C_PERSISTENCE 0 +#endif + +const uint8_t i2c_addresses[IS31FL3731_DRIVER_COUNT] = { + IS31FL3731_I2C_ADDRESS_1, +#ifdef IS31FL3731_I2C_ADDRESS_2 + IS31FL3731_I2C_ADDRESS_2, +# ifdef IS31FL3731_I2C_ADDRESS_3 + IS31FL3731_I2C_ADDRESS_3, +# ifdef IS31FL3731_I2C_ADDRESS_4 + IS31FL3731_I2C_ADDRESS_4, +# endif +# endif +#endif +}; + +// These buffers match the IS31FL3731 PWM registers 0x24-0xB3. +// Storing them like this is optimal for I2C transfers to the registers. +// We could optimize this and take out the unused registers from these +// buffers and the transfers in is31fl3731_write_pwm_buffer() but it's +// probably not worth the extra complexity. +typedef struct is31fl3731_driver_t { + uint8_t pwm_buffer[IS31FL3731_PWM_REGISTER_COUNT]; + bool pwm_buffer_dirty; + uint8_t led_control_buffer[IS31FL3731_LED_CONTROL_REGISTER_COUNT]; + bool led_control_buffer_dirty; +} PACKED is31fl3731_driver_t; + +is31fl3731_driver_t driver_buffers[IS31FL3731_DRIVER_COUNT] = {{ + .pwm_buffer = {0}, + .pwm_buffer_dirty = false, + .led_control_buffer = {0}, + .led_control_buffer_dirty = false, +}}; + +void is31fl3731_write_register(uint8_t index, uint8_t reg, uint8_t data) { +#if IS31FL3731_I2C_PERSISTENCE > 0 + for (uint8_t i = 0; i < IS31FL3731_I2C_PERSISTENCE; i++) { + if (i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3731_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break; + } +#else + i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3731_I2C_TIMEOUT); +#endif +} + +void is31fl3731_select_page(uint8_t index, uint8_t page) { + is31fl3731_write_register(index, IS31FL3731_REG_COMMAND, page); +} + +void is31fl3731_write_pwm_buffer(uint8_t index) { + // Assumes page 0 is already selected. + // Transmit PWM registers in 9 transfers of 16 bytes. + + // Iterate over the pwm_buffer contents at 16 byte intervals. + for (uint8_t i = 0; i < IS31FL3731_PWM_REGISTER_COUNT; i += 16) { +#if IS31FL3731_I2C_PERSISTENCE > 0 + for (uint8_t j = 0; j < IS31FL3731_I2C_PERSISTENCE; j++) { + if (i2c_write_register(i2c_addresses[index] << 1, IS31FL3731_FRAME_REG_PWM + i, driver_buffers[index].pwm_buffer + i, 16, IS31FL3731_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break; + } +#else + i2c_write_register(i2c_addresses[index] << 1, IS31FL3731_FRAME_REG_PWM + i, driver_buffers[index].pwm_buffer + i, 16, IS31FL3731_I2C_TIMEOUT); +#endif + } +} + +void is31fl3731_init_drivers(void) { + i2c_init(); + +#if defined(IS31FL3731_SDB_PIN) + gpio_set_pin_output(IS31FL3731_SDB_PIN); + gpio_write_pin_high(IS31FL3731_SDB_PIN); +#endif + + for (uint8_t i = 0; i < IS31FL3731_DRIVER_COUNT; i++) { + is31fl3731_init(i); + } + + for (int i = 0; i < IS31FL3731_LED_COUNT; i++) { + is31fl3731_set_led_control_register(i, true); + } + + for (uint8_t i = 0; i < IS31FL3731_DRIVER_COUNT; i++) { + is31fl3731_update_led_control_registers(i); + } +} + +void is31fl3731_init(uint8_t index) { + // In order to avoid the LEDs being driven with garbage data + // in the LED driver's PWM registers, first enable software shutdown, + // then set up the mode and other settings, clear the PWM registers, + // then disable software shutdown. + + is31fl3731_select_page(index, IS31FL3731_COMMAND_FUNCTION); + + // enable software shutdown + is31fl3731_write_register(index, IS31FL3731_FUNCTION_REG_SHUTDOWN, 0x00); +#ifdef IS31FL3731_DEGHOST // set to enable de-ghosting of the array + is31fl3731_write_register(index, IS31FL3731_FUNCTION_REG_GHOST_IMAGE_PREVENTION, IS31FL3731_GHOST_IMAGE_PREVENTION_GEN); +#endif + + // this delay was copied from other drivers, might not be needed + wait_ms(10); + + // picture mode + is31fl3731_write_register(index, IS31FL3731_FUNCTION_REG_CONFIG, IS31FL3731_CONFIG_MODE_PICTURE); + // display frame 0 + is31fl3731_write_register(index, IS31FL3731_FUNCTION_REG_PICTURE_DISPLAY, 0x00); + // audio sync off + is31fl3731_write_register(index, IS31FL3731_FUNCTION_REG_AUDIO_SYNC, 0x00); + + is31fl3731_select_page(index, IS31FL3731_COMMAND_FRAME_1); + + // turn off all LEDs in the LED control register + for (uint8_t i = 0; i < IS31FL3731_LED_CONTROL_REGISTER_COUNT; i++) { + is31fl3731_write_register(index, IS31FL3731_FRAME_REG_LED_CONTROL + i, 0x00); + } + + // turn off all LEDs in the blink control register (not really needed) + for (uint8_t i = 0; i < IS31FL3731_LED_CONTROL_REGISTER_COUNT; i++) { + is31fl3731_write_register(index, IS31FL3731_FRAME_REG_BLINK_CONTROL + i, 0x00); + } + + // set PWM on all LEDs to 0 + for (uint8_t i = 0; i < IS31FL3731_PWM_REGISTER_COUNT; i++) { + is31fl3731_write_register(index, IS31FL3731_FRAME_REG_PWM + i, 0x00); + } + + is31fl3731_select_page(index, IS31FL3731_COMMAND_FUNCTION); + + // disable software shutdown + is31fl3731_write_register(index, IS31FL3731_FUNCTION_REG_SHUTDOWN, 0x01); + + // select page 0 and leave it selected. + // most usage after initialization is just writing PWM buffers in page 0 + // as there's not much point in double-buffering + is31fl3731_select_page(index, IS31FL3731_COMMAND_FRAME_1); +} + +void is31fl3731_set_value(int index, uint8_t value) { + is31fl3731_led_t led; + + if (index >= 0 && index < IS31FL3731_LED_COUNT) { + memcpy_P(&led, (&g_is31fl3731_leds[index]), sizeof(led)); + + if (driver_buffers[led.driver].pwm_buffer[led.v] == value) { + return; + } + + driver_buffers[led.driver].pwm_buffer[led.v] = value; + driver_buffers[led.driver].pwm_buffer_dirty = true; + } +} + +void is31fl3731_set_value_all(uint8_t value) { + for (int i = 0; i < IS31FL3731_LED_COUNT; i++) { + is31fl3731_set_value(i, value); + } +} + +void is31fl3731_set_led_control_register(uint8_t index, bool value) { + is31fl3731_led_t led; + memcpy_P(&led, (&g_is31fl3731_leds[index]), sizeof(led)); + + uint8_t control_register = led.v / 8; + uint8_t bit_value = led.v % 8; + + if (value) { + driver_buffers[led.driver].led_control_buffer[control_register] |= (1 << bit_value); + } else { + driver_buffers[led.driver].led_control_buffer[control_register] &= ~(1 << bit_value); + } + + driver_buffers[led.driver].led_control_buffer_dirty = true; +} + +void is31fl3731_update_pwm_buffers(uint8_t index) { + if (driver_buffers[index].pwm_buffer_dirty) { + is31fl3731_write_pwm_buffer(index); + + driver_buffers[index].pwm_buffer_dirty = false; + } +} + +void is31fl3731_update_led_control_registers(uint8_t index) { + if (driver_buffers[index].led_control_buffer_dirty) { + for (uint8_t i = 0; i < IS31FL3731_LED_CONTROL_REGISTER_COUNT; i++) { + is31fl3731_write_register(index, i, driver_buffers[index].led_control_buffer[i]); + } + + driver_buffers[index].led_control_buffer_dirty = false; + } +} + +void is31fl3731_flush(void) { + for (uint8_t i = 0; i < IS31FL3731_DRIVER_COUNT; i++) { + is31fl3731_update_pwm_buffers(i); + } +} diff --git a/drivers/led/issi/is31fl3731-simple.h b/drivers/led/issi/is31fl3731-mono.h index 4d173847dd..e6e107d309 100644 --- a/drivers/led/issi/is31fl3731-simple.h +++ b/drivers/led/issi/is31fl3731-mono.h @@ -61,6 +61,10 @@ #define IS31FL3731_COMMAND_FRAME_8 0x07 #define IS31FL3731_COMMAND_FUNCTION 0x0B +#define IS31FL3731_FRAME_REG_LED_CONTROL 0x00 +#define IS31FL3731_FRAME_REG_BLINK_CONTROL 0x12 +#define IS31FL3731_FRAME_REG_PWM 0x24 + #define IS31FL3731_FUNCTION_REG_CONFIG 0x00 #define IS31FL3731_CONFIG_MODE_PICTURE 0x00 #define IS31FL3731_CONFIG_MODE_AUTO_PLAY 0x08 @@ -101,9 +105,9 @@ typedef struct is31fl3731_led_t { extern const is31fl3731_led_t PROGMEM g_is31fl3731_leds[IS31FL3731_LED_COUNT]; void is31fl3731_init_drivers(void); -void is31fl3731_init(uint8_t addr); -void is31fl3731_write_register(uint8_t addr, uint8_t reg, uint8_t data); -void is31fl3731_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer); +void is31fl3731_init(uint8_t index); +void is31fl3731_write_register(uint8_t index, uint8_t reg, uint8_t data); +void is31fl3731_select_page(uint8_t index, uint8_t page); void is31fl3731_set_value(int index, uint8_t value); void is31fl3731_set_value_all(uint8_t value); @@ -114,169 +118,169 @@ void is31fl3731_set_led_control_register(uint8_t index, bool value); // (eg. from a timer interrupt). // Call this while idle (in between matrix scans). // If the buffer is dirty, it will update the driver with the buffer. -void is31fl3731_update_pwm_buffers(uint8_t addr, uint8_t index); -void is31fl3731_update_led_control_registers(uint8_t addr, uint8_t index); +void is31fl3731_update_pwm_buffers(uint8_t index); +void is31fl3731_update_led_control_registers(uint8_t index); void is31fl3731_flush(void); -#define C1_1 0x24 -#define C1_2 0x25 -#define C1_3 0x26 -#define C1_4 0x27 -#define C1_5 0x28 -#define C1_6 0x29 -#define C1_7 0x2A -#define C1_8 0x2B - -#define C1_9 0x2C -#define C1_10 0x2D -#define C1_11 0x2E -#define C1_12 0x2F -#define C1_13 0x30 -#define C1_14 0x31 -#define C1_15 0x32 -#define C1_16 0x33 - -#define C2_1 0x34 -#define C2_2 0x35 -#define C2_3 0x36 -#define C2_4 0x37 -#define C2_5 0x38 -#define C2_6 0x39 -#define C2_7 0x3A -#define C2_8 0x3B - -#define C2_9 0x3C -#define C2_10 0x3D -#define C2_11 0x3E -#define C2_12 0x3F -#define C2_13 0x40 -#define C2_14 0x41 -#define C2_15 0x42 -#define C2_16 0x43 - -#define C3_1 0x44 -#define C3_2 0x45 -#define C3_3 0x46 -#define C3_4 0x47 -#define C3_5 0x48 -#define C3_6 0x49 -#define C3_7 0x4A -#define C3_8 0x4B - -#define C3_9 0x4C -#define C3_10 0x4D -#define C3_11 0x4E -#define C3_12 0x4F -#define C3_13 0x50 -#define C3_14 0x51 -#define C3_15 0x52 -#define C3_16 0x53 - -#define C4_1 0x54 -#define C4_2 0x55 -#define C4_3 0x56 -#define C4_4 0x57 -#define C4_5 0x58 -#define C4_6 0x59 -#define C4_7 0x5A -#define C4_8 0x5B - -#define C4_9 0x5C -#define C4_10 0x5D -#define C4_11 0x5E -#define C4_12 0x5F -#define C4_13 0x60 -#define C4_14 0x61 -#define C4_15 0x62 -#define C4_16 0x63 - -#define C5_1 0x64 -#define C5_2 0x65 -#define C5_3 0x66 -#define C5_4 0x67 -#define C5_5 0x68 -#define C5_6 0x69 -#define C5_7 0x6A -#define C5_8 0x6B - -#define C5_9 0x6C -#define C5_10 0x6D -#define C5_11 0x6E -#define C5_12 0x6F -#define C5_13 0x70 -#define C5_14 0x71 -#define C5_15 0x72 -#define C5_16 0x73 - -#define C6_1 0x74 -#define C6_2 0x75 -#define C6_3 0x76 -#define C6_4 0x77 -#define C6_5 0x78 -#define C6_6 0x79 -#define C6_7 0x7A -#define C6_8 0x7B - -#define C6_9 0x7C -#define C6_10 0x7D -#define C6_11 0x7E -#define C6_12 0x7F -#define C6_13 0x80 -#define C6_14 0x81 -#define C6_15 0x82 -#define C6_16 0x83 - -#define C7_1 0x84 -#define C7_2 0x85 -#define C7_3 0x86 -#define C7_4 0x87 -#define C7_5 0x88 -#define C7_6 0x89 -#define C7_7 0x8A -#define C7_8 0x8B - -#define C7_9 0x8C -#define C7_10 0x8D -#define C7_11 0x8E -#define C7_12 0x8F -#define C7_13 0x90 -#define C7_14 0x91 -#define C7_15 0x92 -#define C7_16 0x93 - -#define C8_1 0x94 -#define C8_2 0x95 -#define C8_3 0x96 -#define C8_4 0x97 -#define C8_5 0x98 -#define C8_6 0x99 -#define C8_7 0x9A -#define C8_8 0x9B - -#define C8_9 0x9C -#define C8_10 0x9D -#define C8_11 0x9E -#define C8_12 0x9F -#define C8_13 0xA0 -#define C8_14 0xA1 -#define C8_15 0xA2 -#define C8_16 0xA3 - -#define C9_1 0xA4 -#define C9_2 0xA5 -#define C9_3 0xA6 -#define C9_4 0xA7 -#define C9_5 0xA8 -#define C9_6 0xA9 -#define C9_7 0xAA -#define C9_8 0xAB - -#define C9_9 0xAC -#define C9_10 0xAD -#define C9_11 0xAE -#define C9_12 0xAF -#define C9_13 0xB0 -#define C9_14 0xB1 -#define C9_15 0xB2 -#define C9_16 0xB3 +#define C1_1 0x00 +#define C1_2 0x01 +#define C1_3 0x02 +#define C1_4 0x03 +#define C1_5 0x04 +#define C1_6 0x05 +#define C1_7 0x06 +#define C1_8 0x07 + +#define C1_9 0x08 +#define C1_10 0x09 +#define C1_11 0x0A +#define C1_12 0x0B +#define C1_13 0x0C +#define C1_14 0x0D +#define C1_15 0x0E +#define C1_16 0x0F + +#define C2_1 0x10 +#define C2_2 0x11 +#define C2_3 0x12 +#define C2_4 0x13 +#define C2_5 0x14 +#define C2_6 0x15 +#define C2_7 0x16 +#define C2_8 0x17 + +#define C2_9 0x18 +#define C2_10 0x19 +#define C2_11 0x1A +#define C2_12 0x1B +#define C2_13 0x1C +#define C2_14 0x1D +#define C2_15 0x1E +#define C2_16 0x1F + +#define C3_1 0x20 +#define C3_2 0x21 +#define C3_3 0x22 +#define C3_4 0x23 +#define C3_5 0x24 +#define C3_6 0x25 +#define C3_7 0x26 +#define C3_8 0x27 + +#define C3_9 0x28 +#define C3_10 0x29 +#define C3_11 0x2A +#define C3_12 0x2B +#define C3_13 0x2C +#define C3_14 0x2D +#define C3_15 0x2E +#define C3_16 0x2F + +#define C4_1 0x30 +#define C4_2 0x31 +#define C4_3 0x32 +#define C4_4 0x33 +#define C4_5 0x34 +#define C4_6 0x35 +#define C4_7 0x36 +#define C4_8 0x37 + +#define C4_9 0x38 +#define C4_10 0x39 +#define C4_11 0x3A +#define C4_12 0x3B +#define C4_13 0x3C +#define C4_14 0x3D +#define C4_15 0x3E +#define C4_16 0x3F + +#define C5_1 0x40 +#define C5_2 0x41 +#define C5_3 0x42 +#define C5_4 0x43 +#define C5_5 0x44 +#define C5_6 0x45 +#define C5_7 0x46 +#define C5_8 0x47 + +#define C5_9 0x48 +#define C5_10 0x49 +#define C5_11 0x4A +#define C5_12 0x4B +#define C5_13 0x4C +#define C5_14 0x4D +#define C5_15 0x4E +#define C5_16 0x4F + +#define C6_1 0x50 +#define C6_2 0x51 +#define C6_3 0x52 +#define C6_4 0x53 +#define C6_5 0x54 +#define C6_6 0x55 +#define C6_7 0x56 +#define C6_8 0x57 + +#define C6_9 0x58 +#define C6_10 0x59 +#define C6_11 0x5A +#define C6_12 0x5B +#define C6_13 0x5C +#define C6_14 0x5D +#define C6_15 0x5E +#define C6_16 0x5F + +#define C7_1 0x60 +#define C7_2 0x61 +#define C7_3 0x62 +#define C7_4 0x63 +#define C7_5 0x64 +#define C7_6 0x65 +#define C7_7 0x66 +#define C7_8 0x67 + +#define C7_9 0x68 +#define C7_10 0x69 +#define C7_11 0x6A +#define C7_12 0x6B +#define C7_13 0x6C +#define C7_14 0x6D +#define C7_15 0x6E +#define C7_16 0x6F + +#define C8_1 0x70 +#define C8_2 0x71 +#define C8_3 0x72 +#define C8_4 0x73 +#define C8_5 0x74 +#define C8_6 0x75 +#define C8_7 0x76 +#define C8_8 0x77 + +#define C8_9 0x78 +#define C8_10 0x79 +#define C8_11 0x7A +#define C8_12 0x7B +#define C8_13 0x7C +#define C8_14 0x7D +#define C8_15 0x7E +#define C8_16 0x7F + +#define C9_1 0x80 +#define C9_2 0x81 +#define C9_3 0x82 +#define C9_4 0x83 +#define C9_5 0x84 +#define C9_6 0x85 +#define C9_7 0x86 +#define C9_8 0x87 + +#define C9_9 0x88 +#define C9_10 0x89 +#define C9_11 0x8A +#define C9_12 0x8B +#define C9_13 0x8C +#define C9_14 0x8D +#define C9_15 0x8E +#define C9_16 0x8F diff --git a/drivers/led/issi/is31fl3731-simple.c b/drivers/led/issi/is31fl3731-simple.c deleted file mode 100644 index 8dbfc3cd31..0000000000 --- a/drivers/led/issi/is31fl3731-simple.c +++ /dev/null @@ -1,239 +0,0 @@ -/* Copyright 2017 Jason Williams - * Copyright 2018 Jack Humbert - * Copyright 2019 Clueboard - * Copyright 2021 Doni Crosby - * - * 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 2 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/>. - */ - -#include "is31fl3731-simple.h" -#include <string.h> -#include "i2c_master.h" -#include "wait.h" - -#define IS31FL3731_PWM_REGISTER_COUNT 144 -#define IS31FL3731_LED_CONTROL_REGISTER_COUNT 18 - -#ifndef IS31FL3731_I2C_TIMEOUT -# define IS31FL3731_I2C_TIMEOUT 100 -#endif - -#ifndef IS31FL3731_I2C_PERSISTENCE -# define IS31FL3731_I2C_PERSISTENCE 0 -#endif - -// Transfer buffer for TWITransmitData() -uint8_t g_twi_transfer_buffer[20]; - -// These buffers match the IS31FL3731 PWM registers 0x24-0xB3. -// Storing them like this is optimal for I2C transfers to the registers. -// We could optimize this and take out the unused registers from these -// buffers and the transfers in is31fl3731_write_pwm_buffer() but it's -// probably not worth the extra complexity. -uint8_t g_pwm_buffer[IS31FL3731_DRIVER_COUNT][IS31FL3731_PWM_REGISTER_COUNT]; -bool g_pwm_buffer_update_required[IS31FL3731_DRIVER_COUNT] = {false}; - -uint8_t g_led_control_registers[IS31FL3731_DRIVER_COUNT][IS31FL3731_LED_CONTROL_REGISTER_COUNT] = {0}; -bool g_led_control_registers_update_required[IS31FL3731_DRIVER_COUNT] = {false}; - -void is31fl3731_write_register(uint8_t addr, uint8_t reg, uint8_t data) { - g_twi_transfer_buffer[0] = reg; - g_twi_transfer_buffer[1] = data; - -#if IS31FL3731_I2C_PERSISTENCE > 0 - for (uint8_t i = 0; i < IS31FL3731_I2C_PERSISTENCE; i++) { - if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, IS31FL3731_I2C_TIMEOUT) == 0) { - break; - } - } -#else - i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, IS31FL3731_I2C_TIMEOUT); -#endif -} - -void is31fl3731_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer) { - // assumes bank is already selected - - // transmit PWM registers in 9 transfers of 16 bytes - // g_twi_transfer_buffer[] is 20 bytes - - // iterate over the pwm_buffer contents at 16 byte intervals - for (int i = 0; i < IS31FL3731_PWM_REGISTER_COUNT; i += 16) { - // set the first register, e.g. 0x24, 0x34, 0x44, etc. - g_twi_transfer_buffer[0] = 0x24 + i; - // copy the data from i to i+15 - // device will auto-increment register for data after the first byte - // thus this sets registers 0x24-0x33, 0x34-0x43, etc. in one transfer - memcpy(g_twi_transfer_buffer + 1, pwm_buffer + i, 16); - -#if IS31FL3731_I2C_PERSISTENCE > 0 - for (uint8_t i = 0; i < IS31FL3731_I2C_PERSISTENCE; i++) { - if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 17, IS31FL3731_I2C_TIMEOUT) == 0) break; - } -#else - i2c_transmit(addr << 1, g_twi_transfer_buffer, 17, IS31FL3731_I2C_TIMEOUT); -#endif - } -} - -void is31fl3731_init_drivers(void) { - i2c_init(); - - is31fl3731_init(IS31FL3731_I2C_ADDRESS_1); -#if defined(IS31FL3731_I2C_ADDRESS_2) - is31fl3731_init(IS31FL3731_I2C_ADDRESS_2); -# if defined(IS31FL3731_I2C_ADDRESS_3) - is31fl3731_init(IS31FL3731_I2C_ADDRESS_3); -# if defined(IS31FL3731_I2C_ADDRESS_4) - is31fl3731_init(IS31FL3731_I2C_ADDRESS_4); -# endif -# endif -#endif - - for (int i = 0; i < IS31FL3731_LED_COUNT; i++) { - is31fl3731_set_led_control_register(i, true); - } - - is31fl3731_update_led_control_registers(IS31FL3731_I2C_ADDRESS_1, 0); -#if defined(IS31FL3731_I2C_ADDRESS_2) - is31fl3731_update_led_control_registers(IS31FL3731_I2C_ADDRESS_2, 1); -# if defined(IS31FL3731_I2C_ADDRESS_3) - is31fl3731_update_led_control_registers(IS31FL3731_I2C_ADDRESS_3, 2); -# if defined(IS31FL3731_I2C_ADDRESS_4) - is31fl3731_update_led_control_registers(IS31FL3731_I2C_ADDRESS_4, 3); -# endif -# endif -#endif -} - -void is31fl3731_init(uint8_t addr) { - // In order to avoid the LEDs being driven with garbage data - // in the LED driver's PWM registers, first enable software shutdown, - // then set up the mode and other settings, clear the PWM registers, - // then disable software shutdown. - - // select "function register" bank - is31fl3731_write_register(addr, IS31FL3731_REG_COMMAND, IS31FL3731_COMMAND_FUNCTION); - - // enable software shutdown - is31fl3731_write_register(addr, IS31FL3731_FUNCTION_REG_SHUTDOWN, 0x00); -#ifdef IS31FL3731_DEGHOST // set to enable de-ghosting of the array - is31fl3731_write_register(addr, IS31FL3731_FUNCTION_REG_GHOST_IMAGE_PREVENTION, IS31FL3731_GHOST_IMAGE_PREVENTION_GEN); -#endif - - // this delay was copied from other drivers, might not be needed - wait_ms(10); - - // picture mode - is31fl3731_write_register(addr, IS31FL3731_FUNCTION_REG_CONFIG, IS31FL3731_CONFIG_MODE_PICTURE); - // display frame 0 - is31fl3731_write_register(addr, IS31FL3731_FUNCTION_REG_PICTURE_DISPLAY, 0x00); - // audio sync off - is31fl3731_write_register(addr, IS31FL3731_FUNCTION_REG_AUDIO_SYNC, 0x00); - - // select bank 0 - is31fl3731_write_register(addr, IS31FL3731_REG_COMMAND, IS31FL3731_COMMAND_FRAME_1); - - // turn off all LEDs in the LED control register - for (int i = 0; i < IS31FL3731_LED_CONTROL_REGISTER_COUNT; i++) { - is31fl3731_write_register(addr, i, 0x00); - } - - // turn off all LEDs in the blink control register (not really needed) - for (int i = 0x12; i <= 0x23; i++) { - is31fl3731_write_register(addr, i, 0x00); - } - - // set PWM on all LEDs to 0 - for (int i = 0x24; i <= 0xB3; i++) { - is31fl3731_write_register(addr, i, 0x00); - } - - // select "function register" bank - is31fl3731_write_register(addr, IS31FL3731_REG_COMMAND, IS31FL3731_COMMAND_FUNCTION); - - // disable software shutdown - is31fl3731_write_register(addr, IS31FL3731_FUNCTION_REG_SHUTDOWN, 0x01); - - // select bank 0 and leave it selected. - // most usage after initialization is just writing PWM buffers in bank 0 - // as there's not much point in double-buffering - is31fl3731_write_register(addr, IS31FL3731_REG_COMMAND, IS31FL3731_COMMAND_FRAME_1); -} - -void is31fl3731_set_value(int index, uint8_t value) { - is31fl3731_led_t led; - if (index >= 0 && index < IS31FL3731_LED_COUNT) { - memcpy_P(&led, (&g_is31fl3731_leds[index]), sizeof(led)); - - // Subtract 0x24 to get the second index of g_pwm_buffer - - if (g_pwm_buffer[led.driver][led.v - 0x24] == value) { - return; - } - g_pwm_buffer[led.driver][led.v - 0x24] = value; - g_pwm_buffer_update_required[led.driver] = true; - } -} - -void is31fl3731_set_value_all(uint8_t value) { - for (int i = 0; i < IS31FL3731_LED_COUNT; i++) { - is31fl3731_set_value(i, value); - } -} - -void is31fl3731_set_led_control_register(uint8_t index, bool value) { - is31fl3731_led_t led; - memcpy_P(&led, (&g_is31fl3731_leds[index]), sizeof(led)); - - uint8_t control_register = (led.v - 0x24) / 8; - uint8_t bit_value = (led.v - 0x24) % 8; - - if (value) { - g_led_control_registers[led.driver][control_register] |= (1 << bit_value); - } else { - g_led_control_registers[led.driver][control_register] &= ~(1 << bit_value); - } - - g_led_control_registers_update_required[led.driver] = true; -} - -void is31fl3731_update_pwm_buffers(uint8_t addr, uint8_t index) { - if (g_pwm_buffer_update_required[index]) { - is31fl3731_write_pwm_buffer(addr, g_pwm_buffer[index]); - g_pwm_buffer_update_required[index] = false; - } -} - -void is31fl3731_update_led_control_registers(uint8_t addr, uint8_t index) { - if (g_led_control_registers_update_required[index]) { - for (int i = 0; i < IS31FL3731_LED_CONTROL_REGISTER_COUNT; i++) { - is31fl3731_write_register(addr, i, g_led_control_registers[index][i]); - } - g_led_control_registers_update_required[index] = false; - } -} - -void is31fl3731_flush(void) { - is31fl3731_update_pwm_buffers(IS31FL3731_I2C_ADDRESS_1, 0); -#if defined(IS31FL3731_I2C_ADDRESS_2) - is31fl3731_update_pwm_buffers(IS31FL3731_I2C_ADDRESS_2, 1); -# if defined(IS31FL3731_I2C_ADDRESS_3) - is31fl3731_update_pwm_buffers(IS31FL3731_I2C_ADDRESS_3, 2); -# if defined(IS31FL3731_I2C_ADDRESS_4) - is31fl3731_update_pwm_buffers(IS31FL3731_I2C_ADDRESS_4, 3); -# endif -# endif -#endif -} diff --git a/drivers/led/issi/is31fl3731.c b/drivers/led/issi/is31fl3731.c index 1ab8997731..86d953ef25 100644 --- a/drivers/led/issi/is31fl3731.c +++ b/drivers/led/issi/is31fl3731.c @@ -17,8 +17,8 @@ */ #include "is31fl3731.h" -#include <string.h> #include "i2c_master.h" +#include "gpio.h" #include "wait.h" #define IS31FL3731_PWM_REGISTER_COUNT 144 @@ -32,54 +32,64 @@ # define IS31FL3731_I2C_PERSISTENCE 0 #endif -// Transfer buffer for TWITransmitData() -uint8_t g_twi_transfer_buffer[20]; +const uint8_t i2c_addresses[IS31FL3731_DRIVER_COUNT] = { + IS31FL3731_I2C_ADDRESS_1, +#ifdef IS31FL3731_I2C_ADDRESS_2 + IS31FL3731_I2C_ADDRESS_2, +# ifdef IS31FL3731_I2C_ADDRESS_3 + IS31FL3731_I2C_ADDRESS_3, +# ifdef IS31FL3731_I2C_ADDRESS_4 + IS31FL3731_I2C_ADDRESS_4, +# endif +# endif +#endif +}; // These buffers match the IS31FL3731 PWM registers 0x24-0xB3. // Storing them like this is optimal for I2C transfers to the registers. // We could optimize this and take out the unused registers from these // buffers and the transfers in is31fl3731_write_pwm_buffer() but it's // probably not worth the extra complexity. -uint8_t g_pwm_buffer[IS31FL3731_DRIVER_COUNT][IS31FL3731_PWM_REGISTER_COUNT]; -bool g_pwm_buffer_update_required[IS31FL3731_DRIVER_COUNT] = {false}; - -uint8_t g_led_control_registers[IS31FL3731_DRIVER_COUNT][IS31FL3731_LED_CONTROL_REGISTER_COUNT] = {0}; -bool g_led_control_registers_update_required[IS31FL3731_DRIVER_COUNT] = {false}; - -void is31fl3731_write_register(uint8_t addr, uint8_t reg, uint8_t data) { - g_twi_transfer_buffer[0] = reg; - g_twi_transfer_buffer[1] = data; - +typedef struct is31fl3731_driver_t { + uint8_t pwm_buffer[IS31FL3731_PWM_REGISTER_COUNT]; + bool pwm_buffer_dirty; + uint8_t led_control_buffer[IS31FL3731_LED_CONTROL_REGISTER_COUNT]; + bool led_control_buffer_dirty; +} PACKED is31fl3731_driver_t; + +is31fl3731_driver_t driver_buffers[IS31FL3731_DRIVER_COUNT] = {{ + .pwm_buffer = {0}, + .pwm_buffer_dirty = false, + .led_control_buffer = {0}, + .led_control_buffer_dirty = false, +}}; + +void is31fl3731_write_register(uint8_t index, uint8_t reg, uint8_t data) { #if IS31FL3731_I2C_PERSISTENCE > 0 for (uint8_t i = 0; i < IS31FL3731_I2C_PERSISTENCE; i++) { - if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, IS31FL3731_I2C_TIMEOUT) == 0) break; + if (i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3731_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break; } #else - i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, IS31FL3731_I2C_TIMEOUT); + i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3731_I2C_TIMEOUT); #endif } -void is31fl3731_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer) { - // assumes bank is already selected - - // transmit PWM registers in 9 transfers of 16 bytes - // g_twi_transfer_buffer[] is 20 bytes +void is31fl3731_select_page(uint8_t index, uint8_t page) { + is31fl3731_write_register(index, IS31FL3731_REG_COMMAND, page); +} - // iterate over the pwm_buffer contents at 16 byte intervals - for (int i = 0; i < IS31FL3731_PWM_REGISTER_COUNT; i += 16) { - // set the first register, e.g. 0x24, 0x34, 0x44, etc. - g_twi_transfer_buffer[0] = 0x24 + i; - // copy the data from i to i+15 - // device will auto-increment register for data after the first byte - // thus this sets registers 0x24-0x33, 0x34-0x43, etc. in one transfer - memcpy(g_twi_transfer_buffer + 1, pwm_buffer + i, 16); +void is31fl3731_write_pwm_buffer(uint8_t index) { + // Assumes page 0 is already selected. + // Transmit PWM registers in 9 transfers of 16 bytes. + // Iterate over the pwm_buffer contents at 16 byte intervals. + for (uint8_t i = 0; i < IS31FL3731_PWM_REGISTER_COUNT; i += 16) { #if IS31FL3731_I2C_PERSISTENCE > 0 - for (uint8_t i = 0; i < IS31FL3731_I2C_PERSISTENCE; i++) { - if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 17, IS31FL3731_I2C_TIMEOUT) == 0) break; + for (uint8_t j = 0; j < IS31FL3731_I2C_PERSISTENCE; j++) { + if (i2c_write_register(i2c_addresses[index] << 1, IS31FL3731_FRAME_REG_PWM + i, driver_buffers[index].pwm_buffer + i, 16, IS31FL3731_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break; } #else - i2c_transmit(addr << 1, g_twi_transfer_buffer, 17, IS31FL3731_I2C_TIMEOUT); + i2c_write_register(i2c_addresses[index] << 1, IS31FL3731_FRAME_REG_PWM + i, driver_buffers[index].pwm_buffer + i, 16, IS31FL3731_I2C_TIMEOUT); #endif } } @@ -87,101 +97,90 @@ void is31fl3731_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer) { void is31fl3731_init_drivers(void) { i2c_init(); - is31fl3731_init(IS31FL3731_I2C_ADDRESS_1); -#if defined(IS31FL3731_I2C_ADDRESS_2) - is31fl3731_init(IS31FL3731_I2C_ADDRESS_2); -# if defined(IS31FL3731_I2C_ADDRESS_3) - is31fl3731_init(IS31FL3731_I2C_ADDRESS_3); -# if defined(IS31FL3731_I2C_ADDRESS_4) - is31fl3731_init(IS31FL3731_I2C_ADDRESS_4); -# endif -# endif +#if defined(IS31FL3731_SDB_PIN) + gpio_set_pin_output(IS31FL3731_SDB_PIN); + gpio_write_pin_high(IS31FL3731_SDB_PIN); #endif + for (uint8_t i = 0; i < IS31FL3731_DRIVER_COUNT; i++) { + is31fl3731_init(i); + } + for (int i = 0; i < IS31FL3731_LED_COUNT; i++) { is31fl3731_set_led_control_register(i, true, true, true); } - is31fl3731_update_led_control_registers(IS31FL3731_I2C_ADDRESS_1, 0); -#if defined(IS31FL3731_I2C_ADDRESS_2) - is31fl3731_update_led_control_registers(IS31FL3731_I2C_ADDRESS_2, 1); -# if defined(IS31FL3731_I2C_ADDRESS_3) - is31fl3731_update_led_control_registers(IS31FL3731_I2C_ADDRESS_3, 2); -# if defined(IS31FL3731_I2C_ADDRESS_4) - is31fl3731_update_led_control_registers(IS31FL3731_I2C_ADDRESS_4, 3); -# endif -# endif -#endif + for (uint8_t i = 0; i < IS31FL3731_DRIVER_COUNT; i++) { + is31fl3731_update_led_control_registers(i); + } } -void is31fl3731_init(uint8_t addr) { +void is31fl3731_init(uint8_t index) { // In order to avoid the LEDs being driven with garbage data // in the LED driver's PWM registers, first enable software shutdown, // then set up the mode and other settings, clear the PWM registers, // then disable software shutdown. - // select "function register" bank - is31fl3731_write_register(addr, IS31FL3731_REG_COMMAND, IS31FL3731_COMMAND_FUNCTION); + is31fl3731_select_page(index, IS31FL3731_COMMAND_FUNCTION); // enable software shutdown - is31fl3731_write_register(addr, IS31FL3731_FUNCTION_REG_SHUTDOWN, 0x00); + is31fl3731_write_register(index, IS31FL3731_FUNCTION_REG_SHUTDOWN, 0x00); #ifdef IS31FL3731_DEGHOST // set to enable de-ghosting of the array - is31fl3731_write_register(addr, IS31FL3731_FUNCTION_REG_GHOST_IMAGE_PREVENTION, IS31FL3731_GHOST_IMAGE_PREVENTION_GEN); + is31fl3731_write_register(index, IS31FL3731_FUNCTION_REG_GHOST_IMAGE_PREVENTION, IS31FL3731_GHOST_IMAGE_PREVENTION_GEN); #endif // this delay was copied from other drivers, might not be needed wait_ms(10); // picture mode - is31fl3731_write_register(addr, IS31FL3731_FUNCTION_REG_CONFIG, IS31FL3731_CONFIG_MODE_PICTURE); + is31fl3731_write_register(index, IS31FL3731_FUNCTION_REG_CONFIG, IS31FL3731_CONFIG_MODE_PICTURE); // display frame 0 - is31fl3731_write_register(addr, IS31FL3731_FUNCTION_REG_PICTURE_DISPLAY, 0x00); + is31fl3731_write_register(index, IS31FL3731_FUNCTION_REG_PICTURE_DISPLAY, 0x00); // audio sync off - is31fl3731_write_register(addr, IS31FL3731_FUNCTION_REG_AUDIO_SYNC, 0x00); + is31fl3731_write_register(index, IS31FL3731_FUNCTION_REG_AUDIO_SYNC, 0x00); - // select bank 0 - is31fl3731_write_register(addr, IS31FL3731_REG_COMMAND, IS31FL3731_COMMAND_FRAME_1); + is31fl3731_select_page(index, IS31FL3731_COMMAND_FRAME_1); // turn off all LEDs in the LED control register - for (int i = 0; i < IS31FL3731_LED_CONTROL_REGISTER_COUNT; i++) { - is31fl3731_write_register(addr, i, 0x00); + for (uint8_t i = 0; i < IS31FL3731_LED_CONTROL_REGISTER_COUNT; i++) { + is31fl3731_write_register(index, IS31FL3731_FRAME_REG_LED_CONTROL + i, 0x00); } // turn off all LEDs in the blink control register (not really needed) - for (int i = 0x12; i <= 0x23; i++) { - is31fl3731_write_register(addr, i, 0x00); + for (uint8_t i = 0; i < IS31FL3731_LED_CONTROL_REGISTER_COUNT; i++) { + is31fl3731_write_register(index, IS31FL3731_FRAME_REG_BLINK_CONTROL + i, 0x00); } // set PWM on all LEDs to 0 - for (int i = 0x24; i <= 0xB3; i++) { - is31fl3731_write_register(addr, i, 0x00); + for (uint8_t i = 0; i < IS31FL3731_PWM_REGISTER_COUNT; i++) { + is31fl3731_write_register(index, IS31FL3731_FRAME_REG_PWM + i, 0x00); } - // select "function register" bank - is31fl3731_write_register(addr, IS31FL3731_REG_COMMAND, IS31FL3731_COMMAND_FUNCTION); + is31fl3731_select_page(index, IS31FL3731_COMMAND_FUNCTION); // disable software shutdown - is31fl3731_write_register(addr, IS31FL3731_FUNCTION_REG_SHUTDOWN, 0x01); + is31fl3731_write_register(index, IS31FL3731_FUNCTION_REG_SHUTDOWN, 0x01); - // select bank 0 and leave it selected. - // most usage after initialization is just writing PWM buffers in bank 0 + // select page 0 and leave it selected. + // most usage after initialization is just writing PWM buffers in page 0 // as there's not much point in double-buffering - is31fl3731_write_register(addr, IS31FL3731_REG_COMMAND, IS31FL3731_COMMAND_FRAME_1); + is31fl3731_select_page(index, IS31FL3731_COMMAND_FRAME_1); } void is31fl3731_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) { is31fl3731_led_t led; + if (index >= 0 && index < IS31FL3731_LED_COUNT) { memcpy_P(&led, (&g_is31fl3731_leds[index]), sizeof(led)); - // Subtract 0x24 to get the second index of g_pwm_buffer - if (g_pwm_buffer[led.driver][led.r - 0x24] == red && g_pwm_buffer[led.driver][led.g - 0x24] == green && g_pwm_buffer[led.driver][led.b - 0x24] == blue) { + if (driver_buffers[led.driver].pwm_buffer[led.r] == red && driver_buffers[led.driver].pwm_buffer[led.g] == green && driver_buffers[led.driver].pwm_buffer[led.b] == blue) { return; } - g_pwm_buffer[led.driver][led.r - 0x24] = red; - g_pwm_buffer[led.driver][led.g - 0x24] = green; - g_pwm_buffer[led.driver][led.b - 0x24] = blue; - g_pwm_buffer_update_required[led.driver] = true; + + driver_buffers[led.driver].pwm_buffer[led.r] = red; + driver_buffers[led.driver].pwm_buffer[led.g] = green; + driver_buffers[led.driver].pwm_buffer[led.b] = blue; + driver_buffers[led.driver].pwm_buffer_dirty = true; } } @@ -195,57 +194,52 @@ void is31fl3731_set_led_control_register(uint8_t index, bool red, bool green, bo is31fl3731_led_t led; memcpy_P(&led, (&g_is31fl3731_leds[index]), sizeof(led)); - uint8_t control_register_r = (led.r - 0x24) / 8; - uint8_t control_register_g = (led.g - 0x24) / 8; - uint8_t control_register_b = (led.b - 0x24) / 8; - uint8_t bit_r = (led.r - 0x24) % 8; - uint8_t bit_g = (led.g - 0x24) % 8; - uint8_t bit_b = (led.b - 0x24) % 8; + uint8_t control_register_r = led.r / 8; + uint8_t control_register_g = led.g / 8; + uint8_t control_register_b = led.b / 8; + uint8_t bit_r = led.r % 8; + uint8_t bit_g = led.g % 8; + uint8_t bit_b = led.b % 8; if (red) { - g_led_control_registers[led.driver][control_register_r] |= (1 << bit_r); + driver_buffers[led.driver].led_control_buffer[control_register_r] |= (1 << bit_r); } else { - g_led_control_registers[led.driver][control_register_r] &= ~(1 << bit_r); + driver_buffers[led.driver].led_control_buffer[control_register_r] &= ~(1 << bit_r); } if (green) { - g_led_control_registers[led.driver][control_register_g] |= (1 << bit_g); + driver_buffers[led.driver].led_control_buffer[control_register_g] |= (1 << bit_g); } else { - g_led_control_registers[led.driver][control_register_g] &= ~(1 << bit_g); + driver_buffers[led.driver].led_control_buffer[control_register_g] &= ~(1 << bit_g); } if (blue) { - g_led_control_registers[led.driver][control_register_b] |= (1 << bit_b); + driver_buffers[led.driver].led_control_buffer[control_register_b] |= (1 << bit_b); } else { - g_led_control_registers[led.driver][control_register_b] &= ~(1 << bit_b); + driver_buffers[led.driver].led_control_buffer[control_register_b] &= ~(1 << bit_b); } - g_led_control_registers_update_required[led.driver] = true; + driver_buffers[led.driver].led_control_buffer_dirty = true; } -void is31fl3731_update_pwm_buffers(uint8_t addr, uint8_t index) { - if (g_pwm_buffer_update_required[index]) { - is31fl3731_write_pwm_buffer(addr, g_pwm_buffer[index]); +void is31fl3731_update_pwm_buffers(uint8_t index) { + if (driver_buffers[index].pwm_buffer_dirty) { + is31fl3731_write_pwm_buffer(index); + + driver_buffers[index].pwm_buffer_dirty = false; } - g_pwm_buffer_update_required[index] = false; } -void is31fl3731_update_led_control_registers(uint8_t addr, uint8_t index) { - if (g_led_control_registers_update_required[index]) { - for (int i = 0; i < IS31FL3731_LED_CONTROL_REGISTER_COUNT; i++) { - is31fl3731_write_register(addr, i, g_led_control_registers[index][i]); +void is31fl3731_update_led_control_registers(uint8_t index) { + if (driver_buffers[index].led_control_buffer_dirty) { + for (uint8_t i = 0; i < IS31FL3731_LED_CONTROL_REGISTER_COUNT; i++) { + is31fl3731_write_register(index, i, driver_buffers[index].led_control_buffer[i]); } + + driver_buffers[index].led_control_buffer_dirty = false; } - g_led_control_registers_update_required[index] = false; } void is31fl3731_flush(void) { - is31fl3731_update_pwm_buffers(IS31FL3731_I2C_ADDRESS_1, 0); -#if defined(IS31FL3731_I2C_ADDRESS_2) - is31fl3731_update_pwm_buffers(IS31FL3731_I2C_ADDRESS_2, 1); -# if defined(IS31FL3731_I2C_ADDRESS_3) - is31fl3731_update_pwm_buffers(IS31FL3731_I2C_ADDRESS_3, 2); -# if defined(IS31FL3731_I2C_ADDRESS_4) - is31fl3731_update_pwm_buffers(IS31FL3731_I2C_ADDRESS_4, 3); -# endif -# endif -#endif + for (uint8_t i = 0; i < IS31FL3731_DRIVER_COUNT; i++) { + is31fl3731_update_pwm_buffers(i); + } } diff --git a/drivers/led/issi/is31fl3731.h b/drivers/led/issi/is31fl3731.h index b45cb2b07d..dc229c876d 100644 --- a/drivers/led/issi/is31fl3731.h +++ b/drivers/led/issi/is31fl3731.h @@ -60,6 +60,10 @@ #define IS31FL3731_COMMAND_FRAME_8 0x07 #define IS31FL3731_COMMAND_FUNCTION 0x0B +#define IS31FL3731_FRAME_REG_LED_CONTROL 0x00 +#define IS31FL3731_FRAME_REG_BLINK_CONTROL 0x12 +#define IS31FL3731_FRAME_REG_PWM 0x24 + #define IS31FL3731_FUNCTION_REG_CONFIG 0x00 #define IS31FL3731_CONFIG_MODE_PICTURE 0x00 #define IS31FL3731_CONFIG_MODE_AUTO_PLAY 0x08 @@ -102,9 +106,9 @@ typedef struct is31fl3731_led_t { extern const is31fl3731_led_t PROGMEM g_is31fl3731_leds[IS31FL3731_LED_COUNT]; void is31fl3731_init_drivers(void); -void is31fl3731_init(uint8_t addr); -void is31fl3731_write_register(uint8_t addr, uint8_t reg, uint8_t data); -void is31fl3731_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer); +void is31fl3731_init(uint8_t index); +void is31fl3731_write_register(uint8_t index, uint8_t reg, uint8_t data); +void is31fl3731_select_page(uint8_t index, uint8_t page); void is31fl3731_set_color(int index, uint8_t red, uint8_t green, uint8_t blue); void is31fl3731_set_color_all(uint8_t red, uint8_t green, uint8_t blue); @@ -115,169 +119,169 @@ void is31fl3731_set_led_control_register(uint8_t index, bool red, bool green, bo // (eg. from a timer interrupt). // Call this while idle (in between matrix scans). // If the buffer is dirty, it will update the driver with the buffer. -void is31fl3731_update_pwm_buffers(uint8_t addr, uint8_t index); -void is31fl3731_update_led_control_registers(uint8_t addr, uint8_t index); +void is31fl3731_update_pwm_buffers(uint8_t index); +void is31fl3731_update_led_control_registers(uint8_t index); void is31fl3731_flush(void); -#define C1_1 0x24 -#define C1_2 0x25 -#define C1_3 0x26 -#define C1_4 0x27 -#define C1_5 0x28 -#define C1_6 0x29 -#define C1_7 0x2A -#define C1_8 0x2B - -#define C1_9 0x2C -#define C1_10 0x2D -#define C1_11 0x2E -#define C1_12 0x2F -#define C1_13 0x30 -#define C1_14 0x31 -#define C1_15 0x32 -#define C1_16 0x33 - -#define C2_1 0x34 -#define C2_2 0x35 -#define C2_3 0x36 -#define C2_4 0x37 -#define C2_5 0x38 -#define C2_6 0x39 -#define C2_7 0x3A -#define C2_8 0x3B - -#define C2_9 0x3C -#define C2_10 0x3D -#define C2_11 0x3E -#define C2_12 0x3F -#define C2_13 0x40 -#define C2_14 0x41 -#define C2_15 0x42 -#define C2_16 0x43 - -#define C3_1 0x44 -#define C3_2 0x45 -#define C3_3 0x46 -#define C3_4 0x47 -#define C3_5 0x48 -#define C3_6 0x49 -#define C3_7 0x4A -#define C3_8 0x4B - -#define C3_9 0x4C -#define C3_10 0x4D -#define C3_11 0x4E -#define C3_12 0x4F -#define C3_13 0x50 -#define C3_14 0x51 -#define C3_15 0x52 -#define C3_16 0x53 - -#define C4_1 0x54 -#define C4_2 0x55 -#define C4_3 0x56 -#define C4_4 0x57 -#define C4_5 0x58 -#define C4_6 0x59 -#define C4_7 0x5A -#define C4_8 0x5B - -#define C4_9 0x5C -#define C4_10 0x5D -#define C4_11 0x5E -#define C4_12 0x5F -#define C4_13 0x60 -#define C4_14 0x61 -#define C4_15 0x62 -#define C4_16 0x63 - -#define C5_1 0x64 -#define C5_2 0x65 -#define C5_3 0x66 -#define C5_4 0x67 -#define C5_5 0x68 -#define C5_6 0x69 -#define C5_7 0x6A -#define C5_8 0x6B - -#define C5_9 0x6C -#define C5_10 0x6D -#define C5_11 0x6E -#define C5_12 0x6F -#define C5_13 0x70 -#define C5_14 0x71 -#define C5_15 0x72 -#define C5_16 0x73 - -#define C6_1 0x74 -#define C6_2 0x75 -#define C6_3 0x76 -#define C6_4 0x77 -#define C6_5 0x78 -#define C6_6 0x79 -#define C6_7 0x7A -#define C6_8 0x7B - -#define C6_9 0x7C -#define C6_10 0x7D -#define C6_11 0x7E -#define C6_12 0x7F -#define C6_13 0x80 -#define C6_14 0x81 -#define C6_15 0x82 -#define C6_16 0x83 - -#define C7_1 0x84 -#define C7_2 0x85 -#define C7_3 0x86 -#define C7_4 0x87 -#define C7_5 0x88 -#define C7_6 0x89 -#define C7_7 0x8A -#define C7_8 0x8B - -#define C7_9 0x8C -#define C7_10 0x8D -#define C7_11 0x8E -#define C7_12 0x8F -#define C7_13 0x90 -#define C7_14 0x91 -#define C7_15 0x92 -#define C7_16 0x93 - -#define C8_1 0x94 -#define C8_2 0x95 -#define C8_3 0x96 -#define C8_4 0x97 -#define C8_5 0x98 -#define C8_6 0x99 -#define C8_7 0x9A -#define C8_8 0x9B - -#define C8_9 0x9C -#define C8_10 0x9D -#define C8_11 0x9E -#define C8_12 0x9F -#define C8_13 0xA0 -#define C8_14 0xA1 -#define C8_15 0xA2 -#define C8_16 0xA3 - -#define C9_1 0xA4 -#define C9_2 0xA5 -#define C9_3 0xA6 -#define C9_4 0xA7 -#define C9_5 0xA8 -#define C9_6 0xA9 -#define C9_7 0xAA -#define C9_8 0xAB - -#define C9_9 0xAC -#define C9_10 0xAD -#define C9_11 0xAE -#define C9_12 0xAF -#define C9_13 0xB0 -#define C9_14 0xB1 -#define C9_15 0xB2 -#define C9_16 0xB3 +#define C1_1 0x00 +#define C1_2 0x01 +#define C1_3 0x02 +#define C1_4 0x03 +#define C1_5 0x04 +#define C1_6 0x05 +#define C1_7 0x06 +#define C1_8 0x07 + +#define C1_9 0x08 +#define C1_10 0x09 +#define C1_11 0x0A +#define C1_12 0x0B +#define C1_13 0x0C +#define C1_14 0x0D +#define C1_15 0x0E +#define C1_16 0x0F + +#define C2_1 0x10 +#define C2_2 0x11 +#define C2_3 0x12 +#define C2_4 0x13 +#define C2_5 0x14 +#define C2_6 0x15 +#define C2_7 0x16 +#define C2_8 0x17 + +#define C2_9 0x18 +#define C2_10 0x19 +#define C2_11 0x1A +#define C2_12 0x1B +#define C2_13 0x1C +#define C2_14 0x1D +#define C2_15 0x1E +#define C2_16 0x1F + +#define C3_1 0x20 +#define C3_2 0x21 +#define C3_3 0x22 +#define C3_4 0x23 +#define C3_5 0x24 +#define C3_6 0x25 +#define C3_7 0x26 +#define C3_8 0x27 + +#define C3_9 0x28 +#define C3_10 0x29 +#define C3_11 0x2A +#define C3_12 0x2B +#define C3_13 0x2C +#define C3_14 0x2D +#define C3_15 0x2E +#define C3_16 0x2F + +#define C4_1 0x30 +#define C4_2 0x31 +#define C4_3 0x32 +#define C4_4 0x33 +#define C4_5 0x34 +#define C4_6 0x35 +#define C4_7 0x36 +#define C4_8 0x37 + +#define C4_9 0x38 +#define C4_10 0x39 +#define C4_11 0x3A +#define C4_12 0x3B +#define C4_13 0x3C +#define C4_14 0x3D +#define C4_15 0x3E +#define C4_16 0x3F + +#define C5_1 0x40 +#define C5_2 0x41 +#define C5_3 0x42 +#define C5_4 0x43 +#define C5_5 0x44 +#define C5_6 0x45 +#define C5_7 0x46 +#define C5_8 0x47 + +#define C5_9 0x48 +#define C5_10 0x49 +#define C5_11 0x4A +#define C5_12 0x4B +#define C5_13 0x4C +#define C5_14 0x4D +#define C5_15 0x4E +#define C5_16 0x4F + +#define C6_1 0x50 +#define C6_2 0x51 +#define C6_3 0x52 +#define C6_4 0x53 +#define C6_5 0x54 +#define C6_6 0x55 +#define C6_7 0x56 +#define C6_8 0x57 + +#define C6_9 0x58 +#define C6_10 0x59 +#define C6_11 0x5A +#define C6_12 0x5B +#define C6_13 0x5C +#define C6_14 0x5D +#define C6_15 0x5E +#define C6_16 0x5F + +#define C7_1 0x60 +#define C7_2 0x61 +#define C7_3 0x62 +#define C7_4 0x63 +#define C7_5 0x64 +#define C7_6 0x65 +#define C7_7 0x66 +#define C7_8 0x67 + +#define C7_9 0x68 +#define C7_10 0x69 +#define C7_11 0x6A +#define C7_12 0x6B +#define C7_13 0x6C +#define C7_14 0x6D +#define C7_15 0x6E +#define C7_16 0x6F + +#define C8_1 0x70 +#define C8_2 0x71 +#define C8_3 0x72 +#define C8_4 0x73 +#define C8_5 0x74 +#define C8_6 0x75 +#define C8_7 0x76 +#define C8_8 0x77 + +#define C8_9 0x78 +#define C8_10 0x79 +#define C8_11 0x7A +#define C8_12 0x7B +#define C8_13 0x7C +#define C8_14 0x7D +#define C8_15 0x7E +#define C8_16 0x7F + +#define C9_1 0x80 +#define C9_2 0x81 +#define C9_3 0x82 +#define C9_4 0x83 +#define C9_5 0x84 +#define C9_6 0x85 +#define C9_7 0x86 +#define C9_8 0x87 + +#define C9_9 0x88 +#define C9_10 0x89 +#define C9_11 0x8A +#define C9_12 0x8B +#define C9_13 0x8C +#define C9_14 0x8D +#define C9_15 0x8E +#define C9_16 0x8F diff --git a/drivers/led/issi/is31fl3733-mono.c b/drivers/led/issi/is31fl3733-mono.c new file mode 100644 index 0000000000..740fe06097 --- /dev/null +++ b/drivers/led/issi/is31fl3733-mono.c @@ -0,0 +1,265 @@ +/* Copyright 2017 Jason Williams + * Copyright 2018 Jack Humbert + * Copyright 2018 Yiancar + * Copyright 2021 Doni Crosby + * Copyright 2021 Leo Deng + * + * 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 2 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/>. + */ + +#include "is31fl3733-mono.h" +#include "i2c_master.h" +#include "gpio.h" +#include "wait.h" + +#define IS31FL3733_PWM_REGISTER_COUNT 192 +#define IS31FL3733_LED_CONTROL_REGISTER_COUNT 24 + +#ifndef IS31FL3733_I2C_TIMEOUT +# define IS31FL3733_I2C_TIMEOUT 100 +#endif + +#ifndef IS31FL3733_I2C_PERSISTENCE +# define IS31FL3733_I2C_PERSISTENCE 0 +#endif + +#ifndef IS31FL3733_PWM_FREQUENCY +# define IS31FL3733_PWM_FREQUENCY IS31FL3733_PWM_FREQUENCY_8K4_HZ // PFS - IS31FL3733B only +#endif + +#ifndef IS31FL3733_SW_PULLUP +# define IS31FL3733_SW_PULLUP IS31FL3733_PUR_0_OHM +#endif + +#ifndef IS31FL3733_CS_PULLDOWN +# define IS31FL3733_CS_PULLDOWN IS31FL3733_PDR_0_OHM +#endif + +#ifndef IS31FL3733_GLOBAL_CURRENT +# define IS31FL3733_GLOBAL_CURRENT 0xFF +#endif + +#ifndef IS31FL3733_SYNC_1 +# define IS31FL3733_SYNC_1 IS31FL3733_SYNC_NONE +#endif +#ifndef IS31FL3733_SYNC_2 +# define IS31FL3733_SYNC_2 IS31FL3733_SYNC_NONE +#endif +#ifndef IS31FL3733_SYNC_3 +# define IS31FL3733_SYNC_3 IS31FL3733_SYNC_NONE +#endif +#ifndef IS31FL3733_SYNC_4 +# define IS31FL3733_SYNC_4 IS31FL3733_SYNC_NONE +#endif + +const uint8_t i2c_addresses[IS31FL3733_DRIVER_COUNT] = { + IS31FL3733_I2C_ADDRESS_1, +#ifdef IS31FL3733_I2C_ADDRESS_2 + IS31FL3733_I2C_ADDRESS_2, +# ifdef IS31FL3733_I2C_ADDRESS_3 + IS31FL3733_I2C_ADDRESS_3, +# ifdef IS31FL3733_I2C_ADDRESS_4 + IS31FL3733_I2C_ADDRESS_4, +# endif +# endif +#endif +}; + +const uint8_t driver_sync[IS31FL3733_DRIVER_COUNT] = { + IS31FL3733_SYNC_1, +#ifdef IS31FL3733_I2C_ADDRESS_2 + IS31FL3733_SYNC_2, +# ifdef IS31FL3733_I2C_ADDRESS_3 + IS31FL3733_SYNC_3, +# ifdef IS31FL3733_I2C_ADDRESS_4 + IS31FL3733_SYNC_4, +# endif +# endif +#endif +}; + +// These buffers match the IS31FL3733 PWM registers. +// The control buffers match the page 0 LED On/Off registers. +// Storing them like this is optimal for I2C transfers to the registers. +// We could optimize this and take out the unused registers from these +// buffers and the transfers in is31fl3733_write_pwm_buffer() but it's +// probably not worth the extra complexity. +typedef struct is31fl3733_driver_t { + uint8_t pwm_buffer[IS31FL3733_PWM_REGISTER_COUNT]; + bool pwm_buffer_dirty; + uint8_t led_control_buffer[IS31FL3733_LED_CONTROL_REGISTER_COUNT]; + bool led_control_buffer_dirty; +} PACKED is31fl3733_driver_t; + +is31fl3733_driver_t driver_buffers[IS31FL3733_DRIVER_COUNT] = {{ + .pwm_buffer = {0}, + .pwm_buffer_dirty = false, + .led_control_buffer = {0}, + .led_control_buffer_dirty = false, +}}; + +void is31fl3733_write_register(uint8_t index, uint8_t reg, uint8_t data) { +#if IS31FL3733_I2C_PERSISTENCE > 0 + for (uint8_t i = 0; i < IS31FL3733_I2C_PERSISTENCE; i++) { + if (i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3733_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break; + } +#else + i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3733_I2C_TIMEOUT); +#endif +} + +void is31fl3733_select_page(uint8_t index, uint8_t page) { + is31fl3733_write_register(index, IS31FL3733_REG_COMMAND_WRITE_LOCK, IS31FL3733_COMMAND_WRITE_LOCK_MAGIC); + is31fl3733_write_register(index, IS31FL3733_REG_COMMAND, page); +} + +void is31fl3733_write_pwm_buffer(uint8_t index) { + // Assumes page 1 is already selected. + // Transmit PWM registers in 12 transfers of 16 bytes. + + // Iterate over the pwm_buffer contents at 16 byte intervals. + for (uint8_t i = 0; i < IS31FL3733_PWM_REGISTER_COUNT; i += 16) { +#if IS31FL3733_I2C_PERSISTENCE > 0 + for (uint8_t j = 0; j < IS31FL3733_I2C_PERSISTENCE; j++) { + if (i2c_write_register(i2c_addresses[index] << 1, i, driver_buffers[index].pwm_buffer + i, 16, IS31FL3733_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break; + } +#else + i2c_write_register(i2c_addresses[index] << 1, i, driver_buffers[index].pwm_buffer + i, 16, IS31FL3733_I2C_TIMEOUT); +#endif + } +} + +void is31fl3733_init_drivers(void) { + i2c_init(); + +#if defined(IS31FL3733_SDB_PIN) + gpio_set_pin_output(IS31FL3733_SDB_PIN); + gpio_write_pin_high(IS31FL3733_SDB_PIN); +#endif + + for (uint8_t i = 0; i < IS31FL3733_DRIVER_COUNT; i++) { + is31fl3733_init(i); + } + + for (int i = 0; i < IS31FL3733_LED_COUNT; i++) { + is31fl3733_set_led_control_register(i, true); + } + + for (uint8_t i = 0; i < IS31FL3733_DRIVER_COUNT; i++) { + is31fl3733_update_led_control_registers(i); + } +} + +void is31fl3733_init(uint8_t index) { + // In order to avoid the LEDs being driven with garbage data + // in the LED driver's PWM registers, shutdown is enabled last. + // Set up the mode and other settings, clear the PWM registers, + // then disable software shutdown. + + is31fl3733_select_page(index, IS31FL3733_COMMAND_LED_CONTROL); + + // Turn off all LEDs. + for (uint8_t i = 0; i < IS31FL3733_LED_CONTROL_REGISTER_COUNT; i++) { + is31fl3733_write_register(index, i, 0x00); + } + + is31fl3733_select_page(index, IS31FL3733_COMMAND_PWM); + + // Set PWM on all LEDs to 0 + // No need to setup Breath registers to PWM as that is the default. + for (uint8_t i = 0; i < IS31FL3733_PWM_REGISTER_COUNT; i++) { + is31fl3733_write_register(index, i, 0x00); + } + + is31fl3733_select_page(index, IS31FL3733_COMMAND_FUNCTION); + + uint8_t sync = driver_sync[index]; + + // Set de-ghost pull-up resistors (SWx) + is31fl3733_write_register(index, IS31FL3733_FUNCTION_REG_SW_PULLUP, IS31FL3733_SW_PULLUP); + // Set de-ghost pull-down resistors (CSx) + is31fl3733_write_register(index, IS31FL3733_FUNCTION_REG_CS_PULLDOWN, IS31FL3733_CS_PULLDOWN); + // Set global current to maximum. + is31fl3733_write_register(index, IS31FL3733_FUNCTION_REG_GLOBAL_CURRENT, IS31FL3733_GLOBAL_CURRENT); + // Disable software shutdown. + is31fl3733_write_register(index, IS31FL3733_FUNCTION_REG_CONFIGURATION, ((sync & 0b11) << 6) | ((IS31FL3733_PWM_FREQUENCY & 0b111) << 3) | 0x01); + + // Wait 10ms to ensure the device has woken up. + wait_ms(10); +} + +void is31fl3733_set_value(int index, uint8_t value) { + is31fl3733_led_t led; + + if (index >= 0 && index < IS31FL3733_LED_COUNT) { + memcpy_P(&led, (&g_is31fl3733_leds[index]), sizeof(led)); + + if (driver_buffers[led.driver].pwm_buffer[led.v] == value) { + return; + } + + driver_buffers[led.driver].pwm_buffer[led.v] = value; + driver_buffers[led.driver].pwm_buffer_dirty = true; + } +} + +void is31fl3733_set_value_all(uint8_t value) { + for (int i = 0; i < IS31FL3733_LED_COUNT; i++) { + is31fl3733_set_value(i, value); + } +} + +void is31fl3733_set_led_control_register(uint8_t index, bool value) { + is31fl3733_led_t led; + memcpy_P(&led, (&g_is31fl3733_leds[index]), sizeof(led)); + + uint8_t control_register = led.v / 8; + uint8_t bit_value = led.v % 8; + + if (value) { + driver_buffers[led.driver].led_control_buffer[control_register] |= (1 << bit_value); + } else { + driver_buffers[led.driver].led_control_buffer[control_register] &= ~(1 << bit_value); + } + + driver_buffers[led.driver].led_control_buffer_dirty = true; +} + +void is31fl3733_update_pwm_buffers(uint8_t index) { + if (driver_buffers[index].pwm_buffer_dirty) { + is31fl3733_select_page(index, IS31FL3733_COMMAND_PWM); + + is31fl3733_write_pwm_buffer(index); + + driver_buffers[index].pwm_buffer_dirty = false; + } +} + +void is31fl3733_update_led_control_registers(uint8_t index) { + if (driver_buffers[index].led_control_buffer_dirty) { + is31fl3733_select_page(index, IS31FL3733_COMMAND_LED_CONTROL); + + for (uint8_t i = 0; i < IS31FL3733_LED_CONTROL_REGISTER_COUNT; i++) { + is31fl3733_write_register(index, i, driver_buffers[index].led_control_buffer[i]); + } + + driver_buffers[index].led_control_buffer_dirty = false; + } +} + +void is31fl3733_flush(void) { + for (uint8_t i = 0; i < IS31FL3733_DRIVER_COUNT; i++) { + is31fl3733_update_pwm_buffers(i); + } +} diff --git a/drivers/led/issi/is31fl3733-simple.h b/drivers/led/issi/is31fl3733-mono.h index c37b1fe5f2..3786b2ed71 100644 --- a/drivers/led/issi/is31fl3733-simple.h +++ b/drivers/led/issi/is31fl3733-mono.h @@ -115,9 +115,9 @@ typedef struct is31fl3733_led_t { extern const is31fl3733_led_t PROGMEM g_is31fl3733_leds[IS31FL3733_LED_COUNT]; void is31fl3733_init_drivers(void); -void is31fl3733_init(uint8_t addr, uint8_t sync); -bool is31fl3733_write_register(uint8_t addr, uint8_t reg, uint8_t data); -bool is31fl3733_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer); +void is31fl3733_init(uint8_t index); +void is31fl3733_write_register(uint8_t index, uint8_t reg, uint8_t data); +void is31fl3733_select_page(uint8_t index, uint8_t page); void is31fl3733_set_value(int index, uint8_t value); void is31fl3733_set_value_all(uint8_t value); @@ -128,8 +128,8 @@ void is31fl3733_set_led_control_register(uint8_t index, bool value); // (eg. from a timer interrupt). // Call this while idle (in between matrix scans). // If the buffer is dirty, it will update the driver with the buffer. -void is31fl3733_update_pwm_buffers(uint8_t addr, uint8_t index); -void is31fl3733_update_led_control_registers(uint8_t addr, uint8_t index); +void is31fl3733_update_pwm_buffers(uint8_t index); +void is31fl3733_update_led_control_registers(uint8_t index); void is31fl3733_flush(void); @@ -161,206 +161,206 @@ void is31fl3733_flush(void); #define IS31FL3733_SYNC_MASTER 0b01 #define IS31FL3733_SYNC_SLAVE 0b10 -#define A_1 0x00 -#define A_2 0x01 -#define A_3 0x02 -#define A_4 0x03 -#define A_5 0x04 -#define A_6 0x05 -#define A_7 0x06 -#define A_8 0x07 -#define A_9 0x08 -#define A_10 0x09 -#define A_11 0x0A -#define A_12 0x0B -#define A_13 0x0C -#define A_14 0x0D -#define A_15 0x0E -#define A_16 0x0F - -#define B_1 0x10 -#define B_2 0x11 -#define B_3 0x12 -#define B_4 0x13 -#define B_5 0x14 -#define B_6 0x15 -#define B_7 0x16 -#define B_8 0x17 -#define B_9 0x18 -#define B_10 0x19 -#define B_11 0x1A -#define B_12 0x1B -#define B_13 0x1C -#define B_14 0x1D -#define B_15 0x1E -#define B_16 0x1F - -#define C_1 0x20 -#define C_2 0x21 -#define C_3 0x22 -#define C_4 0x23 -#define C_5 0x24 -#define C_6 0x25 -#define C_7 0x26 -#define C_8 0x27 -#define C_9 0x28 -#define C_10 0x29 -#define C_11 0x2A -#define C_12 0x2B -#define C_13 0x2C -#define C_14 0x2D -#define C_15 0x2E -#define C_16 0x2F - -#define D_1 0x30 -#define D_2 0x31 -#define D_3 0x32 -#define D_4 0x33 -#define D_5 0x34 -#define D_6 0x35 -#define D_7 0x36 -#define D_8 0x37 -#define D_9 0x38 -#define D_10 0x39 -#define D_11 0x3A -#define D_12 0x3B -#define D_13 0x3C -#define D_14 0x3D -#define D_15 0x3E -#define D_16 0x3F - -#define E_1 0x40 -#define E_2 0x41 -#define E_3 0x42 -#define E_4 0x43 -#define E_5 0x44 -#define E_6 0x45 -#define E_7 0x46 -#define E_8 0x47 -#define E_9 0x48 -#define E_10 0x49 -#define E_11 0x4A -#define E_12 0x4B -#define E_13 0x4C -#define E_14 0x4D -#define E_15 0x4E -#define E_16 0x4F - -#define F_1 0x50 -#define F_2 0x51 -#define F_3 0x52 -#define F_4 0x53 -#define F_5 0x54 -#define F_6 0x55 -#define F_7 0x56 -#define F_8 0x57 -#define F_9 0x58 -#define F_10 0x59 -#define F_11 0x5A -#define F_12 0x5B -#define F_13 0x5C -#define F_14 0x5D -#define F_15 0x5E -#define F_16 0x5F - -#define G_1 0x60 -#define G_2 0x61 -#define G_3 0x62 -#define G_4 0x63 -#define G_5 0x64 -#define G_6 0x65 -#define G_7 0x66 -#define G_8 0x67 -#define G_9 0x68 -#define G_10 0x69 -#define G_11 0x6A -#define G_12 0x6B -#define G_13 0x6C -#define G_14 0x6D -#define G_15 0x6E -#define G_16 0x6F - -#define H_1 0x70 -#define H_2 0x71 -#define H_3 0x72 -#define H_4 0x73 -#define H_5 0x74 -#define H_6 0x75 -#define H_7 0x76 -#define H_8 0x77 -#define H_9 0x78 -#define H_10 0x79 -#define H_11 0x7A -#define H_12 0x7B -#define H_13 0x7C -#define H_14 0x7D -#define H_15 0x7E -#define H_16 0x7F - -#define I_1 0x80 -#define I_2 0x81 -#define I_3 0x82 -#define I_4 0x83 -#define I_5 0x84 -#define I_6 0x85 -#define I_7 0x86 -#define I_8 0x87 -#define I_9 0x88 -#define I_10 0x89 -#define I_11 0x8A -#define I_12 0x8B -#define I_13 0x8C -#define I_14 0x8D -#define I_15 0x8E -#define I_16 0x8F - -#define J_1 0x90 -#define J_2 0x91 -#define J_3 0x92 -#define J_4 0x93 -#define J_5 0x94 -#define J_6 0x95 -#define J_7 0x96 -#define J_8 0x97 -#define J_9 0x98 -#define J_10 0x99 -#define J_11 0x9A -#define J_12 0x9B -#define J_13 0x9C -#define J_14 0x9D -#define J_15 0x9E -#define J_16 0x9F - -#define K_1 0xA0 -#define K_2 0xA1 -#define K_3 0xA2 -#define K_4 0xA3 -#define K_5 0xA4 -#define K_6 0xA5 -#define K_7 0xA6 -#define K_8 0xA7 -#define K_9 0xA8 -#define K_10 0xA9 -#define K_11 0xAA -#define K_12 0xAB -#define K_13 0xAC -#define K_14 0xAD -#define K_15 0xAE -#define K_16 0xAF - -#define L_1 0xB0 -#define L_2 0xB1 -#define L_3 0xB2 -#define L_4 0xB3 -#define L_5 0xB4 -#define L_6 0xB5 -#define L_7 0xB6 -#define L_8 0xB7 -#define L_9 0xB8 -#define L_10 0xB9 -#define L_11 0xBA -#define L_12 0xBB -#define L_13 0xBC -#define L_14 0xBD -#define L_15 0xBE -#define L_16 0xBF +#define SW1_CS1 0x00 +#define SW1_CS2 0x01 +#define SW1_CS3 0x02 +#define SW1_CS4 0x03 +#define SW1_CS5 0x04 +#define SW1_CS6 0x05 +#define SW1_CS7 0x06 +#define SW1_CS8 0x07 +#define SW1_CS9 0x08 +#define SW1_CS10 0x09 +#define SW1_CS11 0x0A +#define SW1_CS12 0x0B +#define SW1_CS13 0x0C +#define SW1_CS14 0x0D +#define SW1_CS15 0x0E +#define SW1_CS16 0x0F + +#define SW2_CS1 0x10 +#define SW2_CS2 0x11 +#define SW2_CS3 0x12 +#define SW2_CS4 0x13 +#define SW2_CS5 0x14 +#define SW2_CS6 0x15 +#define SW2_CS7 0x16 +#define SW2_CS8 0x17 +#define SW2_CS9 0x18 +#define SW2_CS10 0x19 +#define SW2_CS11 0x1A +#define SW2_CS12 0x1B +#define SW2_CS13 0x1C +#define SW2_CS14 0x1D +#define SW2_CS15 0x1E +#define SW2_CS16 0x1F + +#define SW3_CS1 0x20 +#define SW3_CS2 0x21 +#define SW3_CS3 0x22 +#define SW3_CS4 0x23 +#define SW3_CS5 0x24 +#define SW3_CS6 0x25 +#define SW3_CS7 0x26 +#define SW3_CS8 0x27 +#define SW3_CS9 0x28 +#define SW3_CS10 0x29 +#define SW3_CS11 0x2A +#define SW3_CS12 0x2B +#define SW3_CS13 0x2C +#define SW3_CS14 0x2D +#define SW3_CS15 0x2E +#define SW3_CS16 0x2F + +#define SW4_CS1 0x30 +#define SW4_CS2 0x31 +#define SW4_CS3 0x32 +#define SW4_CS4 0x33 +#define SW4_CS5 0x34 +#define SW4_CS6 0x35 +#define SW4_CS7 0x36 +#define SW4_CS8 0x37 +#define SW4_CS9 0x38 +#define SW4_CS10 0x39 +#define SW4_CS11 0x3A +#define SW4_CS12 0x3B +#define SW4_CS13 0x3C +#define SW4_CS14 0x3D +#define SW4_CS15 0x3E +#define SW4_CS16 0x3F + +#define SW5_CS1 0x40 +#define SW5_CS2 0x41 +#define SW5_CS3 0x42 +#define SW5_CS4 0x43 +#define SW5_CS5 0x44 +#define SW5_CS6 0x45 +#define SW5_CS7 0x46 +#define SW5_CS8 0x47 +#define SW5_CS9 0x48 +#define SW5_CS10 0x49 +#define SW5_CS11 0x4A +#define SW5_CS12 0x4B +#define SW5_CS13 0x4C +#define SW5_CS14 0x4D +#define SW5_CS15 0x4E +#define SW5_CS16 0x4F + +#define SW6_CS1 0x50 +#define SW6_CS2 0x51 +#define SW6_CS3 0x52 +#define SW6_CS4 0x53 +#define SW6_CS5 0x54 +#define SW6_CS6 0x55 +#define SW6_CS7 0x56 +#define SW6_CS8 0x57 +#define SW6_CS9 0x58 +#define SW6_CS10 0x59 +#define SW6_CS11 0x5A +#define SW6_CS12 0x5B +#define SW6_CS13 0x5C +#define SW6_CS14 0x5D +#define SW6_CS15 0x5E +#define SW6_CS16 0x5F + +#define SW7_CS1 0x60 +#define SW7_CS2 0x61 +#define SW7_CS3 0x62 +#define SW7_CS4 0x63 +#define SW7_CS5 0x64 +#define SW7_CS6 0x65 +#define SW7_CS7 0x66 +#define SW7_CS8 0x67 +#define SW7_CS9 0x68 +#define SW7_CS10 0x69 +#define SW7_CS11 0x6A +#define SW7_CS12 0x6B +#define SW7_CS13 0x6C +#define SW7_CS14 0x6D +#define SW7_CS15 0x6E +#define SW7_CS16 0x6F + +#define SW8_CS1 0x70 +#define SW8_CS2 0x71 +#define SW8_CS3 0x72 +#define SW8_CS4 0x73 +#define SW8_CS5 0x74 +#define SW8_CS6 0x75 +#define SW8_CS7 0x76 +#define SW8_CS8 0x77 +#define SW8_CS9 0x78 +#define SW8_CS10 0x79 +#define SW8_CS11 0x7A +#define SW8_CS12 0x7B +#define SW8_CS13 0x7C +#define SW8_CS14 0x7D +#define SW8_CS15 0x7E +#define SW8_CS16 0x7F + +#define SW9_CS1 0x80 +#define SW9_CS2 0x81 +#define SW9_CS3 0x82 +#define SW9_CS4 0x83 +#define SW9_CS5 0x84 +#define SW9_CS6 0x85 +#define SW9_CS7 0x86 +#define SW9_CS8 0x87 +#define SW9_CS9 0x88 +#define SW9_CS10 0x89 +#define SW9_CS11 0x8A +#define SW9_CS12 0x8B +#define SW9_CS13 0x8C +#define SW9_CS14 0x8D +#define SW9_CS15 0x8E +#define SW9_CS16 0x8F + +#define SW10_CS1 0x90 +#define SW10_CS2 0x91 +#define SW10_CS3 0x92 +#define SW10_CS4 0x93 +#define SW10_CS5 0x94 +#define SW10_CS6 0x95 +#define SW10_CS7 0x96 +#define SW10_CS8 0x97 +#define SW10_CS9 0x98 +#define SW10_CS10 0x99 +#define SW10_CS11 0x9A +#define SW10_CS12 0x9B +#define SW10_CS13 0x9C +#define SW10_CS14 0x9D +#define SW10_CS15 0x9E +#define SW10_CS16 0x9F + +#define SW11_CS1 0xA0 +#define SW11_CS2 0xA1 +#define SW11_CS3 0xA2 +#define SW11_CS4 0xA3 +#define SW11_CS5 0xA4 +#define SW11_CS6 0xA5 +#define SW11_CS7 0xA6 +#define SW11_CS8 0xA7 +#define SW11_CS9 0xA8 +#define SW11_CS10 0xA9 +#define SW11_CS11 0xAA +#define SW11_CS12 0xAB +#define SW11_CS13 0xAC +#define SW11_CS14 0xAD +#define SW11_CS15 0xAE +#define SW11_CS16 0xAF + +#define SW12_CS1 0xB0 +#define SW12_CS2 0xB1 +#define SW12_CS3 0xB2 +#define SW12_CS4 0xB3 +#define SW12_CS5 0xB4 +#define SW12_CS6 0xB5 +#define SW12_CS7 0xB6 +#define SW12_CS8 0xB7 +#define SW12_CS9 0xB8 +#define SW12_CS10 0xB9 +#define SW12_CS11 0xBA +#define SW12_CS12 0xBB +#define SW12_CS13 0xBC +#define SW12_CS14 0xBD +#define SW12_CS15 0xBE +#define SW12_CS16 0xBF diff --git a/drivers/led/issi/is31fl3733-simple.c b/drivers/led/issi/is31fl3733-simple.c deleted file mode 100644 index 9f2444c253..0000000000 --- a/drivers/led/issi/is31fl3733-simple.c +++ /dev/null @@ -1,278 +0,0 @@ -/* Copyright 2017 Jason Williams - * Copyright 2018 Jack Humbert - * Copyright 2018 Yiancar - * Copyright 2021 Doni Crosby - * Copyright 2021 Leo Deng - * - * 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 2 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/>. - */ - -#include "is31fl3733-simple.h" -#include <string.h> -#include "i2c_master.h" -#include "wait.h" - -#define IS31FL3733_PWM_REGISTER_COUNT 192 -#define IS31FL3733_LED_CONTROL_REGISTER_COUNT 24 - -#ifndef IS31FL3733_I2C_TIMEOUT -# define IS31FL3733_I2C_TIMEOUT 100 -#endif - -#ifndef IS31FL3733_I2C_PERSISTENCE -# define IS31FL3733_I2C_PERSISTENCE 0 -#endif - -#ifndef IS31FL3733_PWM_FREQUENCY -# define IS31FL3733_PWM_FREQUENCY IS31FL3733_PWM_FREQUENCY_8K4_HZ // PFS - IS31FL3733B only -#endif - -#ifndef IS31FL3733_SW_PULLUP -# define IS31FL3733_SW_PULLUP IS31FL3733_PUR_0_OHM -#endif - -#ifndef IS31FL3733_CS_PULLDOWN -# define IS31FL3733_CSPULLDOWN IS31FL3733_PDR_0_OHM -#endif - -#ifndef IS31FL3733_GLOBAL_CURRENT -# define IS31FL3733_GLOBAL_CURRENT 0xFF -#endif - -#ifndef IS31FL3733_SYNC_1 -# define IS31FL3733_SYNC_1 IS31FL3733_SYNC_NONE -#endif -#ifndef IS31FL3733_SYNC_2 -# define IS31FL3733_SYNC_2 IS31FL3733_SYNC_NONE -#endif -#ifndef IS31FL3733_SYNC_3 -# define IS31FL3733_SYNC_3 IS31FL3733_SYNC_NONE -#endif -#ifndef IS31FL3733_SYNC_4 -# define IS31FL3733_SYNC_4 IS31FL3733_SYNC_NONE -#endif - -// Transfer buffer for TWITransmitData() -uint8_t g_twi_transfer_buffer[20]; - -// These buffers match the IS31FL3733 PWM registers. -// The control buffers match the PG0 LED On/Off registers. -// Storing them like this is optimal for I2C transfers to the registers. -// We could optimize this and take out the unused registers from these -// buffers and the transfers in is31fl3733_write_pwm_buffer() but it's -// probably not worth the extra complexity. -uint8_t g_pwm_buffer[IS31FL3733_DRIVER_COUNT][IS31FL3733_PWM_REGISTER_COUNT]; -bool g_pwm_buffer_update_required[IS31FL3733_DRIVER_COUNT] = {false}; - -uint8_t g_led_control_registers[IS31FL3733_DRIVER_COUNT][IS31FL3733_LED_CONTROL_REGISTER_COUNT] = {0}; -bool g_led_control_registers_update_required[IS31FL3733_DRIVER_COUNT] = {false}; - -bool is31fl3733_write_register(uint8_t addr, uint8_t reg, uint8_t data) { - // If the transaction fails function returns false. - g_twi_transfer_buffer[0] = reg; - g_twi_transfer_buffer[1] = data; - -#if IS31FL3733_I2C_PERSISTENCE > 0 - for (uint8_t i = 0; i < IS31FL3733_I2C_PERSISTENCE; i++) { - if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, IS31FL3733_I2C_TIMEOUT) != 0) { - return false; - } - } -#else - if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, IS31FL3733_I2C_TIMEOUT) != 0) { - return false; - } -#endif - return true; -} - -bool is31fl3733_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer) { - // Assumes PG1 is already selected. - // If any of the transactions fails function returns false. - // Transmit PWM registers in 12 transfers of 16 bytes. - // g_twi_transfer_buffer[] is 20 bytes - - // Iterate over the pwm_buffer contents at 16 byte intervals. - for (int i = 0; i < IS31FL3733_PWM_REGISTER_COUNT; i += 16) { - g_twi_transfer_buffer[0] = i; - // Copy the data from i to i+15. - // Device will auto-increment register for data after the first byte - // Thus this sets registers 0x00-0x0F, 0x10-0x1F, etc. in one transfer. - memcpy(g_twi_transfer_buffer + 1, pwm_buffer + i, 16); - -#if IS31FL3733_I2C_PERSISTENCE > 0 - for (uint8_t i = 0; i < IS31FL3733_I2C_PERSISTENCE; i++) { - if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 17, IS31FL3733_I2C_TIMEOUT) != 0) { - return false; - } - } -#else - if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 17, IS31FL3733_I2C_TIMEOUT) != 0) { - return false; - } -#endif - } - return true; -} - -void is31fl3733_init_drivers(void) { - i2c_init(); - - is31fl3733_init(IS31FL3733_I2C_ADDRESS_1, IS31FL3733_SYNC_1); -#if defined(IS31FL3733_I2C_ADDRESS_2) - is31fl3733_init(IS31FL3733_I2C_ADDRESS_2, IS31FL3733_SYNC_2); -# if defined(IS31FL3733_I2C_ADDRESS_3) - is31fl3733_init(IS31FL3733_I2C_ADDRESS_3, IS31FL3733_SYNC_3); -# if defined(IS31FL3733_I2C_ADDRESS_4) - is31fl3733_init(IS31FL3733_I2C_ADDRESS_4, IS31FL3733_SYNC_4); -# endif -# endif -#endif - - for (int i = 0; i < IS31FL3733_LED_COUNT; i++) { - is31fl3733_set_led_control_register(i, true); - } - - is31fl3733_update_led_control_registers(IS31FL3733_I2C_ADDRESS_1, 0); -#if defined(IS31FL3733_I2C_ADDRESS_2) - is31fl3733_update_led_control_registers(IS31FL3733_I2C_ADDRESS_2, 1); -# if defined(IS31FL3733_I2C_ADDRESS_3) - is31fl3733_update_led_control_registers(IS31FL3733_I2C_ADDRESS_3, 2); -# if defined(IS31FL3733_I2C_ADDRESS_4) - is31fl3733_update_led_control_registers(IS31FL3733_I2C_ADDRESS_4, 3); -# endif -# endif -#endif -} - -void is31fl3733_init(uint8_t addr, uint8_t sync) { - // In order to avoid the LEDs being driven with garbage data - // in the LED driver's PWM registers, shutdown is enabled last. - // Set up the mode and other settings, clear the PWM registers, - // then disable software shutdown. - // Sync is passed so set it according to the datasheet. - - // Unlock the command register. - is31fl3733_write_register(addr, IS31FL3733_REG_COMMAND_WRITE_LOCK, IS31FL3733_COMMAND_WRITE_LOCK_MAGIC); - - // Select PG0 - is31fl3733_write_register(addr, IS31FL3733_REG_COMMAND, IS31FL3733_COMMAND_LED_CONTROL); - // Turn off all LEDs. - for (int i = 0; i < IS31FL3733_LED_CONTROL_REGISTER_COUNT; i++) { - is31fl3733_write_register(addr, i, 0x00); - } - - // Unlock the command register. - is31fl3733_write_register(addr, IS31FL3733_REG_COMMAND_WRITE_LOCK, IS31FL3733_COMMAND_WRITE_LOCK_MAGIC); - - // Select PG1 - is31fl3733_write_register(addr, IS31FL3733_REG_COMMAND, IS31FL3733_COMMAND_PWM); - // Set PWM on all LEDs to 0 - // No need to setup Breath registers to PWM as that is the default. - for (int i = 0; i < IS31FL3733_PWM_REGISTER_COUNT; i++) { - is31fl3733_write_register(addr, i, 0x00); - } - - // Unlock the command register. - is31fl3733_write_register(addr, IS31FL3733_REG_COMMAND_WRITE_LOCK, IS31FL3733_COMMAND_WRITE_LOCK_MAGIC); - - // Select PG3 - is31fl3733_write_register(addr, IS31FL3733_REG_COMMAND, IS31FL3733_COMMAND_FUNCTION); - // Set de-ghost pull-up resistors (SWx) - is31fl3733_write_register(addr, IS31FL3733_FUNCTION_REG_SW_PULLUP, IS31FL3733_SW_PULLUP); - // Set de-ghost pull-down resistors (CSx) - is31fl3733_write_register(addr, IS31FL3733_FUNCTION_REG_CS_PULLDOWN, IS31FL3733_CS_PULLDOWN); - // Set global current to maximum. - is31fl3733_write_register(addr, IS31FL3733_FUNCTION_REG_GLOBAL_CURRENT, IS31FL3733_GLOBAL_CURRENT); - // Disable software shutdown. - is31fl3733_write_register(addr, IS31FL3733_FUNCTION_REG_CONFIGURATION, ((sync & 0b11) << 6) | ((IS31FL3733_PWM_FREQUENCY & 0b111) << 3) | 0x01); - - // Wait 10ms to ensure the device has woken up. - wait_ms(10); -} - -void is31fl3733_set_value(int index, uint8_t value) { - is31fl3733_led_t led; - if (index >= 0 && index < IS31FL3733_LED_COUNT) { - memcpy_P(&led, (&g_is31fl3733_leds[index]), sizeof(led)); - - if (g_pwm_buffer[led.driver][led.v] == value) { - return; - } - g_pwm_buffer[led.driver][led.v] = value; - g_pwm_buffer_update_required[led.driver] = true; - } -} - -void is31fl3733_set_value_all(uint8_t value) { - for (int i = 0; i < IS31FL3733_LED_COUNT; i++) { - is31fl3733_set_value(i, value); - } -} - -void is31fl3733_set_led_control_register(uint8_t index, bool value) { - is31fl3733_led_t led; - memcpy_P(&led, (&g_is31fl3733_leds[index]), sizeof(led)); - - uint8_t control_register = led.v / 8; - uint8_t bit_value = led.v % 8; - - if (value) { - g_led_control_registers[led.driver][control_register] |= (1 << bit_value); - } else { - g_led_control_registers[led.driver][control_register] &= ~(1 << bit_value); - } - - g_led_control_registers_update_required[led.driver] = true; -} - -void is31fl3733_update_pwm_buffers(uint8_t addr, uint8_t index) { - if (g_pwm_buffer_update_required[index]) { - // Firstly we need to unlock the command register and select PG1. - is31fl3733_write_register(addr, IS31FL3733_REG_COMMAND_WRITE_LOCK, IS31FL3733_COMMAND_WRITE_LOCK_MAGIC); - is31fl3733_write_register(addr, IS31FL3733_REG_COMMAND, IS31FL3733_COMMAND_PWM); - - // If any of the transactions fail we risk writing dirty PG0, - // refresh page 0 just in case. - if (!is31fl3733_write_pwm_buffer(addr, g_pwm_buffer[index])) { - g_led_control_registers_update_required[index] = true; - } - g_pwm_buffer_update_required[index] = false; - } -} - -void is31fl3733_update_led_control_registers(uint8_t addr, uint8_t index) { - if (g_led_control_registers_update_required[index]) { - // Firstly we need to unlock the command register and select PG0 - is31fl3733_write_register(addr, IS31FL3733_REG_COMMAND_WRITE_LOCK, IS31FL3733_COMMAND_WRITE_LOCK_MAGIC); - is31fl3733_write_register(addr, IS31FL3733_REG_COMMAND, IS31FL3733_COMMAND_LED_CONTROL); - for (int i = 0; i < IS31FL3733_LED_CONTROL_REGISTER_COUNT; i++) { - is31fl3733_write_register(addr, i, g_led_control_registers[index][i]); - } - g_led_control_registers_update_required[index] = false; - } -} - -void is31fl3733_flush(void) { - is31fl3733_update_pwm_buffers(IS31FL3733_I2C_ADDRESS_1, 0); -#if defined(IS31FL3733_I2C_ADDRESS_2) - is31fl3733_update_pwm_buffers(IS31FL3733_I2C_ADDRESS_2, 1); -# if defined(IS31FL3733_I2C_ADDRESS_3) - is31fl3733_update_pwm_buffers(IS31FL3733_I2C_ADDRESS_3, 2); -# if defined(IS31FL3733_I2C_ADDRESS_4) - is31fl3733_update_pwm_buffers(IS31FL3733_I2C_ADDRESS_4, 3); -# endif -# endif -#endif -} diff --git a/drivers/led/issi/is31fl3733.c b/drivers/led/issi/is31fl3733.c index 5857a800d7..a1d6899114 100644 --- a/drivers/led/issi/is31fl3733.c +++ b/drivers/led/issi/is31fl3733.c @@ -18,8 +18,8 @@ */ #include "is31fl3733.h" -#include <string.h> #include "i2c_master.h" +#include "gpio.h" #include "wait.h" #define IS31FL3733_PWM_REGISTER_COUNT 192 @@ -62,140 +62,137 @@ # define IS31FL3733_SYNC_4 IS31FL3733_SYNC_NONE #endif -// Transfer buffer for TWITransmitData() -uint8_t g_twi_transfer_buffer[20]; +const uint8_t i2c_addresses[IS31FL3733_DRIVER_COUNT] = { + IS31FL3733_I2C_ADDRESS_1, +#ifdef IS31FL3733_I2C_ADDRESS_2 + IS31FL3733_I2C_ADDRESS_2, +# ifdef IS31FL3733_I2C_ADDRESS_3 + IS31FL3733_I2C_ADDRESS_3, +# ifdef IS31FL3733_I2C_ADDRESS_4 + IS31FL3733_I2C_ADDRESS_4, +# endif +# endif +#endif +}; + +const uint8_t driver_sync[IS31FL3733_DRIVER_COUNT] = { + IS31FL3733_SYNC_1, +#ifdef IS31FL3733_I2C_ADDRESS_2 + IS31FL3733_SYNC_2, +# ifdef IS31FL3733_I2C_ADDRESS_3 + IS31FL3733_SYNC_3, +# ifdef IS31FL3733_I2C_ADDRESS_4 + IS31FL3733_SYNC_4, +# endif +# endif +#endif +}; // These buffers match the IS31FL3733 PWM registers. -// The control buffers match the PG0 LED On/Off registers. +// The control buffers match the page 0 LED On/Off registers. // Storing them like this is optimal for I2C transfers to the registers. // We could optimize this and take out the unused registers from these // buffers and the transfers in is31fl3733_write_pwm_buffer() but it's // probably not worth the extra complexity. -uint8_t g_pwm_buffer[IS31FL3733_DRIVER_COUNT][IS31FL3733_PWM_REGISTER_COUNT]; -bool g_pwm_buffer_update_required[IS31FL3733_DRIVER_COUNT] = {false}; - -uint8_t g_led_control_registers[IS31FL3733_DRIVER_COUNT][IS31FL3733_LED_CONTROL_REGISTER_COUNT] = {0}; -bool g_led_control_registers_update_required[IS31FL3733_DRIVER_COUNT] = {false}; - -bool is31fl3733_write_register(uint8_t addr, uint8_t reg, uint8_t data) { - // If the transaction fails function returns false. - g_twi_transfer_buffer[0] = reg; - g_twi_transfer_buffer[1] = data; - +typedef struct is31fl3733_driver_t { + uint8_t pwm_buffer[IS31FL3733_PWM_REGISTER_COUNT]; + bool pwm_buffer_dirty; + uint8_t led_control_buffer[IS31FL3733_LED_CONTROL_REGISTER_COUNT]; + bool led_control_buffer_dirty; +} PACKED is31fl3733_driver_t; + +is31fl3733_driver_t driver_buffers[IS31FL3733_DRIVER_COUNT] = {{ + .pwm_buffer = {0}, + .pwm_buffer_dirty = false, + .led_control_buffer = {0}, + .led_control_buffer_dirty = false, +}}; + +void is31fl3733_write_register(uint8_t index, uint8_t reg, uint8_t data) { #if IS31FL3733_I2C_PERSISTENCE > 0 for (uint8_t i = 0; i < IS31FL3733_I2C_PERSISTENCE; i++) { - if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, IS31FL3733_I2C_TIMEOUT) != 0) { - return false; - } + if (i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3733_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break; } #else - if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, IS31FL3733_I2C_TIMEOUT) != 0) { - return false; - } + i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3733_I2C_TIMEOUT); #endif - return true; } -bool is31fl3733_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer) { - // Assumes PG1 is already selected. - // If any of the transactions fails function returns false. +void is31fl3733_select_page(uint8_t index, uint8_t page) { + is31fl3733_write_register(index, IS31FL3733_REG_COMMAND_WRITE_LOCK, IS31FL3733_COMMAND_WRITE_LOCK_MAGIC); + is31fl3733_write_register(index, IS31FL3733_REG_COMMAND, page); +} + +void is31fl3733_write_pwm_buffer(uint8_t index) { + // Assumes page 1 is already selected. // Transmit PWM registers in 12 transfers of 16 bytes. - // g_twi_transfer_buffer[] is 20 bytes // Iterate over the pwm_buffer contents at 16 byte intervals. - for (int i = 0; i < IS31FL3733_PWM_REGISTER_COUNT; i += 16) { - g_twi_transfer_buffer[0] = i; - // Copy the data from i to i+15. - // Device will auto-increment register for data after the first byte - // Thus this sets registers 0x00-0x0F, 0x10-0x1F, etc. in one transfer. - memcpy(g_twi_transfer_buffer + 1, pwm_buffer + i, 16); - + for (uint8_t i = 0; i < IS31FL3733_PWM_REGISTER_COUNT; i += 16) { #if IS31FL3733_I2C_PERSISTENCE > 0 - for (uint8_t i = 0; i < IS31FL3733_I2C_PERSISTENCE; i++) { - if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 17, IS31FL3733_I2C_TIMEOUT) != 0) { - return false; - } + for (uint8_t j = 0; j < IS31FL3733_I2C_PERSISTENCE; j++) { + if (i2c_write_register(i2c_addresses[index] << 1, i, driver_buffers[index].pwm_buffer + i, 16, IS31FL3733_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break; } #else - if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 17, IS31FL3733_I2C_TIMEOUT) != 0) { - return false; - } + i2c_write_register(i2c_addresses[index] << 1, i, driver_buffers[index].pwm_buffer + i, 16, IS31FL3733_I2C_TIMEOUT); #endif } - return true; } void is31fl3733_init_drivers(void) { i2c_init(); - is31fl3733_init(IS31FL3733_I2C_ADDRESS_1, IS31FL3733_SYNC_1); -#if defined(IS31FL3733_I2C_ADDRESS_2) - is31fl3733_init(IS31FL3733_I2C_ADDRESS_2, IS31FL3733_SYNC_2); -# if defined(IS31FL3733_I2C_ADDRESS_3) - is31fl3733_init(IS31FL3733_I2C_ADDRESS_3, IS31FL3733_SYNC_3); -# if defined(IS31FL3733_I2C_ADDRESS_4) - is31fl3733_init(IS31FL3733_I2C_ADDRESS_4, IS31FL3733_SYNC_4); -# endif -# endif +#if defined(IS31FL3733_SDB_PIN) + gpio_set_pin_output(IS31FL3733_SDB_PIN); + gpio_write_pin_high(IS31FL3733_SDB_PIN); #endif + for (uint8_t i = 0; i < IS31FL3733_DRIVER_COUNT; i++) { + is31fl3733_init(i); + } + for (int i = 0; i < IS31FL3733_LED_COUNT; i++) { is31fl3733_set_led_control_register(i, true, true, true); } - is31fl3733_update_led_control_registers(IS31FL3733_I2C_ADDRESS_1, 0); -#if defined(IS31FL3733_I2C_ADDRESS_2) - is31fl3733_update_led_control_registers(IS31FL3733_I2C_ADDRESS_2, 1); -# if defined(IS31FL3733_I2C_ADDRESS_3) - is31fl3733_update_led_control_registers(IS31FL3733_I2C_ADDRESS_3, 2); -# if defined(IS31FL3733_I2C_ADDRESS_4) - is31fl3733_update_led_control_registers(IS31FL3733_I2C_ADDRESS_4, 3); -# endif -# endif -#endif + for (uint8_t i = 0; i < IS31FL3733_DRIVER_COUNT; i++) { + is31fl3733_update_led_control_registers(i); + } } -void is31fl3733_init(uint8_t addr, uint8_t sync) { +void is31fl3733_init(uint8_t index) { // In order to avoid the LEDs being driven with garbage data // in the LED driver's PWM registers, shutdown is enabled last. // Set up the mode and other settings, clear the PWM registers, // then disable software shutdown. - // Sync is passed so set it according to the datasheet. - // Unlock the command register. - is31fl3733_write_register(addr, IS31FL3733_REG_COMMAND_WRITE_LOCK, IS31FL3733_COMMAND_WRITE_LOCK_MAGIC); + is31fl3733_select_page(index, IS31FL3733_COMMAND_LED_CONTROL); - // Select PG0 - is31fl3733_write_register(addr, IS31FL3733_REG_COMMAND, IS31FL3733_COMMAND_LED_CONTROL); // Turn off all LEDs. - for (int i = 0; i < IS31FL3733_LED_CONTROL_REGISTER_COUNT; i++) { - is31fl3733_write_register(addr, i, 0x00); + for (uint8_t i = 0; i < IS31FL3733_LED_CONTROL_REGISTER_COUNT; i++) { + is31fl3733_write_register(index, i, 0x00); } - // Unlock the command register. - is31fl3733_write_register(addr, IS31FL3733_REG_COMMAND_WRITE_LOCK, IS31FL3733_COMMAND_WRITE_LOCK_MAGIC); + is31fl3733_select_page(index, IS31FL3733_COMMAND_PWM); - // Select PG1 - is31fl3733_write_register(addr, IS31FL3733_REG_COMMAND, IS31FL3733_COMMAND_PWM); // Set PWM on all LEDs to 0 // No need to setup Breath registers to PWM as that is the default. - for (int i = 0; i < IS31FL3733_PWM_REGISTER_COUNT; i++) { - is31fl3733_write_register(addr, i, 0x00); + for (uint8_t i = 0; i < IS31FL3733_PWM_REGISTER_COUNT; i++) { + is31fl3733_write_register(index, i, 0x00); } - // Unlock the command register. - is31fl3733_write_register(addr, IS31FL3733_REG_COMMAND_WRITE_LOCK, IS31FL3733_COMMAND_WRITE_LOCK_MAGIC); + is31fl3733_select_page(index, IS31FL3733_COMMAND_FUNCTION); + + uint8_t sync = driver_sync[index]; - // Select PG3 - is31fl3733_write_register(addr, IS31FL3733_REG_COMMAND, IS31FL3733_COMMAND_FUNCTION); // Set de-ghost pull-up resistors (SWx) - is31fl3733_write_register(addr, IS31FL3733_FUNCTION_REG_SW_PULLUP, IS31FL3733_SW_PULLUP); + is31fl3733_write_register(index, IS31FL3733_FUNCTION_REG_SW_PULLUP, IS31FL3733_SW_PULLUP); // Set de-ghost pull-down resistors (CSx) - is31fl3733_write_register(addr, IS31FL3733_FUNCTION_REG_CS_PULLDOWN, IS31FL3733_CS_PULLDOWN); + is31fl3733_write_register(index, IS31FL3733_FUNCTION_REG_CS_PULLDOWN, IS31FL3733_CS_PULLDOWN); // Set global current to maximum. - is31fl3733_write_register(addr, IS31FL3733_FUNCTION_REG_GLOBAL_CURRENT, IS31FL3733_GLOBAL_CURRENT); + is31fl3733_write_register(index, IS31FL3733_FUNCTION_REG_GLOBAL_CURRENT, IS31FL3733_GLOBAL_CURRENT); // Disable software shutdown. - is31fl3733_write_register(addr, IS31FL3733_FUNCTION_REG_CONFIGURATION, ((sync & 0b11) << 6) | ((IS31FL3733_PWM_FREQUENCY & 0b111) << 3) | 0x01); + is31fl3733_write_register(index, IS31FL3733_FUNCTION_REG_CONFIGURATION, ((sync & 0b11) << 6) | ((IS31FL3733_PWM_FREQUENCY & 0b111) << 3) | 0x01); // Wait 10ms to ensure the device has woken up. wait_ms(10); @@ -203,16 +200,18 @@ void is31fl3733_init(uint8_t addr, uint8_t sync) { void is31fl3733_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) { is31fl3733_led_t led; + if (index >= 0 && index < IS31FL3733_LED_COUNT) { memcpy_P(&led, (&g_is31fl3733_leds[index]), sizeof(led)); - if (g_pwm_buffer[led.driver][led.r] == red && g_pwm_buffer[led.driver][led.g] == green && g_pwm_buffer[led.driver][led.b] == blue) { + if (driver_buffers[led.driver].pwm_buffer[led.r] == red && driver_buffers[led.driver].pwm_buffer[led.g] == green && driver_buffers[led.driver].pwm_buffer[led.b] == blue) { return; } - g_pwm_buffer[led.driver][led.r] = red; - g_pwm_buffer[led.driver][led.g] = green; - g_pwm_buffer[led.driver][led.b] = blue; - g_pwm_buffer_update_required[led.driver] = true; + + driver_buffers[led.driver].pwm_buffer[led.r] = red; + driver_buffers[led.driver].pwm_buffer[led.g] = green; + driver_buffers[led.driver].pwm_buffer[led.b] = blue; + driver_buffers[led.driver].pwm_buffer_dirty = true; } } @@ -234,60 +233,48 @@ void is31fl3733_set_led_control_register(uint8_t index, bool red, bool green, bo uint8_t bit_b = led.b % 8; if (red) { - g_led_control_registers[led.driver][control_register_r] |= (1 << bit_r); + driver_buffers[led.driver].led_control_buffer[control_register_r] |= (1 << bit_r); } else { - g_led_control_registers[led.driver][control_register_r] &= ~(1 << bit_r); + driver_buffers[led.driver].led_control_buffer[control_register_r] &= ~(1 << bit_r); } if (green) { - g_led_control_registers[led.driver][control_register_g] |= (1 << bit_g); + driver_buffers[led.driver].led_control_buffer[control_register_g] |= (1 << bit_g); } else { - g_led_control_registers[led.driver][control_register_g] &= ~(1 << bit_g); + driver_buffers[led.driver].led_control_buffer[control_register_g] &= ~(1 << bit_g); } if (blue) { - g_led_control_registers[led.driver][control_register_b] |= (1 << bit_b); + driver_buffers[led.driver].led_control_buffer[control_register_b] |= (1 << bit_b); } else { - g_led_control_registers[led.driver][control_register_b] &= ~(1 << bit_b); + driver_buffers[led.driver].led_control_buffer[control_register_b] &= ~(1 << bit_b); } - g_led_control_registers_update_required[led.driver] = true; + driver_buffers[led.driver].led_control_buffer_dirty = true; } -void is31fl3733_update_pwm_buffers(uint8_t addr, uint8_t index) { - if (g_pwm_buffer_update_required[index]) { - // Firstly we need to unlock the command register and select PG1. - is31fl3733_write_register(addr, IS31FL3733_REG_COMMAND_WRITE_LOCK, IS31FL3733_COMMAND_WRITE_LOCK_MAGIC); - is31fl3733_write_register(addr, IS31FL3733_REG_COMMAND, IS31FL3733_COMMAND_PWM); +void is31fl3733_update_pwm_buffers(uint8_t index) { + if (driver_buffers[index].pwm_buffer_dirty) { + is31fl3733_select_page(index, IS31FL3733_COMMAND_PWM); - // If any of the transactions fail we risk writing dirty PG0, - // refresh page 0 just in case. - if (!is31fl3733_write_pwm_buffer(addr, g_pwm_buffer[index])) { - g_led_control_registers_update_required[index] = true; - } - g_pwm_buffer_update_required[index] = false; + is31fl3733_write_pwm_buffer(index); + + driver_buffers[index].pwm_buffer_dirty = false; } } -void is31fl3733_update_led_control_registers(uint8_t addr, uint8_t index) { - if (g_led_control_registers_update_required[index]) { - // Firstly we need to unlock the command register and select PG0 - is31fl3733_write_register(addr, IS31FL3733_REG_COMMAND_WRITE_LOCK, IS31FL3733_COMMAND_WRITE_LOCK_MAGIC); - is31fl3733_write_register(addr, IS31FL3733_REG_COMMAND, IS31FL3733_COMMAND_LED_CONTROL); - for (int i = 0; i < IS31FL3733_LED_CONTROL_REGISTER_COUNT; i++) { - is31fl3733_write_register(addr, i, g_led_control_registers[index][i]); +void is31fl3733_update_led_control_registers(uint8_t index) { + if (driver_buffers[index].led_control_buffer_dirty) { + is31fl3733_select_page(index, IS31FL3733_COMMAND_LED_CONTROL); + + for (uint8_t i = 0; i < IS31FL3733_LED_CONTROL_REGISTER_COUNT; i++) { + is31fl3733_write_register(index, i, driver_buffers[index].led_control_buffer[i]); } - g_led_control_registers_update_required[index] = false; + + driver_buffers[index].led_control_buffer_dirty = false; } } void is31fl3733_flush(void) { - is31fl3733_update_pwm_buffers(IS31FL3733_I2C_ADDRESS_1, 0); -#if defined(IS31FL3733_I2C_ADDRESS_2) - is31fl3733_update_pwm_buffers(IS31FL3733_I2C_ADDRESS_2, 1); -# if defined(IS31FL3733_I2C_ADDRESS_3) - is31fl3733_update_pwm_buffers(IS31FL3733_I2C_ADDRESS_3, 2); -# if defined(IS31FL3733_I2C_ADDRESS_4) - is31fl3733_update_pwm_buffers(IS31FL3733_I2C_ADDRESS_4, 3); -# endif -# endif -#endif + for (uint8_t i = 0; i < IS31FL3733_DRIVER_COUNT; i++) { + is31fl3733_update_pwm_buffers(i); + } } diff --git a/drivers/led/issi/is31fl3733.h b/drivers/led/issi/is31fl3733.h index 20804b016b..fb60c76c2d 100644 --- a/drivers/led/issi/is31fl3733.h +++ b/drivers/led/issi/is31fl3733.h @@ -40,13 +40,13 @@ #ifdef DRIVER_SYNC_1 # define IS31FL3733_SYNC_1 DRIVER_SYNC_1 #endif -#ifdef DRIVER_ADDR_2 +#ifdef DRIVER_SYNC_2 # define IS31FL3733_SYNC_2 DRIVER_SYNC_2 #endif -#ifdef DRIVER_ADDR_3 +#ifdef DRIVER_SYNC_3 # define IS31FL3733_SYNC_3 DRIVER_SYNC_3 #endif -#ifdef DRIVER_ADDR_4 +#ifdef DRIVER_SYNC_4 # define IS31FL3733_SYNC_4 DRIVER_SYNC_4 #endif #ifdef ISSI_TIMEOUT @@ -140,9 +140,9 @@ typedef struct is31fl3733_led_t { extern const is31fl3733_led_t PROGMEM g_is31fl3733_leds[IS31FL3733_LED_COUNT]; void is31fl3733_init_drivers(void); -void is31fl3733_init(uint8_t addr, uint8_t sync); -bool is31fl3733_write_register(uint8_t addr, uint8_t reg, uint8_t data); -bool is31fl3733_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer); +void is31fl3733_init(uint8_t index); +void is31fl3733_write_register(uint8_t index, uint8_t reg, uint8_t data); +void is31fl3733_select_page(uint8_t index, uint8_t page); void is31fl3733_set_color(int index, uint8_t red, uint8_t green, uint8_t blue); void is31fl3733_set_color_all(uint8_t red, uint8_t green, uint8_t blue); @@ -153,8 +153,8 @@ void is31fl3733_set_led_control_register(uint8_t index, bool red, bool green, bo // (eg. from a timer interrupt). // Call this while idle (in between matrix scans). // If the buffer is dirty, it will update the driver with the buffer. -void is31fl3733_update_pwm_buffers(uint8_t addr, uint8_t index); -void is31fl3733_update_led_control_registers(uint8_t addr, uint8_t index); +void is31fl3733_update_pwm_buffers(uint8_t index); +void is31fl3733_update_led_control_registers(uint8_t index); void is31fl3733_flush(void); @@ -186,206 +186,412 @@ void is31fl3733_flush(void); #define IS31FL3733_SYNC_MASTER 0b01 #define IS31FL3733_SYNC_SLAVE 0b10 -#define A_1 0x00 -#define A_2 0x01 -#define A_3 0x02 -#define A_4 0x03 -#define A_5 0x04 -#define A_6 0x05 -#define A_7 0x06 -#define A_8 0x07 -#define A_9 0x08 -#define A_10 0x09 -#define A_11 0x0A -#define A_12 0x0B -#define A_13 0x0C -#define A_14 0x0D -#define A_15 0x0E -#define A_16 0x0F - -#define B_1 0x10 -#define B_2 0x11 -#define B_3 0x12 -#define B_4 0x13 -#define B_5 0x14 -#define B_6 0x15 -#define B_7 0x16 -#define B_8 0x17 -#define B_9 0x18 -#define B_10 0x19 -#define B_11 0x1A -#define B_12 0x1B -#define B_13 0x1C -#define B_14 0x1D -#define B_15 0x1E -#define B_16 0x1F - -#define C_1 0x20 -#define C_2 0x21 -#define C_3 0x22 -#define C_4 0x23 -#define C_5 0x24 -#define C_6 0x25 -#define C_7 0x26 -#define C_8 0x27 -#define C_9 0x28 -#define C_10 0x29 -#define C_11 0x2A -#define C_12 0x2B -#define C_13 0x2C -#define C_14 0x2D -#define C_15 0x2E -#define C_16 0x2F - -#define D_1 0x30 -#define D_2 0x31 -#define D_3 0x32 -#define D_4 0x33 -#define D_5 0x34 -#define D_6 0x35 -#define D_7 0x36 -#define D_8 0x37 -#define D_9 0x38 -#define D_10 0x39 -#define D_11 0x3A -#define D_12 0x3B -#define D_13 0x3C -#define D_14 0x3D -#define D_15 0x3E -#define D_16 0x3F - -#define E_1 0x40 -#define E_2 0x41 -#define E_3 0x42 -#define E_4 0x43 -#define E_5 0x44 -#define E_6 0x45 -#define E_7 0x46 -#define E_8 0x47 -#define E_9 0x48 -#define E_10 0x49 -#define E_11 0x4A -#define E_12 0x4B -#define E_13 0x4C -#define E_14 0x4D -#define E_15 0x4E -#define E_16 0x4F - -#define F_1 0x50 -#define F_2 0x51 -#define F_3 0x52 -#define F_4 0x53 -#define F_5 0x54 -#define F_6 0x55 -#define F_7 0x56 -#define F_8 0x57 -#define F_9 0x58 -#define F_10 0x59 -#define F_11 0x5A -#define F_12 0x5B -#define F_13 0x5C -#define F_14 0x5D -#define F_15 0x5E -#define F_16 0x5F - -#define G_1 0x60 -#define G_2 0x61 -#define G_3 0x62 -#define G_4 0x63 -#define G_5 0x64 -#define G_6 0x65 -#define G_7 0x66 -#define G_8 0x67 -#define G_9 0x68 -#define G_10 0x69 -#define G_11 0x6A -#define G_12 0x6B -#define G_13 0x6C -#define G_14 0x6D -#define G_15 0x6E -#define G_16 0x6F - -#define H_1 0x70 -#define H_2 0x71 -#define H_3 0x72 -#define H_4 0x73 -#define H_5 0x74 -#define H_6 0x75 -#define H_7 0x76 -#define H_8 0x77 -#define H_9 0x78 -#define H_10 0x79 -#define H_11 0x7A -#define H_12 0x7B -#define H_13 0x7C -#define H_14 0x7D -#define H_15 0x7E -#define H_16 0x7F - -#define I_1 0x80 -#define I_2 0x81 -#define I_3 0x82 -#define I_4 0x83 -#define I_5 0x84 -#define I_6 0x85 -#define I_7 0x86 -#define I_8 0x87 -#define I_9 0x88 -#define I_10 0x89 -#define I_11 0x8A -#define I_12 0x8B -#define I_13 0x8C -#define I_14 0x8D -#define I_15 0x8E -#define I_16 0x8F - -#define J_1 0x90 -#define J_2 0x91 -#define J_3 0x92 -#define J_4 0x93 -#define J_5 0x94 -#define J_6 0x95 -#define J_7 0x96 -#define J_8 0x97 -#define J_9 0x98 -#define J_10 0x99 -#define J_11 0x9A -#define J_12 0x9B -#define J_13 0x9C -#define J_14 0x9D -#define J_15 0x9E -#define J_16 0x9F - -#define K_1 0xA0 -#define K_2 0xA1 -#define K_3 0xA2 -#define K_4 0xA3 -#define K_5 0xA4 -#define K_6 0xA5 -#define K_7 0xA6 -#define K_8 0xA7 -#define K_9 0xA8 -#define K_10 0xA9 -#define K_11 0xAA -#define K_12 0xAB -#define K_13 0xAC -#define K_14 0xAD -#define K_15 0xAE -#define K_16 0xAF - -#define L_1 0xB0 -#define L_2 0xB1 -#define L_3 0xB2 -#define L_4 0xB3 -#define L_5 0xB4 -#define L_6 0xB5 -#define L_7 0xB6 -#define L_8 0xB7 -#define L_9 0xB8 -#define L_10 0xB9 -#define L_11 0xBA -#define L_12 0xBB -#define L_13 0xBC -#define L_14 0xBD -#define L_15 0xBE -#define L_16 0xBF +#define SW1_CS1 0x00 +#define SW1_CS2 0x01 +#define SW1_CS3 0x02 +#define SW1_CS4 0x03 +#define SW1_CS5 0x04 +#define SW1_CS6 0x05 +#define SW1_CS7 0x06 +#define SW1_CS8 0x07 +#define SW1_CS9 0x08 +#define SW1_CS10 0x09 +#define SW1_CS11 0x0A +#define SW1_CS12 0x0B +#define SW1_CS13 0x0C +#define SW1_CS14 0x0D +#define SW1_CS15 0x0E +#define SW1_CS16 0x0F + +#define SW2_CS1 0x10 +#define SW2_CS2 0x11 +#define SW2_CS3 0x12 +#define SW2_CS4 0x13 +#define SW2_CS5 0x14 +#define SW2_CS6 0x15 +#define SW2_CS7 0x16 +#define SW2_CS8 0x17 +#define SW2_CS9 0x18 +#define SW2_CS10 0x19 +#define SW2_CS11 0x1A +#define SW2_CS12 0x1B +#define SW2_CS13 0x1C +#define SW2_CS14 0x1D +#define SW2_CS15 0x1E +#define SW2_CS16 0x1F + +#define SW3_CS1 0x20 +#define SW3_CS2 0x21 +#define SW3_CS3 0x22 +#define SW3_CS4 0x23 +#define SW3_CS5 0x24 +#define SW3_CS6 0x25 +#define SW3_CS7 0x26 +#define SW3_CS8 0x27 +#define SW3_CS9 0x28 +#define SW3_CS10 0x29 +#define SW3_CS11 0x2A +#define SW3_CS12 0x2B +#define SW3_CS13 0x2C +#define SW3_CS14 0x2D +#define SW3_CS15 0x2E +#define SW3_CS16 0x2F + +#define SW4_CS1 0x30 +#define SW4_CS2 0x31 +#define SW4_CS3 0x32 +#define SW4_CS4 0x33 +#define SW4_CS5 0x34 +#define SW4_CS6 0x35 +#define SW4_CS7 0x36 +#define SW4_CS8 0x37 +#define SW4_CS9 0x38 +#define SW4_CS10 0x39 +#define SW4_CS11 0x3A +#define SW4_CS12 0x3B +#define SW4_CS13 0x3C +#define SW4_CS14 0x3D +#define SW4_CS15 0x3E +#define SW4_CS16 0x3F + +#define SW5_CS1 0x40 +#define SW5_CS2 0x41 +#define SW5_CS3 0x42 +#define SW5_CS4 0x43 +#define SW5_CS5 0x44 +#define SW5_CS6 0x45 +#define SW5_CS7 0x46 +#define SW5_CS8 0x47 +#define SW5_CS9 0x48 +#define SW5_CS10 0x49 +#define SW5_CS11 0x4A +#define SW5_CS12 0x4B +#define SW5_CS13 0x4C +#define SW5_CS14 0x4D +#define SW5_CS15 0x4E +#define SW5_CS16 0x4F + +#define SW6_CS1 0x50 +#define SW6_CS2 0x51 +#define SW6_CS3 0x52 +#define SW6_CS4 0x53 +#define SW6_CS5 0x54 +#define SW6_CS6 0x55 +#define SW6_CS7 0x56 +#define SW6_CS8 0x57 +#define SW6_CS9 0x58 +#define SW6_CS10 0x59 +#define SW6_CS11 0x5A +#define SW6_CS12 0x5B +#define SW6_CS13 0x5C +#define SW6_CS14 0x5D +#define SW6_CS15 0x5E +#define SW6_CS16 0x5F + +#define SW7_CS1 0x60 +#define SW7_CS2 0x61 +#define SW7_CS3 0x62 +#define SW7_CS4 0x63 +#define SW7_CS5 0x64 +#define SW7_CS6 0x65 +#define SW7_CS7 0x66 +#define SW7_CS8 0x67 +#define SW7_CS9 0x68 +#define SW7_CS10 0x69 +#define SW7_CS11 0x6A +#define SW7_CS12 0x6B +#define SW7_CS13 0x6C +#define SW7_CS14 0x6D +#define SW7_CS15 0x6E +#define SW7_CS16 0x6F + +#define SW8_CS1 0x70 +#define SW8_CS2 0x71 +#define SW8_CS3 0x72 +#define SW8_CS4 0x73 +#define SW8_CS5 0x74 +#define SW8_CS6 0x75 +#define SW8_CS7 0x76 +#define SW8_CS8 0x77 +#define SW8_CS9 0x78 +#define SW8_CS10 0x79 +#define SW8_CS11 0x7A +#define SW8_CS12 0x7B +#define SW8_CS13 0x7C +#define SW8_CS14 0x7D +#define SW8_CS15 0x7E +#define SW8_CS16 0x7F + +#define SW9_CS1 0x80 +#define SW9_CS2 0x81 +#define SW9_CS3 0x82 +#define SW9_CS4 0x83 +#define SW9_CS5 0x84 +#define SW9_CS6 0x85 +#define SW9_CS7 0x86 +#define SW9_CS8 0x87 +#define SW9_CS9 0x88 +#define SW9_CS10 0x89 +#define SW9_CS11 0x8A +#define SW9_CS12 0x8B +#define SW9_CS13 0x8C +#define SW9_CS14 0x8D +#define SW9_CS15 0x8E +#define SW9_CS16 0x8F + +#define SW10_CS1 0x90 +#define SW10_CS2 0x91 +#define SW10_CS3 0x92 +#define SW10_CS4 0x93 +#define SW10_CS5 0x94 +#define SW10_CS6 0x95 +#define SW10_CS7 0x96 +#define SW10_CS8 0x97 +#define SW10_CS9 0x98 +#define SW10_CS10 0x99 +#define SW10_CS11 0x9A +#define SW10_CS12 0x9B +#define SW10_CS13 0x9C +#define SW10_CS14 0x9D +#define SW10_CS15 0x9E +#define SW10_CS16 0x9F + +#define SW11_CS1 0xA0 +#define SW11_CS2 0xA1 +#define SW11_CS3 0xA2 +#define SW11_CS4 0xA3 +#define SW11_CS5 0xA4 +#define SW11_CS6 0xA5 +#define SW11_CS7 0xA6 +#define SW11_CS8 0xA7 +#define SW11_CS9 0xA8 +#define SW11_CS10 0xA9 +#define SW11_CS11 0xAA +#define SW11_CS12 0xAB +#define SW11_CS13 0xAC +#define SW11_CS14 0xAD +#define SW11_CS15 0xAE +#define SW11_CS16 0xAF + +#define SW12_CS1 0xB0 +#define SW12_CS2 0xB1 +#define SW12_CS3 0xB2 +#define SW12_CS4 0xB3 +#define SW12_CS5 0xB4 +#define SW12_CS6 0xB5 +#define SW12_CS7 0xB6 +#define SW12_CS8 0xB7 +#define SW12_CS9 0xB8 +#define SW12_CS10 0xB9 +#define SW12_CS11 0xBA +#define SW12_CS12 0xBB +#define SW12_CS13 0xBC +#define SW12_CS14 0xBD +#define SW12_CS15 0xBE +#define SW12_CS16 0xBF + +// DEPRECATED - DO NOT USE + +#define A_1 SW1_CS1 +#define A_2 SW1_CS2 +#define A_3 SW1_CS3 +#define A_4 SW1_CS4 +#define A_5 SW1_CS5 +#define A_6 SW1_CS6 +#define A_7 SW1_CS7 +#define A_8 SW1_CS8 +#define A_9 SW1_CS9 +#define A_10 SW1_CS10 +#define A_11 SW1_CS11 +#define A_12 SW1_CS12 +#define A_13 SW1_CS13 +#define A_14 SW1_CS14 +#define A_15 SW1_CS15 +#define A_16 SW1_CS16 + +#define B_1 SW2_CS1 +#define B_2 SW2_CS2 +#define B_3 SW2_CS3 +#define B_4 SW2_CS4 +#define B_5 SW2_CS5 +#define B_6 SW2_CS6 +#define B_7 SW2_CS7 +#define B_8 SW2_CS8 +#define B_9 SW2_CS9 +#define B_10 SW2_CS10 +#define B_11 SW2_CS11 +#define B_12 SW2_CS12 +#define B_13 SW2_CS13 +#define B_14 SW2_CS14 +#define B_15 SW2_CS15 +#define B_16 SW2_CS16 + +#define C_1 SW3_CS1 +#define C_2 SW3_CS2 +#define C_3 SW3_CS3 +#define C_4 SW3_CS4 +#define C_5 SW3_CS5 +#define C_6 SW3_CS6 +#define C_7 SW3_CS7 +#define C_8 SW3_CS8 +#define C_9 SW3_CS9 +#define C_10 SW3_CS10 +#define C_11 SW3_CS11 +#define C_12 SW3_CS12 +#define C_13 SW3_CS13 +#define C_14 SW3_CS14 +#define C_15 SW3_CS15 +#define C_16 SW3_CS16 + +#define D_1 SW4_CS1 +#define D_2 SW4_CS2 +#define D_3 SW4_CS3 +#define D_4 SW4_CS4 +#define D_5 SW4_CS5 +#define D_6 SW4_CS6 +#define D_7 SW4_CS7 +#define D_8 SW4_CS8 +#define D_9 SW4_CS9 +#define D_10 SW4_CS10 +#define D_11 SW4_CS11 +#define D_12 SW4_CS12 +#define D_13 SW4_CS13 +#define D_14 SW4_CS14 +#define D_15 SW4_CS15 +#define D_16 SW4_CS16 + +#define E_1 SW5_CS1 +#define E_2 SW5_CS2 +#define E_3 SW5_CS3 +#define E_4 SW5_CS4 +#define E_5 SW5_CS5 +#define E_6 SW5_CS6 +#define E_7 SW5_CS7 +#define E_8 SW5_CS8 +#define E_9 SW5_CS9 +#define E_10 SW5_CS10 +#define E_11 SW5_CS11 +#define E_12 SW5_CS12 +#define E_13 SW5_CS13 +#define E_14 SW5_CS14 +#define E_15 SW5_CS15 +#define E_16 SW5_CS16 + +#define F_1 SW6_CS1 +#define F_2 SW6_CS2 +#define F_3 SW6_CS3 +#define F_4 SW6_CS4 +#define F_5 SW6_CS5 +#define F_6 SW6_CS6 +#define F_7 SW6_CS7 +#define F_8 SW6_CS8 +#define F_9 SW6_CS9 +#define F_10 SW6_CS10 +#define F_11 SW6_CS11 +#define F_12 SW6_CS12 +#define F_13 SW6_CS13 +#define F_14 SW6_CS14 +#define F_15 SW6_CS15 +#define F_16 SW6_CS16 + +#define G_1 SW7_CS1 +#define G_2 SW7_CS2 +#define G_3 SW7_CS3 +#define G_4 SW7_CS4 +#define G_5 SW7_CS5 +#define G_6 SW7_CS6 +#define G_7 SW7_CS7 +#define G_8 SW7_CS8 +#define G_9 SW7_CS9 +#define G_10 SW7_CS10 +#define G_11 SW7_CS11 +#define G_12 SW7_CS12 +#define G_13 SW7_CS13 +#define G_14 SW7_CS14 +#define G_15 SW7_CS15 +#define G_16 SW7_CS16 + +#define H_1 SW8_CS1 +#define H_2 SW8_CS2 +#define H_3 SW8_CS3 +#define H_4 SW8_CS4 +#define H_5 SW8_CS5 +#define H_6 SW8_CS6 +#define H_7 SW8_CS7 +#define H_8 SW8_CS8 +#define H_9 SW8_CS9 +#define H_10 SW8_CS10 +#define H_11 SW8_CS11 +#define H_12 SW8_CS12 +#define H_13 SW8_CS13 +#define H_14 SW8_CS14 +#define H_15 SW8_CS15 +#define H_16 SW8_CS16 + +#define I_1 SW9_CS1 +#define I_2 SW9_CS2 +#define I_3 SW9_CS3 +#define I_4 SW9_CS4 +#define I_5 SW9_CS5 +#define I_6 SW9_CS6 +#define I_7 SW9_CS7 +#define I_8 SW9_CS8 +#define I_9 SW9_CS9 +#define I_10 SW9_CS10 +#define I_11 SW9_CS11 +#define I_12 SW9_CS12 +#define I_13 SW9_CS13 +#define I_14 SW9_CS14 +#define I_15 SW9_CS15 +#define I_16 SW9_CS16 + +#define J_1 SW10_CS1 +#define J_2 SW10_CS2 +#define J_3 SW10_CS3 +#define J_4 SW10_CS4 +#define J_5 SW10_CS5 +#define J_6 SW10_CS6 +#define J_7 SW10_CS7 +#define J_8 SW10_CS8 +#define J_9 SW10_CS9 +#define J_10 SW10_CS10 +#define J_11 SW10_CS11 +#define J_12 SW10_CS12 +#define J_13 SW10_CS13 +#define J_14 SW10_CS14 +#define J_15 SW10_CS15 +#define J_16 SW10_CS16 + +#define K_1 SW11_CS1 +#define K_2 SW11_CS2 +#define K_3 SW11_CS3 +#define K_4 SW11_CS4 +#define K_5 SW11_CS5 +#define K_6 SW11_CS6 +#define K_7 SW11_CS7 +#define K_8 SW11_CS8 +#define K_9 SW11_CS9 +#define K_10 SW11_CS10 +#define K_11 SW11_CS11 +#define K_12 SW11_CS12 +#define K_13 SW11_CS13 +#define K_14 SW11_CS14 +#define K_15 SW11_CS15 +#define K_16 SW11_CS16 + +#define L_1 SW12_CS1 +#define L_2 SW12_CS2 +#define L_3 SW12_CS3 +#define L_4 SW12_CS4 +#define L_5 SW12_CS5 +#define L_6 SW12_CS6 +#define L_7 SW12_CS7 +#define L_8 SW12_CS8 +#define L_9 SW12_CS9 +#define L_10 SW12_CS10 +#define L_11 SW12_CS11 +#define L_12 SW12_CS12 +#define L_13 SW12_CS13 +#define L_14 SW12_CS14 +#define L_15 SW12_CS15 +#define L_16 SW12_CS16 diff --git a/drivers/led/issi/is31fl3736-mono.c b/drivers/led/issi/is31fl3736-mono.c new file mode 100644 index 0000000000..7a5415c725 --- /dev/null +++ b/drivers/led/issi/is31fl3736-mono.c @@ -0,0 +1,240 @@ +/* Copyright 2018 Jason Williams (Wilba) + * Copyright 2021 Doni Crosby + * + * 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 2 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/>. + */ + +#include "is31fl3736-mono.h" +#include "i2c_master.h" +#include "gpio.h" +#include "wait.h" + +#define IS31FL3736_PWM_REGISTER_COUNT 192 // actually 96 +#define IS31FL3736_LED_CONTROL_REGISTER_COUNT 24 + +#ifndef IS31FL3736_I2C_TIMEOUT +# define IS31FL3736_I2C_TIMEOUT 100 +#endif + +#ifndef IS31FL3736_I2C_PERSISTENCE +# define IS31FL3736_I2C_PERSISTENCE 0 +#endif + +#ifndef IS31FL3736_PWM_FREQUENCY +# define IS31FL3736_PWM_FREQUENCY IS31FL3736_PWM_FREQUENCY_8K4_HZ // PFS - IS31FL3736B only +#endif + +#ifndef IS31FL3736_SW_PULLUP +# define IS31FL3736_SW_PULLUP IS31FL3736_PUR_0_OHM +#endif + +#ifndef IS31FL3736_CS_PULLDOWN +# define IS31FL3736_CS_PULLDOWN IS31FL3736_PDR_0_OHM +#endif + +#ifndef IS31FL3736_GLOBAL_CURRENT +# define IS31FL3736_GLOBAL_CURRENT 0xFF +#endif + +const uint8_t i2c_addresses[IS31FL3736_DRIVER_COUNT] = { + IS31FL3736_I2C_ADDRESS_1, +#ifdef IS31FL3736_I2C_ADDRESS_2 + IS31FL3736_I2C_ADDRESS_2, +# ifdef IS31FL3736_I2C_ADDRESS_3 + IS31FL3736_I2C_ADDRESS_3, +# ifdef IS31FL3736_I2C_ADDRESS_4 + IS31FL3736_I2C_ADDRESS_4, +# endif +# endif +#endif +}; + +// These buffers match the IS31FL3736 PWM registers. +// The control buffers match the page 0 LED On/Off registers. +// Storing them like this is optimal for I2C transfers to the registers. +// We could optimize this and take out the unused registers from these +// buffers and the transfers in is31fl3736_write_pwm_buffer() but it's +// probably not worth the extra complexity. +typedef struct is31fl3736_driver_t { + uint8_t pwm_buffer[IS31FL3736_PWM_REGISTER_COUNT]; + bool pwm_buffer_dirty; + uint8_t led_control_buffer[IS31FL3736_LED_CONTROL_REGISTER_COUNT]; + bool led_control_buffer_dirty; +} PACKED is31fl3736_driver_t; + +is31fl3736_driver_t driver_buffers[IS31FL3736_DRIVER_COUNT] = {{ + .pwm_buffer = {0}, + .pwm_buffer_dirty = false, + .led_control_buffer = {0}, + .led_control_buffer_dirty = false, +}}; + +void is31fl3736_write_register(uint8_t index, uint8_t reg, uint8_t data) { +#if IS31FL3736_I2C_PERSISTENCE > 0 + for (uint8_t i = 0; i < IS31FL3736_I2C_PERSISTENCE; i++) { + if (i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3736_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break; + } +#else + i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3736_I2C_TIMEOUT); +#endif +} + +void is31fl3736_select_page(uint8_t index, uint8_t page) { + is31fl3736_write_register(index, IS31FL3736_REG_COMMAND_WRITE_LOCK, IS31FL3736_COMMAND_WRITE_LOCK_MAGIC); + is31fl3736_write_register(index, IS31FL3736_REG_COMMAND, page); +} + +void is31fl3736_write_pwm_buffer(uint8_t index) { + // Assumes page 1 is already selected. + // Transmit PWM registers in 12 transfers of 16 bytes. + + // Iterate over the pwm_buffer contents at 16 byte intervals. + for (uint8_t i = 0; i < IS31FL3736_PWM_REGISTER_COUNT; i += 16) { +#if IS31FL3736_I2C_PERSISTENCE > 0 + for (uint8_t j = 0; j < IS31FL3736_I2C_PERSISTENCE; j++) { + if (i2c_write_register(i2c_addresses[index] << 1, i, driver_buffers[index].pwm_buffer + i, 16, IS31FL3736_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break; + } +#else + i2c_write_register(i2c_addresses[index] << 1, i, driver_buffers[index].pwm_buffer + i, 16, IS31FL3736_I2C_TIMEOUT); +#endif + } +} + +void is31fl3736_init_drivers(void) { + i2c_init(); + +#if defined(IS31FL3736_SDB_PIN) + gpio_set_pin_output(IS31FL3736_SDB_PIN); + gpio_write_pin_high(IS31FL3736_SDB_PIN); +#endif + + for (uint8_t i = 0; i < IS31FL3736_DRIVER_COUNT; i++) { + is31fl3736_init(i); + } + + for (int i = 0; i < IS31FL3736_LED_COUNT; i++) { + is31fl3736_set_led_control_register(i, true); + } + + for (uint8_t i = 0; i < IS31FL3736_DRIVER_COUNT; i++) { + is31fl3736_update_led_control_registers(i); + } +} + +void is31fl3736_init(uint8_t index) { + // In order to avoid the LEDs being driven with garbage data + // in the LED driver's PWM registers, shutdown is enabled last. + // Set up the mode and other settings, clear the PWM registers, + // then disable software shutdown. + + is31fl3736_select_page(index, IS31FL3736_COMMAND_LED_CONTROL); + + // Turn off all LEDs. + for (uint8_t i = 0; i < IS31FL3736_LED_CONTROL_REGISTER_COUNT; i++) { + is31fl3736_write_register(index, i, 0x00); + } + + is31fl3736_select_page(index, IS31FL3736_COMMAND_PWM); + + // Set PWM on all LEDs to 0 + // No need to setup Breath registers to PWM as that is the default. + for (uint8_t i = 0; i < IS31FL3736_PWM_REGISTER_COUNT; i++) { + is31fl3736_write_register(index, i, 0x00); + } + + is31fl3736_select_page(index, IS31FL3736_COMMAND_FUNCTION); + + // Set de-ghost pull-up resistors (SWx) + is31fl3736_write_register(index, IS31FL3736_FUNCTION_REG_SW_PULLUP, IS31FL3736_SW_PULLUP); + // Set de-ghost pull-down resistors (CSx) + is31fl3736_write_register(index, IS31FL3736_FUNCTION_REG_CS_PULLDOWN, IS31FL3736_CS_PULLDOWN); + // Set global current to maximum. + is31fl3736_write_register(index, IS31FL3736_FUNCTION_REG_GLOBAL_CURRENT, IS31FL3736_GLOBAL_CURRENT); + // Disable software shutdown. + is31fl3736_write_register(index, IS31FL3736_FUNCTION_REG_CONFIGURATION, ((IS31FL3736_PWM_FREQUENCY & 0b111) << 3) | 0x01); + + // Wait 10ms to ensure the device has woken up. + wait_ms(10); +} + +void is31fl3736_set_value(int index, uint8_t value) { + is31fl3736_led_t led; + + if (index >= 0 && index < IS31FL3736_LED_COUNT) { + memcpy_P(&led, (&g_is31fl3736_leds[index]), sizeof(led)); + + if (driver_buffers[led.driver].pwm_buffer[led.v] == value) { + return; + } + + driver_buffers[led.driver].pwm_buffer[led.v] = value; + driver_buffers[led.driver].pwm_buffer_dirty = true; + } +} + +void is31fl3736_set_value_all(uint8_t value) { + for (int i = 0; i < IS31FL3736_LED_COUNT; i++) { + is31fl3736_set_value(i, value); + } +} + +void is31fl3736_set_led_control_register(uint8_t index, bool value) { + is31fl3736_led_t led; + memcpy_P(&led, (&g_is31fl3736_leds[index]), sizeof(led)); + + // The PWM register for a matrix position (0x00 to 0xBF) is interleaved, so: + // A1=0x00 A2=0x02 A3=0x04 A4=0x06 A5=0x08 A6=0x0A A7=0x0C A8=0x0E + // B1=0x10 B2=0x12 B3=0x14 + // But also, the LED control registers (0x00 to 0x17) are also interleaved, so: + // A1-A4=0x00 A5-A8=0x01 + + uint8_t control_register = led.v / 8; + uint8_t bit_value = led.v % 8; + + if (value) { + driver_buffers[led.driver].led_control_buffer[control_register] |= (1 << bit_value); + } else { + driver_buffers[led.driver].led_control_buffer[control_register] &= ~(1 << bit_value); + } + + driver_buffers[led.driver].led_control_buffer_dirty = true; +} + +void is31fl3736_update_pwm_buffers(uint8_t index) { + if (driver_buffers[index].pwm_buffer_dirty) { + is31fl3736_select_page(index, IS31FL3736_COMMAND_PWM); + + is31fl3736_write_pwm_buffer(index); + + driver_buffers[index].pwm_buffer_dirty = false; + } +} + +void is31fl3736_update_led_control_registers(uint8_t index) { + if (driver_buffers[index].led_control_buffer_dirty) { + is31fl3736_select_page(index, IS31FL3736_COMMAND_LED_CONTROL); + + for (uint8_t i = 0; i < IS31FL3736_LED_CONTROL_REGISTER_COUNT; i++) { + is31fl3736_write_register(index, i, driver_buffers[index].led_control_buffer[i]); + } + + driver_buffers[index].led_control_buffer_dirty = false; + } +} + +void is31fl3736_flush(void) { + for (uint8_t i = 0; i < IS31FL3736_DRIVER_COUNT; i++) { + is31fl3736_update_pwm_buffers(i); + } +} diff --git a/drivers/led/issi/is31fl3736-simple.h b/drivers/led/issi/is31fl3736-mono.h index a73a872545..91c2e0420b 100644 --- a/drivers/led/issi/is31fl3736-simple.h +++ b/drivers/led/issi/is31fl3736-mono.h @@ -110,9 +110,9 @@ typedef struct is31fl3736_led_t { extern const is31fl3736_led_t PROGMEM g_is31fl3736_leds[IS31FL3736_LED_COUNT]; void is31fl3736_init_drivers(void); -void is31fl3736_init(uint8_t addr); -void is31fl3736_write_register(uint8_t addr, uint8_t reg, uint8_t data); -void is31fl3736_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer); +void is31fl3736_init(uint8_t index); +void is31fl3736_write_register(uint8_t index, uint8_t reg, uint8_t data); +void is31fl3736_select_page(uint8_t index, uint8_t page); void is31fl3736_set_value(int index, uint8_t value); void is31fl3736_set_value_all(uint8_t value); @@ -123,8 +123,8 @@ void is31fl3736_set_led_control_register(uint8_t index, bool value); // (eg. from a timer interrupt). // Call this while idle (in between matrix scans). // If the buffer is dirty, it will update the driver with the buffer. -void is31fl3736_update_pwm_buffers(uint8_t addr, uint8_t index); -void is31fl3736_update_led_control_registers(uint8_t addr, uint8_t index); +void is31fl3736_update_pwm_buffers(uint8_t index); +void is31fl3736_update_led_control_registers(uint8_t index); void is31fl3736_flush(void); @@ -152,110 +152,220 @@ void is31fl3736_flush(void); #define IS31FL3736_PWM_FREQUENCY_2K1_HZ 0b011 #define IS31FL3736_PWM_FREQUENCY_1K05_HZ 0b100 -#define A_1 0x00 -#define A_2 0x02 -#define A_3 0x04 -#define A_4 0x06 -#define A_5 0x08 -#define A_6 0x0A -#define A_7 0x0C -#define A_8 0x0E - -#define B_1 0x10 -#define B_2 0x12 -#define B_3 0x14 -#define B_4 0x16 -#define B_5 0x18 -#define B_6 0x1A -#define B_7 0x1C -#define B_8 0x1E - -#define C_1 0x20 -#define C_2 0x22 -#define C_3 0x24 -#define C_4 0x26 -#define C_5 0x28 -#define C_6 0x2A -#define C_7 0x2C -#define C_8 0x2E - -#define D_1 0x30 -#define D_2 0x32 -#define D_3 0x34 -#define D_4 0x36 -#define D_5 0x38 -#define D_6 0x3A -#define D_7 0x3C -#define D_8 0x3E - -#define E_1 0x40 -#define E_2 0x42 -#define E_3 0x44 -#define E_4 0x46 -#define E_5 0x48 -#define E_6 0x4A -#define E_7 0x4C -#define E_8 0x4E - -#define F_1 0x50 -#define F_2 0x52 -#define F_3 0x54 -#define F_4 0x56 -#define F_5 0x58 -#define F_6 0x5A -#define F_7 0x5C -#define F_8 0x5E - -#define G_1 0x60 -#define G_2 0x62 -#define G_3 0x64 -#define G_4 0x66 -#define G_5 0x68 -#define G_6 0x6A -#define G_7 0x6C -#define G_8 0x6E - -#define H_1 0x70 -#define H_2 0x72 -#define H_3 0x74 -#define H_4 0x76 -#define H_5 0x78 -#define H_6 0x7A -#define H_7 0x7C -#define H_8 0x7E - -#define I_1 0x80 -#define I_2 0x82 -#define I_3 0x84 -#define I_4 0x86 -#define I_5 0x88 -#define I_6 0x8A -#define I_7 0x8C -#define I_8 0x8E - -#define J_1 0x90 -#define J_2 0x92 -#define J_3 0x94 -#define J_4 0x96 -#define J_5 0x98 -#define J_6 0x9A -#define J_7 0x9C -#define J_8 0x9E - -#define K_1 0xA0 -#define K_2 0xA2 -#define K_3 0xA4 -#define K_4 0xA6 -#define K_5 0xA8 -#define K_6 0xAA -#define K_7 0xAC -#define K_8 0xAE - -#define L_1 0xB0 -#define L_2 0xB2 -#define L_3 0xB4 -#define L_4 0xB6 -#define L_5 0xB8 -#define L_6 0xBA -#define L_7 0xBC -#define L_8 0xBE +#define SW1_CS1 0x00 +#define SW1_CS2 0x02 +#define SW1_CS3 0x04 +#define SW1_CS4 0x06 +#define SW1_CS5 0x08 +#define SW1_CS6 0x0A +#define SW1_CS7 0x0C +#define SW1_CS8 0x0E + +#define SW2_CS1 0x10 +#define SW2_CS2 0x12 +#define SW2_CS3 0x14 +#define SW2_CS4 0x16 +#define SW2_CS5 0x18 +#define SW2_CS6 0x1A +#define SW2_CS7 0x1C +#define SW2_CS8 0x1E + +#define SW3_CS1 0x20 +#define SW3_CS2 0x22 +#define SW3_CS3 0x24 +#define SW3_CS4 0x26 +#define SW3_CS5 0x28 +#define SW3_CS6 0x2A +#define SW3_CS7 0x2C +#define SW3_CS8 0x2E + +#define SW4_CS1 0x30 +#define SW4_CS2 0x32 +#define SW4_CS3 0x34 +#define SW4_CS4 0x36 +#define SW4_CS5 0x38 +#define SW4_CS6 0x3A +#define SW4_CS7 0x3C +#define SW4_CS8 0x3E + +#define SW5_CS1 0x40 +#define SW5_CS2 0x42 +#define SW5_CS3 0x44 +#define SW5_CS4 0x46 +#define SW5_CS5 0x48 +#define SW5_CS6 0x4A +#define SW5_CS7 0x4C +#define SW5_CS8 0x4E + +#define SW6_CS1 0x50 +#define SW6_CS2 0x52 +#define SW6_CS3 0x54 +#define SW6_CS4 0x56 +#define SW6_CS5 0x58 +#define SW6_CS6 0x5A +#define SW6_CS7 0x5C +#define SW6_CS8 0x5E + +#define SW7_CS1 0x60 +#define SW7_CS2 0x62 +#define SW7_CS3 0x64 +#define SW7_CS4 0x66 +#define SW7_CS5 0x68 +#define SW7_CS6 0x6A +#define SW7_CS7 0x6C +#define SW7_CS8 0x6E + +#define SW8_CS1 0x70 +#define SW8_CS2 0x72 +#define SW8_CS3 0x74 +#define SW8_CS4 0x76 +#define SW8_CS5 0x78 +#define SW8_CS6 0x7A +#define SW8_CS7 0x7C +#define SW8_CS8 0x7E + +#define SW9_CS1 0x80 +#define SW9_CS2 0x82 +#define SW9_CS3 0x84 +#define SW9_CS4 0x86 +#define SW9_CS5 0x88 +#define SW9_CS6 0x8A +#define SW9_CS7 0x8C +#define SW9_CS8 0x8E + +#define SW10_CS1 0x90 +#define SW10_CS2 0x92 +#define SW10_CS3 0x94 +#define SW10_CS4 0x96 +#define SW10_CS5 0x98 +#define SW10_CS6 0x9A +#define SW10_CS7 0x9C +#define SW10_CS8 0x9E + +#define SW11_CS1 0xA0 +#define SW11_CS2 0xA2 +#define SW11_CS3 0xA4 +#define SW11_CS4 0xA6 +#define SW11_CS5 0xA8 +#define SW11_CS6 0xAA +#define SW11_CS7 0xAC +#define SW11_CS8 0xAE + +#define SW12_CS1 0xB0 +#define SW12_CS2 0xB2 +#define SW12_CS3 0xB4 +#define SW12_CS4 0xB6 +#define SW12_CS5 0xB8 +#define SW12_CS6 0xBA +#define SW12_CS7 0xBC +#define SW12_CS8 0xBE + +// DEPRECATED - DO NOT USE + +#define A_1 SW1_CS1 +#define A_2 SW1_CS2 +#define A_3 SW1_CS3 +#define A_4 SW1_CS4 +#define A_5 SW1_CS5 +#define A_6 SW1_CS6 +#define A_7 SW1_CS7 +#define A_8 SW1_CS8 + +#define B_1 SW2_CS1 +#define B_2 SW2_CS2 +#define B_3 SW2_CS3 +#define B_4 SW2_CS4 +#define B_5 SW2_CS5 +#define B_6 SW2_CS6 +#define B_7 SW2_CS7 +#define B_8 SW2_CS8 + +#define C_1 SW3_CS1 +#define C_2 SW3_CS2 +#define C_3 SW3_CS3 +#define C_4 SW3_CS4 +#define C_5 SW3_CS5 +#define C_6 SW3_CS6 +#define C_7 SW3_CS7 +#define C_8 SW3_CS8 + +#define D_1 SW4_CS1 +#define D_2 SW4_CS2 +#define D_3 SW4_CS3 +#define D_4 SW4_CS4 +#define D_5 SW4_CS5 +#define D_6 SW4_CS6 +#define D_7 SW4_CS7 +#define D_8 SW4_CS8 + +#define E_1 SW5_CS1 +#define E_2 SW5_CS2 +#define E_3 SW5_CS3 +#define E_4 SW5_CS4 +#define E_5 SW5_CS5 +#define E_6 SW5_CS6 +#define E_7 SW5_CS7 +#define E_8 SW5_CS8 + +#define F_1 SW6_CS1 +#define F_2 SW6_CS2 +#define F_3 SW6_CS3 +#define F_4 SW6_CS4 +#define F_5 SW6_CS5 +#define F_6 SW6_CS6 +#define F_7 SW6_CS7 +#define F_8 SW6_CS8 + +#define G_1 SW7_CS1 +#define G_2 SW7_CS2 +#define G_3 SW7_CS3 +#define G_4 SW7_CS4 +#define G_5 SW7_CS5 +#define G_6 SW7_CS6 +#define G_7 SW7_CS7 +#define G_8 SW7_CS8 + +#define H_1 SW8_CS1 +#define H_2 SW8_CS2 +#define H_3 SW8_CS3 +#define H_4 SW8_CS4 +#define H_5 SW8_CS5 +#define H_6 SW8_CS6 +#define H_7 SW8_CS7 +#define H_8 SW8_CS8 + +#define I_1 SW9_CS1 +#define I_2 SW9_CS2 +#define I_3 SW9_CS3 +#define I_4 SW9_CS4 +#define I_5 SW9_CS5 +#define I_6 SW9_CS6 +#define I_7 SW9_CS7 +#define I_8 SW9_CS8 + +#define J_1 SW10_CS1 +#define J_2 SW10_CS2 +#define J_3 SW10_CS3 +#define J_4 SW10_CS4 +#define J_5 SW10_CS5 +#define J_6 SW10_CS6 +#define J_7 SW10_CS7 +#define J_8 SW10_CS8 + +#define K_1 SW11_CS1 +#define K_2 SW11_CS2 +#define K_3 SW11_CS3 +#define K_4 SW11_CS4 +#define K_5 SW11_CS5 +#define K_6 SW11_CS6 +#define K_7 SW11_CS7 +#define K_8 SW11_CS8 + +#define L_1 SW12_CS1 +#define L_2 SW12_CS2 +#define L_3 SW12_CS3 +#define L_4 SW12_CS4 +#define L_5 SW12_CS5 +#define L_6 SW12_CS6 +#define L_7 SW12_CS7 +#define L_8 SW12_CS8 diff --git a/drivers/led/issi/is31fl3736-simple.c b/drivers/led/issi/is31fl3736-simple.c deleted file mode 100644 index e1cce3c48a..0000000000 --- a/drivers/led/issi/is31fl3736-simple.c +++ /dev/null @@ -1,252 +0,0 @@ -/* Copyright 2018 Jason Williams (Wilba) - * Copyright 2021 Doni Crosby - * - * 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 2 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/>. - */ - -#include "is31fl3736-simple.h" -#include <string.h> -#include "i2c_master.h" -#include "wait.h" - -#define IS31FL3736_PWM_REGISTER_COUNT 192 // actually 96 -#define IS31FL3736_LED_CONTROL_REGISTER_COUNT 24 - -#ifndef IS31FL3736_I2C_TIMEOUT -# define IS31FL3736_I2C_TIMEOUT 100 -#endif - -#ifndef IS31FL3736_I2C_PERSISTENCE -# define IS31FL3736_I2C_PERSISTENCE 0 -#endif - -#ifndef IS31FL3736_PWM_FREQUENCY -# define IS31FL3736_PWM_FREQUENCY IS31FL3736_PWM_FREQUENCY_8K4_HZ // PFS - IS31FL3736B only -#endif - -#ifndef IS31FL3736_SW_PULLUP -# define IS31FL3736_SW_PULLUP IS31FL3736_PUR_0_OHM -#endif - -#ifndef IS31FL3736_CS_PULLDOWN -# define IS31FL3736_CS_PULLDOWN IS31FL3736_PDR_0_OHM -#endif - -#ifndef IS31FL3736_GLOBAL_CURRENT -# define IS31FL3736_GLOBAL_CURRENT 0xFF -#endif - -// Transfer buffer for TWITransmitData() -uint8_t g_twi_transfer_buffer[20]; - -// These buffers match the IS31FL3736 PWM registers. -// The control buffers match the PG0 LED On/Off registers. -// Storing them like this is optimal for I2C transfers to the registers. -// We could optimize this and take out the unused registers from these -// buffers and the transfers in is31fl3736_write_pwm_buffer() but it's -// probably not worth the extra complexity. -uint8_t g_pwm_buffer[IS31FL3736_DRIVER_COUNT][IS31FL3736_PWM_REGISTER_COUNT]; -bool g_pwm_buffer_update_required[IS31FL3736_DRIVER_COUNT] = {false}; - -uint8_t g_led_control_registers[IS31FL3736_DRIVER_COUNT][IS31FL3736_LED_CONTROL_REGISTER_COUNT] = {0}; -bool g_led_control_registers_update_required[IS31FL3736_DRIVER_COUNT] = {false}; - -void is31fl3736_write_register(uint8_t addr, uint8_t reg, uint8_t data) { - g_twi_transfer_buffer[0] = reg; - g_twi_transfer_buffer[1] = data; - -#if IS31FL3736_I2C_PERSISTENCE > 0 - for (uint8_t i = 0; i < IS31FL3736_I2C_PERSISTENCE; i++) { - if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, IS31FL3736_I2C_TIMEOUT) == 0) break; - } -#else - i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, IS31FL3736_I2C_TIMEOUT); -#endif -} - -void is31fl3736_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer) { - // assumes PG1 is already selected - - // transmit PWM registers in 12 transfers of 16 bytes - // g_twi_transfer_buffer[] is 20 bytes - - // iterate over the pwm_buffer contents at 16 byte intervals - for (int i = 0; i < IS31FL3736_PWM_REGISTER_COUNT; i += 16) { - g_twi_transfer_buffer[0] = i; - // copy the data from i to i+15 - // device will auto-increment register for data after the first byte - // thus this sets registers 0x00-0x0F, 0x10-0x1F, etc. in one transfer - memcpy(g_twi_transfer_buffer + 1, pwm_buffer + i, 16); - -#if IS31FL3736_I2C_PERSISTENCE > 0 - for (uint8_t i = 0; i < IS31FL3736_I2C_PERSISTENCE; i++) { - if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 17, IS31FL3736_I2C_TIMEOUT) == 0) break; - } -#else - i2c_transmit(addr << 1, g_twi_transfer_buffer, 17, IS31FL3736_I2C_TIMEOUT); -#endif - } -} - -void is31fl3736_init_drivers(void) { - i2c_init(); - - is31fl3736_init(IS31FL3736_I2C_ADDRESS_1); -#if defined(IS31FL3736_I2C_ADDRESS_2) - is31fl3736_init(IS31FL3736_I2C_ADDRESS_2); -# if defined(IS31FL3736_I2C_ADDRESS_3) - is31fl3736_init(IS31FL3736_I2C_ADDRESS_3); -# if defined(IS31FL3736_I2C_ADDRESS_4) - is31fl3736_init(IS31FL3736_I2C_ADDRESS_4); -# endif -# endif -#endif - - for (int i = 0; i < IS31FL3736_LED_COUNT; i++) { - is31fl3736_set_led_control_register(i, true); - } - - is31fl3736_update_led_control_registers(IS31FL3736_I2C_ADDRESS_1, 0); -#if defined(IS31FL3736_I2C_ADDRESS_2) - is31fl3736_update_led_control_registers(IS31FL3736_I2C_ADDRESS_2, 1); -# if defined(IS31FL3736_I2C_ADDRESS_3) - is31fl3736_update_led_control_registers(IS31FL3736_I2C_ADDRESS_3, 2); -# if defined(IS31FL3736_I2C_ADDRESS_4) - is31fl3736_update_led_control_registers(IS31FL3736_I2C_ADDRESS_4, 3); -# endif -# endif -#endif -} - -void is31fl3736_init(uint8_t addr) { - // In order to avoid the LEDs being driven with garbage data - // in the LED driver's PWM registers, shutdown is enabled last. - // Set up the mode and other settings, clear the PWM registers, - // then disable software shutdown. - - // Unlock the command register. - is31fl3736_write_register(addr, IS31FL3736_REG_COMMAND_WRITE_LOCK, IS31FL3736_COMMAND_WRITE_LOCK_MAGIC); - - // Select PG0 - is31fl3736_write_register(addr, IS31FL3736_REG_COMMAND, IS31FL3736_COMMAND_LED_CONTROL); - // Turn off all LEDs. - for (int i = 0; i < IS31FL3736_LED_CONTROL_REGISTER_COUNT; i++) { - is31fl3736_write_register(addr, i, 0x00); - } - - // Unlock the command register. - is31fl3736_write_register(addr, IS31FL3736_REG_COMMAND_WRITE_LOCK, IS31FL3736_COMMAND_WRITE_LOCK_MAGIC); - - // Select PG1 - is31fl3736_write_register(addr, IS31FL3736_REG_COMMAND, IS31FL3736_COMMAND_PWM); - // Set PWM on all LEDs to 0 - // No need to setup Breath registers to PWM as that is the default. - for (int i = 0; i < IS31FL3736_PWM_REGISTER_COUNT; i++) { - is31fl3736_write_register(addr, i, 0x00); - } - - // Unlock the command register. - is31fl3736_write_register(addr, IS31FL3736_REG_COMMAND_WRITE_LOCK, IS31FL3736_COMMAND_WRITE_LOCK_MAGIC); - - // Select PG3 - is31fl3736_write_register(addr, IS31FL3736_REG_COMMAND, IS31FL3736_COMMAND_FUNCTION); - // Set de-ghost pull-up resistors (SWx) - is31fl3736_write_register(addr, IS31FL3736_FUNCTION_REG_SW_PULLUP, IS31FL3736_SW_PULLUP); - // Set de-ghost pull-down resistors (CSx) - is31fl3736_write_register(addr, IS31FL3736_FUNCTION_REG_CS_PULLDOWN, IS31FL3736_CS_PULLDOWN); - // Set global current to maximum. - is31fl3736_write_register(addr, IS31FL3736_FUNCTION_REG_GLOBAL_CURRENT, IS31FL3736_GLOBAL_CURRENT); - // Disable software shutdown. - is31fl3736_write_register(addr, IS31FL3736_FUNCTION_REG_CONFIGURATION, ((IS31FL3736_PWM_FREQUENCY & 0b111) << 3) | 0x01); - - // Wait 10ms to ensure the device has woken up. - wait_ms(10); -} - -void is31fl3736_set_value(int index, uint8_t value) { - is31fl3736_led_t led; - if (index >= 0 && index < IS31FL3736_LED_COUNT) { - memcpy_P(&led, (&g_is31fl3736_leds[index]), sizeof(led)); - - if (g_pwm_buffer[led.driver][led.v] == value) { - return; - } - g_pwm_buffer[led.driver][led.v] = value; - g_pwm_buffer_update_required[led.driver] = true; - } -} - -void is31fl3736_set_value_all(uint8_t value) { - for (int i = 0; i < IS31FL3736_LED_COUNT; i++) { - is31fl3736_set_value(i, value); - } -} - -void is31fl3736_set_led_control_register(uint8_t index, bool value) { - is31fl3736_led_t led; - memcpy_P(&led, (&g_is31fl3736_leds[index]), sizeof(led)); - - // The PWM register for a matrix position (0x00 to 0xBF) is interleaved, so: - // A1=0x00 A2=0x02 A3=0x04 A4=0x06 A5=0x08 A6=0x0A A7=0x0C A8=0x0E - // B1=0x10 B2=0x12 B3=0x14 - // But also, the LED control registers (0x00 to 0x17) are also interleaved, so: - // A1-A4=0x00 A5-A8=0x01 - - uint8_t control_register = led.v / 8; - uint8_t bit_value = led.v % 8; - - if (value) { - g_led_control_registers[led.driver][control_register] |= (1 << bit_value); - } else { - g_led_control_registers[led.driver][control_register] &= ~(1 << bit_value); - } - - g_led_control_registers_update_required[led.driver] = true; -} - -void is31fl3736_update_pwm_buffers(uint8_t addr, uint8_t index) { - if (g_pwm_buffer_update_required[index]) { - // Firstly we need to unlock the command register and select PG1 - is31fl3736_write_register(addr, IS31FL3736_REG_COMMAND_WRITE_LOCK, IS31FL3736_COMMAND_WRITE_LOCK_MAGIC); - is31fl3736_write_register(addr, IS31FL3736_REG_COMMAND, IS31FL3736_COMMAND_PWM); - - is31fl3736_write_pwm_buffer(addr, g_pwm_buffer[index]); - g_pwm_buffer_update_required[index] = false; - } -} - -void is31fl3736_update_led_control_registers(uint8_t addr, uint8_t index) { - if (g_led_control_registers_update_required[index]) { - // Firstly we need to unlock the command register and select PG0 - is31fl3736_write_register(addr, IS31FL3736_REG_COMMAND_WRITE_LOCK, IS31FL3736_COMMAND_WRITE_LOCK_MAGIC); - is31fl3736_write_register(addr, IS31FL3736_REG_COMMAND, IS31FL3736_COMMAND_LED_CONTROL); - for (int i = 0; i < IS31FL3736_LED_CONTROL_REGISTER_COUNT; i++) { - is31fl3736_write_register(addr, i, g_led_control_registers[index][i]); - } - g_led_control_registers_update_required[index] = false; - } -} - -void is31fl3736_flush(void) { - is31fl3736_update_pwm_buffers(IS31FL3736_I2C_ADDRESS_1, 0); -#if defined(IS31FL3736_I2C_ADDRESS_2) - is31fl3736_update_pwm_buffers(IS31FL3736_I2C_ADDRESS_2, 1); -# if defined(IS31FL3736_I2C_ADDRESS_3) - is31fl3736_update_pwm_buffers(IS31FL3736_I2C_ADDRESS_3, 2); -# if defined(IS31FL3736_I2C_ADDRESS_4) - is31fl3736_update_pwm_buffers(IS31FL3736_I2C_ADDRESS_4, 3); -# endif -# endif -#endif -} diff --git a/drivers/led/issi/is31fl3736.c b/drivers/led/issi/is31fl3736.c index 30ab796f3e..3ab42e2f7c 100644 --- a/drivers/led/issi/is31fl3736.c +++ b/drivers/led/issi/is31fl3736.c @@ -16,8 +16,8 @@ */ #include "is31fl3736.h" -#include <string.h> #include "i2c_master.h" +#include "gpio.h" #include "wait.h" #define IS31FL3736_PWM_REGISTER_COUNT 192 // actually 96 @@ -47,54 +47,66 @@ # define IS31FL3736_GLOBAL_CURRENT 0xFF #endif -// Transfer buffer for TWITransmitData() -uint8_t g_twi_transfer_buffer[20]; +const uint8_t i2c_addresses[IS31FL3736_DRIVER_COUNT] = { + IS31FL3736_I2C_ADDRESS_1, +#ifdef IS31FL3736_I2C_ADDRESS_2 + IS31FL3736_I2C_ADDRESS_2, +# ifdef IS31FL3736_I2C_ADDRESS_3 + IS31FL3736_I2C_ADDRESS_3, +# ifdef IS31FL3736_I2C_ADDRESS_4 + IS31FL3736_I2C_ADDRESS_4, +# endif +# endif +#endif +}; // These buffers match the IS31FL3736 PWM registers. -// The control buffers match the PG0 LED On/Off registers. +// The control buffers match the page 0 LED On/Off registers. // Storing them like this is optimal for I2C transfers to the registers. // We could optimize this and take out the unused registers from these // buffers and the transfers in is31fl3736_write_pwm_buffer() but it's // probably not worth the extra complexity. -uint8_t g_pwm_buffer[IS31FL3736_DRIVER_COUNT][IS31FL3736_PWM_REGISTER_COUNT]; -bool g_pwm_buffer_update_required[IS31FL3736_DRIVER_COUNT] = {false}; - -uint8_t g_led_control_registers[IS31FL3736_DRIVER_COUNT][IS31FL3736_LED_CONTROL_REGISTER_COUNT] = {0}; -bool g_led_control_registers_update_required[IS31FL3736_DRIVER_COUNT] = {false}; - -void is31fl3736_write_register(uint8_t addr, uint8_t reg, uint8_t data) { - g_twi_transfer_buffer[0] = reg; - g_twi_transfer_buffer[1] = data; - +typedef struct is31fl3736_driver_t { + uint8_t pwm_buffer[IS31FL3736_PWM_REGISTER_COUNT]; + bool pwm_buffer_dirty; + uint8_t led_control_buffer[IS31FL3736_LED_CONTROL_REGISTER_COUNT]; + bool led_control_buffer_dirty; +} PACKED is31fl3736_driver_t; + +is31fl3736_driver_t driver_buffers[IS31FL3736_DRIVER_COUNT] = {{ + .pwm_buffer = {0}, + .pwm_buffer_dirty = false, + .led_control_buffer = {0}, + .led_control_buffer_dirty = false, +}}; + +void is31fl3736_write_register(uint8_t index, uint8_t reg, uint8_t data) { #if IS31FL3736_I2C_PERSISTENCE > 0 for (uint8_t i = 0; i < IS31FL3736_I2C_PERSISTENCE; i++) { - if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, IS31FL3736_I2C_TIMEOUT) == 0) break; + if (i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3736_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break; } #else - i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, IS31FL3736_I2C_TIMEOUT); + i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3736_I2C_TIMEOUT); #endif } -void is31fl3736_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer) { - // assumes PG1 is already selected - - // transmit PWM registers in 12 transfers of 16 bytes - // g_twi_transfer_buffer[] is 20 bytes +void is31fl3736_select_page(uint8_t index, uint8_t page) { + is31fl3736_write_register(index, IS31FL3736_REG_COMMAND_WRITE_LOCK, IS31FL3736_COMMAND_WRITE_LOCK_MAGIC); + is31fl3736_write_register(index, IS31FL3736_REG_COMMAND, page); +} - // iterate over the pwm_buffer contents at 16 byte intervals - for (int i = 0; i < IS31FL3736_PWM_REGISTER_COUNT; i += 16) { - g_twi_transfer_buffer[0] = i; - // copy the data from i to i+15 - // device will auto-increment register for data after the first byte - // thus this sets registers 0x00-0x0F, 0x10-0x1F, etc. in one transfer - memcpy(g_twi_transfer_buffer + 1, pwm_buffer + i, 16); +void is31fl3736_write_pwm_buffer(uint8_t index) { + // Assumes page 1 is already selected. + // Transmit PWM registers in 12 transfers of 16 bytes. + // Iterate over the pwm_buffer contents at 16 byte intervals. + for (uint8_t i = 0; i < IS31FL3736_PWM_REGISTER_COUNT; i += 16) { #if IS31FL3736_I2C_PERSISTENCE > 0 - for (uint8_t i = 0; i < IS31FL3736_I2C_PERSISTENCE; i++) { - if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 17, IS31FL3736_I2C_TIMEOUT) == 0) break; + for (uint8_t j = 0; j < IS31FL3736_I2C_PERSISTENCE; j++) { + if (i2c_write_register(i2c_addresses[index] << 1, i, driver_buffers[index].pwm_buffer + i, 16, IS31FL3736_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break; } #else - i2c_transmit(addr << 1, g_twi_transfer_buffer, 17, IS31FL3736_I2C_TIMEOUT); + i2c_write_register(i2c_addresses[index] << 1, i, driver_buffers[index].pwm_buffer + i, 16, IS31FL3736_I2C_TIMEOUT); #endif } } @@ -102,73 +114,55 @@ void is31fl3736_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer) { void is31fl3736_init_drivers(void) { i2c_init(); - is31fl3736_init(IS31FL3736_I2C_ADDRESS_1); -#if defined(IS31FL3736_I2C_ADDRESS_2) - is31fl3736_init(IS31FL3736_I2C_ADDRESS_2); -# if defined(IS31FL3736_I2C_ADDRESS_3) - is31fl3736_init(IS31FL3736_I2C_ADDRESS_3); -# if defined(IS31FL3736_I2C_ADDRESS_4) - is31fl3736_init(IS31FL3736_I2C_ADDRESS_4); -# endif -# endif +#if defined(IS31FL3736_SDB_PIN) + gpio_set_pin_output(IS31FL3736_SDB_PIN); + gpio_write_pin_high(IS31FL3736_SDB_PIN); #endif + for (uint8_t i = 0; i < IS31FL3736_DRIVER_COUNT; i++) { + is31fl3736_init(i); + } + for (int i = 0; i < IS31FL3736_LED_COUNT; i++) { is31fl3736_set_led_control_register(i, true, true, true); } - is31fl3736_update_led_control_registers(IS31FL3736_I2C_ADDRESS_1, 0); -#if defined(IS31FL3736_I2C_ADDRESS_2) - is31fl3736_update_led_control_registers(IS31FL3736_I2C_ADDRESS_2, 1); -# if defined(IS31FL3736_I2C_ADDRESS_3) - is31fl3736_update_led_control_registers(IS31FL3736_I2C_ADDRESS_3, 2); -# if defined(IS31FL3736_I2C_ADDRESS_4) - is31fl3736_update_led_control_registers(IS31FL3736_I2C_ADDRESS_4, 3); -# endif -# endif -#endif + for (uint8_t i = 0; i < IS31FL3736_DRIVER_COUNT; i++) { + is31fl3736_update_led_control_registers(i); + } } -void is31fl3736_init(uint8_t addr) { +void is31fl3736_init(uint8_t index) { // In order to avoid the LEDs being driven with garbage data // in the LED driver's PWM registers, shutdown is enabled last. // Set up the mode and other settings, clear the PWM registers, // then disable software shutdown. - // Unlock the command register. - is31fl3736_write_register(addr, IS31FL3736_REG_COMMAND_WRITE_LOCK, IS31FL3736_COMMAND_WRITE_LOCK_MAGIC); + is31fl3736_select_page(index, IS31FL3736_COMMAND_LED_CONTROL); - // Select PG0 - is31fl3736_write_register(addr, IS31FL3736_REG_COMMAND, IS31FL3736_REG_LED_CONTROL); // Turn off all LEDs. - for (int i = 0; i < IS31FL3736_LED_CONTROL_REGISTER_COUNT; i++) { - is31fl3736_write_register(addr, i, 0x00); + for (uint8_t i = 0; i < IS31FL3736_LED_CONTROL_REGISTER_COUNT; i++) { + is31fl3736_write_register(index, i, 0x00); } - // Unlock the command register. - is31fl3736_write_register(addr, IS31FL3736_REG_COMMAND_WRITELOCK, IS31FL3736_COMMAND_WRITE_LOCK_MAGIC); + is31fl3736_select_page(index, IS31FL3736_COMMAND_PWM); - // Select PG1 - is31fl3736_write_register(addr, IS31FL3736_REG_COMMAND, IS31FL3736_COMMAND_PWM); // Set PWM on all LEDs to 0 // No need to setup Breath registers to PWM as that is the default. - for (int i = 0; i < IS31FL3736_PWM_REGISTER_COUNT; i++) { - is31fl3736_write_register(addr, i, 0x00); + for (uint8_t i = 0; i < IS31FL3736_PWM_REGISTER_COUNT; i++) { + is31fl3736_write_register(index, i, 0x00); } - // Unlock the command register. - is31fl3736_write_register(addr, IS31FL3736_REG_COMMAND_WRITE_LOCK, IS31FL3736_COMMAND_WRITE_LOCK_MAGIC); + is31fl3736_select_page(index, IS31FL3736_COMMAND_FUNCTION); - // Select PG3 - is31fl3736_write_register(addr, IS31FL3736_REG_COMMAND, IS31FL3736_COMMAND_FUNCTION); // Set de-ghost pull-up resistors (SWx) - is31fl3736_write_register(addr, IS31FL3736_FUNCTION_REG_SW_PULLUP, IS31FL3736_SW_PULLUP); + is31fl3736_write_register(index, IS31FL3736_FUNCTION_REG_SW_PULLUP, IS31FL3736_SW_PULLUP); // Set de-ghost pull-down resistors (CSx) - is31fl3736_write_register(addr, IS31FL3736_FUNCTION_REG_CS_PULLDOWN, IS31FL3736_CS_PULLDOWN); + is31fl3736_write_register(index, IS31FL3736_FUNCTION_REG_CS_PULLDOWN, IS31FL3736_CS_PULLDOWN); // Set global current to maximum. - is31fl3736_write_register(addr, IS31FL3736_FUNCTION_REG_GLOBAL_CURRENT, IS31FL3736_GLOBAL_CURRENT); + is31fl3736_write_register(index, IS31FL3736_FUNCTION_REG_GLOBAL_CURRENT, IS31FL3736_GLOBAL_CURRENT); // Disable software shutdown. - is31fl3736_write_register(addr, IS31FL3736_FUNCTION_REG_CONFIGURATION, ((IS31FL3736_PWM_FREQUENCY & 0b111) << 3) | 0x01); + is31fl3736_write_register(index, IS31FL3736_FUNCTION_REG_CONFIGURATION, ((IS31FL3736_PWM_FREQUENCY & 0b111) << 3) | 0x01); // Wait 10ms to ensure the device has woken up. wait_ms(10); @@ -176,16 +170,18 @@ void is31fl3736_init(uint8_t addr) { void is31fl3736_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) { is31fl3736_led_t led; + if (index >= 0 && index < IS31FL3736_LED_COUNT) { memcpy_P(&led, (&g_is31fl3736_leds[index]), sizeof(led)); - if (g_pwm_buffer[led.driver][led.r] == red && g_pwm_buffer[led.driver][led.g] == green && g_pwm_buffer[led.driver][led.b] == blue) { + if (driver_buffers[led.driver].pwm_buffer[led.r] == red && driver_buffers[led.driver].pwm_buffer[led.g] == green && driver_buffers[led.driver].pwm_buffer[led.b] == blue) { return; } - g_pwm_buffer[led.driver][led.r] = red; - g_pwm_buffer[led.driver][led.g] = green; - g_pwm_buffer[led.driver][led.b] = blue; - g_pwm_buffer_update_required[led.driver] = true; + + driver_buffers[led.driver].pwm_buffer[led.r] = red; + driver_buffers[led.driver].pwm_buffer[led.g] = green; + driver_buffers[led.driver].pwm_buffer[led.b] = blue; + driver_buffers[led.driver].pwm_buffer_dirty = true; } } @@ -214,56 +210,48 @@ void is31fl3736_set_led_control_register(uint8_t index, bool red, bool green, bo uint8_t bit_b = led.b % 8; if (red) { - g_led_control_registers[led.driver][control_register_r] |= (1 << bit_r); + driver_buffers[led.driver].led_control_buffer[control_register_r] |= (1 << bit_r); } else { - g_led_control_registers[led.driver][control_register_r] &= ~(1 << bit_r); + driver_buffers[led.driver].led_control_buffer[control_register_r] &= ~(1 << bit_r); } if (green) { - g_led_control_registers[led.driver][control_register_g] |= (1 << bit_g); + driver_buffers[led.driver].led_control_buffer[control_register_g] |= (1 << bit_g); } else { - g_led_control_registers[led.driver][control_register_g] &= ~(1 << bit_g); + driver_buffers[led.driver].led_control_buffer[control_register_g] &= ~(1 << bit_g); } if (blue) { - g_led_control_registers[led.driver][control_register_b] |= (1 << bit_b); + driver_buffers[led.driver].led_control_buffer[control_register_b] |= (1 << bit_b); } else { - g_led_control_registers[led.driver][control_register_b] &= ~(1 << bit_b); + driver_buffers[led.driver].led_control_buffer[control_register_b] &= ~(1 << bit_b); } - g_led_control_registers_update_required[led.driver] = true; + driver_buffers[led.driver].led_control_buffer_dirty = true; } -void is31fl3736_update_pwm_buffers(uint8_t addr, uint8_t index) { - if (g_pwm_buffer_update_required[index]) { - // Firstly we need to unlock the command register and select PG1 - is31fl3736_write_register(addr, IS31FL3736_REG_COMMAND_WRITE_LOCK, IS31FL3736_COMMAND_WRITE_LOCK_MAGIC); - is31fl3736_write_register(addr, IS31FL3736_REG_COMMAND, IS31FL3736_COMMAND_PWM); +void is31fl3736_update_pwm_buffers(uint8_t index) { + if (driver_buffers[index].pwm_buffer_dirty) { + is31fl3736_select_page(index, IS31FL3736_COMMAND_PWM); + + is31fl3736_write_pwm_buffer(index); - is31fl3736_write_pwm_buffer(addr, g_pwm_buffer[index]); - g_pwm_buffer_update_required[index] = false; + driver_buffers[index].pwm_buffer_dirty = false; } } -void is31fl3736_update_led_control_registers(uint8_t addr, uint8_t index) { - if (g_led_control_registers_update_required[index]) { - // Firstly we need to unlock the command register and select PG0 - is31fl3736_write_register(addr, IS31FL3736_REG_COMMAND_WRITE_LOCK, IS31FL3736_COMMAND_WRITE_LOCK_MAGIC); - is31fl3736_write_register(addr, IS31FL3736_REG_COMMAND, IS31FL3736_COMMAND_LED_CONTROL); - for (int i = 0; i < IS31FL3736_LED_CONTROL_REGISTER_COUNT; i++) { - is31fl3736_write_register(addr, i, g_led_control_registers[index][i]); +void is31fl3736_update_led_control_registers(uint8_t index) { + if (driver_buffers[index].led_control_buffer_dirty) { + is31fl3736_select_page(index, IS31FL3736_COMMAND_LED_CONTROL); + + for (uint8_t i = 0; i < IS31FL3736_LED_CONTROL_REGISTER_COUNT; i++) { + is31fl3736_write_register(index, i, driver_buffers[index].led_control_buffer[i]); } - g_led_control_registers_update_required[index] = false; + + driver_buffers[index].led_control_buffer_dirty = false; } } void is31fl3736_flush(void) { - is31fl3736_update_pwm_buffers(IS31FL3736_I2C_ADDRESS_1, 0); -#if defined(IS31FL3736_I2C_ADDRESS_2) - is31fl3736_update_pwm_buffers(IS31FL3736_I2C_ADDRESS_2, 1); -# if defined(IS31FL3736_I2C_ADDRESS_3) - is31fl3736_update_pwm_buffers(IS31FL3736_I2C_ADDRESS_3, 2); -# if defined(IS31FL3736_I2C_ADDRESS_4) - is31fl3736_update_pwm_buffers(IS31FL3736_I2C_ADDRESS_4, 3); -# endif -# endif -#endif + for (uint8_t i = 0; i < IS31FL3736_DRIVER_COUNT; i++) { + is31fl3736_update_pwm_buffers(i); + } } diff --git a/drivers/led/issi/is31fl3736.h b/drivers/led/issi/is31fl3736.h index a5710d7ed4..dae7b3c812 100644 --- a/drivers/led/issi/is31fl3736.h +++ b/drivers/led/issi/is31fl3736.h @@ -124,9 +124,9 @@ typedef struct is31fl3736_led_t { extern const is31fl3736_led_t PROGMEM g_is31fl3736_leds[IS31FL3736_LED_COUNT]; void is31fl3736_init_drivers(void); -void is31fl3736_init(uint8_t addr); -void is31fl3736_write_register(uint8_t addr, uint8_t reg, uint8_t data); -void is31fl3736_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer); +void is31fl3736_init(uint8_t index); +void is31fl3736_write_register(uint8_t index, uint8_t reg, uint8_t data); +void is31fl3736_select_page(uint8_t index, uint8_t page); void is31fl3736_set_color(int index, uint8_t red, uint8_t green, uint8_t blue); void is31fl3736_set_color_all(uint8_t red, uint8_t green, uint8_t blue); @@ -137,8 +137,8 @@ void is31fl3736_set_led_control_register(uint8_t index, bool red, bool green, bo // (eg. from a timer interrupt). // Call this while idle (in between matrix scans). // If the buffer is dirty, it will update the driver with the buffer. -void is31fl3736_update_pwm_buffers(uint8_t addr, uint8_t index); -void is31fl3736_update_led_control_registers(uint8_t addr, uint8_t index); +void is31fl3736_update_pwm_buffers(uint8_t index); +void is31fl3736_update_led_control_registers(uint8_t index); void is31fl3736_flush(void); @@ -166,110 +166,110 @@ void is31fl3736_flush(void); #define IS31FL3736_PWM_FREQUENCY_2K1_HZ 0b011 #define IS31FL3736_PWM_FREQUENCY_1K05_HZ 0b100 -#define A_1 0x00 -#define A_2 0x02 -#define A_3 0x04 -#define A_4 0x06 -#define A_5 0x08 -#define A_6 0x0A -#define A_7 0x0C -#define A_8 0x0E - -#define B_1 0x10 -#define B_2 0x12 -#define B_3 0x14 -#define B_4 0x16 -#define B_5 0x18 -#define B_6 0x1A -#define B_7 0x1C -#define B_8 0x1E - -#define C_1 0x20 -#define C_2 0x22 -#define C_3 0x24 -#define C_4 0x26 -#define C_5 0x28 -#define C_6 0x2A -#define C_7 0x2C -#define C_8 0x2E - -#define D_1 0x30 -#define D_2 0x32 -#define D_3 0x34 -#define D_4 0x36 -#define D_5 0x38 -#define D_6 0x3A -#define D_7 0x3C -#define D_8 0x3E - -#define E_1 0x40 -#define E_2 0x42 -#define E_3 0x44 -#define E_4 0x46 -#define E_5 0x48 -#define E_6 0x4A -#define E_7 0x4C -#define E_8 0x4E - -#define F_1 0x50 -#define F_2 0x52 -#define F_3 0x54 -#define F_4 0x56 -#define F_5 0x58 -#define F_6 0x5A -#define F_7 0x5C -#define F_8 0x5E - -#define G_1 0x60 -#define G_2 0x62 -#define G_3 0x64 -#define G_4 0x66 -#define G_5 0x68 -#define G_6 0x6A -#define G_7 0x6C -#define G_8 0x6E - -#define H_1 0x70 -#define H_2 0x72 -#define H_3 0x74 -#define H_4 0x76 -#define H_5 0x78 -#define H_6 0x7A -#define H_7 0x7C -#define H_8 0x7E - -#define I_1 0x80 -#define I_2 0x82 -#define I_3 0x84 -#define I_4 0x86 -#define I_5 0x88 -#define I_6 0x8A -#define I_7 0x8C -#define I_8 0x8E - -#define J_1 0x90 -#define J_2 0x92 -#define J_3 0x94 -#define J_4 0x96 -#define J_5 0x98 -#define J_6 0x9A -#define J_7 0x9C -#define J_8 0x9E - -#define K_1 0xA0 -#define K_2 0xA2 -#define K_3 0xA4 -#define K_4 0xA6 -#define K_5 0xA8 -#define K_6 0xAA -#define K_7 0xAC -#define K_8 0xAE - -#define L_1 0xB0 -#define L_2 0xB2 -#define L_3 0xB4 -#define L_4 0xB6 -#define L_5 0xB8 -#define L_6 0xBA -#define L_7 0xBC -#define L_8 0xBE +#define SW1_CS1 0x00 +#define SW1_CS2 0x02 +#define SW1_CS3 0x04 +#define SW1_CS4 0x06 +#define SW1_CS5 0x08 +#define SW1_CS6 0x0A +#define SW1_CS7 0x0C +#define SW1_CS8 0x0E + +#define SW2_CS1 0x10 +#define SW2_CS2 0x12 +#define SW2_CS3 0x14 +#define SW2_CS4 0x16 +#define SW2_CS5 0x18 +#define SW2_CS6 0x1A +#define SW2_CS7 0x1C +#define SW2_CS8 0x1E + +#define SW3_CS1 0x20 +#define SW3_CS2 0x22 +#define SW3_CS3 0x24 +#define SW3_CS4 0x26 +#define SW3_CS5 0x28 +#define SW3_CS6 0x2A +#define SW3_CS7 0x2C +#define SW3_CS8 0x2E + +#define SW4_CS1 0x30 +#define SW4_CS2 0x32 +#define SW4_CS3 0x34 +#define SW4_CS4 0x36 +#define SW4_CS5 0x38 +#define SW4_CS6 0x3A +#define SW4_CS7 0x3C +#define SW4_CS8 0x3E + +#define SW5_CS1 0x40 +#define SW5_CS2 0x42 +#define SW5_CS3 0x44 +#define SW5_CS4 0x46 +#define SW5_CS5 0x48 +#define SW5_CS6 0x4A +#define SW5_CS7 0x4C +#define SW5_CS8 0x4E + +#define SW6_CS1 0x50 +#define SW6_CS2 0x52 +#define SW6_CS3 0x54 +#define SW6_CS4 0x56 +#define SW6_CS5 0x58 +#define SW6_CS6 0x5A +#define SW6_CS7 0x5C +#define SW6_CS8 0x5E + +#define SW7_CS1 0x60 +#define SW7_CS2 0x62 +#define SW7_CS3 0x64 +#define SW7_CS4 0x66 +#define SW7_CS5 0x68 +#define SW7_CS6 0x6A +#define SW7_CS7 0x6C +#define SW7_CS8 0x6E + +#define SW8_CS1 0x70 +#define SW8_CS2 0x72 +#define SW8_CS3 0x74 +#define SW8_CS4 0x76 +#define SW8_CS5 0x78 +#define SW8_CS6 0x7A +#define SW8_CS7 0x7C +#define SW8_CS8 0x7E + +#define SW9_CS1 0x80 +#define SW9_CS2 0x82 +#define SW9_CS3 0x84 +#define SW9_CS4 0x86 +#define SW9_CS5 0x88 +#define SW9_CS6 0x8A +#define SW9_CS7 0x8C +#define SW9_CS8 0x8E + +#define SW10_CS1 0x90 +#define SW10_CS2 0x92 +#define SW10_CS3 0x94 +#define SW10_CS4 0x96 +#define SW10_CS5 0x98 +#define SW10_CS6 0x9A +#define SW10_CS7 0x9C +#define SW10_CS8 0x9E + +#define SW11_CS1 0xA0 +#define SW11_CS2 0xA2 +#define SW11_CS3 0xA4 +#define SW11_CS4 0xA6 +#define SW11_CS5 0xA8 +#define SW11_CS6 0xAA +#define SW11_CS7 0xAC +#define SW11_CS8 0xAE + +#define SW12_CS1 0xB0 +#define SW12_CS2 0xB2 +#define SW12_CS3 0xB4 +#define SW12_CS4 0xB6 +#define SW12_CS5 0xB8 +#define SW12_CS6 0xBA +#define SW12_CS7 0xBC +#define SW12_CS8 0xBE diff --git a/drivers/led/issi/is31fl3737-mono.c b/drivers/led/issi/is31fl3737-mono.c new file mode 100644 index 0000000000..7b2e5a3576 --- /dev/null +++ b/drivers/led/issi/is31fl3737-mono.c @@ -0,0 +1,236 @@ +/* Copyright 2017 Jason Williams + * Copyright 2018 Jack Humbert + * Copyright 2018 Yiancar + * Copyright 2021 Doni Crosby + * + * 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 2 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/>. + */ + +#include "is31fl3737-mono.h" +#include "i2c_master.h" +#include "gpio.h" +#include "wait.h" + +#define IS31FL3737_PWM_REGISTER_COUNT 192 // actually 144 +#define IS31FL3737_LED_CONTROL_REGISTER_COUNT 24 + +#ifndef IS31FL3737_I2C_TIMEOUT +# define IS31FL3737_I2C_TIMEOUT 100 +#endif + +#ifndef IS31FL3737_I2C_PERSISTENCE +# define IS31FL3737_I2C_PERSISTENCE 0 +#endif + +#ifndef IS31FL3737_PWM_FREQUENCY +# define IS31FL3737_PWM_FREQUENCY IS31FL3737_PWM_FREQUENCY_8K4_HZ // PFS - IS31FL3737B only +#endif + +#ifndef IS31FL3737_SW_PULLUP +# define IS31FL3737_SW_PULLUP IS31FL3737_PUR_0_OHM +#endif + +#ifndef IS31FL3737_CS_PULLDOWN +# define IS31FL3737_CS_PULLDOWN IS31FL3737_PDR_0_OHM +#endif + +#ifndef IS31FL3737_GLOBAL_CURRENT +# define IS31FL3737_GLOBAL_CURRENT 0xFF +#endif + +const uint8_t i2c_addresses[IS31FL3737_DRIVER_COUNT] = { + IS31FL3737_I2C_ADDRESS_1, +#ifdef IS31FL3737_I2C_ADDRESS_2 + IS31FL3737_I2C_ADDRESS_2, +# ifdef IS31FL3737_I2C_ADDRESS_3 + IS31FL3737_I2C_ADDRESS_3, +# ifdef IS31FL3737_I2C_ADDRESS_4 + IS31FL3737_I2C_ADDRESS_4, +# endif +# endif +#endif +}; + +// These buffers match the IS31FL3737 PWM registers. +// The control buffers match the page 0 LED On/Off registers. +// Storing them like this is optimal for I2C transfers to the registers. +// We could optimize this and take out the unused registers from these +// buffers and the transfers in is31fl3737_write_pwm_buffer() but it's +// probably not worth the extra complexity. +typedef struct is31fl3737_driver_t { + uint8_t pwm_buffer[IS31FL3737_PWM_REGISTER_COUNT]; + bool pwm_buffer_dirty; + uint8_t led_control_buffer[IS31FL3737_LED_CONTROL_REGISTER_COUNT]; + bool led_control_buffer_dirty; +} PACKED is31fl3737_driver_t; + +is31fl3737_driver_t driver_buffers[IS31FL3737_DRIVER_COUNT] = {{ + .pwm_buffer = {0}, + .pwm_buffer_dirty = false, + .led_control_buffer = {0}, + .led_control_buffer_dirty = false, +}}; + +void is31fl3737_write_register(uint8_t index, uint8_t reg, uint8_t data) { +#if IS31FL3737_I2C_PERSISTENCE > 0 + for (uint8_t i = 0; i < IS31FL3737_I2C_PERSISTENCE; i++) { + if (i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3737_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break; + } +#else + i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3737_I2C_TIMEOUT); +#endif +} + +void is31fl3737_select_page(uint8_t index, uint8_t page) { + is31fl3737_write_register(index, IS31FL3737_REG_COMMAND_WRITE_LOCK, IS31FL3737_COMMAND_WRITE_LOCK_MAGIC); + is31fl3737_write_register(index, IS31FL3737_REG_COMMAND, page); +} + +void is31fl3737_write_pwm_buffer(uint8_t index) { + // Assumes page 1 is already selected. + // Transmit PWM registers in 12 transfers of 16 bytes. + + // Iterate over the pwm_buffer contents at 16 byte intervals. + for (uint8_t i = 0; i < IS31FL3737_PWM_REGISTER_COUNT; i += 16) { +#if IS31FL3737_I2C_PERSISTENCE > 0 + for (uint8_t j = 0; j < IS31FL3737_I2C_PERSISTENCE; j++) { + if (i2c_write_register(i2c_addresses[index] << 1, i, driver_buffers[index].pwm_buffer + i, 16, IS31FL3737_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break; + } +#else + i2c_write_register(i2c_addresses[index] << 1, i, driver_buffers[index].pwm_buffer + i, 16, IS31FL3737_I2C_TIMEOUT); +#endif + } +} + +void is31fl3737_init_drivers(void) { + i2c_init(); + +#if defined(IS31FL3737_SDB_PIN) + gpio_set_pin_output(IS31FL3737_SDB_PIN); + gpio_write_pin_high(IS31FL3737_SDB_PIN); +#endif + + for (uint8_t i = 0; i < IS31FL3737_DRIVER_COUNT; i++) { + is31fl3737_init(i); + } + + for (int i = 0; i < IS31FL3737_LED_COUNT; i++) { + is31fl3737_set_led_control_register(i, true); + } + + for (uint8_t i = 0; i < IS31FL3737_DRIVER_COUNT; i++) { + is31fl3737_update_led_control_registers(i); + } +} + +void is31fl3737_init(uint8_t index) { + // In order to avoid the LEDs being driven with garbage data + // in the LED driver's PWM registers, shutdown is enabled last. + // Set up the mode and other settings, clear the PWM registers, + // then disable software shutdown. + + is31fl3737_select_page(index, IS31FL3737_COMMAND_LED_CONTROL); + + // Turn off all LEDs. + for (uint8_t i = 0; i < IS31FL3737_LED_CONTROL_REGISTER_COUNT; i++) { + is31fl3737_write_register(index, i, 0x00); + } + + is31fl3737_select_page(index, IS31FL3737_COMMAND_PWM); + + // Set PWM on all LEDs to 0 + // No need to setup Breath registers to PWM as that is the default. + for (uint8_t i = 0; i < IS31FL3737_PWM_REGISTER_COUNT; i++) { + is31fl3737_write_register(index, i, 0x00); + } + + is31fl3737_select_page(index, IS31FL3737_COMMAND_FUNCTION); + + // Set de-ghost pull-up resistors (SWx) + is31fl3737_write_register(index, IS31FL3737_FUNCTION_REG_SW_PULLUP, IS31FL3737_SW_PULLUP); + // Set de-ghost pull-down resistors (CSx) + is31fl3737_write_register(index, IS31FL3737_FUNCTION_REG_CS_PULLDOWN, IS31FL3737_CS_PULLDOWN); + // Set global current to maximum. + is31fl3737_write_register(index, IS31FL3737_FUNCTION_REG_GLOBAL_CURRENT, IS31FL3737_GLOBAL_CURRENT); + // Disable software shutdown. + is31fl3737_write_register(index, IS31FL3737_FUNCTION_REG_CONFIGURATION, ((IS31FL3737_PWM_FREQUENCY & 0b111) << 3) | 0x01); + + // Wait 10ms to ensure the device has woken up. + wait_ms(10); +} + +void is31fl3737_set_value(int index, uint8_t value) { + is31fl3737_led_t led; + + if (index >= 0 && index < IS31FL3737_LED_COUNT) { + memcpy_P(&led, (&g_is31fl3737_leds[index]), sizeof(led)); + + if (driver_buffers[led.driver].pwm_buffer[led.v] == value) { + return; + } + + driver_buffers[led.driver].pwm_buffer[led.v] = value; + driver_buffers[led.driver].pwm_buffer_dirty = true; + } +} + +void is31fl3737_set_value_all(uint8_t value) { + for (int i = 0; i < IS31FL3737_LED_COUNT; i++) { + is31fl3737_set_value(i, value); + } +} + +void is31fl3737_set_led_control_register(uint8_t index, bool value) { + is31fl3737_led_t led; + memcpy_P(&led, (&g_is31fl3737_leds[index]), sizeof(led)); + + uint8_t control_register = led.v / 8; + uint8_t bit_value = led.v % 8; + + if (value) { + driver_buffers[led.driver].led_control_buffer[control_register] |= (1 << bit_value); + } else { + driver_buffers[led.driver].led_control_buffer[control_register] &= ~(1 << bit_value); + } + + driver_buffers[led.driver].led_control_buffer_dirty = true; +} + +void is31fl3737_update_pwm_buffers(uint8_t index) { + if (driver_buffers[index].pwm_buffer_dirty) { + is31fl3737_select_page(index, IS31FL3737_COMMAND_PWM); + + is31fl3737_write_pwm_buffer(index); + + driver_buffers[index].pwm_buffer_dirty = false; + } +} + +void is31fl3737_update_led_control_registers(uint8_t index) { + if (driver_buffers[index].led_control_buffer_dirty) { + is31fl3737_select_page(index, IS31FL3737_COMMAND_LED_CONTROL); + + for (uint8_t i = 0; i < IS31FL3737_LED_CONTROL_REGISTER_COUNT; i++) { + is31fl3737_write_register(index, i, driver_buffers[index].led_control_buffer[i]); + } + + driver_buffers[index].led_control_buffer_dirty = false; + } +} + +void is31fl3737_flush(void) { + for (uint8_t i = 0; i < IS31FL3737_DRIVER_COUNT; i++) { + is31fl3737_update_pwm_buffers(i); + } +} diff --git a/drivers/led/issi/is31fl3737-simple.h b/drivers/led/issi/is31fl3737-mono.h index 2658702b1b..99151c1e7a 100644 --- a/drivers/led/issi/is31fl3737-simple.h +++ b/drivers/led/issi/is31fl3737-mono.h @@ -100,9 +100,9 @@ typedef struct is31fl3737_led_t { extern const is31fl3737_led_t PROGMEM g_is31fl3737_leds[IS31FL3737_LED_COUNT]; void is31fl3737_init_drivers(void); -void is31fl3737_init(uint8_t addr); -void is31fl3737_write_register(uint8_t addr, uint8_t reg, uint8_t data); -void is31fl3737_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer); +void is31fl3737_init(uint8_t index); +void is31fl3737_write_register(uint8_t index, uint8_t reg, uint8_t data); +void is31fl3737_select_page(uint8_t index, uint8_t page); void is31fl3737_set_value(int index, uint8_t value); void is31fl3737_set_value_all(uint8_t value); @@ -113,8 +113,8 @@ void is31fl3737_set_led_control_register(uint8_t index, bool value); // (eg. from a timer interrupt). // Call this while idle (in between matrix scans). // If the buffer is dirty, it will update the driver with the buffer. -void is31fl3737_update_pwm_buffers(uint8_t addr, uint8_t index); -void is31fl3737_update_led_control_registers(uint8_t addr, uint8_t index); +void is31fl3737_update_pwm_buffers(uint8_t index); +void is31fl3737_update_led_control_registers(uint8_t index); void is31fl3737_flush(void); @@ -142,158 +142,158 @@ void is31fl3737_flush(void); #define IS31FL3737_PWM_FREQUENCY_2K1_HZ 0b011 #define IS31FL3737_PWM_FREQUENCY_1K05_HZ 0b100 -#define A_1 0x00 -#define A_2 0x01 -#define A_3 0x02 -#define A_4 0x03 -#define A_5 0x04 -#define A_6 0x05 -#define A_7 0x08 -#define A_8 0x09 -#define A_9 0x0A -#define A_10 0x0B -#define A_11 0x0C -#define A_12 0x0D - -#define B_1 0x10 -#define B_2 0x11 -#define B_3 0x12 -#define B_4 0x13 -#define B_5 0x14 -#define B_6 0x15 -#define B_7 0x18 -#define B_8 0x19 -#define B_9 0x1A -#define B_10 0x1B -#define B_11 0x1C -#define B_12 0x1D - -#define C_1 0x20 -#define C_2 0x21 -#define C_3 0x22 -#define C_4 0x23 -#define C_5 0x24 -#define C_6 0x25 -#define C_7 0x28 -#define C_8 0x29 -#define C_9 0x2A -#define C_10 0x2B -#define C_11 0x2C -#define C_12 0x2D - -#define D_1 0x30 -#define D_2 0x31 -#define D_3 0x32 -#define D_4 0x33 -#define D_5 0x34 -#define D_6 0x35 -#define D_7 0x38 -#define D_8 0x39 -#define D_9 0x3A -#define D_10 0x3B -#define D_11 0x3C -#define D_12 0x3D - -#define E_1 0x40 -#define E_2 0x41 -#define E_3 0x42 -#define E_4 0x43 -#define E_5 0x44 -#define E_6 0x45 -#define E_7 0x48 -#define E_8 0x49 -#define E_9 0x4A -#define E_10 0x4B -#define E_11 0x4C -#define E_12 0x4D - -#define F_1 0x50 -#define F_2 0x51 -#define F_3 0x52 -#define F_4 0x53 -#define F_5 0x54 -#define F_6 0x55 -#define F_7 0x58 -#define F_8 0x59 -#define F_9 0x5A -#define F_10 0x5B -#define F_11 0x5C -#define F_12 0x5D - -#define G_1 0x60 -#define G_2 0x61 -#define G_3 0x62 -#define G_4 0x63 -#define G_5 0x64 -#define G_6 0x65 -#define G_7 0x68 -#define G_8 0x69 -#define G_9 0x6A -#define G_10 0x6B -#define G_11 0x6C -#define G_12 0x6D - -#define H_1 0x70 -#define H_2 0x71 -#define H_3 0x72 -#define H_4 0x73 -#define H_5 0x74 -#define H_6 0x75 -#define H_7 0x78 -#define H_8 0x79 -#define H_9 0x7A -#define H_10 0x7B -#define H_11 0x7C -#define H_12 0x7D - -#define I_1 0x80 -#define I_2 0x81 -#define I_3 0x82 -#define I_4 0x83 -#define I_5 0x84 -#define I_6 0x85 -#define I_7 0x88 -#define I_8 0x89 -#define I_9 0x8A -#define I_10 0x8B -#define I_11 0x8C -#define I_12 0x8D - -#define J_1 0x90 -#define J_2 0x91 -#define J_3 0x92 -#define J_4 0x93 -#define J_5 0x94 -#define J_6 0x95 -#define J_7 0x98 -#define J_8 0x99 -#define J_9 0x9A -#define J_10 0x9B -#define J_11 0x9C -#define J_12 0x9D - -#define K_1 0xA0 -#define K_2 0xA1 -#define K_3 0xA2 -#define K_4 0xA3 -#define K_5 0xA4 -#define K_6 0xA5 -#define K_7 0xA8 -#define K_8 0xA9 -#define K_9 0xAA -#define K_10 0xAB -#define K_11 0xAC -#define K_12 0xAD - -#define L_1 0xB0 -#define L_2 0xB1 -#define L_3 0xB2 -#define L_4 0xB3 -#define L_5 0xB4 -#define L_6 0xB5 -#define L_7 0xB8 -#define L_8 0xB9 -#define L_9 0xBA -#define L_10 0xBB -#define L_11 0xBC -#define L_12 0xBD +#define SW1_CS1 0x00 +#define SW1_CS2 0x01 +#define SW1_CS3 0x02 +#define SW1_CS4 0x03 +#define SW1_CS5 0x04 +#define SW1_CS6 0x05 +#define SW1_CS7 0x08 +#define SW1_CS8 0x09 +#define SW1_CS9 0x0A +#define SW1_CS10 0x0B +#define SW1_CS11 0x0C +#define SW1_CS12 0x0D + +#define SW2_CS1 0x10 +#define SW2_CS2 0x11 +#define SW2_CS3 0x12 +#define SW2_CS4 0x13 +#define SW2_CS5 0x14 +#define SW2_CS6 0x15 +#define SW2_CS7 0x18 +#define SW2_CS8 0x19 +#define SW2_CS9 0x1A +#define SW2_CS10 0x1B +#define SW2_CS11 0x1C +#define SW2_CS12 0x1D + +#define SW3_CS1 0x20 +#define SW3_CS2 0x21 +#define SW3_CS3 0x22 +#define SW3_CS4 0x23 +#define SW3_CS5 0x24 +#define SW3_CS6 0x25 +#define SW3_CS7 0x28 +#define SW3_CS8 0x29 +#define SW3_CS9 0x2A +#define SW3_CS10 0x2B +#define SW3_CS11 0x2C +#define SW3_CS12 0x2D + +#define SW4_CS1 0x30 +#define SW4_CS2 0x31 +#define SW4_CS3 0x32 +#define SW4_CS4 0x33 +#define SW4_CS5 0x34 +#define SW4_CS6 0x35 +#define SW4_CS7 0x38 +#define SW4_CS8 0x39 +#define SW4_CS9 0x3A +#define SW4_CS10 0x3B +#define SW4_CS11 0x3C +#define SW4_CS12 0x3D + +#define SW5_CS1 0x40 +#define SW5_CS2 0x41 +#define SW5_CS3 0x42 +#define SW5_CS4 0x43 +#define SW5_CS5 0x44 +#define SW5_CS6 0x45 +#define SW5_CS7 0x48 +#define SW5_CS8 0x49 +#define SW5_CS9 0x4A +#define SW5_CS10 0x4B +#define SW5_CS11 0x4C +#define SW5_CS12 0x4D + +#define SW6_CS1 0x50 +#define SW6_CS2 0x51 +#define SW6_CS3 0x52 +#define SW6_CS4 0x53 +#define SW6_CS5 0x54 +#define SW6_CS6 0x55 +#define SW6_CS7 0x58 +#define SW6_CS8 0x59 +#define SW6_CS9 0x5A +#define SW6_CS10 0x5B +#define SW6_CS11 0x5C +#define SW6_CS12 0x5D + +#define SW7_CS1 0x60 +#define SW7_CS2 0x61 +#define SW7_CS3 0x62 +#define SW7_CS4 0x63 +#define SW7_CS5 0x64 +#define SW7_CS6 0x65 +#define SW7_CS7 0x68 +#define SW7_CS8 0x69 +#define SW7_CS9 0x6A +#define SW7_CS10 0x6B +#define SW7_CS11 0x6C +#define SW7_CS12 0x6D + +#define SW8_CS1 0x70 +#define SW8_CS2 0x71 +#define SW8_CS3 0x72 +#define SW8_CS4 0x73 +#define SW8_CS5 0x74 +#define SW8_CS6 0x75 +#define SW8_CS7 0x78 +#define SW8_CS8 0x79 +#define SW8_CS9 0x7A +#define SW8_CS10 0x7B +#define SW8_CS11 0x7C +#define SW8_CS12 0x7D + +#define SW9_CS1 0x80 +#define SW9_CS2 0x81 +#define SW9_CS3 0x82 +#define SW9_CS4 0x83 +#define SW9_CS5 0x84 +#define SW9_CS6 0x85 +#define SW9_CS7 0x88 +#define SW9_CS8 0x89 +#define SW9_CS9 0x8A +#define SW9_CS10 0x8B +#define SW9_CS11 0x8C +#define SW9_CS12 0x8D + +#define SW10_CS1 0x90 +#define SW10_CS2 0x91 +#define SW10_CS3 0x92 +#define SW10_CS4 0x93 +#define SW10_CS5 0x94 +#define SW10_CS6 0x95 +#define SW10_CS7 0x98 +#define SW10_CS8 0x99 +#define SW10_CS9 0x9A +#define SW10_CS10 0x9B +#define SW10_CS11 0x9C +#define SW10_CS12 0x9D + +#define SW11_CS1 0xA0 +#define SW11_CS2 0xA1 +#define SW11_CS3 0xA2 +#define SW11_CS4 0xA3 +#define SW11_CS5 0xA4 +#define SW11_CS6 0xA5 +#define SW11_CS7 0xA8 +#define SW11_CS8 0xA9 +#define SW11_CS9 0xAA +#define SW11_CS10 0xAB +#define SW11_CS11 0xAC +#define SW11_CS12 0xAD + +#define SW12_CS1 0xB0 +#define SW12_CS2 0xB1 +#define SW12_CS3 0xB2 +#define SW12_CS4 0xB3 +#define SW12_CS5 0xB4 +#define SW12_CS6 0xB5 +#define SW12_CS7 0xB8 +#define SW12_CS8 0xB9 +#define SW12_CS9 0xBA +#define SW12_CS10 0xBB +#define SW12_CS11 0xBC +#define SW12_CS12 0xBD diff --git a/drivers/led/issi/is31fl3737-simple.c b/drivers/led/issi/is31fl3737-simple.c deleted file mode 100644 index 7f641f4ca5..0000000000 --- a/drivers/led/issi/is31fl3737-simple.c +++ /dev/null @@ -1,249 +0,0 @@ -/* Copyright 2017 Jason Williams - * Copyright 2018 Jack Humbert - * Copyright 2018 Yiancar - * Copyright 2021 Doni Crosby - * - * 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 2 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/>. - */ - -#include "is31fl3737-simple.h" -#include <string.h> -#include "i2c_master.h" -#include "wait.h" - -#define IS31FL3737_PWM_REGISTER_COUNT 192 // actually 144 -#define IS31FL3737_LED_CONTROL_REGISTER_COUNT 24 - -#ifndef IS31FL3737_I2C_TIMEOUT -# define IS31FL3737_I2C_TIMEOUT 100 -#endif - -#ifndef IS31FL3737_I2C_PERSISTENCE -# define IS31FL3737_I2C_PERSISTENCE 0 -#endif - -#ifndef IS31FL3737_PWM_FREQUENCY -# define IS31FL3737_PWM_FREQUENCY IS31FL3737_PWM_FREQUENCY_8K4_HZ // PFS - IS31FL3737B only -#endif - -#ifndef IS31FL3737_SW_PULLUP -# define IS31FL3737_SW_PULLUP IS31FL3737_PUR_0_OHM -#endif - -#ifndef IS31FL3737_CS_PULLDOWN -# define IS31FL3737_CS_PULLDOWN IS31FL3737_PDR_0_OHM -#endif - -#ifndef IS31FL3737_GLOBAL_CURRENT -# define IS31FL3737_GLOBAL_CURRENT 0xFF -#endif - -// Transfer buffer for TWITransmitData() -uint8_t g_twi_transfer_buffer[20]; - -// These buffers match the IS31FL3737 PWM registers. -// The control buffers match the PG0 LED On/Off registers. -// Storing them like this is optimal for I2C transfers to the registers. -// We could optimize this and take out the unused registers from these -// buffers and the transfers in is31fl3737_write_pwm_buffer() but it's -// probably not worth the extra complexity. - -uint8_t g_pwm_buffer[IS31FL3737_DRIVER_COUNT][IS31FL3737_PWM_REGISTER_COUNT]; -bool g_pwm_buffer_update_required[IS31FL3737_DRIVER_COUNT] = {false}; - -uint8_t g_led_control_registers[IS31FL3737_DRIVER_COUNT][IS31FL3737_LED_CONTROL_REGISTER_COUNT] = {0}; -bool g_led_control_registers_update_required[IS31FL3737_DRIVER_COUNT] = {false}; - -void is31fl3737_write_register(uint8_t addr, uint8_t reg, uint8_t data) { - g_twi_transfer_buffer[0] = reg; - g_twi_transfer_buffer[1] = data; - -#if IS31FL3737_I2C_PERSISTENCE > 0 - for (uint8_t i = 0; i < IS31FL3737_I2C_PERSISTENCE; i++) { - if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, IS31FL3737_I2C_TIMEOUT) == 0) break; - } -#else - i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, IS31FL3737_I2C_TIMEOUT); -#endif -} - -void is31fl3737_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer) { - // assumes PG1 is already selected - - // transmit PWM registers in 12 transfers of 16 bytes - // g_twi_transfer_buffer[] is 20 bytes - - // iterate over the pwm_buffer contents at 16 byte intervals - for (int i = 0; i < IS31FL3737_PWM_REGISTER_COUNT; i += 16) { - g_twi_transfer_buffer[0] = i; - // copy the data from i to i+15 - // device will auto-increment register for data after the first byte - // thus this sets registers 0x00-0x0F, 0x10-0x1F, etc. in one transfer - memcpy(g_twi_transfer_buffer + 1, pwm_buffer + i, 16); - -#if IS31FL3737_I2C_PERSISTENCE > 0 - for (uint8_t i = 0; i < IS31FL3737_I2C_PERSISTENCE; i++) { - if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 17, IS31FL3737_I2C_TIMEOUT) == 0) break; - } -#else - i2c_transmit(addr << 1, g_twi_transfer_buffer, 17, IS31FL3737_I2C_TIMEOUT); -#endif - } -} - -void is31fl3737_init_drivers(void) { - i2c_init(); - - is31fl3737_init(IS31FL3737_I2C_ADDRESS_1); -#if defined(IS31FL3737_I2C_ADDRESS_2) - is31fl3737_init(IS31FL3737_I2C_ADDRESS_2); -# if defined(IS31FL3737_I2C_ADDRESS_3) - is31fl3737_init(IS31FL3737_I2C_ADDRESS_3); -# if defined(IS31FL3737_I2C_ADDRESS_4) - is31fl3737_init(IS31FL3737_I2C_ADDRESS_4); -# endif -# endif -#endif - - for (int i = 0; i < IS31FL3737_LED_COUNT; i++) { - is31fl3737_set_led_control_register(i, true); - } - - is31fl3737_update_led_control_registers(IS31FL3737_I2C_ADDRESS_1, 0); -#if defined(IS31FL3737_I2C_ADDRESS_2) - is31fl3737_update_led_control_registers(IS31FL3737_I2C_ADDRESS_2, 1); -# if defined(IS31FL3737_I2C_ADDRESS_3) - is31fl3737_update_led_control_registers(IS31FL3737_I2C_ADDRESS_3, 2); -# if defined(IS31FL3737_I2C_ADDRESS_4) - is31fl3737_update_led_control_registers(IS31FL3737_I2C_ADDRESS_4, 3); -# endif -# endif -#endif -} - -void is31fl3737_init(uint8_t addr) { - // In order to avoid the LEDs being driven with garbage data - // in the LED driver's PWM registers, shutdown is enabled last. - // Set up the mode and other settings, clear the PWM registers, - // then disable software shutdown. - - // Unlock the command register. - is31fl3737_write_register(addr, IS31FL3737_REG_COMMAND_WRITE_LOCK, IS31FL3737_COMMAND_WRITE_LOCK_MAGIC); - - // Select PG0 - is31fl3737_write_register(addr, IS31FL3737_REG_COMMAND, IS31FL3737_COMMAND_LED_CONTROL); - // Turn off all LEDs. - for (int i = 0; i < IS31FL3737_LED_CONTROL_REGISTER_COUNT; i++) { - is31fl3737_write_register(addr, i, 0x00); - } - - // Unlock the command register. - is31fl3737_write_register(addr, IS31FL3737_REG_COMMAND_WRITE_LOCK, IS31FL3737_COMMAND_WRITE_LOCK_MAGIC); - - // Select PG1 - is31fl3737_write_register(addr, IS31FL3737_REG_COMMAND, IS31FL3737_COMMAND_PWM); - // Set PWM on all LEDs to 0 - // No need to setup Breath registers to PWM as that is the default. - for (int i = 0; i < IS31FL3737_PWM_REGISTER_COUNT; i++) { - is31fl3737_write_register(addr, i, 0x00); - } - - // Unlock the command register. - is31fl3737_write_register(addr, IS31FL3737_REG_COMMAND_WRITE_LOCK, IS31FL3737_COMMAND_WRITE_LOCK_MAGIC); - - // Select PG3 - is31fl3737_write_register(addr, IS31FL3737_REG_COMMAND, IS31FL3737_COMMAND_FUNCTION); - // Set de-ghost pull-up resistors (SWx) - is31fl3737_write_register(addr, IS31FL3737_FUNCTION_REG_SW_PULLUP, IS31FL3737_SW_PULLUP); - // Set de-ghost pull-down resistors (CSx) - is31fl3737_write_register(addr, IS31FL3737_FUNCTION_REG_CS_PULLDOWN, IS31FL3737_CS_PULLDOWN); - // Set global current to maximum. - is31fl3737_write_register(addr, IS31FL3737_FUNCTION_REG_GLOBAL_CURRENT, IS31FL3737_GLOBAL_CURRENT); - // Disable software shutdown. - is31fl3737_write_register(addr, IS31FL3737_FUNCTION_REG_CONFIGURATION, ((IS31FL3737_PWM_FREQUENCY & 0b111) << 3) | 0x01); - - // Wait 10ms to ensure the device has woken up. - wait_ms(10); -} - -void is31fl3737_set_value(int index, uint8_t value) { - is31fl3737_led_t led; - if (index >= 0 && index < IS31FL3737_LED_COUNT) { - memcpy_P(&led, (&g_is31fl3737_leds[index]), sizeof(led)); - - if (g_pwm_buffer[led.driver][led.v] == value) { - return; - } - g_pwm_buffer[led.driver][led.v] = value; - g_pwm_buffer_update_required[led.driver] = true; - } -} - -void is31fl3737_set_value_all(uint8_t value) { - for (int i = 0; i < IS31FL3737_LED_COUNT; i++) { - is31fl3737_set_value(i, value); - } -} - -void is31fl3737_set_led_control_register(uint8_t index, bool value) { - is31fl3737_led_t led; - memcpy_P(&led, (&g_is31fl3737_leds[index]), sizeof(led)); - - uint8_t control_register = led.v / 8; - uint8_t bit_value = led.v % 8; - - if (value) { - g_led_control_registers[led.driver][control_register] |= (1 << bit_value); - } else { - g_led_control_registers[led.driver][control_register] &= ~(1 << bit_value); - } - - g_led_control_registers_update_required[led.driver] = true; -} - -void is31fl3737_update_pwm_buffers(uint8_t addr, uint8_t index) { - if (g_pwm_buffer_update_required[index]) { - // Firstly we need to unlock the command register and select PG1 - is31fl3737_write_register(addr, IS31FL3737_REG_COMMAND_WRITE_LOCK, IS31FL3737_COMMAND_WRITE_LOCK_MAGIC); - is31fl3737_write_register(addr, IS31FL3737_REG_COMMAND, IS31FL3737_COMMAND_PWM); - - is31fl3737_write_pwm_buffer(addr, g_pwm_buffer[index]); - g_pwm_buffer_update_required[index] = false; - } -} - -void is31fl3737_update_led_control_registers(uint8_t addr, uint8_t index) { - if (g_led_control_registers_update_required[index]) { - // Firstly we need to unlock the command register and select PG0 - is31fl3737_write_register(addr, IS31FL3737_REG_COMMAND_WRITE_LOCK, IS31FL3737_COMMAND_WRITE_LOCK_MAGIC); - is31fl3737_write_register(addr, IS31FL3737_REG_COMMAND, IS31FL3737_COMMAND_LED_CONTROL); - for (int i = 0; i < IS31FL3737_LED_CONTROL_REGISTER_COUNT; i++) { - is31fl3737_write_register(addr, i, g_led_control_registers[index][i]); - } - g_led_control_registers_update_required[index] = false; - } -} - -void is31fl3737_flush(void) { - is31fl3737_update_pwm_buffers(IS31FL3737_I2C_ADDRESS_1, 0); -#if defined(IS31FL3737_I2C_ADDRESS_2) - is31fl3737_update_pwm_buffers(IS31FL3737_I2C_ADDRESS_2, 1); -# if defined(IS31FL3737_I2C_ADDRESS_3) - is31fl3737_update_pwm_buffers(IS31FL3737_I2C_ADDRESS_3, 2); -# if defined(IS31FL3737_I2C_ADDRESS_4) - is31fl3737_update_pwm_buffers(IS31FL3737_I2C_ADDRESS_4, 3); -# endif -# endif -#endif -} diff --git a/drivers/led/issi/is31fl3737.c b/drivers/led/issi/is31fl3737.c index a458431952..b27a4cbb0f 100644 --- a/drivers/led/issi/is31fl3737.c +++ b/drivers/led/issi/is31fl3737.c @@ -18,8 +18,8 @@ */ #include "is31fl3737.h" -#include <string.h> #include "i2c_master.h" +#include "gpio.h" #include "wait.h" #define IS31FL3737_PWM_REGISTER_COUNT 192 // actually 144 @@ -41,7 +41,7 @@ # define IS31FL3737_SW_PULLUP IS31FL3737_PUR_0_OHM #endif -#ifndef IS31FL3737_CS_PULLDONW +#ifndef IS31FL3737_CS_PULLDOWN # define IS31FL3737_CS_PULLDOWN IS31FL3737_PDR_0_OHM #endif @@ -49,55 +49,66 @@ # define IS31FL3737_GLOBAL_CURRENT 0xFF #endif -// Transfer buffer for TWITransmitData() -uint8_t g_twi_transfer_buffer[20]; +const uint8_t i2c_addresses[IS31FL3737_DRIVER_COUNT] = { + IS31FL3737_I2C_ADDRESS_1, +#ifdef IS31FL3737_I2C_ADDRESS_2 + IS31FL3737_I2C_ADDRESS_2, +# ifdef IS31FL3737_I2C_ADDRESS_3 + IS31FL3737_I2C_ADDRESS_3, +# ifdef IS31FL3737_I2C_ADDRESS_4 + IS31FL3737_I2C_ADDRESS_4, +# endif +# endif +#endif +}; // These buffers match the IS31FL3737 PWM registers. -// The control buffers match the PG0 LED On/Off registers. +// The control buffers match the page 0 LED On/Off registers. // Storing them like this is optimal for I2C transfers to the registers. // We could optimize this and take out the unused registers from these // buffers and the transfers in is31fl3737_write_pwm_buffer() but it's // probably not worth the extra complexity. - -uint8_t g_pwm_buffer[IS31FL3737_DRIVER_COUNT][IS31FL3737_PWM_REGISTER_COUNT]; -bool g_pwm_buffer_update_required[IS31FL3737_DRIVER_COUNT] = {false}; - -uint8_t g_led_control_registers[IS31FL3737_DRIVER_COUNT][IS31FL3737_LED_CONTROL_REGISTER_COUNT] = {0}; -bool g_led_control_registers_update_required[IS31FL3737_DRIVER_COUNT] = {false}; - -void is31fl3737_write_register(uint8_t addr, uint8_t reg, uint8_t data) { - g_twi_transfer_buffer[0] = reg; - g_twi_transfer_buffer[1] = data; - +typedef struct is31fl3737_driver_t { + uint8_t pwm_buffer[IS31FL3737_PWM_REGISTER_COUNT]; + bool pwm_buffer_dirty; + uint8_t led_control_buffer[IS31FL3737_LED_CONTROL_REGISTER_COUNT]; + bool led_control_buffer_dirty; +} PACKED is31fl3737_driver_t; + +is31fl3737_driver_t driver_buffers[IS31FL3737_DRIVER_COUNT] = {{ + .pwm_buffer = {0}, + .pwm_buffer_dirty = false, + .led_control_buffer = {0}, + .led_control_buffer_dirty = false, +}}; + +void is31fl3737_write_register(uint8_t index, uint8_t reg, uint8_t data) { #if IS31FL3737_I2C_PERSISTENCE > 0 for (uint8_t i = 0; i < IS31FL3737_I2C_PERSISTENCE; i++) { - if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, IS31FL3737_I2C_TIMEOUT) == 0) break; + if (i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3737_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break; } #else - i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, IS31FL3737_I2C_TIMEOUT); + i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3737_I2C_TIMEOUT); #endif } -void is31fl3737_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer) { - // assumes PG1 is already selected - - // transmit PWM registers in 12 transfers of 16 bytes - // g_twi_transfer_buffer[] is 20 bytes +void is31fl3737_select_page(uint8_t index, uint8_t page) { + is31fl3737_write_register(index, IS31FL3737_REG_COMMAND_WRITE_LOCK, IS31FL3737_COMMAND_WRITE_LOCK_MAGIC); + is31fl3737_write_register(index, IS31FL3737_REG_COMMAND, page); +} - // iterate over the pwm_buffer contents at 16 byte intervals - for (int i = 0; i < IS31FL3737_PWM_REGISTER_COUNT; i += 16) { - g_twi_transfer_buffer[0] = i; - // copy the data from i to i+15 - // device will auto-increment register for data after the first byte - // thus this sets registers 0x00-0x0F, 0x10-0x1F, etc. in one transfer - memcpy(g_twi_transfer_buffer + 1, pwm_buffer + i, 16); +void is31fl3737_write_pwm_buffer(uint8_t index) { + // Assumes page 1 is already selected. + // Transmit PWM registers in 12 transfers of 16 bytes. + // Iterate over the pwm_buffer contents at 16 byte intervals. + for (uint8_t i = 0; i < IS31FL3737_PWM_REGISTER_COUNT; i += 16) { #if IS31FL3737_I2C_PERSISTENCE > 0 - for (uint8_t i = 0; i < IS31FL3737_I2C_PERSISTENCE; i++) { - if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 17, IS31FL3737_I2C_TIMEOUT) == 0) break; + for (uint8_t j = 0; j < IS31FL3737_I2C_PERSISTENCE; j++) { + if (i2c_write_register(i2c_addresses[index] << 1, i, driver_buffers[index].pwm_buffer + i, 16, IS31FL3737_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break; } #else - i2c_transmit(addr << 1, g_twi_transfer_buffer, 17, IS31FL3737_I2C_TIMEOUT); + i2c_write_register(i2c_addresses[index] << 1, i, driver_buffers[index].pwm_buffer + i, 16, IS31FL3737_I2C_TIMEOUT); #endif } } @@ -105,73 +116,55 @@ void is31fl3737_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer) { void is31fl3737_init_drivers(void) { i2c_init(); - is31fl3737_init(IS31FL3737_I2C_ADDRESS_1); -#if defined(IS31FL3737_I2C_ADDRESS_2) - is31fl3737_init(IS31FL3737_I2C_ADDRESS_2); -# if defined(IS31FL3737_I2C_ADDRESS_3) - is31fl3737_init(IS31FL3737_I2C_ADDRESS_3); -# if defined(IS31FL3737_I2C_ADDRESS_4) - is31fl3737_init(IS31FL3737_I2C_ADDRESS_4); -# endif -# endif +#if defined(IS31FL3737_SDB_PIN) + gpio_set_pin_output(IS31FL3737_SDB_PIN); + gpio_write_pin_high(IS31FL3737_SDB_PIN); #endif + for (uint8_t i = 0; i < IS31FL3737_DRIVER_COUNT; i++) { + is31fl3737_init(i); + } + for (int i = 0; i < IS31FL3737_LED_COUNT; i++) { is31fl3737_set_led_control_register(i, true, true, true); } - is31fl3737_update_led_control_registers(IS31FL3737_I2C_ADDRESS_1, 0); -#if defined(IS31FL3737_I2C_ADDRESS_2) - is31fl3737_update_led_control_registers(IS31FL3737_I2C_ADDRESS_2, 1); -# if defined(IS31FL3737_I2C_ADDRESS_3) - is31fl3737_update_led_control_registers(IS31FL3737_I2C_ADDRESS_3, 2); -# if defined(IS31FL3737_I2C_ADDRESS_4) - is31fl3737_update_led_control_registers(IS31FL3737_I2C_ADDRESS_4, 3); -# endif -# endif -#endif + for (uint8_t i = 0; i < IS31FL3737_DRIVER_COUNT; i++) { + is31fl3737_update_led_control_registers(i); + } } -void is31fl3737_init(uint8_t addr) { +void is31fl3737_init(uint8_t index) { // In order to avoid the LEDs being driven with garbage data // in the LED driver's PWM registers, shutdown is enabled last. // Set up the mode and other settings, clear the PWM registers, // then disable software shutdown. - // Unlock the command register. - is31fl3737_write_register(addr, IS31FL3737_REG_COMMAND_WRITE_LOCK, IS31FL3737_COMMAND_WRITE_LOCK_MAGIC); + is31fl3737_select_page(index, IS31FL3737_COMMAND_LED_CONTROL); - // Select PG0 - is31fl3737_write_register(addr, IS31FL3737_REG_COMMAND, IS31FL3737_COMMAND_LED_CONTROL); // Turn off all LEDs. - for (int i = 0; i < IS31FL3737_LED_CONTROL_REGISTER_COUNT; i++) { - is31fl3737_write_register(addr, i, 0x00); + for (uint8_t i = 0; i < IS31FL3737_LED_CONTROL_REGISTER_COUNT; i++) { + is31fl3737_write_register(index, i, 0x00); } - // Unlock the command register. - is31fl3737_write_register(addr, IS31FL3737_REG_COMMAND_WRITE_LOCK, IS31FL3737_COMMAND_WRITE_LOCK_MAGIC); + is31fl3737_select_page(index, IS31FL3737_COMMAND_PWM); - // Select PG1 - is31fl3737_write_register(addr, IS31FL3737_REG_COMMAND, IS31FL3737_COMMAND_PWM); // Set PWM on all LEDs to 0 // No need to setup Breath registers to PWM as that is the default. - for (int i = 0; i < IS31FL3737_PWM_REGISTER_COUNT; i++) { - is31fl3737_write_register(addr, i, 0x00); + for (uint8_t i = 0; i < IS31FL3737_PWM_REGISTER_COUNT; i++) { + is31fl3737_write_register(index, i, 0x00); } - // Unlock the command register. - is31fl3737_write_register(addr, IS31FL3737_REG_COMMAND_WRITE_LOCK, IS31FL3737_COMMAND_WRITE_LOCK_MAGIC); + is31fl3737_select_page(index, IS31FL3737_COMMAND_FUNCTION); - // Select PG3 - is31fl3737_write_register(addr, IS31FL3737_REG_COMMAND, IS31FL3737_COMMAND_FUNCTION); // Set de-ghost pull-up resistors (SWx) - is31fl3737_write_register(addr, IS31FL3737_FUNCTION_REG_SW_PULLUP, IS31FL3737_SW_PULLUP); + is31fl3737_write_register(index, IS31FL3737_FUNCTION_REG_SW_PULLUP, IS31FL3737_SW_PULLUP); // Set de-ghost pull-down resistors (CSx) - is31fl3737_write_register(addr, IS31FL3737_FUNCTION_REG_CS_PULLDOWN, IS31FL3737_CS_PULLDOWN); + is31fl3737_write_register(index, IS31FL3737_FUNCTION_REG_CS_PULLDOWN, IS31FL3737_CS_PULLDOWN); // Set global current to maximum. - is31fl3737_write_register(addr, IS31FL3737_FUNCTION_REG_GLOBAL_CURRENT, IS31FL3737_GLOBAL_CURRENT); + is31fl3737_write_register(index, IS31FL3737_FUNCTION_REG_GLOBAL_CURRENT, IS31FL3737_GLOBAL_CURRENT); // Disable software shutdown. - is31fl3737_write_register(addr, IS31FL3737_FUNCTION_REG_CONFIGURATION, ((IS31FL3737_PWM_FREQUENCY & 0b111) << 3) | 0x01); + is31fl3737_write_register(index, IS31FL3737_FUNCTION_REG_CONFIGURATION, ((IS31FL3737_PWM_FREQUENCY & 0b111) << 3) | 0x01); // Wait 10ms to ensure the device has woken up. wait_ms(10); @@ -179,16 +172,18 @@ void is31fl3737_init(uint8_t addr) { void is31fl3737_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) { is31fl3737_led_t led; + if (index >= 0 && index < IS31FL3737_LED_COUNT) { memcpy_P(&led, (&g_is31fl3737_leds[index]), sizeof(led)); - if (g_pwm_buffer[led.driver][led.r] == red && g_pwm_buffer[led.driver][led.g] == green && g_pwm_buffer[led.driver][led.b] == blue) { + if (driver_buffers[led.driver].pwm_buffer[led.r] == red && driver_buffers[led.driver].pwm_buffer[led.g] == green && driver_buffers[led.driver].pwm_buffer[led.b] == blue) { return; } - g_pwm_buffer[led.driver][led.r] = red; - g_pwm_buffer[led.driver][led.g] = green; - g_pwm_buffer[led.driver][led.b] = blue; - g_pwm_buffer_update_required[led.driver] = true; + + driver_buffers[led.driver].pwm_buffer[led.r] = red; + driver_buffers[led.driver].pwm_buffer[led.g] = green; + driver_buffers[led.driver].pwm_buffer[led.b] = blue; + driver_buffers[led.driver].pwm_buffer_dirty = true; } } @@ -210,56 +205,48 @@ void is31fl3737_set_led_control_register(uint8_t index, bool red, bool green, bo uint8_t bit_b = led.b % 8; if (red) { - g_led_control_registers[led.driver][control_register_r] |= (1 << bit_r); + driver_buffers[led.driver].led_control_buffer[control_register_r] |= (1 << bit_r); } else { - g_led_control_registers[led.driver][control_register_r] &= ~(1 << bit_r); + driver_buffers[led.driver].led_control_buffer[control_register_r] &= ~(1 << bit_r); } if (green) { - g_led_control_registers[led.driver][control_register_g] |= (1 << bit_g); + driver_buffers[led.driver].led_control_buffer[control_register_g] |= (1 << bit_g); } else { - g_led_control_registers[led.driver][control_register_g] &= ~(1 << bit_g); + driver_buffers[led.driver].led_control_buffer[control_register_g] &= ~(1 << bit_g); } if (blue) { - g_led_control_registers[led.driver][control_register_b] |= (1 << bit_b); + driver_buffers[led.driver].led_control_buffer[control_register_b] |= (1 << bit_b); } else { - g_led_control_registers[led.driver][control_register_b] &= ~(1 << bit_b); + driver_buffers[led.driver].led_control_buffer[control_register_b] &= ~(1 << bit_b); } - g_led_control_registers_update_required[led.driver] = true; + driver_buffers[led.driver].led_control_buffer_dirty = true; } -void is31fl3737_update_pwm_buffers(uint8_t addr, uint8_t index) { - if (g_pwm_buffer_update_required[index]) { - // Firstly we need to unlock the command register and select PG1 - is31fl3737_write_register(addr, IS31FL3737_REG_COMMAND_WRITE_LOCK, IS31FL3737_COMMAND_WRITE_LOCK_MAGIC); - is31fl3737_write_register(addr, IS31FL3737_REG_COMMAND, IS31FL3737_COMMAND_PWM); +void is31fl3737_update_pwm_buffers(uint8_t index) { + if (driver_buffers[index].pwm_buffer_dirty) { + is31fl3737_select_page(index, IS31FL3737_COMMAND_PWM); + + is31fl3737_write_pwm_buffer(index); - is31fl3737_write_pwm_buffer(addr, g_pwm_buffer[index]); - g_pwm_buffer_update_required[index] = false; + driver_buffers[index].pwm_buffer_dirty = false; } } -void is31fl3737_update_led_control_registers(uint8_t addr, uint8_t index) { - if (g_led_control_registers_update_required[index]) { - // Firstly we need to unlock the command register and select PG0 - is31fl3737_write_register(addr, IS31FL3737_REG_COMMAND_WRITE_LOCK, IS31FL3737_COMMAND_WRITE_LOCK_MAGIC); - is31fl3737_write_register(addr, IS31FL3737_REG_COMMAND, IS31FL3737_COMMAND_LED_CONTROL); - for (int i = 0; i < IS31FL3737_LED_CONTROL_REGISTER_COUNT; i++) { - is31fl3737_write_register(addr, i, g_led_control_registers[index][i]); +void is31fl3737_update_led_control_registers(uint8_t index) { + if (driver_buffers[index].led_control_buffer_dirty) { + is31fl3737_select_page(index, IS31FL3737_COMMAND_LED_CONTROL); + + for (uint8_t i = 0; i < IS31FL3737_LED_CONTROL_REGISTER_COUNT; i++) { + is31fl3737_write_register(index, i, driver_buffers[index].led_control_buffer[i]); } - g_led_control_registers_update_required[index] = false; + + driver_buffers[index].led_control_buffer_dirty = false; } } void is31fl3737_flush(void) { - is31fl3737_update_pwm_buffers(IS31FL3737_I2C_ADDRESS_1, 0); -#if defined(IS31FL3737_I2C_ADDRESS_2) - is31fl3737_update_pwm_buffers(IS31FL3737_I2C_ADDRESS_2, 1); -# if defined(IS31FL3737_I2C_ADDRESS_3) - is31fl3737_update_pwm_buffers(IS31FL3737_I2C_ADDRESS_3, 2); -# if defined(IS31FL3737_I2C_ADDRESS_4) - is31fl3737_update_pwm_buffers(IS31FL3737_I2C_ADDRESS_4, 3); -# endif -# endif -#endif + for (uint8_t i = 0; i < IS31FL3737_DRIVER_COUNT; i++) { + is31fl3737_update_pwm_buffers(i); + } } diff --git a/drivers/led/issi/is31fl3737.h b/drivers/led/issi/is31fl3737.h index 8de3bf4ef5..735a3a1e30 100644 --- a/drivers/led/issi/is31fl3737.h +++ b/drivers/led/issi/is31fl3737.h @@ -117,9 +117,9 @@ typedef struct is31fl3737_led_t { extern const is31fl3737_led_t PROGMEM g_is31fl3737_leds[IS31FL3737_LED_COUNT]; void is31fl3737_init_drivers(void); -void is31fl3737_init(uint8_t addr); -void is31fl3737_write_register(uint8_t addr, uint8_t reg, uint8_t data); -void is31fl3737_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer); +void is31fl3737_init(uint8_t index); +void is31fl3737_write_register(uint8_t index, uint8_t reg, uint8_t data); +void is31fl3737_select_page(uint8_t index, uint8_t page); void is31fl3737_set_color(int index, uint8_t red, uint8_t green, uint8_t blue); void is31fl3737_set_color_all(uint8_t red, uint8_t green, uint8_t blue); @@ -130,8 +130,8 @@ void is31fl3737_set_led_control_register(uint8_t index, bool red, bool green, bo // (eg. from a timer interrupt). // Call this while idle (in between matrix scans). // If the buffer is dirty, it will update the driver with the buffer. -void is31fl3737_update_pwm_buffers(uint8_t addr, uint8_t index); -void is31fl3737_update_led_control_registers(uint8_t addr, uint8_t index); +void is31fl3737_update_pwm_buffers(uint8_t index); +void is31fl3737_update_led_control_registers(uint8_t index); void is31fl3737_flush(void); @@ -159,158 +159,316 @@ void is31fl3737_flush(void); #define IS31FL3737_PWM_FREQUENCY_2K1_HZ 0b011 #define IS31FL3737_PWM_FREQUENCY_1K05_HZ 0b100 -#define A_1 0x00 -#define A_2 0x01 -#define A_3 0x02 -#define A_4 0x03 -#define A_5 0x04 -#define A_6 0x05 -#define A_7 0x08 -#define A_8 0x09 -#define A_9 0x0A -#define A_10 0x0B -#define A_11 0x0C -#define A_12 0x0D - -#define B_1 0x10 -#define B_2 0x11 -#define B_3 0x12 -#define B_4 0x13 -#define B_5 0x14 -#define B_6 0x15 -#define B_7 0x18 -#define B_8 0x19 -#define B_9 0x1A -#define B_10 0x1B -#define B_11 0x1C -#define B_12 0x1D - -#define C_1 0x20 -#define C_2 0x21 -#define C_3 0x22 -#define C_4 0x23 -#define C_5 0x24 -#define C_6 0x25 -#define C_7 0x28 -#define C_8 0x29 -#define C_9 0x2A -#define C_10 0x2B -#define C_11 0x2C -#define C_12 0x2D - -#define D_1 0x30 -#define D_2 0x31 -#define D_3 0x32 -#define D_4 0x33 -#define D_5 0x34 -#define D_6 0x35 -#define D_7 0x38 -#define D_8 0x39 -#define D_9 0x3A -#define D_10 0x3B -#define D_11 0x3C -#define D_12 0x3D - -#define E_1 0x40 -#define E_2 0x41 -#define E_3 0x42 -#define E_4 0x43 -#define E_5 0x44 -#define E_6 0x45 -#define E_7 0x48 -#define E_8 0x49 -#define E_9 0x4A -#define E_10 0x4B -#define E_11 0x4C -#define E_12 0x4D - -#define F_1 0x50 -#define F_2 0x51 -#define F_3 0x52 -#define F_4 0x53 -#define F_5 0x54 -#define F_6 0x55 -#define F_7 0x58 -#define F_8 0x59 -#define F_9 0x5A -#define F_10 0x5B -#define F_11 0x5C -#define F_12 0x5D - -#define G_1 0x60 -#define G_2 0x61 -#define G_3 0x62 -#define G_4 0x63 -#define G_5 0x64 -#define G_6 0x65 -#define G_7 0x68 -#define G_8 0x69 -#define G_9 0x6A -#define G_10 0x6B -#define G_11 0x6C -#define G_12 0x6D - -#define H_1 0x70 -#define H_2 0x71 -#define H_3 0x72 -#define H_4 0x73 -#define H_5 0x74 -#define H_6 0x75 -#define H_7 0x78 -#define H_8 0x79 -#define H_9 0x7A -#define H_10 0x7B -#define H_11 0x7C -#define H_12 0x7D - -#define I_1 0x80 -#define I_2 0x81 -#define I_3 0x82 -#define I_4 0x83 -#define I_5 0x84 -#define I_6 0x85 -#define I_7 0x88 -#define I_8 0x89 -#define I_9 0x8A -#define I_10 0x8B -#define I_11 0x8C -#define I_12 0x8D - -#define J_1 0x90 -#define J_2 0x91 -#define J_3 0x92 -#define J_4 0x93 -#define J_5 0x94 -#define J_6 0x95 -#define J_7 0x98 -#define J_8 0x99 -#define J_9 0x9A -#define J_10 0x9B -#define J_11 0x9C -#define J_12 0x9D - -#define K_1 0xA0 -#define K_2 0xA1 -#define K_3 0xA2 -#define K_4 0xA3 -#define K_5 0xA4 -#define K_6 0xA5 -#define K_7 0xA8 -#define K_8 0xA9 -#define K_9 0xAA -#define K_10 0xAB -#define K_11 0xAC -#define K_12 0xAD - -#define L_1 0xB0 -#define L_2 0xB1 -#define L_3 0xB2 -#define L_4 0xB3 -#define L_5 0xB4 -#define L_6 0xB5 -#define L_7 0xB8 -#define L_8 0xB9 -#define L_9 0xBA -#define L_10 0xBB -#define L_11 0xBC -#define L_12 0xBD +#define SW1_CS1 0x00 +#define SW1_CS2 0x01 +#define SW1_CS3 0x02 +#define SW1_CS4 0x03 +#define SW1_CS5 0x04 +#define SW1_CS6 0x05 +#define SW1_CS7 0x08 +#define SW1_CS8 0x09 +#define SW1_CS9 0x0A +#define SW1_CS10 0x0B +#define SW1_CS11 0x0C +#define SW1_CS12 0x0D + +#define SW2_CS1 0x10 +#define SW2_CS2 0x11 +#define SW2_CS3 0x12 +#define SW2_CS4 0x13 +#define SW2_CS5 0x14 +#define SW2_CS6 0x15 +#define SW2_CS7 0x18 +#define SW2_CS8 0x19 +#define SW2_CS9 0x1A +#define SW2_CS10 0x1B +#define SW2_CS11 0x1C +#define SW2_CS12 0x1D + +#define SW3_CS1 0x20 +#define SW3_CS2 0x21 +#define SW3_CS3 0x22 +#define SW3_CS4 0x23 +#define SW3_CS5 0x24 +#define SW3_CS6 0x25 +#define SW3_CS7 0x28 +#define SW3_CS8 0x29 +#define SW3_CS9 0x2A +#define SW3_CS10 0x2B +#define SW3_CS11 0x2C +#define SW3_CS12 0x2D + +#define SW4_CS1 0x30 +#define SW4_CS2 0x31 +#define SW4_CS3 0x32 +#define SW4_CS4 0x33 +#define SW4_CS5 0x34 +#define SW4_CS6 0x35 +#define SW4_CS7 0x38 +#define SW4_CS8 0x39 +#define SW4_CS9 0x3A +#define SW4_CS10 0x3B +#define SW4_CS11 0x3C +#define SW4_CS12 0x3D + +#define SW5_CS1 0x40 +#define SW5_CS2 0x41 +#define SW5_CS3 0x42 +#define SW5_CS4 0x43 +#define SW5_CS5 0x44 +#define SW5_CS6 0x45 +#define SW5_CS7 0x48 +#define SW5_CS8 0x49 +#define SW5_CS9 0x4A +#define SW5_CS10 0x4B +#define SW5_CS11 0x4C +#define SW5_CS12 0x4D + +#define SW6_CS1 0x50 +#define SW6_CS2 0x51 +#define SW6_CS3 0x52 +#define SW6_CS4 0x53 +#define SW6_CS5 0x54 +#define SW6_CS6 0x55 +#define SW6_CS7 0x58 +#define SW6_CS8 0x59 +#define SW6_CS9 0x5A +#define SW6_CS10 0x5B +#define SW6_CS11 0x5C +#define SW6_CS12 0x5D + +#define SW7_CS1 0x60 +#define SW7_CS2 0x61 +#define SW7_CS3 0x62 +#define SW7_CS4 0x63 +#define SW7_CS5 0x64 +#define SW7_CS6 0x65 +#define SW7_CS7 0x68 +#define SW7_CS8 0x69 +#define SW7_CS9 0x6A +#define SW7_CS10 0x6B +#define SW7_CS11 0x6C +#define SW7_CS12 0x6D + +#define SW8_CS1 0x70 +#define SW8_CS2 0x71 +#define SW8_CS3 0x72 +#define SW8_CS4 0x73 +#define SW8_CS5 0x74 +#define SW8_CS6 0x75 +#define SW8_CS7 0x78 +#define SW8_CS8 0x79 +#define SW8_CS9 0x7A +#define SW8_CS10 0x7B +#define SW8_CS11 0x7C +#define SW8_CS12 0x7D + +#define SW9_CS1 0x80 +#define SW9_CS2 0x81 +#define SW9_CS3 0x82 +#define SW9_CS4 0x83 +#define SW9_CS5 0x84 +#define SW9_CS6 0x85 +#define SW9_CS7 0x88 +#define SW9_CS8 0x89 +#define SW9_CS9 0x8A +#define SW9_CS10 0x8B +#define SW9_CS11 0x8C +#define SW9_CS12 0x8D + +#define SW10_CS1 0x90 +#define SW10_CS2 0x91 +#define SW10_CS3 0x92 +#define SW10_CS4 0x93 +#define SW10_CS5 0x94 +#define SW10_CS6 0x95 +#define SW10_CS7 0x98 +#define SW10_CS8 0x99 +#define SW10_CS9 0x9A +#define SW10_CS10 0x9B +#define SW10_CS11 0x9C +#define SW10_CS12 0x9D + +#define SW11_CS1 0xA0 +#define SW11_CS2 0xA1 +#define SW11_CS3 0xA2 +#define SW11_CS4 0xA3 +#define SW11_CS5 0xA4 +#define SW11_CS6 0xA5 +#define SW11_CS7 0xA8 +#define SW11_CS8 0xA9 +#define SW11_CS9 0xAA +#define SW11_CS10 0xAB +#define SW11_CS11 0xAC +#define SW11_CS12 0xAD + +#define SW12_CS1 0xB0 +#define SW12_CS2 0xB1 +#define SW12_CS3 0xB2 +#define SW12_CS4 0xB3 +#define SW12_CS5 0xB4 +#define SW12_CS6 0xB5 +#define SW12_CS7 0xB8 +#define SW12_CS8 0xB9 +#define SW12_CS9 0xBA +#define SW12_CS10 0xBB +#define SW12_CS11 0xBC +#define SW12_CS12 0xBD + +// DEPRECATED - DO NOT USE + +#define A_1 SW1_CS1 +#define A_2 SW1_CS2 +#define A_3 SW1_CS3 +#define A_4 SW1_CS4 +#define A_5 SW1_CS5 +#define A_6 SW1_CS6 +#define A_7 SW1_CS7 +#define A_8 SW1_CS8 +#define A_9 SW1_CS9 +#define A_10 SW1_CS10 +#define A_11 SW1_CS11 +#define A_12 SW1_CS12 + +#define B_1 SW2_CS1 +#define B_2 SW2_CS2 +#define B_3 SW2_CS3 +#define B_4 SW2_CS4 +#define B_5 SW2_CS5 +#define B_6 SW2_CS6 +#define B_7 SW2_CS7 +#define B_8 SW2_CS8 +#define B_9 SW2_CS9 +#define B_10 SW2_CS10 +#define B_11 SW2_CS11 +#define B_12 SW2_CS12 + +#define C_1 SW3_CS1 +#define C_2 SW3_CS2 +#define C_3 SW3_CS3 +#define C_4 SW3_CS4 +#define C_5 SW3_CS5 +#define C_6 SW3_CS6 +#define C_7 SW3_CS7 +#define C_8 SW3_CS8 +#define C_9 SW3_CS9 +#define C_10 SW3_CS10 +#define C_11 SW3_CS11 +#define C_12 SW3_CS12 + +#define D_1 SW4_CS1 +#define D_2 SW4_CS2 +#define D_3 SW4_CS3 +#define D_4 SW4_CS4 +#define D_5 SW4_CS5 +#define D_6 SW4_CS6 +#define D_7 SW4_CS7 +#define D_8 SW4_CS8 +#define D_9 SW4_CS9 +#define D_10 SW4_CS10 +#define D_11 SW4_CS11 +#define D_12 SW4_CS12 + +#define E_1 SW5_CS1 +#define E_2 SW5_CS2 +#define E_3 SW5_CS3 +#define E_4 SW5_CS4 +#define E_5 SW5_CS5 +#define E_6 SW5_CS6 +#define E_7 SW5_CS7 +#define E_8 SW5_CS8 +#define E_9 SW5_CS9 +#define E_10 SW5_CS10 +#define E_11 SW5_CS11 +#define E_12 SW5_CS12 + +#define F_1 SW6_CS1 +#define F_2 SW6_CS2 +#define F_3 SW6_CS3 +#define F_4 SW6_CS4 +#define F_5 SW6_CS5 +#define F_6 SW6_CS6 +#define F_7 SW6_CS7 +#define F_8 SW6_CS8 +#define F_9 SW6_CS9 +#define F_10 SW6_CS10 +#define F_11 SW6_CS11 +#define F_12 SW6_CS12 + +#define G_1 SW7_CS1 +#define G_2 SW7_CS2 +#define G_3 SW7_CS3 +#define G_4 SW7_CS4 +#define G_5 SW7_CS5 +#define G_6 SW7_CS6 +#define G_7 SW7_CS7 +#define G_8 SW7_CS8 +#define G_9 SW7_CS9 +#define G_10 SW7_CS10 +#define G_11 SW7_CS11 +#define G_12 SW7_CS12 + +#define H_1 SW8_CS1 +#define H_2 SW8_CS2 +#define H_3 SW8_CS3 +#define H_4 SW8_CS4 +#define H_5 SW8_CS5 +#define H_6 SW8_CS6 +#define H_7 SW8_CS7 +#define H_8 SW8_CS8 +#define H_9 SW8_CS9 +#define H_10 SW8_CS10 +#define H_11 SW8_CS11 +#define H_12 SW8_CS12 + +#define I_1 SW9_CS1 +#define I_2 SW9_CS2 +#define I_3 SW9_CS3 +#define I_4 SW9_CS4 +#define I_5 SW9_CS5 +#define I_6 SW9_CS6 +#define I_7 SW9_CS7 +#define I_8 SW9_CS8 +#define I_9 SW9_CS9 +#define I_10 SW9_CS10 +#define I_11 SW9_CS11 +#define I_12 SW9_CS12 + +#define J_1 SW10_CS1 +#define J_2 SW10_CS2 +#define J_3 SW10_CS3 +#define J_4 SW10_CS4 +#define J_5 SW10_CS5 +#define J_6 SW10_CS6 +#define J_7 SW10_CS7 +#define J_8 SW10_CS8 +#define J_9 SW10_CS9 +#define J_10 SW10_CS10 +#define J_11 SW10_CS11 +#define J_12 SW10_CS12 + +#define K_1 SW11_CS1 +#define K_2 SW11_CS2 +#define K_3 SW11_CS3 +#define K_4 SW11_CS4 +#define K_5 SW11_CS5 +#define K_6 SW11_CS6 +#define K_7 SW11_CS7 +#define K_8 SW11_CS8 +#define K_9 SW11_CS9 +#define K_10 SW11_CS10 +#define K_11 SW11_CS11 +#define K_12 SW11_CS12 + +#define L_1 SW12_CS1 +#define L_2 SW12_CS2 +#define L_3 SW12_CS3 +#define L_4 SW12_CS4 +#define L_5 SW12_CS5 +#define L_6 SW12_CS6 +#define L_7 SW12_CS7 +#define L_8 SW12_CS8 +#define L_9 SW12_CS9 +#define L_10 SW12_CS10 +#define L_11 SW12_CS11 +#define L_12 SW12_CS12 diff --git a/drivers/led/issi/is31fl3741-mono.c b/drivers/led/issi/is31fl3741-mono.c new file mode 100644 index 0000000000..dbccba0fc8 --- /dev/null +++ b/drivers/led/issi/is31fl3741-mono.c @@ -0,0 +1,282 @@ +/* Copyright 2017 Jason Williams + * Copyright 2018 Jack Humbert + * Copyright 2018 Yiancar + * Copyright 2020 MelGeek + * + * 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 2 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/>. + */ + +#include "is31fl3741-mono.h" +#include "i2c_master.h" +#include "gpio.h" +#include "wait.h" + +#define IS31FL3741_PWM_0_REGISTER_COUNT 180 +#define IS31FL3741_PWM_1_REGISTER_COUNT 171 +#define IS31FL3741_SCALING_0_REGISTER_COUNT 180 +#define IS31FL3741_SCALING_1_REGISTER_COUNT 171 + +#ifndef IS31FL3741_I2C_TIMEOUT +# define IS31FL3741_I2C_TIMEOUT 100 +#endif + +#ifndef IS31FL3741_I2C_PERSISTENCE +# define IS31FL3741_I2C_PERSISTENCE 0 +#endif + +#ifndef IS31FL3741_CONFIGURATION +# define IS31FL3741_CONFIGURATION 0x01 +#endif + +#ifndef IS31FL3741_PWM_FREQUENCY +# define IS31FL3741_PWM_FREQUENCY IS31FL3741_PWM_FREQUENCY_29K_HZ +#endif + +#ifndef IS31FL3741_SW_PULLUP +# define IS31FL3741_SW_PULLUP IS31FL3741_PUR_32K_OHM +#endif + +#ifndef IS31FL3741_CS_PULLDOWN +# define IS31FL3741_CS_PULLDOWN IS31FL3741_PDR_32K_OHM +#endif + +#ifndef IS31FL3741_GLOBAL_CURRENT +# define IS31FL3741_GLOBAL_CURRENT 0xFF +#endif + +const uint8_t i2c_addresses[IS31FL3741_DRIVER_COUNT] = { + IS31FL3741_I2C_ADDRESS_1, +#ifdef IS31FL3741_I2C_ADDRESS_2 + IS31FL3741_I2C_ADDRESS_2, +# ifdef IS31FL3741_I2C_ADDRESS_3 + IS31FL3741_I2C_ADDRESS_3, +# ifdef IS31FL3741_I2C_ADDRESS_4 + IS31FL3741_I2C_ADDRESS_4, +# endif +# endif +#endif +}; + +// These buffers match the IS31FL3741 and IS31FL3741A PWM registers. +// The scaling buffers match the page 2 and 3 LED On/Off registers. +// Storing them like this is optimal for I2C transfers to the registers. +// We could optimize this and take out the unused registers from these +// buffers and the transfers in is31fl3741_write_pwm_buffer() but it's +// probably not worth the extra complexity. +typedef struct is31fl3741_driver_t { + uint8_t pwm_buffer_0[IS31FL3741_PWM_0_REGISTER_COUNT]; + uint8_t pwm_buffer_1[IS31FL3741_PWM_1_REGISTER_COUNT]; + bool pwm_buffer_dirty; + uint8_t scaling_buffer_0[IS31FL3741_SCALING_0_REGISTER_COUNT]; + uint8_t scaling_buffer_1[IS31FL3741_SCALING_1_REGISTER_COUNT]; + bool scaling_buffer_dirty; +} PACKED is31fl3741_driver_t; + +is31fl3741_driver_t driver_buffers[IS31FL3741_DRIVER_COUNT] = {{ + .pwm_buffer_0 = {0}, + .pwm_buffer_1 = {0}, + .pwm_buffer_dirty = false, + .scaling_buffer_0 = {0}, + .scaling_buffer_1 = {0}, + .scaling_buffer_dirty = false, +}}; + +void is31fl3741_write_register(uint8_t index, uint8_t reg, uint8_t data) { +#if IS31FL3741_I2C_PERSISTENCE > 0 + for (uint8_t i = 0; i < IS31FL3741_I2C_PERSISTENCE; i++) { + if (i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3741_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break; + } +#else + i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3741_I2C_TIMEOUT); +#endif +} + +void is31fl3741_select_page(uint8_t index, uint8_t page) { + is31fl3741_write_register(index, IS31FL3741_REG_COMMAND_WRITE_LOCK, IS31FL3741_COMMAND_WRITE_LOCK_MAGIC); + is31fl3741_write_register(index, IS31FL3741_REG_COMMAND, page); +} + +void is31fl3741_write_pwm_buffer(uint8_t index) { + is31fl3741_select_page(index, IS31FL3741_COMMAND_PWM_0); + + // Transmit PWM0 registers in 6 transfers of 30 bytes. + + // Iterate over the pwm_buffer_0 contents at 30 byte intervals. + for (uint8_t i = 0; i < IS31FL3741_PWM_0_REGISTER_COUNT; i += 30) { +#if IS31FL3741_I2C_PERSISTENCE > 0 + for (uint8_t j = 0; j < IS31FL3741_I2C_PERSISTENCE; j++) { + if (i2c_write_register(i2c_addresses[index] << 1, i, driver_buffers[index].pwm_buffer_0 + i, 30, IS31FL3741_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break; + } +#else + i2c_write_register(i2c_addresses[index] << 1, i, driver_buffers[index].pwm_buffer_0 + i, 30, IS31FL3741_I2C_TIMEOUT); +#endif + } + + is31fl3741_select_page(index, IS31FL3741_COMMAND_PWM_1); + + // Transmit PWM1 registers in 9 transfers of 19 bytes. + + // Iterate over the pwm_buffer_1 contents at 19 byte intervals. + for (uint8_t i = 0; i < IS31FL3741_PWM_1_REGISTER_COUNT; i += 19) { +#if IS31FL3741_I2C_PERSISTENCE > 0 + for (uint8_t i = 0; i < IS31FL3741_I2C_PERSISTENCE; i++) { + if (i2c_write_register(i2c_addresses[index] << 1, i, driver_buffers[index].pwm_buffer_1 + i, 19, IS31FL3741_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break; + } +#else + i2c_write_register(i2c_addresses[index] << 1, i, driver_buffers[index].pwm_buffer_1 + i, 19, IS31FL3741_I2C_TIMEOUT); +#endif + } +} + +void is31fl3741_init_drivers(void) { + i2c_init(); + +#if defined(IS31FL3741_SDB_PIN) + gpio_set_pin_output(IS31FL3741_SDB_PIN); + gpio_write_pin_high(IS31FL3741_SDB_PIN); +#endif + + for (uint8_t i = 0; i < IS31FL3741_DRIVER_COUNT; i++) { + is31fl3741_init(i); + } + + for (int i = 0; i < IS31FL3741_LED_COUNT; i++) { + is31fl3741_set_led_control_register(i, true); + } + + for (uint8_t i = 0; i < IS31FL3741_DRIVER_COUNT; i++) { + is31fl3741_update_led_control_registers(i); + } +} + +void is31fl3741_init(uint8_t index) { + // In order to avoid the LEDs being driven with garbage data + // in the LED driver's PWM registers, shutdown is enabled last. + // Set up the mode and other settings, clear the PWM registers, + // then disable software shutdown. + // Unlock the command register. + + is31fl3741_select_page(index, IS31FL3741_COMMAND_FUNCTION); + + // Set to Normal operation + is31fl3741_write_register(index, IS31FL3741_FUNCTION_REG_CONFIGURATION, IS31FL3741_CONFIGURATION); + + // Set Golbal Current Control Register + is31fl3741_write_register(index, IS31FL3741_FUNCTION_REG_GLOBAL_CURRENT, IS31FL3741_GLOBAL_CURRENT); + // Set Pull up & Down for SWx CSy + is31fl3741_write_register(index, IS31FL3741_FUNCTION_REG_PULLDOWNUP, ((IS31FL3741_CS_PULLDOWN << 4) | IS31FL3741_SW_PULLUP)); + // Set PWM frequency + is31fl3741_write_register(index, IS31FL3741_FUNCTION_REG_PWM_FREQUENCY, (IS31FL3741_PWM_FREQUENCY & 0b1111)); + + // is31fl3741_update_led_scaling_registers(index, 0xFF, 0xFF, 0xFF); + + // Wait 10ms to ensure the device has woken up. + wait_ms(10); +} + +uint8_t get_pwm_value(uint8_t driver, uint16_t reg) { + if (reg & 0x100) { + return driver_buffers[driver].pwm_buffer_1[reg & 0xFF]; + } else { + return driver_buffers[driver].pwm_buffer_0[reg]; + } +} + +void set_pwm_value(uint8_t driver, uint16_t reg, uint8_t value) { + if (reg & 0x100) { + driver_buffers[driver].pwm_buffer_1[reg & 0xFF] = value; + } else { + driver_buffers[driver].pwm_buffer_0[reg] = value; + } +} + +void is31fl3741_set_value(int index, uint8_t value) { + is31fl3741_led_t led; + + if (index >= 0 && index < IS31FL3741_LED_COUNT) { + memcpy_P(&led, (&g_is31fl3741_leds[index]), sizeof(led)); + + if (get_pwm_value(led.driver, led.v) == value) { + return; + } + + set_pwm_value(led.driver, led.v, value); + driver_buffers[led.driver].pwm_buffer_dirty = true; + } +} + +void is31fl3741_set_value_all(uint8_t value) { + for (int i = 0; i < IS31FL3741_LED_COUNT; i++) { + is31fl3741_set_value(i, value); + } +} + +void set_scaling_value(uint8_t driver, uint16_t reg, uint8_t value) { + if (reg & 0x100) { + driver_buffers[driver].scaling_buffer_1[reg & 0xFF] = value; + } else { + driver_buffers[driver].scaling_buffer_0[reg] = value; + } +} + +void is31fl3741_set_led_control_register(uint8_t index, bool value) { + is31fl3741_led_t led; + memcpy_P(&led, (&g_is31fl3741_leds[index]), sizeof(led)); + + set_scaling_value(led.driver, led.v, value ? 0xFF : 0x00); + + driver_buffers[led.driver].scaling_buffer_dirty = true; +} + +void is31fl3741_update_pwm_buffers(uint8_t index) { + if (driver_buffers[index].pwm_buffer_dirty) { + is31fl3741_write_pwm_buffer(index); + + driver_buffers[index].pwm_buffer_dirty = false; + } +} + +void is31fl3741_set_pwm_buffer(const is31fl3741_led_t *pled, uint8_t value) { + set_pwm_value(pled->driver, pled->v, value); + driver_buffers[pled->driver].pwm_buffer_dirty = true; +} + +void is31fl3741_update_led_control_registers(uint8_t index) { + if (driver_buffers[index].scaling_buffer_dirty) { + is31fl3741_select_page(index, IS31FL3741_COMMAND_SCALING_0); + + for (uint8_t i = 0; i < IS31FL3741_SCALING_0_REGISTER_COUNT; i++) { + is31fl3741_write_register(index, i, driver_buffers[index].scaling_buffer_0[i]); + } + + is31fl3741_select_page(index, IS31FL3741_COMMAND_SCALING_1); + + for (uint8_t i = 0; i < IS31FL3741_SCALING_1_REGISTER_COUNT; i++) { + is31fl3741_write_register(index, i, driver_buffers[index].scaling_buffer_1[i]); + } + + driver_buffers[index].scaling_buffer_dirty = false; + } +} + +void is31fl3741_set_scaling_registers(const is31fl3741_led_t *pled, uint8_t value) { + set_scaling_value(pled->driver, pled->v, value); + driver_buffers[pled->driver].scaling_buffer_dirty = true; +} + +void is31fl3741_flush(void) { + for (uint8_t i = 0; i < IS31FL3741_DRIVER_COUNT; i++) { + is31fl3741_update_pwm_buffers(i); + } +} diff --git a/drivers/led/issi/is31fl3741-mono.h b/drivers/led/issi/is31fl3741-mono.h new file mode 100644 index 0000000000..6d4f70b1b3 --- /dev/null +++ b/drivers/led/issi/is31fl3741-mono.h @@ -0,0 +1,887 @@ +/* Copyright 2017 Jason Williams + * Copyright 2018 Jack Humbert + * Copyright 2018 Yiancar + * Copyright 2020 MelGeek + * + * 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 2 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/>. + */ + +#pragma once + +#include <stdint.h> +#include <stdbool.h> +#include "progmem.h" +#include "util.h" + +// ======== DEPRECATED DEFINES - DO NOT USE ======== +#ifdef ISSI_TIMEOUT +# define IS31FL3741_I2C_TIMEOUT ISSI_TIMEOUT +#endif +#ifdef ISSI_PERSISTENCE +# define IS31FL3741_I2C_PERSISTENCE ISSI_PERSISTENCE +#endif +#ifdef ISSI_CONFIGURATION +# define IS31FL3741_CONFIGURATION ISSI_CONFIGURATION +#endif +#ifdef ISSI_SWPULLUP +# define IS31FL3741_SW_PULLUP ISSI_SWPULLUP +#endif +#ifdef ISSI_CSPULLUP +# define IS31FL3741_CS_PULLDOWN ISSI_CSPULLUP +#endif +#ifdef ISSI_GLOBALCURRENT +# define IS31FL3741_GLOBAL_CURRENT ISSI_GLOBALCURRENT +#endif + +#define PUR_0R IS31FL3741_PUR_0_OHM +#define PUR_05KR IS31FL3741_PUR_0K5_OHM +#define PUR_1KR IS31FL3741_PUR_1K_OHM +#define PUR_2KR IS31FL3741_PUR_2K_OHM +#define PUR_4KR IS31FL3741_PUR_4K_OHM +#define PUR_8KR IS31FL3741_PUR_8K_OHM +#define PUR_16KR IS31FL3741_PUR_16K_OHM +#define PUR_32KR IS31FL3741_PUR_32K_OHM +// ======== + +#define IS31FL3741_REG_INTERRUPT_MASK 0xF0 +#define IS31FL3741_REG_INTERRUPT_STATUS 0xF1 +#define IS31FL3741_REG_ID 0xFC + +#define IS31FL3741_REG_COMMAND 0xFD + +#define IS31FL3741_COMMAND_PWM_0 0x00 +#define IS31FL3741_COMMAND_PWM_1 0x01 +#define IS31FL3741_COMMAND_SCALING_0 0x02 +#define IS31FL3741_COMMAND_SCALING_1 0x03 +#define IS31FL3741_COMMAND_FUNCTION 0x04 + +#define IS31FL3741_FUNCTION_REG_CONFIGURATION 0x00 +#define IS31FL3741_FUNCTION_REG_GLOBAL_CURRENT 0x01 +#define IS31FL3741_FUNCTION_REG_PULLDOWNUP 0x02 +#define IS31FL3741_FUNCTION_REG_PWM_FREQUENCY 0x36 +#define IS31FL3741_FUNCTION_REG_RESET 0x3F + +#define IS31FL3741_REG_COMMAND_WRITE_LOCK 0xFE +#define IS31FL3741_COMMAND_WRITE_LOCK_MAGIC 0xC5 + +#define IS31FL3741_I2C_ADDRESS_GND 0x30 +#define IS31FL3741_I2C_ADDRESS_SCL 0x31 +#define IS31FL3741_I2C_ADDRESS_SDA 0x32 +#define IS31FL3741_I2C_ADDRESS_VCC 0x33 + +#if defined(LED_MATRIX_IS31FL3741) +# define IS31FL3741_LED_COUNT LED_MATRIX_LED_COUNT +#endif + +#if defined(IS31FL3741_I2C_ADDRESS_4) +# define IS31FL3741_DRIVER_COUNT 4 +#elif defined(IS31FL3741_I2C_ADDRESS_3) +# define IS31FL3741_DRIVER_COUNT 3 +#elif defined(IS31FL3741_I2C_ADDRESS_2) +# define IS31FL3741_DRIVER_COUNT 2 +#elif defined(IS31FL3741_I2C_ADDRESS_1) +# define IS31FL3741_DRIVER_COUNT 1 +#endif + +typedef struct is31fl3741_led_t { + uint8_t driver : 2; + uint16_t v : 9; +} PACKED is31fl3741_led_t; + +extern const is31fl3741_led_t PROGMEM g_is31fl3741_leds[IS31FL3741_LED_COUNT]; + +void is31fl3741_init_drivers(void); +void is31fl3741_init(uint8_t index); +void is31fl3741_write_register(uint8_t index, uint8_t reg, uint8_t data); +void is31fl3741_select_page(uint8_t index, uint8_t page); + +void is31fl3741_set_value(int index, uint8_t value); +void is31fl3741_set_value_all(uint8_t value); + +void is31fl3741_set_led_control_register(uint8_t index, bool value); + +// This should not be called from an interrupt +// (eg. from a timer interrupt). +// Call this while idle (in between matrix scans). +// If the buffer is dirty, it will update the driver with the buffer. +void is31fl3741_update_pwm_buffers(uint8_t index); +void is31fl3741_update_led_control_registers(uint8_t index); +void is31fl3741_set_scaling_registers(const is31fl3741_led_t *pled, uint8_t value); + +void is31fl3741_set_pwm_buffer(const is31fl3741_led_t *pled, uint8_t value); + +void is31fl3741_flush(void); + +#define IS31FL3741_PDR_0_OHM 0b000 // No pull-down resistor +#define IS31FL3741_PDR_0K5_OHM 0b001 // 0.5 kOhm resistor +#define IS31FL3741_PDR_1K_OHM 0b010 // 1 kOhm resistor +#define IS31FL3741_PDR_2K_OHM 0b011 // 2 kOhm resistor +#define IS31FL3741_PDR_4K_OHM 0b100 // 4 kOhm resistor +#define IS31FL3741_PDR_8K_OHM 0b101 // 8 kOhm resistor +#define IS31FL3741_PDR_16K_OHM 0b110 // 16 kOhm resistor +#define IS31FL3741_PDR_32K_OHM 0b111 // 32 kOhm resistor + +#define IS31FL3741_PUR_0_OHM 0b000 // No pull-up resistor +#define IS31FL3741_PUR_0K5_OHM 0b001 // 0.5 kOhm resistor +#define IS31FL3741_PUR_1K_OHM 0b010 // 1 kOhm resistor +#define IS31FL3741_PUR_2K_OHM 0b011 // 2 kOhm resistor +#define IS31FL3741_PUR_4K_OHM 0b100 // 4 kOhm resistor +#define IS31FL3741_PUR_8K_OHM 0b101 // 8 kOhm resistor +#define IS31FL3741_PUR_16K_OHM 0b110 // 16 kOhm resistor +#define IS31FL3741_PUR_32K_OHM 0b111 // 32 kOhm resistor + +#define IS31FL3741_PWM_FREQUENCY_29K_HZ 0b0000 +#define IS31FL3741_PWM_FREQUENCY_3K6_HZ 0b0011 +#define IS31FL3741_PWM_FREQUENCY_1K8_HZ 0b0111 +#define IS31FL3741_PWM_FREQUENCY_900_HZ 0b1011 + +#define SW1_CS1 0x00 +#define SW1_CS2 0x01 +#define SW1_CS3 0x02 +#define SW1_CS4 0x03 +#define SW1_CS5 0x04 +#define SW1_CS6 0x05 +#define SW1_CS7 0x06 +#define SW1_CS8 0x07 +#define SW1_CS9 0x08 +#define SW1_CS10 0x09 +#define SW1_CS11 0x0A +#define SW1_CS12 0x0B +#define SW1_CS13 0x0C +#define SW1_CS14 0x0D +#define SW1_CS15 0x0E +#define SW1_CS16 0x0F +#define SW1_CS17 0x10 +#define SW1_CS18 0x11 +#define SW1_CS19 0x12 +#define SW1_CS20 0x13 +#define SW1_CS21 0x14 +#define SW1_CS22 0x15 +#define SW1_CS23 0x16 +#define SW1_CS24 0x17 +#define SW1_CS25 0x18 +#define SW1_CS26 0x19 +#define SW1_CS27 0x1A +#define SW1_CS28 0x1B +#define SW1_CS29 0x1C +#define SW1_CS30 0x1D + +#define SW2_CS1 0x1E +#define SW2_CS2 0x1F +#define SW2_CS3 0x20 +#define SW2_CS4 0x21 +#define SW2_CS5 0x22 +#define SW2_CS6 0x23 +#define SW2_CS7 0x24 +#define SW2_CS8 0x25 +#define SW2_CS9 0x26 +#define SW2_CS10 0x27 +#define SW2_CS11 0x28 +#define SW2_CS12 0x29 +#define SW2_CS13 0x2A +#define SW2_CS14 0x2B +#define SW2_CS15 0x2C +#define SW2_CS16 0x2D +#define SW2_CS17 0x2E +#define SW2_CS18 0x2F +#define SW2_CS19 0x30 +#define SW2_CS20 0x31 +#define SW2_CS21 0x32 +#define SW2_CS22 0x33 +#define SW2_CS23 0x34 +#define SW2_CS24 0x35 +#define SW2_CS25 0x36 +#define SW2_CS26 0x37 +#define SW2_CS27 0x38 +#define SW2_CS28 0x39 +#define SW2_CS29 0x3A +#define SW2_CS30 0x3B + +#define SW3_CS1 0x3C +#define SW3_CS2 0x3D +#define SW3_CS3 0x3E +#define SW3_CS4 0x3F +#define SW3_CS5 0x40 +#define SW3_CS6 0x41 +#define SW3_CS7 0x42 +#define SW3_CS8 0x43 +#define SW3_CS9 0x44 +#define SW3_CS10 0x45 +#define SW3_CS11 0x46 +#define SW3_CS12 0x47 +#define SW3_CS13 0x48 +#define SW3_CS14 0x49 +#define SW3_CS15 0x4A +#define SW3_CS16 0x4B +#define SW3_CS17 0x4C +#define SW3_CS18 0x4D +#define SW3_CS19 0x4E +#define SW3_CS20 0x4F +#define SW3_CS21 0x50 +#define SW3_CS22 0x51 +#define SW3_CS23 0x52 +#define SW3_CS24 0x53 +#define SW3_CS25 0x54 +#define SW3_CS26 0x55 +#define SW3_CS27 0x56 +#define SW3_CS28 0x57 +#define SW3_CS29 0x58 +#define SW3_CS30 0x59 + +#define SW4_CS1 0x5A +#define SW4_CS2 0x5B +#define SW4_CS3 0x5C +#define SW4_CS4 0x5D +#define SW4_CS5 0x5E +#define SW4_CS6 0x5F +#define SW4_CS7 0x60 +#define SW4_CS8 0x61 +#define SW4_CS9 0x62 +#define SW4_CS10 0x63 +#define SW4_CS11 0x64 +#define SW4_CS12 0x65 +#define SW4_CS13 0x66 +#define SW4_CS14 0x67 +#define SW4_CS15 0x68 +#define SW4_CS16 0x69 +#define SW4_CS17 0x6A +#define SW4_CS18 0x6B +#define SW4_CS19 0x6C +#define SW4_CS20 0x6D +#define SW4_CS21 0x6E +#define SW4_CS22 0x6F +#define SW4_CS23 0x70 +#define SW4_CS24 0x71 +#define SW4_CS25 0x72 +#define SW4_CS26 0x73 +#define SW4_CS27 0x74 +#define SW4_CS28 0x75 +#define SW4_CS29 0x76 +#define SW4_CS30 0x77 + +#define SW5_CS1 0x78 +#define SW5_CS2 0x79 +#define SW5_CS3 0x7A +#define SW5_CS4 0x7B +#define SW5_CS5 0x7C +#define SW5_CS6 0x7D +#define SW5_CS7 0x7E +#define SW5_CS8 0x7F +#define SW5_CS9 0x80 +#define SW5_CS10 0x81 +#define SW5_CS11 0x82 +#define SW5_CS12 0x83 +#define SW5_CS13 0x84 +#define SW5_CS14 0x85 +#define SW5_CS15 0x86 +#define SW5_CS16 0x87 +#define SW5_CS17 0x88 +#define SW5_CS18 0x89 +#define SW5_CS19 0x8A +#define SW5_CS20 0x8B +#define SW5_CS21 0x8C +#define SW5_CS22 0x8D +#define SW5_CS23 0x8E +#define SW5_CS24 0x8F +#define SW5_CS25 0x90 +#define SW5_CS26 0x91 +#define SW5_CS27 0x92 +#define SW5_CS28 0x93 +#define SW5_CS29 0x94 +#define SW5_CS30 0x95 + +#define SW6_CS1 0x96 +#define SW6_CS2 0x97 +#define SW6_CS3 0x98 +#define SW6_CS4 0x99 +#define SW6_CS5 0x9A +#define SW6_CS6 0x9B +#define SW6_CS7 0x9C +#define SW6_CS8 0x9D +#define SW6_CS9 0x9E +#define SW6_CS10 0x9F +#define SW6_CS11 0xA0 +#define SW6_CS12 0xA1 +#define SW6_CS13 0xA2 +#define SW6_CS14 0xA3 +#define SW6_CS15 0xA4 +#define SW6_CS16 0xA5 +#define SW6_CS17 0xA6 +#define SW6_CS18 0xA7 +#define SW6_CS19 0xA8 +#define SW6_CS20 0xA9 +#define SW6_CS21 0xAA +#define SW6_CS22 0xAB +#define SW6_CS23 0xAC +#define SW6_CS24 0xAD +#define SW6_CS25 0xAE +#define SW6_CS26 0xAF +#define SW6_CS27 0xB0 +#define SW6_CS28 0xB1 +#define SW6_CS29 0xB2 +#define SW6_CS30 0xB3 + +#define SW7_CS1 0x100 +#define SW7_CS2 0x101 +#define SW7_CS3 0x102 +#define SW7_CS4 0x103 +#define SW7_CS5 0x104 +#define SW7_CS6 0x105 +#define SW7_CS7 0x106 +#define SW7_CS8 0x107 +#define SW7_CS9 0x108 +#define SW7_CS10 0x109 +#define SW7_CS11 0x10A +#define SW7_CS12 0x10B +#define SW7_CS13 0x10C +#define SW7_CS14 0x10D +#define SW7_CS15 0x10E +#define SW7_CS16 0x10F +#define SW7_CS17 0x110 +#define SW7_CS18 0x111 +#define SW7_CS19 0x112 +#define SW7_CS20 0x113 +#define SW7_CS21 0x114 +#define SW7_CS22 0x115 +#define SW7_CS23 0x116 +#define SW7_CS24 0x117 +#define SW7_CS25 0x118 +#define SW7_CS26 0x119 +#define SW7_CS27 0x11A +#define SW7_CS28 0x11B +#define SW7_CS29 0x11C +#define SW7_CS30 0x11D + +#define SW8_CS1 0x11E +#define SW8_CS2 0x11F +#define SW8_CS3 0x120 +#define SW8_CS4 0x121 +#define SW8_CS5 0x122 +#define SW8_CS6 0x123 +#define SW8_CS7 0x124 +#define SW8_CS8 0x125 +#define SW8_CS9 0x126 +#define SW8_CS10 0x127 +#define SW8_CS11 0x128 +#define SW8_CS12 0x129 +#define SW8_CS13 0x12A +#define SW8_CS14 0x12B +#define SW8_CS15 0x12C +#define SW8_CS16 0x12D +#define SW8_CS17 0x12E +#define SW8_CS18 0x12F +#define SW8_CS19 0x130 +#define SW8_CS20 0x131 +#define SW8_CS21 0x132 +#define SW8_CS22 0x133 +#define SW8_CS23 0x134 +#define SW8_CS24 0x135 +#define SW8_CS25 0x136 +#define SW8_CS26 0x137 +#define SW8_CS27 0x138 +#define SW8_CS28 0x139 +#define SW8_CS29 0x13A +#define SW8_CS30 0x13B + +#define SW9_CS1 0x13C +#define SW9_CS2 0x13D +#define SW9_CS3 0x13E +#define SW9_CS4 0x13F +#define SW9_CS5 0x140 +#define SW9_CS6 0x141 +#define SW9_CS7 0x142 +#define SW9_CS8 0x143 +#define SW9_CS9 0x144 +#define SW9_CS10 0x145 +#define SW9_CS11 0x146 +#define SW9_CS12 0x147 +#define SW9_CS13 0x148 +#define SW9_CS14 0x149 +#define SW9_CS15 0x14A +#define SW9_CS16 0x14B +#define SW9_CS17 0x14C +#define SW9_CS18 0x14D +#define SW9_CS19 0x14E +#define SW9_CS20 0x14F +#define SW9_CS21 0x150 +#define SW9_CS22 0x151 +#define SW9_CS23 0x152 +#define SW9_CS24 0x153 +#define SW9_CS25 0x154 +#define SW9_CS26 0x155 +#define SW9_CS27 0x156 +#define SW9_CS28 0x157 +#define SW9_CS29 0x158 +#define SW9_CS30 0x159 + +#define SW1_CS31 0x15A +#define SW1_CS32 0x15B +#define SW1_CS33 0x15C +#define SW1_CS34 0x15D +#define SW1_CS35 0x15E +#define SW1_CS36 0x15F +#define SW1_CS37 0x160 +#define SW1_CS38 0x161 +#define SW1_CS39 0x162 + +#define SW2_CS31 0x163 +#define SW2_CS32 0x164 +#define SW2_CS33 0x165 +#define SW2_CS34 0x166 +#define SW2_CS35 0x167 +#define SW2_CS36 0x168 +#define SW2_CS37 0x169 +#define SW2_CS38 0x16A +#define SW2_CS39 0x16B + +#define SW3_CS31 0x16C +#define SW3_CS32 0x16D +#define SW3_CS33 0x16E +#define SW3_CS34 0x16F +#define SW3_CS35 0x170 +#define SW3_CS36 0x171 +#define SW3_CS37 0x172 +#define SW3_CS38 0x173 +#define SW3_CS39 0x174 + +#define SW4_CS31 0x175 +#define SW4_CS32 0x176 +#define SW4_CS33 0x177 +#define SW4_CS34 0x178 +#define SW4_CS35 0x179 +#define SW4_CS36 0x17A +#define SW4_CS37 0x17B +#define SW4_CS38 0x17C +#define SW4_CS39 0x17D + +#define SW5_CS31 0x17E +#define SW5_CS32 0x17F +#define SW5_CS33 0x180 +#define SW5_CS34 0x181 +#define SW5_CS35 0x182 +#define SW5_CS36 0x183 +#define SW5_CS37 0x184 +#define SW5_CS38 0x185 +#define SW5_CS39 0x186 + +#define SW6_CS31 0x187 +#define SW6_CS32 0x188 +#define SW6_CS33 0x189 +#define SW6_CS34 0x18A +#define SW6_CS35 0x18B +#define SW6_CS36 0x18C +#define SW6_CS37 0x18D +#define SW6_CS38 0x18E +#define SW6_CS39 0x18F + +#define SW7_CS31 0x190 +#define SW7_CS32 0x191 +#define SW7_CS33 0x192 +#define SW7_CS34 0x193 +#define SW7_CS35 0x194 +#define SW7_CS36 0x195 +#define SW7_CS37 0x196 +#define SW7_CS38 0x197 +#define SW7_CS39 0x198 + +#define SW8_CS31 0x199 +#define SW8_CS32 0x19A +#define SW8_CS33 0x19B +#define SW8_CS34 0x19C +#define SW8_CS35 0x19D +#define SW8_CS36 0x19E +#define SW8_CS37 0x19F +#define SW8_CS38 0x1A0 +#define SW8_CS39 0x1A1 + +#define SW9_CS31 0x1A2 +#define SW9_CS32 0x1A3 +#define SW9_CS33 0x1A4 +#define SW9_CS34 0x1A5 +#define SW9_CS35 0x1A6 +#define SW9_CS36 0x1A7 +#define SW9_CS37 0x1A8 +#define SW9_CS38 0x1A9 +#define SW9_CS39 0x1AA + +// DEPRECATED - DO NOT USE + +#define CS1_SW1 SW1_CS1 +#define CS2_SW1 SW1_CS2 +#define CS3_SW1 SW1_CS3 +#define CS4_SW1 SW1_CS4 +#define CS5_SW1 SW1_CS5 +#define CS6_SW1 SW1_CS6 +#define CS7_SW1 SW1_CS7 +#define CS8_SW1 SW1_CS8 +#define CS9_SW1 SW1_CS9 +#define CS10_SW1 SW1_CS10 +#define CS11_SW1 SW1_CS11 +#define CS12_SW1 SW1_CS12 +#define CS13_SW1 SW1_CS13 +#define CS14_SW1 SW1_CS14 +#define CS15_SW1 SW1_CS15 +#define CS16_SW1 SW1_CS16 +#define CS17_SW1 SW1_CS17 +#define CS18_SW1 SW1_CS18 +#define CS19_SW1 SW1_CS19 +#define CS20_SW1 SW1_CS20 +#define CS21_SW1 SW1_CS21 +#define CS22_SW1 SW1_CS22 +#define CS23_SW1 SW1_CS23 +#define CS24_SW1 SW1_CS24 +#define CS25_SW1 SW1_CS25 +#define CS26_SW1 SW1_CS26 +#define CS27_SW1 SW1_CS27 +#define CS28_SW1 SW1_CS28 +#define CS29_SW1 SW1_CS29 +#define CS30_SW1 SW1_CS30 + +#define CS1_SW2 SW2_CS1 +#define CS2_SW2 SW2_CS2 +#define CS3_SW2 SW2_CS3 +#define CS4_SW2 SW2_CS4 +#define CS5_SW2 SW2_CS5 +#define CS6_SW2 SW2_CS6 +#define CS7_SW2 SW2_CS7 +#define CS8_SW2 SW2_CS8 +#define CS9_SW2 SW2_CS9 +#define CS10_SW2 SW2_CS10 +#define CS11_SW2 SW2_CS11 +#define CS12_SW2 SW2_CS12 +#define CS13_SW2 SW2_CS13 +#define CS14_SW2 SW2_CS14 +#define CS15_SW2 SW2_CS15 +#define CS16_SW2 SW2_CS16 +#define CS17_SW2 SW2_CS17 +#define CS18_SW2 SW2_CS18 +#define CS19_SW2 SW2_CS19 +#define CS20_SW2 SW2_CS20 +#define CS21_SW2 SW2_CS21 +#define CS22_SW2 SW2_CS22 +#define CS23_SW2 SW2_CS23 +#define CS24_SW2 SW2_CS24 +#define CS25_SW2 SW2_CS25 +#define CS26_SW2 SW2_CS26 +#define CS27_SW2 SW2_CS27 +#define CS28_SW2 SW2_CS28 +#define CS29_SW2 SW2_CS29 +#define CS30_SW2 SW2_CS30 + +#define CS1_SW3 SW3_CS1 +#define CS2_SW3 SW3_CS2 +#define CS3_SW3 SW3_CS3 +#define CS4_SW3 SW3_CS4 +#define CS5_SW3 SW3_CS5 +#define CS6_SW3 SW3_CS6 +#define CS7_SW3 SW3_CS7 +#define CS8_SW3 SW3_CS8 +#define CS9_SW3 SW3_CS9 +#define CS10_SW3 SW3_CS10 +#define CS11_SW3 SW3_CS11 +#define CS12_SW3 SW3_CS12 +#define CS13_SW3 SW3_CS13 +#define CS14_SW3 SW3_CS14 +#define CS15_SW3 SW3_CS15 +#define CS16_SW3 SW3_CS16 +#define CS17_SW3 SW3_CS17 +#define CS18_SW3 SW3_CS18 +#define CS19_SW3 SW3_CS19 +#define CS20_SW3 SW3_CS20 +#define CS21_SW3 SW3_CS21 +#define CS22_SW3 SW3_CS22 +#define CS23_SW3 SW3_CS23 +#define CS24_SW3 SW3_CS24 +#define CS25_SW3 SW3_CS25 +#define CS26_SW3 SW3_CS26 +#define CS27_SW3 SW3_CS27 +#define CS28_SW3 SW3_CS28 +#define CS29_SW3 SW3_CS29 +#define CS30_SW3 SW3_CS30 + +#define CS1_SW4 SW4_CS1 +#define CS2_SW4 SW4_CS2 +#define CS3_SW4 SW4_CS3 +#define CS4_SW4 SW4_CS4 +#define CS5_SW4 SW4_CS5 +#define CS6_SW4 SW4_CS6 +#define CS7_SW4 SW4_CS7 +#define CS8_SW4 SW4_CS8 +#define CS9_SW4 SW4_CS9 +#define CS10_SW4 SW4_CS10 +#define CS11_SW4 SW4_CS11 +#define CS12_SW4 SW4_CS12 +#define CS13_SW4 SW4_CS13 +#define CS14_SW4 SW4_CS14 +#define CS15_SW4 SW4_CS15 +#define CS16_SW4 SW4_CS16 +#define CS17_SW4 SW4_CS17 +#define CS18_SW4 SW4_CS18 +#define CS19_SW4 SW4_CS19 +#define CS20_SW4 SW4_CS20 +#define CS21_SW4 SW4_CS21 +#define CS22_SW4 SW4_CS22 +#define CS23_SW4 SW4_CS23 +#define CS24_SW4 SW4_CS24 +#define CS25_SW4 SW4_CS25 +#define CS26_SW4 SW4_CS26 +#define CS27_SW4 SW4_CS27 +#define CS28_SW4 SW4_CS28 +#define CS29_SW4 SW4_CS29 +#define CS30_SW4 SW4_CS30 + +#define CS1_SW5 SW5_CS1 +#define CS2_SW5 SW5_CS2 +#define CS3_SW5 SW5_CS3 +#define CS4_SW5 SW5_CS4 +#define CS5_SW5 SW5_CS5 +#define CS6_SW5 SW5_CS6 +#define CS7_SW5 SW5_CS7 +#define CS8_SW5 SW5_CS8 +#define CS9_SW5 SW5_CS9 +#define CS10_SW5 SW5_CS10 +#define CS11_SW5 SW5_CS11 +#define CS12_SW5 SW5_CS12 +#define CS13_SW5 SW5_CS13 +#define CS14_SW5 SW5_CS14 +#define CS15_SW5 SW5_CS15 +#define CS16_SW5 SW5_CS16 +#define CS17_SW5 SW5_CS17 +#define CS18_SW5 SW5_CS18 +#define CS19_SW5 SW5_CS19 +#define CS20_SW5 SW5_CS20 +#define CS21_SW5 SW5_CS21 +#define CS22_SW5 SW5_CS22 +#define CS23_SW5 SW5_CS23 +#define CS24_SW5 SW5_CS24 +#define CS25_SW5 SW5_CS25 +#define CS26_SW5 SW5_CS26 +#define CS27_SW5 SW5_CS27 +#define CS28_SW5 SW5_CS28 +#define CS29_SW5 SW5_CS29 +#define CS30_SW5 SW5_CS30 + +#define CS1_SW6 SW6_CS1 +#define CS2_SW6 SW6_CS2 +#define CS3_SW6 SW6_CS3 +#define CS4_SW6 SW6_CS4 +#define CS5_SW6 SW6_CS5 +#define CS6_SW6 SW6_CS6 +#define CS7_SW6 SW6_CS7 +#define CS8_SW6 SW6_CS8 +#define CS9_SW6 SW6_CS9 +#define CS10_SW6 SW6_CS10 +#define CS11_SW6 SW6_CS11 +#define CS12_SW6 SW6_CS12 +#define CS13_SW6 SW6_CS13 +#define CS14_SW6 SW6_CS14 +#define CS15_SW6 SW6_CS15 +#define CS16_SW6 SW6_CS16 +#define CS17_SW6 SW6_CS17 +#define CS18_SW6 SW6_CS18 +#define CS19_SW6 SW6_CS19 +#define CS20_SW6 SW6_CS20 +#define CS21_SW6 SW6_CS21 +#define CS22_SW6 SW6_CS22 +#define CS23_SW6 SW6_CS23 +#define CS24_SW6 SW6_CS24 +#define CS25_SW6 SW6_CS25 +#define CS26_SW6 SW6_CS26 +#define CS27_SW6 SW6_CS27 +#define CS28_SW6 SW6_CS28 +#define CS29_SW6 SW6_CS29 +#define CS30_SW6 SW6_CS30 + +#define CS1_SW7 SW7_CS1 +#define CS2_SW7 SW7_CS2 +#define CS3_SW7 SW7_CS3 +#define CS4_SW7 SW7_CS4 +#define CS5_SW7 SW7_CS5 +#define CS6_SW7 SW7_CS6 +#define CS7_SW7 SW7_CS7 +#define CS8_SW7 SW7_CS8 +#define CS9_SW7 SW7_CS9 +#define CS10_SW7 SW7_CS10 +#define CS11_SW7 SW7_CS11 +#define CS12_SW7 SW7_CS12 +#define CS13_SW7 SW7_CS13 +#define CS14_SW7 SW7_CS14 +#define CS15_SW7 SW7_CS15 +#define CS16_SW7 SW7_CS16 +#define CS17_SW7 SW7_CS17 +#define CS18_SW7 SW7_CS18 +#define CS19_SW7 SW7_CS19 +#define CS20_SW7 SW7_CS20 +#define CS21_SW7 SW7_CS21 +#define CS22_SW7 SW7_CS22 +#define CS23_SW7 SW7_CS23 +#define CS24_SW7 SW7_CS24 +#define CS25_SW7 SW7_CS25 +#define CS26_SW7 SW7_CS26 +#define CS27_SW7 SW7_CS27 +#define CS28_SW7 SW7_CS28 +#define CS29_SW7 SW7_CS29 +#define CS30_SW7 SW7_CS30 + +#define CS1_SW8 SW8_CS1 +#define CS2_SW8 SW8_CS2 +#define CS3_SW8 SW8_CS3 +#define CS4_SW8 SW8_CS4 +#define CS5_SW8 SW8_CS5 +#define CS6_SW8 SW8_CS6 +#define CS7_SW8 SW8_CS7 +#define CS8_SW8 SW8_CS8 +#define CS9_SW8 SW8_CS9 +#define CS10_SW8 SW8_CS10 +#define CS11_SW8 SW8_CS11 +#define CS12_SW8 SW8_CS12 +#define CS13_SW8 SW8_CS13 +#define CS14_SW8 SW8_CS14 +#define CS15_SW8 SW8_CS15 +#define CS16_SW8 SW8_CS16 +#define CS17_SW8 SW8_CS17 +#define CS18_SW8 SW8_CS18 +#define CS19_SW8 SW8_CS19 +#define CS20_SW8 SW8_CS20 +#define CS21_SW8 SW8_CS21 +#define CS22_SW8 SW8_CS22 +#define CS23_SW8 SW8_CS23 +#define CS24_SW8 SW8_CS24 +#define CS25_SW8 SW8_CS25 +#define CS26_SW8 SW8_CS26 +#define CS27_SW8 SW8_CS27 +#define CS28_SW8 SW8_CS28 +#define CS29_SW8 SW8_CS29 +#define CS30_SW8 SW8_CS30 + +#define CS1_SW9 SW9_CS1 +#define CS2_SW9 SW9_CS2 +#define CS3_SW9 SW9_CS3 +#define CS4_SW9 SW9_CS4 +#define CS5_SW9 SW9_CS5 +#define CS6_SW9 SW9_CS6 +#define CS7_SW9 SW9_CS7 +#define CS8_SW9 SW9_CS8 +#define CS9_SW9 SW9_CS9 +#define CS10_SW9 SW9_CS10 +#define CS11_SW9 SW9_CS11 +#define CS12_SW9 SW9_CS12 +#define CS13_SW9 SW9_CS13 +#define CS14_SW9 SW9_CS14 +#define CS15_SW9 SW9_CS15 +#define CS16_SW9 SW9_CS16 +#define CS17_SW9 SW9_CS17 +#define CS18_SW9 SW9_CS18 +#define CS19_SW9 SW9_CS19 +#define CS20_SW9 SW9_CS20 +#define CS21_SW9 SW9_CS21 +#define CS22_SW9 SW9_CS22 +#define CS23_SW9 SW9_CS23 +#define CS24_SW9 SW9_CS24 +#define CS25_SW9 SW9_CS25 +#define CS26_SW9 SW9_CS26 +#define CS27_SW9 SW9_CS27 +#define CS28_SW9 SW9_CS28 +#define CS29_SW9 SW9_CS29 +#define CS30_SW9 SW9_CS30 + +#define CS31_SW1 SW1_CS31 +#define CS32_SW1 SW1_CS32 +#define CS33_SW1 SW1_CS33 +#define CS34_SW1 SW1_CS34 +#define CS35_SW1 SW1_CS35 +#define CS36_SW1 SW1_CS36 +#define CS37_SW1 SW1_CS37 +#define CS38_SW1 SW1_CS38 +#define CS39_SW1 SW1_CS39 + +#define CS31_SW2 SW2_CS31 +#define CS32_SW2 SW2_CS32 +#define CS33_SW2 SW2_CS33 +#define CS34_SW2 SW2_CS34 +#define CS35_SW2 SW2_CS35 +#define CS36_SW2 SW2_CS36 +#define CS37_SW2 SW2_CS37 +#define CS38_SW2 SW2_CS38 +#define CS39_SW2 SW2_CS39 + +#define CS31_SW3 SW3_CS31 +#define CS32_SW3 SW3_CS32 +#define CS33_SW3 SW3_CS33 +#define CS34_SW3 SW3_CS34 +#define CS35_SW3 SW3_CS35 +#define CS36_SW3 SW3_CS36 +#define CS37_SW3 SW3_CS37 +#define CS38_SW3 SW3_CS38 +#define CS39_SW3 SW3_CS39 + +#define CS31_SW4 SW4_CS31 +#define CS32_SW4 SW4_CS32 +#define CS33_SW4 SW4_CS33 +#define CS34_SW4 SW4_CS34 +#define CS35_SW4 SW4_CS35 +#define CS36_SW4 SW4_CS36 +#define CS37_SW4 SW4_CS37 +#define CS38_SW4 SW4_CS38 +#define CS39_SW4 SW4_CS39 + +#define CS31_SW5 SW5_CS31 +#define CS32_SW5 SW5_CS32 +#define CS33_SW5 SW5_CS33 +#define CS34_SW5 SW5_CS34 +#define CS35_SW5 SW5_CS35 +#define CS36_SW5 SW5_CS36 +#define CS37_SW5 SW5_CS37 +#define CS38_SW5 SW5_CS38 +#define CS39_SW5 SW5_CS39 + +#define CS31_SW6 SW6_CS31 +#define CS32_SW6 SW6_CS32 +#define CS33_SW6 SW6_CS33 +#define CS34_SW6 SW6_CS34 +#define CS35_SW6 SW6_CS35 +#define CS36_SW6 SW6_CS36 +#define CS37_SW6 SW6_CS37 +#define CS38_SW6 SW6_CS38 +#define CS39_SW6 SW6_CS39 + +#define CS31_SW7 SW7_CS31 +#define CS32_SW7 SW7_CS32 +#define CS33_SW7 SW7_CS33 +#define CS34_SW7 SW7_CS34 +#define CS35_SW7 SW7_CS35 +#define CS36_SW7 SW7_CS36 +#define CS37_SW7 SW7_CS37 +#define CS38_SW7 SW7_CS38 +#define CS39_SW7 SW7_CS39 + +#define CS31_SW8 SW8_CS31 +#define CS32_SW8 SW8_CS32 +#define CS33_SW8 SW8_CS33 +#define CS34_SW8 SW8_CS34 +#define CS35_SW8 SW8_CS35 +#define CS36_SW8 SW8_CS36 +#define CS37_SW8 SW8_CS37 +#define CS38_SW8 SW8_CS38 +#define CS39_SW8 SW8_CS39 + +#define CS31_SW9 SW9_CS31 +#define CS32_SW9 SW9_CS32 +#define CS33_SW9 SW9_CS33 +#define CS34_SW9 SW9_CS34 +#define CS35_SW9 SW9_CS35 +#define CS36_SW9 SW9_CS36 +#define CS37_SW9 SW9_CS37 +#define CS38_SW9 SW9_CS38 +#define CS39_SW9 SW9_CS39 diff --git a/drivers/led/issi/is31fl3741-simple.c b/drivers/led/issi/is31fl3741-simple.c deleted file mode 100644 index f7009853ba..0000000000 --- a/drivers/led/issi/is31fl3741-simple.c +++ /dev/null @@ -1,278 +0,0 @@ -/* Copyright 2017 Jason Williams - * Copyright 2018 Jack Humbert - * Copyright 2018 Yiancar - * Copyright 2020 MelGeek - * - * 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 2 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/>. - */ - -#include "is31fl3741-simple.h" -#include <string.h> -#include "i2c_master.h" -#include "wait.h" - -#define IS31FL3741_PWM_REGISTER_COUNT 351 - -#ifndef IS31FL3741_I2C_TIMEOUT -# define IS31FL3741_I2C_TIMEOUT 100 -#endif - -#ifndef IS31FL3741_I2C_PERSISTENCE -# define IS31FL3741_I2C_PERSISTENCE 0 -#endif - -#ifndef IS31FL3741_CONFIGURATION -# define IS31FL3741_CONFIGURATION 0x01 -#endif - -#ifndef IS31FL3741_PWM_FREQUENCY -# define IS31FL3741_PWM_FREQUENCY IS31FL3741_PWM_FREQUENCY_29K_HZ -#endif - -#ifndef IS31FL3741_SW_PULLUP -# define IS31FL3741_SW_PULLUP IS31FL3741_PUR_32K_OHM -#endif - -#ifndef IS31FL3741_CS_PULLDOWN -# define IS31FL3741_CS_PULLDOWN IS31FL3741_PDR_32K_OHM -#endif - -#ifndef IS31FL3741_GLOBAL_CURRENT -# define IS31FL3741_GLOBAL_CURRENT 0xFF -#endif - -// Transfer buffer for TWITransmitData() -uint8_t g_twi_transfer_buffer[20] = {0xFF}; - -// These buffers match the IS31FL3741 and IS31FL3741A PWM registers. -// The scaling buffers match the PG2 and PG3 LED On/Off registers. -// Storing them like this is optimal for I2C transfers to the registers. -// We could optimize this and take out the unused registers from these -// buffers and the transfers in is31fl3741_write_pwm_buffer() but it's -// probably not worth the extra complexity. -uint8_t g_pwm_buffer[IS31FL3741_DRIVER_COUNT][IS31FL3741_PWM_REGISTER_COUNT]; -bool g_pwm_buffer_update_required[IS31FL3741_DRIVER_COUNT] = {false}; -bool g_scaling_registers_update_required[IS31FL3741_DRIVER_COUNT] = {false}; - -uint8_t g_scaling_registers[IS31FL3741_DRIVER_COUNT][IS31FL3741_PWM_REGISTER_COUNT]; - -void is31fl3741_write_register(uint8_t addr, uint8_t reg, uint8_t data) { - g_twi_transfer_buffer[0] = reg; - g_twi_transfer_buffer[1] = data; - -#if IS31FL3741_I2C_PERSISTENCE > 0 - for (uint8_t i = 0; i < IS31FL3741_I2C_PERSISTENCE; i++) { - if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, IS31FL3741_I2C_TIMEOUT) == 0) break; - } -#else - i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, IS31FL3741_I2C_TIMEOUT); -#endif -} - -bool is31fl3741_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer) { - // Assume PG0 is already selected - - for (int i = 0; i < 342; i += 18) { - if (i == 180) { - // unlock the command register and select PG1 - is31fl3741_write_register(addr, IS31FL3741_REG_COMMAND_WRITE_LOCK, IS31FL3741_COMMAND_WRITE_LOCK_MAGIC); - is31fl3741_write_register(addr, IS31FL3741_REG_COMMAND, IS31FL3741_COMMAND_PWM_1); - } - - g_twi_transfer_buffer[0] = i % 180; - memcpy(g_twi_transfer_buffer + 1, pwm_buffer + i, 18); - -#if IS31FL3741_I2C_PERSISTENCE > 0 - for (uint8_t i = 0; i < IS31FL3741_I2C_PERSISTENCE; i++) { - if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 19, IS31FL3741_I2C_TIMEOUT) != 0) { - return false; - } - } -#else - if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 19, IS31FL3741_I2C_TIMEOUT) != 0) { - return false; - } -#endif - } - - // transfer the left cause the total number is 351 - g_twi_transfer_buffer[0] = 162; - memcpy(g_twi_transfer_buffer + 1, pwm_buffer + 342, 9); - -#if IS31FL3741_I2C_PERSISTENCE > 0 - for (uint8_t i = 0; i < IS31FL3741_I2C_PERSISTENCE; i++) { - if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 10, IS31FL3741_I2C_TIMEOUT) != 0) { - return false; - } - } -#else - if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 10, IS31FL3741_I2C_TIMEOUT) != 0) { - return false; - } -#endif - - return true; -} - -void is31fl3741_init_drivers(void) { - i2c_init(); - - is31fl3741_init(IS31FL3741_I2C_ADDRESS_1); -#if defined(IS31FL3741_I2C_ADDRESS_2) - is31fl3741_init(IS31FL3741_I2C_ADDRESS_2); -# if defined(IS31FL3741_I2C_ADDRESS_3) - is31fl3741_init(IS31FL3741_I2C_ADDRESS_3); -# if defined(IS31FL3741_I2C_ADDRESS_4) - is31fl3741_init(IS31FL3741_I2C_ADDRESS_4); -# endif -# endif -#endif - - for (int i = 0; i < IS31FL3741_LED_COUNT; i++) { - is31fl3741_set_led_control_register(i, true); - } - - is31fl3741_update_led_control_registers(IS31FL3741_I2C_ADDRESS_1, 0); -#if defined(IS31FL3741_I2C_ADDRESS_2) - is31fl3741_update_led_control_registers(IS31FL3741_I2C_ADDRESS_2, 1); -# if defined(IS31FL3741_I2C_ADDRESS_3) - is31fl3741_update_led_control_registers(IS31FL3741_I2C_ADDRESS_3, 2); -# if defined(IS31FL3741_I2C_ADDRESS_4) - is31fl3741_update_led_control_registers(IS31FL3741_I2C_ADDRESS_4, 3); -# endif -# endif -#endif -} - -void is31fl3741_init(uint8_t addr) { - // In order to avoid the LEDs being driven with garbage data - // in the LED driver's PWM registers, shutdown is enabled last. - // Set up the mode and other settings, clear the PWM registers, - // then disable software shutdown. - // Unlock the command register. - - // Unlock the command register. - is31fl3741_write_register(addr, IS31FL3741_REG_COMMAND_WRITE_LOCK, IS31FL3741_COMMAND_WRITE_LOCK_MAGIC); - - // Select PG4 - is31fl3741_write_register(addr, IS31FL3741_REG_COMMAND, IS31FL3741_COMMAND_FUNCTION); - - // Set to Normal operation - is31fl3741_write_register(addr, IS31FL3741_FUNCTION_REG_CONFIGURATION, IS31FL3741_CONFIGURATION); - - // Set Golbal Current Control Register - is31fl3741_write_register(addr, IS31FL3741_FUNCTION_REG_GLOBAL_CURRENT, IS31FL3741_GLOBAL_CURRENT); - // Set Pull up & Down for SWx CSy - is31fl3741_write_register(addr, IS31FL3741_FUNCTION_REG_PULLDOWNUP, ((IS31FL3741_CS_PULLDOWN << 4) | IS31FL3741_SW_PULLUP)); - // Set PWM frequency - is31fl3741_write_register(addr, IS31FL3741_FUNCTION_REG_PWM_FREQUENCY, (IS31FL3741_PWM_FREQUENCY & 0b1111)); - - // is31fl3741_update_led_scaling_registers(addr, 0xFF, 0xFF, 0xFF); - - // Wait 10ms to ensure the device has woken up. - wait_ms(10); -} - -void is31fl3741_set_value(int index, uint8_t value) { - is31fl3741_led_t led; - if (index >= 0 && index < IS31FL3741_LED_COUNT) { - memcpy_P(&led, (&g_is31fl3741_leds[index]), sizeof(led)); - - if (g_pwm_buffer[led.driver][led.v] == value) { - return; - } - g_pwm_buffer_update_required[led.driver] = true; - g_pwm_buffer[led.driver][led.v] = value; - } -} - -void is31fl3741_set_value_all(uint8_t value) { - for (int i = 0; i < IS31FL3741_LED_COUNT; i++) { - is31fl3741_set_value(i, value); - } -} - -void is31fl3741_set_led_control_register(uint8_t index, bool value) { - is31fl3741_led_t led; - memcpy_P(&led, (&g_is31fl3741_leds[index]), sizeof(led)); - - if (value) { - g_scaling_registers[led.driver][led.v] = 0xFF; - } else { - g_scaling_registers[led.driver][led.v] = 0x00; - } - - g_scaling_registers_update_required[led.driver] = true; -} - -void is31fl3741_update_pwm_buffers(uint8_t addr, uint8_t index) { - if (g_pwm_buffer_update_required[index]) { - // unlock the command register and select PG2 - is31fl3741_write_register(addr, IS31FL3741_REG_COMMAND_WRITE_LOCK, IS31FL3741_COMMAND_WRITE_LOCK_MAGIC); - is31fl3741_write_register(addr, IS31FL3741_REG_COMMAND, IS31FL3741_COMMAND_PWM_0); - - is31fl3741_write_pwm_buffer(addr, g_pwm_buffer[index]); - } - - g_pwm_buffer_update_required[index] = false; -} - -void is31fl3741_set_pwm_buffer(const is31fl3741_led_t *pled, uint8_t value) { - g_pwm_buffer[pled->driver][pled->v] = value; - - g_pwm_buffer_update_required[pled->driver] = true; -} - -void is31fl3741_update_led_control_registers(uint8_t addr, uint8_t index) { - if (g_scaling_registers_update_required[index]) { - // unlock the command register and select PG2 - is31fl3741_write_register(addr, IS31FL3741_REG_COMMAND_WRITE_LOCK, IS31FL3741_COMMAND_WRITE_LOCK_MAGIC); - is31fl3741_write_register(addr, IS31FL3741_REG_COMMAND, IS31FL3741_COMMAND_SCALING_0); - - // CS1_SW1 to CS30_SW6 are on PG2 - for (int i = CS1_SW1; i <= CS30_SW6; ++i) { - is31fl3741_write_register(addr, i, g_scaling_registers[index][i]); - } - - // unlock the command register and select PG3 - is31fl3741_write_register(addr, IS31FL3741_REG_COMMAND_WRITE_LOCK, IS31FL3741_COMMAND_WRITE_LOCK_MAGIC); - is31fl3741_write_register(addr, IS31FL3741_REG_COMMAND, IS31FL3741_COMMAND_SCALING_1); - - // CS1_SW7 to CS39_SW9 are on PG3 - for (int i = CS1_SW7; i <= CS39_SW9; ++i) { - is31fl3741_write_register(addr, i - CS1_SW7, g_scaling_registers[index][i]); - } - - g_scaling_registers_update_required[index] = false; - } -} - -void is31fl3741_set_scaling_registers(const is31fl3741_led_t *pled, uint8_t value) { - g_scaling_registers[pled->driver][pled->v] = value; - - g_scaling_registers_update_required[pled->driver] = true; -} - -void is31fl3741_flush(void) { - is31fl3741_update_pwm_buffers(IS31FL3741_I2C_ADDRESS_1, 0); -#if defined(IS31FL3741_I2C_ADDRESS_2) - is31fl3741_update_pwm_buffers(IS31FL3741_I2C_ADDRESS_2, 1); -# if defined(IS31FL3741_I2C_ADDRESS_3) - is31fl3741_update_pwm_buffers(IS31FL3741_I2C_ADDRESS_3, 2); -# if defined(IS31FL3741_I2C_ADDRESS_4) - is31fl3741_update_pwm_buffers(IS31FL3741_I2C_ADDRESS_4, 3); -# endif -# endif -#endif -} diff --git a/drivers/led/issi/is31fl3741-simple.h b/drivers/led/issi/is31fl3741-simple.h deleted file mode 100644 index 34608a37e0..0000000000 --- a/drivers/led/issi/is31fl3741-simple.h +++ /dev/null @@ -1,516 +0,0 @@ -/* Copyright 2017 Jason Williams - * Copyright 2018 Jack Humbert - * Copyright 2018 Yiancar - * Copyright 2020 MelGeek - * - * 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 2 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/>. - */ - -#pragma once - -#include <stdint.h> -#include <stdbool.h> -#include "progmem.h" -#include "util.h" - -// ======== DEPRECATED DEFINES - DO NOT USE ======== -#ifdef ISSI_TIMEOUT -# define IS31FL3741_I2C_TIMEOUT ISSI_TIMEOUT -#endif -#ifdef ISSI_PERSISTENCE -# define IS31FL3741_I2C_PERSISTENCE ISSI_PERSISTENCE -#endif -#ifdef ISSI_CONFIGURATION -# define IS31FL3741_CONFIGURATION ISSI_CONFIGURATION -#endif -#ifdef ISSI_SWPULLUP -# define IS31FL3741_SW_PULLUP ISSI_SWPULLUP -#endif -#ifdef ISSI_CSPULLUP -# define IS31FL3741_CS_PULLDOWN ISSI_CSPULLUP -#endif -#ifdef ISSI_GLOBALCURRENT -# define IS31FL3741_GLOBAL_CURRENT ISSI_GLOBALCURRENT -#endif - -#define PUR_0R IS31FL3741_PUR_0_OHM -#define PUR_05KR IS31FL3741_PUR_0K5_OHM -#define PUR_1KR IS31FL3741_PUR_1K_OHM -#define PUR_2KR IS31FL3741_PUR_2K_OHM -#define PUR_4KR IS31FL3741_PUR_4K_OHM -#define PUR_8KR IS31FL3741_PUR_8K_OHM -#define PUR_16KR IS31FL3741_PUR_16K_OHM -#define PUR_32KR IS31FL3741_PUR_32K_OHM -// ======== - -#define IS31FL3741_REG_INTERRUPT_MASK 0xF0 -#define IS31FL3741_REG_INTERRUPT_STATUS 0xF1 -#define IS31FL3741_REG_ID 0xFC - -#define IS31FL3741_REG_COMMAND 0xFD - -#define IS31FL3741_COMMAND_PWM_0 0x00 -#define IS31FL3741_COMMAND_PWM_1 0x01 -#define IS31FL3741_COMMAND_SCALING_0 0x02 -#define IS31FL3741_COMMAND_SCALING_1 0x03 -#define IS31FL3741_COMMAND_FUNCTION 0x04 - -#define IS31FL3741_FUNCTION_REG_CONFIGURATION 0x00 -#define IS31FL3741_FUNCTION_REG_GLOBAL_CURRENT 0x01 -#define IS31FL3741_FUNCTION_REG_PULLDOWNUP 0x02 -#define IS31FL3741_FUNCTION_REG_PWM_FREQUENCY 0x36 -#define IS31FL3741_FUNCTION_REG_RESET 0x3F - -#define IS31FL3741_REG_COMMAND_WRITE_LOCK 0xFE -#define IS31FL3741_COMMAND_WRITE_LOCK_MAGIC 0xC5 - -#define IS31FL3741_I2C_ADDRESS_GND 0x30 -#define IS31FL3741_I2C_ADDRESS_SCL 0x31 -#define IS31FL3741_I2C_ADDRESS_SDA 0x32 -#define IS31FL3741_I2C_ADDRESS_VCC 0x33 - -#if defined(LED_MATRIX_IS31FL3741) -# define IS31FL3741_LED_COUNT LED_MATRIX_LED_COUNT -#endif - -#if defined(IS31FL3741_I2C_ADDRESS_4) -# define IS31FL3741_DRIVER_COUNT 4 -#elif defined(IS31FL3741_I2C_ADDRESS_3) -# define IS31FL3741_DRIVER_COUNT 3 -#elif defined(IS31FL3741_I2C_ADDRESS_2) -# define IS31FL3741_DRIVER_COUNT 2 -#elif defined(IS31FL3741_I2C_ADDRESS_1) -# define IS31FL3741_DRIVER_COUNT 1 -#endif - -typedef struct is31fl3741_led_t { - uint8_t driver : 2; - uint16_t v : 9; -} PACKED is31fl3741_led_t; - -extern const is31fl3741_led_t PROGMEM g_is31fl3741_leds[IS31FL3741_LED_COUNT]; - -void is31fl3741_init_drivers(void); -void is31fl3741_init(uint8_t addr); -void is31fl3741_write_register(uint8_t addr, uint8_t reg, uint8_t data); -bool is31fl3741_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer); - -void is31fl3741_set_value(int index, uint8_t value); -void is31fl3741_set_value_all(uint8_t value); - -void is31fl3741_set_led_control_register(uint8_t index, bool value); - -// This should not be called from an interrupt -// (eg. from a timer interrupt). -// Call this while idle (in between matrix scans). -// If the buffer is dirty, it will update the driver with the buffer. -void is31fl3741_update_pwm_buffers(uint8_t addr, uint8_t index); -void is31fl3741_update_led_control_registers(uint8_t addr, uint8_t index); -void is31fl3741_set_scaling_registers(const is31fl3741_led_t *pled, uint8_t value); - -void is31fl3741_set_pwm_buffer(const is31fl3741_led *pled, uint8_t value); - -void is31fl3741_flush(void); - -#define IS31FL3741_PDR_0_OHM 0b000 // No pull-down resistor -#define IS31FL3741_PDR_0K5_OHM 0b001 // 0.5 kOhm resistor -#define IS31FL3741_PDR_1K_OHM 0b010 // 1 kOhm resistor -#define IS31FL3741_PDR_2K_OHM 0b011 // 2 kOhm resistor -#define IS31FL3741_PDR_4K_OHM 0b100 // 4 kOhm resistor -#define IS31FL3741_PDR_8K_OHM 0b101 // 8 kOhm resistor -#define IS31FL3741_PDR_16K_OHM 0b110 // 16 kOhm resistor -#define IS31FL3741_PDR_32K_OHM 0b111 // 32 kOhm resistor - -#define IS31FL3741_PUR_0_OHM 0b000 // No pull-up resistor -#define IS31FL3741_PUR_0K5_OHM 0b001 // 0.5 kOhm resistor -#define IS31FL3741_PUR_1K_OHM 0b010 // 1 kOhm resistor -#define IS31FL3741_PUR_2K_OHM 0b011 // 2 kOhm resistor -#define IS31FL3741_PUR_4K_OHM 0b100 // 4 kOhm resistor -#define IS31FL3741_PUR_8K_OHM 0b101 // 8 kOhm resistor -#define IS31FL3741_PUR_16K_OHM 0b110 // 16 kOhm resistor -#define IS31FL3741_PUR_32K_OHM 0b111 // 32 kOhm resistor - -#define IS31FL3741_PWM_FREQUENCY_29K_HZ 0b0000 -#define IS31FL3741_PWM_FREQUENCY_3K6_HZ 0b0011 -#define IS31FL3741_PWM_FREQUENCY_1K8_HZ 0b0111 -#define IS31FL3741_PWM_FREQUENCY_900_HZ 0b1011 - -#define CS1_SW1 0x00 -#define CS2_SW1 0x01 -#define CS3_SW1 0x02 -#define CS4_SW1 0x03 -#define CS5_SW1 0x04 -#define CS6_SW1 0x05 -#define CS7_SW1 0x06 -#define CS8_SW1 0x07 -#define CS9_SW1 0x08 -#define CS10_SW1 0x09 -#define CS11_SW1 0x0A -#define CS12_SW1 0x0B -#define CS13_SW1 0x0C -#define CS14_SW1 0x0D -#define CS15_SW1 0x0E -#define CS16_SW1 0x0F -#define CS17_SW1 0x10 -#define CS18_SW1 0x11 -#define CS19_SW1 0x12 -#define CS20_SW1 0x13 -#define CS21_SW1 0x14 -#define CS22_SW1 0x15 -#define CS23_SW1 0x16 -#define CS24_SW1 0x17 -#define CS25_SW1 0x18 -#define CS26_SW1 0x19 -#define CS27_SW1 0x1A -#define CS28_SW1 0x1B -#define CS29_SW1 0x1C -#define CS30_SW1 0x1D - -#define CS1_SW2 0x1E -#define CS2_SW2 0x1F -#define CS3_SW2 0x20 -#define CS4_SW2 0x21 -#define CS5_SW2 0x22 -#define CS6_SW2 0x23 -#define CS7_SW2 0x24 -#define CS8_SW2 0x25 -#define CS9_SW2 0x26 -#define CS10_SW2 0x27 -#define CS11_SW2 0x28 -#define CS12_SW2 0x29 -#define CS13_SW2 0x2A -#define CS14_SW2 0x2B -#define CS15_SW2 0x2C -#define CS16_SW2 0x2D -#define CS17_SW2 0x2E -#define CS18_SW2 0x2F -#define CS19_SW2 0x30 -#define CS20_SW2 0x31 -#define CS21_SW2 0x32 -#define CS22_SW2 0x33 -#define CS23_SW2 0x34 -#define CS24_SW2 0x35 -#define CS25_SW2 0x36 -#define CS26_SW2 0x37 -#define CS27_SW2 0x38 -#define CS28_SW2 0x39 -#define CS29_SW2 0x3A -#define CS30_SW2 0x3B - -#define CS1_SW3 0x3C -#define CS2_SW3 0x3D -#define CS3_SW3 0x3E -#define CS4_SW3 0x3F -#define CS5_SW3 0x40 -#define CS6_SW3 0x41 -#define CS7_SW3 0x42 -#define CS8_SW3 0x43 -#define CS9_SW3 0x44 -#define CS10_SW3 0x45 -#define CS11_SW3 0x46 -#define CS12_SW3 0x47 -#define CS13_SW3 0x48 -#define CS14_SW3 0x49 -#define CS15_SW3 0x4A -#define CS16_SW3 0x4B -#define CS17_SW3 0x4C -#define CS18_SW3 0x4D -#define CS19_SW3 0x4E -#define CS20_SW3 0x4F -#define CS21_SW3 0x50 -#define CS22_SW3 0x51 -#define CS23_SW3 0x52 -#define CS24_SW3 0x53 -#define CS25_SW3 0x54 -#define CS26_SW3 0x55 -#define CS27_SW3 0x56 -#define CS28_SW3 0x57 -#define CS29_SW3 0x58 -#define CS30_SW3 0x59 - -#define CS1_SW4 0x5A -#define CS2_SW4 0x5B -#define CS3_SW4 0x5C -#define CS4_SW4 0x5D -#define CS5_SW4 0x5E -#define CS6_SW4 0x5F -#define CS7_SW4 0x60 -#define CS8_SW4 0x61 -#define CS9_SW4 0x62 -#define CS10_SW4 0x63 -#define CS11_SW4 0x64 -#define CS12_SW4 0x65 -#define CS13_SW4 0x66 -#define CS14_SW4 0x67 -#define CS15_SW4 0x68 -#define CS16_SW4 0x69 -#define CS17_SW4 0x6A -#define CS18_SW4 0x6B -#define CS19_SW4 0x6C -#define CS20_SW4 0x6D -#define CS21_SW4 0x6E -#define CS22_SW4 0x6F -#define CS23_SW4 0x70 -#define CS24_SW4 0x71 -#define CS25_SW4 0x72 -#define CS26_SW4 0x73 -#define CS27_SW4 0x74 -#define CS28_SW4 0x75 -#define CS29_SW4 0x76 -#define CS30_SW4 0x77 - -#define CS1_SW5 0x78 -#define CS2_SW5 0x79 -#define CS3_SW5 0x7A -#define CS4_SW5 0x7B -#define CS5_SW5 0x7C -#define CS6_SW5 0x7D -#define CS7_SW5 0x7E -#define CS8_SW5 0x7F -#define CS9_SW5 0x80 -#define CS10_SW5 0x81 -#define CS11_SW5 0x82 -#define CS12_SW5 0x83 -#define CS13_SW5 0x84 -#define CS14_SW5 0x85 -#define CS15_SW5 0x86 -#define CS16_SW5 0x87 -#define CS17_SW5 0x88 -#define CS18_SW5 0x89 -#define CS19_SW5 0x8A -#define CS20_SW5 0x8B -#define CS21_SW5 0x8C -#define CS22_SW5 0x8D -#define CS23_SW5 0x8E -#define CS24_SW5 0x8F -#define CS25_SW5 0x90 -#define CS26_SW5 0x91 -#define CS27_SW5 0x92 -#define CS28_SW5 0x93 -#define CS29_SW5 0x94 -#define CS30_SW5 0x95 - -#define CS1_SW6 0x96 -#define CS2_SW6 0x97 -#define CS3_SW6 0x98 -#define CS4_SW6 0x99 -#define CS5_SW6 0x9A -#define CS6_SW6 0x9B -#define CS7_SW6 0x9C -#define CS8_SW6 0x9D -#define CS9_SW6 0x9E -#define CS10_SW6 0x9F -#define CS11_SW6 0xA0 -#define CS12_SW6 0xA1 -#define CS13_SW6 0xA2 -#define CS14_SW6 0xA3 -#define CS15_SW6 0xA4 -#define CS16_SW6 0xA5 -#define CS17_SW6 0xA6 -#define CS18_SW6 0xA7 -#define CS19_SW6 0xA8 -#define CS20_SW6 0xA9 -#define CS21_SW6 0xAA -#define CS22_SW6 0xAB -#define CS23_SW6 0xAC -#define CS24_SW6 0xAD -#define CS25_SW6 0xAE -#define CS26_SW6 0xAF -#define CS27_SW6 0xB0 -#define CS28_SW6 0xB1 -#define CS29_SW6 0xB2 -#define CS30_SW6 0xB3 - -#define CS1_SW7 0xB4 -#define CS2_SW7 0xB5 -#define CS3_SW7 0xB6 -#define CS4_SW7 0xB7 -#define CS5_SW7 0xB8 -#define CS6_SW7 0xB9 -#define CS7_SW7 0xBA -#define CS8_SW7 0xBB -#define CS9_SW7 0xBC -#define CS10_SW7 0xBD -#define CS11_SW7 0xBE -#define CS12_SW7 0xBF -#define CS13_SW7 0xC0 -#define CS14_SW7 0xC1 -#define CS15_SW7 0xC2 -#define CS16_SW7 0xC3 -#define CS17_SW7 0xC4 -#define CS18_SW7 0xC5 -#define CS19_SW7 0xC6 -#define CS20_SW7 0xC7 -#define CS21_SW7 0xC8 -#define CS22_SW7 0xC9 -#define CS23_SW7 0xCA -#define CS24_SW7 0xCB -#define CS25_SW7 0xCC -#define CS26_SW7 0xCD -#define CS27_SW7 0xCE -#define CS28_SW7 0xCF -#define CS29_SW7 0xD0 -#define CS30_SW7 0xD1 - -#define CS1_SW8 0xD2 -#define CS2_SW8 0xD3 -#define CS3_SW8 0xD4 -#define CS4_SW8 0xD5 -#define CS5_SW8 0xD6 -#define CS6_SW8 0xD7 -#define CS7_SW8 0xD8 -#define CS8_SW8 0xD9 -#define CS9_SW8 0xDA -#define CS10_SW8 0xDB -#define CS11_SW8 0xDC -#define CS12_SW8 0xDD -#define CS13_SW8 0xDE -#define CS14_SW8 0xDF -#define CS15_SW8 0xE0 -#define CS16_SW8 0xE1 -#define CS17_SW8 0xE2 -#define CS18_SW8 0xE3 -#define CS19_SW8 0xE4 -#define CS20_SW8 0xE5 -#define CS21_SW8 0xE6 -#define CS22_SW8 0xE7 -#define CS23_SW8 0xE8 -#define CS24_SW8 0xE9 -#define CS25_SW8 0xEA -#define CS26_SW8 0xEB -#define CS27_SW8 0xEC -#define CS28_SW8 0xED -#define CS29_SW8 0xEE -#define CS30_SW8 0xEF - -#define CS1_SW9 0xF0 -#define CS2_SW9 0xF1 -#define CS3_SW9 0xF2 -#define CS4_SW9 0xF3 -#define CS5_SW9 0xF4 -#define CS6_SW9 0xF5 -#define CS7_SW9 0xF6 -#define CS8_SW9 0xF7 -#define CS9_SW9 0xF8 -#define CS10_SW9 0xF9 -#define CS11_SW9 0xFA -#define CS12_SW9 0xFB -#define CS13_SW9 0xFC -#define CS14_SW9 0xFD -#define CS15_SW9 0xFE -#define CS16_SW9 0xFF -#define CS17_SW9 0x100 -#define CS18_SW9 0x101 -#define CS19_SW9 0x102 -#define CS20_SW9 0x103 -#define CS21_SW9 0x104 -#define CS22_SW9 0x105 -#define CS23_SW9 0x106 -#define CS24_SW9 0x107 -#define CS25_SW9 0x108 -#define CS26_SW9 0x109 -#define CS27_SW9 0x10A -#define CS28_SW9 0x10B -#define CS29_SW9 0x10C -#define CS30_SW9 0x10D - -#define CS31_SW1 0x10E -#define CS32_SW1 0x10F -#define CS33_SW1 0x110 -#define CS34_SW1 0x111 -#define CS35_SW1 0x112 -#define CS36_SW1 0x113 -#define CS37_SW1 0x114 -#define CS38_SW1 0x115 -#define CS39_SW1 0x116 - -#define CS31_SW2 0x117 -#define CS32_SW2 0x118 -#define CS33_SW2 0x119 -#define CS34_SW2 0x11A -#define CS35_SW2 0x11B -#define CS36_SW2 0x11C -#define CS37_SW2 0x11D -#define CS38_SW2 0x11E -#define CS39_SW2 0x11F - -#define CS31_SW3 0x120 -#define CS32_SW3 0x121 -#define CS33_SW3 0x122 -#define CS34_SW3 0x123 -#define CS35_SW3 0x124 -#define CS36_SW3 0x125 -#define CS37_SW3 0x126 -#define CS38_SW3 0x127 -#define CS39_SW3 0x128 - -#define CS31_SW4 0x129 -#define CS32_SW4 0x12A -#define CS33_SW4 0x12B -#define CS34_SW4 0x12C -#define CS35_SW4 0x12D -#define CS36_SW4 0x12E -#define CS37_SW4 0x12F -#define CS38_SW4 0x130 -#define CS39_SW4 0x131 - -#define CS31_SW5 0x132 -#define CS32_SW5 0x133 -#define CS33_SW5 0x134 -#define CS34_SW5 0x135 -#define CS35_SW5 0x136 -#define CS36_SW5 0x137 -#define CS37_SW5 0x138 -#define CS38_SW5 0x139 -#define CS39_SW5 0x13A - -#define CS31_SW6 0x13B -#define CS32_SW6 0x13C -#define CS33_SW6 0x13D -#define CS34_SW6 0x13E -#define CS35_SW6 0x13F -#define CS36_SW6 0x140 -#define CS37_SW6 0x141 -#define CS38_SW6 0x142 -#define CS39_SW6 0x143 - -#define CS31_SW7 0x144 -#define CS32_SW7 0x145 -#define CS33_SW7 0x146 -#define CS34_SW7 0x147 -#define CS35_SW7 0x148 -#define CS36_SW7 0x149 -#define CS37_SW7 0x14A -#define CS38_SW7 0x14B -#define CS39_SW7 0x14C - -#define CS31_SW8 0x14D -#define CS32_SW8 0x14E -#define CS33_SW8 0x14F -#define CS34_SW8 0x150 -#define CS35_SW8 0x151 -#define CS36_SW8 0x152 -#define CS37_SW8 0x153 -#define CS38_SW8 0x154 -#define CS39_SW8 0x155 - -#define CS31_SW9 0x156 -#define CS32_SW9 0x157 -#define CS33_SW9 0x158 -#define CS34_SW9 0x159 -#define CS35_SW9 0x15A -#define CS36_SW9 0x15B -#define CS37_SW9 0x15C -#define CS38_SW9 0x15D -#define CS39_SW9 0x15E diff --git a/drivers/led/issi/is31fl3741.c b/drivers/led/issi/is31fl3741.c index efcfa77b46..3614d1c104 100644 --- a/drivers/led/issi/is31fl3741.c +++ b/drivers/led/issi/is31fl3741.c @@ -18,11 +18,14 @@ */ #include "is31fl3741.h" -#include <string.h> #include "i2c_master.h" +#include "gpio.h" #include "wait.h" -#define IS31FL3741_PWM_REGISTER_COUNT 351 +#define IS31FL3741_PWM_0_REGISTER_COUNT 180 +#define IS31FL3741_PWM_1_REGISTER_COUNT 171 +#define IS31FL3741_SCALING_0_REGISTER_COUNT 180 +#define IS31FL3741_SCALING_1_REGISTER_COUNT 171 #ifndef IS31FL3741_I2C_TIMEOUT # define IS31FL3741_I2C_TIMEOUT 100 @@ -52,150 +55,166 @@ # define IS31FL3741_GLOBAL_CURRENT 0xFF #endif -// Transfer buffer for TWITransmitData() -uint8_t g_twi_transfer_buffer[20] = {0xFF}; +const uint8_t i2c_addresses[IS31FL3741_DRIVER_COUNT] = { + IS31FL3741_I2C_ADDRESS_1, +#ifdef IS31FL3741_I2C_ADDRESS_2 + IS31FL3741_I2C_ADDRESS_2, +# ifdef IS31FL3741_I2C_ADDRESS_3 + IS31FL3741_I2C_ADDRESS_3, +# ifdef IS31FL3741_I2C_ADDRESS_4 + IS31FL3741_I2C_ADDRESS_4, +# endif +# endif +#endif +}; // These buffers match the IS31FL3741 and IS31FL3741A PWM registers. -// The scaling buffers match the PG2 and PG3 LED On/Off registers. +// The scaling buffers match the page 2 and 3 LED On/Off registers. // Storing them like this is optimal for I2C transfers to the registers. // We could optimize this and take out the unused registers from these // buffers and the transfers in is31fl3741_write_pwm_buffer() but it's // probably not worth the extra complexity. -uint8_t g_pwm_buffer[IS31FL3741_DRIVER_COUNT][IS31FL3741_PWM_REGISTER_COUNT]; -bool g_pwm_buffer_update_required[IS31FL3741_DRIVER_COUNT] = {false}; -bool g_scaling_registers_update_required[IS31FL3741_DRIVER_COUNT] = {false}; - -uint8_t g_scaling_registers[IS31FL3741_DRIVER_COUNT][IS31FL3741_PWM_REGISTER_COUNT]; - -void is31fl3741_write_register(uint8_t addr, uint8_t reg, uint8_t data) { - g_twi_transfer_buffer[0] = reg; - g_twi_transfer_buffer[1] = data; - +typedef struct is31fl3741_driver_t { + uint8_t pwm_buffer_0[IS31FL3741_PWM_0_REGISTER_COUNT]; + uint8_t pwm_buffer_1[IS31FL3741_PWM_1_REGISTER_COUNT]; + bool pwm_buffer_dirty; + uint8_t scaling_buffer_0[IS31FL3741_SCALING_0_REGISTER_COUNT]; + uint8_t scaling_buffer_1[IS31FL3741_SCALING_1_REGISTER_COUNT]; + bool scaling_buffer_dirty; +} PACKED is31fl3741_driver_t; + +is31fl3741_driver_t driver_buffers[IS31FL3741_DRIVER_COUNT] = {{ + .pwm_buffer_0 = {0}, + .pwm_buffer_1 = {0}, + .pwm_buffer_dirty = false, + .scaling_buffer_0 = {0}, + .scaling_buffer_1 = {0}, + .scaling_buffer_dirty = false, +}}; + +void is31fl3741_write_register(uint8_t index, uint8_t reg, uint8_t data) { #if IS31FL3741_I2C_PERSISTENCE > 0 for (uint8_t i = 0; i < IS31FL3741_I2C_PERSISTENCE; i++) { - if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, IS31FL3741_I2C_TIMEOUT) == 0) break; + if (i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3741_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break; } #else - i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, IS31FL3741_I2C_TIMEOUT); + i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3741_I2C_TIMEOUT); #endif } -bool is31fl3741_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer) { - // Assume PG0 is already selected +void is31fl3741_select_page(uint8_t index, uint8_t page) { + is31fl3741_write_register(index, IS31FL3741_REG_COMMAND_WRITE_LOCK, IS31FL3741_COMMAND_WRITE_LOCK_MAGIC); + is31fl3741_write_register(index, IS31FL3741_REG_COMMAND, page); +} - for (int i = 0; i < 342; i += 18) { - if (i == 180) { - // unlock the command register and select PG1 - is31fl3741_write_register(addr, IS31FL3741_REG_COMMAND_WRITE_LOCK, IS31FL3741_COMMAND_WRITE_LOCK_MAGIC); - is31fl3741_write_register(addr, IS31FL3741_REG_COMMAND, IS31FL3741_COMMAND_PWM_1); - } +void is31fl3741_write_pwm_buffer(uint8_t index) { + is31fl3741_select_page(index, IS31FL3741_COMMAND_PWM_0); - g_twi_transfer_buffer[0] = i % 180; - memcpy(g_twi_transfer_buffer + 1, pwm_buffer + i, 18); + // Transmit PWM0 registers in 6 transfers of 30 bytes. + // Iterate over the pwm_buffer_0 contents at 30 byte intervals. + for (uint8_t i = 0; i < IS31FL3741_PWM_0_REGISTER_COUNT; i += 30) { #if IS31FL3741_I2C_PERSISTENCE > 0 - for (uint8_t i = 0; i < IS31FL3741_I2C_PERSISTENCE; i++) { - if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 19, IS31FL3741_I2C_TIMEOUT) != 0) { - return false; - } + for (uint8_t j = 0; j < IS31FL3741_I2C_PERSISTENCE; j++) { + if (i2c_write_register(i2c_addresses[index] << 1, i, driver_buffers[index].pwm_buffer_0 + i, 30, IS31FL3741_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break; } #else - if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 19, IS31FL3741_I2C_TIMEOUT) != 0) { - return false; - } + i2c_write_register(i2c_addresses[index] << 1, i, driver_buffers[index].pwm_buffer_0 + i, 30, IS31FL3741_I2C_TIMEOUT); #endif } - // transfer the left cause the total number is 351 - g_twi_transfer_buffer[0] = 162; - memcpy(g_twi_transfer_buffer + 1, pwm_buffer + 342, 9); + is31fl3741_select_page(index, IS31FL3741_COMMAND_PWM_1); + + // Transmit PWM1 registers in 9 transfers of 19 bytes. + // Iterate over the pwm_buffer_1 contents at 19 byte intervals. + for (uint8_t i = 0; i < IS31FL3741_PWM_1_REGISTER_COUNT; i += 19) { #if IS31FL3741_I2C_PERSISTENCE > 0 - for (uint8_t i = 0; i < IS31FL3741_I2C_PERSISTENCE; i++) { - if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 10, IS31FL3741_I2C_TIMEOUT) != 0) { - return false; + for (uint8_t i = 0; i < IS31FL3741_I2C_PERSISTENCE; i++) { + if (i2c_write_register(i2c_addresses[index] << 1, i, driver_buffers[index].pwm_buffer_1 + i, 19, IS31FL3741_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break; } - } #else - if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 10, IS31FL3741_I2C_TIMEOUT) != 0) { - return false; - } + i2c_write_register(i2c_addresses[index] << 1, i, driver_buffers[index].pwm_buffer_1 + i, 19, IS31FL3741_I2C_TIMEOUT); #endif - - return true; + } } void is31fl3741_init_drivers(void) { i2c_init(); - is31fl3741_init(IS31FL3741_I2C_ADDRESS_1); -#if defined(IS31FL3741_I2C_ADDRESS_2) - is31fl3741_init(IS31FL3741_I2C_ADDRESS_2); -# if defined(IS31FL3741_I2C_ADDRESS_3) - is31fl3741_init(IS31FL3741_I2C_ADDRESS_3); -# if defined(IS31FL3741_I2C_ADDRESS_4) - is31fl3741_init(IS31FL3741_I2C_ADDRESS_4); -# endif -# endif +#if defined(IS31FL3741_SDB_PIN) + gpio_set_pin_output(IS31FL3741_SDB_PIN); + gpio_write_pin_high(IS31FL3741_SDB_PIN); #endif + for (uint8_t i = 0; i < IS31FL3741_DRIVER_COUNT; i++) { + is31fl3741_init(i); + } + for (int i = 0; i < IS31FL3741_LED_COUNT; i++) { is31fl3741_set_led_control_register(i, true, true, true); } - is31fl3741_update_led_control_registers(IS31FL3741_I2C_ADDRESS_1, 0); -#if defined(IS31FL3741_I2C_ADDRESS_2) - is31fl3741_update_led_control_registers(IS31FL3741_I2C_ADDRESS_2, 1); -# if defined(IS31FL3741_I2C_ADDRESS_3) - is31fl3741_update_led_control_registers(IS31FL3741_I2C_ADDRESS_3, 2); -# if defined(IS31FL3741_I2C_ADDRESS_4) - is31fl3741_update_led_control_registers(IS31FL3741_I2C_ADDRESS_4, 3); -# endif -# endif -#endif + for (uint8_t i = 0; i < IS31FL3741_DRIVER_COUNT; i++) { + is31fl3741_update_led_control_registers(i); + } } -void is31fl3741_init(uint8_t addr) { +void is31fl3741_init(uint8_t index) { // In order to avoid the LEDs being driven with garbage data // in the LED driver's PWM registers, shutdown is enabled last. // Set up the mode and other settings, clear the PWM registers, // then disable software shutdown. // Unlock the command register. - // Unlock the command register. - is31fl3741_write_register(addr, IS31FL3741_REG_COMMAND_WRITE_LOCK, IS31FL3741_COMMAND_WRITE_LOCK_MAGIC); - - // Select PG4 - is31fl3741_write_register(addr, IS31FL3741_REG_COMMAND, IS31FL3741_COMMAND_FUNCTION); + is31fl3741_select_page(index, IS31FL3741_COMMAND_FUNCTION); // Set to Normal operation - is31fl3741_write_register(addr, IS31FL3741_FUNCTION_REG_CONFIGURATION, IS31FL3741_CONFIGURATION); + is31fl3741_write_register(index, IS31FL3741_FUNCTION_REG_CONFIGURATION, IS31FL3741_CONFIGURATION); // Set Golbal Current Control Register - is31fl3741_write_register(addr, IS31FL3741_FUNCTION_REG_GLOBAL_CURRENT, IS31FL3741_GLOBAL_CURRENT); + is31fl3741_write_register(index, IS31FL3741_FUNCTION_REG_GLOBAL_CURRENT, IS31FL3741_GLOBAL_CURRENT); // Set Pull up & Down for SWx CSy - is31fl3741_write_register(addr, IS31FL3741_FUNCTION_REG_PULLDOWNUP, ((IS31FL3741_CS_PULLDOWN << 4) | IS31FL3741_SW_PULLUP)); + is31fl3741_write_register(index, IS31FL3741_FUNCTION_REG_PULLDOWNUP, ((IS31FL3741_CS_PULLDOWN << 4) | IS31FL3741_SW_PULLUP)); // Set PWM frequency - is31fl3741_write_register(addr, IS31FL3741_FUNCTION_REG_PWM_FREQUENCY, (IS31FL3741_PWM_FREQUENCY & 0b1111)); + is31fl3741_write_register(index, IS31FL3741_FUNCTION_REG_PWM_FREQUENCY, (IS31FL3741_PWM_FREQUENCY & 0b1111)); - // is31fl3741_update_led_scaling_registers(addr, 0xFF, 0xFF, 0xFF); + // is31fl3741_update_led_scaling_registers(index, 0xFF, 0xFF, 0xFF); // Wait 10ms to ensure the device has woken up. wait_ms(10); } +uint8_t get_pwm_value(uint8_t driver, uint16_t reg) { + if (reg & 0x100) { + return driver_buffers[driver].pwm_buffer_1[reg & 0xFF]; + } else { + return driver_buffers[driver].pwm_buffer_0[reg]; + } +} + +void set_pwm_value(uint8_t driver, uint16_t reg, uint8_t value) { + if (reg & 0x100) { + driver_buffers[driver].pwm_buffer_1[reg & 0xFF] = value; + } else { + driver_buffers[driver].pwm_buffer_0[reg] = value; + } +} + void is31fl3741_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) { is31fl3741_led_t led; + if (index >= 0 && index < IS31FL3741_LED_COUNT) { memcpy_P(&led, (&g_is31fl3741_leds[index]), sizeof(led)); - if (g_pwm_buffer[led.driver][led.r] == red && g_pwm_buffer[led.driver][led.g] == green && g_pwm_buffer[led.driver][led.b] == blue) { + if (get_pwm_value(led.driver, led.r) == red && get_pwm_value(led.driver, led.g) == green && get_pwm_value(led.driver, led.b) == blue) { return; } - g_pwm_buffer_update_required[led.driver] = true; - g_pwm_buffer[led.driver][led.r] = red; - g_pwm_buffer[led.driver][led.g] = green; - g_pwm_buffer[led.driver][led.b] = blue; + + set_pwm_value(led.driver, led.r, red); + set_pwm_value(led.driver, led.g, green); + set_pwm_value(led.driver, led.b, blue); + driver_buffers[led.driver].pwm_buffer_dirty = true; } } @@ -205,92 +224,67 @@ void is31fl3741_set_color_all(uint8_t red, uint8_t green, uint8_t blue) { } } -void is31fl3741_set_led_control_register(uint8_t index, bool red, bool green, bool blue) { - is31fl3741_led_t led; - memcpy_P(&led, (&g_is31fl3741_leds[index]), sizeof(led)); - - if (red) { - g_scaling_registers[led.driver][led.r] = 0xFF; +void set_scaling_value(uint8_t driver, uint16_t reg, uint8_t value) { + if (reg & 0x100) { + driver_buffers[driver].scaling_buffer_1[reg & 0xFF] = value; } else { - g_scaling_registers[led.driver][led.r] = 0x00; + driver_buffers[driver].scaling_buffer_0[reg] = value; } +} - if (green) { - g_scaling_registers[led.driver][led.g] = 0xFF; - } else { - g_scaling_registers[led.driver][led.g] = 0x00; - } +void is31fl3741_set_led_control_register(uint8_t index, bool red, bool green, bool blue) { + is31fl3741_led_t led; + memcpy_P(&led, (&g_is31fl3741_leds[index]), sizeof(led)); - if (blue) { - g_scaling_registers[led.driver][led.b] = 0xFF; - } else { - g_scaling_registers[led.driver][led.b] = 0x00; - } + set_scaling_value(led.driver, led.r, red ? 0xFF : 0x00); + set_scaling_value(led.driver, led.g, green ? 0xFF : 0x00); + set_scaling_value(led.driver, led.b, blue ? 0xFF : 0x00); - g_scaling_registers_update_required[led.driver] = true; + driver_buffers[led.driver].scaling_buffer_dirty = true; } -void is31fl3741_update_pwm_buffers(uint8_t addr, uint8_t index) { - if (g_pwm_buffer_update_required[index]) { - // unlock the command register and select PG2 - is31fl3741_write_register(addr, IS31FL3741_REG_COMMAND_WRITE_LOCK, IS31FL3741_COMMAND_WRITE_LOCK_MAGIC); - is31fl3741_write_register(addr, IS31FL3741_REG_COMMAND, IS31FL3741_COMMAND_PWM_0); +void is31fl3741_update_pwm_buffers(uint8_t index) { + if (driver_buffers[index].pwm_buffer_dirty) { + is31fl3741_write_pwm_buffer(index); - is31fl3741_write_pwm_buffer(addr, g_pwm_buffer[index]); + driver_buffers[index].pwm_buffer_dirty = false; } - - g_pwm_buffer_update_required[index] = false; } void is31fl3741_set_pwm_buffer(const is31fl3741_led_t *pled, uint8_t red, uint8_t green, uint8_t blue) { - g_pwm_buffer[pled->driver][pled->r] = red; - g_pwm_buffer[pled->driver][pled->g] = green; - g_pwm_buffer[pled->driver][pled->b] = blue; - - g_pwm_buffer_update_required[pled->driver] = true; + set_pwm_value(pled->driver, pled->r, red); + set_pwm_value(pled->driver, pled->g, green); + set_pwm_value(pled->driver, pled->b, blue); + driver_buffers[pled->driver].pwm_buffer_dirty = true; } -void is31fl3741_update_led_control_registers(uint8_t addr, uint8_t index) { - if (g_scaling_registers_update_required[index]) { - // unlock the command register and select PG2 - is31fl3741_write_register(addr, IS31FL3741_REG_COMMAND_WRITE_LOCK, IS31FL3741_COMMAND_WRITE_LOCK_MAGIC); - is31fl3741_write_register(addr, IS31FL3741_REG_COMMAND, IS31FL3741_COMMAND_SCALING_0); +void is31fl3741_update_led_control_registers(uint8_t index) { + if (driver_buffers[index].scaling_buffer_dirty) { + is31fl3741_select_page(index, IS31FL3741_COMMAND_SCALING_0); - // CS1_SW1 to CS30_SW6 are on PG2 - for (int i = CS1_SW1; i <= CS30_SW6; ++i) { - is31fl3741_write_register(addr, i, g_scaling_registers[index][i]); + for (uint8_t i = 0; i < IS31FL3741_SCALING_0_REGISTER_COUNT; i++) { + is31fl3741_write_register(index, i, driver_buffers[index].scaling_buffer_0[i]); } - // unlock the command register and select PG3 - is31fl3741_write_register(addr, IS31FL3741_REG_COMMAND_WRITE_LOCK, IS31FL3741_COMMAND_WRITE_LOCK_MAGIC); - is31fl3741_write_register(addr, IS31FL3741_REG_COMMAND, IS31FL3741_COMMAND_SCALING_1); + is31fl3741_select_page(index, IS31FL3741_COMMAND_SCALING_1); - // CS1_SW7 to CS39_SW9 are on PG3 - for (int i = CS1_SW7; i <= CS39_SW9; ++i) { - is31fl3741_write_register(addr, i - CS1_SW7, g_scaling_registers[index][i]); + for (uint8_t i = 0; i < IS31FL3741_SCALING_1_REGISTER_COUNT; i++) { + is31fl3741_write_register(index, i, driver_buffers[index].scaling_buffer_1[i]); } - g_scaling_registers_update_required[index] = false; + driver_buffers[index].scaling_buffer_dirty = false; } } void is31fl3741_set_scaling_registers(const is31fl3741_led_t *pled, uint8_t red, uint8_t green, uint8_t blue) { - g_scaling_registers[pled->driver][pled->r] = red; - g_scaling_registers[pled->driver][pled->g] = green; - g_scaling_registers[pled->driver][pled->b] = blue; - - g_scaling_registers_update_required[pled->driver] = true; + set_scaling_value(pled->driver, pled->r, red); + set_scaling_value(pled->driver, pled->g, green); + set_scaling_value(pled->driver, pled->b, blue); + driver_buffers[pled->driver].scaling_buffer_dirty = true; } void is31fl3741_flush(void) { - is31fl3741_update_pwm_buffers(IS31FL3741_I2C_ADDRESS_1, 0); -#if defined(IS31FL3741_I2C_ADDRESS_2) - is31fl3741_update_pwm_buffers(IS31FL3741_I2C_ADDRESS_2, 1); -# if defined(IS31FL3741_I2C_ADDRESS_3) - is31fl3741_update_pwm_buffers(IS31FL3741_I2C_ADDRESS_3, 2); -# if defined(IS31FL3741_I2C_ADDRESS_4) - is31fl3741_update_pwm_buffers(IS31FL3741_I2C_ADDRESS_4, 3); -# endif -# endif -#endif + for (uint8_t i = 0; i < IS31FL3741_DRIVER_COUNT; i++) { + is31fl3741_update_pwm_buffers(i); + } } diff --git a/drivers/led/issi/is31fl3741.h b/drivers/led/issi/is31fl3741.h index 6466696b60..cc9637a4e8 100644 --- a/drivers/led/issi/is31fl3741.h +++ b/drivers/led/issi/is31fl3741.h @@ -119,9 +119,9 @@ typedef struct is31fl3741_led_t { extern const is31fl3741_led_t PROGMEM g_is31fl3741_leds[IS31FL3741_LED_COUNT]; void is31fl3741_init_drivers(void); -void is31fl3741_init(uint8_t addr); -void is31fl3741_write_register(uint8_t addr, uint8_t reg, uint8_t data); -bool is31fl3741_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer); +void is31fl3741_init(uint8_t index); +void is31fl3741_write_register(uint8_t index, uint8_t reg, uint8_t data); +void is31fl3741_select_page(uint8_t index, uint8_t page); void is31fl3741_set_color(int index, uint8_t red, uint8_t green, uint8_t blue); void is31fl3741_set_color_all(uint8_t red, uint8_t green, uint8_t blue); @@ -132,8 +132,8 @@ void is31fl3741_set_led_control_register(uint8_t index, bool red, bool green, bo // (eg. from a timer interrupt). // Call this while idle (in between matrix scans). // If the buffer is dirty, it will update the driver with the buffer. -void is31fl3741_update_pwm_buffers(uint8_t addr, uint8_t index); -void is31fl3741_update_led_control_registers(uint8_t addr, uint8_t index); +void is31fl3741_update_pwm_buffers(uint8_t index); +void is31fl3741_update_led_control_registers(uint8_t index); void is31fl3741_set_scaling_registers(const is31fl3741_led_t *pled, uint8_t red, uint8_t green, uint8_t blue); void is31fl3741_set_pwm_buffer(const is31fl3741_led_t *pled, uint8_t red, uint8_t green, uint8_t blue); @@ -163,371 +163,742 @@ void is31fl3741_flush(void); #define IS31FL3741_PWM_FREQUENCY_1K8_HZ 0b0111 #define IS31FL3741_PWM_FREQUENCY_900_HZ 0b1011 -#define CS1_SW1 0x00 -#define CS2_SW1 0x01 -#define CS3_SW1 0x02 -#define CS4_SW1 0x03 -#define CS5_SW1 0x04 -#define CS6_SW1 0x05 -#define CS7_SW1 0x06 -#define CS8_SW1 0x07 -#define CS9_SW1 0x08 -#define CS10_SW1 0x09 -#define CS11_SW1 0x0A -#define CS12_SW1 0x0B -#define CS13_SW1 0x0C -#define CS14_SW1 0x0D -#define CS15_SW1 0x0E -#define CS16_SW1 0x0F -#define CS17_SW1 0x10 -#define CS18_SW1 0x11 -#define CS19_SW1 0x12 -#define CS20_SW1 0x13 -#define CS21_SW1 0x14 -#define CS22_SW1 0x15 -#define CS23_SW1 0x16 -#define CS24_SW1 0x17 -#define CS25_SW1 0x18 -#define CS26_SW1 0x19 -#define CS27_SW1 0x1A -#define CS28_SW1 0x1B -#define CS29_SW1 0x1C -#define CS30_SW1 0x1D - -#define CS1_SW2 0x1E -#define CS2_SW2 0x1F -#define CS3_SW2 0x20 -#define CS4_SW2 0x21 -#define CS5_SW2 0x22 -#define CS6_SW2 0x23 -#define CS7_SW2 0x24 -#define CS8_SW2 0x25 -#define CS9_SW2 0x26 -#define CS10_SW2 0x27 -#define CS11_SW2 0x28 -#define CS12_SW2 0x29 -#define CS13_SW2 0x2A -#define CS14_SW2 0x2B -#define CS15_SW2 0x2C -#define CS16_SW2 0x2D -#define CS17_SW2 0x2E -#define CS18_SW2 0x2F -#define CS19_SW2 0x30 -#define CS20_SW2 0x31 -#define CS21_SW2 0x32 -#define CS22_SW2 0x33 -#define CS23_SW2 0x34 -#define CS24_SW2 0x35 -#define CS25_SW2 0x36 -#define CS26_SW2 0x37 -#define CS27_SW2 0x38 -#define CS28_SW2 0x39 -#define CS29_SW2 0x3A -#define CS30_SW2 0x3B - -#define CS1_SW3 0x3C -#define CS2_SW3 0x3D -#define CS3_SW3 0x3E -#define CS4_SW3 0x3F -#define CS5_SW3 0x40 -#define CS6_SW3 0x41 -#define CS7_SW3 0x42 -#define CS8_SW3 0x43 -#define CS9_SW3 0x44 -#define CS10_SW3 0x45 -#define CS11_SW3 0x46 -#define CS12_SW3 0x47 -#define CS13_SW3 0x48 -#define CS14_SW3 0x49 -#define CS15_SW3 0x4A -#define CS16_SW3 0x4B -#define CS17_SW3 0x4C -#define CS18_SW3 0x4D -#define CS19_SW3 0x4E -#define CS20_SW3 0x4F -#define CS21_SW3 0x50 -#define CS22_SW3 0x51 -#define CS23_SW3 0x52 -#define CS24_SW3 0x53 -#define CS25_SW3 0x54 -#define CS26_SW3 0x55 -#define CS27_SW3 0x56 -#define CS28_SW3 0x57 -#define CS29_SW3 0x58 -#define CS30_SW3 0x59 - -#define CS1_SW4 0x5A -#define CS2_SW4 0x5B -#define CS3_SW4 0x5C -#define CS4_SW4 0x5D -#define CS5_SW4 0x5E -#define CS6_SW4 0x5F -#define CS7_SW4 0x60 -#define CS8_SW4 0x61 -#define CS9_SW4 0x62 -#define CS10_SW4 0x63 -#define CS11_SW4 0x64 -#define CS12_SW4 0x65 -#define CS13_SW4 0x66 -#define CS14_SW4 0x67 -#define CS15_SW4 0x68 -#define CS16_SW4 0x69 -#define CS17_SW4 0x6A -#define CS18_SW4 0x6B -#define CS19_SW4 0x6C -#define CS20_SW4 0x6D -#define CS21_SW4 0x6E -#define CS22_SW4 0x6F -#define CS23_SW4 0x70 -#define CS24_SW4 0x71 -#define CS25_SW4 0x72 -#define CS26_SW4 0x73 -#define CS27_SW4 0x74 -#define CS28_SW4 0x75 -#define CS29_SW4 0x76 -#define CS30_SW4 0x77 - -#define CS1_SW5 0x78 -#define CS2_SW5 0x79 -#define CS3_SW5 0x7A -#define CS4_SW5 0x7B -#define CS5_SW5 0x7C -#define CS6_SW5 0x7D -#define CS7_SW5 0x7E -#define CS8_SW5 0x7F -#define CS9_SW5 0x80 -#define CS10_SW5 0x81 -#define CS11_SW5 0x82 -#define CS12_SW5 0x83 -#define CS13_SW5 0x84 -#define CS14_SW5 0x85 -#define CS15_SW5 0x86 -#define CS16_SW5 0x87 -#define CS17_SW5 0x88 -#define CS18_SW5 0x89 -#define CS19_SW5 0x8A -#define CS20_SW5 0x8B -#define CS21_SW5 0x8C -#define CS22_SW5 0x8D -#define CS23_SW5 0x8E -#define CS24_SW5 0x8F -#define CS25_SW5 0x90 -#define CS26_SW5 0x91 -#define CS27_SW5 0x92 -#define CS28_SW5 0x93 -#define CS29_SW5 0x94 -#define CS30_SW5 0x95 - -#define CS1_SW6 0x96 -#define CS2_SW6 0x97 -#define CS3_SW6 0x98 -#define CS4_SW6 0x99 -#define CS5_SW6 0x9A -#define CS6_SW6 0x9B -#define CS7_SW6 0x9C -#define CS8_SW6 0x9D -#define CS9_SW6 0x9E -#define CS10_SW6 0x9F -#define CS11_SW6 0xA0 -#define CS12_SW6 0xA1 -#define CS13_SW6 0xA2 -#define CS14_SW6 0xA3 -#define CS15_SW6 0xA4 -#define CS16_SW6 0xA5 -#define CS17_SW6 0xA6 -#define CS18_SW6 0xA7 -#define CS19_SW6 0xA8 -#define CS20_SW6 0xA9 -#define CS21_SW6 0xAA -#define CS22_SW6 0xAB -#define CS23_SW6 0xAC -#define CS24_SW6 0xAD -#define CS25_SW6 0xAE -#define CS26_SW6 0xAF -#define CS27_SW6 0xB0 -#define CS28_SW6 0xB1 -#define CS29_SW6 0xB2 -#define CS30_SW6 0xB3 - -#define CS1_SW7 0xB4 -#define CS2_SW7 0xB5 -#define CS3_SW7 0xB6 -#define CS4_SW7 0xB7 -#define CS5_SW7 0xB8 -#define CS6_SW7 0xB9 -#define CS7_SW7 0xBA -#define CS8_SW7 0xBB -#define CS9_SW7 0xBC -#define CS10_SW7 0xBD -#define CS11_SW7 0xBE -#define CS12_SW7 0xBF -#define CS13_SW7 0xC0 -#define CS14_SW7 0xC1 -#define CS15_SW7 0xC2 -#define CS16_SW7 0xC3 -#define CS17_SW7 0xC4 -#define CS18_SW7 0xC5 -#define CS19_SW7 0xC6 -#define CS20_SW7 0xC7 -#define CS21_SW7 0xC8 -#define CS22_SW7 0xC9 -#define CS23_SW7 0xCA -#define CS24_SW7 0xCB -#define CS25_SW7 0xCC -#define CS26_SW7 0xCD -#define CS27_SW7 0xCE -#define CS28_SW7 0xCF -#define CS29_SW7 0xD0 -#define CS30_SW7 0xD1 - -#define CS1_SW8 0xD2 -#define CS2_SW8 0xD3 -#define CS3_SW8 0xD4 -#define CS4_SW8 0xD5 -#define CS5_SW8 0xD6 -#define CS6_SW8 0xD7 -#define CS7_SW8 0xD8 -#define CS8_SW8 0xD9 -#define CS9_SW8 0xDA -#define CS10_SW8 0xDB -#define CS11_SW8 0xDC -#define CS12_SW8 0xDD -#define CS13_SW8 0xDE -#define CS14_SW8 0xDF -#define CS15_SW8 0xE0 -#define CS16_SW8 0xE1 -#define CS17_SW8 0xE2 -#define CS18_SW8 0xE3 -#define CS19_SW8 0xE4 -#define CS20_SW8 0xE5 -#define CS21_SW8 0xE6 -#define CS22_SW8 0xE7 -#define CS23_SW8 0xE8 -#define CS24_SW8 0xE9 -#define CS25_SW8 0xEA -#define CS26_SW8 0xEB -#define CS27_SW8 0xEC -#define CS28_SW8 0xED -#define CS29_SW8 0xEE -#define CS30_SW8 0xEF - -#define CS1_SW9 0xF0 -#define CS2_SW9 0xF1 -#define CS3_SW9 0xF2 -#define CS4_SW9 0xF3 -#define CS5_SW9 0xF4 -#define CS6_SW9 0xF5 -#define CS7_SW9 0xF6 -#define CS8_SW9 0xF7 -#define CS9_SW9 0xF8 -#define CS10_SW9 0xF9 -#define CS11_SW9 0xFA -#define CS12_SW9 0xFB -#define CS13_SW9 0xFC -#define CS14_SW9 0xFD -#define CS15_SW9 0xFE -#define CS16_SW9 0xFF -#define CS17_SW9 0x100 -#define CS18_SW9 0x101 -#define CS19_SW9 0x102 -#define CS20_SW9 0x103 -#define CS21_SW9 0x104 -#define CS22_SW9 0x105 -#define CS23_SW9 0x106 -#define CS24_SW9 0x107 -#define CS25_SW9 0x108 -#define CS26_SW9 0x109 -#define CS27_SW9 0x10A -#define CS28_SW9 0x10B -#define CS29_SW9 0x10C -#define CS30_SW9 0x10D - -#define CS31_SW1 0x10E -#define CS32_SW1 0x10F -#define CS33_SW1 0x110 -#define CS34_SW1 0x111 -#define CS35_SW1 0x112 -#define CS36_SW1 0x113 -#define CS37_SW1 0x114 -#define CS38_SW1 0x115 -#define CS39_SW1 0x116 - -#define CS31_SW2 0x117 -#define CS32_SW2 0x118 -#define CS33_SW2 0x119 -#define CS34_SW2 0x11A -#define CS35_SW2 0x11B -#define CS36_SW2 0x11C -#define CS37_SW2 0x11D -#define CS38_SW2 0x11E -#define CS39_SW2 0x11F - -#define CS31_SW3 0x120 -#define CS32_SW3 0x121 -#define CS33_SW3 0x122 -#define CS34_SW3 0x123 -#define CS35_SW3 0x124 -#define CS36_SW3 0x125 -#define CS37_SW3 0x126 -#define CS38_SW3 0x127 -#define CS39_SW3 0x128 - -#define CS31_SW4 0x129 -#define CS32_SW4 0x12A -#define CS33_SW4 0x12B -#define CS34_SW4 0x12C -#define CS35_SW4 0x12D -#define CS36_SW4 0x12E -#define CS37_SW4 0x12F -#define CS38_SW4 0x130 -#define CS39_SW4 0x131 - -#define CS31_SW5 0x132 -#define CS32_SW5 0x133 -#define CS33_SW5 0x134 -#define CS34_SW5 0x135 -#define CS35_SW5 0x136 -#define CS36_SW5 0x137 -#define CS37_SW5 0x138 -#define CS38_SW5 0x139 -#define CS39_SW5 0x13A - -#define CS31_SW6 0x13B -#define CS32_SW6 0x13C -#define CS33_SW6 0x13D -#define CS34_SW6 0x13E -#define CS35_SW6 0x13F -#define CS36_SW6 0x140 -#define CS37_SW6 0x141 -#define CS38_SW6 0x142 -#define CS39_SW6 0x143 - -#define CS31_SW7 0x144 -#define CS32_SW7 0x145 -#define CS33_SW7 0x146 -#define CS34_SW7 0x147 -#define CS35_SW7 0x148 -#define CS36_SW7 0x149 -#define CS37_SW7 0x14A -#define CS38_SW7 0x14B -#define CS39_SW7 0x14C - -#define CS31_SW8 0x14D -#define CS32_SW8 0x14E -#define CS33_SW8 0x14F -#define CS34_SW8 0x150 -#define CS35_SW8 0x151 -#define CS36_SW8 0x152 -#define CS37_SW8 0x153 -#define CS38_SW8 0x154 -#define CS39_SW8 0x155 - -#define CS31_SW9 0x156 -#define CS32_SW9 0x157 -#define CS33_SW9 0x158 -#define CS34_SW9 0x159 -#define CS35_SW9 0x15A -#define CS36_SW9 0x15B -#define CS37_SW9 0x15C -#define CS38_SW9 0x15D -#define CS39_SW9 0x15E +#define SW1_CS1 0x00 +#define SW1_CS2 0x01 +#define SW1_CS3 0x02 +#define SW1_CS4 0x03 +#define SW1_CS5 0x04 +#define SW1_CS6 0x05 +#define SW1_CS7 0x06 +#define SW1_CS8 0x07 +#define SW1_CS9 0x08 +#define SW1_CS10 0x09 +#define SW1_CS11 0x0A +#define SW1_CS12 0x0B +#define SW1_CS13 0x0C +#define SW1_CS14 0x0D +#define SW1_CS15 0x0E +#define SW1_CS16 0x0F +#define SW1_CS17 0x10 +#define SW1_CS18 0x11 +#define SW1_CS19 0x12 +#define SW1_CS20 0x13 +#define SW1_CS21 0x14 +#define SW1_CS22 0x15 +#define SW1_CS23 0x16 +#define SW1_CS24 0x17 +#define SW1_CS25 0x18 +#define SW1_CS26 0x19 +#define SW1_CS27 0x1A +#define SW1_CS28 0x1B +#define SW1_CS29 0x1C +#define SW1_CS30 0x1D + +#define SW2_CS1 0x1E +#define SW2_CS2 0x1F +#define SW2_CS3 0x20 +#define SW2_CS4 0x21 +#define SW2_CS5 0x22 +#define SW2_CS6 0x23 +#define SW2_CS7 0x24 +#define SW2_CS8 0x25 +#define SW2_CS9 0x26 +#define SW2_CS10 0x27 +#define SW2_CS11 0x28 +#define SW2_CS12 0x29 +#define SW2_CS13 0x2A +#define SW2_CS14 0x2B +#define SW2_CS15 0x2C +#define SW2_CS16 0x2D +#define SW2_CS17 0x2E +#define SW2_CS18 0x2F +#define SW2_CS19 0x30 +#define SW2_CS20 0x31 +#define SW2_CS21 0x32 +#define SW2_CS22 0x33 +#define SW2_CS23 0x34 +#define SW2_CS24 0x35 +#define SW2_CS25 0x36 +#define SW2_CS26 0x37 +#define SW2_CS27 0x38 +#define SW2_CS28 0x39 +#define SW2_CS29 0x3A +#define SW2_CS30 0x3B + +#define SW3_CS1 0x3C +#define SW3_CS2 0x3D +#define SW3_CS3 0x3E +#define SW3_CS4 0x3F +#define SW3_CS5 0x40 +#define SW3_CS6 0x41 +#define SW3_CS7 0x42 +#define SW3_CS8 0x43 +#define SW3_CS9 0x44 +#define SW3_CS10 0x45 +#define SW3_CS11 0x46 +#define SW3_CS12 0x47 +#define SW3_CS13 0x48 +#define SW3_CS14 0x49 +#define SW3_CS15 0x4A +#define SW3_CS16 0x4B +#define SW3_CS17 0x4C +#define SW3_CS18 0x4D +#define SW3_CS19 0x4E +#define SW3_CS20 0x4F +#define SW3_CS21 0x50 +#define SW3_CS22 0x51 +#define SW3_CS23 0x52 +#define SW3_CS24 0x53 +#define SW3_CS25 0x54 +#define SW3_CS26 0x55 +#define SW3_CS27 0x56 +#define SW3_CS28 0x57 +#define SW3_CS29 0x58 +#define SW3_CS30 0x59 + +#define SW4_CS1 0x5A +#define SW4_CS2 0x5B +#define SW4_CS3 0x5C +#define SW4_CS4 0x5D +#define SW4_CS5 0x5E +#define SW4_CS6 0x5F +#define SW4_CS7 0x60 +#define SW4_CS8 0x61 +#define SW4_CS9 0x62 +#define SW4_CS10 0x63 +#define SW4_CS11 0x64 +#define SW4_CS12 0x65 +#define SW4_CS13 0x66 +#define SW4_CS14 0x67 +#define SW4_CS15 0x68 +#define SW4_CS16 0x69 +#define SW4_CS17 0x6A +#define SW4_CS18 0x6B +#define SW4_CS19 0x6C +#define SW4_CS20 0x6D +#define SW4_CS21 0x6E +#define SW4_CS22 0x6F +#define SW4_CS23 0x70 +#define SW4_CS24 0x71 +#define SW4_CS25 0x72 +#define SW4_CS26 0x73 +#define SW4_CS27 0x74 +#define SW4_CS28 0x75 +#define SW4_CS29 0x76 +#define SW4_CS30 0x77 + +#define SW5_CS1 0x78 +#define SW5_CS2 0x79 +#define SW5_CS3 0x7A +#define SW5_CS4 0x7B +#define SW5_CS5 0x7C +#define SW5_CS6 0x7D +#define SW5_CS7 0x7E +#define SW5_CS8 0x7F +#define SW5_CS9 0x80 +#define SW5_CS10 0x81 +#define SW5_CS11 0x82 +#define SW5_CS12 0x83 +#define SW5_CS13 0x84 +#define SW5_CS14 0x85 +#define SW5_CS15 0x86 +#define SW5_CS16 0x87 +#define SW5_CS17 0x88 +#define SW5_CS18 0x89 +#define SW5_CS19 0x8A +#define SW5_CS20 0x8B +#define SW5_CS21 0x8C +#define SW5_CS22 0x8D +#define SW5_CS23 0x8E +#define SW5_CS24 0x8F +#define SW5_CS25 0x90 +#define SW5_CS26 0x91 +#define SW5_CS27 0x92 +#define SW5_CS28 0x93 +#define SW5_CS29 0x94 +#define SW5_CS30 0x95 + +#define SW6_CS1 0x96 +#define SW6_CS2 0x97 +#define SW6_CS3 0x98 +#define SW6_CS4 0x99 +#define SW6_CS5 0x9A +#define SW6_CS6 0x9B +#define SW6_CS7 0x9C +#define SW6_CS8 0x9D +#define SW6_CS9 0x9E +#define SW6_CS10 0x9F +#define SW6_CS11 0xA0 +#define SW6_CS12 0xA1 +#define SW6_CS13 0xA2 +#define SW6_CS14 0xA3 +#define SW6_CS15 0xA4 +#define SW6_CS16 0xA5 +#define SW6_CS17 0xA6 +#define SW6_CS18 0xA7 +#define SW6_CS19 0xA8 +#define SW6_CS20 0xA9 +#define SW6_CS21 0xAA +#define SW6_CS22 0xAB +#define SW6_CS23 0xAC +#define SW6_CS24 0xAD +#define SW6_CS25 0xAE +#define SW6_CS26 0xAF +#define SW6_CS27 0xB0 +#define SW6_CS28 0xB1 +#define SW6_CS29 0xB2 +#define SW6_CS30 0xB3 + +#define SW7_CS1 0x100 +#define SW7_CS2 0x101 +#define SW7_CS3 0x102 +#define SW7_CS4 0x103 +#define SW7_CS5 0x104 +#define SW7_CS6 0x105 +#define SW7_CS7 0x106 +#define SW7_CS8 0x107 +#define SW7_CS9 0x108 +#define SW7_CS10 0x109 +#define SW7_CS11 0x10A +#define SW7_CS12 0x10B +#define SW7_CS13 0x10C +#define SW7_CS14 0x10D +#define SW7_CS15 0x10E +#define SW7_CS16 0x10F +#define SW7_CS17 0x110 +#define SW7_CS18 0x111 +#define SW7_CS19 0x112 +#define SW7_CS20 0x113 +#define SW7_CS21 0x114 +#define SW7_CS22 0x115 +#define SW7_CS23 0x116 +#define SW7_CS24 0x117 +#define SW7_CS25 0x118 +#define SW7_CS26 0x119 +#define SW7_CS27 0x11A +#define SW7_CS28 0x11B +#define SW7_CS29 0x11C +#define SW7_CS30 0x11D + +#define SW8_CS1 0x11E +#define SW8_CS2 0x11F +#define SW8_CS3 0x120 +#define SW8_CS4 0x121 +#define SW8_CS5 0x122 +#define SW8_CS6 0x123 +#define SW8_CS7 0x124 +#define SW8_CS8 0x125 +#define SW8_CS9 0x126 +#define SW8_CS10 0x127 +#define SW8_CS11 0x128 +#define SW8_CS12 0x129 +#define SW8_CS13 0x12A +#define SW8_CS14 0x12B +#define SW8_CS15 0x12C +#define SW8_CS16 0x12D +#define SW8_CS17 0x12E +#define SW8_CS18 0x12F +#define SW8_CS19 0x130 +#define SW8_CS20 0x131 +#define SW8_CS21 0x132 +#define SW8_CS22 0x133 +#define SW8_CS23 0x134 +#define SW8_CS24 0x135 +#define SW8_CS25 0x136 +#define SW8_CS26 0x137 +#define SW8_CS27 0x138 +#define SW8_CS28 0x139 +#define SW8_CS29 0x13A +#define SW8_CS30 0x13B + +#define SW9_CS1 0x13C +#define SW9_CS2 0x13D +#define SW9_CS3 0x13E +#define SW9_CS4 0x13F +#define SW9_CS5 0x140 +#define SW9_CS6 0x141 +#define SW9_CS7 0x142 +#define SW9_CS8 0x143 +#define SW9_CS9 0x144 +#define SW9_CS10 0x145 +#define SW9_CS11 0x146 +#define SW9_CS12 0x147 +#define SW9_CS13 0x148 +#define SW9_CS14 0x149 +#define SW9_CS15 0x14A +#define SW9_CS16 0x14B +#define SW9_CS17 0x14C +#define SW9_CS18 0x14D +#define SW9_CS19 0x14E +#define SW9_CS20 0x14F +#define SW9_CS21 0x150 +#define SW9_CS22 0x151 +#define SW9_CS23 0x152 +#define SW9_CS24 0x153 +#define SW9_CS25 0x154 +#define SW9_CS26 0x155 +#define SW9_CS27 0x156 +#define SW9_CS28 0x157 +#define SW9_CS29 0x158 +#define SW9_CS30 0x159 + +#define SW1_CS31 0x15A +#define SW1_CS32 0x15B +#define SW1_CS33 0x15C +#define SW1_CS34 0x15D +#define SW1_CS35 0x15E +#define SW1_CS36 0x15F +#define SW1_CS37 0x160 +#define SW1_CS38 0x161 +#define SW1_CS39 0x162 + +#define SW2_CS31 0x163 +#define SW2_CS32 0x164 +#define SW2_CS33 0x165 +#define SW2_CS34 0x166 +#define SW2_CS35 0x167 +#define SW2_CS36 0x168 +#define SW2_CS37 0x169 +#define SW2_CS38 0x16A +#define SW2_CS39 0x16B + +#define SW3_CS31 0x16C +#define SW3_CS32 0x16D +#define SW3_CS33 0x16E +#define SW3_CS34 0x16F +#define SW3_CS35 0x170 +#define SW3_CS36 0x171 +#define SW3_CS37 0x172 +#define SW3_CS38 0x173 +#define SW3_CS39 0x174 + +#define SW4_CS31 0x175 +#define SW4_CS32 0x176 +#define SW4_CS33 0x177 +#define SW4_CS34 0x178 +#define SW4_CS35 0x179 +#define SW4_CS36 0x17A +#define SW4_CS37 0x17B +#define SW4_CS38 0x17C +#define SW4_CS39 0x17D + +#define SW5_CS31 0x17E +#define SW5_CS32 0x17F +#define SW5_CS33 0x180 +#define SW5_CS34 0x181 +#define SW5_CS35 0x182 +#define SW5_CS36 0x183 +#define SW5_CS37 0x184 +#define SW5_CS38 0x185 +#define SW5_CS39 0x186 + +#define SW6_CS31 0x187 +#define SW6_CS32 0x188 +#define SW6_CS33 0x189 +#define SW6_CS34 0x18A +#define SW6_CS35 0x18B +#define SW6_CS36 0x18C +#define SW6_CS37 0x18D +#define SW6_CS38 0x18E +#define SW6_CS39 0x18F + +#define SW7_CS31 0x190 +#define SW7_CS32 0x191 +#define SW7_CS33 0x192 +#define SW7_CS34 0x193 +#define SW7_CS35 0x194 +#define SW7_CS36 0x195 +#define SW7_CS37 0x196 +#define SW7_CS38 0x197 +#define SW7_CS39 0x198 + +#define SW8_CS31 0x199 +#define SW8_CS32 0x19A +#define SW8_CS33 0x19B +#define SW8_CS34 0x19C +#define SW8_CS35 0x19D +#define SW8_CS36 0x19E +#define SW8_CS37 0x19F +#define SW8_CS38 0x1A0 +#define SW8_CS39 0x1A1 + +#define SW9_CS31 0x1A2 +#define SW9_CS32 0x1A3 +#define SW9_CS33 0x1A4 +#define SW9_CS34 0x1A5 +#define SW9_CS35 0x1A6 +#define SW9_CS36 0x1A7 +#define SW9_CS37 0x1A8 +#define SW9_CS38 0x1A9 +#define SW9_CS39 0x1AA + +// DEPRECATED - DO NOT USE + +#define CS1_SW1 SW1_CS1 +#define CS2_SW1 SW1_CS2 +#define CS3_SW1 SW1_CS3 +#define CS4_SW1 SW1_CS4 +#define CS5_SW1 SW1_CS5 +#define CS6_SW1 SW1_CS6 +#define CS7_SW1 SW1_CS7 +#define CS8_SW1 SW1_CS8 +#define CS9_SW1 SW1_CS9 +#define CS10_SW1 SW1_CS10 +#define CS11_SW1 SW1_CS11 +#define CS12_SW1 SW1_CS12 +#define CS13_SW1 SW1_CS13 +#define CS14_SW1 SW1_CS14 +#define CS15_SW1 SW1_CS15 +#define CS16_SW1 SW1_CS16 +#define CS17_SW1 SW1_CS17 +#define CS18_SW1 SW1_CS18 +#define CS19_SW1 SW1_CS19 +#define CS20_SW1 SW1_CS20 +#define CS21_SW1 SW1_CS21 +#define CS22_SW1 SW1_CS22 +#define CS23_SW1 SW1_CS23 +#define CS24_SW1 SW1_CS24 +#define CS25_SW1 SW1_CS25 +#define CS26_SW1 SW1_CS26 +#define CS27_SW1 SW1_CS27 +#define CS28_SW1 SW1_CS28 +#define CS29_SW1 SW1_CS29 +#define CS30_SW1 SW1_CS30 + +#define CS1_SW2 SW2_CS1 +#define CS2_SW2 SW2_CS2 +#define CS3_SW2 SW2_CS3 +#define CS4_SW2 SW2_CS4 +#define CS5_SW2 SW2_CS5 +#define CS6_SW2 SW2_CS6 +#define CS7_SW2 SW2_CS7 +#define CS8_SW2 SW2_CS8 +#define CS9_SW2 SW2_CS9 +#define CS10_SW2 SW2_CS10 +#define CS11_SW2 SW2_CS11 +#define CS12_SW2 SW2_CS12 +#define CS13_SW2 SW2_CS13 +#define CS14_SW2 SW2_CS14 +#define CS15_SW2 SW2_CS15 +#define CS16_SW2 SW2_CS16 +#define CS17_SW2 SW2_CS17 +#define CS18_SW2 SW2_CS18 +#define CS19_SW2 SW2_CS19 +#define CS20_SW2 SW2_CS20 +#define CS21_SW2 SW2_CS21 +#define CS22_SW2 SW2_CS22 +#define CS23_SW2 SW2_CS23 +#define CS24_SW2 SW2_CS24 +#define CS25_SW2 SW2_CS25 +#define CS26_SW2 SW2_CS26 +#define CS27_SW2 SW2_CS27 +#define CS28_SW2 SW2_CS28 +#define CS29_SW2 SW2_CS29 +#define CS30_SW2 SW2_CS30 + +#define CS1_SW3 SW3_CS1 +#define CS2_SW3 SW3_CS2 +#define CS3_SW3 SW3_CS3 +#define CS4_SW3 SW3_CS4 +#define CS5_SW3 SW3_CS5 +#define CS6_SW3 SW3_CS6 +#define CS7_SW3 SW3_CS7 +#define CS8_SW3 SW3_CS8 +#define CS9_SW3 SW3_CS9 +#define CS10_SW3 SW3_CS10 +#define CS11_SW3 SW3_CS11 +#define CS12_SW3 SW3_CS12 +#define CS13_SW3 SW3_CS13 +#define CS14_SW3 SW3_CS14 +#define CS15_SW3 SW3_CS15 +#define CS16_SW3 SW3_CS16 +#define CS17_SW3 SW3_CS17 +#define CS18_SW3 SW3_CS18 +#define CS19_SW3 SW3_CS19 +#define CS20_SW3 SW3_CS20 +#define CS21_SW3 SW3_CS21 +#define CS22_SW3 SW3_CS22 +#define CS23_SW3 SW3_CS23 +#define CS24_SW3 SW3_CS24 +#define CS25_SW3 SW3_CS25 +#define CS26_SW3 SW3_CS26 +#define CS27_SW3 SW3_CS27 +#define CS28_SW3 SW3_CS28 +#define CS29_SW3 SW3_CS29 +#define CS30_SW3 SW3_CS30 + +#define CS1_SW4 SW4_CS1 +#define CS2_SW4 SW4_CS2 +#define CS3_SW4 SW4_CS3 +#define CS4_SW4 SW4_CS4 +#define CS5_SW4 SW4_CS5 +#define CS6_SW4 SW4_CS6 +#define CS7_SW4 SW4_CS7 +#define CS8_SW4 SW4_CS8 +#define CS9_SW4 SW4_CS9 +#define CS10_SW4 SW4_CS10 +#define CS11_SW4 SW4_CS11 +#define CS12_SW4 SW4_CS12 +#define CS13_SW4 SW4_CS13 +#define CS14_SW4 SW4_CS14 +#define CS15_SW4 SW4_CS15 +#define CS16_SW4 SW4_CS16 +#define CS17_SW4 SW4_CS17 +#define CS18_SW4 SW4_CS18 +#define CS19_SW4 SW4_CS19 +#define CS20_SW4 SW4_CS20 +#define CS21_SW4 SW4_CS21 +#define CS22_SW4 SW4_CS22 +#define CS23_SW4 SW4_CS23 +#define CS24_SW4 SW4_CS24 +#define CS25_SW4 SW4_CS25 +#define CS26_SW4 SW4_CS26 +#define CS27_SW4 SW4_CS27 +#define CS28_SW4 SW4_CS28 +#define CS29_SW4 SW4_CS29 +#define CS30_SW4 SW4_CS30 + +#define CS1_SW5 SW5_CS1 +#define CS2_SW5 SW5_CS2 +#define CS3_SW5 SW5_CS3 +#define CS4_SW5 SW5_CS4 +#define CS5_SW5 SW5_CS5 +#define CS6_SW5 SW5_CS6 +#define CS7_SW5 SW5_CS7 +#define CS8_SW5 SW5_CS8 +#define CS9_SW5 SW5_CS9 +#define CS10_SW5 SW5_CS10 +#define CS11_SW5 SW5_CS11 +#define CS12_SW5 SW5_CS12 +#define CS13_SW5 SW5_CS13 +#define CS14_SW5 SW5_CS14 +#define CS15_SW5 SW5_CS15 +#define CS16_SW5 SW5_CS16 +#define CS17_SW5 SW5_CS17 +#define CS18_SW5 SW5_CS18 +#define CS19_SW5 SW5_CS19 +#define CS20_SW5 SW5_CS20 +#define CS21_SW5 SW5_CS21 +#define CS22_SW5 SW5_CS22 +#define CS23_SW5 SW5_CS23 +#define CS24_SW5 SW5_CS24 +#define CS25_SW5 SW5_CS25 +#define CS26_SW5 SW5_CS26 +#define CS27_SW5 SW5_CS27 +#define CS28_SW5 SW5_CS28 +#define CS29_SW5 SW5_CS29 +#define CS30_SW5 SW5_CS30 + +#define CS1_SW6 SW6_CS1 +#define CS2_SW6 SW6_CS2 +#define CS3_SW6 SW6_CS3 +#define CS4_SW6 SW6_CS4 +#define CS5_SW6 SW6_CS5 +#define CS6_SW6 SW6_CS6 +#define CS7_SW6 SW6_CS7 +#define CS8_SW6 SW6_CS8 +#define CS9_SW6 SW6_CS9 +#define CS10_SW6 SW6_CS10 +#define CS11_SW6 SW6_CS11 +#define CS12_SW6 SW6_CS12 +#define CS13_SW6 SW6_CS13 +#define CS14_SW6 SW6_CS14 +#define CS15_SW6 SW6_CS15 +#define CS16_SW6 SW6_CS16 +#define CS17_SW6 SW6_CS17 +#define CS18_SW6 SW6_CS18 +#define CS19_SW6 SW6_CS19 +#define CS20_SW6 SW6_CS20 +#define CS21_SW6 SW6_CS21 +#define CS22_SW6 SW6_CS22 +#define CS23_SW6 SW6_CS23 +#define CS24_SW6 SW6_CS24 +#define CS25_SW6 SW6_CS25 +#define CS26_SW6 SW6_CS26 +#define CS27_SW6 SW6_CS27 +#define CS28_SW6 SW6_CS28 +#define CS29_SW6 SW6_CS29 +#define CS30_SW6 SW6_CS30 + +#define CS1_SW7 SW7_CS1 +#define CS2_SW7 SW7_CS2 +#define CS3_SW7 SW7_CS3 +#define CS4_SW7 SW7_CS4 +#define CS5_SW7 SW7_CS5 +#define CS6_SW7 SW7_CS6 +#define CS7_SW7 SW7_CS7 +#define CS8_SW7 SW7_CS8 +#define CS9_SW7 SW7_CS9 +#define CS10_SW7 SW7_CS10 +#define CS11_SW7 SW7_CS11 +#define CS12_SW7 SW7_CS12 +#define CS13_SW7 SW7_CS13 +#define CS14_SW7 SW7_CS14 +#define CS15_SW7 SW7_CS15 +#define CS16_SW7 SW7_CS16 +#define CS17_SW7 SW7_CS17 +#define CS18_SW7 SW7_CS18 +#define CS19_SW7 SW7_CS19 +#define CS20_SW7 SW7_CS20 +#define CS21_SW7 SW7_CS21 +#define CS22_SW7 SW7_CS22 +#define CS23_SW7 SW7_CS23 +#define CS24_SW7 SW7_CS24 +#define CS25_SW7 SW7_CS25 +#define CS26_SW7 SW7_CS26 +#define CS27_SW7 SW7_CS27 +#define CS28_SW7 SW7_CS28 +#define CS29_SW7 SW7_CS29 +#define CS30_SW7 SW7_CS30 + +#define CS1_SW8 SW8_CS1 +#define CS2_SW8 SW8_CS2 +#define CS3_SW8 SW8_CS3 +#define CS4_SW8 SW8_CS4 +#define CS5_SW8 SW8_CS5 +#define CS6_SW8 SW8_CS6 +#define CS7_SW8 SW8_CS7 +#define CS8_SW8 SW8_CS8 +#define CS9_SW8 SW8_CS9 +#define CS10_SW8 SW8_CS10 +#define CS11_SW8 SW8_CS11 +#define CS12_SW8 SW8_CS12 +#define CS13_SW8 SW8_CS13 +#define CS14_SW8 SW8_CS14 +#define CS15_SW8 SW8_CS15 +#define CS16_SW8 SW8_CS16 +#define CS17_SW8 SW8_CS17 +#define CS18_SW8 SW8_CS18 +#define CS19_SW8 SW8_CS19 +#define CS20_SW8 SW8_CS20 +#define CS21_SW8 SW8_CS21 +#define CS22_SW8 SW8_CS22 +#define CS23_SW8 SW8_CS23 +#define CS24_SW8 SW8_CS24 +#define CS25_SW8 SW8_CS25 +#define CS26_SW8 SW8_CS26 +#define CS27_SW8 SW8_CS27 +#define CS28_SW8 SW8_CS28 +#define CS29_SW8 SW8_CS29 +#define CS30_SW8 SW8_CS30 + +#define CS1_SW9 SW9_CS1 +#define CS2_SW9 SW9_CS2 +#define CS3_SW9 SW9_CS3 +#define CS4_SW9 SW9_CS4 +#define CS5_SW9 SW9_CS5 +#define CS6_SW9 SW9_CS6 +#define CS7_SW9 SW9_CS7 +#define CS8_SW9 SW9_CS8 +#define CS9_SW9 SW9_CS9 +#define CS10_SW9 SW9_CS10 +#define CS11_SW9 SW9_CS11 +#define CS12_SW9 SW9_CS12 +#define CS13_SW9 SW9_CS13 +#define CS14_SW9 SW9_CS14 +#define CS15_SW9 SW9_CS15 +#define CS16_SW9 SW9_CS16 +#define CS17_SW9 SW9_CS17 +#define CS18_SW9 SW9_CS18 +#define CS19_SW9 SW9_CS19 +#define CS20_SW9 SW9_CS20 +#define CS21_SW9 SW9_CS21 +#define CS22_SW9 SW9_CS22 +#define CS23_SW9 SW9_CS23 +#define CS24_SW9 SW9_CS24 +#define CS25_SW9 SW9_CS25 +#define CS26_SW9 SW9_CS26 +#define CS27_SW9 SW9_CS27 +#define CS28_SW9 SW9_CS28 +#define CS29_SW9 SW9_CS29 +#define CS30_SW9 SW9_CS30 + +#define CS31_SW1 SW1_CS31 +#define CS32_SW1 SW1_CS32 +#define CS33_SW1 SW1_CS33 +#define CS34_SW1 SW1_CS34 +#define CS35_SW1 SW1_CS35 +#define CS36_SW1 SW1_CS36 +#define CS37_SW1 SW1_CS37 +#define CS38_SW1 SW1_CS38 +#define CS39_SW1 SW1_CS39 + +#define CS31_SW2 SW2_CS31 +#define CS32_SW2 SW2_CS32 +#define CS33_SW2 SW2_CS33 +#define CS34_SW2 SW2_CS34 +#define CS35_SW2 SW2_CS35 +#define CS36_SW2 SW2_CS36 +#define CS37_SW2 SW2_CS37 +#define CS38_SW2 SW2_CS38 +#define CS39_SW2 SW2_CS39 + +#define CS31_SW3 SW3_CS31 +#define CS32_SW3 SW3_CS32 +#define CS33_SW3 SW3_CS33 +#define CS34_SW3 SW3_CS34 +#define CS35_SW3 SW3_CS35 +#define CS36_SW3 SW3_CS36 +#define CS37_SW3 SW3_CS37 +#define CS38_SW3 SW3_CS38 +#define CS39_SW3 SW3_CS39 + +#define CS31_SW4 SW4_CS31 +#define CS32_SW4 SW4_CS32 +#define CS33_SW4 SW4_CS33 +#define CS34_SW4 SW4_CS34 +#define CS35_SW4 SW4_CS35 +#define CS36_SW4 SW4_CS36 +#define CS37_SW4 SW4_CS37 +#define CS38_SW4 SW4_CS38 +#define CS39_SW4 SW4_CS39 + +#define CS31_SW5 SW5_CS31 +#define CS32_SW5 SW5_CS32 +#define CS33_SW5 SW5_CS33 +#define CS34_SW5 SW5_CS34 +#define CS35_SW5 SW5_CS35 +#define CS36_SW5 SW5_CS36 +#define CS37_SW5 SW5_CS37 +#define CS38_SW5 SW5_CS38 +#define CS39_SW5 SW5_CS39 + +#define CS31_SW6 SW6_CS31 +#define CS32_SW6 SW6_CS32 +#define CS33_SW6 SW6_CS33 +#define CS34_SW6 SW6_CS34 +#define CS35_SW6 SW6_CS35 +#define CS36_SW6 SW6_CS36 +#define CS37_SW6 SW6_CS37 +#define CS38_SW6 SW6_CS38 +#define CS39_SW6 SW6_CS39 + +#define CS31_SW7 SW7_CS31 +#define CS32_SW7 SW7_CS32 +#define CS33_SW7 SW7_CS33 +#define CS34_SW7 SW7_CS34 +#define CS35_SW7 SW7_CS35 +#define CS36_SW7 SW7_CS36 +#define CS37_SW7 SW7_CS37 +#define CS38_SW7 SW7_CS38 +#define CS39_SW7 SW7_CS39 + +#define CS31_SW8 SW8_CS31 +#define CS32_SW8 SW8_CS32 +#define CS33_SW8 SW8_CS33 +#define CS34_SW8 SW8_CS34 +#define CS35_SW8 SW8_CS35 +#define CS36_SW8 SW8_CS36 +#define CS37_SW8 SW8_CS37 +#define CS38_SW8 SW8_CS38 +#define CS39_SW8 SW8_CS39 + +#define CS31_SW9 SW9_CS31 +#define CS32_SW9 SW9_CS32 +#define CS33_SW9 SW9_CS33 +#define CS34_SW9 SW9_CS34 +#define CS35_SW9 SW9_CS35 +#define CS36_SW9 SW9_CS36 +#define CS37_SW9 SW9_CS37 +#define CS38_SW9 SW9_CS38 +#define CS39_SW9 SW9_CS39 diff --git a/drivers/led/issi/is31fl3742.h b/drivers/led/issi/is31fl3742.h deleted file mode 100644 index c96f12d0f1..0000000000 --- a/drivers/led/issi/is31fl3742.h +++ /dev/null @@ -1,299 +0,0 @@ -/* Copyright 2017 Jason Williams - * Copyright 2018 Jack Humbert - * Copyright 2018 Yiancar - * Copyright 2020 MelGeek - * Copyright 2021 MasterSpoon - * - * 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 2 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/>. - */ - -#pragma once - -// This is a 7-bit address, that gets left-shifted and bit 0 -// set to 0 for write, 1 for read (as per I2C protocol) -// The address will vary depending on your wiring: -// 00 <-> GND -// 01 <-> SCL -// 10 <-> SDA -// 11 <-> VCC -// ADDR represents A1:A0 of the 7-bit address. -// The result is: 0b01100(ADDR) -#ifndef DRIVER_ADDR_1 -# define DRIVER_ADDR_1 0b0110000 -#endif - -// Command Registers -#define ISSI_COMMANDREGISTER_WRITELOCK 0xFE -#define ISSI_COMMANDREGISTER 0xFD -#define ISSI_IDREGISTER 0xFC -#define ISSI_REGISTER_UNLOCK 0xC5 - -// Response Registers -#define ISSI_PAGE_PWM 0x00 -#define ISSI_PAGE_SCALING 0x02 -#define ISSI_PAGE_FUNCTION 0x04 - -// Registers under Function Register -#define ISSI_REG_CONFIGURATION 0x00 -#define ISSI_REG_GLOBALCURRENT 0x01 -#define ISSI_REG_PULLDOWNUP 0x02 -#define ISSI_REG_SSR 0x41 -#define ISSI_REG_RESET 0x3F -#define ISSI_REG_PWM_SET 0x36 - -// Set defaults for Function Registers -#ifndef ISSI_CONFIGURATION -# define ISSI_CONFIGURATION 0x31 -#endif -#ifndef ISSI_GLOBALCURRENT -# define ISSI_GLOBALCURRENT 0xFF -#endif -#ifndef ISSI_PULLDOWNUP -# define ISSI_PULLDOWNUP 0x55 -#endif -#ifndef ISSI_PWM_SET -# define ISSI_PWM_SET 0x00 -#endif - -// Set defaults for Spread Spectrum Register -#ifndef ISSI_SSR_1 -# define ISSI_SSR_1 0x00 -#endif -#ifndef ISSI_SSR_2 -# define ISSI_SSR_2 0x00 -#endif -#ifndef ISSI_SSR_3 -# define ISSI_SSR_3 0x00 -#endif -#ifndef ISSI_SSR_4 -# define ISSI_SSR_4 0x00 -#endif - -// Set defaults for Scaling registers -#ifndef ISSI_SCAL_RED -# define ISSI_SCAL_RED 0xFF -#endif -#ifndef ISSI_SCAL_BLUE -# define ISSI_SCAL_BLUE 0xFF -#endif -#ifndef ISSI_SCAL_GREEN -# define ISSI_SCAL_GREEN 0xFF -#endif -#define ISSI_SCAL_RED_OFF 0x00 -#define ISSI_SCAL_GREEN_OFF 0x00 -#define ISSI_SCAL_BLUE_OFF 0x00 - -#ifndef ISSI_SCAL_LED -# define ISSI_SCAL_LED 0xFF -#endif -#define ISSI_SCAL_LED_OFF 0x00 - -// Set buffer sizes -#define ISSI_MAX_LEDS 180 -#define ISSI_SCALING_SIZE 180 -#define ISSI_PWM_TRF_SIZE 18 -#define ISSI_SCALING_TRF_SIZE 18 - -// Location of 1st bit for PWM and Scaling registers -#define ISSI_PWM_REG_1ST 0x00 -#define ISSI_SCL_REG_1ST 0x00 - -// Map CS SW locations to order in PWM / Scaling buffers -// This matches the ORDER in the Datasheet Register not the POSITION -// It will always count from 0x00 to (ISSI_MAX_LEDS - 1) -#define CS1_SW1 0x00 -#define CS2_SW1 0x01 -#define CS3_SW1 0x02 -#define CS4_SW1 0x03 -#define CS5_SW1 0x04 -#define CS6_SW1 0x05 -#define CS7_SW1 0x06 -#define CS8_SW1 0x07 -#define CS9_SW1 0x08 -#define CS10_SW1 0x09 -#define CS11_SW1 0x0A -#define CS12_SW1 0x0B -#define CS13_SW1 0x0C -#define CS14_SW1 0x0D -#define CS15_SW1 0x0E -#define CS16_SW1 0x0F -#define CS17_SW1 0x10 -#define CS18_SW1 0x11 -#define CS19_SW1 0x12 -#define CS20_SW1 0x13 -#define CS21_SW1 0x14 -#define CS22_SW1 0x15 -#define CS23_SW1 0x16 -#define CS24_SW1 0x17 -#define CS25_SW1 0x18 -#define CS26_SW1 0x19 -#define CS27_SW1 0x1A -#define CS28_SW1 0x1B -#define CS29_SW1 0x1C -#define CS30_SW1 0x1D - -#define CS1_SW2 0x1E -#define CS2_SW2 0x1F -#define CS3_SW2 0x20 -#define CS4_SW2 0x21 -#define CS5_SW2 0x22 -#define CS6_SW2 0x23 -#define CS7_SW2 0x24 -#define CS8_SW2 0x25 -#define CS9_SW2 0x26 -#define CS10_SW2 0x27 -#define CS11_SW2 0x28 -#define CS12_SW2 0x29 -#define CS13_SW2 0x2A -#define CS14_SW2 0x2B -#define CS15_SW2 0x2C -#define CS16_SW2 0x2D -#define CS17_SW2 0x2E -#define CS18_SW2 0x2F -#define CS19_SW2 0x30 -#define CS20_SW2 0x31 -#define CS21_SW2 0x32 -#define CS22_SW2 0x33 -#define CS23_SW2 0x34 -#define CS24_SW2 0x35 -#define CS25_SW2 0x36 -#define CS26_SW2 0x37 -#define CS27_SW2 0x38 -#define CS28_SW2 0x39 -#define CS29_SW2 0x3A -#define CS30_SW2 0x3B - -#define CS1_SW3 0x3C -#define CS2_SW3 0x3D -#define CS3_SW3 0x3E -#define CS4_SW3 0x3F -#define CS5_SW3 0x40 -#define CS6_SW3 0x41 -#define CS7_SW3 0x42 -#define CS8_SW3 0x43 -#define CS9_SW3 0x44 -#define CS10_SW3 0x45 -#define CS11_SW3 0x46 -#define CS12_SW3 0x47 -#define CS13_SW3 0x48 -#define CS14_SW3 0x49 -#define CS15_SW3 0x4A -#define CS16_SW3 0x4B -#define CS17_SW3 0x4C -#define CS18_SW3 0x4D -#define CS19_SW3 0x4E -#define CS20_SW3 0x4F -#define CS21_SW3 0x50 -#define CS22_SW3 0x51 -#define CS23_SW3 0x52 -#define CS24_SW3 0x53 -#define CS25_SW3 0x54 -#define CS26_SW3 0x55 -#define CS27_SW3 0x56 -#define CS28_SW3 0x57 -#define CS29_SW3 0x58 -#define CS30_SW3 0x59 - -#define CS1_SW4 0x5A -#define CS2_SW4 0x5B -#define CS3_SW4 0x5C -#define CS4_SW4 0x5D -#define CS5_SW4 0x5E -#define CS6_SW4 0x5F -#define CS7_SW4 0x60 -#define CS8_SW4 0x61 -#define CS9_SW4 0x62 -#define CS10_SW4 0x63 -#define CS11_SW4 0x64 -#define CS12_SW4 0x65 -#define CS13_SW4 0x66 -#define CS14_SW4 0x67 -#define CS15_SW4 0x68 -#define CS16_SW4 0x69 -#define CS17_SW4 0x6A -#define CS18_SW4 0x6B -#define CS19_SW4 0x6C -#define CS20_SW4 0x6D -#define CS21_SW4 0x6E -#define CS22_SW4 0x6F -#define CS23_SW4 0x70 -#define CS24_SW4 0x71 -#define CS25_SW4 0x72 -#define CS26_SW4 0x73 -#define CS27_SW4 0x74 -#define CS28_SW4 0x75 -#define CS29_SW4 0x76 -#define CS30_SW4 0x77 - -#define CS1_SW5 0x78 -#define CS2_SW5 0x79 -#define CS3_SW5 0x7A -#define CS4_SW5 0x7B -#define CS5_SW5 0x7C -#define CS6_SW5 0x7D -#define CS7_SW5 0x7E -#define CS8_SW5 0x7F -#define CS9_SW5 0x80 -#define CS10_SW5 0x81 -#define CS11_SW5 0x82 -#define CS12_SW5 0x83 -#define CS13_SW5 0x84 -#define CS14_SW5 0x85 -#define CS15_SW5 0x86 -#define CS16_SW5 0x87 -#define CS17_SW5 0x88 -#define CS18_SW5 0x89 -#define CS19_SW5 0x8A -#define CS20_SW5 0x8B -#define CS21_SW5 0x8C -#define CS22_SW5 0x8D -#define CS23_SW5 0x8E -#define CS24_SW5 0x8F -#define CS25_SW5 0x90 -#define CS26_SW5 0x91 -#define CS27_SW5 0x92 -#define CS28_SW5 0x93 -#define CS29_SW5 0x94 -#define CS30_SW5 0x95 - -#define CS1_SW6 0x96 -#define CS2_SW6 0x97 -#define CS3_SW6 0x98 -#define CS4_SW6 0x99 -#define CS5_SW6 0x9A -#define CS6_SW6 0x9B -#define CS7_SW6 0x9C -#define CS8_SW6 0x9D -#define CS9_SW6 0x9E -#define CS10_SW6 0x9F -#define CS11_SW6 0xA0 -#define CS12_SW6 0xA1 -#define CS13_SW6 0xA2 -#define CS14_SW6 0xA3 -#define CS15_SW6 0xA4 -#define CS16_SW6 0xA5 -#define CS17_SW6 0xA6 -#define CS18_SW6 0xA7 -#define CS19_SW6 0xA8 -#define CS20_SW6 0xA9 -#define CS21_SW6 0xAA -#define CS22_SW6 0xAB -#define CS23_SW6 0xAC -#define CS24_SW6 0xAD -#define CS25_SW6 0xAE -#define CS26_SW6 0xAF -#define CS27_SW6 0xB0 -#define CS28_SW6 0xB1 -#define CS29_SW6 0xB2 -#define CS30_SW6 0xB3 diff --git a/drivers/led/issi/is31fl3742a-mono.c b/drivers/led/issi/is31fl3742a-mono.c new file mode 100644 index 0000000000..c63db1a7fc --- /dev/null +++ b/drivers/led/issi/is31fl3742a-mono.c @@ -0,0 +1,221 @@ +/* Copyright 2017 Jason Williams + * Copyright 2018 Jack Humbert + * Copyright 2018 Yiancar + * Copyright 2020 MelGeek + * Copyright 2021 MasterSpoon + * + * 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 2 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/>. + */ + +#include "is31fl3742a-mono.h" +#include "i2c_master.h" +#include "gpio.h" +#include "wait.h" + +#define IS31FL3742A_PWM_REGISTER_COUNT 180 +#define IS31FL3742A_SCALING_REGISTER_COUNT 180 + +#ifndef IS31FL3742A_I2C_TIMEOUT +# define IS31FL3742A_I2C_TIMEOUT 100 +#endif + +#ifndef IS31FL3742A_I2C_PERSISTENCE +# define IS31FL3742A_I2C_PERSISTENCE 0 +#endif + +#ifndef IS31FL3742A_CONFIGURATION +# define IS31FL3742A_CONFIGURATION 0x31 +#endif + +#ifndef IS31FL3742A_PWM_FREQUENCY +# define IS31FL3742A_PWM_FREQUENCY IS31FL3742A_PWM_FREQUENCY_29K_HZ +#endif + +#ifndef IS31FL3742A_SW_PULLDOWN +# define IS31FL3742A_SW_PULLDOWN IS31FL3742A_PDR_8K_OHM +#endif + +#ifndef IS31FL3742A_CS_PULLUP +# define IS31FL3742A_CS_PULLUP IS31FL3742A_PUR_8K_OHM +#endif + +#ifndef IS31FL3742A_GLOBAL_CURRENT +# define IS31FL3742A_GLOBAL_CURRENT 0xFF +#endif + +const uint8_t i2c_addresses[IS31FL3742A_DRIVER_COUNT] = { + IS31FL3742A_I2C_ADDRESS_1, +#ifdef IS31FL3742A_I2C_ADDRESS_2 + IS31FL3742A_I2C_ADDRESS_2, +# ifdef IS31FL3742A_I2C_ADDRESS_3 + IS31FL3742A_I2C_ADDRESS_3, +# ifdef IS31FL3742A_I2C_ADDRESS_4 + IS31FL3742A_I2C_ADDRESS_4, +# endif +# endif +#endif +}; + +typedef struct is31fl3742a_driver_t { + uint8_t pwm_buffer[IS31FL3742A_PWM_REGISTER_COUNT]; + bool pwm_buffer_dirty; + uint8_t scaling_buffer[IS31FL3742A_SCALING_REGISTER_COUNT]; + bool scaling_buffer_dirty; +} PACKED is31fl3742a_driver_t; + +is31fl3742a_driver_t driver_buffers[IS31FL3742A_DRIVER_COUNT] = {{ + .pwm_buffer = {0}, + .pwm_buffer_dirty = false, + .scaling_buffer = {0}, + .scaling_buffer_dirty = false, +}}; + +void is31fl3742a_write_register(uint8_t index, uint8_t reg, uint8_t data) { +#if IS31FL3742A_I2C_PERSISTENCE > 0 + for (uint8_t i = 0; i < IS31FL3742A_I2C_PERSISTENCE; i++) { + if (i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3742A_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break; + } +#else + i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3742A_I2C_TIMEOUT); +#endif +} + +void is31fl3742a_select_page(uint8_t index, uint8_t page) { + is31fl3742a_write_register(index, IS31FL3742A_REG_COMMAND_WRITE_LOCK, IS31FL3742A_COMMAND_WRITE_LOCK_MAGIC); + is31fl3742a_write_register(index, IS31FL3742A_REG_COMMAND, page); +} + +void is31fl3742a_write_pwm_buffer(uint8_t index) { + // Assumes page 0 is already selected. + // Transmit PWM registers in 6 transfers of 30 bytes. + + // Iterate over the pwm_buffer contents at 30 byte intervals. + for (uint8_t i = 0; i < IS31FL3742A_PWM_REGISTER_COUNT; i += 30) { +#if IS31FL3742A_I2C_PERSISTENCE > 0 + for (uint8_t j = 0; j < IS31FL3742A_I2C_PERSISTENCE; j++) { + if (i2c_write_register(i2c_addresses[index] << 1, i, driver_buffers[index].pwm_buffer + i, 30, IS31FL3742A_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break; + } +#else + i2c_write_register(i2c_addresses[index] << 1, i, driver_buffers[index].pwm_buffer + i, 30, IS31FL3742A_I2C_TIMEOUT); +#endif + } +} + +void is31fl3742a_init_drivers(void) { + i2c_init(); + +#if defined(IS31FL3742A_SDB_PIN) + gpio_set_pin_output(IS31FL3742A_SDB_PIN); + gpio_write_pin_high(IS31FL3742A_SDB_PIN); +#endif + + for (uint8_t i = 0; i < IS31FL3742A_DRIVER_COUNT; i++) { + is31fl3742a_init(i); + } + + for (int i = 0; i < IS31FL3742A_LED_COUNT; i++) { + is31fl3742a_set_scaling_register(i, 0xFF); + } + + for (uint8_t i = 0; i < IS31FL3742A_DRIVER_COUNT; i++) { + is31fl3742a_update_scaling_registers(i); + } +} + +void is31fl3742a_init(uint8_t index) { + // In order to avoid the LEDs being driven with garbage data + // in the LED driver's PWM registers, shutdown is enabled last. + // Set up the mode and other settings, clear the PWM registers, + // then disable software shutdown. + + is31fl3742a_select_page(index, IS31FL3742A_COMMAND_SCALING); + + // Turn off all LEDs. + for (uint8_t i = 0; i < IS31FL3742A_SCALING_REGISTER_COUNT; i++) { + is31fl3742a_write_register(index, i, 0x00); + } + + is31fl3742a_select_page(index, IS31FL3742A_COMMAND_PWM); + + for (uint8_t i = 0; i < IS31FL3742A_PWM_REGISTER_COUNT; i++) { + is31fl3742a_write_register(index, i, 0x00); + } + + is31fl3742a_select_page(index, IS31FL3742A_COMMAND_FUNCTION); + + is31fl3742a_write_register(index, IS31FL3742A_FUNCTION_REG_PULLDOWNUP, (IS31FL3742A_SW_PULLDOWN << 4) | IS31FL3742A_CS_PULLUP); + is31fl3742a_write_register(index, IS31FL3742A_FUNCTION_REG_GLOBAL_CURRENT, IS31FL3742A_GLOBAL_CURRENT); + is31fl3742a_write_register(index, IS31FL3742A_FUNCTION_REG_PWM_FREQUENCY, (IS31FL3742A_PWM_FREQUENCY & 0b0111)); + is31fl3742a_write_register(index, IS31FL3742A_FUNCTION_REG_CONFIGURATION, IS31FL3742A_CONFIGURATION); + + // Wait 10ms to ensure the device has woken up. + wait_ms(10); +} + +void is31fl3742a_set_value(int index, uint8_t value) { + is31fl3742a_led_t led; + + if (index >= 0 && index < IS31FL3742A_LED_COUNT) { + memcpy_P(&led, (&g_is31fl3742a_leds[index]), sizeof(led)); + + if (driver_buffers[led.driver].pwm_buffer[led.v] == value) { + return; + } + + driver_buffers[led.driver].pwm_buffer[led.v] = value; + driver_buffers[led.driver].pwm_buffer_dirty = true; + } +} + +void is31fl3742a_set_value_all(uint8_t value) { + for (int i = 0; i < IS31FL3742A_LED_COUNT; i++) { + is31fl3742a_set_value(i, value); + } +} + +void is31fl3742a_set_scaling_register(uint8_t index, uint8_t value) { + is31fl3742a_led_t led; + memcpy_P(&led, (&g_is31fl3742a_leds[index]), sizeof(led)); + + driver_buffers[led.driver].scaling_buffer[led.v] = value; + driver_buffers[led.driver].scaling_buffer_dirty = true; +} + +void is31fl3742a_update_pwm_buffers(uint8_t index) { + if (driver_buffers[index].pwm_buffer_dirty) { + is31fl3742a_select_page(index, IS31FL3742A_COMMAND_PWM); + + is31fl3742a_write_pwm_buffer(index); + + driver_buffers[index].pwm_buffer_dirty = false; + } +} + +void is31fl3742a_update_scaling_registers(uint8_t index) { + if (driver_buffers[index].scaling_buffer_dirty) { + is31fl3742a_select_page(index, IS31FL3742A_COMMAND_SCALING); + + for (uint8_t i = 0; i < IS31FL3742A_SCALING_REGISTER_COUNT; i++) { + is31fl3742a_write_register(index, i, driver_buffers[index].scaling_buffer[i]); + } + + driver_buffers[index].scaling_buffer_dirty = false; + } +} + +void is31fl3742a_flush(void) { + for (uint8_t i = 0; i < IS31FL3742A_DRIVER_COUNT; i++) { + is31fl3742a_update_pwm_buffers(i); + } +} diff --git a/drivers/led/issi/is31fl3742a-mono.h b/drivers/led/issi/is31fl3742a-mono.h new file mode 100644 index 0000000000..5c8938906c --- /dev/null +++ b/drivers/led/issi/is31fl3742a-mono.h @@ -0,0 +1,296 @@ +/* Copyright 2017 Jason Williams + * Copyright 2018 Jack Humbert + * Copyright 2018 Yiancar + * Copyright 2020 MelGeek + * Copyright 2021 MasterSpoon + * + * 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 2 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/>. + */ + +#pragma once + +#include <stdint.h> +#include <stdbool.h> +#include "progmem.h" +#include "util.h" + +#define IS31FL3742A_REG_INTERRUPT_MASK 0xF0 +#define IS31FL3742A_REG_INTERRUPT_STATUS 0xF1 +#define IS31FL3742A_REG_ID 0xFC + +#define IS31FL3742A_REG_COMMAND 0xFD + +#define IS31FL3742A_COMMAND_PWM 0x00 +#define IS31FL3742A_COMMAND_SCALING 0x02 +#define IS31FL3742A_COMMAND_FUNCTION 0x04 + +#define IS31FL3742A_FUNCTION_REG_CONFIGURATION 0x00 +#define IS31FL3742A_FUNCTION_REG_GLOBAL_CURRENT 0x01 +#define IS31FL3742A_FUNCTION_REG_PULLDOWNUP 0x02 +#define IS31FL3742A_FUNCTION_REG_PWM_FREQUENCY 0x36 +#define IS31FL3742A_FUNCTION_REG_RESET 0x3F +#define IS31FL3742A_FUNCTION_REG_SPREAD_SPECTRUM 0x41 + +#define IS31FL3742A_REG_COMMAND_WRITE_LOCK 0xFE +#define IS31FL3742A_COMMAND_WRITE_LOCK_MAGIC 0xC5 + +#define IS31FL3742A_I2C_ADDRESS_GND 0x30 +#define IS31FL3742A_I2C_ADDRESS_SCL 0x31 +#define IS31FL3742A_I2C_ADDRESS_SDA 0x32 +#define IS31FL3742A_I2C_ADDRESS_VCC 0x33 + +#if defined(LED_MATRIX_IS31FL3742A) +# define IS31FL3742A_LED_COUNT LED_MATRIX_LED_COUNT +#endif + +#if defined(IS31FL3742A_I2C_ADDRESS_4) +# define IS31FL3742A_DRIVER_COUNT 4 +#elif defined(IS31FL3742A_I2C_ADDRESS_3) +# define IS31FL3742A_DRIVER_COUNT 3 +#elif defined(IS31FL3742A_I2C_ADDRESS_2) +# define IS31FL3742A_DRIVER_COUNT 2 +#elif defined(IS31FL3742A_I2C_ADDRESS_1) +# define IS31FL3742A_DRIVER_COUNT 1 +#endif + +typedef struct is31fl3742a_led_t { + uint8_t driver : 2; + uint8_t v; +} PACKED is31fl3742a_led_t; + +extern const is31fl3742a_led_t PROGMEM g_is31fl3742a_leds[IS31FL3742A_LED_COUNT]; + +void is31fl3742a_init_drivers(void); +void is31fl3742a_init(uint8_t index); +void is31fl3742a_write_register(uint8_t index, uint8_t reg, uint8_t data); +void is31fl3742a_select_page(uint8_t index, uint8_t page); + +void is31fl3742a_set_value(int index, uint8_t value); +void is31fl3742a_set_value_all(uint8_t value); + +void is31fl3742a_set_scaling_register(uint8_t index, uint8_t value); + +void is31fl3742a_update_pwm_buffers(uint8_t index); +void is31fl3742a_update_scaling_registers(uint8_t index); + +void is31fl3742a_flush(void); + +#define IS31FL3742A_PDR_0_OHM 0b000 // No pull-down resistor +#define IS31FL3742A_PDR_0K5_OHM 0b001 // 0.5 kOhm resistor +#define IS31FL3742A_PDR_1K_OHM 0b010 // 1 kOhm resistor +#define IS31FL3742A_PDR_2K_OHM 0b011 // 2 kOhm resistor +#define IS31FL3742A_PDR_4K_OHM 0b100 // 4 kOhm resistor +#define IS31FL3742A_PDR_8K_OHM 0b101 // 8 kOhm resistor +#define IS31FL3742A_PDR_16K_OHM 0b110 // 16 kOhm resistor +#define IS31FL3742A_PDR_32K_OHM 0b111 // 32 kOhm resistor + +#define IS31FL3742A_PUR_0_OHM 0b000 // No pull-up resistor +#define IS31FL3742A_PUR_0K5_OHM 0b001 // 0.5 kOhm resistor +#define IS31FL3742A_PUR_1K_OHM 0b010 // 1 kOhm resistor +#define IS31FL3742A_PUR_2K_OHM 0b011 // 2 kOhm resistor +#define IS31FL3742A_PUR_4K_OHM 0b100 // 4 kOhm resistor +#define IS31FL3742A_PUR_8K_OHM 0b101 // 8 kOhm resistor +#define IS31FL3742A_PUR_16K_OHM 0b110 // 16 kOhm resistor +#define IS31FL3742A_PUR_32K_OHM 0b111 // 32 kOhm resistor + +#define IS31FL3742A_PWM_FREQUENCY_29K_HZ 0b0000 +#define IS31FL3742A_PWM_FREQUENCY_3K6_HZ 0b0011 +#define IS31FL3742A_PWM_FREQUENCY_1K8_HZ 0b0111 +#define IS31FL3742A_PWM_FREQUENCY_900_HZ 0b1011 + +#define SW1_CS1 0x00 +#define SW1_CS2 0x01 +#define SW1_CS3 0x02 +#define SW1_CS4 0x03 +#define SW1_CS5 0x04 +#define SW1_CS6 0x05 +#define SW1_CS7 0x06 +#define SW1_CS8 0x07 +#define SW1_CS9 0x08 +#define SW1_CS10 0x09 +#define SW1_CS11 0x0A +#define SW1_CS12 0x0B +#define SW1_CS13 0x0C +#define SW1_CS14 0x0D +#define SW1_CS15 0x0E +#define SW1_CS16 0x0F +#define SW1_CS17 0x10 +#define SW1_CS18 0x11 +#define SW1_CS19 0x12 +#define SW1_CS20 0x13 +#define SW1_CS21 0x14 +#define SW1_CS22 0x15 +#define SW1_CS23 0x16 +#define SW1_CS24 0x17 +#define SW1_CS25 0x18 +#define SW1_CS26 0x19 +#define SW1_CS27 0x1A +#define SW1_CS28 0x1B +#define SW1_CS29 0x1C +#define SW1_CS30 0x1D + +#define SW2_CS1 0x1E +#define SW2_CS2 0x1F +#define SW2_CS3 0x20 +#define SW2_CS4 0x21 +#define SW2_CS5 0x22 +#define SW2_CS6 0x23 +#define SW2_CS7 0x24 +#define SW2_CS8 0x25 +#define SW2_CS9 0x26 +#define SW2_CS10 0x27 +#define SW2_CS11 0x28 +#define SW2_CS12 0x29 +#define SW2_CS13 0x2A +#define SW2_CS14 0x2B +#define SW2_CS15 0x2C +#define SW2_CS16 0x2D +#define SW2_CS17 0x2E +#define SW2_CS18 0x2F +#define SW2_CS19 0x30 +#define SW2_CS20 0x31 +#define SW2_CS21 0x32 +#define SW2_CS22 0x33 +#define SW2_CS23 0x34 +#define SW2_CS24 0x35 +#define SW2_CS25 0x36 +#define SW2_CS26 0x37 +#define SW2_CS27 0x38 +#define SW2_CS28 0x39 +#define SW2_CS29 0x3A +#define SW2_CS30 0x3B + +#define SW3_CS1 0x3C +#define SW3_CS2 0x3D +#define SW3_CS3 0x3E +#define SW3_CS4 0x3F +#define SW3_CS5 0x40 +#define SW3_CS6 0x41 +#define SW3_CS7 0x42 +#define SW3_CS8 0x43 +#define SW3_CS9 0x44 +#define SW3_CS10 0x45 +#define SW3_CS11 0x46 +#define SW3_CS12 0x47 +#define SW3_CS13 0x48 +#define SW3_CS14 0x49 +#define SW3_CS15 0x4A +#define SW3_CS16 0x4B +#define SW3_CS17 0x4C +#define SW3_CS18 0x4D +#define SW3_CS19 0x4E +#define SW3_CS20 0x4F +#define SW3_CS21 0x50 +#define SW3_CS22 0x51 +#define SW3_CS23 0x52 +#define SW3_CS24 0x53 +#define SW3_CS25 0x54 +#define SW3_CS26 0x55 +#define SW3_CS27 0x56 +#define SW3_CS28 0x57 +#define SW3_CS29 0x58 +#define SW3_CS30 0x59 + +#define SW4_CS1 0x5A +#define SW4_CS2 0x5B +#define SW4_CS3 0x5C +#define SW4_CS4 0x5D +#define SW4_CS5 0x5E +#define SW4_CS6 0x5F +#define SW4_CS7 0x60 +#define SW4_CS8 0x61 +#define SW4_CS9 0x62 +#define SW4_CS10 0x63 +#define SW4_CS11 0x64 +#define SW4_CS12 0x65 +#define SW4_CS13 0x66 +#define SW4_CS14 0x67 +#define SW4_CS15 0x68 +#define SW4_CS16 0x69 +#define SW4_CS17 0x6A +#define SW4_CS18 0x6B +#define SW4_CS19 0x6C +#define SW4_CS20 0x6D +#define SW4_CS21 0x6E +#define SW4_CS22 0x6F +#define SW4_CS23 0x70 +#define SW4_CS24 0x71 +#define SW4_CS25 0x72 +#define SW4_CS26 0x73 +#define SW4_CS27 0x74 +#define SW4_CS28 0x75 +#define SW4_CS29 0x76 +#define SW4_CS30 0x77 + +#define SW5_CS1 0x78 +#define SW5_CS2 0x79 +#define SW5_CS3 0x7A +#define SW5_CS4 0x7B +#define SW5_CS5 0x7C +#define SW5_CS6 0x7D +#define SW5_CS7 0x7E +#define SW5_CS8 0x7F +#define SW5_CS9 0x80 +#define SW5_CS10 0x81 +#define SW5_CS11 0x82 +#define SW5_CS12 0x83 +#define SW5_CS13 0x84 +#define SW5_CS14 0x85 +#define SW5_CS15 0x86 +#define SW5_CS16 0x87 +#define SW5_CS17 0x88 +#define SW5_CS18 0x89 +#define SW5_CS19 0x8A +#define SW5_CS20 0x8B +#define SW5_CS21 0x8C +#define SW5_CS22 0x8D +#define SW5_CS23 0x8E +#define SW5_CS24 0x8F +#define SW5_CS25 0x90 +#define SW5_CS26 0x91 +#define SW5_CS27 0x92 +#define SW5_CS28 0x93 +#define SW5_CS29 0x94 +#define SW5_CS30 0x95 + +#define SW6_CS1 0x96 +#define SW6_CS2 0x97 +#define SW6_CS3 0x98 +#define SW6_CS4 0x99 +#define SW6_CS5 0x9A +#define SW6_CS6 0x9B +#define SW6_CS7 0x9C +#define SW6_CS8 0x9D +#define SW6_CS9 0x9E +#define SW6_CS10 0x9F +#define SW6_CS11 0xA0 +#define SW6_CS12 0xA1 +#define SW6_CS13 0xA2 +#define SW6_CS14 0xA3 +#define SW6_CS15 0xA4 +#define SW6_CS16 0xA5 +#define SW6_CS17 0xA6 +#define SW6_CS18 0xA7 +#define SW6_CS19 0xA8 +#define SW6_CS20 0xA9 +#define SW6_CS21 0xAA +#define SW6_CS22 0xAB +#define SW6_CS23 0xAC +#define SW6_CS24 0xAD +#define SW6_CS25 0xAE +#define SW6_CS26 0xAF +#define SW6_CS27 0xB0 +#define SW6_CS28 0xB1 +#define SW6_CS29 0xB2 +#define SW6_CS30 0xB3 diff --git a/drivers/led/issi/is31fl3742a.c b/drivers/led/issi/is31fl3742a.c new file mode 100644 index 0000000000..b8e9a58759 --- /dev/null +++ b/drivers/led/issi/is31fl3742a.c @@ -0,0 +1,225 @@ +/* Copyright 2017 Jason Williams + * Copyright 2018 Jack Humbert + * Copyright 2018 Yiancar + * Copyright 2020 MelGeek + * Copyright 2021 MasterSpoon + * + * 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 2 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/>. + */ + +#include "is31fl3742a.h" +#include "i2c_master.h" +#include "gpio.h" +#include "wait.h" + +#define IS31FL3742A_PWM_REGISTER_COUNT 180 +#define IS31FL3742A_SCALING_REGISTER_COUNT 180 + +#ifndef IS31FL3742A_I2C_TIMEOUT +# define IS31FL3742A_I2C_TIMEOUT 100 +#endif + +#ifndef IS31FL3742A_I2C_PERSISTENCE +# define IS31FL3742A_I2C_PERSISTENCE 0 +#endif + +#ifndef IS31FL3742A_CONFIGURATION +# define IS31FL3742A_CONFIGURATION 0x31 +#endif + +#ifndef IS31FL3742A_PWM_FREQUENCY +# define IS31FL3742A_PWM_FREQUENCY IS31FL3742A_PWM_FREQUENCY_29K_HZ +#endif + +#ifndef IS31FL3742A_SW_PULLDOWN +# define IS31FL3742A_SW_PULLDOWN IS31FL3742A_PDR_8K_OHM +#endif + +#ifndef IS31FL3742A_CS_PULLUP +# define IS31FL3742A_CS_PULLUP IS31FL3742A_PUR_8K_OHM +#endif + +#ifndef IS31FL3742A_GLOBAL_CURRENT +# define IS31FL3742A_GLOBAL_CURRENT 0xFF +#endif + +const uint8_t i2c_addresses[IS31FL3742A_DRIVER_COUNT] = { + IS31FL3742A_I2C_ADDRESS_1, +#ifdef IS31FL3742A_I2C_ADDRESS_2 + IS31FL3742A_I2C_ADDRESS_2, +# ifdef IS31FL3742A_I2C_ADDRESS_3 + IS31FL3742A_I2C_ADDRESS_3, +# ifdef IS31FL3742A_I2C_ADDRESS_4 + IS31FL3742A_I2C_ADDRESS_4, +# endif +# endif +#endif +}; + +typedef struct is31fl3742a_driver_t { + uint8_t pwm_buffer[IS31FL3742A_PWM_REGISTER_COUNT]; + bool pwm_buffer_dirty; + uint8_t scaling_buffer[IS31FL3742A_SCALING_REGISTER_COUNT]; + bool scaling_buffer_dirty; +} PACKED is31fl3742a_driver_t; + +is31fl3742a_driver_t driver_buffers[IS31FL3742A_DRIVER_COUNT] = {{ + .pwm_buffer = {0}, + .pwm_buffer_dirty = false, + .scaling_buffer = {0}, + .scaling_buffer_dirty = false, +}}; + +void is31fl3742a_write_register(uint8_t index, uint8_t reg, uint8_t data) { +#if IS31FL3742A_I2C_PERSISTENCE > 0 + for (uint8_t i = 0; i < IS31FL3742A_I2C_PERSISTENCE; i++) { + if (i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3742A_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break; + } +#else + i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3742A_I2C_TIMEOUT); +#endif +} + +void is31fl3742a_select_page(uint8_t index, uint8_t page) { + is31fl3742a_write_register(index, IS31FL3742A_REG_COMMAND_WRITE_LOCK, IS31FL3742A_COMMAND_WRITE_LOCK_MAGIC); + is31fl3742a_write_register(index, IS31FL3742A_REG_COMMAND, page); +} + +void is31fl3742a_write_pwm_buffer(uint8_t index) { + // Assumes page 0 is already selected. + // Transmit PWM registers in 6 transfers of 30 bytes. + + // Iterate over the pwm_buffer contents at 30 byte intervals. + for (uint8_t i = 0; i < IS31FL3742A_PWM_REGISTER_COUNT; i += 30) { +#if IS31FL3742A_I2C_PERSISTENCE > 0 + for (uint8_t j = 0; j < IS31FL3742A_I2C_PERSISTENCE; j++) { + if (i2c_write_register(i2c_addresses[index] << 1, i, driver_buffers[index].pwm_buffer + i, 30, IS31FL3742A_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break; + } +#else + i2c_write_register(i2c_addresses[index] << 1, i, driver_buffers[index].pwm_buffer + i, 30, IS31FL3742A_I2C_TIMEOUT); +#endif + } +} + +void is31fl3742a_init_drivers(void) { + i2c_init(); + +#if defined(IS31FL3742A_SDB_PIN) + gpio_set_pin_output(IS31FL3742A_SDB_PIN); + gpio_write_pin_high(IS31FL3742A_SDB_PIN); +#endif + + for (uint8_t i = 0; i < IS31FL3742A_DRIVER_COUNT; i++) { + is31fl3742a_init(i); + } + + for (int i = 0; i < IS31FL3742A_LED_COUNT; i++) { + is31fl3742a_set_scaling_register(i, 0xFF, 0xFF, 0xFF); + } + + for (uint8_t i = 0; i < IS31FL3742A_DRIVER_COUNT; i++) { + is31fl3742a_update_scaling_registers(i); + } +} + +void is31fl3742a_init(uint8_t index) { + // In order to avoid the LEDs being driven with garbage data + // in the LED driver's PWM registers, shutdown is enabled last. + // Set up the mode and other settings, clear the PWM registers, + // then disable software shutdown. + + is31fl3742a_select_page(index, IS31FL3742A_COMMAND_SCALING); + + // Turn off all LEDs. + for (uint8_t i = 0; i < IS31FL3742A_SCALING_REGISTER_COUNT; i++) { + is31fl3742a_write_register(index, i, 0x00); + } + + is31fl3742a_select_page(index, IS31FL3742A_COMMAND_PWM); + + for (uint8_t i = 0; i < IS31FL3742A_PWM_REGISTER_COUNT; i++) { + is31fl3742a_write_register(index, i, 0x00); + } + + is31fl3742a_select_page(index, IS31FL3742A_COMMAND_FUNCTION); + + is31fl3742a_write_register(index, IS31FL3742A_FUNCTION_REG_PULLDOWNUP, (IS31FL3742A_SW_PULLDOWN << 4) | IS31FL3742A_CS_PULLUP); + is31fl3742a_write_register(index, IS31FL3742A_FUNCTION_REG_GLOBAL_CURRENT, IS31FL3742A_GLOBAL_CURRENT); + is31fl3742a_write_register(index, IS31FL3742A_FUNCTION_REG_PWM_FREQUENCY, (IS31FL3742A_PWM_FREQUENCY & 0b0111)); + is31fl3742a_write_register(index, IS31FL3742A_FUNCTION_REG_CONFIGURATION, IS31FL3742A_CONFIGURATION); + + // Wait 10ms to ensure the device has woken up. + wait_ms(10); +} + +void is31fl3742a_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) { + is31fl3742a_led_t led; + + if (index >= 0 && index < IS31FL3742A_LED_COUNT) { + memcpy_P(&led, (&g_is31fl3742a_leds[index]), sizeof(led)); + + if (driver_buffers[led.driver].pwm_buffer[led.r] == red && driver_buffers[led.driver].pwm_buffer[led.g] == green && driver_buffers[led.driver].pwm_buffer[led.b] == blue) { + return; + } + + driver_buffers[led.driver].pwm_buffer[led.r] = red; + driver_buffers[led.driver].pwm_buffer[led.g] = green; + driver_buffers[led.driver].pwm_buffer[led.b] = blue; + driver_buffers[led.driver].pwm_buffer_dirty = true; + } +} + +void is31fl3742a_set_color_all(uint8_t red, uint8_t green, uint8_t blue) { + for (int i = 0; i < IS31FL3742A_LED_COUNT; i++) { + is31fl3742a_set_color(i, red, green, blue); + } +} + +void is31fl3742a_set_scaling_register(uint8_t index, uint8_t red, uint8_t green, uint8_t blue) { + is31fl3742a_led_t led; + memcpy_P(&led, (&g_is31fl3742a_leds[index]), sizeof(led)); + + driver_buffers[led.driver].scaling_buffer[led.r] = red; + driver_buffers[led.driver].scaling_buffer[led.g] = green; + driver_buffers[led.driver].scaling_buffer[led.b] = blue; + driver_buffers[led.driver].scaling_buffer_dirty = true; +} + +void is31fl3742a_update_pwm_buffers(uint8_t index) { + if (driver_buffers[index].pwm_buffer_dirty) { + is31fl3742a_select_page(index, IS31FL3742A_COMMAND_PWM); + + is31fl3742a_write_pwm_buffer(index); + + driver_buffers[index].pwm_buffer_dirty = false; + } +} + +void is31fl3742a_update_scaling_registers(uint8_t index) { + if (driver_buffers[index].scaling_buffer_dirty) { + is31fl3742a_select_page(index, IS31FL3742A_COMMAND_SCALING); + + for (uint8_t i = 0; i < IS31FL3742A_SCALING_REGISTER_COUNT; i++) { + is31fl3742a_write_register(index, i, driver_buffers[index].scaling_buffer[i]); + } + + driver_buffers[index].scaling_buffer_dirty = false; + } +} + +void is31fl3742a_flush(void) { + for (uint8_t i = 0; i < IS31FL3742A_DRIVER_COUNT; i++) { + is31fl3742a_update_pwm_buffers(i); + } +} diff --git a/drivers/led/issi/is31fl3742a.h b/drivers/led/issi/is31fl3742a.h new file mode 100644 index 0000000000..10ecf50026 --- /dev/null +++ b/drivers/led/issi/is31fl3742a.h @@ -0,0 +1,298 @@ +/* Copyright 2017 Jason Williams + * Copyright 2018 Jack Humbert + * Copyright 2018 Yiancar + * Copyright 2020 MelGeek + * Copyright 2021 MasterSpoon + * + * 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 2 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/>. + */ + +#pragma once + +#include <stdint.h> +#include <stdbool.h> +#include "progmem.h" +#include "util.h" + +#define IS31FL3742A_REG_INTERRUPT_MASK 0xF0 +#define IS31FL3742A_REG_INTERRUPT_STATUS 0xF1 +#define IS31FL3742A_REG_ID 0xFC + +#define IS31FL3742A_REG_COMMAND 0xFD + +#define IS31FL3742A_COMMAND_PWM 0x00 +#define IS31FL3742A_COMMAND_SCALING 0x02 +#define IS31FL3742A_COMMAND_FUNCTION 0x04 + +#define IS31FL3742A_FUNCTION_REG_CONFIGURATION 0x00 +#define IS31FL3742A_FUNCTION_REG_GLOBAL_CURRENT 0x01 +#define IS31FL3742A_FUNCTION_REG_PULLDOWNUP 0x02 +#define IS31FL3742A_FUNCTION_REG_PWM_FREQUENCY 0x36 +#define IS31FL3742A_FUNCTION_REG_RESET 0x3F +#define IS31FL3742A_FUNCTION_REG_SPREAD_SPECTRUM 0x41 + +#define IS31FL3742A_REG_COMMAND_WRITE_LOCK 0xFE +#define IS31FL3742A_COMMAND_WRITE_LOCK_MAGIC 0xC5 + +#define IS31FL3742A_I2C_ADDRESS_GND 0x30 +#define IS31FL3742A_I2C_ADDRESS_SCL 0x31 +#define IS31FL3742A_I2C_ADDRESS_SDA 0x32 +#define IS31FL3742A_I2C_ADDRESS_VCC 0x33 + +#if defined(RGB_MATRIX_IS31FL3742A) +# define IS31FL3742A_LED_COUNT RGB_MATRIX_LED_COUNT +#endif + +#if defined(IS31FL3742A_I2C_ADDRESS_4) +# define IS31FL3742A_DRIVER_COUNT 4 +#elif defined(IS31FL3742A_I2C_ADDRESS_3) +# define IS31FL3742A_DRIVER_COUNT 3 +#elif defined(IS31FL3742A_I2C_ADDRESS_2) +# define IS31FL3742A_DRIVER_COUNT 2 +#elif defined(IS31FL3742A_I2C_ADDRESS_1) +# define IS31FL3742A_DRIVER_COUNT 1 +#endif + +typedef struct is31fl3742a_led_t { + uint8_t driver : 2; + uint8_t r; + uint8_t g; + uint8_t b; +} PACKED is31fl3742a_led_t; + +extern const is31fl3742a_led_t PROGMEM g_is31fl3742a_leds[IS31FL3742A_LED_COUNT]; + +void is31fl3742a_init_drivers(void); +void is31fl3742a_init(uint8_t index); +void is31fl3742a_write_register(uint8_t index, uint8_t reg, uint8_t data); +void is31fl3742a_select_page(uint8_t index, uint8_t page); + +void is31fl3742a_set_color(int index, uint8_t red, uint8_t green, uint8_t blue); +void is31fl3742a_set_color_all(uint8_t red, uint8_t green, uint8_t blue); + +void is31fl3742a_set_scaling_register(uint8_t index, uint8_t red, uint8_t green, uint8_t blue); + +void is31fl3742a_update_pwm_buffers(uint8_t index); +void is31fl3742a_update_scaling_registers(uint8_t index); + +void is31fl3742a_flush(void); + +#define IS31FL3742A_PDR_0_OHM 0b000 // No pull-down resistor +#define IS31FL3742A_PDR_0K5_OHM 0b001 // 0.5 kOhm resistor +#define IS31FL3742A_PDR_1K_OHM 0b010 // 1 kOhm resistor +#define IS31FL3742A_PDR_2K_OHM 0b011 // 2 kOhm resistor +#define IS31FL3742A_PDR_4K_OHM 0b100 // 4 kOhm resistor +#define IS31FL3742A_PDR_8K_OHM 0b101 // 8 kOhm resistor +#define IS31FL3742A_PDR_16K_OHM 0b110 // 16 kOhm resistor +#define IS31FL3742A_PDR_32K_OHM 0b111 // 32 kOhm resistor + +#define IS31FL3742A_PUR_0_OHM 0b000 // No pull-up resistor +#define IS31FL3742A_PUR_0K5_OHM 0b001 // 0.5 kOhm resistor +#define IS31FL3742A_PUR_1K_OHM 0b010 // 1 kOhm resistor +#define IS31FL3742A_PUR_2K_OHM 0b011 // 2 kOhm resistor +#define IS31FL3742A_PUR_4K_OHM 0b100 // 4 kOhm resistor +#define IS31FL3742A_PUR_8K_OHM 0b101 // 8 kOhm resistor +#define IS31FL3742A_PUR_16K_OHM 0b110 // 16 kOhm resistor +#define IS31FL3742A_PUR_32K_OHM 0b111 // 32 kOhm resistor + +#define IS31FL3742A_PWM_FREQUENCY_29K_HZ 0b0000 +#define IS31FL3742A_PWM_FREQUENCY_3K6_HZ 0b0011 +#define IS31FL3742A_PWM_FREQUENCY_1K8_HZ 0b0111 +#define IS31FL3742A_PWM_FREQUENCY_900_HZ 0b1011 + +#define SW1_CS1 0x00 +#define SW1_CS2 0x01 +#define SW1_CS3 0x02 +#define SW1_CS4 0x03 +#define SW1_CS5 0x04 +#define SW1_CS6 0x05 +#define SW1_CS7 0x06 +#define SW1_CS8 0x07 +#define SW1_CS9 0x08 +#define SW1_CS10 0x09 +#define SW1_CS11 0x0A +#define SW1_CS12 0x0B +#define SW1_CS13 0x0C +#define SW1_CS14 0x0D +#define SW1_CS15 0x0E +#define SW1_CS16 0x0F +#define SW1_CS17 0x10 +#define SW1_CS18 0x11 +#define SW1_CS19 0x12 +#define SW1_CS20 0x13 +#define SW1_CS21 0x14 +#define SW1_CS22 0x15 +#define SW1_CS23 0x16 +#define SW1_CS24 0x17 +#define SW1_CS25 0x18 +#define SW1_CS26 0x19 +#define SW1_CS27 0x1A +#define SW1_CS28 0x1B +#define SW1_CS29 0x1C +#define SW1_CS30 0x1D + +#define SW2_CS1 0x1E +#define SW2_CS2 0x1F +#define SW2_CS3 0x20 +#define SW2_CS4 0x21 +#define SW2_CS5 0x22 +#define SW2_CS6 0x23 +#define SW2_CS7 0x24 +#define SW2_CS8 0x25 +#define SW2_CS9 0x26 +#define SW2_CS10 0x27 +#define SW2_CS11 0x28 +#define SW2_CS12 0x29 +#define SW2_CS13 0x2A +#define SW2_CS14 0x2B +#define SW2_CS15 0x2C +#define SW2_CS16 0x2D +#define SW2_CS17 0x2E +#define SW2_CS18 0x2F +#define SW2_CS19 0x30 +#define SW2_CS20 0x31 +#define SW2_CS21 0x32 +#define SW2_CS22 0x33 +#define SW2_CS23 0x34 +#define SW2_CS24 0x35 +#define SW2_CS25 0x36 +#define SW2_CS26 0x37 +#define SW2_CS27 0x38 +#define SW2_CS28 0x39 +#define SW2_CS29 0x3A +#define SW2_CS30 0x3B + +#define SW3_CS1 0x3C +#define SW3_CS2 0x3D +#define SW3_CS3 0x3E +#define SW3_CS4 0x3F +#define SW3_CS5 0x40 +#define SW3_CS6 0x41 +#define SW3_CS7 0x42 +#define SW3_CS8 0x43 +#define SW3_CS9 0x44 +#define SW3_CS10 0x45 +#define SW3_CS11 0x46 +#define SW3_CS12 0x47 +#define SW3_CS13 0x48 +#define SW3_CS14 0x49 +#define SW3_CS15 0x4A +#define SW3_CS16 0x4B +#define SW3_CS17 0x4C +#define SW3_CS18 0x4D +#define SW3_CS19 0x4E +#define SW3_CS20 0x4F +#define SW3_CS21 0x50 +#define SW3_CS22 0x51 +#define SW3_CS23 0x52 +#define SW3_CS24 0x53 +#define SW3_CS25 0x54 +#define SW3_CS26 0x55 +#define SW3_CS27 0x56 +#define SW3_CS28 0x57 +#define SW3_CS29 0x58 +#define SW3_CS30 0x59 + +#define SW4_CS1 0x5A +#define SW4_CS2 0x5B +#define SW4_CS3 0x5C +#define SW4_CS4 0x5D +#define SW4_CS5 0x5E +#define SW4_CS6 0x5F +#define SW4_CS7 0x60 +#define SW4_CS8 0x61 +#define SW4_CS9 0x62 +#define SW4_CS10 0x63 +#define SW4_CS11 0x64 +#define SW4_CS12 0x65 +#define SW4_CS13 0x66 +#define SW4_CS14 0x67 +#define SW4_CS15 0x68 +#define SW4_CS16 0x69 +#define SW4_CS17 0x6A +#define SW4_CS18 0x6B +#define SW4_CS19 0x6C +#define SW4_CS20 0x6D +#define SW4_CS21 0x6E +#define SW4_CS22 0x6F +#define SW4_CS23 0x70 +#define SW4_CS24 0x71 +#define SW4_CS25 0x72 +#define SW4_CS26 0x73 +#define SW4_CS27 0x74 +#define SW4_CS28 0x75 +#define SW4_CS29 0x76 +#define SW4_CS30 0x77 + +#define SW5_CS1 0x78 +#define SW5_CS2 0x79 +#define SW5_CS3 0x7A +#define SW5_CS4 0x7B +#define SW5_CS5 0x7C +#define SW5_CS6 0x7D +#define SW5_CS7 0x7E +#define SW5_CS8 0x7F +#define SW5_CS9 0x80 +#define SW5_CS10 0x81 +#define SW5_CS11 0x82 +#define SW5_CS12 0x83 +#define SW5_CS13 0x84 +#define SW5_CS14 0x85 +#define SW5_CS15 0x86 +#define SW5_CS16 0x87 +#define SW5_CS17 0x88 +#define SW5_CS18 0x89 +#define SW5_CS19 0x8A +#define SW5_CS20 0x8B +#define SW5_CS21 0x8C +#define SW5_CS22 0x8D +#define SW5_CS23 0x8E +#define SW5_CS24 0x8F +#define SW5_CS25 0x90 +#define SW5_CS26 0x91 +#define SW5_CS27 0x92 +#define SW5_CS28 0x93 +#define SW5_CS29 0x94 +#define SW5_CS30 0x95 + +#define SW6_CS1 0x96 +#define SW6_CS2 0x97 +#define SW6_CS3 0x98 +#define SW6_CS4 0x99 +#define SW6_CS5 0x9A +#define SW6_CS6 0x9B +#define SW6_CS7 0x9C +#define SW6_CS8 0x9D +#define SW6_CS9 0x9E +#define SW6_CS10 0x9F +#define SW6_CS11 0xA0 +#define SW6_CS12 0xA1 +#define SW6_CS13 0xA2 +#define SW6_CS14 0xA3 +#define SW6_CS15 0xA4 +#define SW6_CS16 0xA5 +#define SW6_CS17 0xA6 +#define SW6_CS18 0xA7 +#define SW6_CS19 0xA8 +#define SW6_CS20 0xA9 +#define SW6_CS21 0xAA +#define SW6_CS22 0xAB +#define SW6_CS23 0xAC +#define SW6_CS24 0xAD +#define SW6_CS25 0xAE +#define SW6_CS26 0xAF +#define SW6_CS27 0xB0 +#define SW6_CS28 0xB1 +#define SW6_CS29 0xB2 +#define SW6_CS30 0xB3 diff --git a/drivers/led/issi/is31fl3743.h b/drivers/led/issi/is31fl3743.h deleted file mode 100644 index 706b271254..0000000000 --- a/drivers/led/issi/is31fl3743.h +++ /dev/null @@ -1,327 +0,0 @@ -/* Copyright 2017 Jason Williams - * Copyright 2018 Jack Humbert - * Copyright 2018 Yiancar - * Copyright 2020 MelGeek - * Copyright 2021 MasterSpoon - * - * 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 2 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/>. - */ - -#pragma once - -// This is a 7-bit address, that gets left-shifted and bit 0 -// set to 0 for write, 1 for read (as per I2C protocol) -// The address will vary depending on your wiring: -// 00 <-> GND -// 01 <-> SCL -// 10 <-> SDA -// 11 <-> VCC -// ADDR1 represents A1:A0 of the 7-bit address. -// ADDR2 represents A3:A2 of the 7-bit address. -// The result is: 0b010(ADDR2)(ADDR1) -#ifndef DRIVER_ADDR_1 -# define DRIVER_ADDR_1 0b0100000 -#endif - -// Set defaults for Spread Spectrum Register -#ifndef ISSI_SSR_1 -# ifndef DRIVER_ADDR_2 -# define ISSI_SSR_1 0x00 -# else -# define ISSI_SSR_1 0xC0 -# endif -#endif -#ifndef ISSI_SSR_2 -# define ISSI_SSR_2 0x80 -#endif -#ifndef ISSI_SSR_3 -# define ISSI_SSR_3 0x80 -#endif -#ifndef ISSI_SSR_4 -# define ISSI_SSR_4 0x80 -#endif - -// Command Registers -#define ISSI_COMMANDREGISTER_WRITELOCK 0xFE -#define ISSI_COMMANDREGISTER 0xFD -#define ISSI_IDREGISTER 0xFC -#define ISSI_REGISTER_UNLOCK 0xC5 - -// Response Registers -#define ISSI_PAGE_PWM 0x00 -#define ISSI_PAGE_SCALING 0x01 -#define ISSI_PAGE_FUNCTION 0x02 - -// Registers under Function Register -#define ISSI_REG_CONFIGURATION 0x00 -#define ISSI_REG_GLOBALCURRENT 0x01 -#define ISSI_REG_PULLDOWNUP 0x02 -#define ISSI_REG_TEMP 0x24 -#define ISSI_REG_SSR 0x25 -#define ISSI_REG_RESET 0x2F - -// Set defaults for Function Registers -#ifndef ISSI_CONFIGURATION -# define ISSI_CONFIGURATION 0x01 -#endif -#ifndef ISSI_GLOBALCURRENT -# define ISSI_GLOBALCURRENT 0xFF -#endif -#ifndef ISSI_PULLDOWNUP -# define ISSI_PULLDOWNUP 0x33 -#endif -#ifndef ISSI_TEMP -# define ISSI_TEMP 0x00 -#endif - -// Set defaults for Scaling registers -#ifndef ISSI_SCAL_RED -# define ISSI_SCAL_RED 0xFF -#endif -#ifndef ISSI_SCAL_BLUE -# define ISSI_SCAL_BLUE 0xFF -#endif -#ifndef ISSI_SCAL_GREEN -# define ISSI_SCAL_GREEN 0xFF -#endif -#define ISSI_SCAL_RED_OFF 0x00 -#define ISSI_SCAL_GREEN_OFF 0x00 -#define ISSI_SCAL_BLUE_OFF 0x00 - -#ifndef ISSI_SCAL_LED -# define ISSI_SCAL_LED 0xFF -#endif -#define ISSI_SCAL_LED_OFF 0x00 - -// Set buffer sizes -#define ISSI_MAX_LEDS 198 -#define ISSI_SCALING_SIZE 198 -#define ISSI_PWM_TRF_SIZE 18 -#define ISSI_SCALING_TRF_SIZE 18 - -// Location of 1st bit for PWM and Scaling registers -#define ISSI_PWM_REG_1ST 0x01 -#define ISSI_SCL_REG_1ST 0x01 - -// Map CS SW locations to order in PWM / Scaling buffers -// This matches the ORDER in the Datasheet Register not the POSITION -// It will always count from 0x00 to (ISSI_MAX_LEDS - 1) -#define CS1_SW1 0x00 -#define CS2_SW1 0x01 -#define CS3_SW1 0x02 -#define CS4_SW1 0x03 -#define CS5_SW1 0x04 -#define CS6_SW1 0x05 -#define CS7_SW1 0x06 -#define CS8_SW1 0x07 -#define CS9_SW1 0x08 -#define CS10_SW1 0x09 -#define CS11_SW1 0x0A -#define CS12_SW1 0x0B -#define CS13_SW1 0x0C -#define CS14_SW1 0x0D -#define CS15_SW1 0x0E -#define CS16_SW1 0x0F -#define CS17_SW1 0x10 -#define CS18_SW1 0x11 - -#define CS1_SW2 0x12 -#define CS2_SW2 0x13 -#define CS3_SW2 0x14 -#define CS4_SW2 0x15 -#define CS5_SW2 0x16 -#define CS6_SW2 0x17 -#define CS7_SW2 0x18 -#define CS8_SW2 0x19 -#define CS9_SW2 0x1A -#define CS10_SW2 0x1B -#define CS11_SW2 0x1C -#define CS12_SW2 0x1D -#define CS13_SW2 0x1E -#define CS14_SW2 0x1F -#define CS15_SW2 0x20 -#define CS16_SW2 0x21 -#define CS17_SW2 0x22 -#define CS18_SW2 0x23 - -#define CS1_SW3 0x24 -#define CS2_SW3 0x25 -#define CS3_SW3 0x26 -#define CS4_SW3 0x27 -#define CS5_SW3 0x28 -#define CS6_SW3 0x29 -#define CS7_SW3 0x2A -#define CS8_SW3 0x2B -#define CS9_SW3 0x2C -#define CS10_SW3 0x2D -#define CS11_SW3 0x2E -#define CS12_SW3 0x2F -#define CS13_SW3 0x30 -#define CS14_SW3 0x31 -#define CS15_SW3 0x32 -#define CS16_SW3 0x33 -#define CS17_SW3 0x34 -#define CS18_SW3 0x35 - -#define CS1_SW4 0x36 -#define CS2_SW4 0x37 -#define CS3_SW4 0x38 -#define CS4_SW4 0x39 -#define CS5_SW4 0x3A -#define CS6_SW4 0x3B -#define CS7_SW4 0x3C -#define CS8_SW4 0x3D -#define CS9_SW4 0x3E -#define CS10_SW4 0x3F -#define CS11_SW4 0x40 -#define CS12_SW4 0x41 -#define CS13_SW4 0x42 -#define CS14_SW4 0x43 -#define CS15_SW4 0x44 -#define CS16_SW4 0x45 -#define CS17_SW4 0x46 -#define CS18_SW4 0x47 - -#define CS1_SW5 0x48 -#define CS2_SW5 0x49 -#define CS3_SW5 0x4A -#define CS4_SW5 0x4B -#define CS5_SW5 0x4C -#define CS6_SW5 0x4D -#define CS7_SW5 0x4E -#define CS8_SW5 0x4F -#define CS9_SW5 0x50 -#define CS10_SW5 0x51 -#define CS11_SW5 0x52 -#define CS12_SW5 0x53 -#define CS13_SW5 0x54 -#define CS14_SW5 0x55 -#define CS15_SW5 0x56 -#define CS16_SW5 0x57 -#define CS17_SW5 0x58 -#define CS18_SW5 0x59 - -#define CS1_SW6 0x5A -#define CS2_SW6 0x5B -#define CS3_SW6 0x5C -#define CS4_SW6 0x5D -#define CS5_SW6 0x5E -#define CS6_SW6 0x5F -#define CS7_SW6 0x60 -#define CS8_SW6 0x61 -#define CS9_SW6 0x62 -#define CS10_SW6 0x63 -#define CS11_SW6 0x64 -#define CS12_SW6 0x65 -#define CS13_SW6 0x66 -#define CS14_SW6 0x67 -#define CS15_SW6 0x68 -#define CS16_SW6 0x69 -#define CS17_SW6 0x6A -#define CS18_SW6 0x6B - -#define CS1_SW7 0x6C -#define CS2_SW7 0x6D -#define CS3_SW7 0x6E -#define CS4_SW7 0x6F -#define CS5_SW7 0x70 -#define CS6_SW7 0x71 -#define CS7_SW7 0x72 -#define CS8_SW7 0x73 -#define CS9_SW7 0x74 -#define CS10_SW7 0x75 -#define CS11_SW7 0x76 -#define CS12_SW7 0x77 -#define CS13_SW7 0x78 -#define CS14_SW7 0x79 -#define CS15_SW7 0x7A -#define CS16_SW7 0x7B -#define CS17_SW7 0x7C -#define CS18_SW7 0x7D - -#define CS1_SW8 0x7E -#define CS2_SW8 0x7F -#define CS3_SW8 0x80 -#define CS4_SW8 0x81 -#define CS5_SW8 0x82 -#define CS6_SW8 0x83 -#define CS7_SW8 0x84 -#define CS8_SW8 0x85 -#define CS9_SW8 0x86 -#define CS10_SW8 0x87 -#define CS11_SW8 0x88 -#define CS12_SW8 0x89 -#define CS13_SW8 0x8A -#define CS14_SW8 0x8B -#define CS15_SW8 0x8C -#define CS16_SW8 0x8D -#define CS17_SW8 0x8E -#define CS18_SW8 0x8F - -#define CS1_SW9 0x90 -#define CS2_SW9 0x91 -#define CS3_SW9 0x92 -#define CS4_SW9 0x93 -#define CS5_SW9 0x94 -#define CS6_SW9 0x95 -#define CS7_SW9 0x96 -#define CS8_SW9 0x97 -#define CS9_SW9 0x98 -#define CS10_SW9 0x99 -#define CS11_SW9 0x9A -#define CS12_SW9 0x9B -#define CS13_SW9 0x9C -#define CS14_SW9 0x9D -#define CS15_SW9 0x9E -#define CS16_SW9 0x9F -#define CS17_SW9 0xA0 -#define CS18_SW9 0xA1 - -#define CS1_SW10 0xA2 -#define CS2_SW10 0xA3 -#define CS3_SW10 0xA4 -#define CS4_SW10 0xA5 -#define CS5_SW10 0xA6 -#define CS6_SW10 0xA7 -#define CS7_SW10 0xA8 -#define CS8_SW10 0xA9 -#define CS9_SW10 0xAA -#define CS10_SW10 0xAB -#define CS11_SW10 0xAC -#define CS12_SW10 0xAD -#define CS13_SW10 0xAE -#define CS14_SW10 0xAF -#define CS15_SW10 0xB0 -#define CS16_SW10 0xB1 -#define CS17_SW10 0xB2 -#define CS18_SW10 0xB3 - -#define CS1_SW11 0xB4 -#define CS2_SW11 0xB5 -#define CS3_SW11 0xB6 -#define CS4_SW11 0xB7 -#define CS5_SW11 0xB8 -#define CS6_SW11 0xB9 -#define CS7_SW11 0xBA -#define CS8_SW11 0xBB -#define CS9_SW11 0xBC -#define CS10_SW11 0xBD -#define CS11_SW11 0xBE -#define CS12_SW11 0xBF -#define CS13_SW11 0xC0 -#define CS14_SW11 0xC1 -#define CS15_SW11 0xC2 -#define CS16_SW11 0xC3 -#define CS17_SW11 0xC4 -#define CS18_SW11 0xC5 diff --git a/drivers/led/issi/is31fl3743a-mono.c b/drivers/led/issi/is31fl3743a-mono.c new file mode 100644 index 0000000000..6413dbef04 --- /dev/null +++ b/drivers/led/issi/is31fl3743a-mono.c @@ -0,0 +1,245 @@ +/* Copyright 2017 Jason Williams + * Copyright 2018 Jack Humbert + * Copyright 2018 Yiancar + * Copyright 2020 MelGeek + * Copyright 2021 MasterSpoon + * + * 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 2 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/>. + */ + +#include "is31fl3743a-mono.h" +#include "i2c_master.h" +#include "gpio.h" +#include "wait.h" + +#define IS31FL3743A_PWM_REGISTER_COUNT 198 +#define IS31FL3743A_SCALING_REGISTER_COUNT 198 + +#ifndef IS31FL3743A_I2C_TIMEOUT +# define IS31FL3743A_I2C_TIMEOUT 100 +#endif + +#ifndef IS31FL3743A_I2C_PERSISTENCE +# define IS31FL3743A_I2C_PERSISTENCE 0 +#endif + +#ifndef IS31FL3743A_CONFIGURATION +# define IS31FL3743A_CONFIGURATION 0x01 +#endif + +#ifndef IS31FL3743A_SW_PULLDOWN +# define IS31FL3743A_SW_PULLDOWN IS31FL3743A_PDR_2K_OHM_SW_OFF +#endif + +#ifndef IS31FL3743A_CS_PULLUP +# define IS31FL3743A_CS_PULLUP IS31FL3743A_PUR_2K_OHM_CS_OFF +#endif + +#ifndef IS31FL3743A_GLOBAL_CURRENT +# define IS31FL3743A_GLOBAL_CURRENT 0xFF +#endif + +#ifndef IS31FL3743A_SYNC_1 +# define IS31FL3743A_SYNC_1 IS31FL3743A_SYNC_NONE +#endif +#ifndef IS31FL3743A_SYNC_2 +# define IS31FL3743A_SYNC_2 IS31FL3743A_SYNC_NONE +#endif +#ifndef IS31FL3743A_SYNC_3 +# define IS31FL3743A_SYNC_3 IS31FL3743A_SYNC_NONE +#endif +#ifndef IS31FL3743A_SYNC_4 +# define IS31FL3743A_SYNC_4 IS31FL3743A_SYNC_NONE +#endif + +const uint8_t i2c_addresses[IS31FL3743A_DRIVER_COUNT] = { + IS31FL3743A_I2C_ADDRESS_1, +#ifdef IS31FL3743A_I2C_ADDRESS_2 + IS31FL3743A_I2C_ADDRESS_2, +# ifdef IS31FL3743A_I2C_ADDRESS_3 + IS31FL3743A_I2C_ADDRESS_3, +# ifdef IS31FL3743A_I2C_ADDRESS_4 + IS31FL3743A_I2C_ADDRESS_4, +# endif +# endif +#endif +}; + +const uint8_t driver_sync[IS31FL3743A_DRIVER_COUNT] = { + IS31FL3743A_SYNC_1, +#ifdef IS31FL3743A_I2C_ADDRESS_2 + IS31FL3743A_SYNC_2, +# ifdef IS31FL3743A_I2C_ADDRESS_3 + IS31FL3743A_SYNC_3, +# ifdef IS31FL3743A_I2C_ADDRESS_4 + IS31FL3743A_SYNC_4, +# endif +# endif +#endif +}; + +typedef struct is31fl3743a_driver_t { + uint8_t pwm_buffer[IS31FL3743A_PWM_REGISTER_COUNT]; + bool pwm_buffer_dirty; + uint8_t scaling_buffer[IS31FL3743A_SCALING_REGISTER_COUNT]; + bool scaling_buffer_dirty; +} PACKED is31fl3743a_driver_t; + +is31fl3743a_driver_t driver_buffers[IS31FL3743A_DRIVER_COUNT] = {{ + .pwm_buffer = {0}, + .pwm_buffer_dirty = false, + .scaling_buffer = {0}, + .scaling_buffer_dirty = false, +}}; + +void is31fl3743a_write_register(uint8_t index, uint8_t reg, uint8_t data) { +#if IS31FL3743A_I2C_PERSISTENCE > 0 + for (uint8_t i = 0; i < IS31FL3743A_I2C_PERSISTENCE; i++) { + if (i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3743A_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break; + } +#else + i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3743A_I2C_TIMEOUT); +#endif +} + +void is31fl3743a_select_page(uint8_t index, uint8_t page) { + is31fl3743a_write_register(index, IS31FL3743A_REG_COMMAND_WRITE_LOCK, IS31FL3743A_COMMAND_WRITE_LOCK_MAGIC); + is31fl3743a_write_register(index, IS31FL3743A_REG_COMMAND, page); +} + +void is31fl3743a_write_pwm_buffer(uint8_t index) { + // Assumes page 0 is already selected. + // Transmit PWM registers in 11 transfers of 18 bytes. + + // Iterate over the pwm_buffer contents at 18 byte intervals. + for (uint8_t i = 0; i < IS31FL3743A_PWM_REGISTER_COUNT; i += 18) { +#if IS31FL3743A_I2C_PERSISTENCE > 0 + for (uint8_t j = 0; j < IS31FL3743A_I2C_PERSISTENCE; j++) { + if (i2c_write_register(i2c_addresses[index] << 1, i + 1, driver_buffers[index].pwm_buffer + i, 18, IS31FL3743A_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break; + } +#else + i2c_write_register(i2c_addresses[index] << 1, i + 1, driver_buffers[index].pwm_buffer + i, 18, IS31FL3743A_I2C_TIMEOUT); +#endif + } +} + +void is31fl3743a_init_drivers(void) { + i2c_init(); + +#if defined(IS31FL3743A_SDB_PIN) + gpio_set_pin_output(IS31FL3743A_SDB_PIN); + gpio_write_pin_high(IS31FL3743A_SDB_PIN); +#endif + + for (uint8_t i = 0; i < IS31FL3743A_DRIVER_COUNT; i++) { + is31fl3743a_init(i); + } + + for (int i = 0; i < IS31FL3743A_LED_COUNT; i++) { + is31fl3743a_set_scaling_register(i, 0xFF); + } + + for (uint8_t i = 0; i < IS31FL3743A_DRIVER_COUNT; i++) { + is31fl3743a_update_scaling_registers(i); + } +} + +void is31fl3743a_init(uint8_t index) { + // In order to avoid the LEDs being driven with garbage data + // in the LED driver's PWM registers, shutdown is enabled last. + // Set up the mode and other settings, clear the PWM registers, + // then disable software shutdown. + + is31fl3743a_select_page(index, IS31FL3743A_COMMAND_SCALING); + + // Turn off all LEDs. + for (uint8_t i = 0; i < IS31FL3743A_SCALING_REGISTER_COUNT; i++) { + is31fl3743a_write_register(index, i + 1, 0x00); + } + + is31fl3743a_select_page(index, IS31FL3743A_COMMAND_PWM); + + for (uint8_t i = 0; i < IS31FL3743A_PWM_REGISTER_COUNT; i++) { + is31fl3743a_write_register(index, i + 1, 0x00); + } + + is31fl3743a_select_page(index, IS31FL3743A_COMMAND_FUNCTION); + + uint8_t sync = driver_sync[index]; + + is31fl3743a_write_register(index, IS31FL3743A_FUNCTION_REG_PULLDOWNUP, (IS31FL3743A_SW_PULLDOWN << 4) | IS31FL3743A_CS_PULLUP); + is31fl3743a_write_register(index, IS31FL3743A_FUNCTION_REG_GLOBAL_CURRENT, IS31FL3743A_GLOBAL_CURRENT); + is31fl3743a_write_register(index, IS31FL3743A_FUNCTION_REG_SPREAD_SPECTRUM, (sync & 0b11) << 6); + is31fl3743a_write_register(index, IS31FL3743A_FUNCTION_REG_CONFIGURATION, IS31FL3743A_CONFIGURATION); + + // Wait 10ms to ensure the device has woken up. + wait_ms(10); +} + +void is31fl3743a_set_value(int index, uint8_t value) { + is31fl3743a_led_t led; + + if (index >= 0 && index < IS31FL3743A_LED_COUNT) { + memcpy_P(&led, (&g_is31fl3743a_leds[index]), sizeof(led)); + + if (driver_buffers[led.driver].pwm_buffer[led.v] == value) { + return; + } + + driver_buffers[led.driver].pwm_buffer[led.v] = value; + driver_buffers[led.driver].pwm_buffer_dirty = true; + } +} + +void is31fl3743a_set_value_all(uint8_t value) { + for (int i = 0; i < IS31FL3743A_LED_COUNT; i++) { + is31fl3743a_set_value(i, value); + } +} + +void is31fl3743a_set_scaling_register(uint8_t index, uint8_t value) { + is31fl3743a_led_t led; + memcpy_P(&led, (&g_is31fl3743a_leds[index]), sizeof(led)); + + driver_buffers[led.driver].scaling_buffer[led.v] = value; + driver_buffers[led.driver].scaling_buffer_dirty = true; +} + +void is31fl3743a_update_pwm_buffers(uint8_t index) { + if (driver_buffers[index].pwm_buffer_dirty) { + is31fl3743a_select_page(index, IS31FL3743A_COMMAND_PWM); + + is31fl3743a_write_pwm_buffer(index); + + driver_buffers[index].pwm_buffer_dirty = false; + } +} + +void is31fl3743a_update_scaling_registers(uint8_t index) { + if (driver_buffers[index].scaling_buffer_dirty) { + is31fl3743a_select_page(index, IS31FL3743A_COMMAND_SCALING); + + for (uint8_t i = 0; i < IS31FL3743A_SCALING_REGISTER_COUNT; i++) { + is31fl3743a_write_register(index, i + 1, driver_buffers[index].scaling_buffer[i]); + } + + driver_buffers[index].scaling_buffer_dirty = false; + } +} + +void is31fl3743a_flush(void) { + for (uint8_t i = 0; i < IS31FL3743A_DRIVER_COUNT; i++) { + is31fl3743a_update_pwm_buffers(i); + } +} diff --git a/drivers/led/issi/is31fl3743a-mono.h b/drivers/led/issi/is31fl3743a-mono.h new file mode 100644 index 0000000000..4c582d3a7a --- /dev/null +++ b/drivers/led/issi/is31fl3743a-mono.h @@ -0,0 +1,328 @@ +/* Copyright 2017 Jason Williams + * Copyright 2018 Jack Humbert + * Copyright 2018 Yiancar + * Copyright 2020 MelGeek + * Copyright 2021 MasterSpoon + * + * 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 2 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/>. + */ + +#pragma once + +#include <stdint.h> +#include <stdbool.h> +#include "progmem.h" +#include "util.h" + +#define IS31FL3743A_REG_ID 0xFC + +#define IS31FL3743A_REG_COMMAND 0xFD + +#define IS31FL3743A_COMMAND_PWM 0x00 +#define IS31FL3743A_COMMAND_SCALING 0x01 +#define IS31FL3743A_COMMAND_FUNCTION 0x02 + +#define IS31FL3743A_FUNCTION_REG_CONFIGURATION 0x00 +#define IS31FL3743A_FUNCTION_REG_GLOBAL_CURRENT 0x01 +#define IS31FL3743A_FUNCTION_REG_PULLDOWNUP 0x02 +#define IS31FL3743A_FUNCTION_REG_TEMPERATURE 0x24 +#define IS31FL3743A_FUNCTION_REG_SPREAD_SPECTRUM 0x25 +#define IS31FL3743A_FUNCTION_REG_RESET 0x2F + +#define IS31FL3743A_REG_COMMAND_WRITE_LOCK 0xFE +#define IS31FL3743A_COMMAND_WRITE_LOCK_MAGIC 0xC5 + +#define IS31FL3743A_I2C_ADDRESS_GND_GND 0x20 +#define IS31FL3743A_I2C_ADDRESS_GND_SCL 0x21 +#define IS31FL3743A_I2C_ADDRESS_GND_SDA 0x22 +#define IS31FL3743A_I2C_ADDRESS_GND_VCC 0x23 +#define IS31FL3743A_I2C_ADDRESS_SCL_GND 0x24 +#define IS31FL3743A_I2C_ADDRESS_SCL_SCL 0x25 +#define IS31FL3743A_I2C_ADDRESS_SCL_SDA 0x26 +#define IS31FL3743A_I2C_ADDRESS_SCL_VCC 0x27 +#define IS31FL3743A_I2C_ADDRESS_SDA_GND 0x28 +#define IS31FL3743A_I2C_ADDRESS_SDA_SCL 0x29 +#define IS31FL3743A_I2C_ADDRESS_SDA_SDA 0x2A +#define IS31FL3743A_I2C_ADDRESS_SDA_VCC 0x2B +#define IS31FL3743A_I2C_ADDRESS_VCC_GND 0x2C +#define IS31FL3743A_I2C_ADDRESS_VCC_SCL 0x2D +#define IS31FL3743A_I2C_ADDRESS_VCC_SDA 0x2E +#define IS31FL3743A_I2C_ADDRESS_VCC_VCC 0x2F + +#if defined(LED_MATRIX_IS31FL3743A) +# define IS31FL3743A_LED_COUNT LED_MATRIX_LED_COUNT +#endif + +#if defined(IS31FL3743A_I2C_ADDRESS_4) +# define IS31FL3743A_DRIVER_COUNT 4 +#elif defined(IS31FL3743A_I2C_ADDRESS_3) +# define IS31FL3743A_DRIVER_COUNT 3 +#elif defined(IS31FL3743A_I2C_ADDRESS_2) +# define IS31FL3743A_DRIVER_COUNT 2 +#elif defined(IS31FL3743A_I2C_ADDRESS_1) +# define IS31FL3743A_DRIVER_COUNT 1 +#endif + +typedef struct is31fl3743a_led_t { + uint8_t driver : 2; + uint8_t v; +} PACKED is31fl3743a_led_t; + +extern const is31fl3743a_led_t PROGMEM g_is31fl3743a_leds[IS31FL3743A_LED_COUNT]; + +void is31fl3743a_init_drivers(void); +void is31fl3743a_init(uint8_t index); +void is31fl3743a_write_register(uint8_t index, uint8_t reg, uint8_t data); +void is31fl3743a_select_page(uint8_t index, uint8_t page); + +void is31fl3743a_set_value(int index, uint8_t value); +void is31fl3743a_set_value_all(uint8_t value); + +void is31fl3743a_set_scaling_register(uint8_t index, uint8_t value); + +void is31fl3743a_update_pwm_buffers(uint8_t index); +void is31fl3743a_update_scaling_registers(uint8_t index); + +void is31fl3743a_flush(void); + +#define IS31FL3743A_PDR_0_OHM 0b000 // No pull-down resistor +#define IS31FL3743A_PDR_0K5_OHM_SW_OFF 0b001 // 0.5 kOhm resistor in SWx off time +#define IS31FL3743A_PDR_1K_OHM_SW_OFF 0b010 // 1 kOhm resistor in SWx off time +#define IS31FL3743A_PDR_2K_OHM_SW_OFF 0b011 // 2 kOhm resistor in SWx off time +#define IS31FL3743A_PDR_1K_OHM 0b100 // 1 kOhm resistor +#define IS31FL3743A_PDR_2K_OHM 0b101 // 2 kOhm resistor +#define IS31FL3743A_PDR_4K_OHM 0b110 // 4 kOhm resistor +#define IS31FL3743A_PDR_8K_OHM 0b111 // 8 kOhm resistor + +#define IS31FL3743A_PUR_0_OHM 0b000 // No pull-up resistor +#define IS31FL3743A_PUR_0K5_OHM_CS_OFF 0b001 // 0.5 kOhm resistor in CSy off time +#define IS31FL3743A_PUR_1K_OHM_CS_OFF 0b010 // 1 kOhm resistor in CSy off time +#define IS31FL3743A_PUR_2K_OHM_CS_OFF 0b011 // 2 kOhm resistor in CSy off time +#define IS31FL3743A_PUR_1K_OHM 0b100 // 1 kOhm resistor +#define IS31FL3743A_PUR_2K_OHM 0b101 // 2 kOhm resistor +#define IS31FL3743A_PUR_4K_OHM 0b110 // 4 kOhm resistor +#define IS31FL3743A_PUR_8K_OHM 0b111 // 8 kOhm resistor + +#define IS31FL3743A_SYNC_NONE 0b00 +#define IS31FL3743A_SYNC_SLAVE 0b10 +#define IS31FL3743A_SYNC_MASTER 0b11 + +#define SW1_CS1 0x00 +#define SW1_CS2 0x01 +#define SW1_CS3 0x02 +#define SW1_CS4 0x03 +#define SW1_CS5 0x04 +#define SW1_CS6 0x05 +#define SW1_CS7 0x06 +#define SW1_CS8 0x07 +#define SW1_CS9 0x08 +#define SW1_CS10 0x09 +#define SW1_CS11 0x0A +#define SW1_CS12 0x0B +#define SW1_CS13 0x0C +#define SW1_CS14 0x0D +#define SW1_CS15 0x0E +#define SW1_CS16 0x0F +#define SW1_CS17 0x10 +#define SW1_CS18 0x11 + +#define SW2_CS1 0x12 +#define SW2_CS2 0x13 +#define SW2_CS3 0x14 +#define SW2_CS4 0x15 +#define SW2_CS5 0x16 +#define SW2_CS6 0x17 +#define SW2_CS7 0x18 +#define SW2_CS8 0x19 +#define SW2_CS9 0x1A +#define SW2_CS10 0x1B +#define SW2_CS11 0x1C +#define SW2_CS12 0x1D +#define SW2_CS13 0x1E +#define SW2_CS14 0x1F +#define SW2_CS15 0x20 +#define SW2_CS16 0x21 +#define SW2_CS17 0x22 +#define SW2_CS18 0x23 + +#define SW3_CS1 0x24 +#define SW3_CS2 0x25 +#define SW3_CS3 0x26 +#define SW3_CS4 0x27 +#define SW3_CS5 0x28 +#define SW3_CS6 0x29 +#define SW3_CS7 0x2A +#define SW3_CS8 0x2B +#define SW3_CS9 0x2C +#define SW3_CS10 0x2D +#define SW3_CS11 0x2E +#define SW3_CS12 0x2F +#define SW3_CS13 0x30 +#define SW3_CS14 0x31 +#define SW3_CS15 0x32 +#define SW3_CS16 0x33 +#define SW3_CS17 0x34 +#define SW3_CS18 0x35 + +#define SW4_CS1 0x36 +#define SW4_CS2 0x37 +#define SW4_CS3 0x38 +#define SW4_CS4 0x39 +#define SW4_CS5 0x3A +#define SW4_CS6 0x3B +#define SW4_CS7 0x3C +#define SW4_CS8 0x3D +#define SW4_CS9 0x3E +#define SW4_CS10 0x3F +#define SW4_CS11 0x40 +#define SW4_CS12 0x41 +#define SW4_CS13 0x42 +#define SW4_CS14 0x43 +#define SW4_CS15 0x44 +#define SW4_CS16 0x45 +#define SW4_CS17 0x46 +#define SW4_CS18 0x47 + +#define SW5_CS1 0x48 +#define SW5_CS2 0x49 +#define SW5_CS3 0x4A +#define SW5_CS4 0x4B +#define SW5_CS5 0x4C +#define SW5_CS6 0x4D +#define SW5_CS7 0x4E +#define SW5_CS8 0x4F +#define SW5_CS9 0x50 +#define SW5_CS10 0x51 +#define SW5_CS11 0x52 +#define SW5_CS12 0x53 +#define SW5_CS13 0x54 +#define SW5_CS14 0x55 +#define SW5_CS15 0x56 +#define SW5_CS16 0x57 +#define SW5_CS17 0x58 +#define SW5_CS18 0x59 + +#define SW6_CS1 0x5A +#define SW6_CS2 0x5B +#define SW6_CS3 0x5C +#define SW6_CS4 0x5D +#define SW6_CS5 0x5E +#define SW6_CS6 0x5F +#define SW6_CS7 0x60 +#define SW6_CS8 0x61 +#define SW6_CS9 0x62 +#define SW6_CS10 0x63 +#define SW6_CS11 0x64 +#define SW6_CS12 0x65 +#define SW6_CS13 0x66 +#define SW6_CS14 0x67 +#define SW6_CS15 0x68 +#define SW6_CS16 0x69 +#define SW6_CS17 0x6A +#define SW6_CS18 0x6B + +#define SW7_CS1 0x6C +#define SW7_CS2 0x6D +#define SW7_CS3 0x6E +#define SW7_CS4 0x6F +#define SW7_CS5 0x70 +#define SW7_CS6 0x71 +#define SW7_CS7 0x72 +#define SW7_CS8 0x73 +#define SW7_CS9 0x74 +#define SW7_CS10 0x75 +#define SW7_CS11 0x76 +#define SW7_CS12 0x77 +#define SW7_CS13 0x78 +#define SW7_CS14 0x79 +#define SW7_CS15 0x7A +#define SW7_CS16 0x7B +#define SW7_CS17 0x7C +#define SW7_CS18 0x7D + +#define SW8_CS1 0x7E +#define SW8_CS2 0x7F +#define SW8_CS3 0x80 +#define SW8_CS4 0x81 +#define SW8_CS5 0x82 +#define SW8_CS6 0x83 +#define SW8_CS7 0x84 +#define SW8_CS8 0x85 +#define SW8_CS9 0x86 +#define SW8_CS10 0x87 +#define SW8_CS11 0x88 +#define SW8_CS12 0x89 +#define SW8_CS13 0x8A +#define SW8_CS14 0x8B +#define SW8_CS15 0x8C +#define SW8_CS16 0x8D +#define SW8_CS17 0x8E +#define SW8_CS18 0x8F + +#define SW9_CS1 0x90 +#define SW9_CS2 0x91 +#define SW9_CS3 0x92 +#define SW9_CS4 0x93 +#define SW9_CS5 0x94 +#define SW9_CS6 0x95 +#define SW9_CS7 0x96 +#define SW9_CS8 0x97 +#define SW9_CS9 0x98 +#define SW9_CS10 0x99 +#define SW9_CS11 0x9A +#define SW9_CS12 0x9B +#define SW9_CS13 0x9C +#define SW9_CS14 0x9D +#define SW9_CS15 0x9E +#define SW9_CS16 0x9F +#define SW9_CS17 0xA0 +#define SW9_CS18 0xA1 + +#define SW10_CS1 0xA2 +#define SW10_CS2 0xA3 +#define SW10_CS3 0xA4 +#define SW10_CS4 0xA5 +#define SW10_CS5 0xA6 +#define SW10_CS6 0xA7 +#define SW10_CS7 0xA8 +#define SW10_CS8 0xA9 +#define SW10_CS9 0xAA +#define SW10_CS10 0xAB +#define SW10_CS11 0xAC +#define SW10_CS12 0xAD +#define SW10_CS13 0xAE +#define SW10_CS14 0xAF +#define SW10_CS15 0xB0 +#define SW10_CS16 0xB1 +#define SW10_CS17 0xB2 +#define SW10_CS18 0xB3 + +#define SW11_CS1 0xB4 +#define SW11_CS2 0xB5 +#define SW11_CS3 0xB6 +#define SW11_CS4 0xB7 +#define SW11_CS5 0xB8 +#define SW11_CS6 0xB9 +#define SW11_CS7 0xBA +#define SW11_CS8 0xBB +#define SW11_CS9 0xBC +#define SW11_CS10 0xBD +#define SW11_CS11 0xBE +#define SW11_CS12 0xBF +#define SW11_CS13 0xC0 +#define SW11_CS14 0xC1 +#define SW11_CS15 0xC2 +#define SW11_CS16 0xC3 +#define SW11_CS17 0xC4 +#define SW11_CS18 0xC5 diff --git a/drivers/led/issi/is31fl3743a.c b/drivers/led/issi/is31fl3743a.c new file mode 100644 index 0000000000..6f13925f27 --- /dev/null +++ b/drivers/led/issi/is31fl3743a.c @@ -0,0 +1,249 @@ +/* Copyright 2017 Jason Williams + * Copyright 2018 Jack Humbert + * Copyright 2018 Yiancar + * Copyright 2020 MelGeek + * Copyright 2021 MasterSpoon + * + * 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 2 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/>. + */ + +#include "is31fl3743a.h" +#include "i2c_master.h" +#include "gpio.h" +#include "wait.h" + +#define IS31FL3743A_PWM_REGISTER_COUNT 198 +#define IS31FL3743A_SCALING_REGISTER_COUNT 198 + +#ifndef IS31FL3743A_I2C_TIMEOUT +# define IS31FL3743A_I2C_TIMEOUT 100 +#endif + +#ifndef IS31FL3743A_I2C_PERSISTENCE +# define IS31FL3743A_I2C_PERSISTENCE 0 +#endif + +#ifndef IS31FL3743A_CONFIGURATION +# define IS31FL3743A_CONFIGURATION 0x01 +#endif + +#ifndef IS31FL3743A_SW_PULLDOWN +# define IS31FL3743A_SW_PULLDOWN IS31FL3743A_PDR_2K_OHM_SW_OFF +#endif + +#ifndef IS31FL3743A_CS_PULLUP +# define IS31FL3743A_CS_PULLUP IS31FL3743A_PUR_2K_OHM_CS_OFF +#endif + +#ifndef IS31FL3743A_GLOBAL_CURRENT +# define IS31FL3743A_GLOBAL_CURRENT 0xFF +#endif + +#ifndef IS31FL3743A_SYNC_1 +# define IS31FL3743A_SYNC_1 IS31FL3743A_SYNC_NONE +#endif +#ifndef IS31FL3743A_SYNC_2 +# define IS31FL3743A_SYNC_2 IS31FL3743A_SYNC_NONE +#endif +#ifndef IS31FL3743A_SYNC_3 +# define IS31FL3743A_SYNC_3 IS31FL3743A_SYNC_NONE +#endif +#ifndef IS31FL3743A_SYNC_4 +# define IS31FL3743A_SYNC_4 IS31FL3743A_SYNC_NONE +#endif + +const uint8_t i2c_addresses[IS31FL3743A_DRIVER_COUNT] = { + IS31FL3743A_I2C_ADDRESS_1, +#ifdef IS31FL3743A_I2C_ADDRESS_2 + IS31FL3743A_I2C_ADDRESS_2, +# ifdef IS31FL3743A_I2C_ADDRESS_3 + IS31FL3743A_I2C_ADDRESS_3, +# ifdef IS31FL3743A_I2C_ADDRESS_4 + IS31FL3743A_I2C_ADDRESS_4, +# endif +# endif +#endif +}; + +const uint8_t driver_sync[IS31FL3743A_DRIVER_COUNT] = { + IS31FL3743A_SYNC_1, +#ifdef IS31FL3743A_I2C_ADDRESS_2 + IS31FL3743A_SYNC_2, +# ifdef IS31FL3743A_I2C_ADDRESS_3 + IS31FL3743A_SYNC_3, +# ifdef IS31FL3743A_I2C_ADDRESS_4 + IS31FL3743A_SYNC_4, +# endif +# endif +#endif +}; + +typedef struct is31fl3743a_driver_t { + uint8_t pwm_buffer[IS31FL3743A_PWM_REGISTER_COUNT]; + bool pwm_buffer_dirty; + uint8_t scaling_buffer[IS31FL3743A_SCALING_REGISTER_COUNT]; + bool scaling_buffer_dirty; +} PACKED is31fl3743a_driver_t; + +is31fl3743a_driver_t driver_buffers[IS31FL3743A_DRIVER_COUNT] = {{ + .pwm_buffer = {0}, + .pwm_buffer_dirty = false, + .scaling_buffer = {0}, + .scaling_buffer_dirty = false, +}}; + +void is31fl3743a_write_register(uint8_t index, uint8_t reg, uint8_t data) { +#if IS31FL3743A_I2C_PERSISTENCE > 0 + for (uint8_t i = 0; i < IS31FL3743A_I2C_PERSISTENCE; i++) { + if (i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3743A_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break; + } +#else + i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3743A_I2C_TIMEOUT); +#endif +} + +void is31fl3743a_select_page(uint8_t index, uint8_t page) { + is31fl3743a_write_register(index, IS31FL3743A_REG_COMMAND_WRITE_LOCK, IS31FL3743A_COMMAND_WRITE_LOCK_MAGIC); + is31fl3743a_write_register(index, IS31FL3743A_REG_COMMAND, page); +} + +void is31fl3743a_write_pwm_buffer(uint8_t index) { + // Assumes page 0 is already selected. + // Transmit PWM registers in 11 transfers of 18 bytes. + + // Iterate over the pwm_buffer contents at 18 byte intervals. + for (uint8_t i = 0; i < IS31FL3743A_PWM_REGISTER_COUNT; i += 18) { +#if IS31FL3743A_I2C_PERSISTENCE > 0 + for (uint8_t j = 0; j < IS31FL3743A_I2C_PERSISTENCE; j++) { + if (i2c_write_register(i2c_addresses[index] << 1, i + 1, driver_buffers[index].pwm_buffer + i, 18, IS31FL3743A_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break; + } +#else + i2c_write_register(i2c_addresses[index] << 1, i + 1, driver_buffers[index].pwm_buffer + i, 18, IS31FL3743A_I2C_TIMEOUT); +#endif + } +} + +void is31fl3743a_init_drivers(void) { + i2c_init(); + +#if defined(IS31FL3743A_SDB_PIN) + gpio_set_pin_output(IS31FL3743A_SDB_PIN); + gpio_write_pin_high(IS31FL3743A_SDB_PIN); +#endif + + for (uint8_t i = 0; i < IS31FL3743A_DRIVER_COUNT; i++) { + is31fl3743a_init(i); + } + + for (int i = 0; i < IS31FL3743A_LED_COUNT; i++) { + is31fl3743a_set_scaling_register(i, 0xFF, 0xFF, 0xFF); + } + + for (uint8_t i = 0; i < IS31FL3743A_DRIVER_COUNT; i++) { + is31fl3743a_update_scaling_registers(i); + } +} + +void is31fl3743a_init(uint8_t index) { + // In order to avoid the LEDs being driven with garbage data + // in the LED driver's PWM registers, shutdown is enabled last. + // Set up the mode and other settings, clear the PWM registers, + // then disable software shutdown. + + is31fl3743a_select_page(index, IS31FL3743A_COMMAND_SCALING); + + // Turn off all LEDs. + for (uint8_t i = 0; i < IS31FL3743A_SCALING_REGISTER_COUNT; i++) { + is31fl3743a_write_register(index, i + 1, 0x00); + } + + is31fl3743a_select_page(index, IS31FL3743A_COMMAND_PWM); + + for (uint8_t i = 0; i < IS31FL3743A_PWM_REGISTER_COUNT; i++) { + is31fl3743a_write_register(index, i + 1, 0x00); + } + + is31fl3743a_select_page(index, IS31FL3743A_COMMAND_FUNCTION); + + uint8_t sync = driver_sync[index]; + + is31fl3743a_write_register(index, IS31FL3743A_FUNCTION_REG_PULLDOWNUP, (IS31FL3743A_SW_PULLDOWN << 4) | IS31FL3743A_CS_PULLUP); + is31fl3743a_write_register(index, IS31FL3743A_FUNCTION_REG_GLOBAL_CURRENT, IS31FL3743A_GLOBAL_CURRENT); + is31fl3743a_write_register(index, IS31FL3743A_FUNCTION_REG_SPREAD_SPECTRUM, (sync & 0b11) << 6); + is31fl3743a_write_register(index, IS31FL3743A_FUNCTION_REG_CONFIGURATION, IS31FL3743A_CONFIGURATION); + + // Wait 10ms to ensure the device has woken up. + wait_ms(10); +} + +void is31fl3743a_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) { + is31fl3743a_led_t led; + + if (index >= 0 && index < IS31FL3743A_LED_COUNT) { + memcpy_P(&led, (&g_is31fl3743a_leds[index]), sizeof(led)); + + if (driver_buffers[led.driver].pwm_buffer[led.r] == red && driver_buffers[led.driver].pwm_buffer[led.g] == green && driver_buffers[led.driver].pwm_buffer[led.b] == blue) { + return; + } + + driver_buffers[led.driver].pwm_buffer[led.r] = red; + driver_buffers[led.driver].pwm_buffer[led.g] = green; + driver_buffers[led.driver].pwm_buffer[led.b] = blue; + driver_buffers[led.driver].pwm_buffer_dirty = true; + } +} + +void is31fl3743a_set_color_all(uint8_t red, uint8_t green, uint8_t blue) { + for (int i = 0; i < IS31FL3743A_LED_COUNT; i++) { + is31fl3743a_set_color(i, red, green, blue); + } +} + +void is31fl3743a_set_scaling_register(uint8_t index, uint8_t red, uint8_t green, uint8_t blue) { + is31fl3743a_led_t led; + memcpy_P(&led, (&g_is31fl3743a_leds[index]), sizeof(led)); + + driver_buffers[led.driver].scaling_buffer[led.r] = red; + driver_buffers[led.driver].scaling_buffer[led.g] = green; + driver_buffers[led.driver].scaling_buffer[led.b] = blue; + driver_buffers[led.driver].scaling_buffer_dirty = true; +} + +void is31fl3743a_update_pwm_buffers(uint8_t index) { + if (driver_buffers[index].pwm_buffer_dirty) { + is31fl3743a_select_page(index, IS31FL3743A_COMMAND_PWM); + + is31fl3743a_write_pwm_buffer(index); + + driver_buffers[index].pwm_buffer_dirty = false; + } +} + +void is31fl3743a_update_scaling_registers(uint8_t index) { + if (driver_buffers[index].scaling_buffer_dirty) { + is31fl3743a_select_page(index, IS31FL3743A_COMMAND_SCALING); + + for (uint8_t i = 0; i < IS31FL3743A_SCALING_REGISTER_COUNT; i++) { + is31fl3743a_write_register(index, i + 1, driver_buffers[index].scaling_buffer[i]); + } + + driver_buffers[index].scaling_buffer_dirty = false; + } +} + +void is31fl3743a_flush(void) { + for (uint8_t i = 0; i < IS31FL3743A_DRIVER_COUNT; i++) { + is31fl3743a_update_pwm_buffers(i); + } +} diff --git a/drivers/led/issi/is31fl3743a.h b/drivers/led/issi/is31fl3743a.h new file mode 100644 index 0000000000..48aeab46ab --- /dev/null +++ b/drivers/led/issi/is31fl3743a.h @@ -0,0 +1,541 @@ +/* Copyright 2017 Jason Williams + * Copyright 2018 Jack Humbert + * Copyright 2018 Yiancar + * Copyright 2020 MelGeek + * Copyright 2021 MasterSpoon + * + * 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 2 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/>. + */ + +#pragma once + +#include <stdint.h> +#include <stdbool.h> +#include "progmem.h" +#include "util.h" + +#define IS31FL3743A_REG_ID 0xFC + +#define IS31FL3743A_REG_COMMAND 0xFD + +#define IS31FL3743A_COMMAND_PWM 0x00 +#define IS31FL3743A_COMMAND_SCALING 0x01 +#define IS31FL3743A_COMMAND_FUNCTION 0x02 + +#define IS31FL3743A_FUNCTION_REG_CONFIGURATION 0x00 +#define IS31FL3743A_FUNCTION_REG_GLOBAL_CURRENT 0x01 +#define IS31FL3743A_FUNCTION_REG_PULLDOWNUP 0x02 +#define IS31FL3743A_FUNCTION_REG_TEMPERATURE 0x24 +#define IS31FL3743A_FUNCTION_REG_SPREAD_SPECTRUM 0x25 +#define IS31FL3743A_FUNCTION_REG_RESET 0x2F + +#define IS31FL3743A_REG_COMMAND_WRITE_LOCK 0xFE +#define IS31FL3743A_COMMAND_WRITE_LOCK_MAGIC 0xC5 + +#define IS31FL3743A_I2C_ADDRESS_GND_GND 0x20 +#define IS31FL3743A_I2C_ADDRESS_GND_SCL 0x21 +#define IS31FL3743A_I2C_ADDRESS_GND_SDA 0x22 +#define IS31FL3743A_I2C_ADDRESS_GND_VCC 0x23 +#define IS31FL3743A_I2C_ADDRESS_SCL_GND 0x24 +#define IS31FL3743A_I2C_ADDRESS_SCL_SCL 0x25 +#define IS31FL3743A_I2C_ADDRESS_SCL_SDA 0x26 +#define IS31FL3743A_I2C_ADDRESS_SCL_VCC 0x27 +#define IS31FL3743A_I2C_ADDRESS_SDA_GND 0x28 +#define IS31FL3743A_I2C_ADDRESS_SDA_SCL 0x29 +#define IS31FL3743A_I2C_ADDRESS_SDA_SDA 0x2A +#define IS31FL3743A_I2C_ADDRESS_SDA_VCC 0x2B +#define IS31FL3743A_I2C_ADDRESS_VCC_GND 0x2C +#define IS31FL3743A_I2C_ADDRESS_VCC_SCL 0x2D +#define IS31FL3743A_I2C_ADDRESS_VCC_SDA 0x2E +#define IS31FL3743A_I2C_ADDRESS_VCC_VCC 0x2F + +#if defined(RGB_MATRIX_IS31FL3743A) +# define IS31FL3743A_LED_COUNT RGB_MATRIX_LED_COUNT +#endif + +#if defined(IS31FL3743A_I2C_ADDRESS_4) +# define IS31FL3743A_DRIVER_COUNT 4 +#elif defined(IS31FL3743A_I2C_ADDRESS_3) +# define IS31FL3743A_DRIVER_COUNT 3 +#elif defined(IS31FL3743A_I2C_ADDRESS_2) +# define IS31FL3743A_DRIVER_COUNT 2 +#elif defined(IS31FL3743A_I2C_ADDRESS_1) +# define IS31FL3743A_DRIVER_COUNT 1 +#endif + +typedef struct is31fl3743a_led_t { + uint8_t driver : 2; + uint8_t r; + uint8_t g; + uint8_t b; +} PACKED is31fl3743a_led_t; + +extern const is31fl3743a_led_t PROGMEM g_is31fl3743a_leds[IS31FL3743A_LED_COUNT]; + +void is31fl3743a_init_drivers(void); +void is31fl3743a_init(uint8_t index); +void is31fl3743a_write_register(uint8_t index, uint8_t reg, uint8_t data); +void is31fl3743a_select_page(uint8_t index, uint8_t page); + +void is31fl3743a_set_color(int index, uint8_t red, uint8_t green, uint8_t blue); +void is31fl3743a_set_color_all(uint8_t red, uint8_t green, uint8_t blue); + +void is31fl3743a_set_scaling_register(uint8_t index, uint8_t red, uint8_t green, uint8_t blue); + +void is31fl3743a_update_pwm_buffers(uint8_t index); +void is31fl3743a_update_scaling_registers(uint8_t index); + +void is31fl3743a_flush(void); + +#define IS31FL3743A_PDR_0_OHM 0b000 // No pull-down resistor +#define IS31FL3743A_PDR_0K5_OHM_SW_OFF 0b001 // 0.5 kOhm resistor in SWx off time +#define IS31FL3743A_PDR_1K_OHM_SW_OFF 0b010 // 1 kOhm resistor in SWx off time +#define IS31FL3743A_PDR_2K_OHM_SW_OFF 0b011 // 2 kOhm resistor in SWx off time +#define IS31FL3743A_PDR_1K_OHM 0b100 // 1 kOhm resistor +#define IS31FL3743A_PDR_2K_OHM 0b101 // 2 kOhm resistor +#define IS31FL3743A_PDR_4K_OHM 0b110 // 4 kOhm resistor +#define IS31FL3743A_PDR_8K_OHM 0b111 // 8 kOhm resistor + +#define IS31FL3743A_PUR_0_OHM 0b000 // No pull-up resistor +#define IS31FL3743A_PUR_0K5_OHM_CS_OFF 0b001 // 0.5 kOhm resistor in CSy off time +#define IS31FL3743A_PUR_1K_OHM_CS_OFF 0b010 // 1 kOhm resistor in CSy off time +#define IS31FL3743A_PUR_2K_OHM_CS_OFF 0b011 // 2 kOhm resistor in CSy off time +#define IS31FL3743A_PUR_1K_OHM 0b100 // 1 kOhm resistor +#define IS31FL3743A_PUR_2K_OHM 0b101 // 2 kOhm resistor +#define IS31FL3743A_PUR_4K_OHM 0b110 // 4 kOhm resistor +#define IS31FL3743A_PUR_8K_OHM 0b111 // 8 kOhm resistor + +#define IS31FL3743A_SYNC_NONE 0b00 +#define IS31FL3743A_SYNC_SLAVE 0b10 +#define IS31FL3743A_SYNC_MASTER 0b11 + +#define SW1_CS1 0x00 +#define SW1_CS2 0x01 +#define SW1_CS3 0x02 +#define SW1_CS4 0x03 +#define SW1_CS5 0x04 +#define SW1_CS6 0x05 +#define SW1_CS7 0x06 +#define SW1_CS8 0x07 +#define SW1_CS9 0x08 +#define SW1_CS10 0x09 +#define SW1_CS11 0x0A +#define SW1_CS12 0x0B +#define SW1_CS13 0x0C +#define SW1_CS14 0x0D +#define SW1_CS15 0x0E +#define SW1_CS16 0x0F +#define SW1_CS17 0x10 +#define SW1_CS18 0x11 + +#define SW2_CS1 0x12 +#define SW2_CS2 0x13 +#define SW2_CS3 0x14 +#define SW2_CS4 0x15 +#define SW2_CS5 0x16 +#define SW2_CS6 0x17 +#define SW2_CS7 0x18 +#define SW2_CS8 0x19 +#define SW2_CS9 0x1A +#define SW2_CS10 0x1B +#define SW2_CS11 0x1C +#define SW2_CS12 0x1D +#define SW2_CS13 0x1E +#define SW2_CS14 0x1F +#define SW2_CS15 0x20 +#define SW2_CS16 0x21 +#define SW2_CS17 0x22 +#define SW2_CS18 0x23 + +#define SW3_CS1 0x24 +#define SW3_CS2 0x25 +#define SW3_CS3 0x26 +#define SW3_CS4 0x27 +#define SW3_CS5 0x28 +#define SW3_CS6 0x29 +#define SW3_CS7 0x2A +#define SW3_CS8 0x2B +#define SW3_CS9 0x2C +#define SW3_CS10 0x2D +#define SW3_CS11 0x2E +#define SW3_CS12 0x2F +#define SW3_CS13 0x30 +#define SW3_CS14 0x31 +#define SW3_CS15 0x32 +#define SW3_CS16 0x33 +#define SW3_CS17 0x34 +#define SW3_CS18 0x35 + +#define SW4_CS1 0x36 +#define SW4_CS2 0x37 +#define SW4_CS3 0x38 +#define SW4_CS4 0x39 +#define SW4_CS5 0x3A +#define SW4_CS6 0x3B +#define SW4_CS7 0x3C +#define SW4_CS8 0x3D +#define SW4_CS9 0x3E +#define SW4_CS10 0x3F +#define SW4_CS11 0x40 +#define SW4_CS12 0x41 +#define SW4_CS13 0x42 +#define SW4_CS14 0x43 +#define SW4_CS15 0x44 +#define SW4_CS16 0x45 +#define SW4_CS17 0x46 +#define SW4_CS18 0x47 + +#define SW5_CS1 0x48 +#define SW5_CS2 0x49 +#define SW5_CS3 0x4A +#define SW5_CS4 0x4B +#define SW5_CS5 0x4C +#define SW5_CS6 0x4D +#define SW5_CS7 0x4E +#define SW5_CS8 0x4F +#define SW5_CS9 0x50 +#define SW5_CS10 0x51 +#define SW5_CS11 0x52 +#define SW5_CS12 0x53 +#define SW5_CS13 0x54 +#define SW5_CS14 0x55 +#define SW5_CS15 0x56 +#define SW5_CS16 0x57 +#define SW5_CS17 0x58 +#define SW5_CS18 0x59 + +#define SW6_CS1 0x5A +#define SW6_CS2 0x5B +#define SW6_CS3 0x5C +#define SW6_CS4 0x5D +#define SW6_CS5 0x5E +#define SW6_CS6 0x5F +#define SW6_CS7 0x60 +#define SW6_CS8 0x61 +#define SW6_CS9 0x62 +#define SW6_CS10 0x63 +#define SW6_CS11 0x64 +#define SW6_CS12 0x65 +#define SW6_CS13 0x66 +#define SW6_CS14 0x67 +#define SW6_CS15 0x68 +#define SW6_CS16 0x69 +#define SW6_CS17 0x6A +#define SW6_CS18 0x6B + +#define SW7_CS1 0x6C +#define SW7_CS2 0x6D +#define SW7_CS3 0x6E +#define SW7_CS4 0x6F +#define SW7_CS5 0x70 +#define SW7_CS6 0x71 +#define SW7_CS7 0x72 +#define SW7_CS8 0x73 +#define SW7_CS9 0x74 +#define SW7_CS10 0x75 +#define SW7_CS11 0x76 +#define SW7_CS12 0x77 +#define SW7_CS13 0x78 +#define SW7_CS14 0x79 +#define SW7_CS15 0x7A +#define SW7_CS16 0x7B +#define SW7_CS17 0x7C +#define SW7_CS18 0x7D + +#define SW8_CS1 0x7E +#define SW8_CS2 0x7F +#define SW8_CS3 0x80 +#define SW8_CS4 0x81 +#define SW8_CS5 0x82 +#define SW8_CS6 0x83 +#define SW8_CS7 0x84 +#define SW8_CS8 0x85 +#define SW8_CS9 0x86 +#define SW8_CS10 0x87 +#define SW8_CS11 0x88 +#define SW8_CS12 0x89 +#define SW8_CS13 0x8A +#define SW8_CS14 0x8B +#define SW8_CS15 0x8C +#define SW8_CS16 0x8D +#define SW8_CS17 0x8E +#define SW8_CS18 0x8F + +#define SW9_CS1 0x90 +#define SW9_CS2 0x91 +#define SW9_CS3 0x92 +#define SW9_CS4 0x93 +#define SW9_CS5 0x94 +#define SW9_CS6 0x95 +#define SW9_CS7 0x96 +#define SW9_CS8 0x97 +#define SW9_CS9 0x98 +#define SW9_CS10 0x99 +#define SW9_CS11 0x9A +#define SW9_CS12 0x9B +#define SW9_CS13 0x9C +#define SW9_CS14 0x9D +#define SW9_CS15 0x9E +#define SW9_CS16 0x9F +#define SW9_CS17 0xA0 +#define SW9_CS18 0xA1 + +#define SW10_CS1 0xA2 +#define SW10_CS2 0xA3 +#define SW10_CS3 0xA4 +#define SW10_CS4 0xA5 +#define SW10_CS5 0xA6 +#define SW10_CS6 0xA7 +#define SW10_CS7 0xA8 +#define SW10_CS8 0xA9 +#define SW10_CS9 0xAA +#define SW10_CS10 0xAB +#define SW10_CS11 0xAC +#define SW10_CS12 0xAD +#define SW10_CS13 0xAE +#define SW10_CS14 0xAF +#define SW10_CS15 0xB0 +#define SW10_CS16 0xB1 +#define SW10_CS17 0xB2 +#define SW10_CS18 0xB3 + +#define SW11_CS1 0xB4 +#define SW11_CS2 0xB5 +#define SW11_CS3 0xB6 +#define SW11_CS4 0xB7 +#define SW11_CS5 0xB8 +#define SW11_CS6 0xB9 +#define SW11_CS7 0xBA +#define SW11_CS8 0xBB +#define SW11_CS9 0xBC +#define SW11_CS10 0xBD +#define SW11_CS11 0xBE +#define SW11_CS12 0xBF +#define SW11_CS13 0xC0 +#define SW11_CS14 0xC1 +#define SW11_CS15 0xC2 +#define SW11_CS16 0xC3 +#define SW11_CS17 0xC4 +#define SW11_CS18 0xC5 + +// DEPRECATED - DO NOT USE + +#define CS1_SW1 SW1_CS1 +#define CS2_SW1 SW1_CS2 +#define CS3_SW1 SW1_CS3 +#define CS4_SW1 SW1_CS4 +#define CS5_SW1 SW1_CS5 +#define CS6_SW1 SW1_CS6 +#define CS7_SW1 SW1_CS7 +#define CS8_SW1 SW1_CS8 +#define CS9_SW1 SW1_CS9 +#define CS10_SW1 SW1_CS10 +#define CS11_SW1 SW1_CS11 +#define CS12_SW1 SW1_CS12 +#define CS13_SW1 SW1_CS13 +#define CS14_SW1 SW1_CS14 +#define CS15_SW1 SW1_CS15 +#define CS16_SW1 SW1_CS16 +#define CS17_SW1 SW1_CS17 +#define CS18_SW1 SW1_CS18 + +#define CS1_SW2 SW2_CS1 +#define CS2_SW2 SW2_CS2 +#define CS3_SW2 SW2_CS3 +#define CS4_SW2 SW2_CS4 +#define CS5_SW2 SW2_CS5 +#define CS6_SW2 SW2_CS6 +#define CS7_SW2 SW2_CS7 +#define CS8_SW2 SW2_CS8 +#define CS9_SW2 SW2_CS9 +#define CS10_SW2 SW2_CS10 +#define CS11_SW2 SW2_CS11 +#define CS12_SW2 SW2_CS12 +#define CS13_SW2 SW2_CS13 +#define CS14_SW2 SW2_CS14 +#define CS15_SW2 SW2_CS15 +#define CS16_SW2 SW2_CS16 +#define CS17_SW2 SW2_CS17 +#define CS18_SW2 SW2_CS18 + +#define CS1_SW3 SW3_CS1 +#define CS2_SW3 SW3_CS2 +#define CS3_SW3 SW3_CS3 +#define CS4_SW3 SW3_CS4 +#define CS5_SW3 SW3_CS5 +#define CS6_SW3 SW3_CS6 +#define CS7_SW3 SW3_CS7 +#define CS8_SW3 SW3_CS8 +#define CS9_SW3 SW3_CS9 +#define CS10_SW3 SW3_CS10 +#define CS11_SW3 SW3_CS11 +#define CS12_SW3 SW3_CS12 +#define CS13_SW3 SW3_CS13 +#define CS14_SW3 SW3_CS14 +#define CS15_SW3 SW3_CS15 +#define CS16_SW3 SW3_CS16 +#define CS17_SW3 SW3_CS17 +#define CS18_SW3 SW3_CS18 + +#define CS1_SW4 SW4_CS1 +#define CS2_SW4 SW4_CS2 +#define CS3_SW4 SW4_CS3 +#define CS4_SW4 SW4_CS4 +#define CS5_SW4 SW4_CS5 +#define CS6_SW4 SW4_CS6 +#define CS7_SW4 SW4_CS7 +#define CS8_SW4 SW4_CS8 +#define CS9_SW4 SW4_CS9 +#define CS10_SW4 SW4_CS10 +#define CS11_SW4 SW4_CS11 +#define CS12_SW4 SW4_CS12 +#define CS13_SW4 SW4_CS13 +#define CS14_SW4 SW4_CS14 +#define CS15_SW4 SW4_CS15 +#define CS16_SW4 SW4_CS16 +#define CS17_SW4 SW4_CS17 +#define CS18_SW4 SW4_CS18 + +#define CS1_SW5 SW5_CS1 +#define CS2_SW5 SW5_CS2 +#define CS3_SW5 SW5_CS3 +#define CS4_SW5 SW5_CS4 +#define CS5_SW5 SW5_CS5 +#define CS6_SW5 SW5_CS6 +#define CS7_SW5 SW5_CS7 +#define CS8_SW5 SW5_CS8 +#define CS9_SW5 SW5_CS9 +#define CS10_SW5 SW5_CS10 +#define CS11_SW5 SW5_CS11 +#define CS12_SW5 SW5_CS12 +#define CS13_SW5 SW5_CS13 +#define CS14_SW5 SW5_CS14 +#define CS15_SW5 SW5_CS15 +#define CS16_SW5 SW5_CS16 +#define CS17_SW5 SW5_CS17 +#define CS18_SW5 SW5_CS18 + +#define CS1_SW6 SW6_CS1 +#define CS2_SW6 SW6_CS2 +#define CS3_SW6 SW6_CS3 +#define CS4_SW6 SW6_CS4 +#define CS5_SW6 SW6_CS5 +#define CS6_SW6 SW6_CS6 +#define CS7_SW6 SW6_CS7 +#define CS8_SW6 SW6_CS8 +#define CS9_SW6 SW6_CS9 +#define CS10_SW6 SW6_CS10 +#define CS11_SW6 SW6_CS11 +#define CS12_SW6 SW6_CS12 +#define CS13_SW6 SW6_CS13 +#define CS14_SW6 SW6_CS14 +#define CS15_SW6 SW6_CS15 +#define CS16_SW6 SW6_CS16 +#define CS17_SW6 SW6_CS17 +#define CS18_SW6 SW6_CS18 + +#define CS1_SW7 SW7_CS1 +#define CS2_SW7 SW7_CS2 +#define CS3_SW7 SW7_CS3 +#define CS4_SW7 SW7_CS4 +#define CS5_SW7 SW7_CS5 +#define CS6_SW7 SW7_CS6 +#define CS7_SW7 SW7_CS7 +#define CS8_SW7 SW7_CS8 +#define CS9_SW7 SW7_CS9 +#define CS10_SW7 SW7_CS10 +#define CS11_SW7 SW7_CS11 +#define CS12_SW7 SW7_CS12 +#define CS13_SW7 SW7_CS13 +#define CS14_SW7 SW7_CS14 +#define CS15_SW7 SW7_CS15 +#define CS16_SW7 SW7_CS16 +#define CS17_SW7 SW7_CS17 +#define CS18_SW7 SW7_CS18 + +#define CS1_SW8 SW8_CS1 +#define CS2_SW8 SW8_CS2 +#define CS3_SW8 SW8_CS3 +#define CS4_SW8 SW8_CS4 +#define CS5_SW8 SW8_CS5 +#define CS6_SW8 SW8_CS6 +#define CS7_SW8 SW8_CS7 +#define CS8_SW8 SW8_CS8 +#define CS9_SW8 SW8_CS9 +#define CS10_SW8 SW8_CS10 +#define CS11_SW8 SW8_CS11 +#define CS12_SW8 SW8_CS12 +#define CS13_SW8 SW8_CS13 +#define CS14_SW8 SW8_CS14 +#define CS15_SW8 SW8_CS15 +#define CS16_SW8 SW8_CS16 +#define CS17_SW8 SW8_CS17 +#define CS18_SW8 SW8_CS18 + +#define CS1_SW9 SW9_CS1 +#define CS2_SW9 SW9_CS2 +#define CS3_SW9 SW9_CS3 +#define CS4_SW9 SW9_CS4 +#define CS5_SW9 SW9_CS5 +#define CS6_SW9 SW9_CS6 +#define CS7_SW9 SW9_CS7 +#define CS8_SW9 SW9_CS8 +#define CS9_SW9 SW9_CS9 +#define CS10_SW9 SW9_CS10 +#define CS11_SW9 SW9_CS11 +#define CS12_SW9 SW9_CS12 +#define CS13_SW9 SW9_CS13 +#define CS14_SW9 SW9_CS14 +#define CS15_SW9 SW9_CS15 +#define CS16_SW9 SW9_CS16 +#define CS17_SW9 SW9_CS17 +#define CS18_SW9 SW9_CS18 + +#define CS1_SW10 SW10_CS1 +#define CS2_SW10 SW10_CS2 +#define CS3_SW10 SW10_CS3 +#define CS4_SW10 SW10_CS4 +#define CS5_SW10 SW10_CS5 +#define CS6_SW10 SW10_CS6 +#define CS7_SW10 SW10_CS7 +#define CS8_SW10 SW10_CS8 +#define CS9_SW10 SW10_CS9 +#define CS10_SW10 SW10_CS10 +#define CS11_SW10 SW10_CS11 +#define CS12_SW10 SW10_CS12 +#define CS13_SW10 SW10_CS13 +#define CS14_SW10 SW10_CS14 +#define CS15_SW10 SW10_CS15 +#define CS16_SW10 SW10_CS16 +#define CS17_SW10 SW10_CS17 +#define CS18_SW10 SW10_CS18 + +#define CS1_SW11 SW11_CS1 +#define CS2_SW11 SW11_CS2 +#define CS3_SW11 SW11_CS3 +#define CS4_SW11 SW11_CS4 +#define CS5_SW11 SW11_CS5 +#define CS6_SW11 SW11_CS6 +#define CS7_SW11 SW11_CS7 +#define CS8_SW11 SW11_CS8 +#define CS9_SW11 SW11_CS9 +#define CS10_SW11 SW11_CS10 +#define CS11_SW11 SW11_CS11 +#define CS12_SW11 SW11_CS12 +#define CS13_SW11 SW11_CS13 +#define CS14_SW11 SW11_CS14 +#define CS15_SW11 SW11_CS15 +#define CS16_SW11 SW11_CS16 +#define CS17_SW11 SW11_CS17 +#define CS18_SW11 SW11_CS18 diff --git a/drivers/led/issi/is31fl3745-mono.c b/drivers/led/issi/is31fl3745-mono.c new file mode 100644 index 0000000000..a6ab699245 --- /dev/null +++ b/drivers/led/issi/is31fl3745-mono.c @@ -0,0 +1,245 @@ +/* Copyright 2017 Jason Williams + * Copyright 2018 Jack Humbert + * Copyright 2018 Yiancar + * Copyright 2020 MelGeek + * Copyright 2021 MasterSpoon + * + * 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 2 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/>. + */ + +#include "is31fl3745-mono.h" +#include "i2c_master.h" +#include "gpio.h" +#include "wait.h" + +#define IS31FL3745_PWM_REGISTER_COUNT 144 +#define IS31FL3745_SCALING_REGISTER_COUNT 144 + +#ifndef IS31FL3745_I2C_TIMEOUT +# define IS31FL3745_I2C_TIMEOUT 100 +#endif + +#ifndef IS31FL3745_I2C_PERSISTENCE +# define IS31FL3745_I2C_PERSISTENCE 0 +#endif + +#ifndef IS31FL3745_CONFIGURATION +# define IS31FL3745_CONFIGURATION 0x31 +#endif + +#ifndef IS31FL3745_SW_PULLDOWN +# define IS31FL3745_SW_PULLDOWN IS31FL3745_PDR_2K_OHM_SW_OFF +#endif + +#ifndef IS31FL3745_CS_PULLUP +# define IS31FL3745_CS_PULLUP IS31FL3745_PUR_2K_OHM_CS_OFF +#endif + +#ifndef IS31FL3745_GLOBAL_CURRENT +# define IS31FL3745_GLOBAL_CURRENT 0xFF +#endif + +#ifndef IS31FL3745_SYNC_1 +# define IS31FL3745_SYNC_1 IS31FL3745_SYNC_NONE +#endif +#ifndef IS31FL3745_SYNC_2 +# define IS31FL3745_SYNC_2 IS31FL3745_SYNC_NONE +#endif +#ifndef IS31FL3745_SYNC_3 +# define IS31FL3745_SYNC_3 IS31FL3745_SYNC_NONE +#endif +#ifndef IS31FL3745_SYNC_4 +# define IS31FL3745_SYNC_4 IS31FL3745_SYNC_NONE +#endif + +const uint8_t i2c_addresses[IS31FL3745_DRIVER_COUNT] = { + IS31FL3745_I2C_ADDRESS_1, +#ifdef IS31FL3745_I2C_ADDRESS_2 + IS31FL3745_I2C_ADDRESS_2, +# ifdef IS31FL3745_I2C_ADDRESS_3 + IS31FL3745_I2C_ADDRESS_3, +# ifdef IS31FL3745_I2C_ADDRESS_4 + IS31FL3745_I2C_ADDRESS_4, +# endif +# endif +#endif +}; + +const uint8_t driver_sync[IS31FL3745_DRIVER_COUNT] = { + IS31FL3745_SYNC_1, +#ifdef IS31FL3745_I2C_ADDRESS_2 + IS31FL3745_SYNC_2, +# ifdef IS31FL3745_I2C_ADDRESS_3 + IS31FL3745_SYNC_3, +# ifdef IS31FL3745_I2C_ADDRESS_4 + IS31FL3745_SYNC_4, +# endif +# endif +#endif +}; + +typedef struct is31fl3745_driver_t { + uint8_t pwm_buffer[IS31FL3745_PWM_REGISTER_COUNT]; + bool pwm_buffer_dirty; + uint8_t scaling_buffer[IS31FL3745_SCALING_REGISTER_COUNT]; + bool scaling_buffer_dirty; +} PACKED is31fl3745_driver_t; + +is31fl3745_driver_t driver_buffers[IS31FL3745_DRIVER_COUNT] = {{ + .pwm_buffer = {0}, + .pwm_buffer_dirty = false, + .scaling_buffer = {0}, + .scaling_buffer_dirty = false, +}}; + +void is31fl3745_write_register(uint8_t index, uint8_t reg, uint8_t data) { +#if IS31FL3745_I2C_PERSISTENCE > 0 + for (uint8_t i = 0; i < IS31FL3745_I2C_PERSISTENCE; i++) { + if (i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3745_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break; + } +#else + i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3745_I2C_TIMEOUT); +#endif +} + +void is31fl3745_select_page(uint8_t index, uint8_t page) { + is31fl3745_write_register(index, IS31FL3745_REG_COMMAND_WRITE_LOCK, IS31FL3745_COMMAND_WRITE_LOCK_MAGIC); + is31fl3745_write_register(index, IS31FL3745_REG_COMMAND, page); +} + +void is31fl3745_write_pwm_buffer(uint8_t index) { + // Assumes page 0 is already selected. + // Transmit PWM registers in 8 transfers of 18 bytes. + + // Iterate over the pwm_buffer contents at 18 byte intervals. + for (uint8_t i = 0; i < IS31FL3745_PWM_REGISTER_COUNT; i += 18) { +#if IS31FL3745_I2C_PERSISTENCE > 0 + for (uint8_t j = 0; j < IS31FL3745_I2C_PERSISTENCE; j++) { + if (i2c_write_register(i2c_addresses[index] << 1, i + 1, driver_buffers[index].pwm_buffer + i, 18, IS31FL3745_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break; + } +#else + i2c_write_register(i2c_addresses[index] << 1, i + 1, driver_buffers[index].pwm_buffer + i, 18, IS31FL3745_I2C_TIMEOUT); +#endif + } +} + +void is31fl3745_init_drivers(void) { + i2c_init(); + +#if defined(IS31FL3745_SDB_PIN) + gpio_set_pin_output(IS31FL3745_SDB_PIN); + gpio_write_pin_high(IS31FL3745_SDB_PIN); +#endif + + for (uint8_t i = 0; i < IS31FL3745_DRIVER_COUNT; i++) { + is31fl3745_init(i); + } + + for (int i = 0; i < IS31FL3745_LED_COUNT; i++) { + is31fl3745_set_scaling_register(i, 0xFF); + } + + for (uint8_t i = 0; i < IS31FL3745_DRIVER_COUNT; i++) { + is31fl3745_update_scaling_registers(i); + } +} + +void is31fl3745_init(uint8_t index) { + // In order to avoid the LEDs being driven with garbage data + // in the LED driver's PWM registers, shutdown is enabled last. + // Set up the mode and other settings, clear the PWM registers, + // then disable software shutdown. + + is31fl3745_select_page(index, IS31FL3745_COMMAND_SCALING); + + // Turn off all LEDs. + for (uint8_t i = 0; i < IS31FL3745_SCALING_REGISTER_COUNT; i++) { + is31fl3745_write_register(index, i + 1, 0x00); + } + + is31fl3745_select_page(index, IS31FL3745_COMMAND_PWM); + + for (uint8_t i = 0; i < IS31FL3745_PWM_REGISTER_COUNT; i++) { + is31fl3745_write_register(index, i + 1, 0x00); + } + + is31fl3745_select_page(index, IS31FL3745_COMMAND_FUNCTION); + + uint8_t sync = driver_sync[index]; + + is31fl3745_write_register(index, IS31FL3745_FUNCTION_REG_PULLDOWNUP, (IS31FL3745_SW_PULLDOWN << 4) | IS31FL3745_CS_PULLUP); + is31fl3745_write_register(index, IS31FL3745_FUNCTION_REG_GLOBAL_CURRENT, IS31FL3745_GLOBAL_CURRENT); + is31fl3745_write_register(index, IS31FL3745_FUNCTION_REG_SPREAD_SPECTRUM, (sync & 0b11) << 6); + is31fl3745_write_register(index, IS31FL3745_FUNCTION_REG_CONFIGURATION, IS31FL3745_CONFIGURATION); + + // Wait 10ms to ensure the device has woken up. + wait_ms(10); +} + +void is31fl3745_set_value(int index, uint8_t value) { + is31fl3745_led_t led; + + if (index >= 0 && index < IS31FL3745_LED_COUNT) { + memcpy_P(&led, (&g_is31fl3745_leds[index]), sizeof(led)); + + if (driver_buffers[led.driver].pwm_buffer[led.v] == value) { + return; + } + + driver_buffers[led.driver].pwm_buffer[led.v] = value; + driver_buffers[led.driver].pwm_buffer_dirty = true; + } +} + +void is31fl3745_set_value_all(uint8_t value) { + for (int i = 0; i < IS31FL3745_LED_COUNT; i++) { + is31fl3745_set_value(i, value); + } +} + +void is31fl3745_set_scaling_register(uint8_t index, uint8_t value) { + is31fl3745_led_t led; + memcpy_P(&led, (&g_is31fl3745_leds[index]), sizeof(led)); + + driver_buffers[led.driver].scaling_buffer[led.v] = value; + driver_buffers[led.driver].scaling_buffer_dirty = true; +} + +void is31fl3745_update_pwm_buffers(uint8_t index) { + if (driver_buffers[index].pwm_buffer_dirty) { + is31fl3745_select_page(index, IS31FL3745_COMMAND_PWM); + + is31fl3745_write_pwm_buffer(index); + + driver_buffers[index].pwm_buffer_dirty = false; + } +} + +void is31fl3745_update_scaling_registers(uint8_t index) { + if (driver_buffers[index].scaling_buffer_dirty) { + is31fl3745_select_page(index, IS31FL3745_COMMAND_SCALING); + + for (uint8_t i = 0; i < IS31FL3745_SCALING_REGISTER_COUNT; i++) { + is31fl3745_write_register(index, i + 1, driver_buffers[index].scaling_buffer[i]); + } + + driver_buffers[index].scaling_buffer_dirty = false; + } +} + +void is31fl3745_flush(void) { + for (uint8_t i = 0; i < IS31FL3745_DRIVER_COUNT; i++) { + is31fl3745_update_pwm_buffers(i); + } +} diff --git a/drivers/led/issi/is31fl3745-mono.h b/drivers/led/issi/is31fl3745-mono.h new file mode 100644 index 0000000000..29a13004c8 --- /dev/null +++ b/drivers/led/issi/is31fl3745-mono.h @@ -0,0 +1,271 @@ +/* Copyright 2017 Jason Williams + * Copyright 2018 Jack Humbert + * Copyright 2018 Yiancar + * Copyright 2020 MelGeek + * Copyright 2021 MasterSpoon + * + * 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 2 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/>. + */ + +#pragma once + +#include <stdint.h> +#include <stdbool.h> +#include "progmem.h" +#include "util.h" + +#define IS31FL3745_REG_ID 0xFC + +#define IS31FL3745_REG_COMMAND 0xFD + +#define IS31FL3745_COMMAND_PWM 0x00 +#define IS31FL3745_COMMAND_SCALING 0x01 +#define IS31FL3745_COMMAND_FUNCTION 0x02 + +#define IS31FL3745_FUNCTION_REG_CONFIGURATION 0x00 +#define IS31FL3745_FUNCTION_REG_GLOBAL_CURRENT 0x01 +#define IS31FL3745_FUNCTION_REG_PULLDOWNUP 0x02 +#define IS31FL3745_FUNCTION_REG_TEMPERATURE 0x24 +#define IS31FL3745_FUNCTION_REG_SPREAD_SPECTRUM 0x25 +#define IS31FL3745_FUNCTION_REG_RESET 0x2F + +#define IS31FL3745_REG_COMMAND_WRITE_LOCK 0xFE +#define IS31FL3745_COMMAND_WRITE_LOCK_MAGIC 0xC5 + +#define IS31FL3745_I2C_ADDRESS_GND_GND 0x20 +#define IS31FL3745_I2C_ADDRESS_GND_SCL 0x21 +#define IS31FL3745_I2C_ADDRESS_GND_SDA 0x22 +#define IS31FL3745_I2C_ADDRESS_GND_VCC 0x23 +#define IS31FL3745_I2C_ADDRESS_SCL_GND 0x24 +#define IS31FL3745_I2C_ADDRESS_SCL_SCL 0x25 +#define IS31FL3745_I2C_ADDRESS_SCL_SDA 0x26 +#define IS31FL3745_I2C_ADDRESS_SCL_VCC 0x27 +#define IS31FL3745_I2C_ADDRESS_SDA_GND 0x28 +#define IS31FL3745_I2C_ADDRESS_SDA_SCL 0x29 +#define IS31FL3745_I2C_ADDRESS_SDA_SDA 0x2A +#define IS31FL3745_I2C_ADDRESS_SDA_VCC 0x2B +#define IS31FL3745_I2C_ADDRESS_VCC_GND 0x2C +#define IS31FL3745_I2C_ADDRESS_VCC_SCL 0x2D +#define IS31FL3745_I2C_ADDRESS_VCC_SDA 0x2E +#define IS31FL3745_I2C_ADDRESS_VCC_VCC 0x2F + +#if defined(LED_MATRIX_IS31FL3745) +# define IS31FL3745_LED_COUNT LED_MATRIX_LED_COUNT +#endif + +#if defined(IS31FL3745_I2C_ADDRESS_4) +# define IS31FL3745_DRIVER_COUNT 4 +#elif defined(IS31FL3745_I2C_ADDRESS_3) +# define IS31FL3745_DRIVER_COUNT 3 +#elif defined(IS31FL3745_I2C_ADDRESS_2) +# define IS31FL3745_DRIVER_COUNT 2 +#elif defined(IS31FL3745_I2C_ADDRESS_1) +# define IS31FL3745_DRIVER_COUNT 1 +#endif + +typedef struct is31fl3745_led_t { + uint8_t driver : 2; + uint8_t v; +} PACKED is31fl3745_led_t; + +extern const is31fl3745_led_t PROGMEM g_is31fl3745_leds[IS31FL3745_LED_COUNT]; + +void is31fl3745_init_drivers(void); +void is31fl3745_init(uint8_t index); +void is31fl3745_write_register(uint8_t index, uint8_t reg, uint8_t data); +void is31fl3745_select_page(uint8_t index, uint8_t page); + +void is31fl3745_set_value(int index, uint8_t value); +void is31fl3745_set_value_all(uint8_t value); + +void is31fl3745_set_scaling_register(uint8_t index, uint8_t value); + +void is31fl3745_update_pwm_buffers(uint8_t index); +void is31fl3745_update_scaling_registers(uint8_t index); + +void is31fl3745_flush(void); + +#define IS31FL3745_PDR_0_OHM 0b000 // No pull-down resistor +#define IS31FL3745_PDR_0K5_OHM_SW_OFF 0b001 // 0.5 kOhm resistor in SWx off time +#define IS31FL3745_PDR_1K_OHM_SW_OFF 0b010 // 1 kOhm resistor in SWx off time +#define IS31FL3745_PDR_2K_OHM_SW_OFF 0b011 // 2 kOhm resistor in SWx off time +#define IS31FL3745_PDR_1K_OHM 0b100 // 1 kOhm resistor +#define IS31FL3745_PDR_2K_OHM 0b101 // 2 kOhm resistor +#define IS31FL3745_PDR_4K_OHM 0b110 // 4 kOhm resistor +#define IS31FL3745_PDR_8K_OHM 0b111 // 8 kOhm resistor + +#define IS31FL3745_PUR_0_OHM 0b000 // No pull-up resistor +#define IS31FL3745_PUR_0K5_OHM_CS_OFF 0b001 // 0.5 kOhm resistor in CSy off time +#define IS31FL3745_PUR_1K_OHM_CS_OFF 0b010 // 1 kOhm resistor in CSy off time +#define IS31FL3745_PUR_2K_OHM_CS_OFF 0b011 // 2 kOhm resistor in CSy off time +#define IS31FL3745_PUR_1K_OHM 0b100 // 1 kOhm resistor +#define IS31FL3745_PUR_2K_OHM 0b101 // 2 kOhm resistor +#define IS31FL3745_PUR_4K_OHM 0b110 // 4 kOhm resistor +#define IS31FL3745_PUR_8K_OHM 0b111 // 8 kOhm resistor + +#define IS31FL3745_SYNC_NONE 0b00 +#define IS31FL3745_SYNC_SLAVE 0b10 +#define IS31FL3745_SYNC_MASTER 0b11 + +#define SW1_CS1 0x00 +#define SW1_CS2 0x01 +#define SW1_CS3 0x02 +#define SW1_CS4 0x03 +#define SW1_CS5 0x04 +#define SW1_CS6 0x05 +#define SW1_CS7 0x06 +#define SW1_CS8 0x07 +#define SW1_CS9 0x08 +#define SW1_CS10 0x09 +#define SW1_CS11 0x0A +#define SW1_CS12 0x0B +#define SW1_CS13 0x0C +#define SW1_CS14 0x0D +#define SW1_CS15 0x0E +#define SW1_CS16 0x0F +#define SW1_CS17 0x10 +#define SW1_CS18 0x11 + +#define SW2_CS1 0x12 +#define SW2_CS2 0x13 +#define SW2_CS3 0x14 +#define SW2_CS4 0x15 +#define SW2_CS5 0x16 +#define SW2_CS6 0x17 +#define SW2_CS7 0x18 +#define SW2_CS8 0x19 +#define SW2_CS9 0x1A +#define SW2_CS10 0x1B +#define SW2_CS11 0x1C +#define SW2_CS12 0x1D +#define SW2_CS13 0x1E +#define SW2_CS14 0x1F +#define SW2_CS15 0x20 +#define SW2_CS16 0x21 +#define SW2_CS17 0x22 +#define SW2_CS18 0x23 + +#define SW3_CS1 0x24 +#define SW3_CS2 0x25 +#define SW3_CS3 0x26 +#define SW3_CS4 0x27 +#define SW3_CS5 0x28 +#define SW3_CS6 0x29 +#define SW3_CS7 0x2A +#define SW3_CS8 0x2B +#define SW3_CS9 0x2C +#define SW3_CS10 0x2D +#define SW3_CS11 0x2E +#define SW3_CS12 0x2F +#define SW3_CS13 0x30 +#define SW3_CS14 0x31 +#define SW3_CS15 0x32 +#define SW3_CS16 0x33 +#define SW3_CS17 0x34 +#define SW3_CS18 0x35 + +#define SW4_CS1 0x36 +#define SW4_CS2 0x37 +#define SW4_CS3 0x38 +#define SW4_CS4 0x39 +#define SW4_CS5 0x3A +#define SW4_CS6 0x3B +#define SW4_CS7 0x3C +#define SW4_CS8 0x3D +#define SW4_CS9 0x3E +#define SW4_CS10 0x3F +#define SW4_CS11 0x40 +#define SW4_CS12 0x41 +#define SW4_CS13 0x42 +#define SW4_CS14 0x43 +#define SW4_CS15 0x44 +#define SW4_CS16 0x45 +#define SW4_CS17 0x46 +#define SW4_CS18 0x47 + +#define SW5_CS1 0x48 +#define SW5_CS2 0x49 +#define SW5_CS3 0x4A +#define SW5_CS4 0x4B +#define SW5_CS5 0x4C +#define SW5_CS6 0x4D +#define SW5_CS7 0x4E +#define SW5_CS8 0x4F +#define SW5_CS9 0x50 +#define SW5_CS10 0x51 +#define SW5_CS11 0x52 +#define SW5_CS12 0x53 +#define SW5_CS13 0x54 +#define SW5_CS14 0x55 +#define SW5_CS15 0x56 +#define SW5_CS16 0x57 +#define SW5_CS17 0x58 +#define SW5_CS18 0x59 + +#define SW6_CS1 0x5A +#define SW6_CS2 0x5B +#define SW6_CS3 0x5C +#define SW6_CS4 0x5D +#define SW6_CS5 0x5E +#define SW6_CS6 0x5F +#define SW6_CS7 0x60 +#define SW6_CS8 0x61 +#define SW6_CS9 0x62 +#define SW6_CS10 0x63 +#define SW6_CS11 0x64 +#define SW6_CS12 0x65 +#define SW6_CS13 0x66 +#define SW6_CS14 0x67 +#define SW6_CS15 0x68 +#define SW6_CS16 0x69 +#define SW6_CS17 0x6A +#define SW6_CS18 0x6B + +#define SW7_CS1 0x6C +#define SW7_CS2 0x6D +#define SW7_CS3 0x6E +#define SW7_CS4 0x6F +#define SW7_CS5 0x70 +#define SW7_CS6 0x71 +#define SW7_CS7 0x72 +#define SW7_CS8 0x73 +#define SW7_CS9 0x74 +#define SW7_CS10 0x75 +#define SW7_CS11 0x76 +#define SW7_CS12 0x77 +#define SW7_CS13 0x78 +#define SW7_CS14 0x79 +#define SW7_CS15 0x7A +#define SW7_CS16 0x7B +#define SW7_CS17 0x7C +#define SW7_CS18 0x7D + +#define SW8_CS1 0x7E +#define SW8_CS2 0x7F +#define SW8_CS3 0x80 +#define SW8_CS4 0x81 +#define SW8_CS5 0x82 +#define SW8_CS6 0x83 +#define SW8_CS7 0x84 +#define SW8_CS8 0x85 +#define SW8_CS9 0x86 +#define SW8_CS10 0x87 +#define SW8_CS11 0x88 +#define SW8_CS12 0x89 +#define SW8_CS13 0x8A +#define SW8_CS14 0x8B +#define SW8_CS15 0x8C +#define SW8_CS16 0x8D +#define SW8_CS17 0x8E +#define SW8_CS18 0x8F diff --git a/drivers/led/issi/is31fl3745.c b/drivers/led/issi/is31fl3745.c new file mode 100644 index 0000000000..1e3b437e02 --- /dev/null +++ b/drivers/led/issi/is31fl3745.c @@ -0,0 +1,249 @@ +/* Copyright 2017 Jason Williams + * Copyright 2018 Jack Humbert + * Copyright 2018 Yiancar + * Copyright 2020 MelGeek + * Copyright 2021 MasterSpoon + * + * 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 2 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/>. + */ + +#include "is31fl3745.h" +#include "i2c_master.h" +#include "gpio.h" +#include "wait.h" + +#define IS31FL3745_PWM_REGISTER_COUNT 144 +#define IS31FL3745_SCALING_REGISTER_COUNT 144 + +#ifndef IS31FL3745_I2C_TIMEOUT +# define IS31FL3745_I2C_TIMEOUT 100 +#endif + +#ifndef IS31FL3745_I2C_PERSISTENCE +# define IS31FL3745_I2C_PERSISTENCE 0 +#endif + +#ifndef IS31FL3745_CONFIGURATION +# define IS31FL3745_CONFIGURATION 0x31 +#endif + +#ifndef IS31FL3745_SW_PULLDOWN +# define IS31FL3745_SW_PULLDOWN IS31FL3745_PDR_2K_OHM_SW_OFF +#endif + +#ifndef IS31FL3745_CS_PULLUP +# define IS31FL3745_CS_PULLUP IS31FL3745_PUR_2K_OHM_CS_OFF +#endif + +#ifndef IS31FL3745_GLOBAL_CURRENT +# define IS31FL3745_GLOBAL_CURRENT 0xFF +#endif + +#ifndef IS31FL3745_SYNC_1 +# define IS31FL3745_SYNC_1 IS31FL3745_SYNC_NONE +#endif +#ifndef IS31FL3745_SYNC_2 +# define IS31FL3745_SYNC_2 IS31FL3745_SYNC_NONE +#endif +#ifndef IS31FL3745_SYNC_3 +# define IS31FL3745_SYNC_3 IS31FL3745_SYNC_NONE +#endif +#ifndef IS31FL3745_SYNC_4 +# define IS31FL3745_SYNC_4 IS31FL3745_SYNC_NONE +#endif + +const uint8_t i2c_addresses[IS31FL3745_DRIVER_COUNT] = { + IS31FL3745_I2C_ADDRESS_1, +#ifdef IS31FL3745_I2C_ADDRESS_2 + IS31FL3745_I2C_ADDRESS_2, +# ifdef IS31FL3745_I2C_ADDRESS_3 + IS31FL3745_I2C_ADDRESS_3, +# ifdef IS31FL3745_I2C_ADDRESS_4 + IS31FL3745_I2C_ADDRESS_4, +# endif +# endif +#endif +}; + +const uint8_t driver_sync[IS31FL3745_DRIVER_COUNT] = { + IS31FL3745_SYNC_1, +#ifdef IS31FL3745_I2C_ADDRESS_2 + IS31FL3745_SYNC_2, +# ifdef IS31FL3745_I2C_ADDRESS_3 + IS31FL3745_SYNC_3, +# ifdef IS31FL3745_I2C_ADDRESS_4 + IS31FL3745_SYNC_4, +# endif +# endif +#endif +}; + +typedef struct is31fl3745_driver_t { + uint8_t pwm_buffer[IS31FL3745_PWM_REGISTER_COUNT]; + bool pwm_buffer_dirty; + uint8_t scaling_buffer[IS31FL3745_SCALING_REGISTER_COUNT]; + bool scaling_buffer_dirty; +} PACKED is31fl3745_driver_t; + +is31fl3745_driver_t driver_buffers[IS31FL3745_DRIVER_COUNT] = {{ + .pwm_buffer = {0}, + .pwm_buffer_dirty = false, + .scaling_buffer = {0}, + .scaling_buffer_dirty = false, +}}; + +void is31fl3745_write_register(uint8_t index, uint8_t reg, uint8_t data) { +#if IS31FL3745_I2C_PERSISTENCE > 0 + for (uint8_t i = 0; i < IS31FL3745_I2C_PERSISTENCE; i++) { + if (i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3745_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break; + } +#else + i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3745_I2C_TIMEOUT); +#endif +} + +void is31fl3745_select_page(uint8_t index, uint8_t page) { + is31fl3745_write_register(index, IS31FL3745_REG_COMMAND_WRITE_LOCK, IS31FL3745_COMMAND_WRITE_LOCK_MAGIC); + is31fl3745_write_register(index, IS31FL3745_REG_COMMAND, page); +} + +void is31fl3745_write_pwm_buffer(uint8_t index) { + // Assumes page 0 is already selected. + // Transmit PWM registers in 8 transfers of 18 bytes. + + // Iterate over the pwm_buffer contents at 18 byte intervals. + for (uint8_t i = 0; i < IS31FL3745_PWM_REGISTER_COUNT; i += 18) { +#if IS31FL3745_I2C_PERSISTENCE > 0 + for (uint8_t j = 0; j < IS31FL3745_I2C_PERSISTENCE; j++) { + if (i2c_write_register(i2c_addresses[index] << 1, i + 1, driver_buffers[index].pwm_buffer + i, 18, IS31FL3745_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break; + } +#else + i2c_write_register(i2c_addresses[index] << 1, i + 1, driver_buffers[index].pwm_buffer + i, 18, IS31FL3745_I2C_TIMEOUT); +#endif + } +} + +void is31fl3745_init_drivers(void) { + i2c_init(); + +#if defined(IS31FL3745_SDB_PIN) + gpio_set_pin_output(IS31FL3745_SDB_PIN); + gpio_write_pin_high(IS31FL3745_SDB_PIN); +#endif + + for (uint8_t i = 0; i < IS31FL3745_DRIVER_COUNT; i++) { + is31fl3745_init(i); + } + + for (int i = 0; i < IS31FL3745_LED_COUNT; i++) { + is31fl3745_set_scaling_register(i, 0xFF, 0xFF, 0xFF); + } + + for (uint8_t i = 0; i < IS31FL3745_DRIVER_COUNT; i++) { + is31fl3745_update_scaling_registers(i); + } +} + +void is31fl3745_init(uint8_t index) { + // In order to avoid the LEDs being driven with garbage data + // in the LED driver's PWM registers, shutdown is enabled last. + // Set up the mode and other settings, clear the PWM registers, + // then disable software shutdown. + + is31fl3745_select_page(index, IS31FL3745_COMMAND_SCALING); + + // Turn off all LEDs. + for (uint8_t i = 0; i < IS31FL3745_SCALING_REGISTER_COUNT; i++) { + is31fl3745_write_register(index, i + 1, 0x00); + } + + is31fl3745_select_page(index, IS31FL3745_COMMAND_PWM); + + for (uint8_t i = 0; i < IS31FL3745_PWM_REGISTER_COUNT; i++) { + is31fl3745_write_register(index, i + 1, 0x00); + } + + is31fl3745_select_page(index, IS31FL3745_COMMAND_FUNCTION); + + uint8_t sync = driver_sync[index]; + + is31fl3745_write_register(index, IS31FL3745_FUNCTION_REG_PULLDOWNUP, (IS31FL3745_SW_PULLDOWN << 4) | IS31FL3745_CS_PULLUP); + is31fl3745_write_register(index, IS31FL3745_FUNCTION_REG_GLOBAL_CURRENT, IS31FL3745_GLOBAL_CURRENT); + is31fl3745_write_register(index, IS31FL3745_FUNCTION_REG_SPREAD_SPECTRUM, (sync & 0b11) << 6); + is31fl3745_write_register(index, IS31FL3745_FUNCTION_REG_CONFIGURATION, IS31FL3745_CONFIGURATION); + + // Wait 10ms to ensure the device has woken up. + wait_ms(10); +} + +void is31fl3745_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) { + is31fl3745_led_t led; + + if (index >= 0 && index < IS31FL3745_LED_COUNT) { + memcpy_P(&led, (&g_is31fl3745_leds[index]), sizeof(led)); + + if (driver_buffers[led.driver].pwm_buffer[led.r] == red && driver_buffers[led.driver].pwm_buffer[led.g] == green && driver_buffers[led.driver].pwm_buffer[led.b] == blue) { + return; + } + + driver_buffers[led.driver].pwm_buffer[led.r] = red; + driver_buffers[led.driver].pwm_buffer[led.g] = green; + driver_buffers[led.driver].pwm_buffer[led.b] = blue; + driver_buffers[led.driver].pwm_buffer_dirty = true; + } +} + +void is31fl3745_set_color_all(uint8_t red, uint8_t green, uint8_t blue) { + for (int i = 0; i < IS31FL3745_LED_COUNT; i++) { + is31fl3745_set_color(i, red, green, blue); + } +} + +void is31fl3745_set_scaling_register(uint8_t index, uint8_t red, uint8_t green, uint8_t blue) { + is31fl3745_led_t led; + memcpy_P(&led, (&g_is31fl3745_leds[index]), sizeof(led)); + + driver_buffers[led.driver].scaling_buffer[led.r] = red; + driver_buffers[led.driver].scaling_buffer[led.g] = green; + driver_buffers[led.driver].scaling_buffer[led.b] = blue; + driver_buffers[led.driver].scaling_buffer_dirty = true; +} + +void is31fl3745_update_pwm_buffers(uint8_t index) { + if (driver_buffers[index].pwm_buffer_dirty) { + is31fl3745_select_page(index, IS31FL3745_COMMAND_PWM); + + is31fl3745_write_pwm_buffer(index); + + driver_buffers[index].pwm_buffer_dirty = false; + } +} + +void is31fl3745_update_scaling_registers(uint8_t index) { + if (driver_buffers[index].scaling_buffer_dirty) { + is31fl3745_select_page(index, IS31FL3745_COMMAND_SCALING); + + for (uint8_t i = 0; i < IS31FL3745_SCALING_REGISTER_COUNT; i++) { + is31fl3745_write_register(index, i + 1, driver_buffers[index].scaling_buffer[i]); + } + + driver_buffers[index].scaling_buffer_dirty = false; + } +} + +void is31fl3745_flush(void) { + for (uint8_t i = 0; i < IS31FL3745_DRIVER_COUNT; i++) { + is31fl3745_update_pwm_buffers(i); + } +} diff --git a/drivers/led/issi/is31fl3745.h b/drivers/led/issi/is31fl3745.h index 1e88aab4a8..be50dd2cd9 100644 --- a/drivers/led/issi/is31fl3745.h +++ b/drivers/led/issi/is31fl3745.h @@ -20,251 +20,254 @@ #pragma once -// This is a 7-bit address, that gets left-shifted and bit 0 -// set to 0 for write, 1 for read (as per I2C protocol) -// The address will vary depending on your wiring: -// 00 <-> GND -// 01 <-> SCL -// 10 <-> SDA -// 11 <-> VCC -// ADDR1 represents A1:A0 of the 7-bit address. -// ADDR2 represents A3:A2 of the 7-bit address. -// The result is: 0b010(ADDR2)(ADDR1) -#ifndef DRIVER_ADDR_1 -# define DRIVER_ADDR_1 0b0100000 -#endif +#include <stdint.h> +#include <stdbool.h> +#include "progmem.h" +#include "util.h" -// Set defaults for Spread Spectrum Register -#ifndef ISSI_SSR_1 -# ifndef DRIVER_ADDR_2 -# define ISSI_SSR_1 0x00 -# else -# define ISSI_SSR_1 0xC0 -# endif -#endif -#ifndef ISSI_SSR_2 -# define ISSI_SSR_2 0x80 -#endif -#ifndef ISSI_SSR_3 -# define ISSI_SSR_3 0x80 -#endif -#ifndef ISSI_SSR_4 -# define ISSI_SSR_4 0x80 -#endif +#define IS31FL3745_REG_ID 0xFC -// Command Registers -#define ISSI_COMMANDREGISTER_WRITELOCK 0xFE -#define ISSI_COMMANDREGISTER 0xFD -#define ISSI_IDREGISTER 0xFC -#define ISSI_REGISTER_UNLOCK 0xC5 +#define IS31FL3745_REG_COMMAND 0xFD -// Response Registers -#define ISSI_PAGE_PWM 0x00 -#define ISSI_PAGE_SCALING 0x01 -#define ISSI_PAGE_FUNCTION 0x02 +#define IS31FL3745_COMMAND_PWM 0x00 +#define IS31FL3745_COMMAND_SCALING 0x01 +#define IS31FL3745_COMMAND_FUNCTION 0x02 -// Registers under Function Register -#define ISSI_REG_CONFIGURATION 0x00 -#define ISSI_REG_GLOBALCURRENT 0x01 -#define ISSI_REG_PULLDOWNUP 0x02 -#define ISSI_REG_TEMP 0x24 -#define ISSI_REG_SSR 0x25 -#define ISSI_REG_RESET 0x2F +#define IS31FL3745_FUNCTION_REG_CONFIGURATION 0x00 +#define IS31FL3745_FUNCTION_REG_GLOBAL_CURRENT 0x01 +#define IS31FL3745_FUNCTION_REG_PULLDOWNUP 0x02 +#define IS31FL3745_FUNCTION_REG_TEMPERATURE 0x24 +#define IS31FL3745_FUNCTION_REG_SPREAD_SPECTRUM 0x25 +#define IS31FL3745_FUNCTION_REG_RESET 0x2F -// Set defaults for Function Registers -#ifndef ISSI_CONFIGURATION -# define ISSI_CONFIGURATION 0x31 -#endif -#ifndef ISSI_GLOBALCURRENT -# define ISSI_GLOBALCURRENT 0xFF -#endif -#ifndef ISSI_PULLDOWNUP -# define ISSI_PULLDOWNUP 0x33 -#endif -#ifndef ISSI_TEMP -# define ISSI_TEMP 0x00 -#endif +#define IS31FL3745_REG_COMMAND_WRITE_LOCK 0xFE +#define IS31FL3745_COMMAND_WRITE_LOCK_MAGIC 0xC5 -// Set defaults for Scaling registers -#ifndef ISSI_SCAL_RED -# define ISSI_SCAL_RED 0xFF -#endif -#ifndef ISSI_SCAL_BLUE -# define ISSI_SCAL_BLUE 0xFF -#endif -#ifndef ISSI_SCAL_GREEN -# define ISSI_SCAL_GREEN 0xFF +#define IS31FL3745_I2C_ADDRESS_GND_GND 0x20 +#define IS31FL3745_I2C_ADDRESS_GND_SCL 0x21 +#define IS31FL3745_I2C_ADDRESS_GND_SDA 0x22 +#define IS31FL3745_I2C_ADDRESS_GND_VCC 0x23 +#define IS31FL3745_I2C_ADDRESS_SCL_GND 0x24 +#define IS31FL3745_I2C_ADDRESS_SCL_SCL 0x25 +#define IS31FL3745_I2C_ADDRESS_SCL_SDA 0x26 +#define IS31FL3745_I2C_ADDRESS_SCL_VCC 0x27 +#define IS31FL3745_I2C_ADDRESS_SDA_GND 0x28 +#define IS31FL3745_I2C_ADDRESS_SDA_SCL 0x29 +#define IS31FL3745_I2C_ADDRESS_SDA_SDA 0x2A +#define IS31FL3745_I2C_ADDRESS_SDA_VCC 0x2B +#define IS31FL3745_I2C_ADDRESS_VCC_GND 0x2C +#define IS31FL3745_I2C_ADDRESS_VCC_SCL 0x2D +#define IS31FL3745_I2C_ADDRESS_VCC_SDA 0x2E +#define IS31FL3745_I2C_ADDRESS_VCC_VCC 0x2F + +#if defined(RGB_MATRIX_IS31FL3745) +# define IS31FL3745_LED_COUNT RGB_MATRIX_LED_COUNT #endif -#define ISSI_SCAL_RED_OFF 0x00 -#define ISSI_SCAL_GREEN_OFF 0x00 -#define ISSI_SCAL_BLUE_OFF 0x00 -#ifndef ISSI_SCAL_LED -# define ISSI_SCAL_LED 0xFF +#if defined(IS31FL3745_I2C_ADDRESS_4) +# define IS31FL3745_DRIVER_COUNT 4 +#elif defined(IS31FL3745_I2C_ADDRESS_3) +# define IS31FL3745_DRIVER_COUNT 3 +#elif defined(IS31FL3745_I2C_ADDRESS_2) +# define IS31FL3745_DRIVER_COUNT 2 +#elif defined(IS31FL3745_I2C_ADDRESS_1) +# define IS31FL3745_DRIVER_COUNT 1 #endif -#define ISSI_SCAL_LED_OFF 0x00 -// Set buffer sizes -#define ISSI_MAX_LEDS 144 -#define ISSI_SCALING_SIZE 144 -#define ISSI_PWM_TRF_SIZE 18 -#define ISSI_SCALING_TRF_SIZE 18 +typedef struct is31fl3745_led_t { + uint8_t driver : 2; + uint8_t r; + uint8_t g; + uint8_t b; +} PACKED is31fl3745_led_t; + +extern const is31fl3745_led_t PROGMEM g_is31fl3745_leds[IS31FL3745_LED_COUNT]; + +void is31fl3745_init_drivers(void); +void is31fl3745_init(uint8_t index); +void is31fl3745_write_register(uint8_t index, uint8_t reg, uint8_t data); +void is31fl3745_select_page(uint8_t index, uint8_t page); + +void is31fl3745_set_color(int index, uint8_t red, uint8_t green, uint8_t blue); +void is31fl3745_set_color_all(uint8_t red, uint8_t green, uint8_t blue); + +void is31fl3745_set_scaling_register(uint8_t index, uint8_t red, uint8_t green, uint8_t blue); + +void is31fl3745_update_pwm_buffers(uint8_t index); +void is31fl3745_update_scaling_registers(uint8_t index); + +void is31fl3745_flush(void); + +#define IS31FL3745_PDR_0_OHM 0b000 // No pull-down resistor +#define IS31FL3745_PDR_0K5_OHM_SW_OFF 0b001 // 0.5 kOhm resistor in SWx off time +#define IS31FL3745_PDR_1K_OHM_SW_OFF 0b010 // 1 kOhm resistor in SWx off time +#define IS31FL3745_PDR_2K_OHM_SW_OFF 0b011 // 2 kOhm resistor in SWx off time +#define IS31FL3745_PDR_1K_OHM 0b100 // 1 kOhm resistor +#define IS31FL3745_PDR_2K_OHM 0b101 // 2 kOhm resistor +#define IS31FL3745_PDR_4K_OHM 0b110 // 4 kOhm resistor +#define IS31FL3745_PDR_8K_OHM 0b111 // 8 kOhm resistor + +#define IS31FL3745_PUR_0_OHM 0b000 // No pull-up resistor +#define IS31FL3745_PUR_0K5_OHM_CS_OFF 0b001 // 0.5 kOhm resistor in CSy off time +#define IS31FL3745_PUR_1K_OHM_CS_OFF 0b010 // 1 kOhm resistor in CSy off time +#define IS31FL3745_PUR_2K_OHM_CS_OFF 0b011 // 2 kOhm resistor in CSy off time +#define IS31FL3745_PUR_1K_OHM 0b100 // 1 kOhm resistor +#define IS31FL3745_PUR_2K_OHM 0b101 // 2 kOhm resistor +#define IS31FL3745_PUR_4K_OHM 0b110 // 4 kOhm resistor +#define IS31FL3745_PUR_8K_OHM 0b111 // 8 kOhm resistor -// Location of 1st bit for PWM and Scaling registers -#define ISSI_PWM_REG_1ST 0x01 -#define ISSI_SCL_REG_1ST 0x01 +#define IS31FL3745_SYNC_NONE 0b00 +#define IS31FL3745_SYNC_SLAVE 0b10 +#define IS31FL3745_SYNC_MASTER 0b11 -// Map CS SW locations to order in PWM / Scaling buffers -// This matches the ORDER in the Datasheet Register not the POSITION -// It will always count from 0x00 to (ISSI_MAX_LEDS - 1) -#define CS1_SW1 0x00 -#define CS2_SW1 0x01 -#define CS3_SW1 0x02 -#define CS4_SW1 0x03 -#define CS5_SW1 0x04 -#define CS6_SW1 0x05 -#define CS7_SW1 0x06 -#define CS8_SW1 0x07 -#define CS9_SW1 0x08 -#define CS10_SW1 0x09 -#define CS11_SW1 0x0A -#define CS12_SW1 0x0B -#define CS13_SW1 0x0C -#define CS14_SW1 0x0D -#define CS15_SW1 0x0E -#define CS16_SW1 0x0F -#define CS17_SW1 0x10 -#define CS18_SW1 0x11 +#define SW1_CS1 0x00 +#define SW1_CS2 0x01 +#define SW1_CS3 0x02 +#define SW1_CS4 0x03 +#define SW1_CS5 0x04 +#define SW1_CS6 0x05 +#define SW1_CS7 0x06 +#define SW1_CS8 0x07 +#define SW1_CS9 0x08 +#define SW1_CS10 0x09 +#define SW1_CS11 0x0A +#define SW1_CS12 0x0B +#define SW1_CS13 0x0C +#define SW1_CS14 0x0D +#define SW1_CS15 0x0E +#define SW1_CS16 0x0F +#define SW1_CS17 0x10 +#define SW1_CS18 0x11 -#define CS1_SW2 0x12 -#define CS2_SW2 0x13 -#define CS3_SW2 0x14 -#define CS4_SW2 0x15 -#define CS5_SW2 0x16 -#define CS6_SW2 0x17 -#define CS7_SW2 0x18 -#define CS8_SW2 0x19 -#define CS9_SW2 0x1A -#define CS10_SW2 0x1B -#define CS11_SW2 0x1C -#define CS12_SW2 0x1D -#define CS13_SW2 0x1E -#define CS14_SW2 0x1F -#define CS15_SW2 0x20 -#define CS16_SW2 0x21 -#define CS17_SW2 0x22 -#define CS18_SW2 0x23 +#define SW2_CS1 0x12 +#define SW2_CS2 0x13 +#define SW2_CS3 0x14 +#define SW2_CS4 0x15 +#define SW2_CS5 0x16 +#define SW2_CS6 0x17 +#define SW2_CS7 0x18 +#define SW2_CS8 0x19 +#define SW2_CS9 0x1A +#define SW2_CS10 0x1B +#define SW2_CS11 0x1C +#define SW2_CS12 0x1D +#define SW2_CS13 0x1E +#define SW2_CS14 0x1F +#define SW2_CS15 0x20 +#define SW2_CS16 0x21 +#define SW2_CS17 0x22 +#define SW2_CS18 0x23 -#define CS1_SW3 0x24 -#define CS2_SW3 0x25 -#define CS3_SW3 0x26 -#define CS4_SW3 0x27 -#define CS5_SW3 0x28 -#define CS6_SW3 0x29 -#define CS7_SW3 0x2A -#define CS8_SW3 0x2B -#define CS9_SW3 0x2C -#define CS10_SW3 0x2D -#define CS11_SW3 0x2E -#define CS12_SW3 0x2F -#define CS13_SW3 0x30 -#define CS14_SW3 0x31 -#define CS15_SW3 0x32 -#define CS16_SW3 0x33 -#define CS17_SW3 0x34 -#define CS18_SW3 0x35 +#define SW3_CS1 0x24 +#define SW3_CS2 0x25 +#define SW3_CS3 0x26 +#define SW3_CS4 0x27 +#define SW3_CS5 0x28 +#define SW3_CS6 0x29 +#define SW3_CS7 0x2A +#define SW3_CS8 0x2B +#define SW3_CS9 0x2C +#define SW3_CS10 0x2D +#define SW3_CS11 0x2E +#define SW3_CS12 0x2F +#define SW3_CS13 0x30 +#define SW3_CS14 0x31 +#define SW3_CS15 0x32 +#define SW3_CS16 0x33 +#define SW3_CS17 0x34 +#define SW3_CS18 0x35 -#define CS1_SW4 0x36 -#define CS2_SW4 0x37 -#define CS3_SW4 0x38 -#define CS4_SW4 0x39 -#define CS5_SW4 0x3A -#define CS6_SW4 0x3B -#define CS7_SW4 0x3C -#define CS8_SW4 0x3D -#define CS9_SW4 0x3E -#define CS10_SW4 0x3F -#define CS11_SW4 0x40 -#define CS12_SW4 0x41 -#define CS13_SW4 0x42 -#define CS14_SW4 0x43 -#define CS15_SW4 0x44 -#define CS16_SW4 0x45 -#define CS17_SW4 0x46 -#define CS18_SW4 0x47 +#define SW4_CS1 0x36 +#define SW4_CS2 0x37 +#define SW4_CS3 0x38 +#define SW4_CS4 0x39 +#define SW4_CS5 0x3A +#define SW4_CS6 0x3B +#define SW4_CS7 0x3C +#define SW4_CS8 0x3D +#define SW4_CS9 0x3E +#define SW4_CS10 0x3F +#define SW4_CS11 0x40 +#define SW4_CS12 0x41 +#define SW4_CS13 0x42 +#define SW4_CS14 0x43 +#define SW4_CS15 0x44 +#define SW4_CS16 0x45 +#define SW4_CS17 0x46 +#define SW4_CS18 0x47 -#define CS1_SW5 0x48 -#define CS2_SW5 0x49 -#define CS3_SW5 0x4A -#define CS4_SW5 0x4B -#define CS5_SW5 0x4C -#define CS6_SW5 0x4D -#define CS7_SW5 0x4E -#define CS8_SW5 0x4F -#define CS9_SW5 0x50 -#define CS10_SW5 0x51 -#define CS11_SW5 0x52 -#define CS12_SW5 0x53 -#define CS13_SW5 0x54 -#define CS14_SW5 0x55 -#define CS15_SW5 0x56 -#define CS16_SW5 0x57 -#define CS17_SW5 0x58 -#define CS18_SW5 0x59 +#define SW5_CS1 0x48 +#define SW5_CS2 0x49 +#define SW5_CS3 0x4A +#define SW5_CS4 0x4B +#define SW5_CS5 0x4C +#define SW5_CS6 0x4D +#define SW5_CS7 0x4E +#define SW5_CS8 0x4F +#define SW5_CS9 0x50 +#define SW5_CS10 0x51 +#define SW5_CS11 0x52 +#define SW5_CS12 0x53 +#define SW5_CS13 0x54 +#define SW5_CS14 0x55 +#define SW5_CS15 0x56 +#define SW5_CS16 0x57 +#define SW5_CS17 0x58 +#define SW5_CS18 0x59 -#define CS1_SW6 0x5A -#define CS2_SW6 0x5B -#define CS3_SW6 0x5C -#define CS4_SW6 0x5D -#define CS5_SW6 0x5E -#define CS6_SW6 0x5F -#define CS7_SW6 0x60 -#define CS8_SW6 0x61 -#define CS9_SW6 0x62 -#define CS10_SW6 0x63 -#define CS11_SW6 0x64 -#define CS12_SW6 0x65 -#define CS13_SW6 0x66 -#define CS14_SW6 0x67 -#define CS15_SW6 0x68 -#define CS16_SW6 0x69 -#define CS17_SW6 0x6A -#define CS18_SW6 0x6B +#define SW6_CS1 0x5A +#define SW6_CS2 0x5B +#define SW6_CS3 0x5C +#define SW6_CS4 0x5D +#define SW6_CS5 0x5E +#define SW6_CS6 0x5F +#define SW6_CS7 0x60 +#define SW6_CS8 0x61 +#define SW6_CS9 0x62 +#define SW6_CS10 0x63 +#define SW6_CS11 0x64 +#define SW6_CS12 0x65 +#define SW6_CS13 0x66 +#define SW6_CS14 0x67 +#define SW6_CS15 0x68 +#define SW6_CS16 0x69 +#define SW6_CS17 0x6A +#define SW6_CS18 0x6B -#define CS1_SW7 0x6C -#define CS2_SW7 0x6D -#define CS3_SW7 0x6E -#define CS4_SW7 0x6F -#define CS5_SW7 0x70 -#define CS6_SW7 0x71 -#define CS7_SW7 0x72 -#define CS8_SW7 0x73 -#define CS9_SW7 0x74 -#define CS10_SW7 0x75 -#define CS11_SW7 0x76 -#define CS12_SW7 0x77 -#define CS13_SW7 0x78 -#define CS14_SW7 0x79 -#define CS15_SW7 0x7A -#define CS16_SW7 0x7B -#define CS17_SW7 0x7C -#define CS18_SW7 0x7D +#define SW7_CS1 0x6C +#define SW7_CS2 0x6D +#define SW7_CS3 0x6E +#define SW7_CS4 0x6F +#define SW7_CS5 0x70 +#define SW7_CS6 0x71 +#define SW7_CS7 0x72 +#define SW7_CS8 0x73 +#define SW7_CS9 0x74 +#define SW7_CS10 0x75 +#define SW7_CS11 0x76 +#define SW7_CS12 0x77 +#define SW7_CS13 0x78 +#define SW7_CS14 0x79 +#define SW7_CS15 0x7A +#define SW7_CS16 0x7B +#define SW7_CS17 0x7C +#define SW7_CS18 0x7D -#define CS1_SW8 0x7E -#define CS2_SW8 0x7F -#define CS3_SW8 0x80 -#define CS4_SW8 0x81 -#define CS5_SW8 0x82 -#define CS6_SW8 0x83 -#define CS7_SW8 0x84 -#define CS8_SW8 0x85 -#define CS9_SW8 0x86 -#define CS10_SW8 0x87 -#define CS11_SW8 0x88 -#define CS12_SW8 0x89 -#define CS13_SW8 0x8A -#define CS14_SW8 0x8B -#define CS15_SW8 0x8C -#define CS16_SW8 0x8D -#define CS17_SW8 0x8E -#define CS18_SW8 0x8F +#define SW8_CS1 0x7E +#define SW8_CS2 0x7F +#define SW8_CS3 0x80 +#define SW8_CS4 0x81 +#define SW8_CS5 0x82 +#define SW8_CS6 0x83 +#define SW8_CS7 0x84 +#define SW8_CS8 0x85 +#define SW8_CS9 0x86 +#define SW8_CS10 0x87 +#define SW8_CS11 0x88 +#define SW8_CS12 0x89 +#define SW8_CS13 0x8A +#define SW8_CS14 0x8B +#define SW8_CS15 0x8C +#define SW8_CS16 0x8D +#define SW8_CS17 0x8E +#define SW8_CS18 0x8F diff --git a/drivers/led/issi/is31fl3746.h b/drivers/led/issi/is31fl3746.h deleted file mode 100644 index f89f281533..0000000000 --- a/drivers/led/issi/is31fl3746.h +++ /dev/null @@ -1,198 +0,0 @@ -/* Copyright 2017 Jason Williams - * Copyright 2018 Jack Humbert - * Copyright 2018 Yiancar - * Copyright 2020 MelGeek - * Copyright 2021 MasterSpoon - * - * 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 2 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/>. - */ - -#pragma once - -// This is a 7-bit address, that gets left-shifted and bit 0 -// set to 0 for write, 1 for read (as per I2C protocol) -// The address will vary depending on your wiring: -// 00 <-> GND -// 01 <-> SCL -// 10 <-> SDA -// 11 <-> VCC -// ADDR1 represents A1:A0 of the 7-bit address. -// ADDR2 represents A3:A2 of the 7-bit address. -// The result is: 0b110(ADDR2)(ADDR1) -#ifndef DRIVER_ADDR_1 -# define DRIVER_ADDR_1 0b1100000 -#endif - -// Set defaults for Spread Spectrum Register -#ifndef ISSI_SSR_1 -# define ISSI_SSR_1 0x00 -#endif -#ifndef ISSI_SSR_2 -# define ISSI_SSR_2 0x00 -#endif -#ifndef ISSI_SSR_3 -# define ISSI_SSR_3 0x00 -#endif -#ifndef ISSI_SSR_4 -# define ISSI_SSR_4 0x00 -#endif - -// Command Registers -#define ISSI_COMMANDREGISTER_WRITELOCK 0xFE -#define ISSI_COMMANDREGISTER 0xFD -#define ISSI_IDREGISTER 0xFC -#define ISSI_REGISTER_UNLOCK 0xC5 - -// Response Registers -#define ISSI_PAGE_PWM 0x00 -#define ISSI_PAGE_SCALING 0x01 -#define ISSI_PAGE_FUNCTION 0x01 - -// Registers under Function Register -#define ISSI_REG_CONFIGURATION 0x50 -#define ISSI_REG_GLOBALCURRENT 0x51 -#define ISSI_REG_PULLDOWNUP 0x52 -#define ISSI_REG_TEMP 0x5F -#define ISSI_REG_SSR 0x60 -#define ISSI_REG_RESET 0x8F -#define ISSI_REG_PWM_ENABLE 0xE0 -#define ISSI_REG_PWM_SET 0xE2 - -// Set defaults for Function Registers -#ifndef ISSI_CONFIGURATION -# define ISSI_CONFIGURATION 0x01 -#endif -#ifndef ISSI_GLOBALCURRENT -# define ISSI_GLOBALCURRENT 0xFF -#endif -#ifndef ISSI_PULLDOWNUP -# define ISSI_PULLDOWNUP 0x33 -#endif -#ifndef ISSI_TEMP -# define ISSI_TEMP 0x00 -#endif -#ifndef ISSI_PWM_ENABLE -# define ISSI_PWM_ENABLE 0x00 -#endif -#ifndef ISSI_PWM_SET -# define ISSI_PWM_SET 0x00 -#endif - -// Set defaults for Scaling registers -#ifndef ISSI_SCAL_RED -# define ISSI_SCAL_RED 0xFF -#endif -#ifndef ISSI_SCAL_BLUE -# define ISSI_SCAL_BLUE 0xFF -#endif -#ifndef ISSI_SCAL_GREEN -# define ISSI_SCAL_GREEN 0xFF -#endif -#define ISSI_SCAL_RED_OFF 0x00 -#define ISSI_SCAL_GREEN_OFF 0x00 -#define ISSI_SCAL_BLUE_OFF 0x00 - -#ifndef ISSI_SCAL_LED -# define ISSI_SCAL_LED 0xFF -#endif -#define ISSI_SCAL_LED_OFF 0x00 - -// Set buffer sizes -#define ISSI_MAX_LEDS 72 -#define ISSI_SCALING_SIZE 72 -#define ISSI_PWM_TRF_SIZE 18 -#define ISSI_SCALING_TRF_SIZE 18 - -// Location of 1st bit for PWM and Scaling registers -#define ISSI_PWM_REG_1ST 0x01 -#define ISSI_SCL_REG_1ST 0x01 - -// Map CS SW locations to order in PWM / Scaling buffers -// This matches the ORDER in the Datasheet Register not the POSITION -// It will always count from 0x00 to (ISSI_MAX_LEDS - 1) -#define CS1_SW1 0x00 -#define CS2_SW1 0x01 -#define CS3_SW1 0x02 -#define CS4_SW1 0x03 -#define CS5_SW1 0x04 -#define CS6_SW1 0x05 -#define CS7_SW1 0x06 -#define CS8_SW1 0x07 -#define CS9_SW1 0x08 -#define CS10_SW1 0x09 -#define CS11_SW1 0x0A -#define CS12_SW1 0x0B -#define CS13_SW1 0x0C -#define CS14_SW1 0x0D -#define CS15_SW1 0x0E -#define CS16_SW1 0x0F -#define CS17_SW1 0x10 -#define CS18_SW1 0x11 - -#define CS1_SW2 0x12 -#define CS2_SW2 0x13 -#define CS3_SW2 0x14 -#define CS4_SW2 0x15 -#define CS5_SW2 0x16 -#define CS6_SW2 0x17 -#define CS7_SW2 0x18 -#define CS8_SW2 0x19 -#define CS9_SW2 0x1A -#define CS10_SW2 0x1B -#define CS11_SW2 0x1C -#define CS12_SW2 0x1D -#define CS13_SW2 0x1E -#define CS14_SW2 0x1F -#define CS15_SW2 0x20 -#define CS16_SW2 0x21 -#define CS17_SW2 0x22 -#define CS18_SW2 0x23 - -#define CS1_SW3 0x24 -#define CS2_SW3 0x25 -#define CS3_SW3 0x26 -#define CS4_SW3 0x27 -#define CS5_SW3 0x28 -#define CS6_SW3 0x29 -#define CS7_SW3 0x2A -#define CS8_SW3 0x2B -#define CS9_SW3 0x2C -#define CS10_SW3 0x2D -#define CS11_SW3 0x2E -#define CS12_SW3 0x2F -#define CS13_SW3 0x30 -#define CS14_SW3 0x31 -#define CS15_SW3 0x32 -#define CS16_SW3 0x33 -#define CS17_SW3 0x34 -#define CS18_SW3 0x35 - -#define CS1_SW4 0x36 -#define CS2_SW4 0x37 -#define CS3_SW4 0x38 -#define CS4_SW4 0x39 -#define CS5_SW4 0x3A -#define CS6_SW4 0x3B -#define CS7_SW4 0x3C -#define CS8_SW4 0x3D -#define CS9_SW4 0x3E -#define CS10_SW4 0x3F -#define CS11_SW4 0x40 -#define CS12_SW4 0x41 -#define CS13_SW4 0x42 -#define CS14_SW4 0x43 -#define CS15_SW4 0x44 -#define CS16_SW4 0x45 -#define CS17_SW4 0x46 -#define CS18_SW4 0x47 diff --git a/drivers/led/issi/is31fl3746a-mono.c b/drivers/led/issi/is31fl3746a-mono.c new file mode 100644 index 0000000000..6bff10723f --- /dev/null +++ b/drivers/led/issi/is31fl3746a-mono.c @@ -0,0 +1,222 @@ +/* Copyright 2017 Jason Williams + * Copyright 2018 Jack Humbert + * Copyright 2018 Yiancar + * Copyright 2020 MelGeek + * Copyright 2021 MasterSpoon + * + * 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 2 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/>. + */ + +#include "is31fl3746a-mono.h" +#include "i2c_master.h" +#include "gpio.h" +#include "wait.h" + +#define IS31FL3746A_PWM_REGISTER_COUNT 72 +#define IS31FL3746A_SCALING_REGISTER_COUNT 72 + +#ifndef IS31FL3746A_I2C_TIMEOUT +# define IS31FL3746A_I2C_TIMEOUT 100 +#endif + +#ifndef IS31FL3746A_I2C_PERSISTENCE +# define IS31FL3746A_I2C_PERSISTENCE 0 +#endif + +#ifndef IS31FL3746A_CONFIGURATION +# define IS31FL3746A_CONFIGURATION 0x01 +#endif + +#ifndef IS31FL3746A_PWM_FREQUENCY +# define IS31FL3746A_PWM_FREQUENCY IS31FL3746A_PWM_FREQUENCY_29K_HZ +#endif + +#ifndef IS31FL3746A_SW_PULLDOWN +# define IS31FL3746A_SW_PULLDOWN IS31FL3746A_PDR_2K_OHM_SW_OFF +#endif + +#ifndef IS31FL3746A_CS_PULLUP +# define IS31FL3746A_CS_PULLUP IS31FL3746A_PUR_2K_OHM_CS_OFF +#endif + +#ifndef IS31FL3746A_GLOBAL_CURRENT +# define IS31FL3746A_GLOBAL_CURRENT 0xFF +#endif + +const uint8_t i2c_addresses[IS31FL3746A_DRIVER_COUNT] = { + IS31FL3746A_I2C_ADDRESS_1, +#ifdef IS31FL3746A_I2C_ADDRESS_2 + IS31FL3746A_I2C_ADDRESS_2, +# ifdef IS31FL3746A_I2C_ADDRESS_3 + IS31FL3746A_I2C_ADDRESS_3, +# ifdef IS31FL3746A_I2C_ADDRESS_4 + IS31FL3746A_I2C_ADDRESS_4, +# endif +# endif +#endif +}; + +typedef struct is31fl3746a_driver_t { + uint8_t pwm_buffer[IS31FL3746A_PWM_REGISTER_COUNT]; + bool pwm_buffer_dirty; + uint8_t scaling_buffer[IS31FL3746A_SCALING_REGISTER_COUNT]; + bool scaling_buffer_dirty; +} PACKED is31fl3746a_driver_t; + +is31fl3746a_driver_t driver_buffers[IS31FL3746A_DRIVER_COUNT] = {{ + .pwm_buffer = {0}, + .pwm_buffer_dirty = false, + .scaling_buffer = {0}, + .scaling_buffer_dirty = false, +}}; + +void is31fl3746a_write_register(uint8_t index, uint8_t reg, uint8_t data) { +#if IS31FL3746A_I2C_PERSISTENCE > 0 + for (uint8_t i = 0; i < IS31FL3746A_I2C_PERSISTENCE; i++) { + if (i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3746A_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break; + } +#else + i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3746A_I2C_TIMEOUT); +#endif +} + +void is31fl3746a_select_page(uint8_t index, uint8_t page) { + is31fl3746a_write_register(index, IS31FL3746A_REG_COMMAND_WRITE_LOCK, IS31FL3746A_COMMAND_WRITE_LOCK_MAGIC); + is31fl3746a_write_register(index, IS31FL3746A_REG_COMMAND, page); +} + +void is31fl3746a_write_pwm_buffer(uint8_t index) { + // Assumes page 0 is already selected. + // Transmit PWM registers in 4 transfers of 18 bytes. + + // Iterate over the pwm_buffer contents at 18 byte intervals. + for (uint8_t i = 0; i < IS31FL3746A_PWM_REGISTER_COUNT; i += 18) { +#if IS31FL3746A_I2C_PERSISTENCE > 0 + for (uint8_t j = 0; j < IS31FL3746A_I2C_PERSISTENCE; j++) { + if (i2c_write_register(i2c_addresses[index] << 1, i + 1, driver_buffers[index].pwm_buffer + i, 18, IS31FL3746A_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break; + } +#else + i2c_write_register(i2c_addresses[index] << 1, i + 1, driver_buffers[index].pwm_buffer + i, 18, IS31FL3746A_I2C_TIMEOUT); +#endif + } +} + +void is31fl3746a_init_drivers(void) { + i2c_init(); + +#if defined(IS31FL3746A_SDB_PIN) + gpio_set_pin_output(IS31FL3746A_SDB_PIN); + gpio_write_pin_high(IS31FL3746A_SDB_PIN); +#endif + + for (uint8_t i = 0; i < IS31FL3746A_DRIVER_COUNT; i++) { + is31fl3746a_init(i); + } + + for (int i = 0; i < IS31FL3746A_LED_COUNT; i++) { + is31fl3746a_set_scaling_register(i, 0xFF); + } + + for (uint8_t i = 0; i < IS31FL3746A_DRIVER_COUNT; i++) { + is31fl3746a_update_scaling_registers(i); + } +} + +void is31fl3746a_init(uint8_t index) { + // In order to avoid the LEDs being driven with garbage data + // in the LED driver's PWM registers, shutdown is enabled last. + // Set up the mode and other settings, clear the PWM registers, + // then disable software shutdown. + + is31fl3746a_select_page(index, IS31FL3746A_COMMAND_SCALING); + + // Turn off all LEDs. + for (uint8_t i = 0; i < IS31FL3746A_SCALING_REGISTER_COUNT; i++) { + is31fl3746a_write_register(index, i + 1, 0x00); + } + + is31fl3746a_select_page(index, IS31FL3746A_COMMAND_PWM); + + for (uint8_t i = 0; i < IS31FL3746A_PWM_REGISTER_COUNT; i++) { + is31fl3746a_write_register(index, i + 1, 0x00); + } + + is31fl3746a_select_page(index, IS31FL3746A_COMMAND_FUNCTION); + + is31fl3746a_write_register(index, IS31FL3746A_FUNCTION_REG_PULLDOWNUP, (IS31FL3746A_SW_PULLDOWN << 4) | IS31FL3746A_CS_PULLUP); + is31fl3746a_write_register(index, IS31FL3746A_FUNCTION_REG_GLOBAL_CURRENT, IS31FL3746A_GLOBAL_CURRENT); + is31fl3746a_write_register(index, IS31FL3746A_FUNCTION_REG_PWM_ENABLE, 0x01); + is31fl3746a_write_register(index, IS31FL3746A_FUNCTION_REG_PWM_FREQUENCY, IS31FL3746A_PWM_FREQUENCY); + is31fl3746a_write_register(index, IS31FL3746A_FUNCTION_REG_CONFIGURATION, IS31FL3746A_CONFIGURATION); + + // Wait 10ms to ensure the device has woken up. + wait_ms(10); +} + +void is31fl3746a_set_value(int index, uint8_t value) { + is31fl3746a_led_t led; + + if (index >= 0 && index < IS31FL3746A_LED_COUNT) { + memcpy_P(&led, (&g_is31fl3746a_leds[index]), sizeof(led)); + + if (driver_buffers[led.driver].pwm_buffer[led.v] == value) { + return; + } + + driver_buffers[led.driver].pwm_buffer[led.v] = value; + driver_buffers[led.driver].pwm_buffer_dirty = true; + } +} + +void is31fl3746a_set_value_all(uint8_t value) { + for (int i = 0; i < IS31FL3746A_LED_COUNT; i++) { + is31fl3746a_set_value(i, value); + } +} + +void is31fl3746a_set_scaling_register(uint8_t index, uint8_t value) { + is31fl3746a_led_t led; + memcpy_P(&led, (&g_is31fl3746a_leds[index]), sizeof(led)); + + driver_buffers[led.driver].scaling_buffer[led.v] = value; + driver_buffers[led.driver].scaling_buffer_dirty = true; +} + +void is31fl3746a_update_pwm_buffers(uint8_t index) { + if (driver_buffers[index].pwm_buffer_dirty) { + is31fl3746a_select_page(index, IS31FL3746A_COMMAND_PWM); + + is31fl3746a_write_pwm_buffer(index); + + driver_buffers[index].pwm_buffer_dirty = false; + } +} + +void is31fl3746a_update_scaling_registers(uint8_t index) { + if (driver_buffers[index].scaling_buffer_dirty) { + is31fl3746a_select_page(index, IS31FL3746A_COMMAND_SCALING); + + for (uint8_t i = 0; i < IS31FL3746A_SCALING_REGISTER_COUNT; i++) { + is31fl3746a_write_register(index, i + 1, driver_buffers[index].scaling_buffer[i]); + } + + driver_buffers[index].scaling_buffer_dirty = false; + } +} + +void is31fl3746a_flush(void) { + for (uint8_t i = 0; i < IS31FL3746A_DRIVER_COUNT; i++) { + is31fl3746a_update_pwm_buffers(i); + } +} diff --git a/drivers/led/issi/is31fl3746a-mono.h b/drivers/led/issi/is31fl3746a-mono.h new file mode 100644 index 0000000000..295599cbcf --- /dev/null +++ b/drivers/led/issi/is31fl3746a-mono.h @@ -0,0 +1,201 @@ +/* Copyright 2017 Jason Williams + * Copyright 2018 Jack Humbert + * Copyright 2018 Yiancar + * Copyright 2020 MelGeek + * Copyright 2021 MasterSpoon + * + * 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 2 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/>. + */ + +#pragma once + +#include <stdint.h> +#include <stdbool.h> +#include "progmem.h" +#include "util.h" + +#define IS31FL3746A_REG_ID 0xFC + +#define IS31FL3746A_REG_COMMAND 0xFD + +#define IS31FL3746A_COMMAND_PWM 0x00 +#define IS31FL3746A_COMMAND_SCALING 0x01 +#define IS31FL3746A_COMMAND_FUNCTION 0x01 + +#define IS31FL3746A_FUNCTION_REG_CONFIGURATION 0x50 +#define IS31FL3746A_FUNCTION_REG_GLOBAL_CURRENT 0x51 +#define IS31FL3746A_FUNCTION_REG_PULLDOWNUP 0x52 +#define IS31FL3746A_FUNCTION_REG_TEMPERATURE 0x5F +#define IS31FL3746A_FUNCTION_REG_SPREAD_SPECTRUM 0x60 +#define IS31FL3746A_FUNCTION_REG_RESET 0x8F +#define IS31FL3746A_FUNCTION_REG_PWM_ENABLE 0xE0 +#define IS31FL3746A_FUNCTION_REG_PWM_FREQUENCY 0xE2 + +#define IS31FL3746A_REG_COMMAND_WRITE_LOCK 0xFE +#define IS31FL3746A_COMMAND_WRITE_LOCK_MAGIC 0xC5 + +#define IS31FL3746A_I2C_ADDRESS_GND_GND 0x60 +#define IS31FL3746A_I2C_ADDRESS_GND_SCL 0x61 +#define IS31FL3746A_I2C_ADDRESS_GND_SDA 0x62 +#define IS31FL3746A_I2C_ADDRESS_GND_VCC 0x63 +#define IS31FL3746A_I2C_ADDRESS_SCL_GND 0x64 +#define IS31FL3746A_I2C_ADDRESS_SCL_SCL 0x65 +#define IS31FL3746A_I2C_ADDRESS_SCL_SDA 0x66 +#define IS31FL3746A_I2C_ADDRESS_SCL_VCC 0x67 +#define IS31FL3746A_I2C_ADDRESS_SDA_GND 0x68 +#define IS31FL3746A_I2C_ADDRESS_SDA_SCL 0x69 +#define IS31FL3746A_I2C_ADDRESS_SDA_SDA 0x6A +#define IS31FL3746A_I2C_ADDRESS_SDA_VCC 0x6B +#define IS31FL3746A_I2C_ADDRESS_VCC_GND 0x6C +#define IS31FL3746A_I2C_ADDRESS_VCC_SCL 0x6D +#define IS31FL3746A_I2C_ADDRESS_VCC_SDA 0x6E +#define IS31FL3746A_I2C_ADDRESS_VCC_VCC 0x6F + +#if defined(LED_MATRIX_IS31FL3746A) +# define IS31FL3746A_LED_COUNT LED_MATRIX_LED_COUNT +#endif + +#if defined(IS31FL3746A_I2C_ADDRESS_4) +# define IS31FL3746A_DRIVER_COUNT 4 +#elif defined(IS31FL3746A_I2C_ADDRESS_3) +# define IS31FL3746A_DRIVER_COUNT 3 +#elif defined(IS31FL3746A_I2C_ADDRESS_2) +# define IS31FL3746A_DRIVER_COUNT 2 +#elif defined(IS31FL3746A_I2C_ADDRESS_1) +# define IS31FL3746A_DRIVER_COUNT 1 +#endif + +typedef struct is31fl3746a_led_t { + uint8_t driver : 2; + uint8_t v; +} PACKED is31fl3746a_led_t; + +extern const is31fl3746a_led_t PROGMEM g_is31fl3746a_leds[IS31FL3746A_LED_COUNT]; + +void is31fl3746a_init_drivers(void); +void is31fl3746a_init(uint8_t index); +void is31fl3746a_write_register(uint8_t index, uint8_t reg, uint8_t data); +void is31fl3746a_select_page(uint8_t index, uint8_t page); + +void is31fl3746a_set_value(int index, uint8_t value); +void is31fl3746a_set_value_all(uint8_t value); + +void is31fl3746a_set_scaling_register(uint8_t index, uint8_t value); + +void is31fl3746a_update_pwm_buffers(uint8_t index); +void is31fl3746a_update_scaling_registers(uint8_t index); + +void is31fl3746a_flush(void); + +#define IS31FL3746A_PDR_0_OHM 0b000 // No pull-down resistor +#define IS31FL3746A_PDR_0K5_OHM_SW_OFF 0b001 // 0.5 kOhm resistor in SWx off time +#define IS31FL3746A_PDR_1K_OHM_SW_OFF 0b010 // 1 kOhm resistor in SWx off time +#define IS31FL3746A_PDR_2K_OHM_SW_OFF 0b011 // 2 kOhm resistor in SWx off time +#define IS31FL3746A_PDR_1K_OHM 0b100 // 1 kOhm resistor +#define IS31FL3746A_PDR_2K_OHM 0b101 // 2 kOhm resistor +#define IS31FL3746A_PDR_4K_OHM 0b110 // 4 kOhm resistor +#define IS31FL3746A_PDR_8K_OHM 0b111 // 8 kOhm resistor + +#define IS31FL3746A_PUR_0_OHM 0b000 // No pull-up resistor +#define IS31FL3746A_PUR_0K5_OHM_CS_OFF 0b001 // 0.5 kOhm resistor in CSy off time +#define IS31FL3746A_PUR_1K_OHM_CS_OFF 0b010 // 1 kOhm resistor in CSy off time +#define IS31FL3746A_PUR_2K_OHM_CS_OFF 0b011 // 2 kOhm resistor in CSy off time +#define IS31FL3746A_PUR_1K_OHM 0b100 // 1 kOhm resistor +#define IS31FL3746A_PUR_2K_OHM 0b101 // 2 kOhm resistor +#define IS31FL3746A_PUR_4K_OHM 0b110 // 4 kOhm resistor +#define IS31FL3746A_PUR_8K_OHM 0b111 // 8 kOhm resistor + +#define IS31FL3746A_PWM_FREQUENCY_29K_HZ 0b000 +#define IS31FL3746A_PWM_FREQUENCY_14K5_HZ 0b001 +#define IS31FL3746A_PWM_FREQUENCY_7K25_HZ 0b010 +#define IS31FL3746A_PWM_FREQUENCY_3K63_HZ 0b011 +#define IS31FL3746A_PWM_FREQUENCY_1K81_HZ 0b100 +#define IS31FL3746A_PWM_FREQUENCY_906_HZ 0b101 +#define IS31FL3746A_PWM_FREQUENCY_453_HZ 0b110 + +#define SW1_CS1 0x00 +#define SW1_CS2 0x01 +#define SW1_CS3 0x02 +#define SW1_CS4 0x03 +#define SW1_CS5 0x04 +#define SW1_CS6 0x05 +#define SW1_CS7 0x06 +#define SW1_CS8 0x07 +#define SW1_CS9 0x08 +#define SW1_CS10 0x09 +#define SW1_CS11 0x0A +#define SW1_CS12 0x0B +#define SW1_CS13 0x0C +#define SW1_CS14 0x0D +#define SW1_CS15 0x0E +#define SW1_CS16 0x0F +#define SW1_CS17 0x10 +#define SW1_CS18 0x11 + +#define SW2_CS1 0x12 +#define SW2_CS2 0x13 +#define SW2_CS3 0x14 +#define SW2_CS4 0x15 +#define SW2_CS5 0x16 +#define SW2_CS6 0x17 +#define SW2_CS7 0x18 +#define SW2_CS8 0x19 +#define SW2_CS9 0x1A +#define SW2_CS10 0x1B +#define SW2_CS11 0x1C +#define SW2_CS12 0x1D +#define SW2_CS13 0x1E +#define SW2_CS14 0x1F +#define SW2_CS15 0x20 +#define SW2_CS16 0x21 +#define SW2_CS17 0x22 +#define SW2_CS18 0x23 + +#define SW3_CS1 0x24 +#define SW3_CS2 0x25 +#define SW3_CS3 0x26 +#define SW3_CS4 0x27 +#define SW3_CS5 0x28 +#define SW3_CS6 0x29 +#define SW3_CS7 0x2A +#define SW3_CS8 0x2B +#define SW3_CS9 0x2C +#define SW3_CS10 0x2D +#define SW3_CS11 0x2E +#define SW3_CS12 0x2F +#define SW3_CS13 0x30 +#define SW3_CS14 0x31 +#define SW3_CS15 0x32 +#define SW3_CS16 0x33 +#define SW3_CS17 0x34 +#define SW3_CS18 0x35 + +#define SW4_CS1 0x36 +#define SW4_CS2 0x37 +#define SW4_CS3 0x38 +#define SW4_CS4 0x39 +#define SW4_CS5 0x3A +#define SW4_CS6 0x3B +#define SW4_CS7 0x3C +#define SW4_CS8 0x3D +#define SW4_CS9 0x3E +#define SW4_CS10 0x3F +#define SW4_CS11 0x40 +#define SW4_CS12 0x41 +#define SW4_CS13 0x42 +#define SW4_CS14 0x43 +#define SW4_CS15 0x44 +#define SW4_CS16 0x45 +#define SW4_CS17 0x46 +#define SW4_CS18 0x47 diff --git a/drivers/led/issi/is31fl3746a.c b/drivers/led/issi/is31fl3746a.c new file mode 100644 index 0000000000..1ef0b2d632 --- /dev/null +++ b/drivers/led/issi/is31fl3746a.c @@ -0,0 +1,226 @@ +/* Copyright 2017 Jason Williams + * Copyright 2018 Jack Humbert + * Copyright 2018 Yiancar + * Copyright 2020 MelGeek + * Copyright 2021 MasterSpoon + * + * 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 2 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/>. + */ + +#include "is31fl3746a.h" +#include "i2c_master.h" +#include "gpio.h" +#include "wait.h" + +#define IS31FL3746A_PWM_REGISTER_COUNT 72 +#define IS31FL3746A_SCALING_REGISTER_COUNT 72 + +#ifndef IS31FL3746A_I2C_TIMEOUT +# define IS31FL3746A_I2C_TIMEOUT 100 +#endif + +#ifndef IS31FL3746A_I2C_PERSISTENCE +# define IS31FL3746A_I2C_PERSISTENCE 0 +#endif + +#ifndef IS31FL3746A_CONFIGURATION +# define IS31FL3746A_CONFIGURATION 0x01 +#endif + +#ifndef IS31FL3746A_PWM_FREQUENCY +# define IS31FL3746A_PWM_FREQUENCY IS31FL3746A_PWM_FREQUENCY_29K_HZ +#endif + +#ifndef IS31FL3746A_SW_PULLDOWN +# define IS31FL3746A_SW_PULLDOWN IS31FL3746A_PDR_2K_OHM_SW_OFF +#endif + +#ifndef IS31FL3746A_CS_PULLUP +# define IS31FL3746A_CS_PULLUP IS31FL3746A_PUR_2K_OHM_CS_OFF +#endif + +#ifndef IS31FL3746A_GLOBAL_CURRENT +# define IS31FL3746A_GLOBAL_CURRENT 0xFF +#endif + +const uint8_t i2c_addresses[IS31FL3746A_DRIVER_COUNT] = { + IS31FL3746A_I2C_ADDRESS_1, +#ifdef IS31FL3746A_I2C_ADDRESS_2 + IS31FL3746A_I2C_ADDRESS_2, +# ifdef IS31FL3746A_I2C_ADDRESS_3 + IS31FL3746A_I2C_ADDRESS_3, +# ifdef IS31FL3746A_I2C_ADDRESS_4 + IS31FL3746A_I2C_ADDRESS_4, +# endif +# endif +#endif +}; + +typedef struct is31fl3746a_driver_t { + uint8_t pwm_buffer[IS31FL3746A_PWM_REGISTER_COUNT]; + bool pwm_buffer_dirty; + uint8_t scaling_buffer[IS31FL3746A_SCALING_REGISTER_COUNT]; + bool scaling_buffer_dirty; +} PACKED is31fl3746a_driver_t; + +is31fl3746a_driver_t driver_buffers[IS31FL3746A_DRIVER_COUNT] = {{ + .pwm_buffer = {0}, + .pwm_buffer_dirty = false, + .scaling_buffer = {0}, + .scaling_buffer_dirty = false, +}}; + +void is31fl3746a_write_register(uint8_t index, uint8_t reg, uint8_t data) { +#if IS31FL3746A_I2C_PERSISTENCE > 0 + for (uint8_t i = 0; i < IS31FL3746A_I2C_PERSISTENCE; i++) { + if (i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3746A_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break; + } +#else + i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3746A_I2C_TIMEOUT); +#endif +} + +void is31fl3746a_select_page(uint8_t index, uint8_t page) { + is31fl3746a_write_register(index, IS31FL3746A_REG_COMMAND_WRITE_LOCK, IS31FL3746A_COMMAND_WRITE_LOCK_MAGIC); + is31fl3746a_write_register(index, IS31FL3746A_REG_COMMAND, page); +} + +void is31fl3746a_write_pwm_buffer(uint8_t index) { + // Assumes page 0 is already selected. + // Transmit PWM registers in 4 transfers of 18 bytes. + + // Iterate over the pwm_buffer contents at 18 byte intervals. + for (uint8_t i = 0; i < IS31FL3746A_PWM_REGISTER_COUNT; i += 18) { +#if IS31FL3746A_I2C_PERSISTENCE > 0 + for (uint8_t j = 0; j < IS31FL3746A_I2C_PERSISTENCE; j++) { + if (i2c_write_register(i2c_addresses[index] << 1, i + 1, driver_buffers[index].pwm_buffer + i, 18, IS31FL3746A_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break; + } +#else + i2c_write_register(i2c_addresses[index] << 1, i + 1, driver_buffers[index].pwm_buffer + i, 18, IS31FL3746A_I2C_TIMEOUT); +#endif + } +} + +void is31fl3746a_init_drivers(void) { + i2c_init(); + +#if defined(IS31FL3746A_SDB_PIN) + gpio_set_pin_output(IS31FL3746A_SDB_PIN); + gpio_write_pin_high(IS31FL3746A_SDB_PIN); +#endif + + for (uint8_t i = 0; i < IS31FL3746A_DRIVER_COUNT; i++) { + is31fl3746a_init(i); + } + + for (int i = 0; i < IS31FL3746A_LED_COUNT; i++) { + is31fl3746a_set_scaling_register(i, 0xFF, 0xFF, 0xFF); + } + + for (uint8_t i = 0; i < IS31FL3746A_DRIVER_COUNT; i++) { + is31fl3746a_update_scaling_registers(i); + } +} + +void is31fl3746a_init(uint8_t index) { + // In order to avoid the LEDs being driven with garbage data + // in the LED driver's PWM registers, shutdown is enabled last. + // Set up the mode and other settings, clear the PWM registers, + // then disable software shutdown. + + is31fl3746a_select_page(index, IS31FL3746A_COMMAND_SCALING); + + // Turn off all LEDs. + for (uint8_t i = 0; i < IS31FL3746A_SCALING_REGISTER_COUNT; i++) { + is31fl3746a_write_register(index, i + 1, 0x00); + } + + is31fl3746a_select_page(index, IS31FL3746A_COMMAND_PWM); + + for (uint8_t i = 0; i < IS31FL3746A_PWM_REGISTER_COUNT; i++) { + is31fl3746a_write_register(index, i + 1, 0x00); + } + + is31fl3746a_select_page(index, IS31FL3746A_COMMAND_FUNCTION); + + is31fl3746a_write_register(index, IS31FL3746A_FUNCTION_REG_PULLDOWNUP, (IS31FL3746A_SW_PULLDOWN << 4) | IS31FL3746A_CS_PULLUP); + is31fl3746a_write_register(index, IS31FL3746A_FUNCTION_REG_GLOBAL_CURRENT, IS31FL3746A_GLOBAL_CURRENT); + is31fl3746a_write_register(index, IS31FL3746A_FUNCTION_REG_PWM_ENABLE, 0x01); + is31fl3746a_write_register(index, IS31FL3746A_FUNCTION_REG_PWM_FREQUENCY, IS31FL3746A_PWM_FREQUENCY); + is31fl3746a_write_register(index, IS31FL3746A_FUNCTION_REG_CONFIGURATION, IS31FL3746A_CONFIGURATION); + + // Wait 10ms to ensure the device has woken up. + wait_ms(10); +} + +void is31fl3746a_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) { + is31fl3746a_led_t led; + + if (index >= 0 && index < IS31FL3746A_LED_COUNT) { + memcpy_P(&led, (&g_is31fl3746a_leds[index]), sizeof(led)); + + if (driver_buffers[led.driver].pwm_buffer[led.r] == red && driver_buffers[led.driver].pwm_buffer[led.g] == green && driver_buffers[led.driver].pwm_buffer[led.b] == blue) { + return; + } + + driver_buffers[led.driver].pwm_buffer[led.r] = red; + driver_buffers[led.driver].pwm_buffer[led.g] = green; + driver_buffers[led.driver].pwm_buffer[led.b] = blue; + driver_buffers[led.driver].pwm_buffer_dirty = true; + } +} + +void is31fl3746a_set_color_all(uint8_t red, uint8_t green, uint8_t blue) { + for (int i = 0; i < IS31FL3746A_LED_COUNT; i++) { + is31fl3746a_set_color(i, red, green, blue); + } +} + +void is31fl3746a_set_scaling_register(uint8_t index, uint8_t red, uint8_t green, uint8_t blue) { + is31fl3746a_led_t led; + memcpy_P(&led, (&g_is31fl3746a_leds[index]), sizeof(led)); + + driver_buffers[led.driver].scaling_buffer[led.r] = red; + driver_buffers[led.driver].scaling_buffer[led.g] = green; + driver_buffers[led.driver].scaling_buffer[led.b] = blue; + driver_buffers[led.driver].scaling_buffer_dirty = true; +} + +void is31fl3746a_update_pwm_buffers(uint8_t index) { + if (driver_buffers[index].pwm_buffer_dirty) { + is31fl3746a_select_page(index, IS31FL3746A_COMMAND_PWM); + + is31fl3746a_write_pwm_buffer(index); + + driver_buffers[index].pwm_buffer_dirty = false; + } +} + +void is31fl3746a_update_scaling_registers(uint8_t index) { + if (driver_buffers[index].scaling_buffer_dirty) { + is31fl3746a_select_page(index, IS31FL3746A_COMMAND_SCALING); + + for (uint8_t i = 0; i < IS31FL3746A_SCALING_REGISTER_COUNT; i++) { + is31fl3746a_write_register(index, i + 1, driver_buffers[index].scaling_buffer[i]); + } + + driver_buffers[index].scaling_buffer_dirty = false; + } +} + +void is31fl3746a_flush(void) { + for (uint8_t i = 0; i < IS31FL3746A_DRIVER_COUNT; i++) { + is31fl3746a_update_pwm_buffers(i); + } +} diff --git a/drivers/led/issi/is31fl3746a.h b/drivers/led/issi/is31fl3746a.h new file mode 100644 index 0000000000..ba435b8f19 --- /dev/null +++ b/drivers/led/issi/is31fl3746a.h @@ -0,0 +1,203 @@ +/* Copyright 2017 Jason Williams + * Copyright 2018 Jack Humbert + * Copyright 2018 Yiancar + * Copyright 2020 MelGeek + * Copyright 2021 MasterSpoon + * + * 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 2 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/>. + */ + +#pragma once + +#include <stdint.h> +#include <stdbool.h> +#include "progmem.h" +#include "util.h" + +#define IS31FL3746A_REG_ID 0xFC + +#define IS31FL3746A_REG_COMMAND 0xFD + +#define IS31FL3746A_COMMAND_PWM 0x00 +#define IS31FL3746A_COMMAND_SCALING 0x01 +#define IS31FL3746A_COMMAND_FUNCTION 0x01 + +#define IS31FL3746A_FUNCTION_REG_CONFIGURATION 0x50 +#define IS31FL3746A_FUNCTION_REG_GLOBAL_CURRENT 0x51 +#define IS31FL3746A_FUNCTION_REG_PULLDOWNUP 0x52 +#define IS31FL3746A_FUNCTION_REG_TEMPERATURE 0x5F +#define IS31FL3746A_FUNCTION_REG_SPREAD_SPECTRUM 0x60 +#define IS31FL3746A_FUNCTION_REG_RESET 0x8F +#define IS31FL3746A_FUNCTION_REG_PWM_ENABLE 0xE0 +#define IS31FL3746A_FUNCTION_REG_PWM_FREQUENCY 0xE2 + +#define IS31FL3746A_REG_COMMAND_WRITE_LOCK 0xFE +#define IS31FL3746A_COMMAND_WRITE_LOCK_MAGIC 0xC5 + +#define IS31FL3746A_I2C_ADDRESS_GND_GND 0x60 +#define IS31FL3746A_I2C_ADDRESS_GND_SCL 0x61 +#define IS31FL3746A_I2C_ADDRESS_GND_SDA 0x62 +#define IS31FL3746A_I2C_ADDRESS_GND_VCC 0x63 +#define IS31FL3746A_I2C_ADDRESS_SCL_GND 0x64 +#define IS31FL3746A_I2C_ADDRESS_SCL_SCL 0x65 +#define IS31FL3746A_I2C_ADDRESS_SCL_SDA 0x66 +#define IS31FL3746A_I2C_ADDRESS_SCL_VCC 0x67 +#define IS31FL3746A_I2C_ADDRESS_SDA_GND 0x68 +#define IS31FL3746A_I2C_ADDRESS_SDA_SCL 0x69 +#define IS31FL3746A_I2C_ADDRESS_SDA_SDA 0x6A +#define IS31FL3746A_I2C_ADDRESS_SDA_VCC 0x6B +#define IS31FL3746A_I2C_ADDRESS_VCC_GND 0x6C +#define IS31FL3746A_I2C_ADDRESS_VCC_SCL 0x6D +#define IS31FL3746A_I2C_ADDRESS_VCC_SDA 0x6E +#define IS31FL3746A_I2C_ADDRESS_VCC_VCC 0x6F + +#if defined(RGB_MATRIX_IS31FL3746A) +# define IS31FL3746A_LED_COUNT RGB_MATRIX_LED_COUNT +#endif + +#if defined(IS31FL3746A_I2C_ADDRESS_4) +# define IS31FL3746A_DRIVER_COUNT 4 +#elif defined(IS31FL3746A_I2C_ADDRESS_3) +# define IS31FL3746A_DRIVER_COUNT 3 +#elif defined(IS31FL3746A_I2C_ADDRESS_2) +# define IS31FL3746A_DRIVER_COUNT 2 +#elif defined(IS31FL3746A_I2C_ADDRESS_1) +# define IS31FL3746A_DRIVER_COUNT 1 +#endif + +typedef struct is31fl3746a_led_t { + uint8_t driver : 2; + uint8_t r; + uint8_t g; + uint8_t b; +} PACKED is31fl3746a_led_t; + +extern const is31fl3746a_led_t PROGMEM g_is31fl3746a_leds[IS31FL3746A_LED_COUNT]; + +void is31fl3746a_init_drivers(void); +void is31fl3746a_init(uint8_t index); +void is31fl3746a_write_register(uint8_t index, uint8_t reg, uint8_t data); +void is31fl3746a_select_page(uint8_t index, uint8_t page); + +void is31fl3746a_set_color(int index, uint8_t red, uint8_t green, uint8_t blue); +void is31fl3746a_set_color_all(uint8_t red, uint8_t green, uint8_t blue); + +void is31fl3746a_set_scaling_register(uint8_t index, uint8_t red, uint8_t green, uint8_t blue); + +void is31fl3746a_update_pwm_buffers(uint8_t index); +void is31fl3746a_update_scaling_registers(uint8_t index); + +void is31fl3746a_flush(void); + +#define IS31FL3746A_PDR_0_OHM 0b000 // No pull-down resistor +#define IS31FL3746A_PDR_0K5_OHM_SW_OFF 0b001 // 0.5 kOhm resistor in SWx off time +#define IS31FL3746A_PDR_1K_OHM_SW_OFF 0b010 // 1 kOhm resistor in SWx off time +#define IS31FL3746A_PDR_2K_OHM_SW_OFF 0b011 // 2 kOhm resistor in SWx off time +#define IS31FL3746A_PDR_1K_OHM 0b100 // 1 kOhm resistor +#define IS31FL3746A_PDR_2K_OHM 0b101 // 2 kOhm resistor +#define IS31FL3746A_PDR_4K_OHM 0b110 // 4 kOhm resistor +#define IS31FL3746A_PDR_8K_OHM 0b111 // 8 kOhm resistor + +#define IS31FL3746A_PUR_0_OHM 0b000 // No pull-up resistor +#define IS31FL3746A_PUR_0K5_OHM_CS_OFF 0b001 // 0.5 kOhm resistor in CSy off time +#define IS31FL3746A_PUR_1K_OHM_CS_OFF 0b010 // 1 kOhm resistor in CSy off time +#define IS31FL3746A_PUR_2K_OHM_CS_OFF 0b011 // 2 kOhm resistor in CSy off time +#define IS31FL3746A_PUR_1K_OHM 0b100 // 1 kOhm resistor +#define IS31FL3746A_PUR_2K_OHM 0b101 // 2 kOhm resistor +#define IS31FL3746A_PUR_4K_OHM 0b110 // 4 kOhm resistor +#define IS31FL3746A_PUR_8K_OHM 0b111 // 8 kOhm resistor + +#define IS31FL3746A_PWM_FREQUENCY_29K_HZ 0b000 +#define IS31FL3746A_PWM_FREQUENCY_14K5_HZ 0b001 +#define IS31FL3746A_PWM_FREQUENCY_7K25_HZ 0b010 +#define IS31FL3746A_PWM_FREQUENCY_3K63_HZ 0b011 +#define IS31FL3746A_PWM_FREQUENCY_1K81_HZ 0b100 +#define IS31FL3746A_PWM_FREQUENCY_906_HZ 0b101 +#define IS31FL3746A_PWM_FREQUENCY_453_HZ 0b110 + +#define SW1_CS1 0x00 +#define SW1_CS2 0x01 +#define SW1_CS3 0x02 +#define SW1_CS4 0x03 +#define SW1_CS5 0x04 +#define SW1_CS6 0x05 +#define SW1_CS7 0x06 +#define SW1_CS8 0x07 +#define SW1_CS9 0x08 +#define SW1_CS10 0x09 +#define SW1_CS11 0x0A +#define SW1_CS12 0x0B +#define SW1_CS13 0x0C +#define SW1_CS14 0x0D +#define SW1_CS15 0x0E +#define SW1_CS16 0x0F +#define SW1_CS17 0x10 +#define SW1_CS18 0x11 + +#define SW2_CS1 0x12 +#define SW2_CS2 0x13 +#define SW2_CS3 0x14 +#define SW2_CS4 0x15 +#define SW2_CS5 0x16 +#define SW2_CS6 0x17 +#define SW2_CS7 0x18 +#define SW2_CS8 0x19 +#define SW2_CS9 0x1A +#define SW2_CS10 0x1B +#define SW2_CS11 0x1C +#define SW2_CS12 0x1D +#define SW2_CS13 0x1E +#define SW2_CS14 0x1F +#define SW2_CS15 0x20 +#define SW2_CS16 0x21 +#define SW2_CS17 0x22 +#define SW2_CS18 0x23 + +#define SW3_CS1 0x24 +#define SW3_CS2 0x25 +#define SW3_CS3 0x26 +#define SW3_CS4 0x27 +#define SW3_CS5 0x28 +#define SW3_CS6 0x29 +#define SW3_CS7 0x2A +#define SW3_CS8 0x2B +#define SW3_CS9 0x2C +#define SW3_CS10 0x2D +#define SW3_CS11 0x2E +#define SW3_CS12 0x2F +#define SW3_CS13 0x30 +#define SW3_CS14 0x31 +#define SW3_CS15 0x32 +#define SW3_CS16 0x33 +#define SW3_CS17 0x34 +#define SW3_CS18 0x35 + +#define SW4_CS1 0x36 +#define SW4_CS2 0x37 +#define SW4_CS3 0x38 +#define SW4_CS4 0x39 +#define SW4_CS5 0x3A +#define SW4_CS6 0x3B +#define SW4_CS7 0x3C +#define SW4_CS8 0x3D +#define SW4_CS9 0x3E +#define SW4_CS10 0x3F +#define SW4_CS11 0x40 +#define SW4_CS12 0x41 +#define SW4_CS13 0x42 +#define SW4_CS14 0x43 +#define SW4_CS15 0x44 +#define SW4_CS16 0x45 +#define SW4_CS17 0x46 +#define SW4_CS18 0x47 diff --git a/drivers/led/issi/is31flcommon.c b/drivers/led/issi/is31flcommon.c deleted file mode 100644 index d6b9bce93d..0000000000 --- a/drivers/led/issi/is31flcommon.c +++ /dev/null @@ -1,330 +0,0 @@ -/* Copyright 2017 Jason Williams - * Copyright 2018 Jack Humbert - * Copyright 2018 Yiancar - * Copyright 2020 MelGeek - * Copyright 2021 MasterSpoon - * - * 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 2 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/>. - */ - -#include "is31flcommon.h" -#include "i2c_master.h" -#include "wait.h" -#include <string.h> - -// Set defaults for Timeout and Persistence -#ifndef ISSI_TIMEOUT -# define ISSI_TIMEOUT 100 -#endif -#ifndef ISSI_PERSISTENCE -# define ISSI_PERSISTENCE 0 -#endif - -// Transfer buffer for TWITransmitData() -uint8_t g_twi_transfer_buffer[20]; - -// These buffers match the PWM & scaling registers. -// Storing them like this is optimal for I2C transfers to the registers. -uint8_t g_pwm_buffer[DRIVER_COUNT][ISSI_MAX_LEDS]; -bool g_pwm_buffer_update_required[DRIVER_COUNT] = {false}; - -uint8_t g_scaling_buffer[DRIVER_COUNT][ISSI_SCALING_SIZE]; -bool g_scaling_buffer_update_required[DRIVER_COUNT] = {false}; - -// For writing of single register entry -void IS31FL_write_single_register(uint8_t addr, uint8_t reg, uint8_t data) { - // Set register address and register data ready to write - g_twi_transfer_buffer[0] = reg; - g_twi_transfer_buffer[1] = data; - -#if ISSI_PERSISTENCE > 0 - for (uint8_t i = 0; i < ISSI_PERSISTENCE; i++) { - if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, ISSI_TIMEOUT) == 0) break; - } -#else - i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, ISSI_TIMEOUT); -#endif -} - -// For writing of mulitple register entries to make use of address auto increment -// Once the controller has been called and we have written the first bit of data -// the controller will move to the next register meaning we can write sequential blocks. -bool IS31FL_write_multi_registers(uint8_t addr, uint8_t *source_buffer, uint8_t buffer_size, uint8_t transfer_size, uint8_t start_reg_addr) { - // Split the buffer into chunks to transfer - for (int i = 0; i < buffer_size; i += transfer_size) { - // Set the first entry of transfer buffer to the first register we want to write - g_twi_transfer_buffer[0] = i + start_reg_addr; - // Copy the section of our source buffer into the transfer buffer after first register address - memcpy(g_twi_transfer_buffer + 1, source_buffer + i, transfer_size); - -#if ISSI_PERSISTENCE > 0 - for (uint8_t i = 0; i < ISSI_PERSISTENCE; i++) { - if (i2c_transmit(addr << 1, g_twi_transfer_buffer, transfer_size + 1, ISSI_TIMEOUT) != 0) { - return false; - } - } -#else - if (i2c_transmit(addr << 1, g_twi_transfer_buffer, transfer_size + 1, ISSI_TIMEOUT) != 0) { - return false; - } -#endif - } - return true; -} - -void IS31FL_unlock_register(uint8_t addr, uint8_t page) { - // unlock the command register and select Page to write - IS31FL_write_single_register(addr, ISSI_COMMANDREGISTER_WRITELOCK, ISSI_REGISTER_UNLOCK); - IS31FL_write_single_register(addr, ISSI_COMMANDREGISTER, page); -} - -void IS31FL_common_init(uint8_t addr, uint8_t ssr) { - // Setup phase, need to take out of software shutdown and configure - // ISSI_SSR_x is passed to allow Master / Slave setting where applicable - - // Unlock the command register & select Function Register - IS31FL_unlock_register(addr, ISSI_PAGE_FUNCTION); - // Set Configuration Register to remove Software shutdown - IS31FL_write_single_register(addr, ISSI_REG_CONFIGURATION, ISSI_CONFIGURATION); - // Set Golbal Current Control Register - IS31FL_write_single_register(addr, ISSI_REG_GLOBALCURRENT, ISSI_GLOBALCURRENT); - // Set Pull up & Down for SWx CSy - IS31FL_write_single_register(addr, ISSI_REG_PULLDOWNUP, ISSI_PULLDOWNUP); -// Set Tempature Status -#ifdef ISSI_REG_TEMP - IS31FL_write_single_register(addr, ISSI_REG_TEMP, ISSI_TEMP); -#endif - // Set Spread Spectrum Register, passed through as sets SYNC function - IS31FL_write_single_register(addr, ISSI_REG_SSR, ssr); -// Set PWM Frequency Enable Register if applicable -#ifdef ISSI_REG_PWM_ENABLE - IS31FL_write_single_register(addr, ISSI_REG_PWM_ENABLE, ISSI_PWM_ENABLE); -#endif -// Set PWM Frequency Register if applicable -#ifdef ISSI_REG_PWM_SET - IS31FL_write_single_register(addr, ISSI_REG_PWM_SET, ISSI_PWM_SET); -#endif - - // Wait 10ms to ensure the device has woken up. - wait_ms(10); -} - -void IS31FL_common_update_pwm_register(uint8_t addr, uint8_t index) { - if (g_pwm_buffer_update_required[index]) { - // Queue up the correct page - IS31FL_unlock_register(addr, ISSI_PAGE_PWM); - // Hand off the update to IS31FL_write_multi_registers - IS31FL_write_multi_registers(addr, g_pwm_buffer[index], ISSI_MAX_LEDS, ISSI_PWM_TRF_SIZE, ISSI_PWM_REG_1ST); - // Update flags that pwm_buffer has been updated - g_pwm_buffer_update_required[index] = false; - } -} - -#ifdef ISSI_MANUAL_SCALING -void IS31FL_set_manual_scaling_buffer(void) { - is31_led led; - is31_led scale; - for (int i = 0; i < ISSI_MANUAL_SCALING; i++) { - memcpy_P(&scale, (&g_is31_scaling[i]), sizeof(scale)); - -# ifdef RGB_MATRIX_ENABLE - if (scale.driver >= 0 && scale.driver < RGB_MATRIX_LED_COUNT) { - memcpy_P(&led, (&g_is31_leds[scale.driver]), sizeof(led)); - - if (g_scaling_buffer[led.driver][led.r] = scale.r && g_scaling_buffer[led.driver][led.g] = scale.g && g_scaling_buffer[led.driver][led.b] = scale.b) { - return; - } - g_scaling_buffer[led.driver][led.r] = scale.r; - g_scaling_buffer[led.driver][led.g] = scale.g; - g_scaling_buffer[led.driver][led.b] = scale.b; -# elif defined(LED_MATRIX_ENABLE) - if (scale.driver >= 0 && scale.driver < LED_MATRIX_LED_COUNT) { - memcpy_P(&led, (&g_is31_leds[scale.driver]), sizeof(led)); - - if (g_scaling_buffer[led.driver][led.v] == scale.v) { - return; - } - g_scaling_buffer[led.driver][led.v] = scale.v; -# endif - g_scaling_buffer_update_required[led.driver] = true; - } - } -} -#endif - -void IS31FL_common_update_scaling_register(uint8_t addr, uint8_t index) { - if (g_scaling_buffer_update_required[index]) { - // Queue up the correct page - IS31FL_unlock_register(addr, ISSI_PAGE_SCALING); - // Hand off the update to IS31FL_write_multi_registers - IS31FL_write_multi_registers(addr, g_scaling_buffer[index], ISSI_SCALING_SIZE, ISSI_SCALING_TRF_SIZE, ISSI_SCL_REG_1ST); - // Update flags that scaling_buffer has been updated - g_scaling_buffer_update_required[index] = false; - } -} - -void IS31FL_common_flush(void) { - IS31FL_common_update_pwm_register(DRIVER_ADDR_1, 0); -#if defined(DRIVER_ADDR_2) - IS31FL_common_update_pwm_register(DRIVER_ADDR_2, 1); -# if defined(DRIVER_ADDR_3) - IS31FL_common_update_pwm_register(DRIVER_ADDR_3, 2); -# if defined(DRIVER_ADDR_4) - IS31FL_common_update_pwm_register(DRIVER_ADDR_4, 3); -# endif -# endif -#endif -} - -#ifdef RGB_MATRIX_ENABLE -void IS31FL_RGB_init_drivers(void) { - i2c_init(); - - IS31FL_common_init(DRIVER_ADDR_1, ISSI_SSR_1); -# if defined(DRIVER_ADDR_2) - IS31FL_common_init(DRIVER_ADDR_2, ISSI_SSR_2); -# if defined(DRIVER_ADDR_3) - IS31FL_common_init(DRIVER_ADDR_3, ISSI_SSR_3); -# if defined(DRIVER_ADDR_4) - IS31FL_common_init(DRIVER_ADDR_4, ISSI_SSR_4); -# endif -# endif -# endif - - for (int i = 0; i < RGB_MATRIX_LED_COUNT; i++) { - IS31FL_RGB_set_scaling_buffer(i, true, true, true); - } - - // This actually updates the LED drivers -# ifdef ISSI_MANUAL_SCALING - IS31FL_set_manual_scaling_buffer(); -# endif - - IS31FL_common_update_scaling_register(DRIVER_ADDR_1, 0); -# if defined(DRIVER_ADDR_2) - IS31FL_common_update_scaling_register(DRIVER_ADDR_2, 1); -# if defined(DRIVER_ADDR_3) - IS31FL_common_update_scaling_register(DRIVER_ADDR_3, 2); -# if defined(DRIVER_ADDR_4) - IS31FL_common_update_scaling_register(DRIVER_ADDR_4, 3); -# endif -# endif -# endif -} - -// Colour is set by adjusting PWM register -void IS31FL_RGB_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) { - if (index >= 0 && index < RGB_MATRIX_LED_COUNT) { - is31_led led; - memcpy_P(&led, (&g_is31_leds[index]), sizeof(led)); - - g_pwm_buffer[led.driver][led.r] = red; - g_pwm_buffer[led.driver][led.g] = green; - g_pwm_buffer[led.driver][led.b] = blue; - g_pwm_buffer_update_required[led.driver] = true; - } -} - -void IS31FL_RGB_set_color_all(uint8_t red, uint8_t green, uint8_t blue) { - for (int i = 0; i < RGB_MATRIX_LED_COUNT; i++) { - IS31FL_RGB_set_color(i, red, green, blue); - } -} - -// Setup Scaling register that decides the peak current of each LED -void IS31FL_RGB_set_scaling_buffer(uint8_t index, bool red, bool green, bool blue) { - is31_led led; - memcpy_P(&led, (&g_is31_leds[index]), sizeof(led)); - if (red) { - g_scaling_buffer[led.driver][led.r] = ISSI_SCAL_RED; - } else { - g_scaling_buffer[led.driver][led.r] = ISSI_SCAL_RED_OFF; - } - if (green) { - g_scaling_buffer[led.driver][led.g] = ISSI_SCAL_GREEN; - } else { - g_scaling_buffer[led.driver][led.g] = ISSI_SCAL_GREEN_OFF; - } - if (blue) { - g_scaling_buffer[led.driver][led.b] = ISSI_SCAL_BLUE; - } else { - g_scaling_buffer[led.driver][led.b] = ISSI_SCAL_BLUE_OFF; - } - g_scaling_buffer_update_required[led.driver] = true; -} - -#elif defined(LED_MATRIX_ENABLE) -// LED Matrix Specific scripts -void IS31FL_simple_init_drivers(void) { - i2c_init(); - - IS31FL_common_init(DRIVER_ADDR_1, ISSI_SSR_1); -# if defined(DRIVER_ADDR_2) - IS31FL_common_init(DRIVER_ADDR_2, ISSI_SSR_2); -# if defined(DRIVER_ADDR_3) - IS31FL_common_init(DRIVER_ADDR_3, ISSI_SSR_3); -# if defined(DRIVER_ADDR_4) - IS31FL_common_init(DRIVER_ADDR_4, ISSI_SSR_4); -# endif -# endif -# endif - - for (int i = 0; i < LED_MATRIX_LED_COUNT; i++) { - IS31FL_simple_set_scaling_buffer(i, true); - } - -// This actually updates the LED drivers -# ifdef ISSI_MANUAL_SCALING - IS31FL_set_manual_scaling_buffer(); -# endif - - IS31FL_common_update_scaling_register(DRIVER_ADDR_1, 0); -# if defined(DRIVER_ADDR_2) - IS31FL_common_update_scaling_register(DRIVER_ADDR_2, 1); -# if defined(DRIVER_ADDR_3) - IS31FL_common_update_scaling_register(DRIVER_ADDR_3, 2); -# if defined(DRIVER_ADDR_4) - IS31FL_common_update_scaling_register(DRIVER_ADDR_4, 3); -# endif -# endif -# endif -} - -void IS31FL_simple_set_scaling_buffer(uint8_t index, bool value) { - is31_led led; - memcpy_P(&led, (&g_is31_leds[index]), sizeof(led)); - if (value) { - g_scaling_buffer[led.driver][led.v] = ISSI_SCAL_LED; - } else { - g_scaling_buffer[led.driver][led.v] = ISSI_SCAL_LED_OFF; - } - g_scaling_buffer_update_required[led.driver] = true; -} - -void IS31FL_simple_set_brightness(int index, uint8_t value) { - if (index >= 0 && index < LED_MATRIX_LED_COUNT) { - is31_led led; - memcpy_P(&led, (&g_is31_leds[index]), sizeof(led)); - - g_pwm_buffer[led.driver][led.v] = value; - g_pwm_buffer_update_required[led.driver] = true; - } -} - -void IS31FL_simple_set_brigntness_all(uint8_t value) { - for (int i = 0; i < LED_MATRIX_LED_COUNT; i++) { - IS31FL_simple_set_brightness(i, value); - } -} -#endif diff --git a/drivers/led/issi/is31flcommon.h b/drivers/led/issi/is31flcommon.h deleted file mode 100644 index 10613a6eed..0000000000 --- a/drivers/led/issi/is31flcommon.h +++ /dev/null @@ -1,95 +0,0 @@ -/* Copyright 2017 Jason Williams - * Copyright 2018 Jack Humbert - * Copyright 2018 Yiancar - * Copyright 2020 MelGeek - * Copyright 2021 MasterSpoon - * - * 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 2 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/>. - */ - -#pragma once - -#include <stdint.h> -#include <stdbool.h> -#include "progmem.h" -#include "util.h" - -// Which variant header file to use -#if defined(LED_MATRIX_IS31FL3742A) || defined(RGB_MATRIX_IS31FL3742A) -# include "is31fl3742.h" -#elif defined(LED_MATRIX_IS31FL3743A) || defined(RGB_MATRIX_IS31FL3743A) -# include "is31fl3743.h" -#elif defined(LED_MATRIX_IS31FL3745) || defined(RGB_MATRIX_IS31FL3745) -# include "is31fl3745.h" -#elif defined(LED_MATRIX_IS31FL3746A) || defined(RGB_MATRIX_IS31FL3746A) -# include "is31fl3746.h" -#endif - -#if defined DRIVER_ADDR_4 -# define DRIVER_COUNT 4 -#elif defined DRIVER_ADDR_3 -# define DRIVER_COUNT 3 -#elif defined DRIVER_ADDR_2 -# define DRIVER_COUNT 2 -#elif defined DRIVER_ADDR_1 -# define DRIVER_COUNT 1 -#endif - -#ifdef RGB_MATRIX_ENABLE -typedef struct is31_led { - uint8_t driver : 2; - uint8_t r; - uint8_t g; - uint8_t b; -} PACKED is31_led; - -extern const is31_led PROGMEM g_is31_leds[RGB_MATRIX_LED_COUNT]; - -#elif defined(LED_MATRIX_ENABLE) -typedef struct is31_led { - uint8_t driver : 2; - uint8_t v; -} PACKED is31_led; - -extern const is31_led PROGMEM g_is31_leds[LED_MATRIX_LED_COUNT]; -#endif - -#ifdef ISSI_MANUAL_SCALING -extern const is31_led PROGMEM g_is31_scaling[]; -void IS31FL_set_manual_scaling_buffer(void); -#endif - -void IS31FL_write_single_register(uint8_t addr, uint8_t reg, uint8_t data); -bool IS31FL_write_multi_registers(uint8_t addr, uint8_t *source_buffer, uint8_t buffer_size, uint8_t transfer_size, uint8_t start_reg_addr); -void IS31FL_unlock_register(uint8_t addr, uint8_t page); -void IS31FL_common_init(uint8_t addr, uint8_t ssr); - -void IS31FL_common_update_pwm_register(uint8_t addr, uint8_t index); -void IS31FL_common_update_scaling_register(uint8_t addr, uint8_t index); - -void IS31FL_common_flush(void); - -#ifdef RGB_MATRIX_ENABLE -// RGB Matrix Specific scripts -void IS31FL_RGB_init_drivers(void); -void IS31FL_RGB_set_color(int index, uint8_t red, uint8_t green, uint8_t blue); -void IS31FL_RGB_set_color_all(uint8_t red, uint8_t green, uint8_t blue); -void IS31FL_RGB_set_scaling_buffer(uint8_t index, bool red, bool green, bool blue); -#elif defined(LED_MATRIX_ENABLE) -// LED Matrix Specific scripts -void IS31FL_simple_init_drivers(void); -void IS31FL_simple_set_scaling_buffer(uint8_t index, bool value); -void IS31FL_simple_set_brightness(int index, uint8_t value); -void IS31FL_simple_set_brigntness_all(uint8_t value); -#endif diff --git a/drivers/led/snled27351-mono.c b/drivers/led/snled27351-mono.c new file mode 100644 index 0000000000..d87b856db6 --- /dev/null +++ b/drivers/led/snled27351-mono.c @@ -0,0 +1,250 @@ +/* Copyright 2021 @ Keychron (https://www.keychron.com) + * + * 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 2 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/>. + */ + +#include "snled27351-mono.h" +#include "i2c_master.h" +#include "gpio.h" + +#define SNLED27351_PWM_REGISTER_COUNT 192 +#define SNLED27351_LED_CONTROL_REGISTER_COUNT 24 + +#ifndef SNLED27351_I2C_TIMEOUT +# define SNLED27351_I2C_TIMEOUT 100 +#endif + +#ifndef SNLED27351_I2C_PERSISTENCE +# define SNLED27351_I2C_PERSISTENCE 0 +#endif + +#ifndef SNLED27351_PHASE_CHANNEL +# define SNLED27351_PHASE_CHANNEL SNLED27351_SCAN_PHASE_12_CHANNEL +#endif + +#ifndef SNLED27351_CURRENT_TUNE +# define SNLED27351_CURRENT_TUNE \ + { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF } +#endif + +const uint8_t i2c_addresses[SNLED27351_DRIVER_COUNT] = { + SNLED27351_I2C_ADDRESS_1, +#ifdef SNLED27351_I2C_ADDRESS_2 + SNLED27351_I2C_ADDRESS_2, +# ifdef SNLED27351_I2C_ADDRESS_3 + SNLED27351_I2C_ADDRESS_3, +# ifdef SNLED27351_I2C_ADDRESS_4 + SNLED27351_I2C_ADDRESS_4, +# endif +# endif +#endif +}; + +// These buffers match the SNLED27351 PWM registers. +// The control buffers match the PG0 LED On/Off registers. +// Storing them like this is optimal for I2C transfers to the registers. +// We could optimize this and take out the unused registers from these +// buffers and the transfers in snled27351_write_pwm_buffer() but it's +// probably not worth the extra complexity. +typedef struct snled27351_driver_t { + uint8_t pwm_buffer[SNLED27351_PWM_REGISTER_COUNT]; + bool pwm_buffer_dirty; + uint8_t led_control_buffer[SNLED27351_LED_CONTROL_REGISTER_COUNT]; + bool led_control_buffer_dirty; +} PACKED snled27351_driver_t; + +snled27351_driver_t driver_buffers[SNLED27351_DRIVER_COUNT] = {{ + .pwm_buffer = {0}, + .pwm_buffer_dirty = false, + .led_control_buffer = {0}, + .led_control_buffer_dirty = false, +}}; + +void snled27351_write_register(uint8_t index, uint8_t reg, uint8_t data) { +#if SNLED27351_I2C_PERSISTENCE > 0 + for (uint8_t i = 0; i < SNLED27351_I2C_PERSISTENCE; i++) { + if (i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, SNLED27351_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break; + } +#else + i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, SNLED27351_I2C_TIMEOUT); +#endif +} + +void snled27351_select_page(uint8_t index, uint8_t page) { + snled27351_write_register(index, SNLED27351_REG_COMMAND, page); +} + +void snled27351_write_pwm_buffer(uint8_t index) { + // Assumes PG1 is already selected. + // Transmit PWM registers in 12 transfers of 16 bytes. + + // Iterate over the pwm_buffer contents at 16 byte intervals. + for (uint8_t i = 0; i < SNLED27351_PWM_REGISTER_COUNT; i += 16) { +#if SNLED27351_I2C_PERSISTENCE > 0 + for (uint8_t j = 0; j < SNLED27351_I2C_PERSISTENCE; j++) { + if (i2c_write_register(i2c_addresses[index] << 1, i, driver_buffers[index].pwm_buffer + i, 16, SNLED27351_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break; + } +#else + i2c_write_register(i2c_addresses[index] << 1, i, driver_buffers[index].pwm_buffer + i, 16, SNLED27351_I2C_TIMEOUT); +#endif + } +} + +void snled27351_init_drivers(void) { + i2c_init(); + +#if defined(SNLED27351_SDB_PIN) + gpio_set_pin_output(SNLED27351_SDB_PIN); + gpio_write_pin_high(SNLED27351_SDB_PIN); +#endif + + for (uint8_t i = 0; i < SNLED27351_DRIVER_COUNT; i++) { + snled27351_init(i); + } + + for (int i = 0; i < SNLED27351_LED_COUNT; i++) { + snled27351_set_led_control_register(i, true); + } + + for (uint8_t i = 0; i < SNLED27351_DRIVER_COUNT; i++) { + snled27351_update_led_control_registers(i); + } +} + +void snled27351_init(uint8_t index) { + snled27351_select_page(index, SNLED27351_COMMAND_FUNCTION); + + // Setting LED driver to shutdown mode + snled27351_write_register(index, SNLED27351_FUNCTION_REG_SOFTWARE_SHUTDOWN, SNLED27351_SOFTWARE_SHUTDOWN_SSD_SHUTDOWN); + // Setting internal channel pulldown/pullup + snled27351_write_register(index, SNLED27351_FUNCTION_REG_PULLDOWNUP, SNLED27351_PULLDOWNUP_ALL_ENABLED); + // Select number of scan phase + snled27351_write_register(index, SNLED27351_FUNCTION_REG_SCAN_PHASE, SNLED27351_PHASE_CHANNEL); + // Setting PWM Delay Phase + snled27351_write_register(index, SNLED27351_FUNCTION_REG_SLEW_RATE_CONTROL_MODE_1, SNLED27351_SLEW_RATE_CONTROL_MODE_1_PDP_ENABLE); + // Setting Driving/Sinking Channel Slew Rate + snled27351_write_register(index, SNLED27351_FUNCTION_REG_SLEW_RATE_CONTROL_MODE_2, SNLED27351_SLEW_RATE_CONTROL_MODE_2_DSL_ENABLE | SNLED27351_SLEW_RATE_CONTROL_MODE_2_SSL_ENABLE); + // Setting Iref + snled27351_write_register(index, SNLED27351_FUNCTION_REG_SOFTWARE_SLEEP, 0); + + snled27351_select_page(index, SNLED27351_COMMAND_LED_CONTROL); + + for (int i = 0; i < SNLED27351_LED_CONTROL_ON_OFF_LENGTH; i++) { + snled27351_write_register(index, i, 0x00); + } + + snled27351_select_page(index, SNLED27351_COMMAND_PWM); + + for (int i = 0; i < SNLED27351_LED_CURRENT_TUNE_LENGTH; i++) { + snled27351_write_register(index, i, 0x00); + } + + snled27351_select_page(index, SNLED27351_COMMAND_CURRENT_TUNE); + + uint8_t current_tune_reg_list[SNLED27351_LED_CURRENT_TUNE_LENGTH] = SNLED27351_CURRENT_TUNE; + for (int i = 0; i < SNLED27351_LED_CURRENT_TUNE_LENGTH; i++) { + snled27351_write_register(index, i, current_tune_reg_list[i]); + } + + snled27351_select_page(index, SNLED27351_COMMAND_LED_CONTROL); + + for (int i = 0; i < SNLED27351_LED_CONTROL_ON_OFF_LENGTH; i++) { + snled27351_write_register(index, i, 0xFF); + } + + snled27351_select_page(index, SNLED27351_COMMAND_FUNCTION); + + // Setting LED driver to normal mode + snled27351_write_register(index, SNLED27351_FUNCTION_REG_SOFTWARE_SHUTDOWN, SNLED27351_SOFTWARE_SHUTDOWN_SSD_NORMAL); +} + +void snled27351_set_value(int index, uint8_t value) { + snled27351_led_t led; + if (index >= 0 && index < SNLED27351_LED_COUNT) { + memcpy_P(&led, (&g_snled27351_leds[index]), sizeof(led)); + + if (driver_buffers[led.driver].pwm_buffer[led.v] == value) { + return; + } + + driver_buffers[led.driver].pwm_buffer[led.v] = value; + driver_buffers[led.driver].pwm_buffer_dirty = true; + } +} + +void snled27351_set_value_all(uint8_t value) { + for (int i = 0; i < SNLED27351_LED_COUNT; i++) { + snled27351_set_value(i, value); + } +} + +void snled27351_set_led_control_register(uint8_t index, bool value) { + snled27351_led_t led; + memcpy_P(&led, (&g_snled27351_leds[index]), sizeof(led)); + + uint8_t control_register = led.v / 8; + uint8_t bit_value = led.v % 8; + + if (value) { + driver_buffers[led.driver].led_control_buffer[control_register] |= (1 << bit_value); + } else { + driver_buffers[led.driver].led_control_buffer[control_register] &= ~(1 << bit_value); + } + + driver_buffers[led.driver].led_control_buffer_dirty = true; +} + +void snled27351_update_pwm_buffers(uint8_t index) { + if (driver_buffers[index].pwm_buffer_dirty) { + snled27351_select_page(index, SNLED27351_COMMAND_PWM); + + snled27351_write_pwm_buffer(index); + + driver_buffers[index].pwm_buffer_dirty = false; + } +} + +void snled27351_update_led_control_registers(uint8_t index) { + if (driver_buffers[index].led_control_buffer_dirty) { + snled27351_select_page(index, SNLED27351_COMMAND_LED_CONTROL); + + for (uint8_t i = 0; i < SNLED27351_LED_CONTROL_REGISTER_COUNT; i++) { + snled27351_write_register(index, i, driver_buffers[index].led_control_buffer[i]); + } + + driver_buffers[index].led_control_buffer_dirty = false; + } +} + +void snled27351_flush(void) { + for (uint8_t i = 0; i < SNLED27351_DRIVER_COUNT; i++) { + snled27351_update_pwm_buffers(i); + } +} + +void snled27351_sw_return_normal(uint8_t index) { + snled27351_select_page(index, SNLED27351_COMMAND_FUNCTION); + + // Setting LED driver to normal mode + snled27351_write_register(index, SNLED27351_FUNCTION_REG_SOFTWARE_SHUTDOWN, SNLED27351_SOFTWARE_SHUTDOWN_SSD_NORMAL); +} + +void snled27351_sw_shutdown(uint8_t index) { + snled27351_select_page(index, SNLED27351_COMMAND_FUNCTION); + + // Setting LED driver to shutdown mode + snled27351_write_register(index, SNLED27351_FUNCTION_REG_SOFTWARE_SHUTDOWN, SNLED27351_SOFTWARE_SHUTDOWN_SSD_SHUTDOWN); + // Write SW Sleep Register + snled27351_write_register(index, SNLED27351_FUNCTION_REG_SOFTWARE_SLEEP, SNLED27351_SOFTWARE_SLEEP_ENABLE); +} diff --git a/drivers/led/snled27351-mono.h b/drivers/led/snled27351-mono.h new file mode 100644 index 0000000000..43d39934cb --- /dev/null +++ b/drivers/led/snled27351-mono.h @@ -0,0 +1,586 @@ +/* Copyright 2021 @ Keychron (https://www.keychron.com) + * + * 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 2 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/>. + */ + +#pragma once + +#include <stdint.h> +#include <stdbool.h> +#include "progmem.h" +#include "util.h" + +// ======== DEPRECATED DEFINES - DO NOT USE ======== +#ifdef CKLED2001_TIMEOUT +# define SNLED27351_I2C_TIMEOUT CKLED2001_TIMEOUT +#endif +#ifdef CKLED2001_PERSISTENCE +# define SNLED27351_I2C_PERSISTENCE CKLED2001_PERSISTENCE +#endif +#ifdef PHASE_CHANNEL +# define SNLED27351_PHASE_CHANNEL PHASE_CHANNEL +#endif +#ifdef CKLED2001_CURRENT_TUNE +# define SNLED27351_CURRENT_TUNE CKLED2001_CURRENT_TUNE +#endif + +#define MSKPHASE_12CHANNEL SNLED27351_SCAN_PHASE_12_CHANNEL +#define MSKPHASE_11CHANNEL SNLED27351_SCAN_PHASE_11_CHANNEL +#define MSKPHASE_10CHANNEL SNLED27351_SCAN_PHASE_10_CHANNEL +#define MSKPHASE_9CHANNEL SNLED27351_SCAN_PHASE_9_CHANNEL +#define MSKPHASE_8CHANNEL SNLED27351_SCAN_PHASE_8_CHANNEL +#define MSKPHASE_7CHANNEL SNLED27351_SCAN_PHASE_7_CHANNEL +#define MSKPHASE_6CHANNEL SNLED27351_SCAN_PHASE_6_CHANNEL +#define MSKPHASE_5CHANNEL SNLED27351_SCAN_PHASE_5_CHANNEL +#define MSKPHASE_4CHANNEL SNLED27351_SCAN_PHASE_4_CHANNEL +#define MSKPHASE_3CHANNEL SNLED27351_SCAN_PHASE_3_CHANNEL +#define MSKPHASE_2CHANNEL SNLED27351_SCAN_PHASE_2_CHANNEL +#define MSKPHASE_1CHANNEL SNLED27351_SCAN_PHASE_1_CHANNEL + +#define ckled2001_led snled27351_led_t +#define g_ckled2001_leds g_snled27351_leds +// ======== + +#define SNLED27351_REG_COMMAND 0xFD +#define SNLED27351_COMMAND_LED_CONTROL 0x00 +#define SNLED27351_COMMAND_PWM 0x01 +#define SNLED27351_COMMAND_FUNCTION 0x03 +#define SNLED27351_COMMAND_CURRENT_TUNE 0x04 + +#define SNLED27351_FUNCTION_REG_SOFTWARE_SHUTDOWN 0x00 +#define SNLED27351_SOFTWARE_SHUTDOWN_SSD_SHUTDOWN (0x0 << 0) +#define SNLED27351_SOFTWARE_SHUTDOWN_SSD_NORMAL (0x1 << 0) + +#define SNLED27351_FUNCTION_REG_ID 0x11 +#define SNLED27351_DRIVER_ID 0x8A + +#define SNLED27351_FUNCTION_REG_PULLDOWNUP 0x13 +#define SNLED27351_PULLDOWNUP_ALL_ENABLED 0xAA + +#define SNLED27351_FUNCTION_REG_SCAN_PHASE 0x14 +#define SNLED27351_SCAN_PHASE_12_CHANNEL 0x00 +#define SNLED27351_SCAN_PHASE_11_CHANNEL 0x01 +#define SNLED27351_SCAN_PHASE_10_CHANNEL 0x02 +#define SNLED27351_SCAN_PHASE_9_CHANNEL 0x03 +#define SNLED27351_SCAN_PHASE_8_CHANNEL 0x04 +#define SNLED27351_SCAN_PHASE_7_CHANNEL 0x05 +#define SNLED27351_SCAN_PHASE_6_CHANNEL 0x06 +#define SNLED27351_SCAN_PHASE_5_CHANNEL 0x07 +#define SNLED27351_SCAN_PHASE_4_CHANNEL 0x08 +#define SNLED27351_SCAN_PHASE_3_CHANNEL 0x09 +#define SNLED27351_SCAN_PHASE_2_CHANNEL 0x0A +#define SNLED27351_SCAN_PHASE_1_CHANNEL 0x0B + +#define SNLED27351_FUNCTION_REG_SLEW_RATE_CONTROL_MODE_1 0x15 +#define SNLED27351_SLEW_RATE_CONTROL_MODE_1_PDP_ENABLE (0b1 << 2) + +#define SNLED27351_FUNCTION_REG_SLEW_RATE_CONTROL_MODE_2 0x16 +#define SNLED27351_SLEW_RATE_CONTROL_MODE_2_SSL_ENABLE (0b1 << 6) +#define SNLED27351_SLEW_RATE_CONTROL_MODE_2_DSL_ENABLE (0b1 << 7) + +#define SNLED27351_FUNCTION_REG_OPEN_SHORT_ENABLE 0x17 +#define SNLED27351_OPEN_SHORT_ENABLE_SDS_ENABLE (0b1 << 6) +#define SNLED27351_OPEN_SHORT_ENABLE_ODS_ENABLE (0b1 << 7) + +#define SNLED27351_FUNCTION_REG_OPEN_SHORT_DUTY 0x18 + +#define SNLED27351_FUNCTION_REG_OPEN_SHORT_FLAG 0x19 +#define SNLED27351_OPEN_SHORT_FLAG_OSINT_ENABLE (0b1 << 6) +#define SNLED27351_OPEN_SHORT_FLAG_ODINT_ENABLE (0b1 << 7) + +#define SNLED27351_FUNCTION_REG_SOFTWARE_SLEEP 0x1A +#define SNLED27351_SOFTWARE_SLEEP_ENABLE (0b1 << 1) + +// LED Control Registers +#define SNLED27351_LED_CONTROL_ON_OFF_FIRST_ADDR 0x0 +#define SNLED27351_LED_CONTROL_ON_OFF_LAST_ADDR 0x17 +#define SNLED27351_LED_CONTROL_ON_OFF_LENGTH ((SNLED27351_LED_CONTROL_ON_OFF_LAST_ADDR - SNLED27351_LED_CONTROL_ON_OFF_FIRST_ADDR) + 1) + +#define SNLED27351_LED_CONTROL_OPEN_FIRST_ADDR 0x18 +#define SNLED27351_LED_CONTROL_OPEN_LAST_ADDR 0x2F +#define SNLED27351_LED_CONTROL_OPEN_LENGTH ((SNLED27351_LED_CONTROL_OPEN_LAST_ADDR - SNLED27351_LED_CONTROL_OPEN_FIRST_ADDR) + 1) + +#define SNLED27351_LED_CONTROL_SHORT_FIRST_ADDR 0x30 +#define SNLED27351_LED_CONTROL_SHORT_LAST_ADDR 0x47 +#define SNLED27351_LED_CONTROL_SHORT_LENGTH ((SNLED27351_LED_CONTROL_SHORT_LAST_ADDR - SNLED27351_LED_CONTROL_SHORT_FIRST_ADDR) + 1) + +#define SNLED27351_LED_CONTROL_PAGE_LENGTH 0x48 + +// LED Control Registers +#define SNLED27351_LED_PWM_FIRST_ADDR 0x00 +#define SNLED27351_LED_PWM_LAST_ADDR 0xBF +#define SNLED27351_LED_PWM_LENGTH 0xC0 + +// Current Tune Registers +#define SNLED27351_LED_CURRENT_TUNE_FIRST_ADDR 0x00 +#define SNLED27351_LED_CURRENT_TUNE_LAST_ADDR 0x0B +#define SNLED27351_LED_CURRENT_TUNE_LENGTH 0x0C + +#define SNLED27351_I2C_ADDRESS_GND 0x74 +#define SNLED27351_I2C_ADDRESS_SCL 0x75 +#define SNLED27351_I2C_ADDRESS_SDA 0x76 +#define SNLED27351_I2C_ADDRESS_VDDIO 0x77 + +#if defined(LED_MATRIX_SNLED27351) +# define SNLED27351_LED_COUNT LED_MATRIX_LED_COUNT +#endif + +#if defined(SNLED27351_I2C_ADDRESS_4) +# define SNLED27351_DRIVER_COUNT 4 +#elif defined(SNLED27351_I2C_ADDRESS_3) +# define SNLED27351_DRIVER_COUNT 3 +#elif defined(SNLED27351_I2C_ADDRESS_2) +# define SNLED27351_DRIVER_COUNT 2 +#elif defined(SNLED27351_I2C_ADDRESS_1) +# define SNLED27351_DRIVER_COUNT 1 +#endif + +typedef struct snled27351_led_t { + uint8_t driver : 2; + uint8_t v; +} PACKED snled27351_led_t; + +extern const snled27351_led_t PROGMEM g_snled27351_leds[SNLED27351_LED_COUNT]; + +void snled27351_init_drivers(void); +void snled27351_init(uint8_t index); +void snled27351_select_page(uint8_t index, uint8_t page); +void snled27351_write_register(uint8_t index, uint8_t reg, uint8_t data); + +void snled27351_set_value(int index, uint8_t value); +void snled27351_set_value_all(uint8_t value); + +void snled27351_set_led_control_register(uint8_t index, bool value); + +// This should not be called from an interrupt +// (eg. from a timer interrupt). +// Call this while idle (in between matrix scans). +// If the buffer is dirty, it will update the driver with the buffer. +void snled27351_update_pwm_buffers(uint8_t index); +void snled27351_update_led_control_registers(uint8_t index); + +void snled27351_flush(void); + +void snled27351_sw_return_normal(uint8_t index); +void snled27351_sw_shutdown(uint8_t index); + +#define CB1_CA1 0x00 +#define CB1_CA2 0x01 +#define CB1_CA3 0x02 +#define CB1_CA4 0x03 +#define CB1_CA5 0x04 +#define CB1_CA6 0x05 +#define CB1_CA7 0x06 +#define CB1_CA8 0x07 +#define CB1_CA9 0x08 +#define CB1_CA10 0x09 +#define CB1_CA11 0x0A +#define CB1_CA12 0x0B +#define CB1_CA13 0x0C +#define CB1_CA14 0x0D +#define CB1_CA15 0x0E +#define CB1_CA16 0x0F + +#define CB2_CA1 0x10 +#define CB2_CA2 0x11 +#define CB2_CA3 0x12 +#define CB2_CA4 0x13 +#define CB2_CA5 0x14 +#define CB2_CA6 0x15 +#define CB2_CA7 0x16 +#define CB2_CA8 0x17 +#define CB2_CA9 0x18 +#define CB2_CA10 0x19 +#define CB2_CA11 0x1A +#define CB2_CA12 0x1B +#define CB2_CA13 0x1C +#define CB2_CA14 0x1D +#define CB2_CA15 0x1E +#define CB2_CA16 0x1F + +#define CB3_CA1 0x20 +#define CB3_CA2 0x21 +#define CB3_CA3 0x22 +#define CB3_CA4 0x23 +#define CB3_CA5 0x24 +#define CB3_CA6 0x25 +#define CB3_CA7 0x26 +#define CB3_CA8 0x27 +#define CB3_CA9 0x28 +#define CB3_CA10 0x29 +#define CB3_CA11 0x2A +#define CB3_CA12 0x2B +#define CB3_CA13 0x2C +#define CB3_CA14 0x2D +#define CB3_CA15 0x2E +#define CB3_CA16 0x2F + +#define CB4_CA1 0x30 +#define CB4_CA2 0x31 +#define CB4_CA3 0x32 +#define CB4_CA4 0x33 +#define CB4_CA5 0x34 +#define CB4_CA6 0x35 +#define CB4_CA7 0x36 +#define CB4_CA8 0x37 +#define CB4_CA9 0x38 +#define CB4_CA10 0x39 +#define CB4_CA11 0x3A +#define CB4_CA12 0x3B +#define CB4_CA13 0x3C +#define CB4_CA14 0x3D +#define CB4_CA15 0x3E +#define CB4_CA16 0x3F + +#define CB5_CA1 0x40 +#define CB5_CA2 0x41 +#define CB5_CA3 0x42 +#define CB5_CA4 0x43 +#define CB5_CA5 0x44 +#define CB5_CA6 0x45 +#define CB5_CA7 0x46 +#define CB5_CA8 0x47 +#define CB5_CA9 0x48 +#define CB5_CA10 0x49 +#define CB5_CA11 0x4A +#define CB5_CA12 0x4B +#define CB5_CA13 0x4C +#define CB5_CA14 0x4D +#define CB5_CA15 0x4E +#define CB5_CA16 0x4F + +#define CB6_CA1 0x50 +#define CB6_CA2 0x51 +#define CB6_CA3 0x52 +#define CB6_CA4 0x53 +#define CB6_CA5 0x54 +#define CB6_CA6 0x55 +#define CB6_CA7 0x56 +#define CB6_CA8 0x57 +#define CB6_CA9 0x58 +#define CB6_CA10 0x59 +#define CB6_CA11 0x5A +#define CB6_CA12 0x5B +#define CB6_CA13 0x5C +#define CB6_CA14 0x5D +#define CB6_CA15 0x5E +#define CB6_CA16 0x5F + +#define CB7_CA1 0x60 +#define CB7_CA2 0x61 +#define CB7_CA3 0x62 +#define CB7_CA4 0x63 +#define CB7_CA5 0x64 +#define CB7_CA6 0x65 +#define CB7_CA7 0x66 +#define CB7_CA8 0x67 +#define CB7_CA9 0x68 +#define CB7_CA10 0x69 +#define CB7_CA11 0x6A +#define CB7_CA12 0x6B +#define CB7_CA13 0x6C +#define CB7_CA14 0x6D +#define CB7_CA15 0x6E +#define CB7_CA16 0x6F + +#define CB8_CA1 0x70 +#define CB8_CA2 0x71 +#define CB8_CA3 0x72 +#define CB8_CA4 0x73 +#define CB8_CA5 0x74 +#define CB8_CA6 0x75 +#define CB8_CA7 0x76 +#define CB8_CA8 0x77 +#define CB8_CA9 0x78 +#define CB8_CA10 0x79 +#define CB8_CA11 0x7A +#define CB8_CA12 0x7B +#define CB8_CA13 0x7C +#define CB8_CA14 0x7D +#define CB8_CA15 0x7E +#define CB8_CA16 0x7F + +#define CB9_CA1 0x80 +#define CB9_CA2 0x81 +#define CB9_CA3 0x82 +#define CB9_CA4 0x83 +#define CB9_CA5 0x84 +#define CB9_CA6 0x85 +#define CB9_CA7 0x86 +#define CB9_CA8 0x87 +#define CB9_CA9 0x88 +#define CB9_CA10 0x89 +#define CB9_CA11 0x8A +#define CB9_CA12 0x8B +#define CB9_CA13 0x8C +#define CB9_CA14 0x8D +#define CB9_CA15 0x8E +#define CB9_CA16 0x8F + +#define CB10_CA1 0x90 +#define CB10_CA2 0x91 +#define CB10_CA3 0x92 +#define CB10_CA4 0x93 +#define CB10_CA5 0x94 +#define CB10_CA6 0x95 +#define CB10_CA7 0x96 +#define CB10_CA8 0x97 +#define CB10_CA9 0x98 +#define CB10_CA10 0x99 +#define CB10_CA11 0x9A +#define CB10_CA12 0x9B +#define CB10_CA13 0x9C +#define CB10_CA14 0x9D +#define CB10_CA15 0x9E +#define CB10_CA16 0x9F + +#define CB11_CA1 0xA0 +#define CB11_CA2 0xA1 +#define CB11_CA3 0xA2 +#define CB11_CA4 0xA3 +#define CB11_CA5 0xA4 +#define CB11_CA6 0xA5 +#define CB11_CA7 0xA6 +#define CB11_CA8 0xA7 +#define CB11_CA9 0xA8 +#define CB11_CA10 0xA9 +#define CB11_CA11 0xAA +#define CB11_CA12 0xAB +#define CB11_CA13 0xAC +#define CB11_CA14 0xAD +#define CB11_CA15 0xAE +#define CB11_CA16 0xAF + +#define CB12_CA1 0xB0 +#define CB12_CA2 0xB1 +#define CB12_CA3 0xB2 +#define CB12_CA4 0xB3 +#define CB12_CA5 0xB4 +#define CB12_CA6 0xB5 +#define CB12_CA7 0xB6 +#define CB12_CA8 0xB7 +#define CB12_CA9 0xB8 +#define CB12_CA10 0xB9 +#define CB12_CA11 0xBA +#define CB12_CA12 0xBB +#define CB12_CA13 0xBC +#define CB12_CA14 0xBD +#define CB12_CA15 0xBE +#define CB12_CA16 0xBF + +// DEPRECATED - DO NOT USE + +#define A_1 CB1_CA1 +#define A_2 CB1_CA2 +#define A_3 CB1_CA3 +#define A_4 CB1_CA4 +#define A_5 CB1_CA5 +#define A_6 CB1_CA6 +#define A_7 CB1_CA7 +#define A_8 CB1_CA8 +#define A_9 CB1_CA9 +#define A_10 CB1_CA10 +#define A_11 CB1_CA11 +#define A_12 CB1_CA12 +#define A_13 CB1_CA13 +#define A_14 CB1_CA14 +#define A_15 CB1_CA15 +#define A_16 CB1_CA16 + +#define B_1 CB2_CA1 +#define B_2 CB2_CA2 +#define B_3 CB2_CA3 +#define B_4 CB2_CA4 +#define B_5 CB2_CA5 +#define B_6 CB2_CA6 +#define B_7 CB2_CA7 +#define B_8 CB2_CA8 +#define B_9 CB2_CA9 +#define B_10 CB2_CA10 +#define B_11 CB2_CA11 +#define B_12 CB2_CA12 +#define B_13 CB2_CA13 +#define B_14 CB2_CA14 +#define B_15 CB2_CA15 +#define B_16 CB2_CA16 + +#define C_1 CB3_CA1 +#define C_2 CB3_CA2 +#define C_3 CB3_CA3 +#define C_4 CB3_CA4 +#define C_5 CB3_CA5 +#define C_6 CB3_CA6 +#define C_7 CB3_CA7 +#define C_8 CB3_CA8 +#define C_9 CB3_CA9 +#define C_10 CB3_CA10 +#define C_11 CB3_CA11 +#define C_12 CB3_CA12 +#define C_13 CB3_CA13 +#define C_14 CB3_CA14 +#define C_15 CB3_CA15 +#define C_16 CB3_CA16 + +#define D_1 CB4_CA1 +#define D_2 CB4_CA2 +#define D_3 CB4_CA3 +#define D_4 CB4_CA4 +#define D_5 CB4_CA5 +#define D_6 CB4_CA6 +#define D_7 CB4_CA7 +#define D_8 CB4_CA8 +#define D_9 CB4_CA9 +#define D_10 CB4_CA10 +#define D_11 CB4_CA11 +#define D_12 CB4_CA12 +#define D_13 CB4_CA13 +#define D_14 CB4_CA14 +#define D_15 CB4_CA15 +#define D_16 CB4_CA16 + +#define E_1 CB5_CA1 +#define E_2 CB5_CA2 +#define E_3 CB5_CA3 +#define E_4 CB5_CA4 +#define E_5 CB5_CA5 +#define E_6 CB5_CA6 +#define E_7 CB5_CA7 +#define E_8 CB5_CA8 +#define E_9 CB5_CA9 +#define E_10 CB5_CA10 +#define E_11 CB5_CA11 +#define E_12 CB5_CA12 +#define E_13 CB5_CA13 +#define E_14 CB5_CA14 +#define E_15 CB5_CA15 +#define E_16 CB5_CA16 + +#define F_1 CB6_CA1 +#define F_2 CB6_CA2 +#define F_3 CB6_CA3 +#define F_4 CB6_CA4 +#define F_5 CB6_CA5 +#define F_6 CB6_CA6 +#define F_7 CB6_CA7 +#define F_8 CB6_CA8 +#define F_9 CB6_CA9 +#define F_10 CB6_CA10 +#define F_11 CB6_CA11 +#define F_12 CB6_CA12 +#define F_13 CB6_CA13 +#define F_14 CB6_CA14 +#define F_15 CB6_CA15 +#define F_16 CB6_CA16 + +#define G_1 CB7_CA1 +#define G_2 CB7_CA2 +#define G_3 CB7_CA3 +#define G_4 CB7_CA4 +#define G_5 CB7_CA5 +#define G_6 CB7_CA6 +#define G_7 CB7_CA7 +#define G_8 CB7_CA8 +#define G_9 CB7_CA9 +#define G_10 CB7_CA10 +#define G_11 CB7_CA11 +#define G_12 CB7_CA12 +#define G_13 CB7_CA13 +#define G_14 CB7_CA14 +#define G_15 CB7_CA15 +#define G_16 CB7_CA16 + +#define H_1 CB8_CA1 +#define H_2 CB8_CA2 +#define H_3 CB8_CA3 +#define H_4 CB8_CA4 +#define H_5 CB8_CA5 +#define H_6 CB8_CA6 +#define H_7 CB8_CA7 +#define H_8 CB8_CA8 +#define H_9 CB8_CA9 +#define H_10 CB8_CA10 +#define H_11 CB8_CA11 +#define H_12 CB8_CA12 +#define H_13 CB8_CA13 +#define H_14 CB8_CA14 +#define H_15 CB8_CA15 +#define H_16 CB8_CA16 + +#define I_1 CB9_CA1 +#define I_2 CB9_CA2 +#define I_3 CB9_CA3 +#define I_4 CB9_CA4 +#define I_5 CB9_CA5 +#define I_6 CB9_CA6 +#define I_7 CB9_CA7 +#define I_8 CB9_CA8 +#define I_9 CB9_CA9 +#define I_10 CB9_CA10 +#define I_11 CB9_CA11 +#define I_12 CB9_CA12 +#define I_13 CB9_CA13 +#define I_14 CB9_CA14 +#define I_15 CB9_CA15 +#define I_16 CB9_CA16 + +#define J_1 CB10_CA1 +#define J_2 CB10_CA2 +#define J_3 CB10_CA3 +#define J_4 CB10_CA4 +#define J_5 CB10_CA5 +#define J_6 CB10_CA6 +#define J_7 CB10_CA7 +#define J_8 CB10_CA8 +#define J_9 CB10_CA9 +#define J_10 CB10_CA10 +#define J_11 CB10_CA11 +#define J_12 CB10_CA12 +#define J_13 CB10_CA13 +#define J_14 CB10_CA14 +#define J_15 CB10_CA15 +#define J_16 CB10_CA16 + +#define K_1 CB11_CA1 +#define K_2 CB11_CA2 +#define K_3 CB11_CA3 +#define K_4 CB11_CA4 +#define K_5 CB11_CA5 +#define K_6 CB11_CA6 +#define K_7 CB11_CA7 +#define K_8 CB11_CA8 +#define K_9 CB11_CA9 +#define K_10 CB11_CA10 +#define K_11 CB11_CA11 +#define K_12 CB11_CA12 +#define K_13 CB11_CA13 +#define K_14 CB11_CA14 +#define K_15 CB11_CA15 +#define K_16 CB11_CA16 + +#define L_1 CB12_CA1 +#define L_2 CB12_CA2 +#define L_3 CB12_CA3 +#define L_4 CB12_CA4 +#define L_5 CB12_CA5 +#define L_6 CB12_CA6 +#define L_7 CB12_CA7 +#define L_8 CB12_CA8 +#define L_9 CB12_CA9 +#define L_10 CB12_CA10 +#define L_11 CB12_CA11 +#define L_12 CB12_CA12 +#define L_13 CB12_CA13 +#define L_14 CB12_CA14 +#define L_15 CB12_CA15 +#define L_16 CB12_CA16 diff --git a/drivers/led/snled27351-simple.c b/drivers/led/snled27351-simple.c deleted file mode 100644 index b2054c96d5..0000000000 --- a/drivers/led/snled27351-simple.c +++ /dev/null @@ -1,266 +0,0 @@ -/* Copyright 2021 @ Keychron (https://www.keychron.com) - * - * 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 2 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/>. - */ - -#include "snled27351-simple.h" -#include "i2c_master.h" - -#define SNLED27351_PWM_REGISTER_COUNT 192 -#define SNLED27351_LED_CONTROL_REGISTER_COUNT 24 - -#ifndef SNLED27351_I2C_TIMEOUT -# define SNLED27351_I2C_TIMEOUT 100 -#endif - -#ifndef SNLED27351_I2C_PERSISTENCE -# define SNLED27351_I2C_PERSISTENCE 0 -#endif - -#ifndef SNLED27351_PHASE_CHANNEL -# define SNLED27351_PHASE_CHANNEL SNLED27351_SCAN_PHASE_12_CHANNEL -#endif - -#ifndef SNLED27351_CURRENT_TUNE -# define SNLED27351_CURRENT_TUNE \ - { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF } -#endif - -// Transfer buffer for TWITransmitData() -uint8_t g_twi_transfer_buffer[20]; - -// These buffers match the SNLED27351 PWM registers. -// The control buffers match the PG0 LED On/Off registers. -// Storing them like this is optimal for I2C transfers to the registers. -// We could optimize this and take out the unused registers from these -// buffers and the transfers in snled27351_write_pwm_buffer() but it's -// probably not worth the extra complexity. -uint8_t g_pwm_buffer[SNLED27351_DRIVER_COUNT][SNLED27351_PWM_REGISTER_COUNT]; -bool g_pwm_buffer_update_required[SNLED27351_DRIVER_COUNT] = {false}; - -uint8_t g_led_control_registers[SNLED27351_DRIVER_COUNT][SNLED27351_LED_CONTROL_REGISTER_COUNT] = {0}; -bool g_led_control_registers_update_required[SNLED27351_DRIVER_COUNT] = {false}; - -bool snled27351_write_register(uint8_t addr, uint8_t reg, uint8_t data) { - // If the transaction fails function returns false. - g_twi_transfer_buffer[0] = reg; - g_twi_transfer_buffer[1] = data; - -#if SNLED27351_I2C_PERSISTENCE > 0 - for (uint8_t i = 0; i < SNLED27351_I2C_PERSISTENCE; i++) { - if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, SNLED27351_I2C_TIMEOUT) != 0) { - return false; - } - } -#else - if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, SNLED27351_I2C_TIMEOUT) != 0) { - return false; - } -#endif - return true; -} - -bool snled27351_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer) { - // Assumes PG1 is already selected. - // If any of the transactions fails function returns false. - // Transmit PWM registers in 12 transfers of 16 bytes. - // g_twi_transfer_buffer[] is 20 bytes - - // Iterate over the pwm_buffer contents at 16 byte intervals. - for (int i = 0; i < SNLED27351_PWM_REGISTER_COUNT; i += 16) { - g_twi_transfer_buffer[0] = i; - // Copy the data from i to i+15. - // Device will auto-increment register for data after the first byte - // Thus this sets registers 0x00-0x0F, 0x10-0x1F, etc. in one transfer. - for (int j = 0; j < 16; j++) { - g_twi_transfer_buffer[1 + j] = pwm_buffer[i + j]; - } - -#if SNLED27351_I2C_PERSISTENCE > 0 - for (uint8_t i = 0; i < SNLED27351_I2C_PERSISTENCE; i++) { - if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 17, SNLED27351_I2C_TIMEOUT) != 0) { - return false; - } - } -#else - if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 17, SNLED27351_I2C_TIMEOUT) != 0) { - return false; - } -#endif - } - return true; -} - -void snled27351_init_drivers(void) { - i2c_init(); - - snled27351_init(SNLED27351_I2C_ADDRESS_1); -#if defined(SNLED27351_I2C_ADDRESS_2) - snled27351_init(SNLED27351_I2C_ADDRESS_2); -# if defined(SNLED27351_I2C_ADDRESS_3) - snled27351_init(SNLED27351_I2C_ADDRESS_3); -# if defined(SNLED27351_I2C_ADDRESS_4) - snled27351_init(SNLED27351_I2C_ADDRESS_4); -# endif -# endif -#endif - - for (int i = 0; i < SNLED27351_LED_COUNT; i++) { - snled27351_set_led_control_register(i, true); - } - - snled27351_update_led_control_registers(SNLED27351_I2C_ADDRESS_1, 0); -#if defined(SNLED27351_I2C_ADDRESS_2) - snled27351_update_led_control_registers(SNLED27351_I2C_ADDRESS_2, 1); -# if defined(SNLED27351_I2C_ADDRESS_3) - snled27351_update_led_control_registers(SNLED27351_I2C_ADDRESS_3, 2); -# if defined(SNLED27351_I2C_ADDRESS_4) - snled27351_update_led_control_registers(SNLED27351_I2C_ADDRESS_4, 3); -# endif -# endif -#endif -} - -void snled27351_init(uint8_t addr) { - // Select to function page - snled27351_write_register(addr, SNLED27351_REG_COMMAND, SNLED27351_COMMAND_FUNCTION); - // Setting LED driver to shutdown mode - snled27351_write_register(addr, SNLED27351_FUNCTION_REG_SOFTWARE_SHUTDOWN, SNLED27351_SOFTWARE_SHUTDOWN_SSD_SHUTDOWN); - // Setting internal channel pulldown/pullup - snled27351_write_register(addr, SNLED27351_FUNCTION_REG_PULLDOWNUP, SNLED27351_PULLDOWNUP_ALL_ENABLED); - // Select number of scan phase - snled27351_write_register(addr, SNLED27351_FUNCTION_REG_SCAN_PHASE, SNLED27351_PHASE_CHANNEL); - // Setting PWM Delay Phase - snled27351_write_register(addr, SNLED27351_FUNCTION_REG_SLEW_RATE_CONTROL_MODE_1, SNLED27351_SLEW_RATE_CONTROL_MODE_1_PDP_ENABLE); - // Setting Driving/Sinking Channel Slew Rate - snled27351_write_register(addr, SNLED27351_FUNCTION_REG_SLEW_RATE_CONTROL_MODE_2, SNLED27351_SLEW_RATE_CONTROL_MODE_2_DSL_ENABLE | SNLED27351_SLEW_RATE_CONTROL_MODE_2_SSL_ENABLE); - // Setting Iref - snled27351_write_register(addr, SNLED27351_FUNCTION_REG_SOFTWARE_SLEEP, 0); - // Set LED CONTROL PAGE (Page 0) - snled27351_write_register(addr, SNLED27351_REG_COMMAND, SNLED27351_COMMAND_LED_CONTROL); - for (int i = 0; i < SNLED27351_LED_CONTROL_ON_OFF_LENGTH; i++) { - snled27351_write_register(addr, i, 0x00); - } - - // Set PWM PAGE (Page 1) - snled27351_write_register(addr, SNLED27351_REG_COMMAND, SNLED27351_COMMAND_PWM); - for (int i = 0; i < SNLED27351_LED_CURRENT_TUNE_LENGTH; i++) { - snled27351_write_register(addr, i, 0x00); - } - - // Set CURRENT PAGE (Page 4) - uint8_t current_tune_reg_list[SNLED27351_LED_CURRENT_TUNE_LENGTH] = SNLED27351_CURRENT_TUNE; - snled27351_write_register(addr, SNLED27351_REG_COMMAND, SNLED27351_COMMAND_CURRENT_TUNE); - for (int i = 0; i < SNLED27351_LED_CURRENT_TUNE_LENGTH; i++) { - snled27351_write_register(addr, i, current_tune_reg_list[i]); - } - - // Enable LEDs ON/OFF - snled27351_write_register(addr, SNLED27351_REG_COMMAND, SNLED27351_COMMAND_LED_CONTROL); - for (int i = 0; i < SNLED27351_LED_CONTROL_ON_OFF_LENGTH; i++) { - snled27351_write_register(addr, i, 0xFF); - } - - // Select to function page - snled27351_write_register(addr, SNLED27351_REG_COMMAND, SNLED27351_COMMAND_FUNCTION); - // Setting LED driver to normal mode - snled27351_write_register(addr, SNLED27351_FUNCTION_REG_SOFTWARE_SHUTDOWN, SNLED27351_SOFTWARE_SHUTDOWN_SSD_NORMAL); -} - -void snled27351_set_value(int index, uint8_t value) { - snled27351_led_t led; - if (index >= 0 && index < SNLED27351_LED_COUNT) { - memcpy_P(&led, (&g_snled27351_leds[index]), sizeof(led)); - - if (g_pwm_buffer[led.driver][led.v] == value) { - return; - } - g_pwm_buffer[led.driver][led.v] = value; - g_pwm_buffer_update_required[led.driver] = true; - } -} - -void snled27351_set_value_all(uint8_t value) { - for (int i = 0; i < SNLED27351_LED_COUNT; i++) { - snled27351_set_value(i, value); - } -} - -void snled27351_set_led_control_register(uint8_t index, bool value) { - snled27351_led_t led; - memcpy_P(&led, (&g_snled27351_leds[index]), sizeof(led)); - - uint8_t control_register = led.v / 8; - uint8_t bit_value = led.v % 8; - - if (value) { - g_led_control_registers[led.driver][control_register] |= (1 << bit_value); - } else { - g_led_control_registers[led.driver][control_register] &= ~(1 << bit_value); - } - - g_led_control_registers_update_required[led.driver] = true; -} - -void snled27351_update_pwm_buffers(uint8_t addr, uint8_t index) { - if (g_pwm_buffer_update_required[index]) { - snled27351_write_register(addr, SNLED27351_REG_COMMAND, SNLED27351_COMMAND_PWM); - - // If any of the transactions fail we risk writing dirty PG0, - // refresh page 0 just in case. - if (!snled27351_write_pwm_buffer(addr, g_pwm_buffer[index])) { - g_led_control_registers_update_required[index] = true; - } - } - g_pwm_buffer_update_required[index] = false; -} - -void snled27351_update_led_control_registers(uint8_t addr, uint8_t index) { - if (g_led_control_registers_update_required[index]) { - snled27351_write_register(addr, SNLED27351_REG_COMMAND, SNLED27351_COMMAND_LED_CONTROL); - for (int i = 0; i < SNLED27351_LED_CONTROL_REGISTER_COUNT; i++) { - snled27351_write_register(addr, i, g_led_control_registers[index][i]); - } - } - g_led_control_registers_update_required[index] = false; -} - -void snled27351_flush(void) { - snled27351_update_pwm_buffers(SNLED27351_I2C_ADDRESS_1, 0); -#if defined(SNLED27351_I2C_ADDRESS_2) - snled27351_update_pwm_buffers(SNLED27351_I2C_ADDRESS_2, 1); -# if defined(SNLED27351_I2C_ADDRESS_3) - snled27351_update_pwm_buffers(SNLED27351_I2C_ADDRESS_3, 2); -# if defined(SNLED27351_I2C_ADDRESS_4) - snled27351_update_pwm_buffers(SNLED27351_I2C_ADDRESS_4, 3); -# endif -# endif -#endif -} - -void snled27351_sw_return_normal(uint8_t addr) { - // Select to function page - snled27351_write_register(addr, SNLED27351_REG_COMMAND, SNLED27351_COMMAND_FUNCTION); - // Setting LED driver to normal mode - snled27351_write_register(addr, SNLED27351_FUNCTION_REG_SOFTWARE_SHUTDOWN, SNLED27351_SOFTWARE_SHUTDOWN_SSD_NORMAL); -} - -void snled27351_sw_shutdown(uint8_t addr) { - // Select to function page - snled27351_write_register(addr, SNLED27351_REG_COMMAND, SNLED27351_COMMAND_FUNCTION); - // Setting LED driver to shutdown mode - snled27351_write_register(addr, SNLED27351_FUNCTION_REG_SOFTWARE_SHUTDOWN, SNLED27351_SOFTWARE_SHUTDOWN_SSD_SHUTDOWN); - // Write SW Sleep Register - snled27351_write_register(addr, SNLED27351_FUNCTION_REG_SOFTWARE_SLEEP, SNLED27351_SOFTWARE_SLEEP_ENABLE); -} diff --git a/drivers/led/snled27351-simple.h b/drivers/led/snled27351-simple.h deleted file mode 100644 index 2fc62a6f0a..0000000000 --- a/drivers/led/snled27351-simple.h +++ /dev/null @@ -1,380 +0,0 @@ -/* Copyright 2021 @ Keychron (https://www.keychron.com) - * - * 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 2 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/>. - */ - -#pragma once - -#include <stdint.h> -#include <stdbool.h> -#include "progmem.h" -#include "util.h" - -// ======== DEPRECATED DEFINES - DO NOT USE ======== -#ifdef CKLED2001_TIMEOUT -# define SNLED27351_I2C_TIMEOUT CKLED2001_TIMEOUT -#endif -#ifdef CKLED2001_PERSISTENCE -# define SNLED27351_I2C_PERSISTENCE CKLED2001_PERSISTENCE -#endif -#ifdef PHASE_CHANNEL -# define SNLED27351_PHASE_CHANNEL PHASE_CHANNEL -#endif -#ifdef CKLED2001_CURRENT_TUNE -# define SNLED27351_CURRENT_TUNE CKLED2001_CURRENT_TUNE -#endif - -#define MSKPHASE_12CHANNEL SNLED27351_SCAN_PHASE_12_CHANNEL -#define MSKPHASE_11CHANNEL SNLED27351_SCAN_PHASE_11_CHANNEL -#define MSKPHASE_10CHANNEL SNLED27351_SCAN_PHASE_10_CHANNEL -#define MSKPHASE_9CHANNEL SNLED27351_SCAN_PHASE_9_CHANNEL -#define MSKPHASE_8CHANNEL SNLED27351_SCAN_PHASE_8_CHANNEL -#define MSKPHASE_7CHANNEL SNLED27351_SCAN_PHASE_7_CHANNEL -#define MSKPHASE_6CHANNEL SNLED27351_SCAN_PHASE_6_CHANNEL -#define MSKPHASE_5CHANNEL SNLED27351_SCAN_PHASE_5_CHANNEL -#define MSKPHASE_4CHANNEL SNLED27351_SCAN_PHASE_4_CHANNEL -#define MSKPHASE_3CHANNEL SNLED27351_SCAN_PHASE_3_CHANNEL -#define MSKPHASE_2CHANNEL SNLED27351_SCAN_PHASE_2_CHANNEL -#define MSKPHASE_1CHANNEL SNLED27351_SCAN_PHASE_1_CHANNEL - -#define ckled2001_led snled27351_led_t -#define g_ckled2001_leds g_snled27351_leds -// ======== - -#define SNLED27351_REG_COMMAND 0xFD -#define SNLED27351_COMMAND_LED_CONTROL 0x00 -#define SNLED27351_COMMAND_PWM 0x01 -#define SNLED27351_COMMAND_FUNCTION 0x03 -#define SNLED27351_COMMAND_CURRENT_TUNE 0x04 - -#define SNLED27351_FUNCTION_REG_SOFTWARE_SHUTDOWN 0x00 -#define SNLED27351_SOFTWARE_SHUTDOWN_SSD_SHUTDOWN (0x0 << 0) -#define SNLED27351_SOFTWARE_SHUTDOWN_SSD_NORMAL (0x1 << 0) - -#define SNLED27351_FUNCTION_REG_ID 0x11 -#define SNLED27351_DRIVER_ID 0x8A - -#define SNLED27351_FUNCTION_REG_PULLDOWNUP 0x13 -#define SNLED27351_PULLDOWNUP_ALL_ENABLED 0xAA - -#define SNLED27351_FUNCTION_REG_SCAN_PHASE 0x14 -#define SNLED27351_SCAN_PHASE_12_CHANNEL 0x00 -#define SNLED27351_SCAN_PHASE_11_CHANNEL 0x01 -#define SNLED27351_SCAN_PHASE_10_CHANNEL 0x02 -#define SNLED27351_SCAN_PHASE_9_CHANNEL 0x03 -#define SNLED27351_SCAN_PHASE_8_CHANNEL 0x04 -#define SNLED27351_SCAN_PHASE_7_CHANNEL 0x05 -#define SNLED27351_SCAN_PHASE_6_CHANNEL 0x06 -#define SNLED27351_SCAN_PHASE_5_CHANNEL 0x07 -#define SNLED27351_SCAN_PHASE_4_CHANNEL 0x08 -#define SNLED27351_SCAN_PHASE_3_CHANNEL 0x09 -#define SNLED27351_SCAN_PHASE_2_CHANNEL 0x0A -#define SNLED27351_SCAN_PHASE_1_CHANNEL 0x0B - -#define SNLED27351_FUNCTION_REG_SLEW_RATE_CONTROL_MODE_1 0x15 -#define SNLED27351_SLEW_RATE_CONTROL_MODE_1_PDP_ENABLE (0b1 << 2) - -#define SNLED27351_FUNCTION_REG_SLEW_RATE_CONTROL_MODE_2 0x16 -#define SNLED27351_SLEW_RATE_CONTROL_MODE_2_SSL_ENABLE (0b1 << 6) -#define SNLED27351_SLEW_RATE_CONTROL_MODE_2_DSL_ENABLE (0b1 << 7) - -#define SNLED27351_FUNCTION_REG_OPEN_SHORT_ENABLE 0x17 -#define SNLED27351_OPEN_SHORT_ENABLE_SDS_ENABLE (0b1 << 6) -#define SNLED27351_OPEN_SHORT_ENABLE_ODS_ENABLE (0b1 << 7) - -#define SNLED27351_FUNCTION_REG_OPEN_SHORT_DUTY 0x18 - -#define SNLED27351_FUNCTION_REG_OPEN_SHORT_FLAG 0x19 -#define SNLED27351_OPEN_SHORT_FLAG_OSINT_ENABLE (0b1 << 6) -#define SNLED27351_OPEN_SHORT_FLAG_ODINT_ENABLE (0b1 << 7) - -#define SNLED27351_FUNCTION_REG_SOFTWARE_SLEEP 0x1A -#define SNLED27351_SOFTWARE_SLEEP_ENABLE (0b1 << 1) - -// LED Control Registers -#define SNLED27351_LED_CONTROL_ON_OFF_FIRST_ADDR 0x0 -#define SNLED27351_LED_CONTROL_ON_OFF_LAST_ADDR 0x17 -#define SNLED27351_LED_CONTROL_ON_OFF_LENGTH ((SNLED27351_LED_CONTROL_ON_OFF_LAST_ADDR - SNLED27351_LED_CONTROL_ON_OFF_FIRST_ADDR) + 1) - -#define SNLED27351_LED_CONTROL_OPEN_FIRST_ADDR 0x18 -#define SNLED27351_LED_CONTROL_OPEN_LAST_ADDR 0x2F -#define SNLED27351_LED_CONTROL_OPEN_LENGTH ((SNLED27351_LED_CONTROL_OPEN_LAST_ADDR - SNLED27351_LED_CONTROL_OPEN_FIRST_ADDR) + 1) - -#define SNLED27351_LED_CONTROL_SHORT_FIRST_ADDR 0x30 -#define SNLED27351_LED_CONTROL_SHORT_LAST_ADDR 0x47 -#define SNLED27351_LED_CONTROL_SHORT_LENGTH ((SNLED27351_LED_CONTROL_SHORT_LAST_ADDR - SNLED27351_LED_CONTROL_SHORT_FIRST_ADDR) + 1) - -#define SNLED27351_LED_CONTROL_PAGE_LENGTH 0x48 - -// LED Control Registers -#define SNLED27351_LED_PWM_FIRST_ADDR 0x00 -#define SNLED27351_LED_PWM_LAST_ADDR 0xBF -#define SNLED27351_LED_PWM_LENGTH 0xC0 - -// Current Tune Registers -#define SNLED27351_LED_CURRENT_TUNE_FIRST_ADDR 0x00 -#define SNLED27351_LED_CURRENT_TUNE_LAST_ADDR 0x0B -#define SNLED27351_LED_CURRENT_TUNE_LENGTH 0x0C - -#define SNLED27351_I2C_ADDRESS_GND 0x74 -#define SNLED27351_I2C_ADDRESS_SCL 0x75 -#define SNLED27351_I2C_ADDRESS_SDA 0x76 -#define SNLED27351_I2C_ADDRESS_VDDIO 0x77 - -#if defined(LED_MATRIX_SNLED27351) -# define SNLED27351_LED_COUNT LED_MATRIX_LED_COUNT -#endif - -#if defined(SNLED27351_I2C_ADDRESS_4) -# define SNLED27351_DRIVER_COUNT 4 -#elif defined(SNLED27351_I2C_ADDRESS_3) -# define SNLED27351_DRIVER_COUNT 3 -#elif defined(SNLED27351_I2C_ADDRESS_2) -# define SNLED27351_DRIVER_COUNT 2 -#elif defined(SNLED27351_I2C_ADDRESS_1) -# define SNLED27351_DRIVER_COUNT 1 -#endif - -typedef struct snled27351_led_t { - uint8_t driver : 2; - uint8_t v; -} PACKED snled27351_led_t; - -extern const snled27351_led_t PROGMEM g_snled27351_leds[SNLED27351_LED_COUNT]; - -void snled27351_init_drivers(void); -void snled27351_init(uint8_t addr); -bool snled27351_write_register(uint8_t addr, uint8_t reg, uint8_t data); -bool snled27351_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer); - -void snled27351_set_value(int index, uint8_t value); -void snled27351_set_value_all(uint8_t value); - -void snled27351_set_led_control_register(uint8_t index, bool value); - -// This should not be called from an interrupt -// (eg. from a timer interrupt). -// Call this while idle (in between matrix scans). -// If the buffer is dirty, it will update the driver with the buffer. -void snled27351_update_pwm_buffers(uint8_t addr, uint8_t index); -void snled27351_update_led_control_registers(uint8_t addr, uint8_t index); - -void snled27351_flush(void); - -void snled27351_sw_return_normal(uint8_t addr); -void snled27351_sw_shutdown(uint8_t addr); - -#define A_1 0x00 -#define A_2 0x01 -#define A_3 0x02 -#define A_4 0x03 -#define A_5 0x04 -#define A_6 0x05 -#define A_7 0x06 -#define A_8 0x07 -#define A_9 0x08 -#define A_10 0x09 -#define A_11 0x0A -#define A_12 0x0B -#define A_13 0x0C -#define A_14 0x0D -#define A_15 0x0E -#define A_16 0x0F - -#define B_1 0x10 -#define B_2 0x11 -#define B_3 0x12 -#define B_4 0x13 -#define B_5 0x14 -#define B_6 0x15 -#define B_7 0x16 -#define B_8 0x17 -#define B_9 0x18 -#define B_10 0x19 -#define B_11 0x1A -#define B_12 0x1B -#define B_13 0x1C -#define B_14 0x1D -#define B_15 0x1E -#define B_16 0x1F - -#define C_1 0x20 -#define C_2 0x21 -#define C_3 0x22 -#define C_4 0x23 -#define C_5 0x24 -#define C_6 0x25 -#define C_7 0x26 -#define C_8 0x27 -#define C_9 0x28 -#define C_10 0x29 -#define C_11 0x2A -#define C_12 0x2B -#define C_13 0x2C -#define C_14 0x2D -#define C_15 0x2E -#define C_16 0x2F - -#define D_1 0x30 -#define D_2 0x31 -#define D_3 0x32 -#define D_4 0x33 -#define D_5 0x34 -#define D_6 0x35 -#define D_7 0x36 -#define D_8 0x37 -#define D_9 0x38 -#define D_10 0x39 -#define D_11 0x3A -#define D_12 0x3B -#define D_13 0x3C -#define D_14 0x3D -#define D_15 0x3E -#define D_16 0x3F - -#define E_1 0x40 -#define E_2 0x41 -#define E_3 0x42 -#define E_4 0x43 -#define E_5 0x44 -#define E_6 0x45 -#define E_7 0x46 -#define E_8 0x47 -#define E_9 0x48 -#define E_10 0x49 -#define E_11 0x4A -#define E_12 0x4B -#define E_13 0x4C -#define E_14 0x4D -#define E_15 0x4E -#define E_16 0x4F - -#define F_1 0x50 -#define F_2 0x51 -#define F_3 0x52 -#define F_4 0x53 -#define F_5 0x54 -#define F_6 0x55 -#define F_7 0x56 -#define F_8 0x57 -#define F_9 0x58 -#define F_10 0x59 -#define F_11 0x5A -#define F_12 0x5B -#define F_13 0x5C -#define F_14 0x5D -#define F_15 0x5E -#define F_16 0x5F - -#define G_1 0x60 -#define G_2 0x61 -#define G_3 0x62 -#define G_4 0x63 -#define G_5 0x64 -#define G_6 0x65 -#define G_7 0x66 -#define G_8 0x67 -#define G_9 0x68 -#define G_10 0x69 -#define G_11 0x6A -#define G_12 0x6B -#define G_13 0x6C -#define G_14 0x6D -#define G_15 0x6E -#define G_16 0x6F - -#define H_1 0x70 -#define H_2 0x71 -#define H_3 0x72 -#define H_4 0x73 -#define H_5 0x74 -#define H_6 0x75 -#define H_7 0x76 -#define H_8 0x77 -#define H_9 0x78 -#define H_10 0x79 -#define H_11 0x7A -#define H_12 0x7B -#define H_13 0x7C -#define H_14 0x7D -#define H_15 0x7E -#define H_16 0x7F - -#define I_1 0x80 -#define I_2 0x81 -#define I_3 0x82 -#define I_4 0x83 -#define I_5 0x84 -#define I_6 0x85 -#define I_7 0x86 -#define I_8 0x87 -#define I_9 0x88 -#define I_10 0x89 -#define I_11 0x8A -#define I_12 0x8B -#define I_13 0x8C -#define I_14 0x8D -#define I_15 0x8E -#define I_16 0x8F - -#define J_1 0x90 -#define J_2 0x91 -#define J_3 0x92 -#define J_4 0x93 -#define J_5 0x94 -#define J_6 0x95 -#define J_7 0x96 -#define J_8 0x97 -#define J_9 0x98 -#define J_10 0x99 -#define J_11 0x9A -#define J_12 0x9B -#define J_13 0x9C -#define J_14 0x9D -#define J_15 0x9E -#define J_16 0x9F - -#define K_1 0xA0 -#define K_2 0xA1 -#define K_3 0xA2 -#define K_4 0xA3 -#define K_5 0xA4 -#define K_6 0xA5 -#define K_7 0xA6 -#define K_8 0xA7 -#define K_9 0xA8 -#define K_10 0xA9 -#define K_11 0xAA -#define K_12 0xAB -#define K_13 0xAC -#define K_14 0xAD -#define K_15 0xAE -#define K_16 0xAF - -#define L_1 0xB0 -#define L_2 0xB1 -#define L_3 0xB2 -#define L_4 0xB3 -#define L_5 0xB4 -#define L_6 0xB5 -#define L_7 0xB6 -#define L_8 0xB7 -#define L_9 0xB8 -#define L_10 0xB9 -#define L_11 0xBA -#define L_12 0xBB -#define L_13 0xBC -#define L_14 0xBD -#define L_15 0xBE -#define L_16 0xBF diff --git a/drivers/led/snled27351.c b/drivers/led/snled27351.c index 71992b7322..8ebf681bdb 100644 --- a/drivers/led/snled27351.c +++ b/drivers/led/snled27351.c @@ -16,6 +16,7 @@ #include "snled27351.h" #include "i2c_master.h" +#include "gpio.h" #define SNLED27351_PWM_REGISTER_COUNT 192 #define SNLED27351_LED_CONTROL_REGISTER_COUNT 24 @@ -37,8 +38,18 @@ { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF } #endif -// Transfer buffer for TWITransmitData() -uint8_t g_twi_transfer_buffer[65]; +const uint8_t i2c_addresses[SNLED27351_DRIVER_COUNT] = { + SNLED27351_I2C_ADDRESS_1, +#ifdef SNLED27351_I2C_ADDRESS_2 + SNLED27351_I2C_ADDRESS_2, +# ifdef SNLED27351_I2C_ADDRESS_3 + SNLED27351_I2C_ADDRESS_3, +# ifdef SNLED27351_I2C_ADDRESS_4 + SNLED27351_I2C_ADDRESS_4, +# endif +# endif +#endif +}; // These buffers match the SNLED27351 PWM registers. // The control buffers match the PG0 LED On/Off registers. @@ -46,135 +57,116 @@ uint8_t g_twi_transfer_buffer[65]; // We could optimize this and take out the unused registers from these // buffers and the transfers in snled27351_write_pwm_buffer() but it's // probably not worth the extra complexity. -uint8_t g_pwm_buffer[SNLED27351_DRIVER_COUNT][SNLED27351_PWM_REGISTER_COUNT]; -bool g_pwm_buffer_update_required[SNLED27351_DRIVER_COUNT] = {false}; - -uint8_t g_led_control_registers[SNLED27351_DRIVER_COUNT][SNLED27351_LED_CONTROL_REGISTER_COUNT] = {0}; -bool g_led_control_registers_update_required[SNLED27351_DRIVER_COUNT] = {false}; - -bool snled27351_write_register(uint8_t addr, uint8_t reg, uint8_t data) { - // If the transaction fails function returns false. - g_twi_transfer_buffer[0] = reg; - g_twi_transfer_buffer[1] = data; - +typedef struct snled27351_driver_t { + uint8_t pwm_buffer[SNLED27351_PWM_REGISTER_COUNT]; + bool pwm_buffer_dirty; + uint8_t led_control_buffer[SNLED27351_LED_CONTROL_REGISTER_COUNT]; + bool led_control_buffer_dirty; +} PACKED snled27351_driver_t; + +snled27351_driver_t driver_buffers[SNLED27351_DRIVER_COUNT] = {{ + .pwm_buffer = {0}, + .pwm_buffer_dirty = false, + .led_control_buffer = {0}, + .led_control_buffer_dirty = false, +}}; + +void snled27351_write_register(uint8_t index, uint8_t reg, uint8_t data) { #if SNLED27351_I2C_PERSISTENCE > 0 for (uint8_t i = 0; i < SNLED27351_I2C_PERSISTENCE; i++) { - if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, SNLED27351_I2C_TIMEOUT) != 0) { - return false; - } + if (i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, SNLED27351_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break; } #else - if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, SNLED27351_I2C_TIMEOUT) != 0) { - return false; - } + i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, SNLED27351_I2C_TIMEOUT); #endif - return true; } -bool snled27351_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer) { +void snled27351_select_page(uint8_t index, uint8_t page) { + snled27351_write_register(index, SNLED27351_REG_COMMAND, page); +} + +void snled27351_write_pwm_buffer(uint8_t index) { // Assumes PG1 is already selected. - // If any of the transactions fails function returns false. - // Transmit PWM registers in 3 transfers of 64 bytes. - - // Iterate over the pwm_buffer contents at 64 byte intervals. - for (uint8_t i = 0; i < SNLED27351_PWM_REGISTER_COUNT; i += 64) { - g_twi_transfer_buffer[0] = i; - // Copy the data from i to i+63. - // Device will auto-increment register for data after the first byte - // Thus this sets registers 0x00-0x0F, 0x10-0x1F, etc. in one transfer. - for (uint8_t j = 0; j < 64; j++) { - g_twi_transfer_buffer[1 + j] = pwm_buffer[i + j]; - } + // Transmit PWM registers in 12 transfers of 16 bytes. + // Iterate over the pwm_buffer contents at 16 byte intervals. + for (uint8_t i = 0; i < SNLED27351_PWM_REGISTER_COUNT; i += 16) { #if SNLED27351_I2C_PERSISTENCE > 0 - for (uint8_t i = 0; i < SNLED27351_I2C_PERSISTENCE; i++) { - if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 65, SNLED27351_I2C_TIMEOUT) != 0) { - return false; - } + for (uint8_t j = 0; j < SNLED27351_I2C_PERSISTENCE; j++) { + if (i2c_write_register(i2c_addresses[index] << 1, i, driver_buffers[index].pwm_buffer + i, 16, SNLED27351_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break; } #else - if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 65, SNLED27351_I2C_TIMEOUT) != 0) { - return false; - } + i2c_write_register(i2c_addresses[index] << 1, i, driver_buffers[index].pwm_buffer + i, 16, SNLED27351_I2C_TIMEOUT); #endif } - return true; } void snled27351_init_drivers(void) { i2c_init(); - snled27351_init(SNLED27351_I2C_ADDRESS_1); -#if defined(SNLED27351_I2C_ADDRESS_2) - snled27351_init(SNLED27351_I2C_ADDRESS_2); -# if defined(SNLED27351_I2C_ADDRESS_3) - snled27351_init(SNLED27351_I2C_ADDRESS_3); -# if defined(SNLED27351_I2C_ADDRESS_4) - snled27351_init(SNLED27351_I2C_ADDRESS_4); -# endif -# endif +#if defined(SNLED27351_SDB_PIN) + gpio_set_pin_output(SNLED27351_SDB_PIN); + gpio_write_pin_high(SNLED27351_SDB_PIN); #endif + for (uint8_t i = 0; i < SNLED27351_DRIVER_COUNT; i++) { + snled27351_init(i); + } + for (int i = 0; i < SNLED27351_LED_COUNT; i++) { snled27351_set_led_control_register(i, true, true, true); } - snled27351_update_led_control_registers(SNLED27351_I2C_ADDRESS_1, 0); -#if defined(SNLED27351_I2C_ADDRESS_2) - snled27351_update_led_control_registers(SNLED27351_I2C_ADDRESS_2, 1); -# if defined(SNLED27351_I2C_ADDRESS_3) - snled27351_update_led_control_registers(SNLED27351_I2C_ADDRESS_3, 2); -# if defined(SNLED27351_I2C_ADDRESS_4) - snled27351_update_led_control_registers(SNLED27351_I2C_ADDRESS_4, 3); -# endif -# endif -#endif + for (uint8_t i = 0; i < SNLED27351_DRIVER_COUNT; i++) { + snled27351_update_led_control_registers(i); + } } -void snled27351_init(uint8_t addr) { - // Select to function page - snled27351_write_register(addr, SNLED27351_REG_COMMAND, SNLED27351_COMMAND_FUNCTION); +void snled27351_init(uint8_t index) { + snled27351_select_page(index, SNLED27351_COMMAND_FUNCTION); + // Setting LED driver to shutdown mode - snled27351_write_register(addr, SNLED27351_FUNCTION_REG_SOFTWARE_SHUTDOWN, SNLED27351_SOFTWARE_SHUTDOWN_SSD_SHUTDOWN); + snled27351_write_register(index, SNLED27351_FUNCTION_REG_SOFTWARE_SHUTDOWN, SNLED27351_SOFTWARE_SHUTDOWN_SSD_SHUTDOWN); // Setting internal channel pulldown/pullup - snled27351_write_register(addr, SNLED27351_FUNCTION_REG_PULLDOWNUP, SNLED27351_PULLDOWNUP_ALL_ENABLED); + snled27351_write_register(index, SNLED27351_FUNCTION_REG_PULLDOWNUP, SNLED27351_PULLDOWNUP_ALL_ENABLED); // Select number of scan phase - snled27351_write_register(addr, SNLED27351_FUNCTION_REG_SCAN_PHASE, SNLED27351_PHASE_CHANNEL); + snled27351_write_register(index, SNLED27351_FUNCTION_REG_SCAN_PHASE, SNLED27351_PHASE_CHANNEL); // Setting PWM Delay Phase - snled27351_write_register(addr, SNLED27351_FUNCTION_REG_SLEW_RATE_CONTROL_MODE_1, SNLED27351_SLEW_RATE_CONTROL_MODE_1_PDP_ENABLE); + snled27351_write_register(index, SNLED27351_FUNCTION_REG_SLEW_RATE_CONTROL_MODE_1, SNLED27351_SLEW_RATE_CONTROL_MODE_1_PDP_ENABLE); // Setting Driving/Sinking Channel Slew Rate - snled27351_write_register(addr, SNLED27351_FUNCTION_REG_SLEW_RATE_CONTROL_MODE_2, SNLED27351_SLEW_RATE_CONTROL_MODE_2_DSL_ENABLE | SNLED27351_SLEW_RATE_CONTROL_MODE_2_SSL_ENABLE); + snled27351_write_register(index, SNLED27351_FUNCTION_REG_SLEW_RATE_CONTROL_MODE_2, SNLED27351_SLEW_RATE_CONTROL_MODE_2_DSL_ENABLE | SNLED27351_SLEW_RATE_CONTROL_MODE_2_SSL_ENABLE); // Setting Iref - snled27351_write_register(addr, SNLED27351_FUNCTION_REG_SOFTWARE_SLEEP, 0); - // Set LED CONTROL PAGE (Page 0) - snled27351_write_register(addr, SNLED27351_REG_COMMAND, SNLED27351_COMMAND_LED_CONTROL); + snled27351_write_register(index, SNLED27351_FUNCTION_REG_SOFTWARE_SLEEP, 0); + + snled27351_select_page(index, SNLED27351_COMMAND_LED_CONTROL); + for (int i = 0; i < SNLED27351_LED_CONTROL_ON_OFF_LENGTH; i++) { - snled27351_write_register(addr, i, 0x00); + snled27351_write_register(index, i, 0x00); } - // Set PWM PAGE (Page 1) - snled27351_write_register(addr, SNLED27351_REG_COMMAND, SNLED27351_COMMAND_PWM); + snled27351_select_page(index, SNLED27351_COMMAND_PWM); + for (int i = 0; i < SNLED27351_LED_CURRENT_TUNE_LENGTH; i++) { - snled27351_write_register(addr, i, 0x00); + snled27351_write_register(index, i, 0x00); } - // Set CURRENT PAGE (Page 4) + snled27351_select_page(index, SNLED27351_COMMAND_CURRENT_TUNE); + uint8_t current_tune_reg_list[SNLED27351_LED_CURRENT_TUNE_LENGTH] = SNLED27351_CURRENT_TUNE; - snled27351_write_register(addr, SNLED27351_REG_COMMAND, SNLED27351_COMMAND_CURRENT_TUNE); for (int i = 0; i < SNLED27351_LED_CURRENT_TUNE_LENGTH; i++) { - snled27351_write_register(addr, i, current_tune_reg_list[i]); + snled27351_write_register(index, i, current_tune_reg_list[i]); } - // Enable LEDs ON/OFF - snled27351_write_register(addr, SNLED27351_REG_COMMAND, SNLED27351_COMMAND_LED_CONTROL); + snled27351_select_page(index, SNLED27351_COMMAND_LED_CONTROL); + for (int i = 0; i < SNLED27351_LED_CONTROL_ON_OFF_LENGTH; i++) { - snled27351_write_register(addr, i, 0xFF); + snled27351_write_register(index, i, 0xFF); } - // Select to function page - snled27351_write_register(addr, SNLED27351_REG_COMMAND, SNLED27351_COMMAND_FUNCTION); + snled27351_select_page(index, SNLED27351_COMMAND_FUNCTION); + // Setting LED driver to normal mode - snled27351_write_register(addr, SNLED27351_FUNCTION_REG_SOFTWARE_SHUTDOWN, SNLED27351_SOFTWARE_SHUTDOWN_SSD_NORMAL); + snled27351_write_register(index, SNLED27351_FUNCTION_REG_SOFTWARE_SHUTDOWN, SNLED27351_SOFTWARE_SHUTDOWN_SSD_NORMAL); } void snled27351_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) { @@ -182,13 +174,14 @@ void snled27351_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) { if (index >= 0 && index < SNLED27351_LED_COUNT) { memcpy_P(&led, (&g_snled27351_leds[index]), sizeof(led)); - if (g_pwm_buffer[led.driver][led.r] == red && g_pwm_buffer[led.driver][led.g] == green && g_pwm_buffer[led.driver][led.b] == blue) { + if (driver_buffers[led.driver].pwm_buffer[led.r] == red && driver_buffers[led.driver].pwm_buffer[led.g] == green && driver_buffers[led.driver].pwm_buffer[led.b] == blue) { return; } - g_pwm_buffer[led.driver][led.r] = red; - g_pwm_buffer[led.driver][led.g] = green; - g_pwm_buffer[led.driver][led.b] = blue; - g_pwm_buffer_update_required[led.driver] = true; + + driver_buffers[led.driver].pwm_buffer[led.r] = red; + driver_buffers[led.driver].pwm_buffer[led.g] = green; + driver_buffers[led.driver].pwm_buffer[led.b] = blue; + driver_buffers[led.driver].pwm_buffer_dirty = true; } } @@ -210,72 +203,64 @@ void snled27351_set_led_control_register(uint8_t index, bool red, bool green, bo uint8_t bit_b = led.b % 8; if (red) { - g_led_control_registers[led.driver][control_register_r] |= (1 << bit_r); + driver_buffers[led.driver].led_control_buffer[control_register_r] |= (1 << bit_r); } else { - g_led_control_registers[led.driver][control_register_r] &= ~(1 << bit_r); + driver_buffers[led.driver].led_control_buffer[control_register_r] &= ~(1 << bit_r); } if (green) { - g_led_control_registers[led.driver][control_register_g] |= (1 << bit_g); + driver_buffers[led.driver].led_control_buffer[control_register_g] |= (1 << bit_g); } else { - g_led_control_registers[led.driver][control_register_g] &= ~(1 << bit_g); + driver_buffers[led.driver].led_control_buffer[control_register_g] &= ~(1 << bit_g); } if (blue) { - g_led_control_registers[led.driver][control_register_b] |= (1 << bit_b); + driver_buffers[led.driver].led_control_buffer[control_register_b] |= (1 << bit_b); } else { - g_led_control_registers[led.driver][control_register_b] &= ~(1 << bit_b); + driver_buffers[led.driver].led_control_buffer[control_register_b] &= ~(1 << bit_b); } - g_led_control_registers_update_required[led.driver] = true; + driver_buffers[led.driver].led_control_buffer_dirty = true; } -void snled27351_update_pwm_buffers(uint8_t addr, uint8_t index) { - if (g_pwm_buffer_update_required[index]) { - snled27351_write_register(addr, SNLED27351_REG_COMMAND, SNLED27351_COMMAND_PWM); +void snled27351_update_pwm_buffers(uint8_t index) { + if (driver_buffers[index].pwm_buffer_dirty) { + snled27351_select_page(index, SNLED27351_COMMAND_PWM); - // If any of the transactions fail we risk writing dirty PG0, - // refresh page 0 just in case. - if (!snled27351_write_pwm_buffer(addr, g_pwm_buffer[index])) { - g_led_control_registers_update_required[index] = true; - } + snled27351_write_pwm_buffer(index); + + driver_buffers[index].pwm_buffer_dirty = false; } - g_pwm_buffer_update_required[index] = false; } -void snled27351_update_led_control_registers(uint8_t addr, uint8_t index) { - if (g_led_control_registers_update_required[index]) { - snled27351_write_register(addr, SNLED27351_REG_COMMAND, SNLED27351_COMMAND_LED_CONTROL); - for (int i = 0; i < SNLED27351_LED_CONTROL_REGISTER_COUNT; i++) { - snled27351_write_register(addr, i, g_led_control_registers[index][i]); +void snled27351_update_led_control_registers(uint8_t index) { + if (driver_buffers[index].led_control_buffer_dirty) { + snled27351_select_page(index, SNLED27351_COMMAND_LED_CONTROL); + + for (uint8_t i = 0; i < SNLED27351_LED_CONTROL_REGISTER_COUNT; i++) { + snled27351_write_register(index, i, driver_buffers[index].led_control_buffer[i]); } + + driver_buffers[index].led_control_buffer_dirty = false; } - g_led_control_registers_update_required[index] = false; } void snled27351_flush(void) { - snled27351_update_pwm_buffers(SNLED27351_I2C_ADDRESS_1, 0); -#if defined(SNLED27351_I2C_ADDRESS_2) - snled27351_update_pwm_buffers(SNLED27351_I2C_ADDRESS_2, 1); -# if defined(SNLED27351_I2C_ADDRESS_3) - snled27351_update_pwm_buffers(SNLED27351_I2C_ADDRESS_3, 2); -# if defined(SNLED27351_I2C_ADDRESS_4) - snled27351_update_pwm_buffers(SNLED27351_I2C_ADDRESS_4, 3); -# endif -# endif -#endif + for (uint8_t i = 0; i < SNLED27351_DRIVER_COUNT; i++) { + snled27351_update_pwm_buffers(i); + } } -void snled27351_sw_return_normal(uint8_t addr) { - // Select to function page - snled27351_write_register(addr, SNLED27351_REG_COMMAND, SNLED27351_COMMAND_FUNCTION); +void snled27351_sw_return_normal(uint8_t index) { + snled27351_select_page(index, SNLED27351_COMMAND_FUNCTION); + // Setting LED driver to normal mode - snled27351_write_register(addr, SNLED27351_FUNCTION_REG_SOFTWARE_SHUTDOWN, SNLED27351_SOFTWARE_SHUTDOWN_SSD_NORMAL); + snled27351_write_register(index, SNLED27351_FUNCTION_REG_SOFTWARE_SHUTDOWN, SNLED27351_SOFTWARE_SHUTDOWN_SSD_NORMAL); } -void snled27351_sw_shutdown(uint8_t addr) { - // Select to function page - snled27351_write_register(addr, SNLED27351_REG_COMMAND, SNLED27351_COMMAND_FUNCTION); +void snled27351_sw_shutdown(uint8_t index) { + snled27351_select_page(index, SNLED27351_COMMAND_FUNCTION); + // Setting LED driver to shutdown mode - snled27351_write_register(addr, SNLED27351_FUNCTION_REG_SOFTWARE_SHUTDOWN, SNLED27351_SOFTWARE_SHUTDOWN_SSD_SHUTDOWN); + snled27351_write_register(index, SNLED27351_FUNCTION_REG_SOFTWARE_SHUTDOWN, SNLED27351_SOFTWARE_SHUTDOWN_SSD_SHUTDOWN); // Write SW Sleep Register - snled27351_write_register(addr, SNLED27351_FUNCTION_REG_SOFTWARE_SLEEP, SNLED27351_SOFTWARE_SLEEP_ENABLE); + snled27351_write_register(index, SNLED27351_FUNCTION_REG_SOFTWARE_SLEEP, SNLED27351_SOFTWARE_SLEEP_ENABLE); } diff --git a/drivers/led/snled27351.h b/drivers/led/snled27351.h index 77337f177b..d902744d14 100644 --- a/drivers/led/snled27351.h +++ b/drivers/led/snled27351.h @@ -168,9 +168,9 @@ typedef struct snled27351_led_t { extern const snled27351_led_t PROGMEM g_snled27351_leds[SNLED27351_LED_COUNT]; void snled27351_init_drivers(void); -void snled27351_init(uint8_t addr); -bool snled27351_write_register(uint8_t addr, uint8_t reg, uint8_t data); -bool snled27351_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer); +void snled27351_init(uint8_t index); +void snled27351_select_page(uint8_t index, uint8_t page); +void snled27351_write_register(uint8_t index, uint8_t reg, uint8_t data); void snled27351_set_color(int index, uint8_t red, uint8_t green, uint8_t blue); void snled27351_set_color_all(uint8_t red, uint8_t green, uint8_t blue); @@ -181,214 +181,420 @@ void snled27351_set_led_control_register(uint8_t index, bool red, bool green, bo // (eg. from a timer interrupt). // Call this while idle (in between matrix scans). // If the buffer is dirty, it will update the driver with the buffer. -void snled27351_update_pwm_buffers(uint8_t addr, uint8_t index); -void snled27351_update_led_control_registers(uint8_t addr, uint8_t index); +void snled27351_update_pwm_buffers(uint8_t index); +void snled27351_update_led_control_registers(uint8_t index); void snled27351_flush(void); -void snled27351_sw_return_normal(uint8_t addr); -void snled27351_sw_shutdown(uint8_t addr); - -#define A_1 0x00 -#define A_2 0x01 -#define A_3 0x02 -#define A_4 0x03 -#define A_5 0x04 -#define A_6 0x05 -#define A_7 0x06 -#define A_8 0x07 -#define A_9 0x08 -#define A_10 0x09 -#define A_11 0x0A -#define A_12 0x0B -#define A_13 0x0C -#define A_14 0x0D -#define A_15 0x0E -#define A_16 0x0F - -#define B_1 0x10 -#define B_2 0x11 -#define B_3 0x12 -#define B_4 0x13 -#define B_5 0x14 -#define B_6 0x15 -#define B_7 0x16 -#define B_8 0x17 -#define B_9 0x18 -#define B_10 0x19 -#define B_11 0x1A -#define B_12 0x1B -#define B_13 0x1C -#define B_14 0x1D -#define B_15 0x1E -#define B_16 0x1F - -#define C_1 0x20 -#define C_2 0x21 -#define C_3 0x22 -#define C_4 0x23 -#define C_5 0x24 -#define C_6 0x25 -#define C_7 0x26 -#define C_8 0x27 -#define C_9 0x28 -#define C_10 0x29 -#define C_11 0x2A -#define C_12 0x2B -#define C_13 0x2C -#define C_14 0x2D -#define C_15 0x2E -#define C_16 0x2F - -#define D_1 0x30 -#define D_2 0x31 -#define D_3 0x32 -#define D_4 0x33 -#define D_5 0x34 -#define D_6 0x35 -#define D_7 0x36 -#define D_8 0x37 -#define D_9 0x38 -#define D_10 0x39 -#define D_11 0x3A -#define D_12 0x3B -#define D_13 0x3C -#define D_14 0x3D -#define D_15 0x3E -#define D_16 0x3F - -#define E_1 0x40 -#define E_2 0x41 -#define E_3 0x42 -#define E_4 0x43 -#define E_5 0x44 -#define E_6 0x45 -#define E_7 0x46 -#define E_8 0x47 -#define E_9 0x48 -#define E_10 0x49 -#define E_11 0x4A -#define E_12 0x4B -#define E_13 0x4C -#define E_14 0x4D -#define E_15 0x4E -#define E_16 0x4F - -#define F_1 0x50 -#define F_2 0x51 -#define F_3 0x52 -#define F_4 0x53 -#define F_5 0x54 -#define F_6 0x55 -#define F_7 0x56 -#define F_8 0x57 -#define F_9 0x58 -#define F_10 0x59 -#define F_11 0x5A -#define F_12 0x5B -#define F_13 0x5C -#define F_14 0x5D -#define F_15 0x5E -#define F_16 0x5F - -#define G_1 0x60 -#define G_2 0x61 -#define G_3 0x62 -#define G_4 0x63 -#define G_5 0x64 -#define G_6 0x65 -#define G_7 0x66 -#define G_8 0x67 -#define G_9 0x68 -#define G_10 0x69 -#define G_11 0x6A -#define G_12 0x6B -#define G_13 0x6C -#define G_14 0x6D -#define G_15 0x6E -#define G_16 0x6F - -#define H_1 0x70 -#define H_2 0x71 -#define H_3 0x72 -#define H_4 0x73 -#define H_5 0x74 -#define H_6 0x75 -#define H_7 0x76 -#define H_8 0x77 -#define H_9 0x78 -#define H_10 0x79 -#define H_11 0x7A -#define H_12 0x7B -#define H_13 0x7C -#define H_14 0x7D -#define H_15 0x7E -#define H_16 0x7F - -#define I_1 0x80 -#define I_2 0x81 -#define I_3 0x82 -#define I_4 0x83 -#define I_5 0x84 -#define I_6 0x85 -#define I_7 0x86 -#define I_8 0x87 -#define I_9 0x88 -#define I_10 0x89 -#define I_11 0x8A -#define I_12 0x8B -#define I_13 0x8C -#define I_14 0x8D -#define I_15 0x8E -#define I_16 0x8F - -#define J_1 0x90 -#define J_2 0x91 -#define J_3 0x92 -#define J_4 0x93 -#define J_5 0x94 -#define J_6 0x95 -#define J_7 0x96 -#define J_8 0x97 -#define J_9 0x98 -#define J_10 0x99 -#define J_11 0x9A -#define J_12 0x9B -#define J_13 0x9C -#define J_14 0x9D -#define J_15 0x9E -#define J_16 0x9F - -#define K_1 0xA0 -#define K_2 0xA1 -#define K_3 0xA2 -#define K_4 0xA3 -#define K_5 0xA4 -#define K_6 0xA5 -#define K_7 0xA6 -#define K_8 0xA7 -#define K_9 0xA8 -#define K_10 0xA9 -#define K_11 0xAA -#define K_12 0xAB -#define K_13 0xAC -#define K_14 0xAD -#define K_15 0xAE -#define K_16 0xAF - -#define L_1 0xB0 -#define L_2 0xB1 -#define L_3 0xB2 -#define L_4 0xB3 -#define L_5 0xB4 -#define L_6 0xB5 -#define L_7 0xB6 -#define L_8 0xB7 -#define L_9 0xB8 -#define L_10 0xB9 -#define L_11 0xBA -#define L_12 0xBB -#define L_13 0xBC -#define L_14 0xBD -#define L_15 0xBE -#define L_16 0xBF +void snled27351_sw_return_normal(uint8_t index); +void snled27351_sw_shutdown(uint8_t index); + +#define CB1_CA1 0x00 +#define CB1_CA2 0x01 +#define CB1_CA3 0x02 +#define CB1_CA4 0x03 +#define CB1_CA5 0x04 +#define CB1_CA6 0x05 +#define CB1_CA7 0x06 +#define CB1_CA8 0x07 +#define CB1_CA9 0x08 +#define CB1_CA10 0x09 +#define CB1_CA11 0x0A +#define CB1_CA12 0x0B +#define CB1_CA13 0x0C +#define CB1_CA14 0x0D +#define CB1_CA15 0x0E +#define CB1_CA16 0x0F + +#define CB2_CA1 0x10 +#define CB2_CA2 0x11 +#define CB2_CA3 0x12 +#define CB2_CA4 0x13 +#define CB2_CA5 0x14 +#define CB2_CA6 0x15 +#define CB2_CA7 0x16 +#define CB2_CA8 0x17 +#define CB2_CA9 0x18 +#define CB2_CA10 0x19 +#define CB2_CA11 0x1A +#define CB2_CA12 0x1B +#define CB2_CA13 0x1C +#define CB2_CA14 0x1D +#define CB2_CA15 0x1E +#define CB2_CA16 0x1F + +#define CB3_CA1 0x20 +#define CB3_CA2 0x21 +#define CB3_CA3 0x22 +#define CB3_CA4 0x23 +#define CB3_CA5 0x24 +#define CB3_CA6 0x25 +#define CB3_CA7 0x26 +#define CB3_CA8 0x27 +#define CB3_CA9 0x28 +#define CB3_CA10 0x29 +#define CB3_CA11 0x2A +#define CB3_CA12 0x2B +#define CB3_CA13 0x2C +#define CB3_CA14 0x2D +#define CB3_CA15 0x2E +#define CB3_CA16 0x2F + +#define CB4_CA1 0x30 +#define CB4_CA2 0x31 +#define CB4_CA3 0x32 +#define CB4_CA4 0x33 +#define CB4_CA5 0x34 +#define CB4_CA6 0x35 +#define CB4_CA7 0x36 +#define CB4_CA8 0x37 +#define CB4_CA9 0x38 +#define CB4_CA10 0x39 +#define CB4_CA11 0x3A +#define CB4_CA12 0x3B +#define CB4_CA13 0x3C +#define CB4_CA14 0x3D +#define CB4_CA15 0x3E +#define CB4_CA16 0x3F + +#define CB5_CA1 0x40 +#define CB5_CA2 0x41 +#define CB5_CA3 0x42 +#define CB5_CA4 0x43 +#define CB5_CA5 0x44 +#define CB5_CA6 0x45 +#define CB5_CA7 0x46 +#define CB5_CA8 0x47 +#define CB5_CA9 0x48 +#define CB5_CA10 0x49 +#define CB5_CA11 0x4A +#define CB5_CA12 0x4B +#define CB5_CA13 0x4C +#define CB5_CA14 0x4D +#define CB5_CA15 0x4E +#define CB5_CA16 0x4F + +#define CB6_CA1 0x50 +#define CB6_CA2 0x51 +#define CB6_CA3 0x52 +#define CB6_CA4 0x53 +#define CB6_CA5 0x54 +#define CB6_CA6 0x55 +#define CB6_CA7 0x56 +#define CB6_CA8 0x57 +#define CB6_CA9 0x58 +#define CB6_CA10 0x59 +#define CB6_CA11 0x5A +#define CB6_CA12 0x5B +#define CB6_CA13 0x5C +#define CB6_CA14 0x5D +#define CB6_CA15 0x5E +#define CB6_CA16 0x5F + +#define CB7_CA1 0x60 +#define CB7_CA2 0x61 +#define CB7_CA3 0x62 +#define CB7_CA4 0x63 +#define CB7_CA5 0x64 +#define CB7_CA6 0x65 +#define CB7_CA7 0x66 +#define CB7_CA8 0x67 +#define CB7_CA9 0x68 +#define CB7_CA10 0x69 +#define CB7_CA11 0x6A +#define CB7_CA12 0x6B +#define CB7_CA13 0x6C +#define CB7_CA14 0x6D +#define CB7_CA15 0x6E +#define CB7_CA16 0x6F + +#define CB8_CA1 0x70 +#define CB8_CA2 0x71 +#define CB8_CA3 0x72 +#define CB8_CA4 0x73 +#define CB8_CA5 0x74 +#define CB8_CA6 0x75 +#define CB8_CA7 0x76 +#define CB8_CA8 0x77 +#define CB8_CA9 0x78 +#define CB8_CA10 0x79 +#define CB8_CA11 0x7A +#define CB8_CA12 0x7B +#define CB8_CA13 0x7C +#define CB8_CA14 0x7D +#define CB8_CA15 0x7E +#define CB8_CA16 0x7F + +#define CB9_CA1 0x80 +#define CB9_CA2 0x81 +#define CB9_CA3 0x82 +#define CB9_CA4 0x83 +#define CB9_CA5 0x84 +#define CB9_CA6 0x85 +#define CB9_CA7 0x86 +#define CB9_CA8 0x87 +#define CB9_CA9 0x88 +#define CB9_CA10 0x89 +#define CB9_CA11 0x8A +#define CB9_CA12 0x8B +#define CB9_CA13 0x8C +#define CB9_CA14 0x8D +#define CB9_CA15 0x8E +#define CB9_CA16 0x8F + +#define CB10_CA1 0x90 +#define CB10_CA2 0x91 +#define CB10_CA3 0x92 +#define CB10_CA4 0x93 +#define CB10_CA5 0x94 +#define CB10_CA6 0x95 +#define CB10_CA7 0x96 +#define CB10_CA8 0x97 +#define CB10_CA9 0x98 +#define CB10_CA10 0x99 +#define CB10_CA11 0x9A +#define CB10_CA12 0x9B +#define CB10_CA13 0x9C +#define CB10_CA14 0x9D +#define CB10_CA15 0x9E +#define CB10_CA16 0x9F + +#define CB11_CA1 0xA0 +#define CB11_CA2 0xA1 +#define CB11_CA3 0xA2 +#define CB11_CA4 0xA3 +#define CB11_CA5 0xA4 +#define CB11_CA6 0xA5 +#define CB11_CA7 0xA6 +#define CB11_CA8 0xA7 +#define CB11_CA9 0xA8 +#define CB11_CA10 0xA9 +#define CB11_CA11 0xAA +#define CB11_CA12 0xAB +#define CB11_CA13 0xAC +#define CB11_CA14 0xAD +#define CB11_CA15 0xAE +#define CB11_CA16 0xAF + +#define CB12_CA1 0xB0 +#define CB12_CA2 0xB1 +#define CB12_CA3 0xB2 +#define CB12_CA4 0xB3 +#define CB12_CA5 0xB4 +#define CB12_CA6 0xB5 +#define CB12_CA7 0xB6 +#define CB12_CA8 0xB7 +#define CB12_CA9 0xB8 +#define CB12_CA10 0xB9 +#define CB12_CA11 0xBA +#define CB12_CA12 0xBB +#define CB12_CA13 0xBC +#define CB12_CA14 0xBD +#define CB12_CA15 0xBE +#define CB12_CA16 0xBF + +// DEPRECATED - DO NOT USE + +#define A_1 CB1_CA1 +#define A_2 CB1_CA2 +#define A_3 CB1_CA3 +#define A_4 CB1_CA4 +#define A_5 CB1_CA5 +#define A_6 CB1_CA6 +#define A_7 CB1_CA7 +#define A_8 CB1_CA8 +#define A_9 CB1_CA9 +#define A_10 CB1_CA10 +#define A_11 CB1_CA11 +#define A_12 CB1_CA12 +#define A_13 CB1_CA13 +#define A_14 CB1_CA14 +#define A_15 CB1_CA15 +#define A_16 CB1_CA16 + +#define B_1 CB2_CA1 +#define B_2 CB2_CA2 +#define B_3 CB2_CA3 +#define B_4 CB2_CA4 +#define B_5 CB2_CA5 +#define B_6 CB2_CA6 +#define B_7 CB2_CA7 +#define B_8 CB2_CA8 +#define B_9 CB2_CA9 +#define B_10 CB2_CA10 +#define B_11 CB2_CA11 +#define B_12 CB2_CA12 +#define B_13 CB2_CA13 +#define B_14 CB2_CA14 +#define B_15 CB2_CA15 +#define B_16 CB2_CA16 + +#define C_1 CB3_CA1 +#define C_2 CB3_CA2 +#define C_3 CB3_CA3 +#define C_4 CB3_CA4 +#define C_5 CB3_CA5 +#define C_6 CB3_CA6 +#define C_7 CB3_CA7 +#define C_8 CB3_CA8 +#define C_9 CB3_CA9 +#define C_10 CB3_CA10 +#define C_11 CB3_CA11 +#define C_12 CB3_CA12 +#define C_13 CB3_CA13 +#define C_14 CB3_CA14 +#define C_15 CB3_CA15 +#define C_16 CB3_CA16 + +#define D_1 CB4_CA1 +#define D_2 CB4_CA2 +#define D_3 CB4_CA3 +#define D_4 CB4_CA4 +#define D_5 CB4_CA5 +#define D_6 CB4_CA6 +#define D_7 CB4_CA7 +#define D_8 CB4_CA8 +#define D_9 CB4_CA9 +#define D_10 CB4_CA10 +#define D_11 CB4_CA11 +#define D_12 CB4_CA12 +#define D_13 CB4_CA13 +#define D_14 CB4_CA14 +#define D_15 CB4_CA15 +#define D_16 CB4_CA16 + +#define E_1 CB5_CA1 +#define E_2 CB5_CA2 +#define E_3 CB5_CA3 +#define E_4 CB5_CA4 +#define E_5 CB5_CA5 +#define E_6 CB5_CA6 +#define E_7 CB5_CA7 +#define E_8 CB5_CA8 +#define E_9 CB5_CA9 +#define E_10 CB5_CA10 +#define E_11 CB5_CA11 +#define E_12 CB5_CA12 +#define E_13 CB5_CA13 +#define E_14 CB5_CA14 +#define E_15 CB5_CA15 +#define E_16 CB5_CA16 + +#define F_1 CB6_CA1 +#define F_2 CB6_CA2 +#define F_3 CB6_CA3 +#define F_4 CB6_CA4 +#define F_5 CB6_CA5 +#define F_6 CB6_CA6 +#define F_7 CB6_CA7 +#define F_8 CB6_CA8 +#define F_9 CB6_CA9 +#define F_10 CB6_CA10 +#define F_11 CB6_CA11 +#define F_12 CB6_CA12 +#define F_13 CB6_CA13 +#define F_14 CB6_CA14 +#define F_15 CB6_CA15 +#define F_16 CB6_CA16 + +#define G_1 CB7_CA1 +#define G_2 CB7_CA2 +#define G_3 CB7_CA3 +#define G_4 CB7_CA4 +#define G_5 CB7_CA5 +#define G_6 CB7_CA6 +#define G_7 CB7_CA7 +#define G_8 CB7_CA8 +#define G_9 CB7_CA9 +#define G_10 CB7_CA10 +#define G_11 CB7_CA11 +#define G_12 CB7_CA12 +#define G_13 CB7_CA13 +#define G_14 CB7_CA14 +#define G_15 CB7_CA15 +#define G_16 CB7_CA16 + +#define H_1 CB8_CA1 +#define H_2 CB8_CA2 +#define H_3 CB8_CA3 +#define H_4 CB8_CA4 +#define H_5 CB8_CA5 +#define H_6 CB8_CA6 +#define H_7 CB8_CA7 +#define H_8 CB8_CA8 +#define H_9 CB8_CA9 +#define H_10 CB8_CA10 +#define H_11 CB8_CA11 +#define H_12 CB8_CA12 +#define H_13 CB8_CA13 +#define H_14 CB8_CA14 +#define H_15 CB8_CA15 +#define H_16 CB8_CA16 + +#define I_1 CB9_CA1 +#define I_2 CB9_CA2 +#define I_3 CB9_CA3 +#define I_4 CB9_CA4 +#define I_5 CB9_CA5 +#define I_6 CB9_CA6 +#define I_7 CB9_CA7 +#define I_8 CB9_CA8 +#define I_9 CB9_CA9 +#define I_10 CB9_CA10 +#define I_11 CB9_CA11 +#define I_12 CB9_CA12 +#define I_13 CB9_CA13 +#define I_14 CB9_CA14 +#define I_15 CB9_CA15 +#define I_16 CB9_CA16 + +#define J_1 CB10_CA1 +#define J_2 CB10_CA2 +#define J_3 CB10_CA3 +#define J_4 CB10_CA4 +#define J_5 CB10_CA5 +#define J_6 CB10_CA6 +#define J_7 CB10_CA7 +#define J_8 CB10_CA8 +#define J_9 CB10_CA9 +#define J_10 CB10_CA10 +#define J_11 CB10_CA11 +#define J_12 CB10_CA12 +#define J_13 CB10_CA13 +#define J_14 CB10_CA14 +#define J_15 CB10_CA15 +#define J_16 CB10_CA16 + +#define K_1 CB11_CA1 +#define K_2 CB11_CA2 +#define K_3 CB11_CA3 +#define K_4 CB11_CA4 +#define K_5 CB11_CA5 +#define K_6 CB11_CA6 +#define K_7 CB11_CA7 +#define K_8 CB11_CA8 +#define K_9 CB11_CA9 +#define K_10 CB11_CA10 +#define K_11 CB11_CA11 +#define K_12 CB11_CA12 +#define K_13 CB11_CA13 +#define K_14 CB11_CA14 +#define K_15 CB11_CA15 +#define K_16 CB11_CA16 + +#define L_1 CB12_CA1 +#define L_2 CB12_CA2 +#define L_3 CB12_CA3 +#define L_4 CB12_CA4 +#define L_5 CB12_CA5 +#define L_6 CB12_CA6 +#define L_7 CB12_CA7 +#define L_8 CB12_CA8 +#define L_9 CB12_CA9 +#define L_10 CB12_CA10 +#define L_11 CB12_CA11 +#define L_12 CB12_CA12 +#define L_13 CB12_CA13 +#define L_14 CB12_CA14 +#define L_15 CB12_CA15 +#define L_16 CB12_CA16 |