写驱动之路历经坎坷,同一家公司的同一模块控制的时序图还没能统一,试了网上三四种时序最后才成功点亮。最后成功的时序图是按照下图所示
需要注意的是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;
}
}