前面已经实现了通过DMA方式改变脉冲PWM占空比和个数。本次就利用这个实现WS2812彩灯输出。
首先我们要知道WS2812的驱动方式。WS2812彩灯是通过不同脉冲高电平的占空比来标识0和1的BIT位的。
BIT1:是高低占空比时间为1us-0.25us(800K速率);BIT1:是高低占空比时间为2us-0.5us(400K速率);
BIT0:是高低占空比时间为0.25us-1us(800K速率);BIT0:是高低占空比时间为0.5us-2us(400K速率);
这里要注意,BIT0或者BIT1主要是高电平脉冲要精准,误差要小。而占空比中低电平时间不需要特别精准。意思就是说BIT1的高电平要在1us左右,误差+-0.2us,但是低电平可以0.25us到2us左右都可以识别为BIT1.BIT0也一样。主要是靠高电平时间宽度识别。
WS2812一个RGB彩灯需要3个字节24BIT,RGB3种颜色各1字节8BIT。
这里我初始化了PWM的频率为500K。TIM定时器系统频率48MHz,计数96个为一周期500KHz。
下面定义WS2812的BIT位占空比数值:
#define WS_BIT1 48 //1us-1.25us
#define WS_BIT0 12 //0.25us-0.5us
#define WS2812_PIXELS 8 // WS2812 LEDs 数量
#define WS_BIT 24u //一个RGB灯的BIT位数
#define WS_TRANSFER_SIZE ((WS2812_PIXELS) * (WS_BIT)) // Transfer dataSize
__ALIGNED(4)
volatile uint8_t g_WS2812TxBuf[WS_TRANSFER_SIZE]; // WS2812 Strip transmit data buffer
发送数据:
void WS2812_SendData(void)
{
HAL_TIM_PWM_Start_DMA(&htim3,TIM_CHANNEL_1,(uint32_t *)g_WS2812TxBuf,WS_TRANSFER_SIZE);
}
发送数据完成中断回调:
void WS2812_SendDataCpl_cb(void)
{
TIM3->CCR1 = 0;
if(ws2812_send_lock) ws2812_send_lock = 0;
}
static void _WS2812_SetPointColor(uint8_t *ptr, uint8_t r, uint8_t g, uint8_t b,uint8_t val)
{
if(r)
{
if( ((r>>7)&0x01)) ptr[0+0] = val;
if( ((r>>6)&0x01)) ptr[0+1] = val;
if( ((r>>5)&0x01)) ptr[0+2] = val;
if( ((r>>4)&0x01)) ptr[0+3] = val;
if( ((r>>3)&0x01)) ptr[0+4] = val;
if( ((r>>2)&0x01)) ptr[0+5] = val;
if( ((r>>1)&0x01)) ptr[0+6] = val;
if( ((r>>0)&0x01)) ptr[0+7] = val;
}
if(g)
{
if( ((g>>7)&0x01)) ptr[8+0] = val;
if( ((g>>6)&0x01)) ptr[8+1] = val;
if( ((g>>5)&0x01)) ptr[8+2] = val;
if( ((g>>4)&0x01)) ptr[8+3] = val;
if( ((g>>3)&0x01)) ptr[8+4] = val;
if( ((g>>2)&0x01)) ptr[8+5] = val;
if( ((g>>1)&0x01)) ptr[8+6] = val;
if( ((g>>0)&0x01)) ptr[8+7] = val;
}
if(b)
{
if( ((b>>7)&0x01)) ptr[16+0] = val;
if( ((b>>6)&0x01)) ptr[16+1] = val;
if( ((b>>5)&0x01)) ptr[16+2] = val;
if( ((b>>4)&0x01)) ptr[16+3] = val;
if( ((b>>3)&0x01)) ptr[16+4] = val;
if( ((b>>2)&0x01)) ptr[16+5] = val;
if( ((b>>1)&0x01)) ptr[16+6] = val;
if( ((b>>0)&0x01)) ptr[16+7] = val;
}
}
void WS2812_PixelSetIndexRGB(uint16_t n, uint8_t r, uint8_t g, uint8_t b)
{
if(n < WS2812_PIXELS)
{
uint32_t i;
for(i=0;i<WS_BIT;i++)
{
g_WS2812TxBuf[n*WS_BIT + i] = WS_BIT0;
}
_WS2812_SetPointColor((uint8_t *)&g_WS2812TxBuf[n*WS_BIT],r,g,b,WS_BIT1);
}
}
void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance == TIM3)
{
if(pwm_lock) pwm_lock = 0;
WS2812_SendDataCpl_cb();
}
}
这样就可以驱动WS2812彩灯了。
在main中调用WS2812驱动接口就可以驱动彩灯了。
引脚脉冲:
下面就是驱动8个RGB彩灯效果:
|