找回密碼
 立即注冊

QQ登錄

只需一步,快速開始

搜索
查看: 649|回復: 22
打印 上一主題 下一主題
收起左側(cè)

輝芒微61E143 如何控制ws2812B燈珠,求大佬幫忙

  [復制鏈接]
跳轉(zhuǎn)到指定樓層
樓主
ID:1142265 發(fā)表于 2025-4-7 17:20 | 只看該作者 回帖獎勵 |倒序瀏覽 |閱讀模式
    最近遇到了一個棘手的問題,那就是ws2812B燈珠的驅(qū)動問題。
我使用的是輝芒微的FT61E143的單片機,主頻是16Mhz  2T的指令周期,一條命令執(zhí)行下來差不多 125ns左右。
想用它來驅(qū)動ws2812B 3528-BT00燈珠,能驅(qū)動,但是顏色不對,也無法關(guān)閉,很奇怪。
下面是我的代碼


#include "SYSCFG.h"

#define WS2812_PIN PB4   // WS2812 信號引腳
#define LED_NUM 8        // WS2812 顆數(shù)

unsigned char LED_DATA[LED_NUM * 3]; // WS2812 RGB 數(shù)據(jù)存儲

// **精準延時**
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();
    }
}


// **發(fā)送單個 bit**
void ws2812_send_bit(unsigned char bit_1) {
    if (bit_1) {
        WS2812_PIN = 1; // 發(fā)送 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; // 發(fā)送 0
        //NOP();NOP();NOP();
        delay_ns(292);
        WS2812_PIN = 0;
        delay_ns(595);
        //NOP();NOP();NOP();NOP();NOP();
    }
}

// **發(fā)送一個字節(jié)**
void ws2812_send_byte(unsigned char byte) {
    for (unsigned char i = 0; i < 8; i++) {
        ws2812_send_bit(byte & (0x80>>i));
    }
}

// **發(fā)送整個 LED 數(shù)組**
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;          // 關(guān)閉所有中斷
    PORTB = 0B00000000;
    TRISB = 0B00000000;
    ANSELA = 0B00000000; // 數(shù)字引腳
}

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(); // 發(fā)送 WS2812 數(shù)據(jù)
        delay_us(500000);   // 500ms 間隔
    }
}



分享到:  QQ好友和群QQ好友和群 QQ空間QQ空間 騰訊微博騰訊微博 騰訊朋友騰訊朋友
收藏收藏2 分享淘帖 頂 踩
回復

使用道具 舉報

沙發(fā)
ID:192020 發(fā)表于 2025-4-7 18:26 | 只看該作者
有用邏輯分析儀或者示波器看過波形嗎?估計就是時序不對,有for有移位,這些指令也得算進去的,用這單片機驅(qū)動有點勉強。要不用匯編,要不就極限優(yōu)化下代碼
回復

使用道具 舉報

板凳
ID:1093493 發(fā)表于 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                                //燈的個數(shù)(最大73個)

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

ws2812_ptr          led;  
/*----------------------------------------------------
*        函數(shù)名稱:DelayUs
*        功能:   短延時函數(shù) --16M-2T--大概快1%左右.
*        輸入?yún)?shù):Time 延時時間長度 延時時長Time Us
*        返回參數(shù):無
----------------------------------------------------*/
void DelayUs(unsigned char Time)
{
        unsigned char a;
        for(a=0;a<Time;a++)
        {
                NOP();
        }
}                  
/*----------------------------------------------------
*        函數(shù)名稱:DelayMs
*        功能:   短延時函數(shù)
*        輸入?yún)?shù):Time 延時時間長度 延時時長Time ms
*        返回參數(shù):無
----------------------------------------------------*/
void DelayMs(unsigned char Time)
{
        unsigned char a,b;
        for(a=0;a<Time;a++)
        {
                for(b=0;b<5;b++)
                {
                         DelayUs(197);                 //快1%
                }
        }
}
/*----------------------------------------------------
*        函數(shù)名稱:DelayS
*        功能:   短延時函數(shù)
*        輸入?yún)?shù):Time 延時時間長度 延時時長Time S
*        返回參數(shù):無
----------------------------------------------------*/
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;
}
/*-------------------------------------------------
* 函數(shù)名稱:WDT_INITIAL
* 功能:           初始化設置看門狗1S時間復位
* 相關(guān)寄存器:
* 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
}
/*-------------------------------------------------
*  函數(shù)名:initMcu
*        功能:  上電系統(tǒng)初始化
*  輸入:  無
*  輸出:  無
--------------------------------------------------*/       
void initMcu (void)
{
        OSCCON = 0B01110001;        //WDT 32KHZ IRCF=111=16MHZ/2=8MHZ,0.125US/T
                                                         //Bit0=1,系統(tǒng)時鐘為內(nèi)部振蕩器
                                                         //Bit0=0,時鐘源由FOSC<2:0>決定即編譯選項時選擇

        INTCON = 0;                          // 暫禁止所有中斷
        LED_DIRECTION = 0;
}


/*-------------------------------------------------
*  函數(shù)名: main
*        功能:  主函數(shù)
*  輸入:  無
*  輸出:  無
--------------------------------------------------*/
void main()
{
    unsigned char cnt=0,i,frame =0;i;
                initMcu();                        //系統(tǒng)初始化
        //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;
                }
               
        }
}

評分

參與人數(shù) 1黑幣 +30 收起 理由
copower + 30 贊一個!

查看全部評分

回復

使用道具 舉報

地板
ID:1093493 發(fā)表于 2025-4-8 09:22 | 只看該作者
發(fā)你份出自輝忙群的代碼
回復

使用道具 舉報

5#
ID:341045 發(fā)表于 2025-4-8 09:54 | 只看該作者
用匯編寫,極限優(yōu)化可以。WS2812最低時序220--380nS,極限優(yōu)化剛好可3T*125=375nS完成電平變換,要點是不用循環(huán),硬寫24位色,循環(huán)移動數(shù)據(jù)要在長電平時間段完成
回復

使用道具 舉報

6#
ID:18297 發(fā)表于 2025-4-8 10:51 | 只看該作者
你的這個時序不對。delay延時時間長了。使用匯編輸出控制,我完成過這個WS3812的控制程序。
回復

使用道具 舉報

7#
ID:1146851 發(fā)表于 2025-4-8 13:49 | 只看該作者
要依據(jù) WS2812B 的數(shù)據(jù)手冊,對高低電平的延時時間進行精確調(diào)整。一般而言,發(fā)送邏輯 1 時,高電平持續(xù)時間約為 0.7µs,低電平持續(xù)時間約為 0.35µs;發(fā)送邏輯 0 時,高電平持續(xù)時間約為 0.35µs,低電平持續(xù)時間約為 0.7µs。WS2812B 燈珠的數(shù)據(jù)傳輸先測試能否正確點亮單色
回復

使用道具 舉報

8#
ID:1142265 發(fā)表于 2025-4-8 16:03 | 只看該作者
我不會寫匯編,今天用邏輯分析儀抓了一下波形,一個nop要300-400ns去了 時間也不是很準,然后執(zhí)行一條RA5=1 ;這樣的代碼,要花100ns,如果執(zhí)行while循環(huán)體要400ns左右的時間,for循環(huán)體是400ns-1.2us的時間。 想換個高頻的芯片寫,但是老板說市面上的人都是用這個芯片跑ws2812的,一定可以跑通的。很糾結(jié)
回復

使用道具 舉報

9#
ID:4867 發(fā)表于 2025-4-8 22:35 | 只看該作者
// **精準延時**
void delay_us(unsigned char us) {
    while (us--) {
        NOP();
        NOP();
        NOP();
        NOP();
    }
}

// **發(fā)送單個 bit**
void ws2812_send_bit(unsigned char bit_1) {
    if (bit_1) {
        WS2812_PIN = 1; // 發(fā)送 1
        delay_us(1);
        WS2812_PIN = 0;
    } else {
        WS2812_PIN = 1; // 發(fā)送 0
        WS2812_PIN = 0;
    }
}

// **發(fā)送一個字節(jié)**
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);  // 低電平持續(xù)時間大于 280us
}

// **發(fā)送整個 LED 數(shù)組**
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 發(fā)表于 2025-4-9 18:14 | 只看該作者
不用循環(huán),用位bit變量,只用if來硬寫24次賦值,估計還是可以夠用的
回復

使用道具 舉報

11#
ID:1088185 發(fā)表于 2025-4-10 21:40 | 只看該作者
最近才幫客人解決同樣的問題, 他還需要同時接收數(shù)據(jù), 我給的第一條意見就是換MCU。
回復

使用道具 舉報

12#
ID:1088185 發(fā)表于 2025-4-10 21:47 | 只看該作者
lzts88 發(fā)表于 2025-4-8 09:54
用匯編寫,極限優(yōu)化可以。WS2812最低時序220--380nS,極限優(yōu)化剛好可3T*125=375nS完成電平變換,要點是不用 ...

24位只是一顆燈, 你還要再加個判斷下一個燈的數(shù)據(jù)完沒完, 再加下一數(shù)據(jù)加載, 再來一個判斷是否最后一數(shù)據(jù)
回復

使用道具 舉報

13#
ID:285258 發(fā)表于 2025-4-11 16:48 | 只看該作者
gods701_LUO 發(fā)表于 2025-4-8 16:03
我不會寫匯編,今天用邏輯分析儀抓了一下波形,一個nop要300-400ns去了 時間也不是很準,然后執(zhí)行一條RA5=1 ...

任何單片機都可以跑ws2812,看燈珠數(shù)量,串聯(lián)的燈珠數(shù)量越多每次刷新獨霸cpu的時間就越長。
回復

使用道具 舉報

14#
ID:1113066 發(fā)表于 2025-4-11 17:40 | 只看該作者
您好,我這邊是ws2812的原廠家,你寫的時序不對,可以聯(lián)系我,發(fā)一份示例程序給你。
回復

使用道具 舉報

15#
ID:341045 發(fā)表于 2025-4-11 20:49 | 只看該作者
1600277881 發(fā)表于 2025-4-10 21:47
24位只是一顆燈, 你還要再加個判斷下一個燈的數(shù)據(jù)完沒完, 再加下一數(shù)據(jù)加載, 再來一個判斷是否最后一 ...

時序只是對應一個燈,下一個燈的延時不影響
回復

使用道具 舉報

16#
ID:341045 發(fā)表于 2025-4-11 20:52 | 只看該作者
1600277881 發(fā)表于 2025-4-10 21:40
最近才幫客人解決同樣的問題, 他還需要同時接收數(shù)據(jù), 我給的第一條意見就是換MCU。

用匯編就可以
回復

使用道具 舉報

17#
ID:401564 發(fā)表于 2025-4-12 13:07 | 只看該作者
gods701_LUO 發(fā)表于 2025-4-8 16:03
我不會寫匯編,今天用邏輯分析儀抓了一下波形,一個nop要300-400ns去了 時間也不是很準,然后執(zhí)行一條RA5=1 ...

不需要匯編
設定好之后,一個NOP就應該是對應的125uS,如果是300uS,那就是你代碼或者配置的問題
這種單片機隨便寫2812的,不管多少個燈
寫入的時候是要關(guān)閉所有中斷的,不然顏色很容易出錯
如果程序需要高的效率,肯定是不能一下子寫完所有的燈,燈多的話,可能會卡個幾十mS的,這肯定是不行的
你可以把代碼邏輯優(yōu)化一下,每5mS寫一個燈,然后去執(zhí)行一下主程序的其它代碼,下一個5mS再寫第二個燈,這樣下來,100個燈的用時會用到500mS,整體效果不會差太多,或者是改成1mS寫入一個燈
市場上大多的2812都是用這個低端單片機控制的,想改不太現(xiàn)實的
有的是因為單片機供應商是固定的,有的是不想換單片機,產(chǎn)品后期維護太難,有的就是成本問題,大批量產(chǎn)品,1分錢的成本都是很敏感的
回復

使用道具 舉報

18#
ID:1067318 發(fā)表于 2025-4-15 22:22 | 只看該作者
匯編寫,用STC8單片機11.0592時鐘沒問題
回復

使用道具 舉報

19#
ID:1142265 發(fā)表于 2025-4-16 13:50 | 只看該作者
我自己修改了一下,先整體實現(xiàn) 點燈效果,然后再去套for循環(huán)就可以了,如果用for循環(huán)去實現(xiàn) 根本就點不亮,因為一個for循環(huán)就要有1.875us的延時,一條if判斷語句要有20多us的延時,不用匯編寫,只能另辟蹊徑
回復

使用道具 舉報

20#
ID:1093493 發(fā)表于 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 發(fā)表于 2025-4-16 16:43 | 只看該作者
芯片有SPI接口嗎?如果你的SPI能跑到6M的時鐘,就可以用SPI發(fā)送0xc0作為bit 0,發(fā)送0xf8作為bit 1,來實現(xiàn)全部字節(jié)的發(fā)送了
回復

使用道具 舉報

22#
ID:401564 發(fā)表于 2025-4-17 12:51 | 只看該作者
gods701_LUO 發(fā)表于 2025-4-16 13:50
我自己修改了一下,先整體實現(xiàn) 點燈效果,然后再去套for循環(huán)就可以了,如果用for循環(huán)去實現(xiàn) 根本就點不亮, ...

你這個代碼本身就有問題
第一:  for(j = 0; j < 24; j++),在這里,你定義了int j,那么,j就是一個16位的數(shù)據(jù)了,就要用到兩個字節(jié)在存放,8位單片機處理兩個字節(jié)數(shù)據(jù)是很耗時間的,至少在WS2812中是這樣的
第二: 底下定義的val也有問題,它是一個 long int val;,這就是4個字節(jié)的數(shù)據(jù),這不更耗時了叫嗎?
這是我一個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 發(fā)表于 2025-4-17 13:25 | 只看該作者
Y_G_G 發(fā)表于 2025-4-17 12:51
你這個代碼本身就有問題
第一:  for(j = 0; j < 24; j++),在這里,你定義了int j,那么,j就是一個16位的數(shù) ...

點評一下,這位大佬,心腸好,技術(shù)高!完畢!
回復

使用道具 舉報

您需要登錄后才可以回帖 登錄 | 立即注冊

本版積分規(guī)則

手機版|小黑屋|51黑電子論壇 |51黑電子論壇6群 QQ 管理員QQ:125739409;技術(shù)交流QQ群281945664

Powered by 單片機教程網(wǎng)

快速回復 返回頂部 返回列表