Arduino環境でRaspberry Pi Picoを使う

コンピュータ、組み込み

Raspberry Pi Pico とはRP2040マイコン搭載の評価基板の一つです。
Arduino IDE環境を使って IOの設定、通信ピン(UART, I2C, SPI)のピン設定の方法を記載します。

Raspberry Pi Pico

特徴

Raspberry Pi Picoは多くのGPIOピンと、通信機能を有した基板です。
ほかにもアナログ出力(PWM)、アナログ入力(ADC)を備えています。
また、HID機能(Human Interface Device)を備えているので、マウスやキーボードとして動作させることができます。

CPUARM Cortex M0+
接続Micro B Connector
USB1.1 ホスト/デバイス両対応
MemorySRAM 264KB
フラッシュメモリ 2MB
ペリフェラルビルトイン LED(緑) GP25
内部温度センサ
SPI x 2
I2C x 2
UART x 2
ADC x 3 [10bit (0~1023)]
PWM x 16 [8bit (0~255)]
ロジックレベル3.3V

当記事ではRaspberryPi Picoを使ったスケッチを中心に記載しています。
無線を使ったRaspberryPi PicoWでの記事はこちら

ピン配置

外観

400穴ブレッドボードの場合、左右2列ずつ使えます。

使ってみて

Arduino nanoやSAMD21では通信線が固定でしたが、RP2040系はUART, I2C, SPIについてはどのピンに割り当てるかを、選択範囲から決められるようになったので配線の取り回しがしやすくなりました。

CPUの処理速度もかなり早く、メモリも潤沢にあり、GPIO、通信線含めて多くのピンも出ているのでかなり大きなプロジェクトでも作成できます。
安価で入手性もいいですがArduino環境の入門では、I2CやSPIのピン設定で躓きやすいと感じます。

基板サイズが互換のジェネリック基板も多く出回っています。
特に電源回りが違うとソフトウエアの修正では吸収しきれないので、安価なジェネリック品は選定段階でピン配置をよく確認してから購入する必要があります。

当記事ではRaspberryPi Picoを使ったスケッチを中心に記載しています。
無線を使ったRaspberryPi PicoWでの記事はこちら

準備

基板の初期化

1.Raspberry Pi Pico のBOOTSELボタンを押しながら、USBケーブルをパソコンに接続します。

2.パソコンの画面では、Raspberry Pi Picoをストレージとして認識します。

3.UF2ファイルを以下のサイトからダウンロードします。

circuit python UF2 Download

4.ダウンロードしたファイル[adafruit-circuitpython-raspberry_pi_pico-ja-7.3.0.uf2]※をストレージ認識したRaspberry Pi Picoにドラッグ&ドロップする。

以上の作業でRaspberry Pi PicoはCOM認識されます。

今回ダウンロードしたuf2ファイルは、保存しておいてください。
Raspberry Pi Picoを初期化したいとき(今何のファームウエアが書かれているかわからなくなったとき)には、BOOTSELボタンを押しながら再起動することで、やり直しができます。

※2022/Jun 時点ではVersion 7.3.0

ライブラリのインストール

Arduino IDEのボードマネージャからRaspberry Pi Pico用のライブラリのインストールとボードの選択をします。

追加のボードマネージャのURLhttps://github.com/earlephilhower/arduino-pico/releases/download/global/package_rp2040_index.json
検索RP2040
ボードライブラリRaspberry Pi RP2040 Boards(x.x.x)※
x.x.x は最新の値を選択してください。

ボードライブラリ

ボードライブラリRaspberry Pi RP2040 Boards(x.x.x) > Raspberry Pi Pico
※動作確認は2.0.3

UF2ファイルの書き込み

「基板の初期化」ではCircuitPythonを書き込むことでパソコンにCOM認識させました。
Arduino環境からはCOMポートを選択することで、スケッチの書き込みボタンから書き込みができるようになります。
この方法は作成したスケッチを手軽に動作確認できるメリットがありますが、書き込みをするたびにコンパイルする時間が必要です。

Arduino環境でコンパイルしたuf2ファイルを、ストレージ認識されたRP2040に直接書き込むことができます。
スケッチが完成して修正が不要なら、uf2ファイルを生成しておいてドラッグ&ドロップするだけでスケッチの書き込みが終了します。

手順

1.スケッチを任意のフォルダに保存します。
2.メニュー>スケッチ>コンパイル済みバイナリをエクスポート を実行します。

3.1で保存したフォルダの下位フォルダにuf2ファイルが生成されます。
4.RP2040基板のBOOTボタンを押下しながらパソコンにUSB接続します。
5.uf2ファイルをストレージ認識されたRP2040基板にドラッグ&ドロップします。

基本スケッチ

Raspberry Pi Pico のペリフェラルの使い方についてサンプルを記載します。

ビルトインLED

サンプルスケッチ

void setup() {
  pinMode(25, OUTPUT);
}

void loop() {
  digitalWrite(25, HIGH);
  delay(100);
  digitalWrite(25, LOW);
  delay(100);
}

スケッチの説明

100ms点灯、100ms消灯を繰り返します。

結果

ビルトインLED(PWMホタル)

サンプルスケッチ

void setup()
{
  pinMode(25, OUTPUT);
}

void loop()
{
  int i;
  for (i = 0; i < 256; i ++)
  {
    analogWrite(25, i);
    delay(2);
  }

  for (i = 0; i < 256; i ++)
  {
    analogWrite(25, 255 - i);
    delay(2);
  }
}

スケッチの説明

アナログ出力を行います。
設定できる値は8bit(0~255)。
ゆっくり明るくして、ゆっくり暗くします。

結果

内部温度モニタ

サンプルスケッチ

void setup() {
  Serial.begin(115200);
}

void loop() {
  float Temp_degC = analogReadTemp();

  Serial.printf(“CPU Temperature = %lf\n”, Temp_degC);
  delay(1000);
}

スケッチの説明

1秒ごとに、内部温度をモニタして表示します。
RP2040 では analogReadTemp()関数を使うことにより、温度を℃単位で読みだせます。
またRP2040では、printf()関数が使えるので、書式付き出力が簡単にできるようになりました。

結果

結果は、Arduinoのシリアルモニタで確認することができます。

内部温度モニタの実行結果

HID(マウス)

RP2040はキーボードやマウスとして動作することができます。

準備

使うもの

名称(型名)用途
Raspberry Pi Picoマウスになってもらいます
ブレッドボードマイコンや部品の配置
タクトスイッチマウスカーソルの移動ボタンに
なってもらいます
ジャンパワイヤ 数本

配線

Raspberry
Pi Pico
配線色ボタン青配線色ボタン黒
3.3V端子
GPIO 0端子
3.3V端子
GPIO 1端子

サンプルスケッチ

#include <Mouse.h>

#define BLUEBUTTON 0                  //青いボタンはGPIO 0を使う
#define BLACKBUTTON 1                 //黒いボタンはGPIO 1を使う

void setup() {

  //ボタンの情報を読み取るためのピン読み取り設定
  pinMode(BLUEBUTTON, INPUT_PULLDOWN);
  pinMode(BLACKBUTTON, INPUT_PULLDOWN);

//  Serial.begin(115200);
  Mouse.begin();                        //これを唱えるとマウス制御が開始される。
}

void loop() {

  //青いボタンが押されていればマウスカーソルを左へ移動
  int blue = digitalRead(BLUEBUTTON);
  if (blue == HIGH)
  {
    Mouse.move(-1, 0, 0);
  }

  //黒いボタンが押されていればマウスカーソルを右へ移動
  int black = digitalRead(BLACKBUTTON);
  if (black == HIGH)
  {
    Mouse.move(1, 0, 0);
  }
}

スケッチの説明

GPIO0(青ボタン)とGPIO1(黒ボタン)は、PullDown入力です。
ボタンが押されている間はHIGHになります。

青いボタンを押している間マウスカーソルが左へ移動します。
黒いボタンを押している間マウスカーソルが右へ移動します。

結果

HID(キーボード)

RP2040はキーボードやマウスとして動作することができます。

準備

使うもの

使うものと配線は「HID(マウス)」と同じです。

名称(型名)用途
Raspberry Pi Picoキーボードになってもらいます
ブレッドボードマイコンや部品の配置
タクトスイッチキーボードのボタンに
なってもらいます
ジャンパワイヤ 数本

配線

Raspberry
Pi Pico
配線色ボタン青配線色ボタン黒
3.3V端子
GPIO 0端子
3.3V端子
GPIO 1端子

サンプルスケッチ

#include <Keyboard.h>

#define BLUEBUTTON 0                  //青いボタンはGPIO 0を使う
#define BLACKBUTTON 1                 //黒いボタンはGPIO 1を使う

const char strKey[] = "tamanegi";     //1文字ずつ出力するための文字列
uint iIndexKey = 0;                   //配列の出力インデックス
uint iSizeKey = 0;                    //文字列サイズの保存

bool bButtonBlueOn = false;           //ボタン押下状態の保存
bool bButtonBlackOn = false;

void setup() {

  iSizeKey = sizeof(strKey);          //配列サイズの読み取り
  
  //ボタンの情報を読み取るためのピン読み取り設定
  pinMode(BLUEBUTTON, INPUT_PULLDOWN);
  pinMode(BLACKBUTTON, INPUT_PULLDOWN);

  Serial.begin(115200);
Serial.printf("size = %d\n", iSizeKey);
  Keyboard.begin();                        //これを唱えるとキーボード制御が開始される。
}

void loop() {

  delay(10);

  //青いボタンが押されたら 押されるたびに"tamanegi"を一文字ずつキーボード出力
  if(digitalRead(BLUEBUTTON) == HIGH)
  {
    if(bButtonBlueOn == false)              //ボタンが押されていない状態から押された状態への変化を確認。キーを連発させないための処理
    {
      bButtonBlueOn = true;                 //ボタン押下フラグOn

      Keyboard.write(strKey[iIndexKey]);    //一文字ずつキーボード出力
      iIndexKey ++;
      if(strKey[iIndexKey] == 0x00)iIndexKey = 0;       //NULL文字を検出したら最初から
    }
  }
  else 
  {
    bButtonBlueOn = false;                //ボタン押下フラグをOff
  }

  if(digitalRead(BLACKBUTTON) == HIGH)
  {
    if(bButtonBlackOn == false)              //ボタンが押されていない状態から押された状態への変化を確認。キーを連発させないための処理
    {
      bButtonBlackOn = true;                 //ボタン押下フラグOn

      Keyboard.printf("TAMANEGI");          //まとめてキー出力
    }
  }
  else 
  {
    bButtonBlackOn = false;                //ボタン押下フラグをOff
  }
}

スケッチの説明

GPIO0(青ボタン)とGPIO1(黒ボタン)は、PullDown入力です。
ボタンが押されている間はHIGHになります。

青いボタンを押すたびに”t” -> “a” -> “m” -> “a” -> “n” -> “e” -> “g” -> “i” の文字が順番にキーボード入力されます。
黒いボタンを押すと、一度に”TAMANEGI”の文字列がキーボード入力されます。

結果

UARTの設定

UARTのピン表

RP2040でUARTを使用する場合2系統から任意のピンを選択できます。
UARTを使った実際の通信は「マイコン同士のUART通信」で紹介します。

オブジェクト名表上の表記使用できるピン
Serial1UART0(赤破線枠)TX : GP0, GP12, GP16
RX : GP1, GP13, GP17
Serial2UART1(青破線枠)TX : GP4, GP8
RX : GP5, GP9
オブジェクトSerial はUSB接続したパソコンとの通信に使います。スケッチの書き込みやシリアル出力など。

使用するピンの組み合わせは同系統から選択します。
Serial1(UART0)を使う場合、Serial2(UART1)のピンを指定することはできません。

OK例:UART0のTXにGP0 と RXにGP13を設定する。
NG例:UART0のTXにGP0 と RXにGP5を設定する(GP5は UART1のため)

スケッチ
//UART0に GP0(TX)とGP1(RX)を設定します。

   Serial1.setTX(0);            //UART0はSerial1オブジェクトを使用します。
   Serial1.setRX(1);
   
//UART1に GP4(TX)とGP5(RX)を設定します。

   Serial2.setTX(4);            //UART1はSerial2オブジェクトを使用します。
   Serial2.setRX(5);

I2Cの設定

RP2040でI2Cを使用する場合2系統から選択できます。
I2Cを使った実際の通信は「マイコン同士のI2C通信」で紹介します。

I2C用のピン位置

オブジェクト名表上の表記使用できるピン
WireI2C0(赤破線枠)TX : GP0, GP4, GP8, GP12, GP16, GP20
RX : GP1, GP5, GP9, GP13, GP17, GP21
Wire1I2C1(青破線枠)TX : GP2, GP6, GP10, GP14, GP18, GP26
RX : GP3, GP7, GP11, GP15, GP19, GP27
オブジェクトSerial はUSB接続したパソコンとの通信に使います。スケッチの書き込みやシリアル出力など。

使用するピンの組み合わせは同系統から選択します。
Wire(I2C0)を使う場合、Wire1(I2C1)のピンを指定することはできません。

OK例:I2C0のSDAにGP0 と SCLにGP5を設定する。
NG例:I2C0のSDAにGP0 と SCLにGP3を設定する(GP3は I2C1のため)

スケッチ

//I2C0に GP0(SDA)とGP1(SCL)を設定します。

   Wire.setSDA(0);            //I2C0はWireオブジェクトを使用します。
   Wire.setSCL(1);

//I2C1に GP6(SDA)とGP7(SCL)を設定します。

   Wire1.setSDA(6);           //I2C1はWire1オブジェクトを使用します。
   Wire1.setSCL(7);

タイマー割り込み

スケッチの説明

タイマー割り込みは、指定した時間が経過すると決められた命令を実行します。
このサンプルでは、500msごとにGPIO(実装LED)の出力のLOWとHIGHを反転させる処理を実行します。
5回LEDを点灯させると割り込みの発生を終了させます。

サンプルスケッチ

#include "pico/stdlib.h"

#define PIN_LED (25)
#define INTERVAL (500)
bool timer_Test( repeating_timer_t *rt);

bool Signal = false;
long Counter = 0;

void setup()
{
  pinMode(PIN_LED, OUTPUT);

  //500ms 毎に計測を行うための割り込みを発生させる。
  static repeating_timer_t timer;
  add_repeating_timer_ms(INTERVAL, &timer_Test, NULL, &timer);
}

void loop()
{
}

bool timer_Test(repeating_timer_t *rt)
{
  //Signal : false = LEDを消灯させる, true = LEDを点灯させる
  if(Signal == true)
  {
    digitalWrite(PIN_LED, HIGH);
    Signal = false;
    Counter ++;
  }
  else
  {
    digitalWrite(PIN_LED, LOW);
    Signal = true;
  }

  //LEDが5回点灯したら割り込みを終了する
  if(Counter >= 5)
  {
    return (false);

  }
  return (true);
}

ハードウエア API

RP2040 には多くのハードウエアAPIが用意されています。
ここでは当方で使用したことのあるAPIについて掲載します。

そのほかのAPIについては下記のリンクをクリックしてください。

Raspberry Pi Pico SDK: API Documentation

GPIO信号のLow/High

サンプルスケッチ

void setup() 
{
  gpio_init(0);
  gpio_set_dir(0, GPIO_OUT);
}

void loop()
{
  while(1)
  {
   gpio_put_masked(0x0001,0x0001);
   gpio_put_masked(0x0001,0x0000);
  }
}

スケッチの説明

setup()ではハードウエアAPIを使用するためのピン指定と出力方向の設定を行います。
loop()ではloop()関数呼び出しのオーバーヘッドをなくすために関数内でループします。

ハードウエアAPIでは、GPIOのピン番号をビット番号で操作します。

結果

Arduino標準のdigitalWrite()を使用するより実行速度が20倍弱速い。

digitalWrite()で同等の処理を実行した時の信号を比較します。

複数ch設定時の遅延確認

digitalWrite()では、1chずつ設定するため関数呼び出しのオーバーヘッド分の遅延が発生する。
HW APIでは、同時に設定ができるため遅延は見られない(2chで確認した際の推定)

サンプルスケッチ(HW API)

void setup()
{
  gpio_init(0);
  gpio_set_dir(0, GPIO_OUT);
  gpio_init(1);
  gpio_set_dir(1, GPIO_OUT);
}

void loop()
{
  while(1)
  {
    gpio_put_masked(0x0003,0x0003);
    delay(100);

    gpio_put_masked(0x0003,0x0000);
    delay(100);
  }
}

サンプルスケッチ(digitawWrite)

#define LED_YELLOW 0
#define LED_BLUE   1


void setup()
{
  pinMode(LED_YELLOW, OUTPUT);
  pinMode(LED_BLUE, OUTPUT);
}

void loop()
{
  while(1)
  {
    digitalWrite(LED_YELLOW, HIGH);
    digitalWrite(LED_BLUE, HIGH);
    delay(100);

    digitalWrite(LED_YELLOW, LOW);
    digitalWrite(LED_BLUE, LOW);
    delay(100);
  }
}

スケッチの説明

0chと1chの信号を連続で変化させます。
100ms周期で行います。

結果

LEDの点滅は目視で確認できますが、遅延は認識できません。
オシロスコープを使用し信号を確認します。

DSC_0237

結果の画像は信号がHighからLowになったところです。
digitalWrite()では0ch(黄)がLowになってから、1ch(青)がLowになるまでに0.65usの遅延が確認できましたが、HW APIでは計測ができませんでした。

IRQ割り込み

スケッチの説明

GPIOの信号を検知することで割り込み処理を実行します。

割り込みを使用しない場合、定期的にGPIOの状態をモニタして処理を実行します。
IRQを使用することでCPUがGPIOの状態を常に監視し、信号を検知したタイミングで任意の処理を実行することができます。

サンプル1 :
割り込み用GPIO0(タクトボタン)が押下されると割り込み回数のカウンタ値がUARTに出力されます。

サンプルスケッチ


#define PIN_IRQ (0)       //IRQ GPIOピン

int Counter = 0;

void IRQ_callback(uint gpio, uint32_t events)
{
  //割り込み発生回数のカウントと表示
  Counter ++;
  Serial.printf("Counter = %d\n", Counter);
}

void setup()
{
  pinMode(PIN_IRQ, INPUT_PULLDOWN);
  Serial.begin(115200);

  gpio_set_irq_enabled_with_callback(PIN_IRQ, 0x4u, false, IRQ_callback);   //IRQコールバック関数の登録
  gpio_set_irq_enabled(PIN_IRQ, 0x4u, true);                                //監視開始
}

void loop()
{
}

結果

結果はシリアルモニタで確認します。
ボタンを押すごとにUARTに割り込み回数が表示されました。

何回かボタンを押していると、1回のボタン押下に対し複数回の割り込みが発生することがありました。
ボタンの出力をオシロスコープで確認するとチャタリングが確認できます。
この時は3回の割り込みに対し、2回分の信号しか見つかりませんでした。

コメント

タイトルとURLをコピーしました