使用STM32F103C8T6控制WS2811(WS2812)灯带

写驱动之路历经坎坷,同一家公司的同一模块控制的时序图还没能统一,试了网上三四种时序最后才成功点亮。最后成功的时序图是按照下图所示在这里插入图片描述
需要注意的是0码及1码的高低电平总时间相同以及高低电平时间比1:3以及3:1当然不同的模块时序要求不尽相同请自行测试。
控制方式采用GPIO直接控制,比较简单,缺点是需要占用大量CPU时间。
在这里插入图片描述

位编码
一个数据位被编码为一个时间周期(T0H、T1H、T0L、T1L)内的电平变化。
逻辑 1 的编码是高电平(T1H)后跟低电平(T1L)。
逻辑 0 的编码是高电平(T0H)后跟低电平(T0L)。

通过__NOP();实现ns的延时,当然也可以使用数据移位来代替。
在72MHZ频率下一个__NOP();大约为13.89ns

在满足持续时间以及3:1的比例情况下
T1H使用了48个__NOP();
T1L使用了16个__NOP();

void SendOne(){//T1H -> T1L
	GPIO_SetBits(RGB_GPIOx,RGB_GPIO_PIN);//设置为高电平
	//延时580ns~1μs
	__NOP(); //13.89ns*48 ~
	__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();
	
	GPIO_ResetBits(RGB_GPIOx,RGB_GPIO_PIN);//设置为低电平
	//延时220ns~380ns
	__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();//13.89ns*16
	__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();
}

T0H以及T0L则相反

void SendZero(){//T0H -> T0L
	GPIO_SetBits(RGB_GPIOx,RGB_GPIO_PIN);//设置为高电平
	//延时220ns~380ns
	__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();//13.89ns*16
	__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();
	
	//延时580ns~1μs
	GPIO_ResetBits(RGB_GPIOx,RGB_GPIO_PIN);//设置为低电平
	__NOP();//13.89ns*48 
	__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();	
}

一个灯珠需要分别控制RED、GREEN、BLUE三种颜色,每种颜色的范围是0-255,即每种颜色需要8位数据来控制,所以控制一个灯珠需要三个字节共24bit的数据。
所以控制颜色的函数如下所示:

void SendRGB(u8 red,u8 green ,u8 blue){
	u8 i = 0;
	for(i = 0;i < 8; i++){
		if(red&0x80){
			SendOne();
		}
		else{
			SendZero();
		}
		red = red<<1;
	}
	for(i = 0;i < 8; i++){
		if(green&0x80){
			SendOne();
		}
		else{
			SendZero();
		}
		green = green<<1;
	}
	for(i = 0;i < 8; i++){
		if(blue&0x80){
			SendOne();
		}
		else{
			SendZero();
		}
		blue = blue<<1;
	}
}

每个字节从最高位发送数据,发送完一位就左移一位,直到8位数据都发送完成。
在发送完所有颜色后需要一个复位信号才能让灯点亮

void RGB_Reset(void){//复位信号
	GPIO_ResetBits(RGB_GPIOx,RGB_GPIO_PIN);//设置为低电平
	delay_ms(1);//延时1ms 满足50us的最小持续时间
	
	GPIO_SetBits(RGB_GPIOx,RGB_GPIO_PIN);//设置为高电平
	delay_ms(1);//延时1ms 满足280us的最小持续时间
}

当发送复位信号后再次发送数据会从第一个重新开始每24位数据代表一个灯珠(12V供电的灯带每24位控制三个灯珠显示同一种颜色

请添加图片描述

ws2812.c

#include "ws2812.h"
#include "delay.h"
/*******************************************************************
位编码:

一个数据位被编码为一个时间周期(T0H、T1H、T0L、T1L)内的电平变化。
逻辑 1 的编码是高电平(T1H)后跟低电平(T1L)。
逻辑 0 的编码是高电平(T0H)后跟低电平(T0L)。
时间周期和电平持续时间:

T0H:逻辑 0 的高电平时间,一般为 220ns~380ns 
T1H:逻辑 1 的高电平时间,一般为 580ns~1μs
T0L:逻辑 0 的低电平时间,一般为 580ns~1μs
T1L:逻辑 1 的低电平时间,一般为 220ns~420ns 

复位信号:

发送至少 50 微秒的低电平信号。这是WS2812识别复位信号的最小持续时间。
发送至少 280 微秒的高电平信号。这是复位信号结束后,WS2812开始接收数据的最小等待时间。
******************************************************************/

void RGB_GPIO_Init(){//使用的引脚初始化
	GPIO_InitTypeDef GPIO_InitStructor;
	//开启硬件时钟
	RCC_APB2PeriphClockCmd(RGB_RCC,ENABLE);
	
	GPIO_InitStructor.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStructor.GPIO_Pin = RGB_GPIO_PIN;
	GPIO_InitStructor.GPIO_Speed = GPIO_Speed_50MHz;
	
	GPIO_Init(RGB_GPIOx,&GPIO_InitStructor);
}
void RGB_Reset(void){//复位信号
	GPIO_ResetBits(RGB_GPIOx,RGB_GPIO_PIN);//设置为低电平
	delay_ms(1);//延时1ms 满足50us的最小持续时间
	
	GPIO_SetBits(RGB_GPIOx,RGB_GPIO_PIN);//设置为高电平
	delay_ms(1);//延时1ms 满足280us的最小持续时间
}
void SendOne(){//T1H -> T1L
	GPIO_SetBits(RGB_GPIOx,RGB_GPIO_PIN);//设置为高电平
	//延时580ns~1μs
	__NOP(); //13.89ns*48 ~
	__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();
	
	GPIO_ResetBits(RGB_GPIOx,RGB_GPIO_PIN);//设置为低电平
	//延时220ns~380ns
	__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();//13.89ns*16
	__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();
}
void SendZero(){//T0H -> T0L
	GPIO_SetBits(RGB_GPIOx,RGB_GPIO_PIN);//设置为高电平
	//延时220ns~380ns
	__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();//13.89ns*16
	__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();
	
	//延时580ns~1μs
	GPIO_ResetBits(RGB_GPIOx,RGB_GPIO_PIN);//设置为低电平
	__NOP();//13.89ns*48 
	__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();	
}
void SendRGB(u8 red,u8 green ,u8 blue){
	u8 i = 0;	
	for(i = 0;i < 8; i++){
		if(blue&0x80){
			SendOne();
		}
		else{
			SendZero();
		}
		blue = blue<<1;
	}
	for(i = 0;i < 8; i++){
		if(red&0x80){
			SendOne();
		}
		else{
			SendZero();
		}
		red = red<<1;
	}
	for(i = 0;i < 8; i++){
		if(green&0x80){
			SendOne();
		}
		else{
			SendZero();
		}
		green = green<<1;
	}

}
void TestRGB(u16 n){//测试N个灯珠
	u8 i = 0,j = 0;
	for(i = 0 ;i < n ;i++){//流水灯
		for(j = 0;j <= i;j++){
			SendRGB(0,0,0);
		}
		SendRGB(255,0,0);
		RGB_Reset(); 
		delay_ms(500);
	}
}


ws2812.h

#ifndef __WS2812_H_
#define __WS2812_H_

#include "sys.h" //包含u8等定义

//单总线引脚定义
#define RGB_GPIOx GPIOA
#define RGB_GPIO_PIN GPIO_Pin_0
#define RGB_RCC RCC_APB2Periph_GPIOA
//控制的灯珠数量
#define RGB_COUNT 10

void RGB_GPIO_Init(void);
void RGB_Reset(void);
void SendOne(void);
void SendZero(void);
void SendRGB(u8 red,u8 green ,u8 blue);
void TestRGB(u16 n);//测试N个灯珠
#endif //__WS2812_H_

如需移植请自行根据CPU频率修改__NOP();的数量
如无sys.h文件自行修改为#include "stm32f10x.h"等适合的核心文件
u8 修改为unsigned char

main.c

#include "sys.h"
#include "delay.h"
#include "led.h"
#include "ws2812.h"

int main()
{
	delay_init();
	RGB_GPIO_Init();
	//NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	LedInit();
	while(1)
	{
		TestRGB(RGB_COUNT);
		delay_ms(200);
		LED1 = !LED1;
	}
}


  • 13
    点赞
  • 60
    收藏
    觉得还不错? 一键收藏
  • 12
    评论
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值