diff options
Diffstat (limited to 'drivers/arm')
-rw-r--r-- | drivers/arm/i2c_master.c | 127 | ||||
-rw-r--r-- | drivers/arm/i2c_master.h | 85 | ||||
-rw-r--r-- | drivers/arm/ws2812.c | 95 | ||||
-rw-r--r-- | drivers/arm/ws2812.h | 16 | ||||
-rw-r--r-- | drivers/arm/ws2812_pwm.c | 1 | ||||
-rw-r--r-- | drivers/arm/ws2812_spi.c | 1 |
6 files changed, 225 insertions, 100 deletions
diff --git a/drivers/arm/i2c_master.c b/drivers/arm/i2c_master.c index 5814375f3..2a43ba239 100644 --- a/drivers/arm/i2c_master.c +++ b/drivers/arm/i2c_master.c @@ -33,88 +33,83 @@ static uint8_t i2c_address; static const I2CConfig i2cconfig = { - STM32_TIMINGR_PRESC(I2C1_TIMINGR_PRESC) | - STM32_TIMINGR_SCLDEL(I2C1_TIMINGR_SCLDEL) | STM32_TIMINGR_SDADEL(I2C1_TIMINGR_SDADEL) | - STM32_TIMINGR_SCLH(I2C1_TIMINGR_SCLH) | STM32_TIMINGR_SCLL(I2C1_TIMINGR_SCLL), - 0, - 0 +#ifdef USE_I2CV1 + I2C1_OPMODE, + I2C1_CLOCK_SPEED, + I2C1_DUTY_CYCLE, +#else + // This configures the I2C clock to 400khz assuming a 72Mhz clock + // For more info : https://www.st.com/en/embedded-software/stsw-stm32126.html + STM32_TIMINGR_PRESC(I2C1_TIMINGR_PRESC) | STM32_TIMINGR_SCLDEL(I2C1_TIMINGR_SCLDEL) | STM32_TIMINGR_SDADEL(I2C1_TIMINGR_SDADEL) | STM32_TIMINGR_SCLH(I2C1_TIMINGR_SCLH) | STM32_TIMINGR_SCLL(I2C1_TIMINGR_SCLL), 0, 0 +#endif }; static i2c_status_t chibios_to_qmk(const msg_t* status) { - switch (*status) { - case I2C_NO_ERROR: - return I2C_STATUS_SUCCESS; - case I2C_TIMEOUT: - return I2C_STATUS_TIMEOUT; - // I2C_BUS_ERROR, I2C_ARBITRATION_LOST, I2C_ACK_FAILURE, I2C_OVERRUN, I2C_PEC_ERROR, I2C_SMB_ALERT - default: - return I2C_STATUS_ERROR; - } + switch (*status) { + case I2C_NO_ERROR: + return I2C_STATUS_SUCCESS; + case I2C_TIMEOUT: + return I2C_STATUS_TIMEOUT; + // I2C_BUS_ERROR, I2C_ARBITRATION_LOST, I2C_ACK_FAILURE, I2C_OVERRUN, I2C_PEC_ERROR, I2C_SMB_ALERT + default: + return I2C_STATUS_ERROR; + } } -__attribute__ ((weak)) -void i2c_init(void) -{ - // Try releasing special pins for a short time - palSetPadMode(I2C1_SCL_BANK, I2C1_SCL, PAL_MODE_INPUT); - palSetPadMode(I2C1_SDA_BANK, I2C1_SDA, PAL_MODE_INPUT); - - chThdSleepMilliseconds(10); - - palSetPadMode(I2C1_SCL_BANK, I2C1_SCL, PAL_MODE_ALTERNATE(I2C1_SCL_PAL_MODE) | PAL_STM32_OTYPE_OPENDRAIN); - palSetPadMode(I2C1_SDA_BANK, I2C1_SDA, PAL_MODE_ALTERNATE(I2C1_SDA_PAL_MODE) | PAL_STM32_OTYPE_OPENDRAIN); - - //i2cInit(); //This is invoked by halInit() so no need to redo it. +__attribute__((weak)) void i2c_init(void) { + // Try releasing special pins for a short time + palSetPadMode(I2C1_SCL_BANK, I2C1_SCL, PAL_MODE_INPUT); + palSetPadMode(I2C1_SDA_BANK, I2C1_SDA, PAL_MODE_INPUT); + + chThdSleepMilliseconds(10); +#ifdef USE_I2CV1 + palSetPadMode(I2C1_SCL_BANK, I2C1_SCL, PAL_MODE_STM32_ALTERNATE_OPENDRAIN); + palSetPadMode(I2C1_SDA_BANK, I2C1_SDA, PAL_MODE_STM32_ALTERNATE_OPENDRAIN); +#else + palSetPadMode(I2C1_SCL_BANK, I2C1_SCL, PAL_MODE_ALTERNATE(I2C1_SCL_PAL_MODE) | PAL_STM32_OTYPE_OPENDRAIN); + palSetPadMode(I2C1_SDA_BANK, I2C1_SDA, PAL_MODE_ALTERNATE(I2C1_SDA_PAL_MODE) | PAL_STM32_OTYPE_OPENDRAIN); +#endif } -i2c_status_t i2c_start(uint8_t address) -{ - i2c_address = address; - i2cStart(&I2C_DRIVER, &i2cconfig); - return I2C_STATUS_SUCCESS; +i2c_status_t i2c_start(uint8_t address) { + i2c_address = address; + i2cStart(&I2C_DRIVER, &i2cconfig); + return I2C_STATUS_SUCCESS; } -i2c_status_t i2c_transmit(uint8_t address, const uint8_t* data, uint16_t length, uint16_t timeout) -{ - i2c_address = address; - i2cStart(&I2C_DRIVER, &i2cconfig); - msg_t status = i2cMasterTransmitTimeout(&I2C_DRIVER, (i2c_address >> 1), data, length, 0, 0, MS2ST(timeout)); - return chibios_to_qmk(&status); +i2c_status_t i2c_transmit(uint8_t address, const uint8_t* data, uint16_t length, uint16_t timeout) { + i2c_address = address; + i2cStart(&I2C_DRIVER, &i2cconfig); + msg_t status = i2cMasterTransmitTimeout(&I2C_DRIVER, (i2c_address >> 1), data, length, 0, 0, MS2ST(timeout)); + return chibios_to_qmk(&status); } -i2c_status_t i2c_receive(uint8_t address, uint8_t* data, uint16_t length, uint16_t timeout) -{ - i2c_address = address; - i2cStart(&I2C_DRIVER, &i2cconfig); - msg_t status = i2cMasterReceiveTimeout(&I2C_DRIVER, (i2c_address >> 1), data, length, MS2ST(timeout)); - return chibios_to_qmk(&status); +i2c_status_t i2c_receive(uint8_t address, uint8_t* data, uint16_t length, uint16_t timeout) { + i2c_address = address; + i2cStart(&I2C_DRIVER, &i2cconfig); + msg_t status = i2cMasterReceiveTimeout(&I2C_DRIVER, (i2c_address >> 1), data, length, MS2ST(timeout)); + return chibios_to_qmk(&status); } -i2c_status_t i2c_writeReg(uint8_t devaddr, uint8_t regaddr, const uint8_t* data, uint16_t length, uint16_t timeout) -{ - i2c_address = devaddr; - i2cStart(&I2C_DRIVER, &i2cconfig); +i2c_status_t i2c_writeReg(uint8_t devaddr, uint8_t regaddr, const uint8_t* data, uint16_t length, uint16_t timeout) { + i2c_address = devaddr; + i2cStart(&I2C_DRIVER, &i2cconfig); - uint8_t complete_packet[length + 1]; - for(uint8_t i = 0; i < length; i++) - { - complete_packet[i+1] = data[i]; - } - complete_packet[0] = regaddr; + uint8_t complete_packet[length + 1]; + for (uint8_t i = 0; i < length; i++) { + complete_packet[i + 1] = data[i]; + } + complete_packet[0] = regaddr; - msg_t status = i2cMasterTransmitTimeout(&I2C_DRIVER, (i2c_address >> 1), complete_packet, length + 1, 0, 0, MS2ST(timeout)); - return chibios_to_qmk(&status); + msg_t status = i2cMasterTransmitTimeout(&I2C_DRIVER, (i2c_address >> 1), complete_packet, length + 1, 0, 0, MS2ST(timeout)); + return chibios_to_qmk(&status); } -i2c_status_t i2c_readReg(uint8_t devaddr, uint8_t* regaddr, uint8_t* data, uint16_t length, uint16_t timeout) -{ - i2c_address = devaddr; - i2cStart(&I2C_DRIVER, &i2cconfig); - msg_t status = i2cMasterTransmitTimeout(&I2C_DRIVER, (i2c_address >> 1), regaddr, 1, data, length, MS2ST(timeout)); - return chibios_to_qmk(&status); +i2c_status_t i2c_readReg(uint8_t devaddr, uint8_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout) { + i2c_address = devaddr; + i2cStart(&I2C_DRIVER, &i2cconfig); + msg_t status = i2cMasterTransmitTimeout(&I2C_DRIVER, (i2c_address >> 1), ®addr, 1, data, length, MS2ST(timeout)); + return chibios_to_qmk(&status); } -void i2c_stop(void) -{ - i2cStop(&I2C_DRIVER); -} +void i2c_stop(void) { i2cStop(&I2C_DRIVER); } diff --git a/drivers/arm/i2c_master.h b/drivers/arm/i2c_master.h index 1bb74c800..efe3909a6 100644 --- a/drivers/arm/i2c_master.h +++ b/drivers/arm/i2c_master.h @@ -1,9 +1,9 @@ /* Copyright 2018 Jack Humbert * Copyright 2018 Yiancar * - * This program is free sofare: you can redistribute it and/or modify + * 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 Sofare Foundation, either version 2 of the License, or + * 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, @@ -22,71 +22,88 @@ * Please ensure that HAL_USE_I2C is TRUE in the halconf.h file and that * STM32_I2C_USE_I2C1 is TRUE in the mcuconf.h file. */ +#pragma once #include "ch.h" #include <hal.h> +#if defined(STM32F1XX) || defined(STM32F1xx) || defined(STM32F2xx) || defined(STM32F4xx) || defined(STM32L0xx) || defined(STM32L1xx) +# define USE_I2CV1 +#endif + #ifdef I2C1_BANK - #define I2C1_SCL_BANK I2C1_BANK - #define I2C1_SDA_BANK I2C1_BANK +# define I2C1_SCL_BANK I2C1_BANK +# define I2C1_SDA_BANK I2C1_BANK #endif #ifndef I2C1_SCL_BANK - #define I2C1_SCL_BANK GPIOB +# define I2C1_SCL_BANK GPIOB #endif #ifndef I2C1_SDA_BANK - #define I2C1_SDA_BANK GPIOB +# define I2C1_SDA_BANK GPIOB #endif #ifndef I2C1_SCL - #define I2C1_SCL 6 +# define I2C1_SCL 6 #endif #ifndef I2C1_SDA - #define I2C1_SDA 7 +# define I2C1_SDA 7 #endif +#ifdef USE_I2CV1 +# ifndef I2C1_OPMODE +# define I2C1_OPMODE OPMODE_I2C +# endif +# ifndef I2C1_CLOCK_SPEED +# define I2C1_CLOCK_SPEED 100000 /* 400000 */ +# endif +# ifndef I2C1_DUTY_CYCLE +# define I2C1_DUTY_CYCLE STD_DUTY_CYCLE /* FAST_DUTY_CYCLE_2 */ +# endif +#else // The default PAL alternate modes are used to signal that the pins are used for I2C -#ifndef I2C1_SCL_PAL_MODE - #define I2C1_SCL_PAL_MODE 4 -#endif -#ifndef I2C1_SDA_PAL_MODE - #define I2C1_SDA_PAL_MODE 4 -#endif +# ifndef I2C1_SCL_PAL_MODE +# define I2C1_SCL_PAL_MODE 4 +# endif +# ifndef I2C1_SDA_PAL_MODE +# define I2C1_SDA_PAL_MODE 4 +# endif // The default timing values below configures the I2C clock to 400khz assuming a 72Mhz clock // For more info : https://www.st.com/en/embedded-software/stsw-stm32126.html -#ifndef I2C1_TIMINGR_PRESC - #define I2C1_TIMINGR_PRESC 15U -#endif -#ifndef I2C1_TIMINGR_SCLDEL - #define I2C1_TIMINGR_SCLDEL 4U -#endif -#ifndef I2C1_TIMINGR_SDADEL - #define I2C1_TIMINGR_SDADEL 2U -#endif -#ifndef I2C1_TIMINGR_SCLH - #define I2C1_TIMINGR_SCLH 15U -#endif -#ifndef I2C1_TIMINGR_SCLL - #define I2C1_TIMINGR_SCLL 21U +# ifndef I2C1_TIMINGR_PRESC +# define I2C1_TIMINGR_PRESC 0U +# endif +# ifndef I2C1_TIMINGR_SCLDEL +# define I2C1_TIMINGR_SCLDEL 7U +# endif +# ifndef I2C1_TIMINGR_SDADEL +# define I2C1_TIMINGR_SDADEL 0U +# endif +# ifndef I2C1_TIMINGR_SCLH +# define I2C1_TIMINGR_SCLH 38U +# endif +# ifndef I2C1_TIMINGR_SCLL +# define I2C1_TIMINGR_SCLL 129U +# endif #endif #ifndef I2C_DRIVER - #define I2C_DRIVER I2CD1 +# define I2C_DRIVER I2CD1 #endif typedef int16_t i2c_status_t; #define I2C_STATUS_SUCCESS (0) -#define I2C_STATUS_ERROR (-1) +#define I2C_STATUS_ERROR (-1) #define I2C_STATUS_TIMEOUT (-2) -void i2c_init(void); +void i2c_init(void); i2c_status_t i2c_start(uint8_t address); i2c_status_t i2c_transmit(uint8_t address, const uint8_t* data, uint16_t length, uint16_t timeout); i2c_status_t i2c_receive(uint8_t address, uint8_t* data, uint16_t length, uint16_t timeout); -i2c_status_t i2c_transmit_receive(uint8_t address, uint8_t * tx_body, uint16_t tx_length, uint8_t * rx_body, uint16_t rx_length); +i2c_status_t i2c_transmit_receive(uint8_t address, uint8_t* tx_body, uint16_t tx_length, uint8_t* rx_body, uint16_t rx_length); i2c_status_t i2c_writeReg(uint8_t devaddr, uint8_t regaddr, const uint8_t* data, uint16_t length, uint16_t timeout); -i2c_status_t i2c_readReg(uint8_t devaddr, uint8_t* regaddr, uint8_t* data, uint16_t length, uint16_t timeout); -void i2c_stop(void); +i2c_status_t i2c_readReg(uint8_t devaddr, uint8_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout); +void i2c_stop(void); diff --git a/drivers/arm/ws2812.c b/drivers/arm/ws2812.c new file mode 100644 index 000000000..fa702fca9 --- /dev/null +++ b/drivers/arm/ws2812.c @@ -0,0 +1,95 @@ +#include "quantum.h" +#include "ws2812.h" +#include "ch.h" +#include "hal.h" + +/* Adapted from https://github.com/bigjosh/SimpleNeoPixelDemo/ */ + +#ifndef NOP_FUDGE +# if defined(STM32F1XX) || defined(STM32F1xx) || defined(STM32F0XX) || defined(STM32F0xx) || defined(STM32F3XX) || defined(STM32F3xx) || defined(STM32L0XX) || defined(STM32L0xx) +# define NOP_FUDGE 0.4 +# else +# error("NOP_FUDGE configuration required") +# define NOP_FUDGE 1 // this just pleases the compile so the above error is easier to spot +# endif +#endif + +#define NUMBER_NOPS 6 +#define CYCLES_PER_SEC (STM32_SYSCLK / NUMBER_NOPS * NOP_FUDGE) +#define NS_PER_SEC (1000000000L) // Note that this has to be SIGNED since we want to be able to check for negative values of derivatives +#define NS_PER_CYCLE (NS_PER_SEC / CYCLES_PER_SEC) +#define NS_TO_CYCLES(n) ((n) / NS_PER_CYCLE) + +#define wait_ns(x) \ + do { \ + for (int i = 0; i < NS_TO_CYCLES(x); i++) { \ + __asm__ volatile("nop\n\t" \ + "nop\n\t" \ + "nop\n\t" \ + "nop\n\t" \ + "nop\n\t" \ + "nop\n\t"); \ + } \ + } while (0) + +// These are the timing constraints taken mostly from the WS2812 datasheets +// These are chosen to be conservative and avoid problems rather than for maximum throughput + +#define T1H 900 // Width of a 1 bit in ns +#define T1L (1250 - T1H) // Width of a 1 bit in ns + +#define T0H 350 // Width of a 0 bit in ns +#define T0L (1250 - T0H) // Width of a 0 bit in ns + +// The reset gap can be 6000 ns, but depending on the LED strip it may have to be increased +// to values like 600000 ns. If it is too small, the pixels will show nothing most of the time. +#define RES 10000 // Width of the low gap between bits to cause a frame to latch + +void sendByte(uint8_t byte) { + // WS2812 protocol wants most significant bits first + for (unsigned char bit = 0; bit < 8; bit++) { + bool is_one = byte & (1 << (7 - bit)); + // using something like wait_ns(is_one ? T1L : T0L) here throws off timings + if (is_one) { + // 1 + writePinHigh(RGB_DI_PIN); + wait_ns(T1H); + writePinLow(RGB_DI_PIN); + wait_ns(T1L); + } else { + // 0 + writePinHigh(RGB_DI_PIN); + wait_ns(T0H); + writePinLow(RGB_DI_PIN); + wait_ns(T0L); + } + } +} + +void ws2812_init(void) { setPinOutput(RGB_DI_PIN); } + +// Setleds for standard RGB +void ws2812_setleds(LED_TYPE *ledarray, uint16_t leds) { + static bool s_init = false; + if (!s_init) { + ws2812_init(); + s_init = true; + } + + // this code is very time dependent, so we need to disable interrupts + chSysLock(); + + for (uint8_t i = 0; i < leds; i++) { + // WS2812 protocol dictates grb order + sendByte(ledarray[i].g); + sendByte(ledarray[i].r); + sendByte(ledarray[i].b); +#ifdef RGBW + sendByte(ledarray[i].w); +#endif + } + + wait_ns(RES); + + chSysUnlock(); +} diff --git a/drivers/arm/ws2812.h b/drivers/arm/ws2812.h new file mode 100644 index 000000000..41c22a00b --- /dev/null +++ b/drivers/arm/ws2812.h @@ -0,0 +1,16 @@ +#pragma once + +#include "quantum/color.h" + +/* User Interface + * + * Input: + * ledarray: An array of GRB data describing the LED colors + * number_of_leds: The number of LEDs to write + * + * The functions will perform the following actions: + * - Set the data-out pin as output + * - Send out the LED data + * - Wait 50us to reset the LEDs + */ +void ws2812_setleds(LED_TYPE *ledarray, uint16_t number_of_leds); diff --git a/drivers/arm/ws2812_pwm.c b/drivers/arm/ws2812_pwm.c new file mode 100644 index 000000000..2094e5009 --- /dev/null +++ b/drivers/arm/ws2812_pwm.c @@ -0,0 +1 @@ +#error("NOT SUPPORTED")
\ No newline at end of file diff --git a/drivers/arm/ws2812_spi.c b/drivers/arm/ws2812_spi.c new file mode 100644 index 000000000..2094e5009 --- /dev/null +++ b/drivers/arm/ws2812_spi.c @@ -0,0 +1 @@ +#error("NOT SUPPORTED")
\ No newline at end of file |