Co-authored-by: Xelus22 <preyas22@gmail.com> Co-authored-by: dexter93 <d3xter93@gmail.com>pull/23095/head
@ -0,0 +1,220 @@ | |||||
/* 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 "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_PULLDOWNUP | |||||
# define IS31FL3729_PULLDOWNUP 0x33 | |||||
#endif | |||||
#ifndef IS31FL3729_SPREAD_SPECTRUM | |||||
# define IS31FL3729_SPREAD_SPECTRUM IS31FL3729_SSP_DISABLE | |||||
#endif | |||||
#ifndef IS31FL3729_SPREAD_SPECTRUM_RANGE | |||||
# define IS31FL3729_SPREAD_SPECTRUM_RANGE IS31FL3729_RNG_5_PERCENT | |||||
#endif | |||||
#ifndef IS31FL3729_SPREAD_SPECTRUM_CYCLE_TIME | |||||
# define IS31FL3729_SPREAD_SPECTRUM_CYCLE_TIME IS31FL3729_CLT_1980_US | |||||
#endif | |||||
#ifndef IS31FL3729_PWM_FREQUENCY | |||||
# define IS31FL3729_PWM_FREQUENCY IS31FL3729_PWM_FREQUENCY_32K_HZ | |||||
#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 addr, 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(addr << 1, reg, &data, 1, IS31FL3729_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break; | |||||
} | |||||
#else | |||||
i2c_write_register(addr << 1, reg, &data, 1, IS31FL3729_I2C_TIMEOUT); | |||||
#endif | |||||
} | |||||
void is31fl3729_write_pwm_buffer(uint8_t addr, uint8_t index) { | |||||
// 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 <= IS31FL3729_PWM_REGISTER_COUNT; i += 16) { | |||||
#if IS31FL3729_I2C_PERSISTENCE > 0 | |||||
for (uint8_t j = 0; j < IS31FL3729_I2C_PERSISTENCE; j++) { | |||||
if (i2c_write_register(addr << 1, i, driver_buffers[index].pwm_buffer + i, 16, IS31FL3729_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break; | |||||
} | |||||
#else | |||||
i2c_write_register(addr << 1, i, driver_buffers[index].pwm_buffer + i, 16, IS31FL3729_I2C_TIMEOUT); | |||||
#endif | |||||
} | |||||
} | |||||
void is31fl3729_init_drivers(void) { | |||||
i2c_init(); | |||||
is31fl3729_init(IS31FL3729_I2C_ADDRESS_1); | |||||
#if defined(IS31FL3729_I2C_ADDRESS_2) | |||||
is31fl3729_init(IS31FL3729_I2C_ADDRESS_2); | |||||
# if defined(IS31FL3729_I2C_ADDRESS_3) | |||||
is31fl3729_init(IS31FL3729_I2C_ADDRESS_3); | |||||
# if defined(IS31FL3729_I2C_ADDRESS_4) | |||||
is31fl3729_init(IS31FL3729_I2C_ADDRESS_4); | |||||
# endif | |||||
# endif | |||||
#endif | |||||
for (int i = 0; i < IS31FL3729_LED_COUNT; i++) { | |||||
is31fl3729_set_scaling_register(i, 0xFF); | |||||
} | |||||
is31fl3729_update_scaling_registers(IS31FL3729_I2C_ADDRESS_1, 0); | |||||
#if defined(IS31FL3729_I2C_ADDRESS_2) | |||||
is31fl3729_update_scaling_registers(IS31FL3729_I2C_ADDRESS_2, 1); | |||||
# if defined(IS31FL3729_I2C_ADDRESS_3) | |||||
is31fl3729_update_scaling_registers(IS31FL3729_I2C_ADDRESS_3, 2); | |||||
# if defined(IS31FL3729_I2C_ADDRESS_4) | |||||
is31fl3729_update_scaling_registers(IS31FL3729_I2C_ADDRESS_4, 3); | |||||
# endif | |||||
# endif | |||||
#endif | |||||
} | |||||
void is31fl3729_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. | |||||
// Set Pull up & Down for SWx CSy | |||||
is31fl3729_write_register(addr, IS31FL3729_REG_PULLDOWNUP, IS31FL3729_PULLDOWNUP); | |||||
// Set Spread Spectrum Register if applicable | |||||
is31fl3729_write_register(addr, IS31FL3729_REG_SPREAD_SPECTRUM, ((IS31FL3729_SPREAD_SPECTRUM & 0b1) << 4) | ((IS31FL3729_SPREAD_SPECTRUM_RANGE & 0b11) << 2) | (IS31FL3729_SPREAD_SPECTRUM_CYCLE_TIME & 0b11)); | |||||
// Set PWM Frequency Register if applicable | |||||
is31fl3729_write_register(addr, IS31FL3729_REG_PWM_FREQUENCY, IS31FL3729_PWM_FREQUENCY); | |||||
// Set Golbal Current Control Register | |||||
is31fl3729_write_register(addr, IS31FL3729_REG_GLOBAL_CURRENT, IS31FL3729_GLOBAL_CURRENT); | |||||
// Set to Normal operation | |||||
is31fl3729_write_register(addr, 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 addr, uint8_t index) { | |||||
if (driver_buffers[index].pwm_buffer_dirty) { | |||||
is31fl3729_write_pwm_buffer(addr, index); | |||||
driver_buffers[index].pwm_buffer_dirty = false; | |||||
} | |||||
} | |||||
void is31fl3729_update_scaling_registers(uint8_t addr, uint8_t index) { | |||||
if (driver_buffers[index].scaling_buffer_dirty) { | |||||
for (uint8_t i = 0; i < IS31FL3729_SCALING_REGISTER_COUNT; i++) { | |||||
is31fl3729_write_register(addr, IS31FL3729_REG_SCALING + i, driver_buffers[index].scaling_buffer[i]); | |||||
} | |||||
driver_buffers[index].scaling_buffer_dirty = false; | |||||
} | |||||
} | |||||
void is31fl3729_flush(void) { | |||||
is31fl3729_update_pwm_buffers(IS31FL3729_I2C_ADDRESS_1, 0); | |||||
#if defined(IS31FL3729_I2C_ADDRESS_2) | |||||
is31fl3729_update_pwm_buffers(IS31FL3729_I2C_ADDRESS_2, 1); | |||||
# if defined(IS31FL3729_I2C_ADDRESS_3) | |||||
is31fl3729_update_pwm_buffers(IS31FL3729_I2C_ADDRESS_3, 2); | |||||
# if defined(IS31FL3729_I2C_ADDRESS_4) | |||||
is31fl3729_update_pwm_buffers(IS31FL3729_I2C_ADDRESS_4, 3); | |||||
# endif | |||||
# endif | |||||
#endif | |||||
} |
@ -0,0 +1,265 @@ | |||||
/* 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_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 addr); | |||||
void is31fl3729_write_register(uint8_t addr, 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 addr, uint8_t index); | |||||
void is31fl3729_update_scaling_registers(uint8_t addr, uint8_t index); | |||||
void is31fl3729_flush(void); | |||||
// Noise reduction using Spread Spectrum register | |||||
#define IS31FL3729_SSP_DISABLE 0b0 | |||||
#define IS31FL3729_SSP_ENABLE 0b1 | |||||
#define IS31FL3729_RNG_5_PERCENT 0b00 | |||||
#define IS31FL3729_RNG_15_PERCENT 0b01 | |||||
#define IS31FL3729_RNG_24_PERCENT 0b10 | |||||
#define IS31FL3729_RNG_34_PERCENT 0b11 | |||||
#define IS31FL3729_CLT_1980_US 0b00 | |||||
#define IS31FL3729_CLT_1200_US 0b01 | |||||
#define IS31FL3729_CLT_820_US 0b10 | |||||
#define IS31FL3729_CLT_660_US 0b11 | |||||
// Noise reduction using PWM Frequency register | |||||
#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 | |||||
// Change SWx Setting using Configuration register | |||||
#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 | |||||
// 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 0x01 to (ISSI_MAX_LEDS - 1) | |||||
#define SW1_CS1 0x01 | |||||
#define SW1_CS2 0x02 | |||||
#define SW1_CS3 0x03 | |||||
#define SW1_CS4 0x04 | |||||
#define SW1_CS5 0x05 | |||||
#define SW1_CS6 0x06 | |||||
#define SW1_CS7 0x07 | |||||
#define SW1_CS8 0x08 | |||||
#define SW1_CS9 0x09 | |||||
#define SW1_CS10 0x0A | |||||
#define SW1_CS11 0x0B | |||||
#define SW1_CS12 0x0C | |||||
#define SW1_CS13 0x0D | |||||
#define SW1_CS14 0x0E | |||||
#define SW1_CS15 0x0F | |||||
#define SW1_CS16 0x10 | |||||
#define SW2_CS1 0x11 | |||||
#define SW2_CS2 0x12 | |||||
#define SW2_CS3 0x13 | |||||
#define SW2_CS4 0x14 | |||||
#define SW2_CS5 0x15 | |||||
#define SW2_CS6 0x16 | |||||
#define SW2_CS7 0x17 | |||||
#define SW2_CS8 0x18 | |||||
#define SW2_CS9 0x19 | |||||
#define SW2_CS10 0x1A | |||||
#define SW2_CS11 0x1B | |||||
#define SW2_CS12 0x1C | |||||
#define SW2_CS13 0x1D | |||||
#define SW2_CS14 0x1E | |||||
#define SW2_CS15 0x1F | |||||
#define SW2_CS16 0x20 | |||||
#define SW3_CS1 0x21 | |||||
#define SW3_CS2 0x22 | |||||
#define SW3_CS3 0x23 | |||||
#define SW3_CS4 0x24 | |||||
#define SW3_CS5 0x25 | |||||
#define SW3_CS6 0x26 | |||||
#define SW3_CS7 0x27 | |||||
#define SW3_CS8 0x28 | |||||
#define SW3_CS9 0x29 | |||||
#define SW3_CS10 0x2A | |||||
#define SW3_CS11 0x2B | |||||
#define SW3_CS12 0x2C | |||||
#define SW3_CS13 0x2D | |||||
#define SW3_CS14 0x2E | |||||
#define SW3_CS15 0x2F | |||||
#define SW3_CS16 0x30 | |||||
#define SW4_CS1 0x31 | |||||
#define SW4_CS2 0x32 | |||||
#define SW4_CS3 0x33 | |||||
#define SW4_CS4 0x34 | |||||
#define SW4_CS5 0x35 | |||||
#define SW4_CS6 0x36 | |||||
#define SW4_CS7 0x37 | |||||
#define SW4_CS8 0x38 | |||||
#define SW4_CS9 0x39 | |||||
#define SW4_CS10 0x3A | |||||
#define SW4_CS11 0x3B | |||||
#define SW4_CS12 0x3C | |||||
#define SW4_CS13 0x3D | |||||
#define SW4_CS14 0x3E | |||||
#define SW4_CS15 0x3F | |||||
#define SW4_CS16 0x40 | |||||
#define SW5_CS1 0x41 | |||||
#define SW5_CS2 0x42 | |||||
#define SW5_CS3 0x43 | |||||
#define SW5_CS4 0x44 | |||||
#define SW5_CS5 0x45 | |||||
#define SW5_CS6 0x46 | |||||
#define SW5_CS7 0x47 | |||||
#define SW5_CS8 0x48 | |||||
#define SW5_CS9 0x49 | |||||
#define SW5_CS10 0x4A | |||||
#define SW5_CS11 0x4B | |||||
#define SW5_CS12 0x4C | |||||
#define SW5_CS13 0x4D | |||||
#define SW5_CS14 0x4E | |||||
#define SW5_CS15 0x4F | |||||
#define SW5_CS16 0x50 | |||||
#define SW6_CS1 0x51 | |||||
#define SW6_CS2 0x52 | |||||
#define SW6_CS3 0x53 | |||||
#define SW6_CS4 0x54 | |||||
#define SW6_CS5 0x55 | |||||
#define SW6_CS6 0x56 | |||||
#define SW6_CS7 0x57 | |||||
#define SW6_CS8 0x58 | |||||
#define SW6_CS9 0x59 | |||||
#define SW6_CS10 0x5A | |||||
#define SW6_CS11 0x5B | |||||
#define SW6_CS12 0x5C | |||||
#define SW6_CS13 0x5D | |||||
#define SW6_CS14 0x5E | |||||
#define SW6_CS15 0x5F | |||||
#define SW6_CS16 0x60 | |||||
#define SW7_CS1 0x61 | |||||
#define SW7_CS2 0x62 | |||||
#define SW7_CS3 0x63 | |||||
#define SW7_CS4 0x64 | |||||
#define SW7_CS5 0x65 | |||||
#define SW7_CS6 0x66 | |||||
#define SW7_CS7 0x67 | |||||
#define SW7_CS8 0x68 | |||||
#define SW7_CS9 0x69 | |||||
#define SW7_CS10 0x6A | |||||
#define SW7_CS11 0x6B | |||||
#define SW7_CS12 0x6C | |||||
#define SW7_CS13 0x6D | |||||
#define SW7_CS14 0x6E | |||||
#define SW7_CS15 0x6F | |||||
#define SW7_CS16 0x70 | |||||
#define SW8_CS1 0x71 | |||||
#define SW8_CS2 0x72 | |||||
#define SW8_CS3 0x73 | |||||
#define SW8_CS4 0x74 | |||||
#define SW8_CS5 0x75 | |||||
#define SW8_CS6 0x76 | |||||
#define SW8_CS7 0x77 | |||||
#define SW8_CS8 0x78 | |||||
#define SW8_CS9 0x79 | |||||
#define SW8_CS10 0x7A | |||||
#define SW8_CS11 0x7B | |||||
#define SW8_CS12 0x7C | |||||
#define SW8_CS13 0x7D | |||||
#define SW8_CS14 0x7E | |||||
#define SW8_CS15 0x7F | |||||
#define SW8_CS16 0x80 | |||||
#define SW9_CS1 0x81 | |||||
#define SW9_CS2 0x82 | |||||
#define SW9_CS3 0x83 | |||||
#define SW9_CS4 0x84 | |||||
#define SW9_CS5 0x85 | |||||
#define SW9_CS6 0x86 | |||||
#define SW9_CS7 0x87 | |||||
#define SW9_CS8 0x88 | |||||
#define SW9_CS9 0x89 | |||||
#define SW9_CS10 0x8A | |||||
#define SW9_CS11 0x8B | |||||
#define SW9_CS12 0x8C | |||||
#define SW9_CS13 0x8D | |||||
#define SW9_CS14 0x8E | |||||
#define SW9_CS15 0x8F |
@ -0,0 +1,226 @@ | |||||
/* 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 "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_PULLDOWNUP | |||||
# define IS31FL3729_PULLDOWNUP 0x33 | |||||
#endif | |||||
#ifndef IS31FL3729_SPREAD_SPECTRUM | |||||
# define IS31FL3729_SPREAD_SPECTRUM IS31FL3729_SSP_DISABLE | |||||
#endif | |||||
#ifndef IS31FL3729_SPREAD_SPECTRUM_RANGE | |||||
# define IS31FL3729_SPREAD_SPECTRUM_RANGE IS31FL3729_RNG_5_PERCENT | |||||
#endif | |||||
#ifndef IS31FL3729_SPREAD_SPECTRUM_CYCLE_TIME | |||||
# define IS31FL3729_SPREAD_SPECTRUM_CYCLE_TIME IS31FL3729_CLT_1980_US | |||||
#endif | |||||
#ifndef IS31FL3729_PWM_FREQUENCY | |||||
# define IS31FL3729_PWM_FREQUENCY IS31FL3729_PWM_FREQUENCY_32K_HZ | |||||
#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 addr, 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(addr << 1, reg, &data, 1, IS31FL3729_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break; | |||||
} | |||||
#else | |||||
i2c_write_register(addr << 1, reg, &data, 1, IS31FL3729_I2C_TIMEOUT); | |||||
#endif | |||||
} | |||||
void is31fl3729_write_pwm_buffer(uint8_t addr, uint8_t index) { | |||||
// 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 <= IS31FL3729_PWM_REGISTER_COUNT; i += 16) { | |||||
#if IS31FL3729_I2C_PERSISTENCE > 0 | |||||
for (uint8_t j = 0; j < IS31FL3729_I2C_PERSISTENCE; j++) { | |||||
if (i2c_write_register(addr << 1, i, driver_buffers[index].pwm_buffer + i, 16, IS31FL3729_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break; | |||||
} | |||||
#else | |||||
i2c_write_register(addr << 1, i, driver_buffers[index].pwm_buffer + i, 16, IS31FL3729_I2C_TIMEOUT); | |||||
#endif | |||||
} | |||||
} | |||||
void is31fl3729_init_drivers(void) { | |||||
i2c_init(); | |||||
is31fl3729_init(IS31FL3729_I2C_ADDRESS_1); | |||||
#if defined(IS31FL3729_I2C_ADDRESS_2) | |||||
is31fl3729_init(IS31FL3729_I2C_ADDRESS_2); | |||||
# if defined(IS31FL3729_I2C_ADDRESS_3) | |||||
is31fl3729_init(IS31FL3729_I2C_ADDRESS_3); | |||||
# if defined(IS31FL3729_I2C_ADDRESS_4) | |||||
is31fl3729_init(IS31FL3729_I2C_ADDRESS_4); | |||||
# endif | |||||
# endif | |||||
#endif | |||||
for (int i = 0; i < IS31FL3729_LED_COUNT; i++) { | |||||
is31fl3729_set_scaling_register(i, 0xFF, 0xFF, 0xFF); | |||||
} | |||||
is31fl3729_update_scaling_registers(IS31FL3729_I2C_ADDRESS_1, 0); | |||||
#if defined(IS31FL3729_I2C_ADDRESS_2) | |||||
is31fl3729_update_scaling_registers(IS31FL3729_I2C_ADDRESS_2, 1); | |||||
# if defined(IS31FL3729_I2C_ADDRESS_3) | |||||
is31fl3729_update_scaling_registers(IS31FL3729_I2C_ADDRESS_3, 2); | |||||
# if defined(IS31FL3729_I2C_ADDRESS_4) | |||||
is31fl3729_update_scaling_registers(IS31FL3729_I2C_ADDRESS_4, 3); | |||||
# endif | |||||
# endif | |||||
#endif | |||||
} | |||||
void is31fl3729_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. | |||||
// Set Pull up & Down for SWx CSy | |||||
is31fl3729_write_register(addr, IS31FL3729_REG_PULLDOWNUP, IS31FL3729_PULLDOWNUP); | |||||
// Set Spread Spectrum Register if applicable | |||||
is31fl3729_write_register(addr, IS31FL3729_REG_SPREAD_SPECTRUM, ((IS31FL3729_SPREAD_SPECTRUM & 0b1) << 4) | ((IS31FL3729_SPREAD_SPECTRUM_RANGE & 0b11) << 2) | (IS31FL3729_SPREAD_SPECTRUM_CYCLE_TIME & 0b11)); | |||||
// Set PWM Frequency Register if applicable | |||||
is31fl3729_write_register(addr, IS31FL3729_REG_PWM_FREQUENCY, IS31FL3729_PWM_FREQUENCY); | |||||
// Set Golbal Current Control Register | |||||
is31fl3729_write_register(addr, IS31FL3729_REG_GLOBAL_CURRENT, IS31FL3729_GLOBAL_CURRENT); | |||||
// Set to Normal operation | |||||
is31fl3729_write_register(addr, 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 addr, uint8_t index) { | |||||
if (driver_buffers[index].pwm_buffer_dirty) { | |||||
is31fl3729_write_pwm_buffer(addr, index); | |||||
driver_buffers[index].pwm_buffer_dirty = false; | |||||
} | |||||
} | |||||
void is31fl3729_update_scaling_registers(uint8_t addr, uint8_t index) { | |||||
if (driver_buffers[index].scaling_buffer_dirty) { | |||||
for (uint8_t i = 0; i < IS31FL3729_SCALING_REGISTER_COUNT; i++) { | |||||
is31fl3729_write_register(addr, IS31FL3729_REG_SCALING + i, driver_buffers[index].scaling_buffer[i]); | |||||
} | |||||
driver_buffers[index].scaling_buffer_dirty = false; | |||||
} | |||||
} | |||||
void is31fl3729_flush(void) { | |||||
is31fl3729_update_pwm_buffers(IS31FL3729_I2C_ADDRESS_1, 0); | |||||
#if defined(IS31FL3729_I2C_ADDRESS_2) | |||||
is31fl3729_update_pwm_buffers(IS31FL3729_I2C_ADDRESS_2, 1); | |||||
# if defined(IS31FL3729_I2C_ADDRESS_3) | |||||
is31fl3729_update_pwm_buffers(IS31FL3729_I2C_ADDRESS_3, 2); | |||||
# if defined(IS31FL3729_I2C_ADDRESS_4) | |||||
is31fl3729_update_pwm_buffers(IS31FL3729_I2C_ADDRESS_4, 3); | |||||
# endif | |||||
# endif | |||||
#endif | |||||
} |
@ -0,0 +1,267 @@ | |||||
/* 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_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 addr); | |||||
void is31fl3729_write_register(uint8_t addr, 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 addr, uint8_t index); | |||||
void is31fl3729_update_scaling_registers(uint8_t addr, uint8_t index); | |||||
void is31fl3729_flush(void); | |||||
// Noise reduction using Spread Spectrum register | |||||
#define IS31FL3729_SSP_DISABLE 0b0 | |||||
#define IS31FL3729_SSP_ENABLE 0b1 | |||||
#define IS31FL3729_RNG_5_PERCENT 0b00 | |||||
#define IS31FL3729_RNG_15_PERCENT 0b01 | |||||
#define IS31FL3729_RNG_24_PERCENT 0b10 | |||||
#define IS31FL3729_RNG_34_PERCENT 0b11 | |||||
#define IS31FL3729_CLT_1980_US 0b00 | |||||
#define IS31FL3729_CLT_1200_US 0b01 | |||||
#define IS31FL3729_CLT_820_US 0b10 | |||||
#define IS31FL3729_CLT_660_US 0b11 | |||||
// Noise reduction using PWM Frequency register | |||||
#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 | |||||
// Change SWx Setting using Configuration register | |||||
#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 | |||||
// 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 0x01 to (ISSI_MAX_LEDS - 1) | |||||
#define SW1_CS1 0x01 | |||||
#define SW1_CS2 0x02 | |||||
#define SW1_CS3 0x03 | |||||
#define SW1_CS4 0x04 | |||||
#define SW1_CS5 0x05 | |||||
#define SW1_CS6 0x06 | |||||
#define SW1_CS7 0x07 | |||||
#define SW1_CS8 0x08 | |||||
#define SW1_CS9 0x09 | |||||
#define SW1_CS10 0x0A | |||||
#define SW1_CS11 0x0B | |||||
#define SW1_CS12 0x0C | |||||
#define SW1_CS13 0x0D | |||||
#define SW1_CS14 0x0E | |||||
#define SW1_CS15 0x0F | |||||
#define SW1_CS16 0x10 | |||||
#define SW2_CS1 0x11 | |||||
#define SW2_CS2 0x12 | |||||
#define SW2_CS3 0x13 | |||||
#define SW2_CS4 0x14 | |||||
#define SW2_CS5 0x15 | |||||
#define SW2_CS6 0x16 | |||||
#define SW2_CS7 0x17 | |||||
#define SW2_CS8 0x18 | |||||
#define SW2_CS9 0x19 | |||||
#define SW2_CS10 0x1A | |||||
#define SW2_CS11 0x1B | |||||
#define SW2_CS12 0x1C | |||||
#define SW2_CS13 0x1D | |||||
#define SW2_CS14 0x1E | |||||
#define SW2_CS15 0x1F | |||||
#define SW2_CS16 0x20 | |||||
#define SW3_CS1 0x21 | |||||
#define SW3_CS2 0x22 | |||||
#define SW3_CS3 0x23 | |||||
#define SW3_CS4 0x24 | |||||
#define SW3_CS5 0x25 | |||||
#define SW3_CS6 0x26 | |||||
#define SW3_CS7 0x27 | |||||
#define SW3_CS8 0x28 | |||||
#define SW3_CS9 0x29 | |||||
#define SW3_CS10 0x2A | |||||
#define SW3_CS11 0x2B | |||||
#define SW3_CS12 0x2C | |||||
#define SW3_CS13 0x2D | |||||
#define SW3_CS14 0x2E | |||||
#define SW3_CS15 0x2F | |||||
#define SW3_CS16 0x30 | |||||
#define SW4_CS1 0x31 | |||||
#define SW4_CS2 0x32 | |||||
#define SW4_CS3 0x33 | |||||
#define SW4_CS4 0x34 | |||||
#define SW4_CS5 0x35 | |||||
#define SW4_CS6 0x36 | |||||
#define SW4_CS7 0x37 | |||||
#define SW4_CS8 0x38 | |||||
#define SW4_CS9 0x39 | |||||
#define SW4_CS10 0x3A | |||||
#define SW4_CS11 0x3B | |||||
#define SW4_CS12 0x3C | |||||
#define SW4_CS13 0x3D | |||||
#define SW4_CS14 0x3E | |||||
#define SW4_CS15 0x3F | |||||
#define SW4_CS16 0x40 | |||||
#define SW5_CS1 0x41 | |||||
#define SW5_CS2 0x42 | |||||
#define SW5_CS3 0x43 | |||||
#define SW5_CS4 0x44 | |||||
#define SW5_CS5 0x45 | |||||
#define SW5_CS6 0x46 | |||||
#define SW5_CS7 0x47 | |||||
#define SW5_CS8 0x48 | |||||
#define SW5_CS9 0x49 | |||||
#define SW5_CS10 0x4A | |||||
#define SW5_CS11 0x4B | |||||
#define SW5_CS12 0x4C | |||||
#define SW5_CS13 0x4D | |||||
#define SW5_CS14 0x4E | |||||
#define SW5_CS15 0x4F | |||||
#define SW5_CS16 0x50 | |||||
#define SW6_CS1 0x51 | |||||
#define SW6_CS2 0x52 | |||||
#define SW6_CS3 0x53 | |||||
#define SW6_CS4 0x54 | |||||
#define SW6_CS5 0x55 | |||||
#define SW6_CS6 0x56 | |||||
#define SW6_CS7 0x57 | |||||
#define SW6_CS8 0x58 | |||||
#define SW6_CS9 0x59 | |||||
#define SW6_CS10 0x5A | |||||
#define SW6_CS11 0x5B | |||||
#define SW6_CS12 0x5C | |||||
#define SW6_CS13 0x5D | |||||
#define SW6_CS14 0x5E | |||||
#define SW6_CS15 0x5F | |||||
#define SW6_CS16 0x60 | |||||
#define SW7_CS1 0x61 | |||||
#define SW7_CS2 0x62 | |||||
#define SW7_CS3 0x63 | |||||
#define SW7_CS4 0x64 | |||||
#define SW7_CS5 0x65 | |||||
#define SW7_CS6 0x66 | |||||
#define SW7_CS7 0x67 | |||||
#define SW7_CS8 0x68 | |||||
#define SW7_CS9 0x69 | |||||
#define SW7_CS10 0x6A | |||||
#define SW7_CS11 0x6B | |||||
#define SW7_CS12 0x6C | |||||
#define SW7_CS13 0x6D | |||||
#define SW7_CS14 0x6E | |||||
#define SW7_CS15 0x6F | |||||
#define SW7_CS16 0x70 | |||||
#define SW8_CS1 0x71 | |||||
#define SW8_CS2 0x72 | |||||
#define SW8_CS3 0x73 | |||||
#define SW8_CS4 0x74 | |||||
#define SW8_CS5 0x75 | |||||
#define SW8_CS6 0x76 | |||||
#define SW8_CS7 0x77 | |||||
#define SW8_CS8 0x78 | |||||
#define SW8_CS9 0x79 | |||||
#define SW8_CS10 0x7A | |||||
#define SW8_CS11 0x7B | |||||
#define SW8_CS12 0x7C | |||||
#define SW8_CS13 0x7D | |||||
#define SW8_CS14 0x7E | |||||
#define SW8_CS15 0x7F | |||||
#define SW8_CS16 0x80 | |||||
#define SW9_CS1 0x81 | |||||
#define SW9_CS2 0x82 | |||||
#define SW9_CS3 0x83 | |||||
#define SW9_CS4 0x84 | |||||
#define SW9_CS5 0x85 | |||||
#define SW9_CS6 0x86 | |||||
#define SW9_CS7 0x87 | |||||
#define SW9_CS8 0x88 | |||||
#define SW9_CS9 0x89 | |||||
#define SW9_CS10 0x8A | |||||
#define SW9_CS11 0x8B | |||||
#define SW9_CS12 0x8C | |||||
#define SW9_CS13 0x8D | |||||
#define SW9_CS14 0x8E | |||||
#define SW9_CS15 0x8F |