欧美极品高清xxxxhd,国产日产欧美最新,无码AV国产东京热AV无码,国产精品人与动性XXX,国产传媒亚洲综合一区二区,四库影院永久国产精品,毛片免费免费高清视频,福利所导航夜趣136

 找回密碼
 立即注冊(cè)

QQ登錄

只需一步,快速開(kāi)始

搜索
查看: 7227|回復(fù): 0
收起左側(cè)

STC15F104W單片機(jī)驅(qū)動(dòng)WS2812程序

[復(fù)制鏈接]
ID:1015912 發(fā)表于 2022-5-15 19:49 | 顯示全部樓層 |閱讀模式
1,WS2812時(shí)序
WS2812是一個(gè)集控制電路與發(fā)光電路于一體的智能外控LED光源,每個(gè)ws2812均含有4個(gè)引腳,引腳功能如下圖:

WS2812可采用級(jí)聯(lián)的方式,將上一個(gè)WS2812的DOUT引腳連接到下一個(gè)WS2812的DIN引腳,即可實(shí)現(xiàn)一個(gè)引腳控制多個(gè)WS2812。一般自己設(shè)計(jì)電路時(shí)還要在電源輸入處添加一個(gè)0.1uF的小電容進(jìn)行濾波。注意,雖然理論上可以連接足夠多的ws2812,但是使用時(shí)要注意驅(qū)動(dòng)電壓和驅(qū)動(dòng)電流是否足夠驅(qū)動(dòng)這些彩燈。

由于只有一個(gè)引腳控制ws2812,所以我們只能通過(guò)控制引腳輸出高、低電平的時(shí)間,來(lái)讓W(xué)S2812知道我們想讓他顯示哪一個(gè)燈,顯示什么顏色。
想要讓1個(gè)ws2812顯示我們想要的顏色,我們需要給它發(fā)送顏色數(shù)據(jù)。每一個(gè)ws2812的顏色數(shù)據(jù)都是24位:8位綠色+8位紅色+8位藍(lán)色。

如果我們想控制1個(gè)ws2812,我們發(fā)送24位顏色數(shù)據(jù);如果我們想控制2個(gè)ws2812,我們需要連續(xù)發(fā)送48位顏色數(shù)據(jù);如果我們想控制n個(gè)ws2812,我們需要連續(xù)發(fā)送n*24位顏色數(shù)據(jù)。

那么ws2812如何知道我們發(fā)送的每一個(gè)位的數(shù)據(jù)是“1”還是“0”呢?
WS2812手冊(cè)中對(duì)于數(shù)據(jù)有明確的時(shí)間定義:

可以看出,當(dāng)我們想要發(fā)送一個(gè)位的數(shù)據(jù)時(shí),如果這個(gè)位是“1”,我們就控制單片機(jī)的引腳輸出高電平0.85us,然后控制引腳輸出低電平0.40us;如果這個(gè)位是“0”,則控制引腳輸出高電平0.40us,然后控制引腳輸出低電平0.85us。

而如果我們想控制1個(gè)燈顯示顏色,我們需要發(fā)送24個(gè)位的顏色數(shù)據(jù)給ws2812。如果我們想控制n個(gè)燈顯示顏色,我們從第1個(gè)燈的顏色數(shù)據(jù)開(kāi)始發(fā)送,直至發(fā)送完n*24個(gè)數(shù)據(jù)。最后我們需要控制單片機(jī)引腳輸出低電平超過(guò)50us,讓彩燈顯示顏色。

2,寫出驅(qū)動(dòng)程序
注意,STC15F104W只有8個(gè)引腳,我們一般采用內(nèi)部晶振電路。下面的代碼都是基于12MHz的晶振頻率寫的。WS2812對(duì)于單片機(jī)的引腳時(shí)序要求比較高。我們一般調(diào)用intrins.h這個(gè)頭文件的機(jī)器周期函數(shù),nop()函數(shù)執(zhí)行一次占用一個(gè)機(jī)器周期(此處時(shí)鐘周期和機(jī)器周期相等),所以每條機(jī)器周期時(shí)間為1/12MHz=83.3us。(代碼中很多處使用宏定義,宏定義在預(yù)編譯階段程序就完成代碼替代工作,不會(huì)影響程序的執(zhí)行時(shí)間,且修改方便)
下面是h文件代碼:

#ifndef __WS2812_H
#define __WS2812_H

//頭文件區(qū)
#include <STC15F2K60S2.H>
#include <intrins.h>

//用戶修改參數(shù)區(qū)
//#define WS2812_FREQUENCY
#define RGB_PIN           P33                       //控制彩燈引腳(需要配置為強(qiáng)推挽輸出)
#define WS2812_MAX        25                        //彩燈最大個(gè)數(shù)
#define WS2812_NUMBERS    8                         //彩燈個(gè)數(shù)


#define RED               0xff0000                  //紅色
#define GREEN             0x00ff00                  //綠色
#define BLUE              0x0000ff                  //藍(lán)色
#define BLACK             0x000000                  //熄滅
#define WHITE             0xffffff                  //白色

#define RGB_PIN_H()  RGB_PIN = 1
#define RGB_PIN_L()  RGB_PIN = 0
#define delay1NOP()  _nop_();
#define delay1NOP()  _nop_();
#define delay2NOP()  delay1NOP(); _nop_();
#define delay3NOP()  delay2NOP();_nop_();
#define delay5NOP()  delay3NOP();delay2NOP();
#define delay7NOP()  delay5NOP();delay2NOP();

void Ws2812b_WriteByte(unsigned char byte);//發(fā)送一個(gè)字節(jié)數(shù)據(jù)(@12.000MHz,理論每個(gè)機(jī)器周期83ns,測(cè)試約為76ns)                                                      
void setLedCount(unsigned char count);//設(shè)置彩燈數(shù)目,范圍0-25.                                                           
unsigned char getLedCount();//彩燈數(shù)目查詢函數(shù)                                                                     
void rgb_SetColor(unsigned char LedId, unsigned long color);//設(shè)置彩燈顏色                                    
void rgb_SetRGB(unsigned char LedId, unsigned long red, unsigned long green, unsigned long blue);//設(shè)置彩燈顏色
void rgb_SendArray();//發(fā)送彩燈數(shù)據(jù)                                                                           
#endif                                                                     

接著我們對(duì)c代碼進(jìn)行詳解。
首先調(diào)用h文件,然后定義一個(gè)數(shù)組,用來(lái)存放彩燈的顏色數(shù)據(jù)。這里數(shù)據(jù)存放在data空間(空間有限,只能存放25個(gè)燈左右的數(shù)據(jù)),后面有需要再優(yōu)化到xdata空間。ledsCount 記錄實(shí)際我們想要控制的彩燈的數(shù)目,nbLedsBytes 用來(lái)記錄彩燈的數(shù)據(jù)個(gè)數(shù)(一個(gè)燈需要3個(gè)字節(jié))。

#include "ws2812.h"

unsigned char LedsArray[WS2812_MAX * 3];      //定義顏色數(shù)據(jù)存儲(chǔ)數(shù)組
unsigned int ledsCount = WS2812_NUMBERS;      //定義實(shí)際彩燈默認(rèn)個(gè)數(shù)
unsigned int nbLedsBytes = WS2812_NUMBERS*3;  //定義實(shí)際彩燈顏色數(shù)據(jù)個(gè)數(shù)

接著我們開(kāi)始編寫彩燈的設(shè)置數(shù)目函數(shù),查詢數(shù)目函數(shù),以及兩個(gè)設(shè)置指定彩燈顏色的函數(shù)。

//設(shè)置彩燈數(shù)目,范圍0-25.
void setLedCount(unsigned char count)
{
    ledsCount = WS2812_MAX > count ? count : WS2812_MAX;
    nbLedsBytes = ledsCount*3;
}

//彩燈數(shù)目查詢函數(shù)
unsigned char getLedCount()
{
    return ledsCount;
}

//設(shè)置彩燈顏色(在這里我將綠和紅色進(jìn)行顛倒,這樣比較符合我們?nèi)粘I畹募t綠藍(lán)的順序)
void rgb_SetColor(unsigned char LedId, unsigned long color)
{
    if( LedId > ledsCount )
    {
        return;    //to avoid overflow
    }
    LedsArray[LedId * 3]     = (color>>8)&0xff;
    LedsArray[LedId * 3 + 1] = (color>>16)&0xff;
    LedsArray[LedId * 3 + 2] = (color>>0)&0xff;
}

//設(shè)置彩燈顏色
void rgb_SetRGB(unsigned char LedId, unsigned long red, unsigned long green, unsigned long blue)
{
    unsigned long Color=red<<16|green<<8|blue;
    rgb_SetColor(LedId,Color);
}


然后我們對(duì)彩燈數(shù)據(jù)通過(guò)引腳發(fā)送出去。注意,發(fā)送彩燈數(shù)據(jù)的過(guò)程中,請(qǐng)將中斷關(guān)閉,否則有可能會(huì)導(dǎo)致數(shù)據(jù)發(fā)送到一半被中斷打斷,導(dǎo)致顯示異常。Ws2812b_WriteByte()函數(shù)是這個(gè)驅(qū)動(dòng)代碼的核心。

//發(fā)送彩燈數(shù)據(jù)
void rgb_SendArray()
{
    unsigned int i;
    bit a=EA;
    //發(fā)送數(shù)據(jù)
    EA=0;
    for(i=0; i<nbLedsBytes; i++)
        Ws2812b_WriteByte(LedsArray[ i]);
    EA=a;
}

下面的Ws2812b_WriteByte函數(shù)是我基于stc15f104w調(diào)試的。我寫過(guò)不同單片機(jī)的驅(qū)動(dòng)。不同單片機(jī)的每條指令花費(fèi)的時(shí)間都不一樣。如果您想要采用其他系列的單片機(jī),我們只需要修改Ws2812b_WriteByte()函數(shù)里面的內(nèi)容即可。其他的函數(shù)可以不做修改。方便代碼的移植。
主要修改的地方是高電平總的拉高時(shí)間。這里之所以不對(duì)“1”和“0”的內(nèi)容進(jìn)行函數(shù)封裝,是為了我自己調(diào)試的方便。如果您覺(jué)得代碼不夠簡(jiǎn)短好看,您可以將判斷條件里面的內(nèi)容用宏定義進(jìn)行一下封裝。

/*
//使用12.000MHz頻率,理論每個(gè)機(jī)器周期83ns,實(shí)際測(cè)試約為76ns。
//下面都是基于76ns納秒一個(gè)機(jī)器周期計(jì)算的。
//測(cè)試時(shí)發(fā)現(xiàn)可以支持12-20Mhz頻率。
//如果想要使用11.0592Mhz頻率,在h文件取消“#define WS2812_FREQUENCY”的注釋
//在實(shí)際測(cè)試中發(fā)現(xiàn),ws2812對(duì)高電平時(shí)間較為敏感,對(duì)低電平時(shí)間不敏感
//也就是說(shuō),低電平時(shí)間可以稍微長(zhǎng)一些也不會(huì)影響程序,但是高電平時(shí)間需要控制的比較準(zhǔn)確才行。
*/
void Ws2812b_WriteByte(unsigned char byte)
{
#ifndef WS2812_FREQUENCY
    if(byte & 0x80)
    {
        RGB_PIN_H();//4個(gè)機(jī)器周期(STC15F104W的拉高拉低都需要4個(gè)機(jī)器周期,其他系列暫時(shí)不知道,但是很大可能不是4個(gè)機(jī)器周期)
        delay7NOP();//7個(gè)機(jī)器周期
        RGB_PIN_L();//4+8(跳出這個(gè)if判斷需要5個(gè)機(jī)器周期,再進(jìn)入下一個(gè)if判斷需要3個(gè)機(jī)器周期)
    }
    else
    {
        RGB_PIN_H();
        delay2NOP();//4+2
        RGB_PIN_L();
        delay3NOP();//4+3+5(跳出這個(gè)else需要3個(gè)機(jī)器周期,進(jìn)入下一個(gè)else則需要2個(gè)機(jī)器周期)
    }
    if(byte & 0x40)
    {
        RGB_PIN_H();//
        delay7NOP();//4+7
        RGB_PIN_L();//4+8
    }
    else
    {
        RGB_PIN_H();
        delay2NOP();//4+2
        RGB_PIN_L();
        delay3NOP();//4+3+5
    }
    if(byte & 0x20)
    {
        RGB_PIN_H();//
        delay7NOP();//4+7
        RGB_PIN_L();//4+8
    }
    else
    {
        RGB_PIN_H();
        delay2NOP();//4+2
        RGB_PIN_L();
        delay3NOP();//4+3+5
    }
    if(byte & 0x10)
    {
        RGB_PIN_H();//
        delay7NOP();//4+7
        RGB_PIN_L();//4+8
    }
    else
    {
        RGB_PIN_H();
        delay2NOP();//4+2
        RGB_PIN_L();
        delay3NOP();//4+3+5
    }
    if(byte & 0x8)
    {
        RGB_PIN_H();//
        delay7NOP();//4+7
        RGB_PIN_L();//4+8
    }
    else
    {
        RGB_PIN_H();
        delay2NOP();//4+2
        RGB_PIN_L();
        delay3NOP();//4+3+5
    }
    if(byte & 0x4)
    {
        RGB_PIN_H();//
        delay7NOP();//4+7
        RGB_PIN_L();//4+8
    }
    else
    {
        RGB_PIN_H();
        delay2NOP();//4+2
        RGB_PIN_L();
        delay3NOP();//4+3+5
    }
    if(byte & 0x2)
    {
        RGB_PIN_H();//
        delay7NOP();//4+7
        RGB_PIN_L();//4+8
    }
    else
    {
        RGB_PIN_H();
        delay2NOP();//4+2
        RGB_PIN_L();
        delay3NOP();//4+3+5
    }
    if(byte & 0x1)
    {
        RGB_PIN_H();//
        delay7NOP();//4+7
        RGB_PIN_L();//4+8
    }
    else
    {
        RGB_PIN_H();
        delay2NOP();//4+2
        RGB_PIN_L();
        delay3NOP();//4+3+5
    }
#else
    if(byte & 0x80)
    {
        RGB_PIN_H();//
        delay5NOP();//4+5
        RGB_PIN_L();//4+8
    }
    else
    {
        RGB_PIN_H();
        delay1NOP();//4+1
        RGB_PIN_L();
        delay2NOP();//4+2+5
    }
    if(byte & 0x40)
    {
        RGB_PIN_H();//
        delay5NOP();//4+5
        RGB_PIN_L();//4+8
    }
    else
    {
        RGB_PIN_H();
        delay1NOP();//4+1
        RGB_PIN_L();
        delay2NOP();//4+2+5
    }
    if(byte & 0x20)
    {
        RGB_PIN_H();//
        delay5NOP();//4+5
        RGB_PIN_L();//4+8
    }
    else
    {
        RGB_PIN_H();
        delay1NOP();//4+1
        RGB_PIN_L();
        delay2NOP();//4+2+5
    }
    if(byte & 0x10)
    {
        RGB_PIN_H();//
        delay5NOP();//4+5
        RGB_PIN_L();//4+8
    }
    else
    {
        RGB_PIN_H();
        delay1NOP();//4+1
        RGB_PIN_L();
        delay2NOP();//4+2+5
    }
    if(byte & 0x8)
    {
        RGB_PIN_H();//
        delay5NOP();//4+5
        RGB_PIN_L();//4+8
    }
    else
    {
        RGB_PIN_H();
        delay1NOP();//4+1
        RGB_PIN_L();
        delay2NOP();//4+2+5
    }
    if(byte & 0x4)
    {
        RGB_PIN_H();//
        delay5NOP();//4+5
        RGB_PIN_L();//4+8
    }
    else
    {
        RGB_PIN_H();
        delay1NOP();//4+1
        RGB_PIN_L();
        delay2NOP();//4+2+5
    }
    if(byte & 0x2)
    {
        RGB_PIN_H();//
        delay5NOP();//4+5
        RGB_PIN_L();//4+8
    }
    else
    {
        RGB_PIN_H();
        delay1NOP();//4+1
        RGB_PIN_L();
        delay2NOP();//4+2+5
    }
    if(byte & 0x1)
    {
        RGB_PIN_H();//
        delay5NOP();//4+5
        RGB_PIN_L();//4+8
    }
    else
    {
        RGB_PIN_H();
        delay1NOP();//4+1
        RGB_PIN_L();
        delay2NOP();//4+2+5
    }
#endif
}


main文件里面,我是用stc生成了一個(gè)300ms的延時(shí)函數(shù)。讓程序每隔300ms更換一下顏色并輸出。紅綠藍(lán)三色交替閃爍。

#include <STC15F2K60S2.H>
#include "ws2812.h"
void Delay300ms();//@12.000MHz
void main()
{
    int i=0;
    setLedCount(12);//可以設(shè)置0-25之間的任何數(shù)目,根據(jù)實(shí)際情況而定
    rgb_SetRGB(0,0,0,0);//設(shè)置第一個(gè)燈關(guān)閉(這里作為使用示例,下面的rgb_SetColor覆蓋了這個(gè)函數(shù)的作用)
    while(1)
    {
        for(i=0; i<getLedCount(); i++)//設(shè)置所有的燈為紅色
        {
            rgb_SetColor(i,RED);
        }
        rgb_SendArray();//發(fā)送給ws2812,顯示顏色
        Delay300ms();
        for(i=0; i<getLedCount(); i++)//設(shè)置所有的燈為綠色
        {
            rgb_SetColor(i,GREEN);
        }
        rgb_SendArray();//發(fā)送給ws2812,顯示顏色
        Delay300ms();
        for(i=0; i<getLedCount(); i++)//設(shè)置所有的燈為藍(lán)色
        {
            rgb_SetColor(i,BLUE);
        }
        rgb_SendArray();//發(fā)送給ws2812,顯示顏色
        Delay300ms();
    }
}
void Delay300ms()//@12.000MHz
{
    unsigned char i, j, k;
    _nop_();
    _nop_();
    i = 14;
    j = 174;
    k = 224;
    do
    {
        do
        {
            while (--k);
        } while (--j);
    } while (--i);
}

3,軟件調(diào)試
確保您的芯片型號(hào)選擇正確。

填寫軟件調(diào)試的晶振頻率,我這里使用16MHz(只是用來(lái)軟件計(jì)算機(jī)器周期,和實(shí)際晶振無(wú)關(guān))。1/16MHz=62.5ns。如果采用12MHz,我計(jì)算機(jī)器周期不太方便。

點(diǎn)擊調(diào)試。沒(méi)有配置時(shí)默認(rèn)是軟件調(diào)試模式。
點(diǎn)擊引腳信號(hào)的邏輯分析。

在新彈出的窗口中點(diǎn)擊Setup。然后輸入您的控制引腳。

使用調(diào)試控件,一邊觀察波形圖。

在程序執(zhí)行RGB_PIN_H();這一句之前,我用紅色的線定位了此時(shí)的時(shí)間位置,執(zhí)行這一句后,我用藍(lán)色的線查看它們之間的差值,差值為0.25us。這里我的軟件仿真使用的是16MHz晶振,每個(gè)機(jī)器周期為62.5ns。0.25us相當(dāng)于4個(gè)機(jī)器周期。正如我上面的發(fā)送函數(shù)里面的機(jī)器周期備注一樣。同理可以計(jì)算出每行代碼需要花費(fèi)的機(jī)器周期時(shí)間,從而準(zhǔn)確寫出驅(qū)動(dòng)。

比如,我們需要0.85us的高電平時(shí)間,也就是850ns,然后實(shí)際上我們使用的晶振是12MHz。每個(gè)機(jī)器周期的時(shí)間是理論是1/12Mhz=83.3ns。那么我們就需要850/83.3=10.2個(gè)機(jī)器周期。這里取11個(gè)機(jī)器周期。根據(jù)前面我們知道電平拉高需要用去4個(gè)機(jī)器周期,因此我們還需要讓高電平維持7個(gè)機(jī)器周期的時(shí)間,然后將電平拉低。以此原理,寫出我們的Ws2812b_WriteByte()函數(shù)的驅(qū)動(dòng)。

4,硬件調(diào)試
實(shí)際硬件調(diào)試時(shí),我寫了一個(gè)測(cè)試程序。我用軟件調(diào)試時(shí),波形為引腳電平拉高10個(gè)機(jī)器周期,然后拉低10個(gè)機(jī)器周期。循環(huán)動(dòng)作。

while(1)
{
        P33=1;//4
        delay6NOP();//6
        P33=0;//4
        delay2NOP();//2+4
}
但是實(shí)際用示波器測(cè)試時(shí),發(fā)現(xiàn)高電平時(shí)間和低電平時(shí)間均為3.8格*200ns/格=760ns。所以實(shí)際上每個(gè)機(jī)器周期的時(shí)間我這里是76ns左右。距離理論值83.3ns相差不多,而且ws2812每個(gè)高電平信號(hào)都可以有150ns左右的誤差,因此這里按照理論計(jì)算和按照實(shí)際計(jì)算均可。我按理論值計(jì)算。
————————————————
版權(quán)聲明:本文為CSDN博主「DIY愛(ài)好玩家」的原創(chuàng)文章,遵循CC 4.0 BY-SA版權(quán)協(xié)議,轉(zhuǎn)載請(qǐng)附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/weixin_38476200/article/details/115519393

回復(fù)

使用道具 舉報(bào)

本版積分規(guī)則

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

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

快速回復(fù) 返回頂部 返回列表