Created
March 13, 2016 22:19
-
-
Save vooon/b4a7d36cadf995ffab54 to your computer and use it in GitHub Desktop.
Code snippet of WS2812b smart led driver for STM32F373 & ChibiOS
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#pragma once | |
typedef struct { | |
uint8_t r; | |
uint8_t g; | |
uint8_t b; | |
} rgb_t; | |
void wsInit(void); | |
/** | |
* Set pixel color | |
* | |
* @param[in] n pixel number | |
*/ | |
void wsSet(size_t n, rgb_t rgb); | |
/** | |
* Send framebuffer to leds. | |
* | |
* Suspend thread until done. | |
*/ | |
msg_t wsSync(void); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "ch.h" | |
#include "hal.h" | |
#include "ws2812b.h" | |
#include "fw_common.h" | |
#include <string.h> | |
/* | |
* DMA + TIM code has some problems on F373 since i enable other perith drivers. | |
* This is alternative driver wich uses USART3 module and one DMA channel. | |
* | |
* We use data inversion, pin inversion & Rx/Tx swap features of F3's USARTs. | |
* For best timings we need 7N1 mode, but STM32 do not support it. So we emulate 7N2 by 8N1 mode. | |
* WS2812 datasheet allow longer 0 state (+600 usec) so additional 400 usec should be ok. | |
* | |
* @code | |
* Timing: | |
* "0" "1" "0" | |
* S 0 1 2 3 4 5 6 7 P | |
* _ ___ _ | |
* _! !___! !_! !_____ | |
* @endcode | |
*/ | |
#define NR_LEDS 8 | |
#define NR_PORTS 2 | |
#define FB_INIT 0b00100100 /* framebuffer initial pattern */ | |
#define BYTES_PER_LED 8 /* 24 bit word / 3 bit per byte */ | |
static uint8_t ws_fb[NR_PORTS][BYTES_PER_LED * (NR_LEDS / NR_PORTS)]; | |
static thread_reference_t ws_thread = NULL; | |
/* PB8/PB9 - USART3 Tx/Rx */ | |
static USART_TypeDef *ws_usart = USART3; | |
static const stm32_dma_stream_t *ws_dmatx = STM32_DMA_STREAM(STM32_UART_USART3_TX_DMA_STREAM); | |
static void ws_usart_stop(void) | |
{ | |
dmaStreamDisable(ws_dmatx); | |
ws_usart->CR1 = 0; | |
ws_usart->CR2 = 0; | |
ws_usart->CR3 = 0; | |
} | |
/** | |
* USART3 IRQ handler. (we use TC to wake up therad) | |
* | |
* @isr | |
*/ | |
OSAL_IRQ_HANDLER(STM32_USART3_HANDLER) | |
{ | |
OSAL_IRQ_PROLOGUE(); | |
uint32_t cr1 = ws_usart->CR1; | |
// Reading and clearing status | |
uint32_t isr = ws_usart->ISR; | |
ws_usart->ICR = isr; | |
// TC - we are done. stop all | |
if ((isr & USART_ISR_TC) && (cr1 & USART_CR1_TCIE)) { | |
// disable TC interrupt | |
ws_usart->CR1 = cr1 & ~USART_CR1_TCIE; | |
// Disconnect DOUT lines from USART, make it OUT PP | |
GPIOB->MODER = (GPIOB->MODER & 0xfff0ffff) | (0b0101 << 16); | |
// stop USART | |
ws_usart_stop(); | |
// wake up thread | |
osalSysLockFromISR(); | |
osalThreadResumeI(&ws_thread, MSG_OK); | |
osalSysUnlockFromISR(); | |
} | |
OSAL_IRQ_EPILOGUE(); | |
} | |
static void ws_sync_one(size_t port) | |
{ | |
chDbgAssert(port < NR_PORTS, "wrong port"); | |
osalSysLock(); | |
ws_usart_stop(); | |
/* Connect port to USART: PB8 & PB9 | |
* Faster than regular PAL functions, but not portable. | |
* | |
* DOUT0 -> PB9 OUT : PB8 ALT(7) | |
* DOUT1 -> PB8 ALT(7) : PB8 OUT | |
*/ | |
GPIOB->MODER = (GPIOB->MODER & 0xfff0ffff) | ((port == 1)? (0b1001 << 16) : (0b0110 << 16)); | |
GPIOB->AFRH = (GPIOB->AFRH & 0xffffff00) | ((port == 1)? (7 << 4) : (7 << 0)); | |
/* Setting baudrate to 2.5 Mbit => bit time is 400 usec | |
* | |
* Usual function [1] does not work, because BRR must be greater than 15. | |
* So we use OVER8 [2] mode and choose nearest USARTDIV = 28 -> 2571428 Bit/s or 388 usec. | |
* | |
* [1]: BRR = STM32_USART3CLK / 2500000UL | |
* [2]: USARTDIV = BRR[15:4] | BRR[2:0] << 1 | |
*/ | |
ws_usart->BRR = 0b10110; | |
// Reset any pending status flags | |
ws_usart->ICR = 0xffffffffU; | |
// Enable inversion, check port by Rx/Tx swap | |
ws_usart->CR2 = USART_CR2_DATAINV | USART_CR2_TXINV | USART_CR2_RXINV | ((port == 1)? USART_CR2_SWAP : 0); | |
ws_usart->CR3 = USART_CR3_DMAT; | |
// Enable USART, TC must be enabled later | |
ws_usart->CR1 = USART_CR1_UE | USART_CR1_TE | USART_CR1_OVER8; | |
// Setup DMA transfer | |
dmaStreamSetMemory0(ws_dmatx, ws_fb[port]); | |
dmaStreamSetPeripheral(ws_dmatx, &ws_usart->TDR); | |
dmaStreamSetTransactionSize(ws_dmatx, sizeof(ws_fb[port])); | |
dmaStreamSetMode(ws_dmatx, STM32_DMA_CR_DIR_M2P | STM32_DMA_CR_MINC | | |
STM32_DMA_CR_MSIZE_BYTE | STM32_DMA_CR_PSIZE_BYTE | | |
STM32_DMA_CR_PL(STM32_UART_USART3_DMA_PRIORITY)); | |
// enable TC | |
ws_usart->ICR = USART_ICR_TCCF; | |
ws_usart->CR1 |= USART_CR1_TCIE; | |
// Start transfer | |
dmaStreamEnable(ws_dmatx); | |
// wait for completion | |
osalThreadSuspendS(&ws_thread); | |
osalSysUnlock(); | |
} | |
msg_t wsSync(void) | |
{ | |
ws_sync_one(0); | |
ws_sync_one(1); | |
return MSG_OK; | |
} | |
static inline void fb_set(size_t p, size_t n, rgb_t rgb) | |
{ | |
uint32_t grb = rgb.g << 16 | rgb.r << 8 | rgb.b; | |
for (size_t i = n * BYTES_PER_LED; i < (n + 1) * BYTES_PER_LED; i++, grb <<= 3) { | |
uint8_t b3 = FB_INIT; // 0b00100100 | |
if (grb & 0x800000) b3 |= 0b00000001; | |
if (grb & 0x400000) b3 |= 0b00001000; | |
if (grb & 0x200000) b3 |= 0b01000000; | |
ws_fb[p][i] = b3; | |
} | |
} | |
void wsSet(size_t n, rgb_t rgb) | |
{ | |
const size_t nsplit = NR_LEDS / NR_PORTS; | |
if (n < nsplit) // 0..3 -> WS_DOUT1 | |
fb_set(0, n, rgb); | |
else if (n >= nsplit && n < NR_LEDS) // 4..7 -> WS_DOUT2 | |
fb_set(1, n - nsplit, rgb); | |
} | |
void wsInit(void) | |
{ | |
// Set GPIO to initial state | |
const uint16_t mask = (1 << GPIOB_WS_DOUT1) | (1 << GPIOB_WS_DOUT2); | |
palClearPort(GPIOB, mask); | |
palSetGroupMode(GPIOB, mask, 0, PAL_MODE_OUTPUT_PUSHPULL | PAL_STM32_OSPEED_HIGHEST | PAL_STM32_PUPDR_FLOATING); | |
// Framebuffer initial state: three 0 bits | |
memset(ws_fb, FB_INIT, sizeof(ws_fb)); | |
// alloc TX DMA stream | |
bool b = dmaStreamAllocate(ws_dmatx, STM32_UART_USART3_IRQ_PRIORITY, NULL, NULL); | |
osalDbgAssert(!b, "stream allocated"); | |
rccEnableUSART3(false); | |
nvicEnableVector(STM32_USART3_NUMBER, STM32_UART_USART3_IRQ_PRIORITY); | |
// USART initial state | |
ws_usart_stop(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment