diff options
Diffstat (limited to 'drivers')
26 files changed, 3035 insertions, 540 deletions
diff --git a/drivers/bluetooth/adafruit_ble.cpp b/drivers/bluetooth/bluefruit_le.cpp index 34a780e9a5..86581a1a48 100644 --- a/drivers/bluetooth/adafruit_ble.cpp +++ b/drivers/bluetooth/bluefruit_le.cpp @@ -1,4 +1,4 @@ -#include "adafruit_ble.h" +#include "bluefruit_le.h" #include <stdio.h> #include <stdlib.h> @@ -16,20 +16,20 @@ // These are the pin assignments for the 32u4 boards. // You may define them to something else in your config.h // if yours is wired up differently. -#ifndef ADAFRUIT_BLE_RST_PIN -# define ADAFRUIT_BLE_RST_PIN D4 +#ifndef BLUEFRUIT_LE_RST_PIN +# define BLUEFRUIT_LE_RST_PIN D4 #endif -#ifndef ADAFRUIT_BLE_CS_PIN -# define ADAFRUIT_BLE_CS_PIN B4 +#ifndef BLUEFRUIT_LE_CS_PIN +# define BLUEFRUIT_LE_CS_PIN B4 #endif -#ifndef ADAFRUIT_BLE_IRQ_PIN -# define ADAFRUIT_BLE_IRQ_PIN E6 +#ifndef BLUEFRUIT_LE_IRQ_PIN +# define BLUEFRUIT_LE_IRQ_PIN E6 #endif -#ifndef ADAFRUIT_BLE_SCK_DIVISOR -# define ADAFRUIT_BLE_SCK_DIVISOR 2 // 4MHz SCK/8MHz CPU, calculated for Feather 32U4 BLE +#ifndef BLUEFRUIT_LE_SCK_DIVISOR +# define BLUEFRUIT_LE_SCK_DIVISOR 2 // 4MHz SCK/8MHz CPU, calculated for Feather 32U4 BLE #endif #define SAMPLE_BATTERY @@ -143,7 +143,7 @@ static bool at_command_P(const char *cmd, char *resp, uint16_t resplen, bool ver // Send a single SDEP packet static bool sdep_send_pkt(const struct sdep_msg *msg, uint16_t timeout) { - spi_start(ADAFRUIT_BLE_CS_PIN, false, 0, ADAFRUIT_BLE_SCK_DIVISOR); + spi_start(BLUEFRUIT_LE_CS_PIN, false, 0, BLUEFRUIT_LE_SCK_DIVISOR); uint16_t timerStart = timer_read(); bool success = false; bool ready = false; @@ -157,7 +157,7 @@ static bool sdep_send_pkt(const struct sdep_msg *msg, uint16_t timeout) { // Release it and let it initialize spi_stop(); wait_us(SdepBackOff); - spi_start(ADAFRUIT_BLE_CS_PIN, false, 0, ADAFRUIT_BLE_SCK_DIVISOR); + spi_start(BLUEFRUIT_LE_CS_PIN, false, 0, BLUEFRUIT_LE_SCK_DIVISOR); } while (timer_elapsed(timerStart) < timeout); if (ready) { @@ -190,7 +190,7 @@ static bool sdep_recv_pkt(struct sdep_msg *msg, uint16_t timeout) { bool ready = false; do { - ready = readPin(ADAFRUIT_BLE_IRQ_PIN); + ready = readPin(BLUEFRUIT_LE_IRQ_PIN); if (ready) { break; } @@ -198,7 +198,7 @@ static bool sdep_recv_pkt(struct sdep_msg *msg, uint16_t timeout) { } while (timer_elapsed(timerStart) < timeout); if (ready) { - spi_start(ADAFRUIT_BLE_CS_PIN, false, 0, ADAFRUIT_BLE_SCK_DIVISOR); + spi_start(BLUEFRUIT_LE_CS_PIN, false, 0, BLUEFRUIT_LE_SCK_DIVISOR); do { // Read the command type, waiting for the data to be ready @@ -207,7 +207,7 @@ static bool sdep_recv_pkt(struct sdep_msg *msg, uint16_t timeout) { // Release it and let it initialize spi_stop(); wait_us(SdepBackOff); - spi_start(ADAFRUIT_BLE_CS_PIN, false, 0, ADAFRUIT_BLE_SCK_DIVISOR); + spi_start(BLUEFRUIT_LE_CS_PIN, false, 0, BLUEFRUIT_LE_SCK_DIVISOR); continue; } @@ -233,7 +233,7 @@ static void resp_buf_read_one(bool greedy) { return; } - if (readPin(ADAFRUIT_BLE_IRQ_PIN)) { + if (readPin(BLUEFRUIT_LE_IRQ_PIN)) { struct sdep_msg msg; again: @@ -244,7 +244,7 @@ static void resp_buf_read_one(bool greedy) { dprintf("recv latency %dms\n", TIMER_DIFF_16(timer_read(), last_send)); } - if (greedy && resp_buf.peek(last_send) && readPin(ADAFRUIT_BLE_IRQ_PIN)) { + if (greedy && resp_buf.peek(last_send) && readPin(BLUEFRUIT_LE_IRQ_PIN)) { goto again; } } @@ -295,16 +295,16 @@ static bool ble_init(void) { state.configured = false; state.is_connected = false; - setPinInput(ADAFRUIT_BLE_IRQ_PIN); + setPinInput(BLUEFRUIT_LE_IRQ_PIN); spi_init(); // Perform a hardware reset - setPinOutput(ADAFRUIT_BLE_RST_PIN); - writePinHigh(ADAFRUIT_BLE_RST_PIN); - writePinLow(ADAFRUIT_BLE_RST_PIN); + setPinOutput(BLUEFRUIT_LE_RST_PIN); + writePinHigh(BLUEFRUIT_LE_RST_PIN); + writePinLow(BLUEFRUIT_LE_RST_PIN); wait_ms(10); - writePinHigh(ADAFRUIT_BLE_RST_PIN); + writePinHigh(BLUEFRUIT_LE_RST_PIN); wait_ms(1000); // Give it a second to initialize @@ -424,9 +424,9 @@ bool at_command_P(const char *cmd, char *resp, uint16_t resplen, bool verbose) { return at_command(cmdbuf, resp, resplen, verbose); } -bool adafruit_ble_is_connected(void) { return state.is_connected; } +bool bluefruit_le_is_connected(void) { return state.is_connected; } -bool adafruit_ble_enable_keyboard(void) { +bool bluefruit_le_enable_keyboard(void) { char resbuf[128]; if (!state.initialized && !ble_init()) { @@ -498,16 +498,16 @@ static void set_connected(bool connected) { } } -void adafruit_ble_task(void) { +void bluefruit_le_task(void) { char resbuf[48]; - if (!state.configured && !adafruit_ble_enable_keyboard()) { + if (!state.configured && !bluefruit_le_enable_keyboard()) { return; } resp_buf_read_one(true); send_buf_send_one(SdepShortTimeout); - if (resp_buf.empty() && (state.event_flags & UsingEvents) && readPin(ADAFRUIT_BLE_IRQ_PIN)) { + if (resp_buf.empty() && (state.event_flags & UsingEvents) && readPin(BLUEFRUIT_LE_IRQ_PIN)) { // Must be an event update if (at_command_P(PSTR("AT+EVENTSTATUS"), resbuf, sizeof(resbuf))) { uint32_t mask = strtoul(resbuf, NULL, 16); @@ -609,7 +609,7 @@ static bool process_queue_item(struct queue_item *item, uint16_t timeout) { } } -void adafruit_ble_send_keys(uint8_t hid_modifier_mask, uint8_t *keys, uint8_t nkeys) { +void bluefruit_le_send_keys(uint8_t hid_modifier_mask, uint8_t *keys, uint8_t nkeys) { struct queue_item item; bool didWait = false; @@ -643,7 +643,7 @@ void adafruit_ble_send_keys(uint8_t hid_modifier_mask, uint8_t *keys, uint8_t nk } } -void adafruit_ble_send_consumer_key(uint16_t usage) { +void bluefruit_le_send_consumer_key(uint16_t usage) { struct queue_item item; item.queue_type = QTConsumer; @@ -655,7 +655,7 @@ void adafruit_ble_send_consumer_key(uint16_t usage) { } #ifdef MOUSE_ENABLE -void adafruit_ble_send_mouse_move(int8_t x, int8_t y, int8_t scroll, int8_t pan, uint8_t buttons) { +void bluefruit_le_send_mouse_move(int8_t x, int8_t y, int8_t scroll, int8_t pan, uint8_t buttons) { struct queue_item item; item.queue_type = QTMouseMove; @@ -671,9 +671,9 @@ void adafruit_ble_send_mouse_move(int8_t x, int8_t y, int8_t scroll, int8_t pan, } #endif -uint32_t adafruit_ble_read_battery_voltage(void) { return state.vbat; } +uint32_t bluefruit_le_read_battery_voltage(void) { return state.vbat; } -bool adafruit_ble_set_mode_leds(bool on) { +bool bluefruit_le_set_mode_leds(bool on) { if (!state.configured) { return false; } @@ -689,7 +689,7 @@ bool adafruit_ble_set_mode_leds(bool on) { } // https://learn.adafruit.com/adafruit-feather-32u4-bluefruit-le/ble-generic#at-plus-blepowerlevel -bool adafruit_ble_set_power_level(int8_t level) { +bool bluefruit_le_set_power_level(int8_t level) { char cmd[46]; if (!state.configured) { return false; diff --git a/drivers/bluetooth/adafruit_ble.h b/drivers/bluetooth/bluefruit_le.h index b43e0771d9..de301c6167 100644 --- a/drivers/bluetooth/adafruit_ble.h +++ b/drivers/bluetooth/bluefruit_le.h @@ -16,43 +16,43 @@ extern "C" { #endif /* Instruct the module to enable HID keyboard support and reset */ -extern bool adafruit_ble_enable_keyboard(void); +extern bool bluefruit_le_enable_keyboard(void); /* Query to see if the BLE module is connected */ -extern bool adafruit_ble_query_is_connected(void); +extern bool bluefruit_le_query_is_connected(void); /* Returns true if we believe that the BLE module is connected. * This uses our cached understanding that is maintained by * calling ble_task() periodically. */ -extern bool adafruit_ble_is_connected(void); +extern bool bluefruit_le_is_connected(void); /* Call this periodically to process BLE-originated things */ -extern void adafruit_ble_task(void); +extern void bluefruit_le_task(void); /* Generates keypress events for a set of keys. * The hid modifier mask specifies the state of the modifier keys for * this set of keys. * Also sends a key release indicator, so that the keys do not remain * held down. */ -extern void adafruit_ble_send_keys(uint8_t hid_modifier_mask, uint8_t *keys, uint8_t nkeys); +extern void bluefruit_le_send_keys(uint8_t hid_modifier_mask, uint8_t *keys, uint8_t nkeys); /* Send a consumer usage. * (milliseconds) */ -extern void adafruit_ble_send_consumer_key(uint16_t usage); +extern void bluefruit_le_send_consumer_key(uint16_t usage); #ifdef MOUSE_ENABLE /* Send a mouse/wheel movement report. * The parameters are signed and indicate positive or negative direction * change. */ -extern void adafruit_ble_send_mouse_move(int8_t x, int8_t y, int8_t scroll, int8_t pan, uint8_t buttons); +extern void bluefruit_le_send_mouse_move(int8_t x, int8_t y, int8_t scroll, int8_t pan, uint8_t buttons); #endif /* Compute battery voltage by reading an analog pin. * Returns the integer number of millivolts */ -extern uint32_t adafruit_ble_read_battery_voltage(void); +extern uint32_t bluefruit_le_read_battery_voltage(void); -extern bool adafruit_ble_set_mode_leds(bool on); -extern bool adafruit_ble_set_power_level(int8_t level); +extern bool bluefruit_le_set_mode_leds(bool on); +extern bool bluefruit_le_set_power_level(int8_t level); #ifdef __cplusplus } diff --git a/drivers/bluetooth/outputselect.c b/drivers/bluetooth/outputselect.c index f758c65280..44bc4a9aa3 100644 --- a/drivers/bluetooth/outputselect.c +++ b/drivers/bluetooth/outputselect.c @@ -13,13 +13,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include "outputselect.h" +#include "usb_util.h" -#if defined(PROTOCOL_LUFA) -# include "lufa.h" -#endif - -#ifdef MODULE_ADAFRUIT_BLE -# include "adafruit_ble.h" +#ifdef BLUETOOTH_BLUEFRUIT_LE +# include "bluefruit_le.h" #endif uint8_t desired_output = OUTPUT_DEFAULT; @@ -39,23 +36,17 @@ void set_output(uint8_t output) { */ __attribute__((weak)) void set_output_user(uint8_t output) {} -static bool is_usb_configured(void) { -#if defined(PROTOCOL_LUFA) - return USB_DeviceState == DEVICE_STATE_Configured; -#endif -} - /** \brief Auto Detect Output * * FIXME: Needs doc */ uint8_t auto_detect_output(void) { - if (is_usb_configured()) { + if (usb_connected_state()) { return OUTPUT_USB; } -#ifdef MODULE_ADAFRUIT_BLE - if (adafruit_ble_is_connected()) { +#ifdef BLUETOOTH_BLUEFRUIT_LE + if (bluefruit_le_is_connected()) { return OUTPUT_BLUETOOTH; } #endif diff --git a/drivers/bluetooth/rn42.c b/drivers/bluetooth/rn42.c new file mode 100644 index 0000000000..2ef40bb7e0 --- /dev/null +++ b/drivers/bluetooth/rn42.c @@ -0,0 +1,99 @@ +/* Copyright 2021 + * + * 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 "report.h" +#include "uart.h" + +#ifndef RN42_BAUD_RATE +# define RN42_BAUD_RATE 115200 +#endif + +// https://cdn.sparkfun.com/datasheets/Wireless/Bluetooth/bluetooth_cr_UG-v1.0r.pdf#G7.663734 +static inline uint16_t rn42_consumer_usage_to_bitmap(uint16_t usage) { + switch (usage) { + case AC_HOME: + return 0x0001; + case AL_EMAIL: + return 0x0002; + case AC_SEARCH: + return 0x0004; + case AL_KEYBOARD_LAYOUT: + return 0x0008; + case AUDIO_VOL_UP: + return 0x0010; + case AUDIO_VOL_DOWN: + return 0x0020; + case AUDIO_MUTE: + return 0x0040; + case TRANSPORT_PLAY_PAUSE: + return 0x0080; + case TRANSPORT_NEXT_TRACK: + return 0x0100; + case TRANSPORT_PREV_TRACK: + return 0x0200; + case TRANSPORT_STOP: + return 0x0400; + case TRANSPORT_EJECT: + return 0x0800; + case TRANSPORT_FAST_FORWARD: + return 0x1000; + case TRANSPORT_REWIND: + return 0x2000; + case TRANSPORT_STOP_EJECT: + return 0x4000; + case AL_LOCAL_BROWSER: + return 0x8000; + default: + return 0; + } +} + +void rn42_init(void) { uart_init(RN42_BAUD_RATE); } + +void rn42_send_keyboard(report_keyboard_t *report) { + uart_write(0xFD); + uart_write(0x09); + uart_write(0x01); + uart_write(report->mods); + uart_write(0x00); + for (uint8_t i = 0; i < KEYBOARD_REPORT_KEYS; i++) { + uart_write(report->keys[i]); + } +} + +void rn42_send_mouse(report_mouse_t *report) { + uart_write(0xFD); + uart_write(0x00); + uart_write(0x03); + uart_write(report->buttons); + uart_write(report->x); + uart_write(report->y); + uart_write(report->v); // should try sending the wheel v here + uart_write(report->h); // should try sending the wheel h here + uart_write(0x00); +} + +void rn42_send_consumer(uint16_t data) { + static uint16_t last_data = 0; + if (data == last_data) return; + last_data = data; + uint16_t bitmap = rn42_consumer_usage_to_bitmap(data); + uart_write(0xFD); + uart_write(0x03); + uart_write(0x03); + uart_write(bitmap & 0xFF); + uart_write((bitmap >> 8) & 0xFF); +} diff --git a/drivers/bluetooth/rn42.h b/drivers/bluetooth/rn42.h new file mode 100644 index 0000000000..4747759111 --- /dev/null +++ b/drivers/bluetooth/rn42.h @@ -0,0 +1,25 @@ +/* Copyright 2021 + * + * 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 "report.h" + +void rn42_init(void); + +void rn42_send_keyboard(report_keyboard_t *report); + +void rn42_send_mouse(report_mouse_t *report); + +void rn42_send_consumer(uint16_t data); diff --git a/drivers/gpio/sn74x138.c b/drivers/gpio/sn74x138.c new file mode 100644 index 0000000000..222e5db56c --- /dev/null +++ b/drivers/gpio/sn74x138.c @@ -0,0 +1,65 @@ +/* Copyright 2022 + * + * 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 "sn74x138.h" +#include "gpio.h" + +#define ADDRESS_PIN_COUNT 3 + +#ifndef SN74X138_ADDRESS_PINS +# error sn74x138: no address pins defined! +#endif + +static const pin_t address_pins[ADDRESS_PIN_COUNT] = SN74X138_ADDRESS_PINS; + +void sn74x138_init(void) { + for (int i = 0; i < ADDRESS_PIN_COUNT; i++) { + setPinOutput(address_pins[i]); + writePinLow(address_pins[i]); + } + +#if defined(SN74X138_E1_PIN) + setPinOutput(SN74X138_E1_PIN); + writePinHigh(SN74X138_E1_PIN); +#endif + +#if defined(SN74X138_E2_PIN) + setPinOutput(SN74X138_E2_PIN); + writePinHigh(SN74X138_E2_PIN); +#endif +#if defined(SN74X138_E3_PIN) + setPinOutput(SN74X138_E3_PIN); + writePinLow(SN74X138_E3_PIN); +#endif +} + +void sn74x138_set_enabled(bool enabled) { +#if defined(SN74X138_E1_PIN) + writePin(SN74X138_E1_PIN, !enabled); +#endif +#if defined(SN74X138_E2_PIN) + writePin(SN74X138_E2_PIN, !enabled); +#endif +#if defined(SN74X138_E3_PIN) + writePin(SN74X138_E3_PIN, enabled); +#endif +} + +void sn74x138_set_addr(uint8_t address) { + for (int i = 0; i < ADDRESS_PIN_COUNT; i++) { + writePin(address_pins[i], address & (1 << i)); + } +} diff --git a/drivers/gpio/sn74x138.h b/drivers/gpio/sn74x138.h new file mode 100644 index 0000000000..6f1f20e618 --- /dev/null +++ b/drivers/gpio/sn74x138.h @@ -0,0 +1,48 @@ +/* Copyright 2022 + * + * 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> + +/** + * Driver for 74x138 3-to-8 decoder/demultiplexer with inverting outputs + * https://assets.nexperia.com/documents/data-sheet/74HC_HCT138.pdf + */ + +/** + * Initialize the address and output enable pins. + */ +void sn74x138_init(void); + +/** + * Set the enabled state. + * + * When enabled is true, pulls the E1 and E2 pins low, and the E3 pin high. + * + * \param enabled The enable state to set. + */ +void sn74x138_set_enabled(bool enabled); + +/** + * Set the output pin address. + * + * The selected output pin will be pulled low, while the remaining output pins will be high. + * + * \param address The address to set, from 0 to 7. + */ +void sn74x138_set_addr(uint8_t address); diff --git a/drivers/led/ckled2001.c b/drivers/led/ckled2001.c index 990e50cb60..8d71805a24 100644 --- a/drivers/led/ckled2001.c +++ b/drivers/led/ckled2001.c @@ -125,7 +125,16 @@ void CKLED2001_init(uint8_t addr) { // Set CURRENT PAGE (Page 4) CKLED2001_write_register(addr, CONFIGURE_CMD_PAGE, CURRENT_TUNE_PAGE); for (int i = 0; i < LED_CURRENT_TUNE_LENGTH; i++) { - CKLED2001_write_register(addr, i, 0xFF); + switch (i) { + case 2: + case 5: + case 8: + case 11: + CKLED2001_write_register(addr, i, 0xA0); + break; + default: + CKLED2001_write_register(addr, i, 0xFF); + } } // Enable LEDs ON/OFF diff --git a/drivers/led/issi/is31fl3733-simple.c b/drivers/led/issi/is31fl3733-simple.c new file mode 100644 index 0000000000..777895bf89 --- /dev/null +++ b/drivers/led/issi/is31fl3733-simple.c @@ -0,0 +1,248 @@ +/* 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 "i2c_master.h" +#include "wait.h" + +// 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: 0b101(ADDR2)(ADDR1) +#define ISSI_ADDR_DEFAULT 0x50 + +#define ISSI_COMMANDREGISTER 0xFD +#define ISSI_COMMANDREGISTER_WRITELOCK 0xFE +#define ISSI_INTERRUPTMASKREGISTER 0xF0 +#define ISSI_INTERRUPTSTATUSREGISTER 0xF1 + +#define ISSI_PAGE_LEDCONTROL 0x00 // PG0 +#define ISSI_PAGE_PWM 0x01 // PG1 +#define ISSI_PAGE_AUTOBREATH 0x02 // PG2 +#define ISSI_PAGE_FUNCTION 0x03 // PG3 + +#define ISSI_REG_CONFIGURATION 0x00 // PG3 +#define ISSI_REG_GLOBALCURRENT 0x01 // PG3 +#define ISSI_REG_RESET 0x11 // PG3 +#define ISSI_REG_SWPULLUP 0x0F // PG3 +#define ISSI_REG_CSPULLUP 0x10 // PG3 + +#ifndef ISSI_TIMEOUT +# define ISSI_TIMEOUT 100 +#endif + +#ifndef ISSI_PERSISTENCE +# define ISSI_PERSISTENCE 0 +#endif + +#ifndef ISSI_PWM_FREQUENCY +# define ISSI_PWM_FREQUENCY 0b000 // PFS - IS31FL3733B only +#endif + +#ifndef ISSI_SWPULLUP +# define ISSI_SWPULLUP PUR_0R +#endif + +#ifndef ISSI_CSPULLUP +# define ISSI_CSPULLUP PUR_0R +#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[LED_DRIVER_COUNT][192]; +bool g_pwm_buffer_update_required[LED_DRIVER_COUNT] = {false}; + +/* There's probably a better way to init this... */ +#if LED_DRIVER_COUNT == 1 +uint8_t g_led_control_registers[LED_DRIVER_COUNT][24] = {{0}}; +#elif LED_DRIVER_COUNT == 2 +uint8_t g_led_control_registers[LED_DRIVER_COUNT][24] = {{0}, {0}}; +#elif LED_DRIVER_COUNT == 3 +uint8_t g_led_control_registers[LED_DRIVER_COUNT][24] = {{0}, {0}, {0}}; +#elif LED_DRIVER_COUNT == 4 +uint8_t g_led_control_registers[LED_DRIVER_COUNT][24] = {{0}, {0}, {0}, {0}}; +#endif +bool g_led_control_registers_update_required[LED_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 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) { + return false; + } + } +#else + if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, ISSI_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 < 192; 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 ISSI_PERSISTENCE > 0 + for (uint8_t i = 0; i < ISSI_PERSISTENCE; i++) { + if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 17, ISSI_TIMEOUT) != 0) { + return false; + } + } +#else + if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 17, ISSI_TIMEOUT) != 0) { + return false; + } +#endif + } + return true; +} + +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, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5); + + // Select PG0 + IS31FL3733_write_register(addr, ISSI_COMMANDREGISTER, ISSI_PAGE_LEDCONTROL); + // Turn off all LEDs. + for (int i = 0x00; i <= 0x17; i++) { + IS31FL3733_write_register(addr, i, 0x00); + } + + // Unlock the command register. + IS31FL3733_write_register(addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5); + + // Select PG1 + IS31FL3733_write_register(addr, ISSI_COMMANDREGISTER, ISSI_PAGE_PWM); + // Set PWM on all LEDs to 0 + // No need to setup Breath registers to PWM as that is the default. + for (int i = 0x00; i <= 0xBF; i++) { + IS31FL3733_write_register(addr, i, 0x00); + } + + // Unlock the command register. + IS31FL3733_write_register(addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5); + + // Select PG3 + IS31FL3733_write_register(addr, ISSI_COMMANDREGISTER, ISSI_PAGE_FUNCTION); + // Set de-ghost pull-up resistors (SWx) + IS31FL3733_write_register(addr, ISSI_REG_SWPULLUP, ISSI_SWPULLUP); + // Set de-ghost pull-down resistors (CSx) + IS31FL3733_write_register(addr, ISSI_REG_CSPULLUP, ISSI_CSPULLUP); + // Set global current to maximum. + IS31FL3733_write_register(addr, ISSI_REG_GLOBALCURRENT, 0xFF); + // Disable software shutdown. + IS31FL3733_write_register(addr, ISSI_REG_CONFIGURATION, ((sync & 0b11) << 6) | ((ISSI_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) { + if (index >= 0 && index < DRIVER_LED_TOTAL) { + is31_led led = g_is31_leds[index]; + + 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 < DRIVER_LED_TOTAL; i++) { + IS31FL3733_set_value(i, value); + } +} + +void IS31FL3733_set_led_control_register(uint8_t index, bool value) { + is31_led led = g_is31_leds[index]; + + 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, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5); + IS31FL3733_write_register(addr, ISSI_COMMANDREGISTER, ISSI_PAGE_PWM); + + // If any of the transactions fail we risk writing dirty PG0, + // refresh page 0 just in case. + if (!IS31FL3733_write_pwm_buffer( |