STM32はHAL_DMA_PWMモードを使用して、WS2812フルカラーランプビーズを駆動します

1.WS2812フルカラーランプビーズの原理と駆動モード

ここでは、WS2812シリーズのランプビーズの紹介については説明しません。オンラインリソースはたくさんあります。チュートリアルが見つからない場合は、この記事を参照してください:STM32はWS2812DフルカラーLEDを駆動します
タイミング波形図は、後で説明するためにここに添付されています。

ここに画像の説明を挿入
WS2812を駆動するための鍵は、800KHzの方形波を出力することであり、2つ目は、WS2812が認識できるロジック1と0を出力するために、各サイクルの高レベルと低レベルの時間を正確に制御することです。その後、出力バッファ配列を定義し、配列に数値を入力するだけで済みます。重要なのは、方形波を出力する方法を選択することです。結論として:

(1)最も単純で最も馬鹿げた方法、高レベルと低レベルのフリップを実現するためのソフトウェア遅延、STM32F1シリーズの主周波数は72MHz、F7シリーズは216MHzに達し、出力800KHzの方形波で十分です。使用する場合は、オシロスコープまたは論理解析機器は、結局のところ、ソフトウェアの遅延が不正確であることを監視します。

(2)STM32のPWM連続転送モード、つまりDMA出力PWMを使用すると、高精度の方形波を出力でき、パルス数とデューティサイクルを調整できます。

(3)STM32ハードウェアSPIを使用してデータを転送することで、高精度も実現できます。

もちろん、方法1はお勧めしません。精度は保証できません。CPUリソースの大きな浪費です。プログラムがWS2812のみを駆動する場合、他のタスクがある場合、CPUはそれを遅らせる。複雑なシステムでは、WS2812は、CPUリソースを独占するために、インジケーターライトまたは追加のカラフルな効果としてのみ使用する必要がありますか?プロジェクトは黄色になる可能性があります。

したがって、方法2と方法3をお勧めします。この記事では、HALライブラリのDMA出力PWMモードを使用してWS2812を制御します。

DMA出力PWMの原理とエンジニアリング構成については、次の記事を参照してください。STM32F1/ F7は、HALライブラリDMAメソッドを使用してPWMの詳細を出力します(正確な量と調整可能な周期とデューティサイクルを出力します)

2. STM32は、HALライブラリを使用して、PWM制御WS2812を出力するようにDMAモードを構成します。

以前のSTM32F1 / F7に基づいて、HALライブラリDMAメソッドを使用してPWMの詳細な説明(正確な数量と調整可能な期間とデューティサイクルを出力)プロジェクトを出力し、WS2812.cファイルとWS2812.hファイルをプロジェクトに追加します。

1つ目はPWMの構成です。この例で使用されているSTM32F767チップのメイン周波数は216MHzで、WS2812を駆動するようにT4_CH2を構成します。周波数分割が0の場合、周波数分割がないことを意味します。周期は1 / 108M = 9.26ns、自動リロード値は135です。周期は1.25us、周波数は800KHzです。たまたまWS2812の制御周波数です。

WS2812.h

#ifndef _WS2812_H
#define _WS2812_H
#endif

#include "main.h"
#include "delay.h"

#define PIXEL_NUM  64
#define NUM (24*PIXEL_NUM + 300)        // Reset 280us / 1.25us = 224
#define WS1 100
#define WS0  35

extern uint16_t send_Buf[NUM];

void WS_Load(void);
void WS_WriteAll_RGB(uint8_t n_R, uint8_t n_G, uint8_t n_B);
void WS_CloseAll(void);

uint32_t WS281x_Color(uint8_t red, uint8_t green, uint8_t blue);
void WS281x_SetPixelColor(uint16_t n, uint32_t GRBColor);
void WS281x_SetPixelRGB(uint16_t n ,uint8_t red, uint8_t green, uint8_t blue);

uint32_t Wheel(uint8_t WheelPos);
void rainbow(uint8_t wait);
void rainbowCycle(uint8_t wait);

WS2812.hで送信バッファuint16_tsend_Buf [NUM]を定義します。NUMの値は、単一のランプビーズのビット幅(24)*ランプビーズの数(PIXEL_NUM)+リセットパルスの数です。ここでは、リセット時間が満たされている限り、300、300 * 9.26ns> 280usを指定します。

マクロWS1とWS0は、それぞれ1と0を出力するために設定する必要があるPWM比較値を表し、100と35に設定するとタイミング要件を満たすことができます。

WS2812.cでは、WS2812のドライバー関数を記述できます。最初に、伝達開始関数を定義します。

void WS_Load(void)
{
    
    
	HAL_TIM_PWM_Start_DMA(&htim4, TIM_CHANNEL_2, (uint32_t *)send_Buf, NUM);
}

機能はDMA-PWM転送を開始することです。

すべてのライトをオフにします。

void WS_CloseAll(void)
{
    
    
	uint16_t i;
	for (i = 0; i < PIXEL_NUM * 24; i++)
		send_Buf[i] = WS0; // 写入逻辑0的占空比
	for (i = PIXEL_NUM * 24; i < NUM; i++)
		send_Buf[i] = 0; // 占空比比为0,全为低电平
	WS_Load();
}

すべてのライトを同じ色に設定します。

void WS_WriteAll_RGB(uint8_t n_R, uint8_t n_G, uint8_t n_B)
{
    
    
	uint16_t i, j;
	uint8_t dat[24];
	// 将RGB数据进行转换
	for (i = 0; i < 8; i++)
	{
    
    
		dat[i] = ((n_G & 0x80) ? WS1 : WS0);
		n_G <<= 1;
	}
	for (i = 0; i < 8; i++)
	{
    
    
		dat[i + 8] = ((n_R & 0x80) ? WS1 : WS0);
		n_R <<= 1;
	}
	for (i = 0; i < 8; i++)
	{
    
    
		dat[i + 16] = ((n_B & 0x80) ? WS1 : WS0);
		n_B <<= 1;
	}
	for (i = 0; i < PIXEL_NUM; i++)
	{
    
    
		for (j = 0; j < 24; j++)
		{
    
    
			send_Buf[i * 24 + j] = dat[j];
		}
	}
	for (i = PIXEL_NUM * 24; i < NUM; i++)
		send_Buf[i] = 0; // 占空比比为0,全为低电平
	WS_Load();
}

main関数でWS_WriteAll_RGB()を呼び出すと、すべてのランプビーズが点灯します。たとえば、WS_WriteAll_RGB(0xFF、0,0)は、すべてのライトを緑色に設定します。

いくつかのクールな照明効果は、以下で達成できます。

単一のランプの色を設定します(2つの機能はそれぞれ異なる形式で色を設定します):

uint32_t WS281x_Color(uint8_t red, uint8_t green, uint8_t blue)
{
    
    
	return green << 16 | red << 8 | blue;
}

void WS281x_SetPixelColor(uint16_t n, uint32_t GRBColor)
{
    
    
	uint8_t i;
	if (n < PIXEL_NUM)
	{
    
    
		for (i = 0; i < 24; ++i)
			send_Buf[24 * n + i] = (((GRBColor << i) & 0X800000) ? WS1 : WS0);
	}
}

void WS281x_SetPixelRGB(uint16_t n, uint8_t red, uint8_t green, uint8_t blue)
{
    
    
	uint8_t i;

	if (n < PIXEL_NUM)
	{
    
    
		for (i = 0; i < 24; ++i)
			send_Buf[24 * n + i] = (((WS281x_Color(red, green, blue) << i) & 0X800000) ? WS1 : WS0);
	}
}

ここでは、レインボーライト効果を実現するために2つの関数のみを移植しました。

uint32_t Wheel(uint8_t WheelPos)
{
    
    
	WheelPos = 255 - WheelPos;
	if (WheelPos < 85)
	{
    
    
		return WS281x_Color(255 - WheelPos * 3, 0, WheelPos * 3);
	}
	if (WheelPos < 170)
	{
    
    
		WheelPos -= 85;
		return WS281x_Color(0, WheelPos * 3, 255 - WheelPos * 3);
	}
	WheelPos -= 170;
	return WS281x_Color(WheelPos * 3, 255 - WheelPos * 3, 0);
}

void rainbow(uint8_t wait)
{
    
    
	uint32_t timestamp = HAL_GetTick();
	uint16_t i;
	static uint8_t j;
	static uint32_t next_time = 0;

	uint32_t flag = 0;
	if (next_time < wait)
	{
    
    
		if ((uint64_t)timestamp + wait - next_time > 0)
			flag = 1;
	}
	else if (timestamp > next_time)
	{
    
    
		flag = 1;
	}
	if (flag) // && (timestamp - next_time < wait*5))
	{
    
    
		j++;
		next_time = timestamp + wait;
		for (i = 0; i < PIXEL_NUM; i++)
		{
    
    
			WS281x_SetPixelColor(i, Wheel((i + j) & 255));
		}
	}
	WS_Load();
}

void rainbowCycle(uint8_t wait)
{
    
    
	uint32_t timestamp = HAL_GetTick();
	uint16_t i;
	static uint8_t j;
	static uint32_t next_time = 0;

	static uint8_t loop = 0;
	if (loop == 0)
		next_time = timestamp;
	loop = 1; //首次调用初始化

	if ((timestamp > next_time)) // && (timestamp - next_time < wait*5))
	{
    
    
		j++;
		next_time = timestamp + wait;
		for (i = 0; i < PIXEL_NUM; i++)
		{
    
    
			WS281x_SetPixelColor(i, Wheel(((i * 256 / (PIXEL_NUM)) + j) & 255));
		}
	}
	WS_Load();
}

あなたが自分で開発する他の興味深い照明効果があります。

コードリソースはCSDNにアップロードされ、無料でダウンロードできます。ぜひ気に入ってください。

ポータル:STM32F767はHAL_DMA_PWMを使用してWS2812ランプビーズを駆動します

おすすめ

転載: blog.csdn.net/qq_30267617/article/details/109600880