GPIO模拟时序控制外设1——WS2812B

这篇具有很好参考价值的文章主要介绍了GPIO模拟时序控制外设1——WS2812B。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言

上一篇文章中介绍了整个板子的最基本功能模块——使用GPIO的通用输入输出实现简单的按键输入以及推挽输出控制的功能。本文深入一步,在只使用GPIO的输入输出功能的基础上,通过查看对应模块的芯片手册,模拟其对应的通信时序来驱动对应的模块。

WS2812B

首先来个网红模块——WS2812B的彩灯,它在RGB灯的邻域可以说是一方霸主的存在,内部集成了驱动,可以实现三色(255 * 255 * 255=16777216种颜色)的全真色彩,且支持串行控制,单片机可以通过一个GPIO实现对一组灯的控制。详细的特征可以看芯片手册的介绍。
ws2812b,STM32小项目实战,stm32,单片机,嵌入式硬件,arm开发,经验分享
ws2812b,STM32小项目实战,stm32,单片机,嵌入式硬件,arm开发,经验分享

1.模块简介

ws2812b,STM32小项目实战,stm32,单片机,嵌入式硬件,arm开发,经验分享
通过手册的产品概述,可以看出其大致的控制逻辑:
1.单个灯需要一个24位的数据来控制;
2.同时控制多个点时,需要根据串联的灯个数来发送对应个数的24bit控制数据;
3.其复位时间需要280us以上的时间。
ws2812b,STM32小项目实战,stm32,单片机,嵌入式硬件,arm开发,经验分享
上面提到了一个24bit的控制数据,那么这个24bit的数据每一位代表的含义是什么呢?
在数据手册中也有介绍,如下图所示:24bit的数据是由 绿 红 蓝 三个颜色的色度数据拼接而成的,当数据是0xFF 00 00时,对应的灯会亮起绿色,当数据是0x 00 FF 00时小灯是红色,当数据是0x 00 00 FF 时小灯是蓝色。根据数值的不同可以组合出不同的颜色。
ws2812b,STM32小项目实战,stm32,单片机,嵌入式硬件,arm开发,经验分享
这里给个颜色表供大家去查找自己想要的颜色——https://tool.oschina.net/commons?type=3
ws2812b,STM32小项目实战,stm32,单片机,嵌入式硬件,arm开发,经验分享
需要注意的是,写入的24bit数据的顺序是G R B (绿红蓝),而在此查询到的颜色数据是R G B(红绿蓝),在控制时需要调换一下顺序。
那么到此是不是就可以开始写代码了呢?

2.时序介绍

答案是否定的,为了提高数据传输的稳定性,尽可能的消除信号传输过程中的干扰;模块会采取特殊的输出格式来区分电平的0与1。在上一篇的GPIO输入输出中,STM32的“0”就是对应低电平,“1”就是对应高电平,但是对于WS2812B来说,“0”与“1”对应的并不是单纯的高低电平,而是需要根据如下的时序波形图:
ws2812b,STM32小项目实战,stm32,单片机,嵌入式硬件,arm开发,经验分享
直接看这个描述可能会有点抽象,这里笔者用逻辑分析仪抓取了一段波形,我们一起结合上面的描述来分析一下:
ws2812b,STM32小项目实战,stm32,单片机,嵌入式硬件,arm开发,经验分享
这是使用STM32模拟时序来控制一个RGB显示113355的蓝色的数据脚的实际采样波形。根据上面的时序波形图做个对照,
ws2812b,STM32小项目实战,stm32,单片机,嵌入式硬件,arm开发,经验分享
可以依次读出整段波形发送的数据内容是
0011 0011 0001 0001 0101 0101
分别对应三个颜色 G R B ()绿 红 蓝
而波形的最开头和最后结尾的一个很长的低电平就是对应的复位信号。
最终逻辑分析仪解析出来的数据如下图所示:
ws2812b,STM32小项目实战,stm32,单片机,嵌入式硬件,arm开发,经验分享
至此,就已经基本上那个搞清楚了WS2812B单个灯的通信时序了。唯一还没确定的数据是发送0和1分别对应的具体高低电平时间,时序图中给定的是一个范围,实际使用时需要自己进行微调,这个我们放到后面的代码编写中再定。
搞定了单个灯的控制,接下里就是多个灯的串行控制了
ws2812b,STM32小项目实战,stm32,单片机,嵌入式硬件,arm开发,经验分享
根据上图的数据传输逻辑,可以知道,控制多个灯时,只需要连续发送多组24位数据就可以了,比如说要控制八个灯,就一次发送8个24位数据,中间不断,当需要修改时,在最后加一个大于280us的复位信号即可。
下图中,红色部分是实际发送数据的波形,中间的低电平端就是复位信号。
ws2812b,STM32小项目实战,stm32,单片机,嵌入式硬件,arm开发,经验分享
为了方便理解,我们将上图中中间的那一段红色波形给放大一点,此时就可以看出,一次发送了两个24bit的控制数据,此时会有两个串联的小灯被点亮。
ws2812b,STM32小项目实战,stm32,单片机,嵌入式硬件,arm开发,经验分享
好了,基本捋清控制的思路了,接下来再来看看手册中的其他需要注意的参数。

3.硬件介绍

每个灯珠都是如下所示的四个脚位,其中,VDD与VSS是提供电源的,DIN是输入信号,而DOUT则是数据输出脚,需要一次控制多个灯时,只需要将前一个灯的数据输出脚与后一个灯的数据输入脚连接在一起即可实现串行控制。这里我们可以对照引脚介绍图,找到对应的管脚,方便使用逻辑分析仪抓取波形以及其他的调试。
ws2812b,STM32小项目实战,stm32,单片机,嵌入式硬件,arm开发,经验分享
ws2812b,STM32小项目实战,stm32,单片机,嵌入式硬件,arm开发,经验分享

4.传输速率,以及帧数要求

手册里的其他内容大部分都和硬件相关了,这里不做赘述,需要的同学自己去查看,这里还有两个编程需要注意的重要参数,一个就是但刷速录大于30帧/秒时,至少要有1024个灯,而我们这里只有八个灯,所以1s内控制的灯数据包不能超过30个。
另一个就更重要了,数据的发送速度可达800Kbps

插补一点小知识:bps是bit/s ,叫做比特率,也叫码率就是每秒可以传输的数据位数
而baud/s,是波特率,字节每秒的意思,也就是一秒可以传输多少个字节的数据。(一个字节等于八位数据位)。
ws2812b,STM32小项目实战,stm32,单片机,嵌入式硬件,arm开发,经验分享

也就是说,WS2812B最高支持的数据包传输速率是1s接收800 000 位个2进制的数据,换算下来就是每个二进制位最少最少要保持1/800 000=1.25us的时间,还记得前面的时序图吗,单个数据“0”或者单个数据“1”是由不同的高低电平时间段组合成的,也就是说,单个数据“1”或者单个数据“0”的高低电平时间之和不能少于1250ns。
T0H+T0L>1250ns
T1H+T1L>1250ns
ws2812b,STM32小项目实战,stm32,单片机,嵌入式硬件,arm开发,经验分享
搞清楚这些以后,差不多就可以开始写代码了。
在这之前还需要去瞧一眼原理图,找到这个板子中WS2812B的控制引脚,通过原理图可以看出,控制脚是GPIOA_8。
ws2812b,STM32小项目实战,stm32,单片机,嵌入式硬件,arm开发,经验分享
为啥选用PA8呢,这里笔者做了两手准备,WS2812B的常用驱动方式有PWM、SPI来配合DMA的高效率方式,这种驱动方案不占用CPU的资源;另外一种就是使用纯GPIO模拟输出的方案,为了兼顾,所以笔者选了个有复用功能的IO口。只不过,我们这里灯少,8个灯,选则软件模拟的方案也没啥大问题,加上这里的四个定时器都有他用了,为了避免干扰,所以最终选择软件模拟的方案来实现。想看PWM+DMA或者SPI+DMA的方案的可以自己去搜索一下哈,我看CSDN很多大佬都是用的这两种方案,讲的也很细致。
ws2812b,STM32小项目实战,stm32,单片机,嵌入式硬件,arm开发,经验分享
另外就是供电部分,这里笔者选用的是5V供电。

代码

1.初始化

根据上面的总结,这里选用GPIO模拟时序图的方案来进行,首先既要输出高又要输出低,所以GPIOA8需要配置为通用推挽输出模式,这个不再赘述了。

/*********************************
函数名:RGB_Init
函数功能:RGB初始化
形参:void
返回值:void
备注:
RGB-----PA8--------通用推挽输出
**********************************/
void RGB_Init(void)
{
	GPIO_InitTypeDef  GPIO_InitStructure;//定义一个结构体的变量
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//初始化GPIOA端口的时钟
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode =  GPIO_Mode_Out_PP;//通用推挽输出
	GPIO_Init(GPIOA,&GPIO_InitStructure );
}

为了方便操作,这里最好是进行宏定义输出高输出低
这里需要注意,结合上一篇的介绍,我们知道,控制GPIO的输出可以使用库函数,也可以使用位带操作,除此之外还有一种执行效率更高,时延更低的方案,直接使用寄存器来驱动。这里笔者把三种宏定义都贴出来,原因是,这个模块它设计到ns的延时,多运行一条代码都会跑过30ns-50ns,这是会严重影响控制效果的。三种宏定义大家自己选择一种即可,最快的是寄存器的,然后位带操作与库函数的差不多。

//位带操作
#define RGB_H   PAout(8)=1
#define RGB_L   PAout(8)=0
//库函数
#define RGB_H   GPIO_SetBits(GPIOA,GPIO_Pin_8)
#define RGB_L   GPIO_ResetBits(GPIOA,GPIO_Pin_8)
//直接操作寄存器(最快)
#define RGB_H   GPIOA->ODR |= (1<<8)
#define RGB_L   GPIOA->ODR &= ~(1<<8)

2.模拟时序

搞定了初始化以后,就该开始最重要的部分了,对照时序图,模拟出时序。
对于这类需要模拟时序的模块,我们一定要自底向上的思想,从最基础的发送“0”,发送“1”开始写,将发0与发1封装成对应的函数;除此之外还需要根据模块的实际需求封装复位、初始化之类的最底层函数,这里WS2812B就还需要一个复位的函数。封装好这些函数后,再来根据时序进行数据包发送的函数封装,进而拼凑出整个模块的时序代码。

1.复位函数

先来最简单的,复位函数,根据数据手册介绍,复位电路的时序就是将信号脚拉低,拉低时间不少于280us,这里我们给个350us。这里的延时是us级的,所以可以使用系统滴答的us延时来实现,系统滴答是所以延时中最准的了,定时器都没他准,所以这里最好是用系统滴答,关于系统滴答的介绍估计得放到和GPIO复用也就是定时器那一部分一起介绍,这里先用。

/*********************************
函数名:RGB_Reset
函数功能:RGB复位信号
形参:void
返回值:void
备注:
**********************************/
void RGB_Reset(void)
{
	RGB_L;
	Systick_Delay_us(350);//调用系统滴答来实现
}

2.发送0、1码

首先需要解决的是ns的延时问题,STM32F103C8T6的主频是72MHZ,也就是1s可以运行72 000 000条机器指令。系统滴答和定时器只能提供us级的延时,这个ns延时只能采用运行固定数量的机器指令来进行了。
计算过程如下:
1s 可以运行72 000 000 条机器指令,那么
1us 可以运行 72条机器指令,换句话说,CPU执行72个机器指令花费的时间是1us。那么1个机器指令花费的时间就是1us/72=13.88889ns;也就是说,CPU每运行一个机器指令,时间过去了13.8889ns。
然后再来看具体的时序图:
ws2812b,STM32小项目实战,stm32,单片机,嵌入式硬件,arm开发,经验分享
这里0码的高电平时间T0H是要在220ns~380ns之间的,而低电平时间T0L要控制在580ns-1600ns之间,而且T0H+T0L>1250ns
这里我们先定T0H,折中,选择300ns,
300/13.889=21.5999,四舍五入,取22个机器指令,
那么问题来了,我们平时都是用的C代码,一条C代码等于一个机器指令吗?
答案是否定的,一条C代码与机器指令之间没有固定的关系,这是因为每条C代码的底层汇编代码都不一样。但是好在留有专门的机器指令__nop()
一个__nop()就是一个机器指令。
于是得到了第一个延时:

//220-380 ns折中 300 13.89*22=305.5558
void delay_300ns(void)  //72 000 000MHZ  ==1s  72hz 1us  一个机器指令周期要耗时13.89ns
{
	__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();
	__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();
	__nop();__nop();
}

然后来解决第二个延时还是采取去中间值的方案,取1090ns,
1090/13.889=78.4,四舍五入取78个
于是得到第二个延时

//折中 1090 78*13.889
void delay_1090ns(void)  //72 000 000MHZ  ==1s  72hz 1us  一个机器指令周期要耗时13.8ns
{
	__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();__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码的发送代码就有了

/*********************************
函数名:RGB_Send0
函数功能:RGB发送0
形参:void
返回值:void
备注:
**********************************/
void RGB_Send0()
{
	RGB_H;
	delay_300ns();
	RGB_L;
	delay_1090ns();
}

依次类推可以退出1码的发送,延时可以就用上面的,不过最好是再弄一个delay_320ns的。实测delay_320的效果会好一点。

/*********************************
函数名:RGB_Send1
函数功能:RGB发送1
形参:void
返回值:void
备注:
**********************************/
void RGB_Send1()
{
	RGB_H;
	delay_1090ns();
	RGB_L;
	delay_320ns();
}
3.封装发送函数

前面介绍过24bit的数据分别对应着GRB的8位,所以这里先整个8位数据的发送函数,高位先发,于是可以得到如下代码:


/*********************************
函数名:RGB_Send_Data
函数功能:RGB发送8位数据
形参:u8 data需要发送的数据
返回值:void
备注:
**********************************/
void RGB_Send_Data(u8 data) 
{ 
   uint8_t i;
   for(i=8;i>0;i--) 
   {
     if(data & 0x80)//按位与,为真发送1,为假发送零
     {
        RGB_Send1();
     }
     else
     {
        RGB_Send0();
     }
     data <<=1;//
   }
}

然后再来封装一个发送24bit的函数

/*********************************
函数名:Send_GRB
函数功能:GRB发送24位数据
形参:u8 G,u8 R,u8 B
返回值:void
备注:
**********************************/
void Send_GRB(uint8_t G,uint8_t R,uint8_t B)
{
	RGB_Send_Data(G);
	RGB_Send_Data(R);
	RGB_Send_Data(B);
	RGB_Reset();
}

有了这个函数就已经可以实现单个灯的控制了,
ws2812b,STM32小项目实战,stm32,单片机,嵌入式硬件,arm开发,经验分享
ws2812b,STM32小项目实战,stm32,单片机,嵌入式硬件,arm开发,经验分享
然后为了控制后面的灯,我们需要封装一个控制多个灯的函数,根据前面分析的时序,
代码如下:


/*********************************
函数名:Continuous_Set_LED
函数功能:设置n个灯为 GRB的颜色
形参:u8 G,u8 R,u8 B
返回值:uint8_t n多少个灯
,uint32_t GRB设置的颜色
备注:
**********************************/
void Continuous_Set_LED(uint8_t n,uint32_t GRB)
{
	while(n--)
	{
		RGB_Send_Data((GRB>>16)&0xFF);
		RGB_Send_Data((GRB>>8)&0xFF);
		RGB_Send_Data((GRB>>0)&0xFF);
	}
	RGB_Reset();
}

调用这个函数就可以实现对指定个数的灯实现控制了,当然进一步还可以做流水,滚动,等等功能,这个有需要的就去查阅一下其他大佬的代码吧。
ws2812b,STM32小项目实战,stm32,单片机,嵌入式硬件,arm开发,经验分享
ws2812b,STM32小项目实战,stm32,单片机,嵌入式硬件,arm开发,经验分享
ws2812b,STM32小项目实战,stm32,单片机,嵌入式硬件,arm开发,经验分享

总结

至此,关于WS2812B的模拟驱动就介绍完了,文如有不足欢迎批评指正,下一篇继续使用模拟时序完成对DHT11的温湿度数据获取,先放个逻辑分析抓取的数据波形解析在这里供大家参考。
ws2812b,STM32小项目实战,stm32,单片机,嵌入式硬件,arm开发,经验分享

ws2812b,STM32小项目实战,stm32,单片机,嵌入式硬件,arm开发,经验分享文章来源地址https://www.toymoban.com/news/detail-660718.html

到了这里,关于GPIO模拟时序控制外设1——WS2812B的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • ESP32(MicroPython) 网页控制环形WS2812

    ESP32(MicroPython)网页控制环形WS2812 本程序用于提供网页控制环形WS2812,为了不影响网页正常运行,本程序取消了流水灯形式,改为每个灯或每2个灯或每4个灯取随机亮度,延时和亮度可调。主控换成ESP32C3,感觉网页流畅度比ESP32-EROOM32低一些。 程序如下

    2024年02月15日
    浏览(41)
  • (STM32笔记5)ws2812驱动开发

    ws2812 需要开发的外设:ws2812 开发环境:MDK 开发板:stm32最小系统板 杜邦线无数 一、原理阐述 (一) 这个ws2812有很多种,一个小灯、灯带,矩形像素屏等等。 这些都是DIN连接输入,DOUT连接输出,另外两个引脚是GNE和VCC   (二) 数据传输原理: 第1个WS2812B灯珠接收到了第1个

    2024年01月19日
    浏览(41)
  • STM32驱动全彩LED灯模块WS2812

    WS2812全彩LED灯模块系列,可以进行级联实现灯带的效果,MCU端通过一个管脚可以控制所有级联的LED灯的不同发光颜色显示。 WS2811(未集成LED)的级联电路如下所示: WS2812(集成LED)的级联电路如下所示: STM32是3.3V供电芯片,输出Push-Pull模式只有3.3V,WS2812采用5V供电,输入Vi

    2024年01月17日
    浏览(38)
  • 【ESP32 IDF】用RMT控制 WS2812 彩色灯带

    在上一篇中,老周用 .NET Nano Framework 给大伙伴们演示了 WS2812 灯带的控制,包括用 SPI 和 红外RMT 的方式。利用 RMT 是一个很机灵的方案,不过,可能很多大伙伴对 ESP32 的 RMT 不是很熟悉。除了乐鑫自己的文档,没几个人写过相关的水文,可见这里头空白的水市场很充足,老周

    2024年04月22日
    浏览(65)
  • [HAL]STM32 SPI+DMA驱动WS2812

    该程序是纯手敲,非Cube生成!所有代码均注释。 源码在文章后面获取 Keyword: 单线通讯、归零码、Reset、RGB顺序 RGB一共有24bit位 -相当于驱动一个灯要24bit位 -驱动若干个灯要24* n bit位,通过Reset码决定数据终止(保持) 24bit位应该如何发送? 可见: 表示低电平需要 T0H和T0L的配

    2024年02月09日
    浏览(48)
  • STM32+PWM+DMA驱动WS2812彩灯模块(附源码)

    WS2812是一颗数字LED灯珠,采用单总线通讯,每颗灯珠支持24bit的颜色控制,也即RGB888,信号线通过DIN输入,经过一颗灯珠之后,信号线上前24bit数据会被该灯珠锁存,之后将剩下的数据信号整形之后通过DOUT输出 C1为VDD的滤波电容,一般大小为100NF。 WS2812.c pwm.c DMA.c main.c 需要源

    2024年02月15日
    浏览(43)
  • STM32C0开发(1)----SPI 驱动WS2812灯珠

    本文介绍了如何使用STM32微控制器,结合STM32CubeMX配置工具和SPI通讯接口,来驱动和控制WS2812 LED灯带。这是一个集硬件连接、软件配置和编程开发于一体的综合性项目,目标是实现对LED灯带颜色和亮度的精确控制。 最近在弄ST的课程,需要样片的可以加群申请:615061293 。 ht

    2024年02月04日
    浏览(44)
  • WS2812+ESP32 diy拾音灯+各种灯效 通过Blinker控制

    软件版本:2.0.0 2023.02.08 修复软件开机自启动相关bug,具体为bat文件创建快捷方式时起始路径错误导致无法创建ip.bin 修复方式为快捷方式时起始路径向前一级 后续如有更新会更新博客。 ESP32开发板一个 ws2812灯带一条(我买的60灯珠) 杜邦线几根(公对母,主要用于灯带和开

    2023年04月18日
    浏览(42)
  • 心跳跟随的心形灯(STM32(HAL)+WS2812+MAX30102)

    在好几年前,我好像就看到了焊武帝 jiripraus在纪念结婚五周年时,制作的一个心跳跟随心形灯,感觉太浪漫了,于是在假期的时候,我也仿照做了一个,虽然还有很多需要完善的地方,但是大致功能已经实现了,下面开源讲讲开源的项目。 心脏的外壳采用紫铜丝或黄铜丝焊

    2024年02月14日
    浏览(44)
  • STM32例程分享-05-WS2812B灯带(单线归零码)

    例程运行结果:    WS2812B是一款全彩LED控制IC,单总线控制, 灯带上的每一位灯珠都可用编程控制全彩显示,通过单片机,我们可以实现任意数量任意颜色任意组合效果的炫彩灯光显示效果。    数据协议采用单线归零码的通讯方式,像素点在上电复位以后,DIN端接受从控制

    2024年02月11日
    浏览(37)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包