找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 892|回复: 22
打印 上一主题 下一主题
收起左侧

辉芒微61E143 如何控制ws2812B灯珠,求大佬帮忙

  [复制链接]
跳转到指定楼层
楼主
ID:1142265 发表于 2025-4-7 17:20 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
    最近遇到了一个棘手的问题,那就是ws2812B灯珠的驱动问题。
我使用的是辉芒微的FT61E143的单片机,主频是16Mhz  2T的指令周期,一条命令执行下来差不多 125ns左右。
想用它来驱动ws2812B 3528-BT00灯珠,能驱动,但是颜色不对,也无法关闭,很奇怪。
下面是我的代码


#include "SYSCFG.h"

#define WS2812_PIN PB4   // WS2812 信号引脚
#define LED_NUM 8        // WS2812 颗数

unsigned char LED_DATA[LED_NUM * 3]; // WS2812 RGB 数据存储

// **精准延时**
void delay_us(unsigned char us) {
    while (us--) {
        NOP();
        NOP();
        NOP();
        NOP();
    }
}
// **精准延时**
void delay_ns(unsigned char ns) {
    unsigned char i;
    for(i=0;i<(ns/62);i++){
                NOP();
    }
}


// **发送单个 bit**
void ws2812_send_bit(unsigned char bit_1) {
    if (bit_1) {
        WS2812_PIN = 1; // 发送 1
        //delay_us(1);  // 高电平 0.7us
        delay_ns(595);
        //NOP();NOP();NOP();NOP();NOP();
        WS2812_PIN = 0;
        //NOP();NOP();NOP();
        delay_ns(295);
    } else {
        WS2812_PIN = 1; // 发送 0
        //NOP();NOP();NOP();
        delay_ns(292);
        WS2812_PIN = 0;
        delay_ns(595);
        //NOP();NOP();NOP();NOP();NOP();
    }
}

// **发送一个字节**
void ws2812_send_byte(unsigned char byte) {
    for (unsigned char i = 0; i < 8; i++) {
        ws2812_send_bit(byte & (0x80>>i));
    }
}

// **发送整个 LED 数组**
void ws2812_send_data() {
    for (unsigned int i = 0; i < LED_NUM * 3; i++) {
        ws2812_send_byte(LED_DATA[i]);
    }
    delay_us(100); // 复位信号 >50μs
}

void POWER_INITIAL() {
    OSCCON = 0B01110001; // 16MHz 时钟
    INTCON = 0;          // 关闭所有中断
    PORTB = 0B00000000;
    TRISB = 0B00000000;
    ANSELA = 0B00000000; // 数字引脚
}

void main() {
    POWER_INITIAL();

    // 设置 LED 颜色(红、绿、蓝)
    LED_DATA[0] = 0x00; // 红色最大亮度
    LED_DATA[1] = 0x00; // 绿色
    LED_DATA[2] = 0x00; // 蓝色
        LED_DATA[3] = 0xFF;
    while (1) {
                delay_us(100);
        ws2812_send_data(); // 发送 WS2812 数据
        delay_us(500000);   // 500ms 间隔
    }
}



分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏2 分享淘帖 顶 踩
回复

使用道具 举报

沙发
ID:192020 发表于 2025-4-7 18:26 | 只看该作者
有用逻辑分析仪或者示波器看过波形吗?估计就是时序不对,有for有移位,这些指令也得算进去的,用这单片机驱动有点勉强。要不用汇编,要不就极限优化下代码
回复

使用道具 举报

板凳
ID:1093493 发表于 2025-4-8 08:09 | 只看该作者
#include "SYSCFG.h"
/*************************************************
功能说明:点亮RGB灯不同颜色闪烁
*************************************************/
#define PIN RA7//  RGB灯
#define  LED_DIRECTION        TRISA7
#define  unchar     unsigned char
#define  unint      unsigned int
#define  unlong     unsigned long
#define NUM 12                                //灯的个数(最大73个)

typedef struct {
    unsigned char   r;
    unsigned char   b;
    unsigned char   g;
} ws2812_ptr;

ws2812_ptr          led;  
/*----------------------------------------------------
*        函数名称:DelayUs
*        功能:   短延时函数 --16M-2T--大概快1%左右.
*        输入参数:Time 延时时间长度 延时时长Time Us
*        返回参数:无
----------------------------------------------------*/
void DelayUs(unsigned char Time)
{
        unsigned char a;
        for(a=0;a<Time;a++)
        {
                NOP();
        }
}                  
/*----------------------------------------------------
*        函数名称:DelayMs
*        功能:   短延时函数
*        输入参数:Time 延时时间长度 延时时长Time ms
*        返回参数:无
----------------------------------------------------*/
void DelayMs(unsigned char Time)
{
        unsigned char a,b;
        for(a=0;a<Time;a++)
        {
                for(b=0;b<5;b++)
                {
                         DelayUs(197);                 //快1%
                }
        }
}
/*----------------------------------------------------
*        函数名称:DelayS
*        功能:   短延时函数
*        输入参数:Time 延时时间长度 延时时长Time S
*        返回参数:无
----------------------------------------------------*/
void DelayS(unsigned char Time)
{
        unsigned char a,b;
        for(a=0;a<Time;a++)
        {
                for(b=0;b<10;b++)
                {
                         DelayMs(100);                
                }
        }
}
unsigned long int bitflip(unsigned char b);

// transmit the ws2812 led
void ws2812_send(ws2812_ptr* led)
{
    int j;
    long int val;
    // the WS2812 wants bits in the order of:
    // GGGGGGGGRRRRRRRRBBBBBBBB
    // but I want to work in the opposite order. so i'm going to flip
    // the bits around and do some shifting so my order is
    // BBBBBBBBRRRRRRRRGGGGGGGG
    // with the most significant bit on the far right. so the RGB value
    // of 128 64 32, which normally would be:
    // R : 0b10000000
    // G : 0b01000000
    // B : 0b00100000
    // will become:
    // BBBBBBBBRRRRRRRRGGGGGGGG
    // 000001000000000100000010
    val = (bitflip(led->b) << 16) + (bitflip(led->r) << 8) + (bitflip(led->g));

    // now begin shifting them over one at a time      //每次移动一个
    for(j = 0; j < 24; j++)
    {
        // depending on if the currently viewed bit is 1 or 0
        // the pin will stay high for different times

        if (val & 1 == 1)
        {
            // if it is a 1, let it stay higher a bit longer
            PIN = 1;
            NOP();
            NOP();
            PIN = 0;
                        NOP();
           
                       
        }
        
         else
        
        {
            // but a 0 should go high and then low as fast as possible
            PIN = 1;
                        NOP();
           
            PIN = 0;
                        NOP();
                        NOP();
        }
        
        // and then right shift to get the next bit
        val = val >> (unsigned char)1;
    }
}

// reverse the bits in a char
unsigned long int bitflip(unsigned char b)
{
   b = (b & 0xF0) >> 4 | (b & 0x0F) << 4;
   b = (b & 0xCC) >> 2 | (b & 0x33) << 2;
   b = (b & 0xAA) >> 1 | (b & 0x55) << 1;
   return (unsigned long int)b;
}
/*-------------------------------------------------
* 函数名称:WDT_INITIAL
* 功能:           初始化设置看门狗1S时间复位
* 相关寄存器:
* 1、INTCON
* 2、OPTION
* 3、WDTCON
-------------------------------------------------*/
void WDT_INITIAL (void)
{
        CLRWDT();                          //清看门狗
        PSA=1;                             //时钟分频分给WDT
        WDTCON = 0B00010100;        //WDTPS=1010=1:32768,PS=000=1:1
                                                                //定时时间=(32768*1)/32000=1024ms
}
/*-------------------------------------------------
*  函数名:initMcu
*        功能:  上电系统初始化
*  输入:  无
*  输出:  无
--------------------------------------------------*/       
void initMcu (void)
{
        OSCCON = 0B01110001;        //WDT 32KHZ IRCF=111=16MHZ/2=8MHZ,0.125US/T
                                                         //Bit0=1,系统时钟为内部振荡器
                                                         //Bit0=0,时钟源由FOSC<2:0>决定即编译选项时选择

        INTCON = 0;                          // 暂禁止所有中断
        LED_DIRECTION = 0;
}


/*-------------------------------------------------
*  函数名: main
*        功能:  主函数
*  输入:  无
*  输出:  无
--------------------------------------------------*/
void main()
{
    unsigned char cnt=0,i,frame =0;i;
                initMcu();                        //系统初始化
        //WDT_INITIAL();
       
        led.r = 0;
        led.g = 255;
        led.b = 255;
        while(1)
        {
                if(frame == 0)
                {
                        for(i=0;i<NUM;i++)
                        {
                                if(i<cnt)
                                {
                                        led.r = 0;
                                        led.g = 255;
                                        led.b = 0;
                                }
                                else
                                {
                                        led.r = 0;
                                        led.g = 0;
                                        led.b = 0;
                                }
                               
                                ws2812_send(&led);
                        }
                        DelayMs(60);
                }
                else if(frame == 1)
                {
                        for(i=0;i<NUM;i++)
                        {
                                if(i<(NUM-cnt))
                                {
                                        led.r = 0;
                                        led.g = 255;
                                        led.b = 0;
                                }
                                else
                                {
                                        led.r = 0;
                                        led.g = 0;
                                        led.b = 0;
                                }
                               
                                ws2812_send(&led);
                        }
                        DelayMs(60);
                }       
                else if(frame == 2)
                {
                        for(i=0;i<NUM;i++)
                        {
                                if(i<cnt)
                                {
                                        led.r = 255;
                                        led.g = 0;
                                        led.b = 0;
                                }
                                else
                                {
                                        led.r = 0;
                                        led.g = 0;
                                        led.b = 0;
                                }
                               
                                ws2812_send(&led);
                        }
                        DelayMs(60);
                }
                else if(frame == 3)
                {
                        for(i=0;i<NUM;i++)
                        {
                                if(i<(NUM-cnt))
                                {
                                        led.r = 255;
                                        led.g = 0;
                                        led.b = 0;
                                }
                                else
                                {
                                        led.r = 0;
                                        led.g = 0;
                                        led.b = 0;
                                }
                               
                                ws2812_send(&led);
                        }
                        DelayMs(60);
                }
                else if(frame == 4)
                {
                        for(i=0;i<NUM;i++)
                        {
                                if(i<cnt)
                                {
                                        led.r = 0;
                                        led.g = 0;
                                        led.b = 255;
                                }
                                else
                                {
                                        led.r = 0;
                                        led.g = 0;
                                        led.b = 0;
                                }
                               
                                ws2812_send(&led);
                        }
                        DelayMs(60);
                }
                else if(frame == 5)
                {
                        for(i=0;i<NUM;i++)
                        {
                                if(i<(NUM-cnt))
                                {
                                        led.r = 0;
                                        led.g = 0;
                                        led.b = 255;
                                }
                                else
                                {
                                        led.r = 0;
                                        led.g = 0;
                                        led.b = 0;
                                }
                               
                                ws2812_send(&led);
                        }
                        DelayMs(60);
                }       
                else if(frame == 6)
                {
                        for(i=0;i<NUM;i++)
                        {
                                if(i<cnt)
                                {
                                        led.r = 255;
                                        led.g = 0;
                                        led.b = 255;
                                }
                                else
                                {
                                        led.r = 0;
                                        led.g = 0;
                                        led.b = 0;
                                }
                               
                                ws2812_send(&led);
                        }
                        DelayMs(60);
                }
                else if(frame == 7)
                {
                        for(i=0;i<NUM;i++)
                        {
                                if(i<(NUM-cnt))
                                {
                                        led.r = 255;
                                        led.g = 0;
                                        led.b = 255;
                                }
                                else
                                {
                                        led.r = 0;
                                        led.g = 0;
                                        led.b = 0;
                                }
                               
                                ws2812_send(&led);
                        }
                        DelayMs(60);
                }       
                if(cnt<NUM)
                        cnt++;
                else
                {
                        cnt = 0;
                        if(frame<7)
                                frame++;
                        else
                                frame =0;
                }
               
        }
}

评分

参与人数 1黑币 +30 收起 理由
copower + 30 赞一个!

查看全部评分

回复

使用道具 举报

地板
ID:1093493 发表于 2025-4-8 09:22 | 只看该作者
发你份出自辉忙群的代码
回复

使用道具 举报

5#
ID:341045 发表于 2025-4-8 09:54 | 只看该作者
用汇编写,极限优化可以。WS2812最低时序220--380nS,极限优化刚好可3T*125=375nS完成电平变换,要点是不用循环,硬写24位色,循环移动数据要在长电平时间段完成
回复

使用道具 举报

6#
ID:18297 发表于 2025-4-8 10:51 | 只看该作者
你的这个时序不对。delay延时时间长了。使用汇编输出控制,我完成过这个WS3812的控制程序。
回复

使用道具 举报

7#
ID:1146851 发表于 2025-4-8 13:49 | 只看该作者
要依据 WS2812B 的数据手册,对高低电平的延时时间进行精确调整。一般而言,发送逻辑 1 时,高电平持续时间约为 0.7µs,低电平持续时间约为 0.35µs;发送逻辑 0 时,高电平持续时间约为 0.35µs,低电平持续时间约为 0.7µs。WS2812B 灯珠的数据传输先测试能否正确点亮单色
回复

使用道具 举报

8#
ID:1142265 发表于 2025-4-8 16:03 | 只看该作者
我不会写汇编,今天用逻辑分析仪抓了一下波形,一个nop要300-400ns去了 时间也不是很准,然后执行一条RA5=1 ;这样的代码,要花100ns,如果执行while循环体要400ns左右的时间,for循环体是400ns-1.2us的时间。 想换个高频的芯片写,但是老板说市面上的人都是用这个芯片跑ws2812的,一定可以跑通的。很纠结
回复

使用道具 举报

9#
ID:4867 发表于 2025-4-8 22:35 | 只看该作者
// **精准延时**
void delay_us(unsigned char us) {
    while (us--) {
        NOP();
        NOP();
        NOP();
        NOP();
    }
}

// **发送单个 bit**
void ws2812_send_bit(unsigned char bit_1) {
    if (bit_1) {
        WS2812_PIN = 1; // 发送 1
        delay_us(1);
        WS2812_PIN = 0;
    } else {
        WS2812_PIN = 1; // 发送 0
        WS2812_PIN = 0;
    }
}

// **发送一个字节**
void ws2812_send_byte(unsigned char byte) {
    for (unsigned char i = 0; i < 8; i++) {
        ws2812_send_bit(byte & (0x80>>i));
    }
}

// 复位信号,用于刷新显示
void reset_ws2812b(void) {
    WS2812B_PIN = 0;
    delay_us(50);  // 低电平持续时间大于 280us
}

// **发送整个 LED 数组**
void ws2812_send_data() {
    for (unsigned int i = 0; i < LED_NUM * 3; i++) {
        ws2812_send_byte(LED_DATA[i]);
    }
    reset_ws2812b(); // 复位信号 >50μs
}
回复

使用道具 举报

10#
ID:192020 发表于 2025-4-9 18:14 | 只看该作者
不用循环,用位bit变量,只用if来硬写24次赋值,估计还是可以够用的
回复

使用道具 举报

11#
ID:1088185 发表于 2025-4-10 21:40 | 只看该作者
最近才帮客人解决同样的问题, 他还需要同时接收数据, 我给的第一条意见就是换MCU。
回复

使用道具 举报

12#
ID:1088185 发表于 2025-4-10 21:47 | 只看该作者
lzts88 发表于 2025-4-8 09:54
用汇编写,极限优化可以。WS2812最低时序220--380nS,极限优化刚好可3T*125=375nS完成电平变换,要点是不用 ...

24位只是一颗灯, 你还要再加个判断下一个灯的数据完没完, 再加下一数据加载, 再来一个判断是否最后一数据
回复

使用道具 举报

13#
ID:285258 发表于 2025-4-11 16:48 | 只看该作者
gods701_LUO 发表于 2025-4-8 16:03
我不会写汇编,今天用逻辑分析仪抓了一下波形,一个nop要300-400ns去了 时间也不是很准,然后执行一条RA5=1 ...

任何单片机都可以跑ws2812,看灯珠数量,串联的灯珠数量越多每次刷新独霸cpu的时间就越长。
回复

使用道具 举报

14#
ID:1113066 发表于 2025-4-11 17:40 | 只看该作者
您好,我这边是ws2812的原厂家,你写的时序不对,可以联系我,发一份示例程序给你。
回复

使用道具 举报

15#
ID:341045 发表于 2025-4-11 20:49 | 只看该作者
1600277881 发表于 2025-4-10 21:47
24位只是一颗灯, 你还要再加个判断下一个灯的数据完没完, 再加下一数据加载, 再来一个判断是否最后一 ...

时序只是对应一个灯,下一个灯的延时不影响
回复

使用道具 举报

16#
ID:341045 发表于 2025-4-11 20:52 | 只看该作者
1600277881 发表于 2025-4-10 21:40
最近才帮客人解决同样的问题, 他还需要同时接收数据, 我给的第一条意见就是换MCU。

用汇编就可以
回复

使用道具 举报

17#
ID:401564 发表于 2025-4-12 13:07 | 只看该作者
gods701_LUO 发表于 2025-4-8 16:03
我不会写汇编,今天用逻辑分析仪抓了一下波形,一个nop要300-400ns去了 时间也不是很准,然后执行一条RA5=1 ...

不需要汇编
设定好之后,一个NOP就应该是对应的125uS,如果是300uS,那就是你代码或者配置的问题
这种单片机随便写2812的,不管多少个灯
写入的时候是要关闭所有中断的,不然颜色很容易出错
如果程序需要高的效率,肯定是不能一下子写完所有的灯,灯多的话,可能会卡个几十mS的,这肯定是不行的
你可以把代码逻辑优化一下,每5mS写一个灯,然后去执行一下主程序的其它代码,下一个5mS再写第二个灯,这样下来,100个灯的用时会用到500mS,整体效果不会差太多,或者是改成1mS写入一个灯
市场上大多的2812都是用这个低端单片机控制的,想改不太现实的
有的是因为单片机供应商是固定的,有的是不想换单片机,产品后期维护太难,有的就是成本问题,大批量产品,1分钱的成本都是很敏感的
回复

使用道具 举报

18#
ID:1067318 发表于 2025-4-15 22:22 | 只看该作者
汇编写,用STC8单片机11.0592时钟没问题
回复

使用道具 举报

19#
ID:1142265 发表于 2025-4-16 13:50 | 只看该作者
我自己修改了一下,先整体实现 点灯效果,然后再去套for循环就可以了,如果用for循环去实现 根本就点不亮,因为一个for循环就要有1.875us的延时,一条if判断语句要有20多us的延时,不用汇编写,只能另辟蹊径
回复

使用道具 举报

20#
ID:1093493 发表于 2025-4-16 15:37 | 只看该作者
void write24_GRB2() {  unsigned long  grb;  unsigned char  i;  unsigned long  temp;    grb=0;  temp=0;        grb = (gren | grb) << 8 ;      grb=  (read | grb) << 8;      grb=  (biue | grb) << 8 ;     for(i=0;i<24;i++)    {       temp=grb & 0x80000000;            if(temp)            {                  out1=1;                  DelayUs(1);                  out1=0;                              }          else         {           out1=1;           out1=0;           DelayUs(1);            }              grb <<= 1;     } }        亲测没问题视频为证
回复

使用道具 举报

21#
ID:108361 发表于 2025-4-16 16:43 | 只看该作者
芯片有SPI接口吗?如果你的SPI能跑到6M的时钟,就可以用SPI发送0xc0作为bit 0,发送0xf8作为bit 1,来实现全部字节的发送了
回复

使用道具 举报

22#
ID:401564 发表于 2025-4-17 12:51 | 只看该作者
gods701_LUO 发表于 2025-4-16 13:50
我自己修改了一下,先整体实现 点灯效果,然后再去套for循环就可以了,如果用for循环去实现 根本就点不亮, ...

你这个代码本身就有问题
第一:  for(j = 0; j < 24; j++),在这里,你定义了int j,那么,j就是一个16位的数据了,就要用到两个字节在存放,8位单片机处理两个字节数据是很耗时间的,至少在WS2812中是这样的
第二: 底下定义的val也有问题,它是一个 long int val;,这就是4个字节的数据,这不更耗时了叫吗?
这是我一个WS2812的写入程序,用的是16MHZ/4T的,写入完全没有问题
void write_ws2812(u8 rgb_data)
{
  u8 i;
  for(i = 0; i < 8; i++)
  {
    if(rgb_data & 0x80)//写"1"
    {
      DO = HI;
          delay_750ns
      DO = LOW;
    }
    else //写"0"
    {
      DO = HI;
      DO = LOW;
    }
    rgb_data <<= 1;
  }
}
回复

使用道具 举报

23#
ID:1147008 发表于 2025-4-17 13:25 | 只看该作者
Y_G_G 发表于 2025-4-17 12:51
你这个代码本身就有问题
第一:  for(j = 0; j < 24; j++),在这里,你定义了int j,那么,j就是一个16位的数 ...

点评一下,这位大佬,心肠好,技术高!完毕!
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表