GPIO アナログ タイミング制御ペリフェラル 1 - WS2812B

序文

ボード全体の最も基本的な機能モジュールは、前回の記事で紹介しました。GPIO の汎用入出力を使用して、簡単なキー入力とプッシュプル出力制御機能を実現します。この記事では、GPIO の入出力機能のみを使用することに基づいて、対応するモジュールのチップ マニュアルを参照し、対応するモジュールを駆動するための対応する通信タイミングをシミュレートすることにより、さらに一歩進めます。

WS2812B

まず、RGBライト界隈の覇者ともいえる、インターネット有名モジュールWS2812Bカラーライトがあり、ドライバが内蔵されており、3色(255*255*)のフル表示を実現できます。 255 = 16777216 色) トゥルー カラーで、シリアル制御をサポートするマイクロコントローラーは、GPIO を介してライトのグループを制御できます。詳細な機能については、チップのマニュアルの紹介を参照してください。
ここに画像の説明を挿入
ここに画像の説明を挿入

1. モジュールの紹介

ここに画像の説明を挿入
マニュアルの製品概要から、その一般的な制御ロジックを確認できます:
1. 単一のライトを制御するには 24 ビットのデータが必要です;
2. 複数のポイントを同時に制御する場合、対応する数のライトを送信する必要があります。直列に接続されたライトの数に応じて点灯します 24 ビット制御データ;
3. リセット時間は 280us 以上必要です。
ここに画像の説明を挿入
24 ビットの制御データについて説明しましたが、この 24 ビットのデータの各ビットは何を意味するのでしょうか。
以下の図に示すように、データシートにも紹介されています: 24bit データは、緑、赤、青の 3 色の色度データで結合され、データが 0xFF 00 00 の場合、対応するライトが緑色に点灯しますデータが 0x 00 FF 00 の場合はスモールライトが赤、データが 0x 00 00 FF の場合はスモールライトが青になります。さまざまな値に応じて、さまざまな色を組み合わせることができます。
ここに画像の説明を挿入
これは、誰もが必要な色を見つけるためのカラーテーブルです - https://tool.oschina.net/commons?type=3
ここに画像の説明を挿入
書き込まれる 24 ビットデータの順序は GRB (緑、赤、青) であることに注意してください。ここで問い合わせるカラーデータはRGB(赤、緑、青)であり、制御する際は順序を変更する必要があります。
さて、この時点でコードを書き始めてもいいでしょうか?

2. タイミングの概要

答えは「ノー」で、データ伝送の安定性を向上させるために、信号伝送プロセスにおける干渉を可能な限り排除する必要があり、モジュールはレベルの 0 と 1 を区別するための特別な出力フォーマットを採用します。前回の記事のGPIO入出力では、STM32の「0」がローレベル、「1」がハイレベルに対応していましたが、WS2812Bでは「0」と「1」は単純なハイレベルとローレベルに対応しません。 , しかし、次のタイミング波形図に基づく必要があります:
ここに画像の説明を挿入
この説明を直接見るのは少し抽象的かもしれません. ここでは、著者はロジック アナライザを使用して波形のセクションをキャプチャしています. 上記の説明と合わせて分析しましょう:
ここに画像の説明を挿入
これは、STM32 アナログ タイミングを使用して、RGB ディスプレイ 113355 の青色データ ピンの実際のサンプリング波形を制御します。比較のための上記のタイミング波形図によれば、
ここに画像の説明を挿入
波形全体で送信されるデータ内容を順番に読み取ることができ、
0011 0011 0001 0001 0101 0101はそれぞれ GRB() 緑、赤、青の3
色に対応します。
波形の終わりは非常に長く、長い Low レベルは、対応するリセット信号です。
ロジック アナライザで分析された最終データは次の図に示されています。
ここに画像の説明を挿入
これまでのところ、WS2812B 単一ランプの通信タイミングは基本的に把握されています。まだ決定されていない唯一のデータは、それぞれ 0 と 1 の送信に対応する特定のハイレベルとローレベルの時間です。タイミング図は範囲として示されており、実際の使用では微調整する必要があります。後でコードを書いてから決定してください。
単一の照明の制御が完了したら、次のステップは複数の照明のシリアル制御です
ここに画像の説明を挿入
上図のデータ送信ロジックによれば、複数の照明を制御する場合は、複数のライトのセットを送信するだけでよいことがわかります。 24 ビット データを連続的に送信します。たとえば、8 つのライトを制御したい場合は、一度に 8 個の 24 ビット データを途中で連続して送信します。変更する必要がある場合は、その部分に 280us を超えるリセット信号を追加するだけです。終わり。
下図の赤い部分が実際に送信されるデータの波形で、真ん中のローレベル端がリセット信号です。
ここに画像の説明を挿入
理解を容易にするために、上図の中央にある赤い波形を拡大すると、24 ビットの制御データが一度に 2 つ送信され、このとき小さなライトが 2 つ直列に点灯していることがわかります。
ここに画像の説明を挿入
さて、制御の概念を基本的に理解したので、マニュアルで注意が必要な他のパラメータを見てみましょう。

3. ハードウェアの紹介

各ランプビーズには以下に示すように 4 つのピンがあり、そのうち VDD と VSS は電源を供給し、DIN は入力信号、DOUT はデータ出力ピンであり、1 つのランプのデータ出力ピンをデータに接続することでシリアル制御を実現できます。後者のランプの入力ピン。ここでは、ピン紹介図を比較して対応するピンを見つけることができます。これは、ロジック アナライザを使用して波形をキャプチャしたり、その他のデバッグを行うのに便利です。
ここに画像の説明を挿入
ここに画像の説明を挿入

4. 伝送速度とフレーム数の要件

マニュアルのその他の内容のほとんどはハードウェアに関連するものであるため、ここでは詳しく説明しませんが、必要な学生は自分で確認してください。プログラミングで注意する必要がある重要なパラメータが 2 つあります。 1 つは、速度記録が 1 秒あたり 30 フレームを超える場合です。少なくとも 1024 個のライトが必要ですが、ここではライトが 8 つしかないため、1 秒以内に制御されるライト データ パケットの数は 30 を超えることはできません。
もう 1 つはより重要で、データ送信速度は 800Kbps に達する可能性があります。

少し知識を補間します。bps は bit/s、ビット レートとも呼ばれ、1 秒あたりに送信できるデータのビット数です。一方、
baud/s はボー レート、つまり 1 秒あたりのバイト数を意味します。 、1 秒 転送できるデータのバイト数。(1 バイトは 8 データビットに相当します)。
ここに画像の説明を挿入

つまり、WS2812B でサポートされる最高のデータ パケット伝送速度は、1 秒間に 800,000 個のバイナリ データを受信することであり、これは、各バイナリ ビットを少なくとも 1/800,000 = 1.25us 保持する必要があることを意味します。 、単一のデータ「0」または単一のデータ「1」は、異なるハイ時間とロー時間の期間、つまり、単一のデータ「1」または単一のデータ「0」のハイ時間とロー時間の合計で構成されます。 " 1250ns 未満にすることはできません。
T0H+T0L>1250ns
T1H+T1L>1250ns
ここに画像の説明を挿入
これを理解したら、ほぼコードを書き始めることができます。
その前に、回路図を見て、このボードの WS2812B の制御ピンを見つける必要がありますが、回路図から、制御ピンが GPIOA_8 であることがわかります。
ここに画像の説明を挿入
なぜ PA8 を選んだのか? ここで、筆者は両手で準備をしました. WS2812B の一般的な駆動方式には、DMA の高効率方式と連携するための PWM と SPI が含まれます. この駆動方式は CPU リソースを占有しません。純粋な GPIO アナログ出力 この解決策を考慮するために、著者は多重化機能を備えた IO ポートを選択しました。ここにはライトの数が少なく、ライトが 8 つあるだけなので、ソフトウェア シミュレーション ソリューションを選択するのは大きな問題ではありません。また、ここにある 4 つのタイマーは他のユーザーによって使用されています。干渉を避けるために、最終的にソフトウェア シミュレーション ソリューションを選択します。 . 達成する。PWM+DMA または SPI+DMA のスキームを知りたい場合は、自分で検索してください。CSDN の多くの大物がこれら 2 つのスキームを使用していることがわかり、非常に詳細に説明されています。
ここに画像の説明を挿入
もう1つは電源部分で、ここでは筆者は5V電源を選択します。

コード

1. 初期化

上記の概要によると、ここでは GPIO アナログ タイミング図のスキームが使用されています。まず、出力がハイとローである必要があるため、GPIOA8 は一般的なプッシュプル出力モードとして設定する必要がありますが、ここでは繰り返しません。

/*********************************
函数名:RGB_Init
函数功能:RGB初始化
形参:void
返回值:void
备注:
RGB-----PA8--------通用推挽输出
**********************************/
void RGB_Init(void)
{
    
    
	GPIO_InitTypeDef  GPIO_InitStructure;//定义一个结构体的变量
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//初始化GPIOA端口的时钟
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode =  GPIO_Mode_Out_PP;//通用推挽输出
	GPIO_Init(GPIOA,&GPIO_InitStructure );
}

操作の便宜上、高出力と低出力を出力するマクロを定義するのが最適ですが、ここで
注意が必要です 前回の紹介と合わせて、ライブラリ関数を使用して出力を制御できることが分かりました。 GPIOやビットバンド演算も利用可能で、レジスタを直接駆動することで実行効率が高く、レイテンシが低いソリューションもあります。ここでは 3 種類のマクロ定義をすべて掲載しますが、これはこのモジュールが ns の遅延を持たせるように設計されているためで、もう 1 つのコードを実行すると 30ns ~ 50ns かかり、制御効果に重大な影響を及ぼします。3 つのマクロ定義から 1 つを選択できます。最も高速なのはレジスタで、ビット バンド操作はライブラリ関数と似ています。

//位带操作
#define RGB_H   PAout(8)=1
#define RGB_L   PAout(8)=0
//库函数
#define RGB_H   GPIO_SetBits(GPIOA,GPIO_Pin_8)
#define RGB_L   GPIO_ResetBits(GPIOA,GPIO_Pin_8)
//直接操作寄存器(最快)
#define RGB_H   GPIOA->ODR |= (1<<8)
#define RGB_L   GPIOA->ODR &= ~(1<<8)

2. アナログタイミング

初期化が完了したら、最も重要な部分を開始します。タイミング図を比較してタイミングをシミュレーションします。
この種のタイミングをシミュレートする必要があるモジュールの場合、ボトムアップで考え、最も基本的な送信 "0" と送信 "1" から書き始め、送信 0 と送信 1 を対応する関数にカプセル化する必要があります。モジュールの実際のニーズに応じて、リセットや初期化などの最下位レベルの機能をカプセル化することも必要であり、WS2812B にはリセット機能も必要です。これらの機能をカプセル化した後、タイミングに合わせてデータパケット送信の機能カプセル化を行い、モジュール全体のタイミングコードをつなぎ合わせます。

1. リセット機能

データシートによると、リセット回路のタイミングは信号ピンを Low にプルすることであり、プルダウン時間は 280us 以上となっていますが、ここでは 350us とします。ここでの遅延は米国レベルであるため、システム ティックの米国遅延を使用することで実現できます。システム ティックは遅延の中で最も正確ですが、タイマーはそれほど正確ではないため、システム ティックを使用するのが最善ですシステムティックの導入は、タイマーである GPIO 多重化の部分とともに導入される予定であり、ここで最初に使用されます。

/*********************************
函数名:RGB_Reset
函数功能:RGB复位信号
形参:void
返回值:void
备注:
**********************************/
void RGB_Reset(void)
{
    
    
	RGB_L;
	Systick_Delay_us(350);//调用系统滴答来实现
}

2. 0、1コードを送信する

最初に解決する必要があるのは ns の遅延問題で、STM32F103C8T6 の主周波数は 72MHZ、つまり 1 秒で 72 000 000 個の機械語命令を実行できます。システム ティックとタイマーは us レベルの遅延のみを提供でき、この ns 遅延は固定数の機械語命令を実行することによってのみ実行できます。
計算プロセスは次のとおりです。1
秒で 72 000 000 の機械命令を実行でき、次に
1 μs で 72 の機械命令を実行できます。つまり、CPU が 72 の機械命令を実行するのにかかる時間は 1 μs です。この場合、1 つの機械命令に費やされる時間は 1us/72=13.88889ns になります。つまり、CPU が機械命令を実行するたびに 13.8889ns が経過します。
次に、特定のタイミング図を見てください。
ここに画像の説明を挿入
ここでは、コード 0 のハイレベル時間 T0H は 220ns ~ 380ns の間にある必要があり、ローレベル時間 T0L は 580ns ~ 1600ns の間で制御され、T0H+T0L>1250ns である必要があります。ここで最初に設定します
。 T0H、妥協、300ns を選択、
300/13.889=21.5999、切り上げ、22 の機械語命令を取得します。
その後、質問が来ます。通常は C コードを使用しますが、1 つの C コードは 1 つの機械語に等しいですか?
答えは「いいえ」です。各 C コードの基礎となるアセンブリ コードは異なるため、C コードと機械命令の間には固定された関係はありません。しかし幸いなことに、特別な機械命令 __nop() が存在します。__
nop() は機械命令です。
したがって、最初の遅延が得られます。

//220-380 ns折中 300 13.89*22=305.5558
void delay_300ns(void)  //72 000 000MHZ  ==1s  72hz 1us  一个机器指令周期要耗时13.89ns
{
    
    
	__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();
	__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();
	__nop();__nop();
}

次に、2 番目の遅延を解決するか、中間値を削除する解決策を採用するには、1090ns、
1090/13.889=78.4 を取り、78 に切り上げて
2 番目の遅延を取得します。

//折中 1090 78*13.889
void delay_1090ns(void)  //72 000 000MHZ  ==1s  72hz 1us  一个机器指令周期要耗时13.8ns
{
    
    
	__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();__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();
}

つまり、送信コードは 0 ヤードです

/*********************************
函数名:RGB_Send0
函数功能:RGB发送0
形参:void
返回值:void
备注:
**********************************/
void RGB_Send0()
{
    
    
	RGB_H;
	delay_300ns();
	RGB_L;
	delay_1090ns();
}

類推すると、1 コードの送信を終了し、上記の遅延を使用できますが、別の遅延 320ns を取得するのが最善です。測定された遅延_320 の効果はより優れています。

/*********************************
函数名:RGB_Send1
函数功能:RGB发送1
形参:void
返回值:void
备注:
**********************************/
void RGB_Send1()
{
    
    
	RGB_H;
	delay_1090ns();
	RGB_L;
	delay_320ns();
}

3. 送信関数をカプセル化する

前述したように、24 ビット データは GRB の 8 ビットに相当するため、8 ビット データ全体の送信関数を示します。上位ビットが最初に送信されるため、次のコードが得られます。


/*********************************
函数名:RGB_Send_Data
函数功能:RGB发送8位数据
形参:u8 data需要发送的数据
返回值:void
备注:
**********************************/
void RGB_Send_Data(u8 data) 
{
    
     
   uint8_t i;
   for(i=8;i>0;i--) 
   {
    
    
     if(data & 0x80)//按位与,为真发送1,为假发送零
     {
    
    
        RGB_Send1();
     }
     else
     {
    
    
        RGB_Send0();
     }
     data <<=1;//
   }
}

次に、24ビットを送信する関数をカプセル化します。

/*********************************
函数名:Send_GRB
函数功能:GRB发送24位数据
形参:u8 G,u8 R,u8 B
返回值:void
备注:
**********************************/
void Send_GRB(uint8_t G,uint8_t R,uint8_t B)
{
    
    
	RGB_Send_Data(G);
	RGB_Send_Data(R);
	RGB_Send_Data(B);
	RGB_Reset();
}

この関数を使用すると、単一のライトの制御はすでに実現できますが、
ここに画像の説明を挿入
ここに画像の説明を挿入
その後、背後のライトを制御するには、複数のライトを制御する関数をカプセル化する必要があります。上で分析したタイミング シーケンスによると、
コードは次のようになります。


/*********************************
函数名:Continuous_Set_LED
函数功能:设置n个灯为 GRB的颜色
形参:u8 G,u8 R,u8 B
返回值:uint8_t n多少个灯
,uint32_t GRB设置的颜色
备注:
**********************************/
void Continuous_Set_LED(uint8_t n,uint32_t GRB)
{
    
    
	while(n--)
	{
    
    
		RGB_Send_Data((GRB>>16)&0xFF);
		RGB_Send_Data((GRB>>8)&0xFF);
		RGB_Send_Data((GRB>>0)&0xFF);
	}
	RGB_Reset();
}

この関数を呼び出すと、指定した数のライトの制御を実現できます。もちろん、水を流したり、スクロールしたりすることもできます。これが必要な場合は、他の大手のコードを確認することができます。
ここに画像の説明を挿入
ここに画像の説明を挿入
ここに画像の説明を挿入

要約する

これで、WS2812B のアナログ ドライバの紹介は終了です。記事に不備があれば、ご批判、修正をお願いします。次の記事では、引き続きアナログ タイミング シーケンスを使用して、WS2812B の温度と湿度のデータの取得を完了します。 DHT11. まず、皆さんのためにキャプチャしたデータの波形解析の論理的な解析をここに載せておきます。
ここに画像の説明を挿入

ここに画像の説明を挿入

おすすめ

転載: blog.csdn.net/qq_41954556/article/details/131140231