LPC845月饼板上有12颗WS2812数字灯。下面说说如何把这个灯点亮呢。
首先简单说明下WS2812灯,他是RGB 3种颜色,每种颜色8Bit,总共24Bit数据。每个Bit的0,1码是按照高低电平占空比来确定的。这个网上资料很多,不过这个0,1码的占空比大小有好几种。
有的是0码是:H:0.5us,L:2.0us;1码是H:1.2us,L:1.3us。
有的是0码是:H:0.3us,L:0.9us;1码是H:0.6us,L:0.7us。速度比上面的快1倍。
还有其他的我就不多说了。
我就是实现的0码是H:0.3us,L:0.9us;1码是H:0.6us,L:0.7us,这一种情况的。驱动WS2812B常用的方式有SPI、PWM、UASRT、GPIO模拟PWM等方式。本次就是用的SPI模式。使用SPI的mosi引脚驱动WS2812。
根据WS2812的0,1码的占空比确定SPI输出数据,1字节8Bit。
WS_BIT1 0x7C 1码是H:0.6us,L:0.7us
WS_BIT0 0x60 0码是H:0.3us,L:0.9us
根据电路图得到灯的驱动管脚,然后初始化GPIO和SPI。如下图可以看到使用的管脚是PIO1_13。
GPIO和SPI初始化如下,参照上一期LCD屏的SPI初始化。
void drv_spi_gpio_init(void)
{
CLOCK_EnableClock(kCLOCK_Iocon);
CLOCK_EnableClock(kCLOCK_Gpio0);
CLOCK_EnableClock(kCLOCK_Gpio1);
CLOCK_EnableClock(kCLOCK_Spi0);
CLOCK_EnableClock(kCLOCK_Spi1);
CLOCK_Select(kSPI0_Clk_From_MainClk);
CLOCK_Select(kSPI1_Clk_From_MainClk);
RESET_PeripheralReset(kSPI0_RST_N_SHIFT_RSTn);
RESET_PeripheralReset(kSPI1_RST_N_SHIFT_RSTn);
gpio_pin_config_t SPILCD_IN_config = {
.pinDirection = kGPIO_DigitalInput,
.outputLogic = 1U,
};
gpio_pin_config_t SPILCD_IOH_config = {
.pinDirection = kGPIO_DigitalOutput,
.outputLogic = 1U,
};
gpio_pin_config_t SPILCD_IOL_config = {
.pinDirection = kGPIO_DigitalOutput,
.outputLogic = 0U,
};
/* Initialize GPIO functionality on pin */
GPIO_PinInit(GPIO, 0, 7, &SPILCD_IOL_config);//LCD_BLK
GPIO_PinInit(GPIO, 1, 6, &SPILCD_IOH_config);//LCD_DC
GPIO_PinInit(GPIO, 1, 7, &SPILCD_IOH_config);//LCD_RST
GPIO_PinInit(GPIO, 1,18, &SPILCD_IOH_config);//LCD_CS
GPIO_PinInit(GPIO, 1,19, &SPILCD_IOH_config);//LCD_CLK
GPIO_PinInit(GPIO, 0, 6, &SPILCD_IOH_config);//LCD_MOSI
GPIO_PinInit(GPIO, 1,13, &SPILCD_IOH_config);//RGB_MOSI
GPIO_PinInit(GPIO, 1,8, &SPILCD_IOH_config); //FLASH_CS
GPIO_PinInit(GPIO, 1,9, &SPILCD_IN_config); //FLASH_MISO
GPIO_PinInit(GPIO, 0,12, &SPILCD_IOH_config);//FLASH_CLK
GPIO_PinInit(GPIO, 0,13, &SPILCD_IOH_config);//FLASH_MOSI
const uint32_t spilcd_ioc = (/* Selects pull-up function */
IOCON_PIO_MODE_PULLUP |
/* Enable hysteresis */
IOCON_PIO_HYS_EN |
/* Input not invert */
IOCON_PIO_INV_DI |
/* Disables Open-drain function */
IOCON_PIO_OD_DI |
/* Bypass input filter */
IOCON_PIO_SMODE_BYPASS |
/* IOCONCLKDIV0 */
IOCON_PIO_CLKDIV0);
IOCON_PinMuxSet(IOCON, IOCON_INDEX_PIO0_7, spilcd_ioc);
IOCON_PinMuxSet(IOCON, IOCON_INDEX_PIO1_6, spilcd_ioc);
IOCON_PinMuxSet(IOCON, IOCON_INDEX_PIO1_7, spilcd_ioc);
IOCON_PinMuxSet(IOCON, IOCON_INDEX_PIO1_18, spilcd_ioc);
/* Enables clock for switch matrix.: enable */
const uint32_t SPI_LCD_CLK = (/* Selects pull-up function */
IOCON_PIO_MODE_PULLUP |
/* Enable hysteresis */
IOCON_PIO_HYS_EN |
/* Input not invert */
IOCON_PIO_INV_DI |
/* Disables Open-drain function */
IOCON_PIO_OD_DI |
/* Bypass input filter */
IOCON_PIO_SMODE_BYPASS |
/* IOCONCLKDIV0 */
IOCON_PIO_CLKDIV0);
IOCON_PinMuxSet(IOCON, IOCON_INDEX_PIO1_19, SPI_LCD_CLK);
const uint32_t SPI_LCD_MOSI = (/* Selects pull-up function */
IOCON_PIO_MODE_PULLUP |
/* Enable hysteresis */
IOCON_PIO_HYS_EN |
/* Input not invert */
IOCON_PIO_INV_DI |
/* Disables Open-drain function */
IOCON_PIO_OD_DI |
/* Bypass input filter */
IOCON_PIO_SMODE_BYPASS |
/* IOCONCLKDIV0 */
IOCON_PIO_CLKDIV0);
IOCON_PinMuxSet(IOCON, IOCON_INDEX_PIO0_6, SPI_LCD_MOSI);
IOCON_PinMuxSet(IOCON, IOCON_INDEX_PIO1_13, SPI_LCD_MOSI); //RGB_IO=MOSI
const uint32_t SPI_MISO = (/* Selects pull-up function */
0 |
/* Enable hysteresis */
IOCON_PIO_HYS_EN |
/* Input not invert */
IOCON_PIO_INV_DI |
/* Disables Open-drain function */
IOCON_PIO_OD_DI |
/* Bypass input filter */
IOCON_PIO_SMODE_BYPASS |
/* IOCONCLKDIV0 */
IOCON_PIO_CLKDIV0);
IOCON_PinMuxSet(IOCON, IOCON_INDEX_PIO1_9, SPI_MISO); //f_miso
IOCON_PinMuxSet(IOCON, IOCON_INDEX_PIO1_8, spilcd_ioc); //f_cs
IOCON_PinMuxSet(IOCON, IOCON_INDEX_PIO0_12, spilcd_ioc); //f_clk
IOCON_PinMuxSet(IOCON, IOCON_INDEX_PIO0_13, spilcd_ioc); //f_mosi
CLOCK_EnableClock(kCLOCK_Swm);
SWM_SetMovablePinSelect(SWM0, kSWM_SPI0_SCK, kSWM_PortPin_P1_19);
SWM_SetMovablePinSelect(SWM0, kSWM_SPI0_MOSI, kSWM_PortPin_P0_6);
SWM_SetMovablePinSelect(SWM0, kSWM_SPI1_MOSI, kSWM_PortPin_P1_13);
/* Disable clock for switch matrix. */
CLOCK_DisableClock(kCLOCK_Swm);
spi_master_config_t userConfig = {0};
uint32_t srcFreq = 0U;
/* Note: The slave board using interrupt way, slave will spend more time to write data
* to TX register, to prevent TX data missing in slave, we will add some delay between
* frames and capture data at the second edge, this operation will make the slave
* has more time to prapare the data.
*/
SPI_MasterGetDefaultConfig(&userConfig);
userConfig.baudRate_Bps = 30000000;
userConfig.sselNumber = kSPI_Ssel0Assert;
userConfig.clockPolarity = kSPI_ClockPolarityActiveHigh;
userConfig.clockPhase = kSPI_ClockPhaseFirstEdge;
userConfig.direction = kSPI_MsbFirst;
userConfig.delayConfig.preDelay = 0x0U;
userConfig.delayConfig.postDelay = 0x0U;
userConfig.delayConfig.frameDelay = 0x0U;
userConfig.delayConfig.transferDelay = 0x0U;
srcFreq = CLOCK_GetFreq(kCLOCK_MainClk);
SPI_MasterInit(SPI0, &userConfig, srcFreq);
userConfig.baudRate_Bps = 6000000;
SPI_MasterInit(SPI1, &userConfig, srcFreq);
}
SPI_Type * spi_table[2]=
{
SPI0,
SPI1,
};
void spi_pre_h(void)
{
CLOCK_EnableClock(kCLOCK_Swm);
SWM_SetMovablePinSelect(SWM0, kSWM_SPI1_MISO, kSWM_PortPin_P1_9); //F_MISO
SWM_SetMovablePinSelect(SWM0, kSWM_SPI1_MOSI, kSWM_PortPin_P0_13); //F_MOSI
SWM_SetMovablePinSelect(SWM0, kSWM_SPI1_SCK , kSWM_PortPin_P0_12); //F_CLK
CLOCK_DisableClock(kCLOCK_Swm);
SPI_MasterSetBaudRate(SPI1,30000000, CLOCK_GetFreq(kCLOCK_MainClk));
}
void spi_pre_l(void)
{
CLOCK_EnableClock(kCLOCK_Swm);
SWM_SetMovablePinSelect(SWM0, kSWM_SPI1_MOSI, kSWM_PortPin_P1_13); //RGB_IO
SWM_SetMovablePinSelect(SWM0, kSWM_SPI1_MISO, 0XFF); //
SWM_SetMovablePinSelect(SWM0, kSWM_SPI1_SCK , 0XFF); //
CLOCK_DisableClock(kCLOCK_Swm);
SPI_MasterSetBaudRate(SPI1,6000000, CLOCK_GetFreq(kCLOCK_MainClk));
}
ws2812驱动代码如下:
#include "stdint.h"
#include "string.h"
#include "main.h"
#include "drv_ws2812.h"
#include "drv_spi.h"
/*******************************************************************************
* Definitions
******************************************************************************/
#define WS_TRANSFER_SIZE (WS2812_PIXELS*24) // Transfer dataSize
#define WS_BIT 24
#define WS_BIT1 0x7C
#define WS_BIT0 0x60
/*******************************************************************************
* Variables
******************************************************************************/
volatile uint8_t g_WS2812TxBuf[WS_TRANSFER_SIZE]; // WS2812 Strip transmit data buffer
typedef union WS2812_ARGB
{
struct
{
uint8_t b;
uint8_t g;
uint8_t r;
uint8_t a;
}s_argb;
uint32_t u32_rgba;
}WS2812_ARGB_U;
/*
* @brief WS2812_StripInit
* @param NULL
* @return NULL
*/
void WS2812_Init(void)
{
spi_pre_l();
WS2812_ClearData();
WS2812_SendData();
}
/*
* @brief WS2812_PixelSetBuff
* @param n, LED num / c, color / brightness, brightness
* @return NULL
*/
void WS2812_PixelSetIndexColor(uint16_t n, uint32_t color)
{
volatile WS2812_ARGB_U l_color;
uint16_t c_temp;
if(n < WS2812_PIXELS)
{
memset((void *)&g_WS2812TxBuf[n*WS_BIT], WS_BIT0, 24);
l_color.u32_rgba = color;
c_temp = l_color.s_argb.a + 1;
l_color.s_argb.r = (l_color.s_argb.r * c_temp) >> 8;
l_color.s_argb.g = (l_color.s_argb.g * c_temp) >> 8;
l_color.s_argb.b = (l_color.s_argb.b * c_temp) >> 8;
if( ((l_color.s_argb.g>>7)&0x01)) g_WS2812TxBuf[n*WS_BIT+ 0+0] = WS_BIT1;
if( ((l_color.s_argb.g>>6)&0x01)) g_WS2812TxBuf[n*WS_BIT+ 0+1] = WS_BIT1;
if( ((l_color.s_argb.g>>5)&0x01)) g_WS2812TxBuf[n*WS_BIT+ 0+2] = WS_BIT1;
if( ((l_color.s_argb.g>>4)&0x01)) g_WS2812TxBuf[n*WS_BIT+ 0+3] = WS_BIT1;
if( ((l_color.s_argb.g>>3)&0x01)) g_WS2812TxBuf[n*WS_BIT+ 0+4] = WS_BIT1;
if( ((l_color.s_argb.g>>2)&0x01)) g_WS2812TxBuf[n*WS_BIT+ 0+5] = WS_BIT1;
if( ((l_color.s_argb.g>>1)&0x01)) g_WS2812TxBuf[n*WS_BIT+ 0+6] = WS_BIT1;
if( ((l_color.s_argb.g>>0)&0x01)) g_WS2812TxBuf[n*WS_BIT+ 0+7] = WS_BIT1;
if( ((l_color.s_argb.r>>7)&0x01)) g_WS2812TxBuf[n*WS_BIT+ 8+0] = WS_BIT1;
if( ((l_color.s_argb.r>>6)&0x01)) g_WS2812TxBuf[n*WS_BIT+ 8+1] = WS_BIT1;
if( ((l_color.s_argb.r>>5)&0x01)) g_WS2812TxBuf[n*WS_BIT+ 8+2] = WS_BIT1;
if( ((l_color.s_argb.r>>4)&0x01)) g_WS2812TxBuf[n*WS_BIT+ 8+3] = WS_BIT1;
if( ((l_color.s_argb.r>>3)&0x01)) g_WS2812TxBuf[n*WS_BIT+ 8+4] = WS_BIT1;
if( ((l_color.s_argb.r>>2)&0x01)) g_WS2812TxBuf[n*WS_BIT+ 8+5] = WS_BIT1;
if( ((l_color.s_argb.r>>1)&0x01)) g_WS2812TxBuf[n*WS_BIT+ 8+6] = WS_BIT1;
if( ((l_color.s_argb.r>>0)&0x01)) g_WS2812TxBuf[n*WS_BIT+ 8+7] = WS_BIT1;
if( ((l_color.s_argb.b>>7)&0x01)) g_WS2812TxBuf[n*WS_BIT+16+0] = WS_BIT1;
if( ((l_color.s_argb.b>>6)&0x01)) g_WS2812TxBuf[n*WS_BIT+16+1] = WS_BIT1;
if( ((l_color.s_argb.b>>5)&0x01)) g_WS2812TxBuf[n*WS_BIT+16+2] = WS_BIT1;
if( ((l_color.s_argb.b>>4)&0x01)) g_WS2812TxBuf[n*WS_BIT+16+3] = WS_BIT1;
if( ((l_color.s_argb.b>>3)&0x01)) g_WS2812TxBuf[n*WS_BIT+16+4] = WS_BIT1;
if( ((l_color.s_argb.b>>2)&0x01)) g_WS2812TxBuf[n*WS_BIT+16+5] = WS_BIT1;
if( ((l_color.s_argb.b>>1)&0x01)) g_WS2812TxBuf[n*WS_BIT+16+6] = WS_BIT1;
if( ((l_color.s_argb.b>>0)&0x01)) g_WS2812TxBuf[n*WS_BIT+16+7] = WS_BIT1;
}
}
void WS2812_PixelSetIndexRGB(uint16_t n, uint8_t r, uint8_t g, uint8_t b)
{
if(n < WS2812_PIXELS)
{
memset((void *)&g_WS2812TxBuf[n*WS_BIT], WS_BIT0, 24);
if( ((g>>7)&0x01)) g_WS2812TxBuf[n*WS_BIT+ 0+0] = WS_BIT1;
if( ((g>>6)&0x01)) g_WS2812TxBuf[n*WS_BIT+ 0+1] = WS_BIT1;
if( ((g>>5)&0x01)) g_WS2812TxBuf[n*WS_BIT+ 0+2] = WS_BIT1;
if( ((g>>4)&0x01)) g_WS2812TxBuf[n*WS_BIT+ 0+3] = WS_BIT1;
if( ((g>>3)&0x01)) g_WS2812TxBuf[n*WS_BIT+ 0+4] = WS_BIT1;
if( ((g>>2)&0x01)) g_WS2812TxBuf[n*WS_BIT+ 0+5] = WS_BIT1;
if( ((g>>1)&0x01)) g_WS2812TxBuf[n*WS_BIT+ 0+6] = WS_BIT1;
if( ((g>>0)&0x01)) g_WS2812TxBuf[n*WS_BIT+ 0+7] = WS_BIT1;
if( ((r>>7)&0x01)) g_WS2812TxBuf[n*WS_BIT+ 8+0] = WS_BIT1;
if( ((r>>6)&0x01)) g_WS2812TxBuf[n*WS_BIT+ 8+1] = WS_BIT1;
if( ((r>>5)&0x01)) g_WS2812TxBuf[n*WS_BIT+ 8+2] = WS_BIT1;
if( ((r>>4)&0x01)) g_WS2812TxBuf[n*WS_BIT+ 8+3] = WS_BIT1;
if( ((r>>3)&0x01)) g_WS2812TxBuf[n*WS_BIT+ 8+4] = WS_BIT1;
if( ((r>>2)&0x01)) g_WS2812TxBuf[n*WS_BIT+ 8+5] = WS_BIT1;
if( ((r>>1)&0x01)) g_WS2812TxBuf[n*WS_BIT+ 8+6] = WS_BIT1;
if( ((r>>0)&0x01)) g_WS2812TxBuf[n*WS_BIT+ 8+7] = WS_BIT1;
if( ((b>>7)&0x01)) g_WS2812TxBuf[n*WS_BIT+16+0] = WS_BIT1;
if( ((b>>6)&0x01)) g_WS2812TxBuf[n*WS_BIT+16+1] = WS_BIT1;
if( ((b>>5)&0x01)) g_WS2812TxBuf[n*WS_BIT+16+2] = WS_BIT1;
if( ((b>>4)&0x01)) g_WS2812TxBuf[n*WS_BIT+16+3] = WS_BIT1;
if( ((b>>3)&0x01)) g_WS2812TxBuf[n*WS_BIT+16+4] = WS_BIT1;
if( ((b>>2)&0x01)) g_WS2812TxBuf[n*WS_BIT+16+5] = WS_BIT1;
if( ((b>>1)&0x01)) g_WS2812TxBuf[n*WS_BIT+16+6] = WS_BIT1;
if( ((b>>0)&0x01)) g_WS2812TxBuf[n*WS_BIT+16+7] = WS_BIT1;
}
}
/*
* @brief strip_clear, clean-up LED strip display
* @param NULL
* @return NULL
*/
void WS2812_ClearData(void)
{
memset((void *)&g_WS2812TxBuf[0], WS_BIT0, (WS_TRANSFER_SIZE));//
}
/*
* @brief color_dim dimme color
* @param color, number of LEDs
* @return dimmed color
*/
void WS2812_SendData(void)
{
volatile uint32_t i;
spi_pre_l();
for(i=0; i<WS_TRANSFER_SIZE; i++)
{
spi_writebyte(1,g_WS2812TxBuf[i]);
}
spi_pre_h();
}
void WS2812_ShowRGB(uint8_t r,uint8_t g,uint8_t b)
{
uint8_t i;
for(i=0; i<WS2812_PIXELS; i++)
{
WS2812_PixelSetIndexRGB(i, r,g,b);
}
WS2812_SendData();
}
void WS2812_ShowColor(uint32_t color)
{
uint8_t i;
for(i=0; i<WS2812_PIXELS; i++)
{
WS2812_PixelSetIndexColor(i, color);
}
WS2812_SendData();
}
void WS2812_ShowBuff(uint8_t *buff)
{
uint8_t i;
uint8_t *pLedData = buff;
for(i=0; i<WS2812_PIXELS; i++)
{
WS2812_PixelSetIndexRGB(i, pLedData[0],pLedData[1],pLedData[2]);
pLedData += 3;
}
WS2812_SendData();
}
好了,下面就可以开始测试WS2812效果了。
写了2种效果,一个时钟模式,一个转圈模式
uint8_t clock_color_level[8]=
{
0x10,
0x30,
0x70,
0xa0,
0xff,
0,0,0,
};
uint8_t _hour,_minute,_second;
void ws2812rgb_clock_fsm(fsm_pt_t *pt)
{
static int i;
WS2812_ARGB_U rgb;
PT_BEGIN(pt);
_hour = 5;
_minute = 5;
_second = 40;
while(1)
{
for(i=0;i<12;i++)
{
if(_second/5 == i) rgb.s_argb.r = clock_color_level[_second%5];
else rgb.s_argb.r = 0;
if(_minute/5 == i) rgb.s_argb.g = clock_color_level[_minute%5];
else rgb.s_argb.g = 0;
if(_hour == i) rgb.s_argb.b = 0xff;
else rgb.s_argb.b = 0;
rgb.s_argb.a = rgb_light;
WS2812_PixelSetIndexColor(i,rgb.u32_rgba);
}
WS2812_SendData();
PT_WAIT_TIME(pt,1000); //延时1秒
_second++;
if(_second == 60)
{
_minute ++;
_second = 0;
if(_minute == 60)
{
_minute = 0;
_hour ++;
if(_hour == 12)
{
_hour = 0;
}
}
}
}
PT_END(pt);
}
void ws2812rgb_demo_fsm(fsm_pt_t *pt)
{
static int i;
PT_BEGIN(pt);
while(1)
{
for(i=0;i<12;i++)
{
WS2812_PixelSetIndexColor(i,WS2812_RED|(rgb_light<<24));
WS2812_SendData();
PT_WAIT_TIME(pt,100);
}
for(i=0;i<12;i++)
{
WS2812_PixelSetIndexColor(i,WS2812_GREEN|(rgb_light<<24));
WS2812_SendData();
PT_WAIT_TIME(pt,100);
}
for(i=0;i<12;i++)
{
WS2812_PixelSetIndexColor(i,WS2812_BLUE|(rgb_light<<24));
WS2812_SendData();
PT_WAIT_TIME(pt,100);
}
}
PT_END(pt);
}
main函数
int main(void)
{
BOARD_InitBootClocks();
SysTick_Config(SystemCoreClock / 1000U);
init_cycle_counter(true);
BOARD_InitBootPins();
drv_gpio_init();
BOARD_InitDebugConsole();
drv_spi_gpio_init();
drv_i2c_gpio_init();
DbgConsole_Printf("LPC845 Mooncake Board Test!rn");
spilcd_init();
__cycleof__("lcd_fresh_time(n/30000=X ms):")
{
spilcd_fill(0,0,240,240,LCD_RED);
}
delay_ms(100);
__cycleof__("lcd_fresh_time(n/30000=X ms):")
{
spilcd_fill(0,0,240,240,LCD_GREEN);
}
delay_ms(100);
__cycleof__("lcd_fresh_time(n/30000=X ms):")
{
spilcd_fill(0,0,240,240,LCD_BLUE);
}
delay_ms(100);
__cycleof__("show 40*40 picture time:")
{
spilcd_draw_bitmap(100,100,40,40,(uint8_t *)gImage_1);
}
delay_ms(100);
rgb_light = 0xff;
WS2812_Init();
while(1)
{
keyscan_fsm(&pt_key_scan);
资料如下:
lpc845_mooncake.zip (15.77 MB)