Raspberry Pi Pico バーサライタ解像度向上 (C/マルチコア) ーRaspberry Pi Picoへの道7ー
前回はRaspberry Pi Pico を用いてバーサライタを製作しました。
ここでは、表示映像の解像度向上のためにSPI複数出力とデュアルコア動作を検証しましたので報告します。
目次
SPI複数出力
Raspberry Pi Pico にはハードウェアSPI端子が2個あります (SPI0, SPI1)。
この2つのSPI端子でそれぞれLEDテープを制御してみました。
Raspberry Pi Pico
ハードウェアSPI0とSPI1の2つで
LED制御ステートマシンってのを分けると
独立で制御できるらしい#RaspberryPiPico pic.twitter.com/obmS05NFdM— HomeMadeGarbage (@H0meMadeGarbage) February 14, 2021
Cコード
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
#include "pico/stdlib.h" #include "hardware/pio.h" #include "apa102.pio.h" #define PIN_CLK0 2 #define PIN_DIN0 3 #define PIN_CLK1 14 #define PIN_DIN1 15 #define N_LEDS 11 #define SERIAL_FREQ (30 * 1000 * 1000) // Global brightness value 0->31 #define BRIGHTNESS 3 uint32_t color[] ={ 0xFF0000, 0x00FF00, 0x0000FF, 0x00FFFF }; void put_start_frame(PIO pio, uint sm) { pio_sm_put_blocking(pio, sm, 0u); } void put_end_frame(PIO pio, uint sm) { pio_sm_put_blocking(pio, sm, ~0u); } void put_bgr888(PIO pio, uint sm, uint32_t c) { pio_sm_put_blocking(pio, sm, 0x7 << 29 | // magic (BRIGHTNESS & 0x1f) << 24 | // global brightness parameter c ); } int main() { stdio_init_all(); PIO pio = pio0; uint sm0 = 0; uint sm1 = 1; uint offset = pio_add_program(pio, &apa102_mini_program); apa102_mini_program_init(pio, sm0, offset, SERIAL_FREQ, PIN_CLK0, PIN_DIN0); apa102_mini_program_init(pio, sm1, offset, SERIAL_FREQ, PIN_CLK1, PIN_DIN1); while (true) { for (int j = 0; j < 4; ++j) { put_start_frame(pio, sm0); put_start_frame(pio, sm1); for (int i = 0; i < N_LEDS; ++i) { put_bgr888(pio, sm0, color[j]); put_bgr888(pio, sm1, color[3-j]); } put_end_frame(pio, sm0); put_end_frame(pio, sm1); sleep_ms(500); } } } |
PIO
SPI入力LEDをPIOなる機能で点灯制御しているのですが、2本制御の際はステートマシンなるモノを分けて使用するとよいようです。
PIOブロックは2個内蔵 (pio0, pio1)され、それぞれにステートマシン4個搭載されています (sm0~3)。
狭ピッチバーサライタ
以前製作した狭ピッチのLEDバーサライタをRaspberry Pi Picoで制御してみます。
このLEDはリフレッシュレートが低いためRGBそれぞれをフル輝度で使用しなくてはならず8色しか出力できませんが
2mm角のLEDが狭ピッチで配置されていますので解像度向上実験にはもってこいです。
構成
2つのSPI出力でLEDを64セル, 63セルずつ制御します。
動作
先ほどと同様に2個のSPI出力で制御し、回転速度470rpmで回してみたところ、計127個のLEDを1周 240分割で表示できました。
Raspberry Pi Pico
SPI0:LED 64セル
SPI1:LED 63セル回転速度 470rpmで1周 240分割達成
このLEDバーで240分割初めて見たデュアルコアで高みを目指す#RaspberryPiPico pic.twitter.com/gljJGGmxjb
— HomeMadeGarbage (@H0meMadeGarbage) February 14, 2021
ESP32のデュアルコア制御でも200分割が限界でしたので、やはりRaspberry Pi Pico の高速IOは素晴らしいです。
デュアルコア制御
Raspberry Pi PicoはCortex-M0+を2個搭載しています。
更なる高解像表示を目指してデュアルコア制御を試します。
Cコード
core0で回転速度計測とSPI0のLED制御し、
core1でSPI1のLED制御しています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 |
#include <stdio.h> #include "pico/stdlib.h" #include "hardware/pio.h" #include "apa102.pio.h" #include "pico/multicore.h" #define PIN_CLK0 2 #define PIN_DIN0 3 #define PIN_CLK1 14 #define PIN_DIN1 15 #define N_LEDS 64 #define Div 240 #define SERIAL_FREQ (30 * 1000 * 1000) // Global brightness value 0->31 #define BRIGHTNESS 1 uint32_t color[] ={ 0xFF0000, 0x00FF00, 0x0000FF, 0x00FFFF }; unsigned long rotTime, timeOld, timeNow; int numRot = 0; int stateDiv = 0; int stateDiv2 = 0; int numDiv = 0; int num = 0; void gpio_callback() { timeNow = time_us_64(); rotTime = timeNow - timeOld; timeOld = timeNow; numRot++; if(numRot >= Div ) numRot = 0; } void put_start_frame(PIO pio, uint sm) { pio_sm_put_blocking(pio, sm, 0u); } void put_end_frame(PIO pio, uint sm) { pio_sm_put_blocking(pio, sm, ~0u); } void put_bgr888(PIO pio, uint sm, uint32_t c) { pio_sm_put_blocking(pio, sm, 0x7 << 29 | // magic (BRIGHTNESS & 0x1f) << 24 | // global brightness parameter c ); } void core1_entry() { PIO pio = pio0; uint sm = 1; uint offset = pio_add_program(pio, &apa102_mini_program); apa102_mini_program_init(pio, sm, offset, SERIAL_FREQ, PIN_CLK1, PIN_DIN1); while (true) { if(stateDiv2 == 1){ put_start_frame(pio, sm); for (int i = 0; i < N_LEDS; ++i) { put_bgr888(pio, sm, color[num]); } put_end_frame(pio, sm); stateDiv2 = 0; } } } int main() { stdio_init_all(); gpio_set_irq_enabled_with_callback(22, GPIO_IRQ_EDGE_RISE, true, &gpio_callback); multicore_launch_core1(core1_entry); PIO pio = pio0; uint sm = 0; uint offset = pio_add_program(pio, &apa102_mini_program); apa102_mini_program_init(pio, sm, offset, SERIAL_FREQ, PIN_CLK0, PIN_DIN0); while (true) { if(stateDiv == 1 && time_us_64() - timeOld > rotTime / Div * (numDiv)){ stateDiv = 0; } if(stateDiv == 0 && time_us_64() - timeOld < rotTime / Div * (numDiv + 1)){ stateDiv = 1; stateDiv2 = 1; put_start_frame(pio, sm); for (int i = 0; i < N_LEDS; ++i) { put_bgr888(pio, sm, color[num]); } put_end_frame(pio, sm); numDiv++; if(numDiv >= Div ) numDiv = 0; num++; if(num > 1 ) num = 0; } } } |
しかし、
デュアルコアでの1周の分割数の向上は確認できませんでした。。。
core1でのLED点灯制御は確実にできていると思うのですが。
core1の動かし方に何か問題があるのでしょうか?
参考
- Raspberry Pi Pico – Multicore Adventures
- Getting Started with Multicore programming on the Raspberry Pi Pico
画像表示
浮世絵を表示してみました。
シングルでもデュアルでも1周240分割以上にすると表示がおかしくなります。
シングルコア
この投稿をInstagramで見る
デュアルコア
この投稿をInstagramで見る
おわりに
ここではRaspberry Pi Picoを用いて狭ピッチLEDバーサライタを製作しました。
高解像表示を目指しましたがデュアルコアによる向上は達成されませんでした。
しかしLED 127個で1周 240分割は過去最大の分解能ですので、ひとまず嬉しいです。
デュアルコアはについては引き続き勉強します。なにか間違ってるかもしれません。
それでは次の道でお会いしましょう!
追記
回転検出部を別コア化(2021/2/17)
以下のブログでPIOで別ステートマシンでのLED制御がすでに並列処理されているために、
デュアルコアによる表示解像度の向上がなかったことがわかりました。
そこでステートマシンでの並列処理については理解できたので、LED制御と回転計測をデュアルコアで試してみました。
以下のようにcore0で回転計測、core1でPIOのステートマシン2個でLED(64セル、63セル)を制御してみます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 |
#include <stdio.h> #include "pico/stdlib.h" #include "hardware/pio.h" #include "apa102.pio.h" #include "pico/multicore.h" #define PIN_CLK0 2 #define PIN_DIN0 3 #define PIN_CLK1 14 #define PIN_DIN1 15 #define N_LEDS 64 #define Div 240 #define SERIAL_FREQ (30 * 1000 * 1000 ) // Global brightness value 0->31 #define BRIGHTNESS 1 uint32_t color[] ={ 0xFF0000, 0x00FF00, 0x0000FF, 0x00FFFF }; unsigned long rotTime, timeOld, timeNow; int numRot = 0; int stateDiv = 0; int numDiv = 0; int num = 0; void gpio_callback() { timeNow = time_us_64(); rotTime = timeNow - timeOld; timeOld = timeNow; numRot++; if(numRot >= Div ) numRot = 0; } void put_start_frame(PIO pio, uint sm) { pio_sm_put_blocking(pio, sm, 0u); } void put_end_frame(PIO pio, uint sm) { pio_sm_put_blocking(pio, sm, ~0u); } void put_bgr888(PIO pio, uint sm, uint32_t c) { pio_sm_put_blocking(pio, sm, 0x7 << 29 | // magic (BRIGHTNESS & 0x1f) << 24 | // global brightness parameter c ); } void core1_entry() { PIO pio = pio0; uint sm0 = 0; uint sm1 = 1; uint offset = pio_add_program(pio, &apa102_mini_program); apa102_mini_program_init(pio, sm0, offset, SERIAL_FREQ, PIN_CLK0, PIN_DIN0); apa102_mini_program_init(pio, sm1, offset, SERIAL_FREQ, PIN_CLK1, PIN_DIN1); while (true) { if(multicore_fifo_pop_blocking() == 1){ put_start_frame(pio, sm0); put_start_frame(pio, sm1); for (int i = 0; i < N_LEDS; ++i) { put_bgr888(pio, sm0, color[num]); put_bgr888(pio, sm1, color[num]); } put_end_frame(pio, sm0); put_end_frame(pio, sm1); } } } int main() { stdio_init_all(); gpio_set_irq_enabled_with_callback(22, GPIO_IRQ_EDGE_RISE, true, &gpio_callback); multicore_launch_core1(core1_entry); while (true) { if(stateDiv == 1 && time_us_64() - timeOld > rotTime / Div * (numDiv)){ stateDiv = 0; } if(stateDiv == 0 && time_us_64() - timeOld < rotTime / Div * (numDiv + 1)){ stateDiv = 1; multicore_fifo_push_blocking(stateDiv); numDiv++; if(numDiv >= Div ) numDiv = 0; num++; if(num > 3 ) num = 0; } } } |
結果としましては大きな分解能向上はありませんでした。
1周 240分解能がギリギリ260分割できそうになるくらいの変化でした。
ステートマシンが完全に並列処理できていて、回転が470rpm、SPI信号が30MHzの場合、理論分解能値はだいたい
30MHz ÷ (32bit × 64セル + 64bit) × 60sec ÷ 470rpm = 1813分割
ですので、SPIクロックが遅すぎることはないようです。
まだまだデータ転送方法など工夫次第で高解像度化できそうですが、とにかくPIOは素晴らしいということがわかりました。