~ruther/qmk_firmware

0f507f01696eae0e8fe808d17a19db3f6d9e2ce4 — Andrew Kannan 6 years ago d912041
Practice60 RGB and PWM Backlight (#4929)

* Update Practice60 to enable RGB via SPI DMA and use PWM backlight breathing

* Correct stm32f103c8t6 flash size in eeprom definition

* Remove unused files and improve ifdef checks

* Update quantum/rgblight.c

Co-Authored-By: awkannan <andrew.kannan@klaviyo.com>

* Update quantum/rgblight.c

Co-Authored-By: awkannan <andrew.kannan@klaviyo.com>

* EEPROM implementation fix and updated p60 code

* Update define

* Remove dead code

* Update keymap to remove test key

* Update keymap again
M drivers/avr/ws2812.c => drivers/avr/ws2812.c +2 -2
@@ 128,11 128,11 @@ unsigned char I2C_Write(unsigned char c)
        c <<= 1;
    }

    

    I2C_WriteBit(0);
    _delay_us(I2C_DELAY);
    _delay_us(I2C_DELAY);
  

    // _delay_us(I2C_DELAY);
    //return I2C_ReadBit();
    return 0;

M keyboards/handwired/practice60/bootloader_defs.h => keyboards/handwired/practice60/bootloader_defs.h +1 -1
@@ 7,4 7,4 @@

// STM32F103* does NOT have an USB bootloader in ROM (only serial),
//  so setting anything here does not make much sense
// #define STM32_BOOTLOADER_ADDRESS 0x1FFFC800
#define STM32_BOOTLOADER_ADDRESS 0x80000000

M keyboards/handwired/practice60/config.h => keyboards/handwired/practice60/config.h +12 -1
@@ 35,7 35,9 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
#define MATRIX_ROW_PINS { B3, B4, B5, B6, B7 }
#define DIODE_DIRECTION COL2ROW

#define BACKLIGHT_LEVELS 1
#define BACKLIGHT_LEVELS 6
#define BACKLIGHT_BREATHING
#define BREATHING_PERIOD 6

/* define if matrix has ghost */
//#define MATRIX_HAS_GHOST


@@ 48,6 50,15 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
/* Locking resynchronize hack */
#define LOCKING_RESYNC_ENABLE

#define RGBLIGHT_ANIMATIONS

#define WS2812_LED_N 9
#define RGBLED_NUM WS2812_LED_N
#define PORT_WS2812     GPIOB
#define PIN_WS2812      15
#define WS2812_SPI SPID2


/*
 * Feature disable options
 *  These options are also useful to firmware size reduction.

M keyboards/handwired/practice60/halconf.h => keyboards/handwired/practice60/halconf.h +1 -1
@@ 111,7 111,7 @@
 * @brief   Enables the PWM subsystem.
 */
#if !defined(HAL_USE_PWM) || defined(__DOXYGEN__)
#define HAL_USE_PWM                 FALSE
#define HAL_USE_PWM                 TRUE
#endif

/**

D keyboards/handwired/practice60/hsv2rgb.c => keyboards/handwired/practice60/hsv2rgb.c +0 -80
@@ 1,80 0,0 @@
/* hsv2rgb.c
 * Integer only conversion functions between HSV and RGB
 */

#include "hsv2rgb.h"

// TODO fix these buggy macros
#define max(x,y) ((x>y) ? x:y)
#define min(x,y) ((x>y) ? y:x)
#define min3(x,y,z) (min(min(x,y),z))
#define max3(x,y,z) (max(max(x,y),z))


rgb_color hsv2rgb(hsv_color hsv)
{
  // From : http://qscribble.blogspot.fr/2008/06/integer-conversion-from-hsl-to-rgb.html
  int h = hsv.h;
  int s = hsv.s;
  int v = hsv.v;
  rgb_color rgb = {0, 0, 0};

  if (v == 0)
    return rgb;

  // sextant = 0 .. 5
  int sextant = (h*6)/256;
  // f = 0 .. 42
  int f = h - (sextant*256)/6;

  int p = (v * (256 - s))/256;
  int q = (v * (256*43 - s*f))/(256*43);
  int t = (v * (256*43 - s*(43-f)))/(256*43);

  // Corrige les erreurs dues aux arrondis
  p = max(min(p, 255), 0);
  q = max(min(q, 255), 0);
  t = max(min(t, 255), 0);

  switch(sextant){
  case 0: rgb.r = v; rgb.g = t; rgb.b = p; break;
  case 1: rgb.r = q; rgb.g = v; rgb.b = p; break;
  case 2: rgb.r = p; rgb.g = v; rgb.b = t; break;
  case 3: rgb.r = p; rgb.g = q; rgb.b = v; break;
  case 4: rgb.r = t; rgb.g = p; rgb.b = v; break;
  default:rgb.r = v; rgb.g = p; rgb.b = q; break;
  }
  return rgb;
}


hsv_color rgb2hsv(rgb_color rgb)
{
  // From : http://www.ruinelli.ch/rgb-to-hsv
  hsv_color hsv = {0, 0, 0};
  int min, max, delta;

  min = min3(rgb.r, rgb.g, rgb.b);
  max = max3(rgb.r, rgb.g, rgb.b);

  if(max==0) {
    hsv.h = 0;
    hsv.s = 0;
    hsv.v = 0;
    return hsv;
  }

  hsv.v = max;
  delta = max - min;

  hsv.s = (delta)*255 / max;

  if(rgb.r == max)
    hsv.h = (rgb.g - rgb.b)*42/delta;        // between yellow & magenta
  else if(rgb.g == max)
    hsv.h = 120 + (rgb.b - rgb.r)*42/delta;    // between cyan & yellow
  else
    hsv.h = 240 + (rgb.r - rgb.g)*42/delta;    // between magenta & cyan

  return hsv;
}
\ No newline at end of file

D keyboards/handwired/practice60/hsv2rgb.h => keyboards/handwired/practice60/hsv2rgb.h +0 -23
@@ 1,23 0,0 @@
/* hsv2rgb.h
 * Convert Hue Saturation Value to Red Green Blue
 *
 * Programme de convertion d'une information HSV en RGB
 */
#ifndef HSV2RGB_H
#define HSV2RGB_H

typedef struct {
  unsigned char h;
  unsigned char s;
  unsigned char v;
} hsv_color;

typedef struct {
  unsigned char r;
  unsigned char g;
  unsigned char b;
} rgb_color;

rgb_color hsv2rgb(hsv_color hsv);

#endif
\ No newline at end of file

M keyboards/handwired/practice60/keymaps/default/keymap.c => keyboards/handwired/practice60/keymaps/default/keymap.c +3 -3
@@ 32,16 32,16 @@ enum custom_keycodes {
const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
  [_BASE] = LAYOUT_60_ansi(
    KC_GESC, KC_1,    KC_2,    KC_3,    KC_4,    KC_5,    KC_6,                 KC_7,    KC_8,    KC_9,    KC_0,    KC_MINS, KC_EQL,   KC_BSPC, \
    KC_TAB,  KC_Q,    KC_W,    KC_E,    KC_R,    KC_T,    MT(MOD_LSFT, KC_Y),   KC_U,    KC_I,    KC_O,    KC_P,    KC_LBRC, KC_RBRC,  KC_BSLS, \
    KC_TAB,  KC_Q,    KC_W,    KC_E,    KC_R,    KC_T,    KC_Y,   KC_U,    KC_I,    KC_O,    KC_P,    KC_LBRC, KC_RBRC,  KC_BSLS, \
    KC_CAPS, KC_A,    KC_S,    KC_D,    KC_F,    KC_G,    KC_H,                 KC_J,    KC_K,    KC_L,    KC_SCLN, KC_QUOT,           KC_ENT,  \
    KC_LSFT, KC_Z,    KC_X,    KC_C,    KC_V,    KC_B,    KC_N,                 KC_M,    KC_COMM, KC_DOT,  KC_SLSH,                    KC_RSFT, \
    KC_LCTL, KC_LGUI, KC_LALT,                            KC_SPC,                                          KC_RALT, KC_RGUI, MO(_FN1), KC_RCTL
  ),

  [_FN1] = LAYOUT_60_ansi(
    KC_GESC, KC_F1,   KC_F2,   KC_F3,   KC_F4,   KC_F5,   KC_F6,   KC_F7,   KC_F8,   KC_F9,   KC_F10,  KC_F11,  KC_F12,  KC_BSPC, \
    KC_GESC, KC_F1,   KC_F2,   KC_F3,   KC_F4,   KC_F5,   KC_F6,   KC_F7,   KC_F8,   KC_F9,   KC_F10,  KC_F11,  KC_F12,  KC_DEL, \
    RGB_TOG, RGB_MOD, KC_UP,   _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, \
    _______, KC_LEFT, KC_DOWN, KC_RGHT, _______, _______, _______, _______, _______, _______, _______, _______,          _______, \
    BL_BRTG, KC_LEFT, KC_DOWN, KC_RGHT, _______, _______, _______, _______, _______, _______, _______, _______,          _______, \
    BL_INC,  BL_DEC,  BL_TOGG, _______, _______, _______, _______, _______, _______, _______, _______,                   _______, \
    KC_GRV,  _______, _______,                            _______,                            _______, _______, _______, _______
  )

M keyboards/handwired/practice60/led.c => keyboards/handwired/practice60/led.c +213 -7
@@ 18,34 18,240 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
#include "hal.h"
#include "backlight.h"
#include "led.h"
#include "led_custom.h"
#include "printf.h"

static void breathing_callback(PWMDriver *pwmp);

static PWMConfig pwmCFG = {
  0xFFFF,                              /* PWM clock frequency  */
  256,                              /* PWM period (in ticks) 1S (1/10kHz=0.1mS 0.1ms*10000 ticks=1S) */
  NULL,                               /* No Callback */
  {
      {PWM_OUTPUT_ACTIVE_HIGH, NULL}, /* Enable Channel 0 */
      {PWM_OUTPUT_DISABLED, NULL},
      {PWM_OUTPUT_DISABLED, NULL},
      {PWM_OUTPUT_DISABLED, NULL}
  },
  0,                                  /* HW dependent part.*/
  0
};

static PWMConfig pwmCFG_breathing = {
  0xFFFF,                              /* 10kHz PWM clock frequency  */
  256,                              /* PWM period (in ticks) 1S (1/10kHz=0.1mS 0.1ms*10000 ticks=1S) */
  breathing_callback,                               /* Breathing Callback */
  {
      {PWM_OUTPUT_ACTIVE_HIGH, NULL}, /* Enable Channel 0 */
      {PWM_OUTPUT_DISABLED, NULL},
      {PWM_OUTPUT_DISABLED, NULL},
      {PWM_OUTPUT_DISABLED, NULL}
  },
  0,                                  /* HW dependent part.*/
  0
};

// See http://jared.geek.nz/2013/feb/linear-led-pwm
static uint16_t cie_lightness(uint16_t v) {
  if (v <= 5243) // if below 8% of max
    return v / 9; // same as dividing by 900%
  else {
    uint32_t y = (((uint32_t) v + 10486) << 8) / (10486 + 0xFFFFUL); // add 16% of max and compare
    // to get a useful result with integer division, we shift left in the expression above
    // and revert what we've done again after squaring.
    y = y * y * y >> 8;
    if (y > 0xFFFFUL) // prevent overflow
      return 0xFFFFU;
    else
      return (uint16_t) y;
  }
}


void backlight_init_ports(void) {
    printf("backlight_init_ports()\n");
    #ifdef BACKLIGHT_ENABLE
    palSetPadMode(GPIOA, 8, PAL_MODE_OUTPUT_PUSHPULL);
    palSetPad(GPIOA, 8);

    palSetPadMode(GPIOA, 8, PAL_MODE_STM32_ALTERNATE_PUSHPULL);
    pwmStart(&PWMD1, &pwmCFG);
    pwmEnableChannel(&PWMD1, 0, PWM_FRACTION_TO_WIDTH(&PWMD1, 0xFFFF,cie_lightness(0xFFFF)));
    #endif
}

void backlight_set(uint8_t level) {
    printf("backlight_set(%d)\n", level);
    #ifdef BACKLIGHT_ENABLE
    uint32_t duty = (uint32_t)(cie_lightness(0xFFFF * (uint32_t) level / BACKLIGHT_LEVELS));
    printf("duty: (%d)\n", duty);
    if (level == 0) {
        // Turn backlight off
        palSetPad(GPIOA, 8);
        pwmDisableChannel(&PWMD1, 0);
    } else {
        // Turn backlight on
        palClearPad(GPIOA, 8);
      // Turn backlight on
      if(!is_breathing()){
        pwmEnableChannel(&PWMD1, 0, PWM_FRACTION_TO_WIDTH(&PWMD1,0xFFFF,duty));
      }
    }
    #endif
}


uint8_t backlight_tick = 0;

void backlight_task(void) {
}

#define BREATHING_NO_HALT  0
#define BREATHING_HALT_OFF 1
#define BREATHING_HALT_ON  2
#define BREATHING_STEPS 128

static uint8_t breathing_period = BREATHING_PERIOD;
static uint8_t breathing_halt = BREATHING_NO_HALT;
static uint16_t breathing_counter = 0;

bool is_breathing(void) {
    return PWMD1.config == &pwmCFG_breathing;
}

#define breathing_min() do {breathing_counter = 0;} while (0)
#define breathing_max() do {breathing_counter = breathing_period * 256 / 2;} while (0)


void breathing_interrupt_enable(void){
    pwmStop(&PWMD1);
    printf("starting with callback\n");
    pwmStart(&PWMD1, &pwmCFG_breathing);
    chSysLockFromISR();
    pwmEnablePeriodicNotification(&PWMD1);
    pwmEnableChannelI(
      &PWMD1,
      0,
      PWM_FRACTION_TO_WIDTH(
        &PWMD1,
        0xFFFF,
        0xFFFF
      )
    );
    chSysUnlockFromISR();
}

void breathing_interrupt_disable(void){
    pwmStop(&PWMD1);
    printf("starting without callback\n");
    pwmStart(&PWMD1, &pwmCFG);
}

void breathing_enable(void)
{
  printf("breathing_enable()\n");
  breathing_counter = 0;
  breathing_halt = BREATHING_NO_HALT;
  breathing_interrupt_enable();
}

void breathing_pulse(void)
{
    if (get_backlight_level() == 0)
      breathing_min();
    else
      breathing_max();
    breathing_halt = BREATHING_HALT_ON;
    breathing_interrupt_enable();
}

void breathing_disable(void)
{
    printf("breathing_disable()\n");
    breathing_interrupt_disable();
    // Restore backlight level
    backlight_set(get_backlight_level());
}

void breathing_self_disable(void)
{
  if (get_backlight_level() == 0)
    breathing_halt = BREATHING_HALT_OFF;
  else
    breathing_halt = BREATHING_HALT_ON;
}

void breathing_toggle(void) {
  if (is_breathing()){
    printf("disable breathing\n");
    breathing_disable();
  } else {
    printf("enable breathing\n");
    breathing_enable();
  }
}

void breathing_period_set(uint8_t value)
{
  if (!value)
    value = 1;
  breathing_period = value;
}

void breathing_period_default(void) {
  breathing_period_set(BREATHING_PERIOD);
}

void breathing_period_inc(void)
{
  breathing_period_set(breathing_period+1);
}

void breathing_period_dec(void)
{
  breathing_period_set(breathing_period-1);
}

/* To generate breathing curve in python:
 * from math import sin, pi; [int(sin(x/128.0*pi)**4*255) for x in range(128)]
 */
static const uint8_t breathing_table[BREATHING_STEPS] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 3, 4, 5, 6, 8, 10, 12, 15, 17, 20, 24, 28, 32, 36, 41, 46, 51, 57, 63, 70, 76, 83, 91, 98, 106, 113, 121, 129, 138, 146, 154, 162, 170, 178, 185, 193, 200, 207, 213, 220, 225, 231, 235, 240, 244, 247, 250, 252, 253, 254, 255, 254, 253, 252, 250, 247, 244, 240, 235, 231, 225, 220, 213, 207, 200, 193, 185, 178, 170, 162, 154, 146, 138, 129, 121, 113, 106, 98, 91, 83, 76, 70, 63, 57, 51, 46, 41, 36, 32, 28, 24, 20, 17, 15, 12, 10, 8, 6, 5, 4, 3, 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

// Use this before the cie_lightness function.
static inline uint16_t scale_backlight(uint16_t v) {
  return v / BACKLIGHT_LEVELS * get_backlight_level();
}

static void breathing_callback(PWMDriver *pwmp)
{
  (void)pwmp;
  uint16_t interval = (uint16_t) breathing_period * 256 / BREATHING_STEPS;
  // resetting after one period to prevent ugly reset at overflow.
  breathing_counter = (breathing_counter + 1) % (breathing_period * 256);
  uint8_t index = breathing_counter / interval % BREATHING_STEPS;

  if (((breathing_halt == BREATHING_HALT_ON) && (index == BREATHING_STEPS / 2)) ||
      ((breathing_halt == BREATHING_HALT_OFF) && (index == BREATHING_STEPS - 1)))
  {
      breathing_interrupt_disable();
  }

  uint32_t duty = cie_lightness(scale_backlight(breathing_table[index] * 256));

  chSysLockFromISR();
  pwmEnableChannelI(
    &PWMD1,
    0,
    PWM_FRACTION_TO_WIDTH(
      &PWMD1,
      0xFFFF,
      duty
    )
  );
  chSysUnlockFromISR();
}


void led_set(uint8_t usb_led)
{
    if (usb_led & (1<<USB_LED_CAPS_LOCK)) {
        palClearPad(GPIOC, 13);
      palSetPad(GPIOC, 13);
    } else {
    	palSetPad(GPIOC, 13);
    	palClearPad(GPIOC, 13);
    }
}

A keyboards/handwired/practice60/led_custom.h => keyboards/handwired/practice60/led_custom.h +6 -0
@@ 0,0 1,6 @@
#pragma once

void backlight_task(void);
void breathing_interrupt_disable(void);
void breathing_interrupt_enable(void);
bool is_breathing(void);

M keyboards/handwired/practice60/mcuconf.h => keyboards/handwired/practice60/mcuconf.h +1 -1
@@ 132,7 132,7 @@
 * PWM driver system settings.
 */
#define STM32_PWM_USE_ADVANCED              FALSE
#define STM32_PWM_USE_TIM1                  FALSE
#define STM32_PWM_USE_TIM1                  TRUE
#define STM32_PWM_USE_TIM2                  FALSE
#define STM32_PWM_USE_TIM3                  FALSE
#define STM32_PWM_USE_TIM4                  FALSE

M keyboards/handwired/practice60/practice60.c => keyboards/handwired/practice60/practice60.c +9 -6
@@ 3,8 3,7 @@

#include "ch.h"
#include "hal.h"

#include "underglow.h"
#include "led_custom.h"
#include "print.h"
#include "debug.h"
#include "util.h"


@@ 19,14 18,18 @@
void matrix_init_kb(void){
      /* MOSI pin*/
    palSetPadMode(GPIOB, 15, PAL_MODE_STM32_ALTERNATE_PUSHPULL);

    LED_ON();
    palSetPad(GPIOA, 8);
    wait_ms(500);
    palClearPad(GPIOA, 8);
    LED_OFF();

#ifdef RGBLIGHT_ENABLE
    leds_init();
#endif
}


void matrix_scan_kb(void)
{
  #ifdef RGBLIGHT_ENABLE
    rgblight_task();
  #endif
}

M keyboards/handwired/practice60/rules.mk => keyboards/handwired/practice60/rules.mk +2 -2
@@ 1,7 1,6 @@
# project specific files
SRC =	led.c \
		  underglow.c \
		  hsv2rgb.c
		  ws2812.c

# GENERIC STM32F103C8T6 board - stm32duino bootloader
OPT_DEFS = -DCORTEX_VTOR_INIT=0x2000


@@ 50,6 49,7 @@ COMMAND_ENABLE = yes    # Commands for debug and configuration
SLEEP_LED_ENABLE = yes  # Breathing sleep LED during USB suspend
NKRO_ENABLE = yes	    # USB Nkey Rollover
BACKLIGHT_ENABLE = yes
RGBLIGHT_ENABLE = yes

LAYOUTS = 60_ansi


D keyboards/handwired/practice60/underglow.h => keyboards/handwired/practice60/underglow.h +0 -10
@@ 1,10 0,0 @@
#pragma once

#include "hsv2rgb.h"

void set_leds_color_hsv(hsv_color color);
void set_leds_color_rgb(rgb_color color);
void set_led_color_hsv(hsv_color color, int pos);
void set_led_color_rgb(rgb_color color, int pos);

void leds_init(void);

R keyboards/handwired/practice60/underglow.c => keyboards/handwired/practice60/ws2812.c +44 -69
@@ 1,8 1,12 @@
#include "ch.h"
#include "hal.h"
/*
 * LEDDriver.c
 *
 *  Created on: Aug 26, 2013
 *      Author: Omri Iluz
 */

#include "hsv2rgb.h"
#include "underglow.h"
#include "ws2812.h"
#include "stdlib.h"

#define BYTES_FOR_LED_BYTE 4
#define NB_COLORS 3


@@ 10,18 14,17 @@
#define DATA_SIZE BYTES_FOR_LED*NB_LEDS
#define RESET_SIZE 200
#define PREAMBLE_SIZE 4

// Define the spi your LEDs are plugged to here
#define LEDS_SPI SPID2
#define WS2812_SPI SPID2
// Define the number of LEDs you wish to control in your LED strip
#define NB_LEDS 8
#define NB_LEDS RGBLED_NUM

#define LED_SPIRAL 1
 #define LED_SPIRAL 1

static uint8_t txbuf[PREAMBLE_SIZE + DATA_SIZE + RESET_SIZE];
 static uint8_t txbuf[PREAMBLE_SIZE + DATA_SIZE + RESET_SIZE];
static uint8_t get_protocol_eq(uint8_t data, int pos);

/*
 /*
 * This lib is meant to be used asynchronously, thus the colors contained in
 * the txbuf will be sent in loop, so that the colors are always the ones you
 * put in the table (the user thus have less to worry about)


@@ 37,38 40,18 @@ static THD_WORKING_AREA(LEDS_THREAD_WA, 128);
static THD_FUNCTION(ledsThread, arg) {
  (void) arg;
  while(1){
    spiSend(&LEDS_SPI, PREAMBLE_SIZE + DATA_SIZE + RESET_SIZE, txbuf);
  }
}

#if LED_SPIRAL
/*
 * 'Led spiral' is a simple demo in which we put all the leds to the same
 * color, where this color does all the hsv circle in loop.
 * If you want to launch the thread that will chage the led colors to the
 * appropriate value, simply set LED_SPIRAL to 1.
 */
static THD_WORKING_AREA(HSVTRANS_WA, 128);
static THD_FUNCTION(hsv_transThread, arg){
  (void) arg;
  hsv_color color = {0, 255, 127};
  while(1){
    color.h += 1;
    color.h %= 256;
    set_leds_color_hsv(color);
    chThdSleepMilliseconds(50);
    spiSend(&WS2812_SPI, PREAMBLE_SIZE + DATA_SIZE + RESET_SIZE, txbuf);
  }
}
#endif

static const SPIConfig spicfg = {
 static const SPIConfig spicfg = {
  NULL,
  GPIOB,
  15,
  SPI_CR1_BR_1|SPI_CR1_BR_0 // baudrate : fpclk / 8 => 1tick is 0.32us
  PORT_WS2812,
  PIN_WS2812,
  SPI_CR1_BR_1|SPI_CR1_BR_0 // baudrate : fpclk / 8 => 1tick is 0.32us (2.25 MHz)
};

/*
 /*
 * Function used to initialize the driver.
 *
 * Starts by shutting off all the LEDs.


@@ 77,21 60,19 @@ static const SPIConfig spicfg = {
 * txbuff values)
 */
void leds_init(void){
  /* MOSI pin*/
  palSetPadMode(PORT_WS2812, PIN_WS2812, PAL_MODE_STM32_ALTERNATE_PUSHPULL);
  for(int i = 0; i < RESET_SIZE; i++)
    txbuf[DATA_SIZE+i] = 0x00;
  for (int i=0; i<PREAMBLE_SIZE; i++)
    txbuf[i] = 0x00;
  spiAcquireBus(&LEDS_SPI);              /* Acquire ownership of the bus.    */
  spiStart(&LEDS_SPI, &spicfg);          /* Setup transfer parameters.       */
  spiSelect(&LEDS_SPI);                  /* Slave Select assertion.          */
  spiAcquireBus(&WS2812_SPI);              /* Acquire ownership of the bus.    */
  spiStart(&WS2812_SPI, &spicfg);          /* Setup transfer parameters.       */
  spiSelect(&WS2812_SPI);                  /* Slave Select assertion.          */
  chThdCreateStatic(LEDS_THREAD_WA, sizeof(LEDS_THREAD_WA),NORMALPRIO, ledsThread, NULL);
#if LED_SPIRAL
  chThdCreateStatic(HSVTRANS_WA, sizeof(HSVTRANS_WA),
      NORMALPRIO, hsv_transThread, NULL);
#endif
}

/*
 /*
 * As the trick here is to use the SPI to send a huge pattern of 0 and 1 to
 * the ws2812b protocol, we use this helper function to translate bytes into
 * 0s and 1s for the LED (with the appropriate timing).


@@ 109,20 90,20 @@ static uint8_t get_protocol_eq(uint8_t data, int pos){
  return eq;
}

/*
 * If you want to set a LED's color in the HSV color space, simply call this
 * function with a hsv_color containing the desired color and the index of the
 * led on the LED strip (starting from 0, the first one being the closest the
 * first plugged to the board)
 *
 * Only set the color of the LEDs through the functions given by this API
 * (unless you really know what you are doing)
 */
void set_led_color_hsv(hsv_color color, int pos){
  set_led_color_rgb(hsv2rgb(color), pos);

 void WS2812_init(void) {
  leds_init();
}

/*
 void ws2812_setleds(LED_TYPE *ledarray, uint16_t number_of_leds) {
  uint8_t i = 0;
  while (i < number_of_leds) {
    set_led_color_rgb(ledarray[i], i);
    i++;
  }
}

 /*
 * If you want to set a LED's color in the RGB color space, simply call this
 * function with a hsv_color containing the desired color and the index of the
 * led on the LED strip (starting from 0, the first one being the closest the


@@ 131,7 112,7 @@ void set_led_color_hsv(hsv_color color, int pos){
 * Only set the color of the LEDs through the functions given by this API
 * (unless you really know what you are doing)
 */
void set_led_color_rgb(rgb_color color, int pos){
void set_led_color_rgb(LED_TYPE color, int pos){
  for(int j = 0; j < 4; j++)
    txbuf[PREAMBLE_SIZE + BYTES_FOR_LED*pos + j] = get_protocol_eq(color.g, j);
  for(int j = 0; j < 4; j++)


@@ 140,18 121,12 @@ void set_led_color_rgb(rgb_color color, int pos){
    txbuf[PREAMBLE_SIZE + BYTES_FOR_LED*pos + BYTES_FOR_LED_BYTE*2+j] = get_protocol_eq(color.b, j);
}

/*
 * Same as the two above, but sets all the LEDs in the LED strip (HSV)
 */
void set_leds_color_hsv(hsv_color color){
 void set_leds_color_rgb(LED_TYPE color){
  for(int i = 0; i < NB_LEDS; i++)
    set_led_color_hsv(color, i);
    set_led_color_rgb(color, i);
}

/*
 * Same as the two above, but sets all the LEDs in the LED strip (RGB)
 */
void set_leds_color_rgb(rgb_color color){
  for(int i = 0; i < NB_LEDS; i++)
    set_led_color_rgb(color, i);
}
\ No newline at end of file

 void ws2812_setleds_rgbw(LED_TYPE *ledarray, uint16_t number_of_leds) {

 }

A keyboards/handwired/practice60/ws2812.h => keyboards/handwired/practice60/ws2812.h +20 -0
@@ 0,0 1,20 @@
#pragma once

#include "hal.h"
#include "rgblight_types.h"


void set_leds_color_rgb(LED_TYPE color);
void set_led_color_rgb(LED_TYPE color, int pos);
void leds_init(void);


 // This is what users will use to interface with this
void ws2812_setleds(LED_TYPE *ledarray, uint16_t number_of_leds);
void ws2812_setleds_rgbw(LED_TYPE *ledarray, uint16_t number_of_leds);


void WS2812_init(void);
void WS2812_set_color( uint8_t index, uint8_t red, uint8_t green, uint8_t blue );
void WS2812_set_color_all( uint8_t red, uint8_t green, uint8_t blue );
void WS2812_send_colors(void);

M quantum/rgblight.c => quantum/rgblight.c +8 -3
@@ 19,6 19,11 @@
  #include <avr/eeprom.h>
  #include <avr/interrupt.h>
#endif
#ifdef STM32_EEPROM_ENABLE
  #include "hal.h"
  #include "eeprom.h"
  #include "eeprom_stm32.h"
#endif
#include "wait.h"
#include "progmem.h"
#include "timer.h"


@@ 120,14 125,14 @@ void setrgb(uint8_t r, uint8_t g, uint8_t b, LED_TYPE *led1) {


uint32_t eeconfig_read_rgblight(void) {
  #ifdef __AVR__
  #if defined(__AVR__) || defined(STM32_EEPROM_ENABLE) || defined(PROTOCOL_ARM_ATSAM) || defined(EEPROM_SIZE)
    return eeprom_read_dword(EECONFIG_RGBLIGHT);
  #else
    return 0;
  #endif
}
void eeconfig_update_rgblight(uint32_t val) {
  #ifdef __AVR__
  #if defined(__AVR__) || defined(STM32_EEPROM_ENABLE) || defined(PROTOCOL_ARM_ATSAM) || defined(EEPROM_SIZE)
  if (eeconfig_read_rgblight() != val) {
    eeprom_update_dword(EECONFIG_RGBLIGHT, val);
  }


@@ 333,7 338,7 @@ void rgblight_disable_noeeprom(void) {
#ifdef RGBLIGHT_USE_TIMER
    rgblight_timer_disable();
#endif
  _delay_ms(50);
  wait_ms(50);
  rgblight_set();
}


M tmk_core/common/chibios/eeprom_stm32.c => tmk_core/common/chibios/eeprom_stm32.c +6 -11
@@ 75,17 75,13 @@ uint16_t EEPROM_WriteDataByte (uint16_t Address, uint8_t DataByte) {
    }

    // calculate which page is affected (Pagenum1/Pagenum2...PagenumN)
    page = (FEE_PAGE_BASE_ADDRESS + FEE_ADDR_OFFSET(Address)) & 0x00000FFF;

    if (page % FEE_PAGE_SIZE) page = page + FEE_PAGE_SIZE;
    page = (page / FEE_PAGE_SIZE) - 1;
    page = FEE_ADDR_OFFSET(Address) / FEE_PAGE_SIZE;

    // if current data is 0xFF, the byte is empty, just overwrite with the new one
    if ((*(__IO uint16_t*)(FEE_PAGE_BASE_ADDRESS + FEE_ADDR_OFFSET(Address))) == FEE_EMPTY_WORD) {

        FlashStatus = FLASH_ProgramHalfWord(FEE_PAGE_BASE_ADDRESS + FEE_ADDR_OFFSET(Address), (uint16_t)(0x00FF & DataByte));
    }
    else {
    } else {

        // Copy Page to a buffer
        memcpy(DataBuf, (uint8_t*)FEE_PAGE_BASE_ADDRESS + (page * FEE_PAGE_SIZE), FEE_PAGE_SIZE); // !!! Calculate base address for the desired page


@@ 96,18 92,17 @@ uint16_t EEPROM_WriteDataByte (uint16_t Address, uint8_t DataByte) {
        }

        // manipulate desired data byte in temp data array if new byte is differ to the current
        DataBuf[FEE_ADDR_OFFSET(Address)] = DataByte;
        DataBuf[FEE_ADDR_OFFSET(Address) % FEE_PAGE_SIZE] = DataByte;

        //Erase Page
        FlashStatus = FLASH_ErasePage(FEE_PAGE_BASE_ADDRESS + page);
        FlashStatus = FLASH_ErasePage(FEE_PAGE_BASE_ADDRESS + (page * FEE_PAGE_SIZE));

        // Write new data (whole page) to flash if data has beed changed
        // Write new data (whole page) to flash if data has been changed
        for(i = 0; i < (FEE_PAGE_SIZE / 2); i++) {
            if ((__IO uint16_t)(0xFF00 | DataBuf[FEE_ADDR_OFFSET(i)]) != 0xFFFF) {
                FlashStatus = FLASH_ProgramHalfWord((FEE_PAGE_BASE_ADDRESS + (page * FEE_PAGE_SIZE)) + (i * 2), (uint16_t)(0xFF00 | DataBuf[FEE_ADDR_OFFSET(i)]));
            }
        }

    }
    return FlashStatus;
}


@@ 168,7 163,7 @@ void eeprom_update_word (uint16_t *Address, uint16_t Value)
uint32_t eeprom_read_dword (const uint32_t *Address)
{
    const uint16_t p = (const uint32_t) Address;
    return EEPROM_ReadDataByte(p) | (EEPROM_ReadDataByte(p+1) << 8) 
    return EEPROM_ReadDataByte(p) | (EEPROM_ReadDataByte(p+1) << 8)
        | (EEPROM_ReadDataByte(p+2) << 16) | (EEPROM_ReadDataByte(p+3) << 24);
}