diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/chibios/analog.c | 57 | ||||
-rw-r--r-- | drivers/chibios/serial_usart.c | 69 | ||||
-rw-r--r-- | drivers/chibios/serial_usart.h | 78 | ||||
-rw-r--r-- | drivers/chibios/serial_usart_duplex.c | 261 | ||||
-rw-r--r-- | drivers/chibios/ws2812_pwm.c | 11 | ||||
-rw-r--r-- | drivers/chibios/ws2812_spi.c | 45 | ||||
-rw-r--r-- | drivers/issi/is31fl3733.c | 2 | ||||
-rw-r--r-- | drivers/oled/oled_driver.c | 29 | ||||
-rw-r--r-- | drivers/oled/oled_driver.h | 12 |
9 files changed, 499 insertions, 65 deletions
diff --git a/drivers/chibios/analog.c b/drivers/chibios/analog.c index 2b3872afbb..b1081623d3 100644 --- a/drivers/chibios/analog.c +++ b/drivers/chibios/analog.c @@ -101,7 +101,11 @@ // Options are 12, 10, 8, and 6 bit. #ifndef ADC_RESOLUTION -# define ADC_RESOLUTION ADC_CFGR1_RES_10BIT +# ifdef ADC_CFGR_RES_10BITS // ADCv3, ADCv4 +# define ADC_RESOLUTION ADC_CFGR_RES_10BITS +# else // ADCv1, ADCv5, or the bodge for ADCv2 above +# define ADC_RESOLUTION ADC_CFGR1_RES_10BIT +# endif #endif static ADCConfig adcCfg = {}; @@ -161,8 +165,8 @@ __attribute__((weak)) adc_mux pinToMux(pin_t pin) { case B0: return TO_MUX( ADC_CHANNEL_IN12, 2 ); case B1: return TO_MUX( ADC_CHANNEL_IN1, 2 ); case B2: return TO_MUX( ADC_CHANNEL_IN12, 1 ); - case B12: return TO_MUX( ADC_CHANNEL_IN2, 3 ); - case B13: return TO_MUX( ADC_CHANNEL_IN3, 3 ); + case B12: return TO_MUX( ADC_CHANNEL_IN3, 3 ); + case B13: return TO_MUX( ADC_CHANNEL_IN5, 2 ); case B14: return TO_MUX( ADC_CHANNEL_IN4, 3 ); case B15: return TO_MUX( ADC_CHANNEL_IN5, 3 ); case C0: return TO_MUX( ADC_CHANNEL_IN6, 0 ); // Can also be ADC2 @@ -189,11 +193,52 @@ __attribute__((weak)) adc_mux pinToMux(pin_t pin) { case E15: return TO_MUX( ADC_CHANNEL_IN2, 3 ); case F2: return TO_MUX( ADC_CHANNEL_IN10, 0 ); // Can also be ADC2 case F4: return TO_MUX( ADC_CHANNEL_IN5, 0 ); -#elif defined(STM32F4XX) // TODO: add all pins +#elif defined(STM32F4XX) case A0: return TO_MUX( ADC_CHANNEL_IN0, 0 ); - //case A1: return TO_MUX( ADC_CHANNEL_IN1, 0 ); -#elif defined(STM32F1XX) // TODO: add all pins + case A1: return TO_MUX( ADC_CHANNEL_IN1, 0 ); + case A2: return TO_MUX( ADC_CHANNEL_IN2, 0 ); + case A3: return TO_MUX( ADC_CHANNEL_IN3, 0 ); + case A4: return TO_MUX( ADC_CHANNEL_IN4, 0 ); + case A5: return TO_MUX( ADC_CHANNEL_IN5, 0 ); + case A6: return TO_MUX( ADC_CHANNEL_IN6, 0 ); + case A7: return TO_MUX( ADC_CHANNEL_IN7, 0 ); + case B0: return TO_MUX( ADC_CHANNEL_IN8, 0 ); + case B1: return TO_MUX( ADC_CHANNEL_IN9, 0 ); + case C0: return TO_MUX( ADC_CHANNEL_IN10, 0 ); + case C1: return TO_MUX( ADC_CHANNEL_IN11, 0 ); + case C2: return TO_MUX( ADC_CHANNEL_IN12, 0 ); + case C3: return TO_MUX( ADC_CHANNEL_IN13, 0 ); + case C4: return TO_MUX( ADC_CHANNEL_IN14, 0 ); + case C5: return TO_MUX( ADC_CHANNEL_IN15, 0 ); +# if STM32_ADC_USE_ADC3 + case F3: return TO_MUX( ADC_CHANNEL_IN9, 2 ); + case F4: return TO_MUX( ADC_CHANNEL_IN14, 2 ); + case F5: return TO_MUX( ADC_CHANNEL_IN15, 2 ); + case F6: return TO_MUX( ADC_CHANNEL_IN4, 2 ); + case F7: return TO_MUX( ADC_CHANNEL_IN5, 2 ); + case F8: return TO_MUX( ADC_CHANNEL_IN6, 2 ); + case F9: return TO_MUX( ADC_CHANNEL_IN7, 2 ); + case F10: return TO_MUX( ADC_CHANNEL_IN8, 2 ); +# endif +#elif defined(STM32F1XX) case A0: return TO_MUX( ADC_CHANNEL_IN0, 0 ); + case A1: return TO_MUX( ADC_CHANNEL_IN1, 0 ); + case A2: return TO_MUX( ADC_CHANNEL_IN2, 0 ); + case A3: return TO_MUX( ADC_CHANNEL_IN3, 0 ); + case A4: return TO_MUX( ADC_CHANNEL_IN4, 0 ); + case A5: return TO_MUX( ADC_CHANNEL_IN5, 0 ); + case A6: return TO_MUX( ADC_CHANNEL_IN6, 0 ); + case A7: return TO_MUX( ADC_CHANNEL_IN7, 0 ); + case B0: return TO_MUX( ADC_CHANNEL_IN8, 0 ); + case B1: return TO_MUX( ADC_CHANNEL_IN9, 0 ); + case C0: return TO_MUX( ADC_CHANNEL_IN10, 0 ); + case C1: return TO_MUX( ADC_CHANNEL_IN11, 0 ); + case C2: return TO_MUX( ADC_CHANNEL_IN12, 0 ); + case C3: return TO_MUX( ADC_CHANNEL_IN13, 0 ); + case C4: return TO_MUX( ADC_CHANNEL_IN14, 0 ); + case C5: return TO_MUX( ADC_CHANNEL_IN15, 0 ); + // STM32F103x[C-G] in 144-pin packages also have analog inputs on F6...F10, but they are on ADC3, and the + // ChibiOS ADC driver for STM32F1xx currently supports only ADC1, therefore these pins are not usable. #endif } diff --git a/drivers/chibios/serial_usart.c b/drivers/chibios/serial_usart.c index 7c81b16464..cae29388c3 100644 --- a/drivers/chibios/serial_usart.c +++ b/drivers/chibios/serial_usart.c @@ -1,13 +1,20 @@ -#include "quantum.h" -#include "serial.h" -#include "print.h" - -#include <ch.h> -#include <hal.h> +/* Copyright 2021 QMK + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ -#ifndef USART_CR1_M0 -# define USART_CR1_M0 USART_CR1_M // some platforms (f1xx) dont have this so -#endif +#include "serial_usart.h" #ifndef USE_GPIOV1 // The default PAL alternate modes are used to signal that the pins are used for USART @@ -20,50 +27,10 @@ # define SERIAL_USART_DRIVER SD1 #endif -#ifndef SERIAL_USART_CR1 -# define SERIAL_USART_CR1 (USART_CR1_PCE | USART_CR1_PS | USART_CR1_M0) // parity enable, odd parity, 9 bit length -#endif - -#ifndef SERIAL_USART_CR2 -# define SERIAL_USART_CR2 (USART_CR2_STOP_1) // 2 stop bits -#endif - -#ifndef SERIAL_USART_CR3 -# define SERIAL_USART_CR3 0 -#endif - #ifdef SOFT_SERIAL_PIN # define SERIAL_USART_TX_PIN SOFT_SERIAL_PIN #endif -#ifndef SELECT_SOFT_SERIAL_SPEED -# define SELECT_SOFT_SERIAL_SPEED 1 -#endif - -#ifdef SERIAL_USART_SPEED -// Allow advanced users to directly set SERIAL_USART_SPEED -#elif SELECT_SOFT_SERIAL_SPEED == 0 -# define SERIAL_USART_SPEED 460800 -#elif SELECT_SOFT_SERIAL_SPEED == 1 -# define SERIAL_USART_SPEED 230400 -#elif SELECT_SOFT_SERIAL_SPEED == 2 -# define SERIAL_USART_SPEED 115200 -#elif SELECT_SOFT_SERIAL_SPEED == 3 -# define SERIAL_USART_SPEED 57600 -#elif SELECT_SOFT_SERIAL_SPEED == 4 -# define SERIAL_USART_SPEED 38400 -#elif SELECT_SOFT_SERIAL_SPEED == 5 -# define SERIAL_USART_SPEED 19200 -#else -# error invalid SELECT_SOFT_SERIAL_SPEED value -#endif - -#ifndef SERIAL_USART_TIMEOUT -# define SERIAL_USART_TIMEOUT 100 -#endif - -#define HANDSHAKE_MAGIC 7 - static inline msg_t sdWriteHalfDuplex(SerialDriver* driver, uint8_t* data, uint8_t size) { msg_t ret = sdWrite(driver, data, size); @@ -123,6 +90,10 @@ __attribute__((weak)) void usart_init(void) { #else palSetLineMode(SERIAL_USART_TX_PIN, PAL_MODE_ALTERNATE(SERIAL_USART_TX_PAL_MODE) | PAL_STM32_OTYPE_OPENDRAIN); #endif + +#if defined(USART_REMAP) + USART_REMAP; +#endif } void usart_master_init(void) { diff --git a/drivers/chibios/serial_usart.h b/drivers/chibios/serial_usart.h new file mode 100644 index 0000000000..d35b5d12c6 --- /dev/null +++ b/drivers/chibios/serial_usart.h @@ -0,0 +1,78 @@ +/* Copyright 2021 QMK + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include "quantum.h" +#include "serial.h" +#include "printf.h" + +#include <ch.h> +#include <hal.h> + +#ifndef USART_CR1_M0 +# define USART_CR1_M0 USART_CR1_M // some platforms (f1xx) dont have this so +#endif + +#ifndef SERIAL_USART_CR1 +# define SERIAL_USART_CR1 (USART_CR1_PCE | USART_CR1_PS | USART_CR1_M0) // parity enable, odd parity, 9 bit length +#endif + +#ifndef SERIAL_USART_CR2 +# define SERIAL_USART_CR2 (USART_CR2_STOP_1) // 2 stop bits +#endif + +#ifndef SERIAL_USART_CR3 +# define SERIAL_USART_CR3 0 +#endif + +#if defined(USART1_REMAP) +# define USART_REMAP do { (AFIO->MAPR |= AFIO_MAPR_USART1_REMAP); } while(0) +#elif defined(USART2_REMAP) +# define USART_REMAP do { (AFIO->MAPR |= AFIO_MAPR_USART2_REMAP); } while(0) +#elif defined(USART3_PARTIALREMAP) +# define USART_REMAP do { (AFIO->MAPR |= AFIO_MAPR_USART3_REMAP_PARTIALREMAP); } while(0) +#elif defined(USART3_FULLREMAP) +# define USART_REMAP do { (AFIO->MAPR |= AFIO_MAPR_USART3_REMAP_FULLREMAP); } while(0) +#endif + +#ifndef SELECT_SOFT_SERIAL_SPEED +# define SELECT_SOFT_SERIAL_SPEED 1 +#endif + +#ifdef SERIAL_USART_SPEED +// Allow advanced users to directly set SERIAL_USART_SPEED +#elif SELECT_SOFT_SERIAL_SPEED == 0 +# define SERIAL_USART_SPEED 460800 +#elif SELECT_SOFT_SERIAL_SPEED == 1 +# define SERIAL_USART_SPEED 230400 +#elif SELECT_SOFT_SERIAL_SPEED == 2 +# define SERIAL_USART_SPEED 115200 +#elif SELECT_SOFT_SERIAL_SPEED == 3 +# define SERIAL_USART_SPEED 57600 +#elif SELECT_SOFT_SERIAL_SPEED == 4 +# define SERIAL_USART_SPEED 38400 +#elif SELECT_SOFT_SERIAL_SPEED == 5 +# define SERIAL_USART_SPEED 19200 +#else +# error invalid SELECT_SOFT_SERIAL_SPEED value +#endif + +#ifndef SERIAL_USART_TIMEOUT +# define SERIAL_USART_TIMEOUT 100 +#endif + +#define HANDSHAKE_MAGIC 7 diff --git a/drivers/chibios/serial_usart_duplex.c b/drivers/chibios/serial_usart_duplex.c new file mode 100644 index 0000000000..cc9b889ac2 --- /dev/null +++ b/drivers/chibios/serial_usart_duplex.c @@ -0,0 +1,261 @@ +/* Copyright 2021 QMK + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "serial_usart.h" + +#include <stdatomic.h> + +#if !defined(USE_GPIOV1) +// The default PAL alternate modes are used to signal that the pins are used for USART +# if !defined(SERIAL_USART_TX_PAL_MODE) +# define SERIAL_USART_TX_PAL_MODE 7 +# endif +# if !defined(SERIAL_USART_RX_PAL_MODE) +# define SERIAL_USART_RX_PAL_MODE 7 +# endif +#endif + +#if !defined(SERIAL_USART_DRIVER) +# define SERIAL_USART_DRIVER UARTD1 +#endif + +#if !defined(SERIAL_USART_TX_PIN) +# define SERIAL_USART_TX_PIN A9 +#endif + +#if !defined(SERIAL_USART_RX_PIN) +# define SERIAL_USART_RX_PIN A10 +#endif + +#define SIGNAL_HANDSHAKE_RECEIVED 0x1 + +void handle_transactions_slave(uint8_t sstd_index); +static void receive_transaction_handshake(UARTDriver* uartp, uint16_t received_handshake); + +/* + * UART driver configuration structure. We use the blocking DMA enabled API and + * the rxchar callback to receive handshake tokens but only on the slave halve. + */ +// clang-format off +static UARTConfig uart_config = { + .txend1_cb = NULL, + .txend2_cb = NULL, + .rxend_cb = NULL, + .rxchar_cb = NULL, + .rxerr_cb = NULL, + .timeout_cb = NULL, + .speed = (SERIAL_USART_SPEED), + .cr1 = (SERIAL_USART_CR1), + .cr2 = (SERIAL_USART_CR2), + .cr3 = (SERIAL_USART_CR3) +}; +// clang-format on + +static SSTD_t* Transaction_table = NULL; +static uint8_t Transaction_table_size = 0; +static atomic_uint_least8_t handshake = 0xFF; +static thread_reference_t tp_target = NULL; + +/* + * This callback is invoked when a character is received but the application + * was not ready to receive it, the character is passed as parameter. + * Receive transaction table index from initiator, which doubles as basic handshake token. */ +static void receive_transaction_handshake(UARTDriver* uartp, uint16_t received_handshake) { + /* Check if received handshake is not a valid transaction id. + * Please note that we can still catch a seemingly valid handshake + * i.e. a byte from a ongoing transfer which is in the allowed range. + * So this check mainly prevents any obviously wrong handshakes and + * subsequent wakeups of the receiving thread, which is a costly operation. */ + if (received_handshake > Transaction_table_size) { + return; + } + + handshake = (uint8_t)received_handshake; + chSysLockFromISR(); + /* Wakeup receiving thread to start a transaction. */ + chEvtSignalI(tp_target, (eventmask_t)SIGNAL_HANDSHAKE_RECEIVED); + chSysUnlockFromISR(); +} + +__attribute__((weak)) void usart_init(void) { +#if defined(USE_GPIOV1) + palSetLineMode(SERIAL_USART_TX_PIN, PAL_MODE_STM32_ALTERNATE_PUSHPULL); + palSetLineMode(SERIAL_USART_RX_PIN, PAL_MODE_INPUT); +#else + palSetLineMode(SERIAL_USART_TX_PIN, PAL_MODE_ALTERNATE(SERIAL_USART_TX_PAL_MODE) | PAL_STM32_OTYPE_PUSHPULL | PAL_STM32_OSPEED_HIGHEST); + palSetLineMode(SERIAL_USART_RX_PIN, PAL_MODE_ALTERNATE(SERIAL_USART_RX_PAL_MODE) | PAL_STM32_OTYPE_PUSHPULL | PAL_STM32_OSPEED_HIGHEST); +#endif +} + +/* + * This thread runs on the slave half and reacts to transactions initiated from the master. + */ +static THD_WORKING_AREA(waSlaveThread, 1024); +static THD_FUNCTION(SlaveThread, arg) { + (void)arg; + chRegSetThreadName("slave_usart_tx_rx"); + + while (true) { + /* We sleep as long as there is no handshake waiting for us. */ + chEvtWaitAny((eventmask_t)SIGNAL_HANDSHAKE_RECEIVED); + handle_transactions_slave(handshake); + } +} + +void soft_serial_target_init(SSTD_t* const sstd_table, int sstd_table_size) { + Transaction_table = sstd_table; + Transaction_table_size = (uint8_t)sstd_table_size; + usart_init(); + +#if defined(USART_REMAP) + USART_REMAP; +#endif + + tp_target = chThdCreateStatic(waSlaveThread, sizeof(waSlaveThread), HIGHPRIO, SlaveThread, NULL); + + // Start receiving handshake tokens on slave halve + uart_config.rxchar_cb = receive_transaction_handshake; + uartStart(&SERIAL_USART_DRIVER, &uart_config); +} + +/** + * @brief React to transactions started by the master. + * This version uses duplex send and receive usart pheriphals and DMA backed transfers. + */ +void inline handle_transactions_slave(uint8_t sstd_index) { + size_t buffer_size = 0; + msg_t msg = 0; + SSTD_t* trans = &Transaction_table[sstd_index]; + + /* Send back the handshake which is XORed as a simple checksum, + to signal that the slave is ready to receive possible transaction buffers */ + sstd_index ^= HANDSHAKE_MAGIC; + buffer_size = (size_t)sizeof(sstd_index); + msg = uartSendTimeout(&SERIAL_USART_DRIVER, &buffer_size, &sstd_index, TIME_MS2I(SERIAL_USART_TIMEOUT)); + + if (msg != MSG_OK) { + if (trans->status) { + *trans->status = TRANSACTION_NO_RESPONSE; + } + return; + } + + /* Receive transaction buffer from the master. If this transaction requires it.*/ + buffer_size = (size_t)trans->initiator2target_buffer_size; + if (buffer_size) { + msg = uartReceiveTimeout(&SERIAL_USART_DRIVER, &buffer_size, trans->initiator2target_buffer, TIME_MS2I(SERIAL_USART_TIMEOUT)); + if (msg != MSG_OK) { + if (trans->status) { + *trans->status = TRANSACTION_NO_RESPONSE; + } + return; + } + } + + /* Send transaction buffer to the master. If this transaction requires it. */ + buffer_size = (size_t)trans->target2initiator_buffer_size; + if (buffer_size) { + msg = uartSendFullTimeout(&SERIAL_USART_DRIVER, &buffer_size, trans->target2initiator_buffer, TIME_MS2I(SERIAL_USART_TIMEOUT)); + if (msg != MSG_OK) { + if (trans->status) { + *trans->status = TRANSACTION_NO_RESPONSE; + } + return; + } + } + + if (trans->status) { + *trans->status = TRANSACTION_ACCEPTED; + } +} + +void soft_serial_initiator_init(SSTD_t* const sstd_table, int sstd_table_size) { + Transaction_table = sstd_table; + Transaction_table_size = (uint8_t)sstd_table_size; + usart_init(); + +#if defined(SERIAL_USART_PIN_SWAP) + uart_config.cr2 |= USART_CR2_SWAP; // master has swapped TX/RX pins +#endif + +#if defined(USART_REMAP) + USART_REMAP; +#endif + + uartStart(&SERIAL_USART_DRIVER, &uart_config); +} + +/** + * @brief Start transaction from the master to the slave. + * This version uses duplex send and receive usart pheriphals and DMA backed transfers. + * + * @param index Transaction Table index of the transaction to start. + * @return int TRANSACTION_NO_RESPONSE in case of Timeout. + * TRANSACTION_TYPE_ERROR in case of invalid transaction index. + * TRANSACTION_END in case of success. + */ +#if !defined(SERIAL_USE_MULTI_TRANSACTION) +int soft_serial_transaction(void) { + uint8_t sstd_index = 0; +#else +int soft_serial_transaction(int index) { + uint8_t sstd_index = index; +#endif + + if (sstd_index > Transaction_table_size) { + return TRANSACTION_TYPE_ERROR; + } + + SSTD_t* const trans = &Transaction_table[sstd_index]; + msg_t msg = 0; + size_t buffer_size = (size_t)sizeof(sstd_index); + + /* Send transaction table index to the slave, which doubles as basic handshake token. */ + uartSendFullTimeout(&SERIAL_USART_DRIVER, &buffer_size, &sstd_index, TIME_MS2I(SERIAL_USART_TIMEOUT)); + + uint8_t sstd_index_shake = 0xFF; + buffer_size = (size_t)sizeof(sstd_index_shake); + + /* Receive the handshake token from the slave. The token was XORed by the slave as a simple checksum. + If the tokens match, the master will start to send and receive possible transaction buffers. */ + msg = uartReceiveTimeout(&SERIAL_USART_DRIVER, &buffer_size, &sstd_index_shake, TIME_MS2I(SERIAL_USART_TIMEOUT)); + if (msg != MSG_OK || (sstd_index_shake != (sstd_index ^ HANDSHAKE_MAGIC))) { + dprintln("USART: Handshake Failed"); + return TRANSACTION_NO_RESPONSE; + } + + /* Send transaction buffer to the slave. If this transaction requires it. */ + buffer_size = (size_t)trans->initiator2target_buffer_size; + if (buffer_size) { + msg = uartSendFullTimeout(&SERIAL_USART_DRIVER, &buffer_size, trans->initiator2target_buffer, TIME_MS2I(SERIAL_USART_TIMEOUT)); + if (msg != MSG_OK) { + dprintln("USART: Send Failed"); + return TRANSACTION_NO_RESPONSE; + } + } + + /* Receive transaction buffer from the slave. If this transaction requires it. */ + buffer_size = (size_t)trans->target2initiator_buffer_size; + if (buffer_size) { + msg = uartReceiveTimeout(&SERIAL_USART_DRIVER, &buffer_size, trans->target2initiator_buffer, TIME_MS2I(SERIAL_USART_TIMEOUT)); + if (msg != MSG_OK) { + dprintln("USART: Receive Failed"); + return TRANSACTION_NO_RESPONSE; + } + } + + return TRANSACTION_END; +} diff --git a/drivers/chibios/ws2812_pwm.c b/drivers/chibios/ws2812_pwm.c index 140120d488..e6af55b6b3 100644 --- a/drivers/chibios/ws2812_pwm.c +++ b/drivers/chibios/ws2812_pwm.c @@ -27,6 +27,15 @@ # error "please consult your MCU's datasheet and specify in your config.h: #define WS2812_DMAMUX_ID STM32_DMAMUX1_TIM?_UP" #endif +#ifndef WS2812_PWM_COMPLEMENTARY_OUTPUT +# define WS2812_PWM_OUTPUT_MODE PWM_OUTPUT_ACTIVE_HIGH +#else +# if !STM32_PWM_USE_ADVANCED +# error "WS2812_PWM_COMPLEMENTARY_OUTPUT requires STM32_PWM_USE_ADVANCED == TRUE" +# endif +# define WS2812_PWM_OUTPUT_MODE PWM_COMPLEMENTARY_OUTPUT_ACTIVE_HIGH +#endif + // Push Pull or Open Drain Configuration // Default Push Pull #ifndef WS2812_EXTERNAL_PULLUP @@ -247,7 +256,7 @@ void ws2812_init(void) { .channels = { [0 ... 3] = {.mode = PWM_OUTPUT_DISABLED, .callback = NULL}, // Channels default to disabled - [WS2812_PWM_CHANNEL - 1] = {.mode = PWM_OUTPUT_ACTIVE_HIGH, .callback = NULL}, // Turn on the channel we care about + [WS2812_PWM_CHANNEL - 1] = {.mode = WS2812_PWM_OUTPUT_MODE, .callback = NULL}, // Turn on the channel we care about }, .cr2 = 0, .dier = TIM_DIER_UDE, // DMA on update event for next period diff --git a/drivers/chibios/ws2812_spi.c b/drivers/chibios/ws2812_spi.c index 89df2987b5..e02cbabc02 100644 --- a/drivers/chibios/ws2812_spi.c +++ b/drivers/chibios/ws2812_spi.c @@ -32,6 +32,37 @@ # endif #endif +// Define SPI config speed +// baudrate should target 3.2MHz +// F072 fpclk = 48MHz +// 48/16 = 3Mhz +#if WS2812_SPI_DIVISOR == 2 +# define WS2812_SPI_DIVISOR (0) +#elif WS2812_SPI_DIVISOR == 4 +# define WS2812_SPI_DIVISOR (SPI_CR1_BR_0) +#elif WS2812_SPI_DIVISOR == 8 +# define WS2812_SPI_DIVISOR (SPI_CR1_BR_1) +#elif WS2812_SPI_DIVISOR == 16 // same as default +# define WS2812_SPI_DIVISOR (SPI_CR1_BR_1 | SPI_CR1_BR_0) +#elif WS2812_SPI_DIVISOR == 32 +# define WS2812_SPI_DIVISOR (SPI_CR1_BR_2) +#elif WS2812_SPI_DIVISOR == 64 +# define WS2812_SPI_DIVISOR (SPI_CR1_BR_2 | SPI_CR1_BR_0) +#elif WS2812_SPI_DIVISOR == 128 +# define WS2812_SPI_DIVISOR (SPI_CR1_BR_2 | SPI_CR1_BR_1) +#elif WS2812_SPI_DIVISOR == 256 +# define WS2812_SPI_DIVISOR (SPI_CR1_BR_2 | SPI_CR1_BR_1 | SPI_CR1_BR_0) +#else +# define WS2812_SPI_DIVISOR (SPI_CR1_BR_1 | SPI_CR1_BR_0) // default +#endif + +// Use SPI circular buffer +#ifdef WS2812_SPI_USE_CIRCULAR_BUFFER +# define WS2812_SPI_BUFFER_MODE 1 // circular buffer +#else +# define WS2812_SPI_BUFFER_MODE 0 // normal buffer +#endif + #define BYTES_FOR_LED_BYTE 4 #define NB_COLORS 3 #define BYTES_FOR_LED (BYTES_FOR_LED_BYTE * NB_COLORS) @@ -81,14 +112,14 @@ void ws2812_init(void) { palSetLineMode(RGB_DI_PIN, WS2812_OUTPUT_MODE); // TODO: more dynamic baudrate - static const SPIConfig spicfg = { - 0, NULL, PAL_PORT(RGB_DI_PIN), PAL_PAD(RGB_DI_PIN), - SPI_CR1_BR_1 | SPI_CR1_BR_0 // baudrate : fpclk / 8 => 1tick is 0.32us (2.25 MHz) - }; + static const SPIConfig spicfg = {WS2812_SPI_BUFFER_MODE, NULL, PAL_PORT(RGB_DI_PIN), PAL_PAD(RGB_DI_PIN), WS2812_SPI_DIVISOR}; spiAcquireBus(&WS2812_SPI); /* Acquire ownership of the bus. */ spiStart(&WS2812_SPI, &spicfg); /* Setup transfer parameters. */ spiSelect(&WS2812_SPI); /* Slave Select assertion. */ +#ifdef WS2812_SPI_USE_CIRCULAR_BUFFER + spiStartSend(&WS2812_SPI, sizeof(txbuf) / sizeof(txbuf[0]), txbuf); +#endif } void ws2812_setleds(LED_TYPE* ledarray, uint16_t leds) { @@ -104,9 +135,11 @@ void ws2812_setleds(LED_TYPE* ledarray, uint16_t leds) { // Send async - each led takes ~0.03ms, 50 leds ~1.5ms, animations flushing faster than send will cause issues. // Instead spiSend can be used to send synchronously (or the thread logic can be added back). -#ifdef WS2812_SPI_SYNC +#ifndef WS2812_SPI_USE_CIRCULAR_BUFFER +# ifdef WS2812_SPI_SYNC spiSend(&WS2812_SPI, sizeof(txbuf) / sizeof(txbuf[0]), txbuf); -#else +# else spiStartSend(&WS2812_SPI, sizeof(txbuf) / sizeof(txbuf[0]), txbuf); +# endif #endif } diff --git a/drivers/issi/is31fl3733.c b/drivers/issi/is31fl3733.c index dddf0cb734..d99e5339c9 100644 --- a/drivers/issi/is31fl3733.c +++ b/drivers/issi/is31fl3733.c @@ -68,7 +68,7 @@ uint8_t g_twi_transfer_buffer[20]; uint8_t g_pwm_buffer[DRIVER_COUNT][192]; bool g_pwm_buffer_update_required[DRIVER_COUNT] = {false}; -uint8_t g_led_control_registers[DRIVER_COUNT][24] = {{0}, {0}}; +uint8_t g_led_control_registers[DRIVER_COUNT][24] = {0}; bool g_led_control_registers_update_required[DRIVER_COUNT] = {false}; bool IS31FL3733_write_register(uint8_t addr, uint8_t reg, uint8_t data) { diff --git a/drivers/oled/oled_driver.c b/drivers/oled/oled_driver.c index 92c64399e2..082115d534 100644 --- a/drivers/oled/oled_driver.c +++ b/drivers/oled/oled_driver.c @@ -24,6 +24,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. #include "progmem.h" +#include "keyboard.h" + // Used commands from spec sheet: https://cdn-shop.adafruit.com/datasheets/SSD1306.pdf // for SH1106: https://www.velleman.eu/downloads/29/infosheets/sh1106_datasheet.pdf @@ -71,6 +73,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. #define PRE_CHARGE_PERIOD 0xD9 #define VCOM_DETECT 0xDB +// Advance Graphic Commands +#define FADE_BLINK 0x23 +#define ENABLE_FADE 0x20 +#define ENABLE_BLINK 0x30 + // Charge Pump Commands #define CHARGE_PUMP 0x8D @@ -152,6 +159,12 @@ static void InvertCharacter(uint8_t *cursor) { } bool oled_init(uint8_t rotation) { +#if defined(USE_I2C) && defined(SPLIT_KEYBOARD) + if (!is_keyboard_master()) { + return true; + } +#endif + oled_rotation = oled_init_user(rotation); if (!HAS_FLAGS(oled_rotation, OLED_ROTATION_90)) { oled_rotation_width = OLED_DISPLAY_WIDTH; @@ -539,7 +552,13 @@ bool oled_on(void) { oled_timeout = timer_read32() + OLED_TIMEOUT; #endif - static const uint8_t PROGMEM display_on[] = {I2C_CMD, DISPLAY_ON}; + static const uint8_t PROGMEM display_on[] = +#ifdef OLED_FADE_OUT + {I2C_CMD, FADE_BLINK, 0x00}; +#else + {I2C_CMD, DISPLAY_ON}; +#endif + if (!oled_active) { if (I2C_TRANSMIT_P(display_on) != I2C_STATUS_SUCCESS) { print("oled_on cmd failed\n"); @@ -555,7 +574,13 @@ bool oled_off(void) { return !oled_active; } - static const uint8_t PROGMEM display_off[] = {I2C_CMD, DISPLAY_OFF}; + static const uint8_t PROGMEM display_off[] = +#ifdef OLED_FADE_OUT + {I2C_CMD, FADE_BLINK, ENABLE_FADE | OLED_FADE_OUT_INTERVAL}; +#else + {I2C_CMD, DISPLAY_OFF}; +#endif + if (oled_active) { if (I2C_TRANSMIT_P(display_off) != I2C_STATUS_SUCCESS) { print("oled_off cmd failed\n"); diff --git a/drivers/oled/oled_driver.h b/drivers/oled/oled_driver.h index 72ab21247d..cbf5380ee0 100644 --- a/drivers/oled/oled_driver.h +++ b/drivers/oled/oled_driver.h @@ -154,10 +154,22 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. # endif #endif +#if !defined(OLED_FADE_OUT_INTERVAL) +# define OLED_FADE_OUT_INTERVAL 0x00 +#endif + +#if OLED_FADE_OUT_INTERVAL > 0x0F || OLED_FADE_OUT_INTERVAL < 0x00 +# error OLED_FADE_OUT_INTERVAL must be between 0x00 and 0x0F +#endif + #if !defined(OLED_I2C_TIMEOUT) # define OLED_I2C_TIMEOUT 100 #endif +#if !defined(OLED_UPDATE_INTERVAL) && defined(SPLIT_KEYBOARD) +# define OLED_UPDATE_INTERVAL 50 +#endif + typedef struct __attribute__((__packed__)) { uint8_t *current_element; uint16_t remaining_element_count; |