Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update AW20216S driver and configuration for GMMK/Pro keyboard #13173

Closed
wants to merge 13 commits into from
Closed
9 changes: 8 additions & 1 deletion common_features.mk
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,7 @@ endif
endif

RGB_MATRIX_ENABLE ?= no
VALID_RGB_MATRIX_TYPES := AW20216 IS31FL3731 IS31FL3733 IS31FL3737 IS31FL3741 WS2812 custom
VALID_RGB_MATRIX_TYPES := AW20216 IS31FL3731 IS31FL3733 IS31FL3737 IS31FL3741 WS2812 AW20216S custom
drashna marked this conversation as resolved.
Show resolved Hide resolved

ifeq ($(strip $(RGB_MATRIX_ENABLE)), yes)
ifeq ($(filter $(RGB_MATRIX_DRIVER),$(VALID_RGB_MATRIX_TYPES)),)
Expand Down Expand Up @@ -308,6 +308,13 @@ endif
WS2812_DRIVER_REQUIRED := yes
endif

ifeq ($(strip $(RGB_MATRIX_DRIVER)), AW20216S)
OPT_DEFS += -DAW20216S -DSTM32_SPI -DHAL_USE_SPI=TRUE
COMMON_VPATH += $(DRIVER_PATH)/awinic
SRC += aw20216s.c
QUANTUM_LIB_SRC += spi_master.c
endif
glorious-qmk marked this conversation as resolved.
Show resolved Hide resolved

ifeq ($(strip $(RGB_MATRIX_DRIVER)), APA102)
OPT_DEFS += -DAPA102
APA102_DRIVER_REQUIRED := yes
Expand Down
138 changes: 138 additions & 0 deletions drivers/awinic/aw20216s.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
/* Copyright 2017 Jason Williams
glorious-qmk marked this conversation as resolved.
Show resolved Hide resolved
* Copyright 2018 Jack Humbert
*
* 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 "quantum.h"
#include "spi_master.h"
#include "aw20216s.h"
#include <ch.h>
#include <hal.h>

#define COMMAND_CHIP_ID 0xA0
#define US_POR_DELAY 5000 // Min is 2000 us
#define US_ENABLE_DELAY 500 // Min is 100 us
#define PAGE_0 0
#define PAGE_1 1
#define PAGE_2 2
#define PAGE_3 3
#define PAGE_0_REG_GCR 0x00
#define REG_GCR_BIT_CHIPEN_MASK 0x01
#define PAGE_0_REG_GCCR 0x01
#define US_BETWEEN_REG_WRITE_DELAY 100
#define PWM_REGISTER_COUNT 216

#ifndef SPI_MODE
# define SPI_MODE 0
#endif
#ifndef SPI_DIVISOR
# define SPI_DIVISOR 4
#endif

// These buffers match the AW20216S PWM registers 0x00-0xD7.
uint8_t g_pwm_buffer[DRIVER_COUNT][PWM_REGISTER_COUNT];
bool g_pwm_buffer_update_required[DRIVER_COUNT] = {false};

void AW20216S_spi_start(int32_t csPin) { spi_start(csPin, false, SPI_MODE, SPI_DIVISOR); }

void AW20216S_write_register(int32_t csPin, uint8_t page, uint8_t reg, uint8_t data) {
uint8_t cmd = COMMAND_CHIP_ID | ((page & 0x07) << 1);

AW20216S_spi_start(csPin);
spi_write(cmd);
spi_write(reg);
spi_write(data);
spi_stop();
}

void AW20216S_write_pwm_buffer(int32_t csPin, uint8_t *pwm_buffer) {
uint8_t cmd = COMMAND_CHIP_ID | (PAGE_1 << 1);

AW20216S_spi_start(csPin);
spi_write(cmd);
spi_write(0);

for (int i = 0; i < PWM_REGISTER_COUNT; i++) {
spi_write(pwm_buffer[i]);
}

spi_stop();
}

void AW20216S_enable(int32_t csPin, int32_t enablePin) {
// Configure chip select pin as output and set high level.
palSetLineMode(csPin, PAL_MODE_OUTPUT_PUSHPULL);
palSetLine(csPin);

// Delay for POR to complete
wait_us(US_POR_DELAY);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does this wait even do?
Why would driving the CS pin cause a POR?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Like the comment says; this delay waits for POR to complete. More specifically it waits for Over Temperature Protection to finish loading after POR. Without it, Over Temperature Protection doesn't work and you risk frying your AW20216S. This needs to happen before anything else. OTP is on by default. I think this is the only required delay.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't follow, asserting the CS pin has nothing to do with POR.

If the intention is to guarantee that this wait happens after POR, the chip should be reset first.

As it is, the time it takes for QMK to boot up and even reach this code is almost certainly long enough to not bother with this.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function AW20216S_enable() contains the delays that you can find in the Power up Timing figure in the DS
imagen
imagen

Copy link

@Gigahawk Gigahawk Jul 3, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's... not what this code does.

EDIT: If we're suddenly going to refer to the datasheet, care to actually release it?


// Configure enable pin as output and set high level.
palSetLineMode(enablePin, PAL_MODE_OUTPUT_PUSHPULL);
palSetLine(enablePin);

// Delay for enable to complete
wait_us(US_ENABLE_DELAY);
}

void AW20216S_init(int32_t csPin, uint8_t swLinesEnabled) {
// Enable SW lines and chip
uint8_t valueGCR = (swLinesEnabled << 4) | REG_GCR_BIT_CHIPEN_MASK;
AW20216S_write_register(csPin, PAGE_0, PAGE_0_REG_GCR, valueGCR);

wait_us(US_BETWEEN_REG_WRITE_DELAY);

// Maximum sink current
uint8_t valueGCCR = RGB_MATRIX_MAXIMUM_BRIGHTNESS;
AW20216S_write_register(csPin, PAGE_0, PAGE_0_REG_GCCR, valueGCCR);

wait_us(US_BETWEEN_REG_WRITE_DELAY);

// Set Constant Current for LEDs (Registers SL)
for (uint8_t i = 0; i < PWM_REGISTER_COUNT; i++) {
AW20216S_write_register(csPin, PAGE_2, i, 0xFF);
wait_us(US_BETWEEN_REG_WRITE_DELAY);
}

// Turn off all LEDs (Registers PWM)
for (uint8_t i = 0; i < PWM_REGISTER_COUNT; i++) {
AW20216S_write_register(csPin, PAGE_1, i, 0x00);
wait_us(US_BETWEEN_REG_WRITE_DELAY);
glorious-qmk marked this conversation as resolved.
Show resolved Hide resolved
}
}

void AW20216S_set_color(int indexLED, uint8_t red, uint8_t green, uint8_t blue) {
if (indexLED >= 0 && indexLED < DRIVER_LED_TOTAL) {
aw_led led = g_aw_leds[indexLED];

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 AW20216S_set_color_all(uint8_t red, uint8_t green, uint8_t blue) {
for (int i = 0; i < DRIVER_LED_TOTAL; i++) {
AW20216S_set_color(i, red, green, blue);
}
}

void AW20216S_update_pwm_buffers(int32_t csPin, uint8_t driver) {
if (g_pwm_buffer_update_required[driver]) {
AW20216S_write_pwm_buffer(csPin, g_pwm_buffer[driver]);
}
g_pwm_buffer_update_required[driver] = false;
}
Loading