フルカラーLEDテープのDMAを使った完全自動制御  投稿者:ケンケン  投稿日:2018年11月25日(日)12時18分39秒 NE0356lan1.rev.em-net.ne.jp 返信・引用
  フルカラーのLEDテープを購入して遊んでいます。購入したのはこれです。
https://www.amazon.co.jp/ALITOVE-WS2812B-LED%E3%83%86%E3%83%BC%E3%83%971m-NeoPixel-LED%E3%83%86%E3%83%BC%E3%83%97%E3%83%A9%E3%82%A4%E3%83%88%E3%83%94%E3%82%AF%E3%82%BB%E3%83%AB/dp/B01N2NBVC2/

LED1つ1つに簡易マイコンが内蔵されていてシリアルに連結されているので、1番目のLEDにシリアル信号を送るだけで任意のLEDを任意の色で点灯させることができます。
28ピンのPIC32MX1xx、PIC32MX2xxシリーズで動作させてみました。せっかくなのでソースを公開します。RA0にLEDのシリアルINを接続します。システムクロックは内蔵FSRで48MHzです。

このLEDはパルス幅で1、0を認識するので、信号出力にはPWM機能を使いました。それをDMA機能を使って完全自動で連続出力させています。LEDが144個の場合、毎秒約280回書き換えが行われますが、信号出力にはCPUは一切介在していません。
そのため、CPUは好きなタイミングでLEDのカラーバッファを更新することができます。このサンプルではタイマー割り込みで0.01秒ごとにバッファを更新しています。

ツイッターに動画を上げているのでよかったら見てください.
https://twitter.com/KenKenMkIISR/status/1066333754203955201

------------------------------------------------------------------

// Tape LED (WS2812B)をPIC32MX1xx/2xxのPWM+DMAで制御
// System Clock: 48MHz with FSR
// Serial Output: RA0

// DEVCFG3
// USERID = No Setting
#pragma config PMDL1WAY = OFF           // Peripheral Module Disable Configuration (Allow multiple reconfigurations)
#pragma config IOL1WAY = OFF            // Peripheral Pin Select Configuration (Allow multiple reconfigurations)
#pragma config FUSBIDIO = OFF           // USB USID Selection (Controlled by Port Function)
#pragma config FVBUSONIO = OFF          // USB VBUS ON Selection (Controlled by Port Function)

// DEVCFG2
#pragma config FPLLIDIV = DIV_2         // PLL Input Divider (2x Divider)
#pragma config FPLLMUL = MUL_24         // PLL Multiplier (24x Multiplier)
#pragma config FPLLODIV = DIV_2         // System PLL Output Clock Divider (PLL Divide by 2)

// DEVCFG1
#pragma config FNOSC = FRCPLL           // Oscillator Selection Bits (Fast RC Osc with PLL)
#pragma config FSOSCEN = OFF            // Secondary Oscillator Enable (Disabled)
#pragma config IESO = OFF               // Internal/External Switch Over (Disabled)
#pragma config POSCMOD = OFF            // Primary Oscillator Configuration (Primary osc disabled)
#pragma config OSCIOFNC = OFF           // CLKO Output Signal Active on the OSCO Pin (Disabled)
#pragma config FPBDIV = DIV_1           // Peripheral Clock Divisor (Pb_Clk is Sys_Clk/1)
#pragma config FCKSM = CSDCMD           // Clock Switching and Monitor Selection (Clock Switch Disable, FSCM Disabled)
#pragma config WDTPS = PS1048576        // Watchdog Timer Postscaler (1:1048576)
#pragma config WINDIS = OFF             // Watchdog Timer Window Enable (Watchdog Timer is in Non-Window Mode)
#pragma config FWDTEN = OFF             // Watchdog Timer Enable (WDT Disabled (SWDTEN Bit Controls))

// DEVCFG0
#pragma config JTAGEN = OFF             // JTAG Enable (JTAG Disabled)
#pragma config ICESEL = ICS_PGx1        // ICE/ICD Comm Channel Select (Communicate on PGEC1/PGED1)
#pragma config PWP = OFF                // Program Flash Write Protect (Disable)
#pragma config BWP = OFF                // Boot Flash Write Protect bit (Protection Disabled)
#pragma config CP = OFF                 // Code Protect (Protection Disabled)

#include 
#include 
#include 

#define NUMofLED 144 //連結LEDの個数
#define PERIOD 46 //LED1個分の信号周期(H+L)クロック数
#define VAL_0 12 //T0Hのクロック数
#define VAL_1 31 //T1Hのクロック数
#define RESTIM 292 //RESの期間 280μs*SYSTEMCLOCK/PERIOD

unsigned char DMAbuffer[NUMofLED * 24 + RESTIM]; //DMA出力用バッファ
unsigned int LEDbuffer[NUMofLED]; //LED色情報格納バッファ

void setLED(unsigned int pos, unsigned char r, unsigned char g, unsigned char b) {
    // 個別LEDの色を設定
    // pos:LEDの位置
    // r,g,b:0-255で明るさを設定

    unsigned char *p;
    int i;
    unsigned int d;
    if (pos >= NUMofLED) return;
    p = DMAbuffer + pos * 24;
    d = (g << 24)+(r << 16)+(b << 8);
    LEDbuffer[pos] = d >> 8;
    for (i = 0; i < 24; i++) {
        if (d & 0x80000000) *p = VAL_1;
        else *p = VAL_0;
        p++;
        d <<= 1;
    }
}

int main(void) {
    int i;
    // Data Memory SRAM wait states: Default Setting = 1; set it to 0
    BMXCONbits.BMXWSDRM = 0; // SRAMのウェイトステートを0にする

    // Set the interrupt controller for multi-vector mode
    INTCONSET = _INTCON_MVEC_MASK; //割り込みをマルチベクタモードに設定

    // Set the CP0 Status IE bit to turn on interrupts globally
    __builtin_enable_interrupts(); //割り込み有効化

    // ピンの入出力設定(全て出力)
    TRISA = 0;
    TRISB = 0;
    ANSELA = 0;
    ANSELB = 0;
    ODCA = 0;
    ODCB = 0;
    RPA0R = 5; //RPA0にOC1を割り当て

    for (i = 0; i < NUMofLED; i++) {
        setLED(i, 0, 0, 0);
    }
    for (i = NUMofLED * 24; i < NUMofLED * 24 + RESTIM; i++) {
        DMAbuffer[i] = 0; //最後に280マイクロ秒以上Low出力させる
    }

    DMACON = 0x8000; // DMA有効化

    //DMAチャネル1設定
    DCH1CON = 0x0010; //自動有効化モード
    DCH1ECON = (_TIMER_3_IRQ << 8) | 0x10; //Tiemr3割り込みでトリガ
    DCH1DSA = (unsigned int) &OC1RS & 0x1FFFFFFF; //転送先をOC1RS(Low byte)出力に設定
    DCH1SSA = (unsigned int) DMAbuffer & 0x1FFFFFFF; //転送元にLEDバッファを指定
    DCH1SSIZ = NUMofLED * 24 + RESTIM; //転送元サイズ
    DCH1DSIZ = 1; //転送先サイズ
    DCH1CSIZ = 1; //転送セルサイズ
    DCH1INT = 0x00000000; //DMA割り込み設定なし

    //OC1設定
    OC1CON = 0; //OC1無効化
    OC1R = 0;
    OC1RS = 0;
    OC1CON = 0x000e; //Timer3ベース、PWMモード

    //Timer3設定
    T3CON = 0x0000; //プレスケーラ1:1
    PR3 = PERIOD; //PWM周期
    TMR3 = 0; //タイマー初期化

    DCH1CONSET = 0x0080; // DMAチャネル1有効化
    T3CONSET = 0x8000; //Timer3開始
    OC1CONSET = 0x8000; //OC1有効化

//--------------------------------
//ここまででLEDへの自動出力開始

    //Timer2割り込み設定
    T2CON = 0x70; // プリスケーラ1:256, 16bit mode
    PR2 = 1875; // 10ms (48MHz/256*0.01)
    TMR2 = 0; // タイマー初期化

    IPC2bits.T2IP = 4; // T2割り込み優先度4に設定
    IFS0bits.T2IF = 0; // T2割り込みフラグをクリア
    IEC0bits.T2IE = 1; // T2割り込み有効化

    T2CONSET = 0x8000; // Timer2開始

    while (1) asm("wait"); // 割り込み待ち
}

// タイマー2割り込み処理ルーチン
void __ISR(_TIMER_2_VECTOR, IPL4AUTO) T2Interrupt(void) {
    unsigned int c,i;
    static int j=0, k=0;

    for (i = 0; i < NUMofLED / 2; i++) {
        c = LEDbuffer[i + 1];
        setLED(i, (c >> 8) & 0xff, c >> 16, c & 0xff);
    }
    for (i = NUMofLED - 1; i > NUMofLED / 2; i--) {
        c = LEDbuffer[i - 1];
        setLED(i, (c >> 8) & 0xff, c >> 16, c & 0xff);
    }
    c = (unsigned int) (sin((float) j * 6.28 / 64)*120 + 128);
    if (k < 100) {
        setLED(NUMofLED / 2, c * k / 100, c * (100 - k) / 100, 0);
    } else if (k < 200) {
        setLED(NUMofLED / 2, c * (200 - k) / 100, 0, c * (k - 100) / 100);
    } else {
        setLED(NUMofLED / 2, 0, c * (k - 200) / 200, c * (300 - k) / 200);
    }
    j = (j + 1) % 256;
    k = (k + 1) % 300;
    IFS0bits.T2IF = 0; // Reset interrupt flag
}