资讯详情

ws2812C驱动示例主控芯片为HC32F460系列

这款LED灯最神奇的地方是只有一条控制线,即一条IO它可以控制一个和多个灯的亮灭和RGB颜色,当我第一次接触到这个时,我质疑了二进制的状态。直到看完协议,发现是时间顺序上的串行,我才豁然开朗。

多位(几个LED也就是几个)通过引脚级联,一个接一个LED的DOUT引脚到另一个LED的DIN引脚,通过这种级联,只需要使用一个IO口(单片机引脚)可以尽可能多地控制LED。

每个LED里面集成了驱动芯片,让我们LED变得智能和寻址,每个内部都有恒流驱动,所以LED颜色非常一致,即使电压有轻微的抖动,电压变化也是如此。

不需要外部电阻-限流电阻LED灯的布局设计变得简单。

单线通信可以最大限度地减少单片机IO口压,另外这个RGB灯使用了WS2812B(WS2812C跟B的01码的高低电平持续时间是一样的)驱动芯片,让外围电路能够满足电路的需求,使电路最大限度地简单美观。

特点

  1. 智能反接保护,电源反接不会损坏IC;

  2. IC控制电路与LED点光源公用一个电源;

  3. 控制电路与RGB芯片集成在5050包装元件中,形成外部控制像素点;

  4. 内置信号整形电路,任何像素点收到信号后通过波形整形输出,确保线路波形畸变不会累积;

  5. 内置上电复位和掉电复位电路;

  6. 每个像素点的三基色可显示256亮度,1677216种颜色的全真色可显示,扫描频率不低于400Hz;

  7. 串行级联接口可以通过信号线接收和解码数据;

  8. 当任何两点传输距离不超过5米时,无需增加额外电路;

  9. 当刷新率为30帧/秒时,级联数不小于1024点;

  10. 数据发送速度可达8000Kbps;

  11. 光的颜色高度一致,性价比高。

800Kbps,相当于1.25us传输一比特数据。

引脚图

引脚功能描述:

1 VDD LED,Vdd 范围 3.5~ 5.3 V
2 DOUT 控制信号数据输出
3 VSS
4 DIN 引脚控制信号数据输入

典型电路

串联方法

原理图

除了灯珠,只需要额外添加0.1uF电容。

硬件连接

PA15(可以选择任何一个GPIO,即使是PA15作为JTAG口也可以重用IO) DIN
VCC 5V
GND GND

驱动原理

数据协议采用单线归零码的通信方式,上电复位后像素点,DIN端接收从控制器传输的数据,首先发送的24bit数据从第一个像素点提取后,发送到像素点内部的数据器。剩余数据通过内部整形电路整形放大DOUT端口开始向下一级联的像素点转发输出,每次传输一个像素点,信号减少24bit。

采用自动整形转发技术,使像素点的级联数不受信号传输的限制,仅限于信号传输速度的要求。

由于数据被内部锁定,只要颜色值不改变(模块连续供电),颜色就不会改变,设置颜色的脉冲也不需要连续提供(单片机复位无影响),只需在修改颜色值时再次发送。

0和1的区分

Treset:复位时间

从上图可以看出,我们必须发送它 '' ,需要将GPIO引脚位高并持续0.4 us(400 ns),然后GPIO置低并持续0.85 us(850 ns),这个过程就完成了发送。如何产生这个ns水平延迟,常用的方法是使用nop空指令:

#define WS_TIMES 2  //与您的芯片主频有关,HC32F460主频168M使用 void delay_250ns(void) {  u8 del_t=WS_TIMES;  while(del_t--)   __NOP(); }  

然后用示波器观察和模块DIN引脚相连的GPIO输出脉冲信号,检查其高电平是否与我们的预定义一致,并增加或减少空指令进行调整。

经过示波器大约是这么长的时间。

////发送0和1函数  void send_0(void) {  TH;  delay_250ns();  TL;  delay_250ns();  delay_250ns();  delay_250ns(); } void send_1(void) {  TH;  delay_250ns();  delay_250ns();  delay_250ns();  TL;  delay_250ns();     delay_250ns();     delay_250ns(); }

实际调试的时候需要根据实际情况调整。

24 bit数据的组成

按数据传输顺序GRB顺序传输,高位在前。

voidws2812_rgb(u8ws_num,u8ws_r,u8ws_g,u8ws_b) { ws_data[(ws_num-1)*3]=ws_g; ws_data[(ws_num-1)*3 1]=ws_r; ws_data[(ws_num-1)*3 2]=ws_b; } 

数组用于记录待传输的数组RGB由于数据传输顺序按照三个字节,每个灯珠的颜色占用数据GRB顺序传输,所以赋值时要注意顺序,上面的函数是设置一个灯珠的颜色值。

设置数组中的颜色值后,将数组的数据发送到模块中,具体实现函数如下:

voidws2812_refresh(u8ws_count) { u8ws_ri=0;  for(;ws_ri<ws_count*3;ws_ri  ) { if((ws_data[ws_ri]&0x80)==0)send_0();elsesend_1(); if((ws_data[ws_ri]&0x40)==0)send_0();elsesend_1(); if((ws_data[ws_ri]&0x20)==0)send_0();elsesend_1(); if((ws_data[ws_ri]&0x10)==0)send_0();elsesend_1(); &bsp;       if((ws_data[ws_ri]&0x08)==0) send_0(); else send_1();
        if((ws_data[ws_ri]&0x04)==0) send_0(); else send_1();
        if((ws_data[ws_ri]&0x02)==0) send_0(); else send_1();
        if((ws_data[ws_ri]&0x01)==0) send_0(); else send_1();
    }
    
    //延时一段时间
    ws2812_reset();
}

 数组中的每一个字节按位发送,因为高位在前,所以先发送每个字节的高位,获取最高位的值的方法为: 。

数据传输方法

N位的模块,一次就要发送字节的数据。

 上图中D1的数据是通过单片机发送,D2,D3,D4通过像素内重塑放大传输。

 主函数就是点灯逻辑了。上传参考的代码:

#ifndef ws2812_h__
#define ws2812_h__
#include "sys.h"

//用户修改处
#define WS_TIMES 6		//和你的芯片主频有关,stm32f407用6,自己多试下,或者有条件的用示波器看下 
#define TH	PEout(4)=1;
#define TL	PEout(4)=0;

//RGB--定义了几个标准的rgb值 
#define WS_DARK 	0,0,0
#define WS_WHITE 	255,255,255
#define WS_RED 		255,0,0
#define WS_GREEN 	0,255,0
#define WS_BLUE 	0,0,255
#define WS_YELLO 	255,255,0
#define WS_PURPLE   255,0,255
#define WS_CYAN 	0,255,255

//extern u8 ws_data[];如果你需要在其他地方用到这个数组,就不注释 

void ws2812_init(void);
void ws2812_rgb(u8 ws_i,u8 ws_r,u8 ws_g,u8 ws_b);
void ws2812_rgb_all(u8 ws_i,u8 ws_r,u8 ws_g,u8 ws_b);
void ws2812_refresh(u8 ws_i);
void ws2812_reset(u8 ws_set);


void delay_250ns(void);
void send_0(void);
void send_1(void);
void send_res(void);

#endif //ws2812_h__
#include "ws2812.h"
//用法示例,先保存到数组,用 ws2812_rgb函数,然后用ws2812_refresh函数发送 
// ws2812_rgb(1, WS_RED);
// ws2812_rgb(2, WS_GREEN);
// ws2812_rgb(3, WS_BLUE);
// ws2812_rgb(4, WS_WHITE);
// ws2812_rgb(5, WS_PURPLE);
// ws2812_rgb(6, WS_YELLO);
// ws2812_rgb(7, WS_DARK);
// ws2812_rgb(8, WS_BLUE);
// ws2812_refresh(8);

void ws2812_init(void)//PE4
{
	GPIO_InitTypeDef  GPIO_InitStructure;
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE);
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
	GPIO_Init(GPIOE, &GPIO_InitStructure);
	GPIO_ResetBits(GPIOE,GPIO_Pin_4);
}


u8 ws_data[200]={0};
void ws2812_rgb(u8 ws_i,u8 ws_r,u8 ws_g,u8 ws_b)	
{
	ws_data[(ws_i-1)*3]=ws_g;
	ws_data[(ws_i-1)*3+1]=ws_r;
	ws_data[(ws_i-1)*3+2]=ws_b;
}
void ws2812_rgb_all(u8 ws_i,u8 ws_r,u8 ws_g,u8 ws_b)	
{
	static u8 rgb_wsi;
	for(rgb_wsi=1;rgb_wsi<=ws_i;rgb_wsi++)
	{
		ws_data[(rgb_wsi-1)*3]=ws_g;
		ws_data[(rgb_wsi-1)*3+1]=ws_r;
		ws_data[(rgb_wsi-1)*3+2]=ws_b;
	}
}

void ws2812_refresh(u8 ws_i)
{
	u8 ws_ri=0;
	for(;ws_ri<ws_i*3;ws_ri++)
	{
		if((ws_data[ws_ri]&0x80)==0)	send_0();	else send_1();
		if((ws_data[ws_ri]&0x40)==0)	send_0();	else send_1();
		if((ws_data[ws_ri]&0x20)==0)	send_0();	else send_1();
		if((ws_data[ws_ri]&0x10)==0)	send_0();	else send_1();
		if((ws_data[ws_ri]&0x08)==0)	send_0();	else send_1();
		if((ws_data[ws_ri]&0x04)==0)	send_0();	else send_1();
		if((ws_data[ws_ri]&0x02)==0)	send_0();	else send_1();
		if((ws_data[ws_ri]&0x01)==0)	send_0();	else send_1();
	}
	send_res();
}

void ws2812_reset(u8 ws_set)
{
	for(int i=0;i<200;i++)
	{
		ws_data[i]=ws_set;
	}
}

void delay_250ns(void)
{
	u8 del_t=WS_TIMES;
	while(del_t--)
		__NOP();
}

void send_0(void)
{
	TH;
	delay_250ns();
	TL;
	delay_250ns();
	delay_250ns();
	delay_250ns();
	delay_250ns();
}
void send_1(void)
{
	TH;
	delay_250ns();
	delay_250ns();
	delay_250ns();
	delay_250ns();
	TL;
	delay_250ns();
}
void send_res(void)
{
	TL;
	delay_us(300);
}

最后感谢B站的视频教程,感谢网友们的分享。

收益最大的是这个:一个IO控制很多个LED,这个技能你get到了吗

标签: hc319变送器内置电阻led灯珠

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台