STM32F 驱动WS2812B (1) IO口

STM32F107通过IO翻转驱动WS2812BLED灯组


基本原理

根据WS2812B的datasheet,这个灯组的控制方式是单IO控制,以高电平的时间不同来判定当前的数据是“1”还是“0”。如下图所示:
在这里插入图片描述
在这里插入图片描述
从图中可以看出0 code对应一个高电平400ns低电平850ns的脉冲,而1 code对应一个高电平800ns,低电平450ns的脉冲,无论0 code还是1 code的脉冲周期都是1250ns。还有一个reset电平是一个大于50us的低电平表示reset。
显示1个LED灯珠的数据需要24位,GRB3种颜色分别占8位,假设现在有3个灯珠,每个灯需要显示红色也就是R=0xFF需要发送如下的数据:
RESET电平 -> LED1(G(00),R(FF),B(00)) -> LED2(G(00),R(FF),B(00)) -> LED3(G(00),R(FF),B(00))

基本思路

第一步是实现1 code跟0 code的脉冲,这里用IO口反转的方式实现,使用nop命令达到ns的延时,通过写BSRR跟BRR来实现输出高低电平,具体实现如下:
1 code:

#define WS_WRITE_ONE 	GPIOB->BSRR = 4;  \
	__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();\
	__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();\
	__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();\
	__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();\
	__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();\
	__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();\
	__nop();__nop();__nop();__nop();\
	GPIOB->BRR  = 4; \
	__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();\
	__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();\
	__nop();__nop();__nop();__nop();__nop();__nop()

逻辑分析仪抓波形:
1 code的波形:高电平750ns,低电平417ns,根据数据手册高电平800ns±150ns,低电平450ns±150ns在范围之内
在这里插入图片描述

0 code:

#define WS_WRITE_ZERO 	GPIOB->BSRR = 4; \
	__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();\
	__nop();__nop();__nop();__nop();__nop();__nop();\
	__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();\
	__nop();__nop();__nop();__nop();__nop();__nop();	\
	GPIOB->BRR  = 4; \
	__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();\
	__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();\
	__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();\
	__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();\
	__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();\
	__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();\
	__nop();__nop()

逻辑分析仪抓波形:
0 code的波形:高电平417ns,低电平792ns,根据数据手册高电平400ns±150ns,低电平850ns±150ns在范围之内
在这里插入图片描述
对于RESET信号,直接通过for循环完成:

void RGB_LED_Reset(void)
{
	GPIOB->BRR  = 4;
	for(int i=0;i<480;i++)
	{
		__nop();__nop();__nop();__nop();
	}
}

代码实现

以一个灯为例,一个灯需要24个0 code或者1 code的脉冲加上reset信号的低电平组成,首先定义一个24*LED_NUM大小的数组用来存放灯的脉冲数据:

#define LED_NUM 1
uint8_t io_data[24*LED_NUM]={0};

其次需要将传入的color值分解以下,传入的color是RGB格式的,由于ws2812b传输格式是GRB所以先处理G数据:

for(i=15;i>=8;i--)//G
{
	(((color>>i)&0x01)==1)?(io_data[index*24+(15-i)]=1):(io_data[index*24+(15-i)]=0);
}

G的数据是从color的第15bit开始到8bit结束,每次获取到bit需要判断是不是1,如果是1就需要在io_data的相应位置填入1,否则填入0.
用相同的方式处理R,B的数据:

for(i=23;i>=16;i--)//R
{
	(((color>>i)&0x01)==1)?(io_data[index*24+(31-i)]=1):(io_data[index*24+(31-i)]=0);
}
for(i=7;i>=0;i--)//B
{
	(((color>>i)&0x01)==1)?(io_data[index*24+(23-i)]=1):(io_data[index*24+(23-i)]=0);
}

使用一个函数循环发送出去:

void led_show(void)
{
	RGB_LED_Reset();
	for(int i=0;i<LED_NUM*24;i++)
	{
		if(io_data[i])
		{
			WS_WRITE_ONE;
		}
		else
		{
			WS_WRITE_ZERO;
		}
	}
}

总结

优点:

  1. 使用单IO口,不用选择外设。
  2. 程序简单,只需要填充所有显示的数据之后发送出去就行
    缺点:
  3. 有一定的硬延时会对系统造成影响。
    针对这个缺点,用逻辑分析仪抓取波形观察:
    在这里插入图片描述

这个抓的波形是显示256个灯的时候,程序中的延时给的是10ms,但实际抓出来的是17.9ms,通过计算256241.25=7680us差不多是,跟抓到的波形符合。所以这种方式不能用在需要时间很准的地方。如果显示的灯数量不多可以忽略这个问题。

关键源码

#define WS_WRITE_ONE 	GPIOB->BSRR = 4;  \
	__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();\
	__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();\
	__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();\
	__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();\
	__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();\
	__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();\
	__nop();__nop();__nop();__nop();\
	GPIOB->BRR  = 4; \
	__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();\
	__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();\
	__nop();__nop();__nop();__nop();__nop();__nop()
	
#define WS_WRITE_ZERO 	GPIOB->BSRR = 4; \
	__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();\
	__nop();__nop();__nop();__nop();__nop();__nop();\
	__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();\
	__nop();__nop();__nop();__nop();__nop();__nop();	\
	GPIOB->BRR  = 4; \
	__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();\
	__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();\
	__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();\
	__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();\
	__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();\
	__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();\
	__nop();__nop()

#define LED_NUM 1
uint8_t io_data[24*LED_NUM]={0};
void RGB_LED_Reset(void)
{
	GPIOB->BRR  = 4;
	for(int i=0;i<480;i++)
	{
		__nop();__nop();__nop();__nop();
	}
}
void led_show(void)
{
	HAL_SuspendTick();
	RGB_LED_Reset();
	for(int i=0;i<LED_NUM*24;i++)
	{
		if(io_data[i])
		{
			WS_WRITE_ONE;
		}
		else
		{
			WS_WRITE_ZERO;
		}
	}
	HAL_ResumeTick();
}
void show_dot(unsigned int index,unsigned int color)
{
	int i=0;
	for(i=15;i>=8;i--)//G
	{
		(((color>>i)&0x01)==1)?(io_data[index*24+(15-i)]=1):(io_data[index*24+(15-i)]=0);
	}
	for(i=23;i>=16;i--)//R
	{
		(((color>>i)&0x01)==1)?(io_data[index*24+(31-i)]=1):(io_data[index*24+(31-i)]=0);
	}
	for(i=7;i>=0;i--)//B
	{
		(((color>>i)&0x01)==1)?(io_data[index*24+(23-i)]=1):(io_data[index*24+(23-i)]=0);
	}
	led_show();
}
int main(void)
{
  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();
  while (1)
  {
		show_dot(0,0x00000f);
		HAL_Delay(500);
  }
}
  • 1
    点赞
  • 1
    评论
  • 3
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值