Compare commits
35 Commits
Author | SHA1 | Date |
---|---|---|
![]() |
e917fa2eb4 | 6 years ago |
![]() |
69147de462 | 7 years ago |
![]() |
018b764e6d | 7 years ago |
![]() |
b87dd45ae0 | 7 years ago |
![]() |
16110aff6f | 7 years ago |
![]() |
5a305d9472 | 7 years ago |
![]() |
4c68334ca6 | 7 years ago |
![]() |
5112eab859 | 7 years ago |
![]() |
b088f77fbd | 7 years ago |
![]() |
64230257b0 | 7 years ago |
![]() |
e8bbcaa671 | 7 years ago |
![]() |
05af481c00 | 7 years ago |
![]() |
d3e14c0d43 | 7 years ago |
![]() |
4fdc9badd3 | 7 years ago |
![]() |
af6107bee8 | 7 years ago |
![]() |
d233737c95 | 7 years ago |
![]() |
3e282ab203 | 7 years ago |
![]() |
1c0d85c143 | 7 years ago |
![]() |
7c19e9fa04 | 7 years ago |
![]() |
9fccfc8dd5 | 7 years ago |
![]() |
1cb72a9c59 | 7 years ago |
![]() |
82146ecfc0 | 7 years ago |
![]() |
4a1984d33e | 7 years ago |
![]() |
676080372c | 7 years ago |
![]() |
0af7415981 | 7 years ago |
![]() |
df371458b3 | 7 years ago |
![]() |
e0e5efbead | 7 years ago |
![]() |
edb4460e64 | 7 years ago |
![]() |
fe72bfa070 | 7 years ago |
![]() |
25642c8840 | 7 years ago |
![]() |
03b1904b2e | 7 years ago |
![]() |
bb71a988c2 | 7 years ago |
![]() |
ddee61c9ba | 7 years ago |
![]() |
91efe74365 | 7 years ago |
![]() |
12a64ff24b | 7 years ago |
@ -0,0 +1,204 @@
|
|||||||
|
/*
|
||||||
|
* LEDDriver.c
|
||||||
|
*
|
||||||
|
* Created on: Aug 26, 2013
|
||||||
|
* Author: Omri Iluz
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "ws2812.h"
|
||||||
|
#include "stdlib.h"
|
||||||
|
#include "quantum.h"
|
||||||
|
|
||||||
|
static uint8_t *fb;
|
||||||
|
static int sLeds;
|
||||||
|
static stm32_gpio_t *sPort;
|
||||||
|
static uint32_t sMask;
|
||||||
|
uint8_t* dma_source;
|
||||||
|
static LED_TYPE led_array[RGBLED_NUM];
|
||||||
|
|
||||||
|
void setColor(uint8_t color, uint8_t *buf,uint32_t mask){
|
||||||
|
int i;
|
||||||
|
for (i=0;i<8;i++){
|
||||||
|
buf[i]=((color<<i)&0b10000000?0x0:mask);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void setColorRGB(Color c, uint8_t *buf, uint32_t mask) {
|
||||||
|
setColor(c.G,buf, mask);
|
||||||
|
setColor(c.R,buf+8, mask);
|
||||||
|
setColor(c.B,buf+16, mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initialize Led Driver
|
||||||
|
* @details Initialize the Led Driver based on parameters.
|
||||||
|
* Following initialization, the frame buffer would automatically be
|
||||||
|
* exported to the supplied port and pins in the right timing to drive
|
||||||
|
* a chain of WS2812B controllers
|
||||||
|
* @note The function assumes the controller is running at 72Mhz
|
||||||
|
* @note Timing is critical for WS2812. While all timing is done in hardware
|
||||||
|
* need to verify memory bandwidth is not exhausted to avoid DMA delays
|
||||||
|
*
|
||||||
|
* @param[in] leds length of the LED chain controlled by each pin
|
||||||
|
* @param[in] port which port would be used for output
|
||||||
|
* @param[in] mask Which pins would be used for output, each pin is a full chain
|
||||||
|
* @param[out] o_fb initialized frame buffer
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
void WS2812_init(void) {
|
||||||
|
static uint8_t * p;
|
||||||
|
//uint32_t port = RGBLED_PORT;
|
||||||
|
//ledDriverInit(RGBLED_NUM, (stm32_gpio_t *)(port & 0xFFF0), 1 << (port & 0xF), &p);
|
||||||
|
pin_t rgb_pin = RGB_DI_PIN;
|
||||||
|
ledDriverInit(RGBLED_NUM, PAL_PORT(rgb_pin), 1 << PAL_PAD(rgb_pin), &p);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ledDriverInit(int leds, stm32_gpio_t *port, uint32_t mask, uint8_t **o_fb) {
|
||||||
|
sLeds=leds;
|
||||||
|
sPort=port;
|
||||||
|
sMask=mask;
|
||||||
|
palSetGroupMode(port, sMask, 0, PAL_MODE_OUTPUT_PUSHPULL|PAL_STM32_OSPEED_HIGHEST|PAL_STM32_PUPDR_FLOATING);
|
||||||
|
|
||||||
|
// maybe don't do whole port?
|
||||||
|
// palSetPadMode(port, 8, PAL_MODE_OUTPUT_PUSHPULL|PAL_STM32_OSPEED_HIGHEST|PAL_STM32_PUPDR_FLOATING);
|
||||||
|
|
||||||
|
|
||||||
|
// configure pwm timers -
|
||||||
|
// timer 2 as master, active for data transmission and inactive to disable transmission during reset period (50uS)
|
||||||
|
// timer 3 as slave, during active time creates a 1.25 uS signal, with duty cycle controlled by frame buffer values
|
||||||
|
static PWMConfig pwmc2 = {72000000 / 90, /* 800Khz PWM clock frequency. 1/90 of PWMC3 */
|
||||||
|
(72000000 / 90) * 0.05, /*Total period is 50ms (20FPS), including sLeds cycles + reset length for ws2812b and FB writes */
|
||||||
|
NULL,
|
||||||
|
{ {PWM_OUTPUT_ACTIVE_HIGH, NULL},
|
||||||
|
{PWM_OUTPUT_DISABLED, NULL},
|
||||||
|
{PWM_OUTPUT_DISABLED, NULL},
|
||||||
|
{PWM_OUTPUT_DISABLED, NULL}},
|
||||||
|
TIM_CR2_MMS_2, /* master mode selection */
|
||||||
|
0, };
|
||||||
|
/* master mode selection */
|
||||||
|
static PWMConfig pwmc3 = {72000000,/* 72Mhz PWM clock frequency. */
|
||||||
|
90, /* 90 cycles period (1.25 uS per period @72Mhz */
|
||||||
|
NULL,
|
||||||
|
{ {PWM_OUTPUT_ACTIVE_HIGH, NULL},
|
||||||
|
{PWM_OUTPUT_ACTIVE_HIGH, NULL},
|
||||||
|
{PWM_OUTPUT_ACTIVE_HIGH, NULL},
|
||||||
|
{PWM_OUTPUT_ACTIVE_HIGH, NULL}},
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
};
|
||||||
|
dma_source = chHeapAlloc(NULL, 1);
|
||||||
|
fb = chHeapAlloc(NULL, ((sLeds) * 24)+10);
|
||||||
|
*o_fb=fb;
|
||||||
|
int j;
|
||||||
|
for (j = 0; j < (sLeds) * 24; j++) fb[j] = 0;
|
||||||
|
dma_source[0] = sMask;
|
||||||
|
// DMA stream 2, triggered by channel3 pwm signal. if FB indicates, reset output value early to indicate "0" bit to ws2812
|
||||||
|
dmaStreamAllocate(STM32_DMA1_STREAM2, 10, NULL, NULL);
|
||||||
|
dmaStreamSetPeripheral(STM32_DMA1_STREAM2, &(sPort->BSRR.H.clear));
|
||||||
|
dmaStreamSetMemory0(STM32_DMA1_STREAM2, fb);
|
||||||
|
dmaStreamSetTransactionSize(STM32_DMA1_STREAM2, (sLeds) * 24);
|
||||||
|
dmaStreamSetMode(
|
||||||
|
STM32_DMA1_STREAM2,
|
||||||
|
STM32_DMA_CR_DIR_M2P | STM32_DMA_CR_MINC | STM32_DMA_CR_PSIZE_BYTE
|
||||||
|
| STM32_DMA_CR_MSIZE_BYTE | STM32_DMA_CR_CIRC | STM32_DMA_CR_PL(2));
|
||||||
|
// DMA stream 3, triggered by pwm update event. output high at beginning of signal
|
||||||
|
dmaStreamAllocate(STM32_DMA1_STREAM3, 10, NULL, NULL);
|
||||||
|
dmaStreamSetPeripheral(STM32_DMA1_STREAM3, &(sPort->BSRR.H.set));
|
||||||
|
dmaStreamSetMemory0(STM32_DMA1_STREAM3, dma_source);
|
||||||
|
dmaStreamSetTransactionSize(STM32_DMA1_STREAM3, 1);
|
||||||
|
dmaStreamSetMode(
|
||||||
|
STM32_DMA1_STREAM3, STM32_DMA_CR_TEIE |
|
||||||
|
STM32_DMA_CR_DIR_M2P | STM32_DMA_CR_PSIZE_BYTE | STM32_DMA_CR_MSIZE_BYTE
|
||||||
|
| STM32_DMA_CR_CIRC | STM32_DMA_CR_PL(3));
|
||||||
|
// DMA stream 6, triggered by channel1 update event. reset output value late to indicate "1" bit to ws2812.
|
||||||
|
// always triggers but no affect if dma stream 2 already change output value to 0
|
||||||
|
dmaStreamAllocate(STM32_DMA1_STREAM6, 10, NULL, NULL);
|
||||||
|
dmaStreamSetPeripheral(STM32_DMA1_STREAM6, &(sPort->BSRR.H.clear));
|
||||||
|
dmaStreamSetMemory0(STM32_DMA1_STREAM6, dma_source);
|
||||||
|
dmaStreamSetTransactionSize(STM32_DMA1_STREAM6, 1);
|
||||||
|
dmaStreamSetMode(
|
||||||
|
STM32_DMA1_STREAM6,
|
||||||
|
STM32_DMA_CR_DIR_M2P | STM32_DMA_CR_PSIZE_BYTE | STM32_DMA_CR_MSIZE_BYTE
|
||||||
|
| STM32_DMA_CR_CIRC | STM32_DMA_CR_PL(3));
|
||||||
|
pwmStart(&PWMD2, &pwmc2);
|
||||||
|
pwmStart(&PWMD3, &pwmc3);
|
||||||
|
// set pwm3 as slave, triggerd by pwm2 oc1 event. disables pwmd2 for synchronization.
|
||||||
|
PWMD3.tim->SMCR |= TIM_SMCR_SMS_0 | TIM_SMCR_SMS_2 | TIM_SMCR_TS_0;
|
||||||
|
PWMD2.tim->CR1 &= ~TIM_CR1_CEN;
|
||||||
|
// set pwm values.
|
||||||
|
// 28 (duty in ticks) / 90 (period in ticks) * 1.25uS (period in S) = 0.39 uS
|
||||||
|
pwmEnableChannel(&PWMD3, 2, 28);
|
||||||
|
// 58 (duty in ticks) / 90 (period in ticks) * 1.25uS (period in S) = 0.806 uS
|
||||||
|
pwmEnableChannel(&PWMD3, 0, 58);
|
||||||
|
// active during transfer of 90 cycles * sLeds * 24 bytes * 1/90 multiplier
|
||||||
|
pwmEnableChannel(&PWMD2, 0, 90 * sLeds * 24 / 90);
|
||||||
|
// stop and reset counters for synchronization
|
||||||
|
PWMD2.tim->CNT = 0;
|
||||||
|
// Slave (TIM3) needs to "update" immediately after master (TIM2) start in order to start in sync.
|
||||||
|
// this initial sync is crucial for the stability of the run
|
||||||
|
PWMD3.tim->CNT = 89;
|
||||||
|
PWMD3.tim->DIER |= TIM_DIER_CC3DE | TIM_DIER_CC1DE | TIM_DIER_UDE;
|
||||||
|
dmaStreamEnable(STM32_DMA1_STREAM3);
|
||||||
|
dmaStreamEnable(STM32_DMA1_STREAM6);
|
||||||
|
dmaStreamEnable(STM32_DMA1_STREAM2);
|
||||||
|
// all systems go! both timers and all channels are configured to resonate
|
||||||
|
// in complete sync without any need for CPU cycles (only DMA and timers)
|
||||||
|
// start pwm2 for system to start resonating
|
||||||
|
PWMD2.tim->CR1 |= TIM_CR1_CEN;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ledDriverWaitCycle(void){
|
||||||
|
while (PWMD2.tim->CNT < 90 * sLeds * 24 / 90){chThdSleepMicroseconds(1);};
|
||||||
|
}
|
||||||
|
|
||||||
|
void testPatternFB(uint8_t *fb){
|
||||||
|
int i;
|
||||||
|
Color tmpC = {rand()%256, rand()%256, rand()%256};
|
||||||
|
for (i=0;i<sLeds;i++){
|
||||||
|
setColorRGB(tmpC,fb+24*i, sMask);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ws2812_setleds(LED_TYPE *ledarray, uint16_t number_of_leds) {
|
||||||
|
// uint8_t i = 0;
|
||||||
|
// while (i < number_of_leds) {
|
||||||
|
// ws2812_write_led(i, ledarray[i].r, ledarray[i].g, ledarray[i].b);
|
||||||
|
// i++;
|
||||||
|
// }
|
||||||
|
uint8_t i = 0;
|
||||||
|
while (i < number_of_leds) {
|
||||||
|
setColor(ledarray[i].g, (fb+24*i), sMask);
|
||||||
|
setColor(ledarray[i].r, (fb+24*i)+8, sMask);
|
||||||
|
setColor(ledarray[i].b, (fb+24*i)+16, sMask);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ws2812_setleds_rgbw(LED_TYPE *ledarray, uint16_t number_of_leds) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void WS2812_send_color( uint8_t index ) {
|
||||||
|
setColor(led_array[index].g, (fb+24*index), sMask);
|
||||||
|
setColor(led_array[index].r, (fb+24*index)+8, sMask);
|
||||||
|
setColor(led_array[index].b, (fb+24*index)+16, sMask);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WS2812_set_color( int index, uint8_t red, uint8_t green, uint8_t blue ) {
|
||||||
|
led_array[index].r = red;
|
||||||
|
led_array[index].g = green;
|
||||||
|
led_array[index].b = blue;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WS2812_set_color_all( uint8_t red, uint8_t green, uint8_t blue ) {
|
||||||
|
for (int i = 0; i < RGBLED_NUM; i++) {
|
||||||
|
WS2812_set_color( i, red, green, blue );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WS2812_send_colors(void) {
|
||||||
|
for (int i = 0; i < RGBLED_NUM; i++) {
|
||||||
|
WS2812_send_color( i );
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
Copyright (C) 2013 Omri Iluz
|
||||||
|
2018 Jack Humbert
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "hal.h"
|
||||||
|
#include "rgblight_types.h"
|
||||||
|
|
||||||
|
#define sign(x) (( x > 0 ) - ( x < 0 ))
|
||||||
|
|
||||||
|
typedef struct Color Color;
|
||||||
|
struct Color {
|
||||||
|
uint8_t R;
|
||||||
|
uint8_t G;
|
||||||
|
uint8_t B;
|
||||||
|
};
|
||||||
|
|
||||||
|
void ledDriverInit(int leds, stm32_gpio_t *port, uint32_t mask, uint8_t **o_fb);
|
||||||
|
void setColorRGB(Color c, uint8_t *buf, uint32_t mask);
|
||||||
|
void testPatternFB(uint8_t *fb);
|
||||||
|
void ledDriverWaitCycle(void);
|
||||||
|
|
||||||
|
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( int 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);
|
@ -1,21 +0,0 @@
|
|||||||
# Build Options
|
|
||||||
# change to "no" to disable the options, or define them in the Makefile in
|
|
||||||
# the appropriate keymap folder that will get included automatically
|
|
||||||
#
|
|
||||||
BOOTMAGIC_ENABLE = no # Virtual DIP switch configuration(+1000)
|
|
||||||
MOUSEKEY_ENABLE = no # Mouse keys(+4700)
|
|
||||||
EXTRAKEY_ENABLE = no # Audio control and System control(+450)
|
|
||||||
CONSOLE_ENABLE = yes # Console for debug(+400)
|
|
||||||
COMMAND_ENABLE = yes # Commands for debug and configuration
|
|
||||||
NKRO_ENABLE = yes # Nkey Rollover - if this doesn't work, see here: https://github.com/tmk/tmk_keyboard/wiki/FAQ#nkro-doesnt-work
|
|
||||||
BACKLIGHT_ENABLE = yes # Enable keyboard backlight functionality
|
|
||||||
MIDI_ENABLE = no # MIDI controls
|
|
||||||
AUDIO_ENABLE = yes # Audio output on port C6
|
|
||||||
UNICODE_ENABLE = no # Unicode
|
|
||||||
BLUETOOTH_ENABLE = no # Enable Bluetooth with the Adafruit EZ-Key HID
|
|
||||||
RGBLIGHT_ENABLE = yes # Enable WS2812 RGB underlight.
|
|
||||||
SLEEP_LED_ENABLE = no # Breathing sleep LED during USB suspend
|
|
||||||
|
|
||||||
ifndef QUANTUM_DIR
|
|
||||||
include ../../../../Makefile
|
|
||||||
endif
|
|
Loading…
Reference in new issue