Arduino環境でRaspberry Pi Picoを使う

it

RaspberryPi PicoをArduino環境で使うための準備や基本的なスケッチをまとめました。
IOの設定、通信(UART, I2C, SPI)、マウスやキーボードにするHID、割り込み等のサンプルを記載します。

記事の内容と目的

この記事ではRaspberryPi PicoをArduino環境で使用するための情報やサンプルスケッチを記載します。
記事の内容は
・主なスペックと基板の機能
・Arduino環境での準備やトラブル
・I/Oと通信を使用するための注意点と簡単なサンプルスケッチ

Arduino 環境の作成方法はこちら

Raspberry Pi Pico

主なスペック

Arduino環境ではUSBケーブルを使ってスケッチを書き込むことができます。
RP2040デバッガを使ってステップ実行をしたり、動作中の変数をモニタすることができます。
UART、I2C、SPIのシリアル通信では信号線を決められた範囲から任意ピンに設定することができます。

接続Micro B
(USB1.1 ホスト/デバイス)
CPURP2040
(50 – 300MHz)
メモリRAM 264KByte
フラッシュ 2MByte
ロジックレベル3.3V
GPIOI/O 26
ADC 3ch(10bit 0 ~ 1023)
PWM 26ch(8bit 0 ~ 255)
通信UART 2ch
I2C 2ch
SPI 2ch
そのほかLED 緑(GPIO25)
入手国内ショッピングサイト
ネットストア
実店舗
参考価格国内 \1,100 ~ \1,300
Ali Express \600 ~ \800
入手:主観です。
参考価格:変動することがあります。保証価格ではありません。単品、送料税込み価格です。

ピン配置

外観

400穴ブレッドボードでは左右2列使用することができます。
基板の表面にピン番号のシルクがないので挿し間違いに注意が必要です。

使ってみて

Good
入手性、コスパ、性能面で高いパフォーマンスを発揮します。
国内のショッピングサイトでも多く扱っているので、必要になってから注文してもすぐ配送されるので安心して使っていけます。

多くのGPIOと通信線があるので、たくさんのモジュールをつなげることができます。
特にRP2040に対応した各種モジュールライブラリが多く、開発や評価も円滑に進みます。

完成したスケッチをuf2ファイルにしておくことで、簡単に書き込みをすることができる。

Bad
BOOTボタンの使い方や通信線の設定での躓きがあります。
慣れてしまえば問題になることはないと思います。

リセットボタンがないので再起動するときにわずらわしさを感じます。
基板の表面にピン番号の表示がないため挿し間違えに注意が必要です。

USBコネクタがMicroBのため、挿抜時にもげそうな不安があります。

そのほか
RaspberryPi Picoと同サイズのジェネリック基板が多く発売されています。
上位互換のものが多く、この基板よりも安価なものも出ています。

多くの互換基板を使ったなかでそれぞれの良さや特徴もありますが、それでもこの本家の基板は常にレギュラーで実験や評価に使っています。
シンプルな安心感があります。(主観)

準備

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

ArduinoIDEでRaspberryPi Picoを開発するために、RP2040ボードライブラリをインストールします。
この作業を実行するために、あらかじめArduinoIDEがインストールされている必要があります。

ArduinoIDEのインストールとライブラリのインストール方法はこちら。

使用するボードライブラリの情報は以下の通りです。

追加のボードマネージャのURLhttps://github.com/earlephilhower/arduino-pico/releases/download/global/package_rp2040_index.json
検索RP2040
ボードライブラリRaspberry Pi RP2040 Boards(x.x.x)※
選択するボードRaspberry Pi RP2040 Boards(x.x.x) > Raspberry Pi Pico
※動作確認はバージョン 3.7.0

基板の初期化とシリアル認識

Arduino環境で手軽に開発するために、USBケーブルを使用したCOM通信ができる準備をします。
COM通信を使用するために次の2ステップを実行します。
1.UF2ファイルを作成する
2.基板に書き込む

この手順を実行するために、あらかじめArduinoIDEとRP2040ボードライブラリが必要です。

1.UF2ファイルを作成する

ArduinoIDEを起動します。
メニュー Tools > Board > Raspberry Pi Pico/RP2040 > Raspberry Pi Pico をクリックします。


新しいプロジェクトを作成し、名前を付けて保存します。
ここでは「RPI_blank」としました。


Binary(uf2)を出力するコンパイルをします。
Sketch > Export Compiled Binary (日本語版 : コンパイル済みバイナリをエクスポート)

コンパイルが終わると保存したフォルダの下位改装にuf2ファイルのほかに全部で4つのファイルが作成されます。

以上がuf2ファイルの作成です。
この作業は完成したスケッチでも同じです。


2.基板に書き込む

RaspberryPi PicoのBOOTボタンを押しながらパソコンとUSBケーブルで接続します。

RaspberryPi Picoはストレージ認識されます。

uf2ファイルをRaspberryPi Picoにドラッグ&ドロップします。

ArduinoIDEのPortでは「COM5」でシリアル認識されました。
認識されたCOMを選択することで、ArduinoIDEからの書き込みができます。

COM番号は新しいArduino環境の基板を接続したり、書き込みをすることで変わることがあります。
書き込めないときはPort番号を確認します。

以上で基板に書き込む作業は完了です。
今回使用したRPI_blank.uf2ファイルの書き込み作業は、基板に書かれたスケッチを消去したいとき、
RaspberryPi Pico(他RP2040搭載基板)がシリアル認識されないときに同じ作業をすることで普及できます。

完成したスケッチをバイナリ出力(uf2ファイル)した時も同じ作業で書き込むことができます。

スケッチの書き込み

ArduinoIDEでスケッチが出来上がったら書き込みを行います。
RaspberryPi Picoが接続されているPort番号を選択している状態で、書き込みボタンを押します。

COM認識されていなければ、「基板の初期化とシリアル認識」の手順を実施します。

トラブル

RaspberryPi Picoを使っていてよくあるトラブルを記載します。

■書き込みができない
Outputメッセージに “Failed uploading: uploading error: exit status 1″と表示される。

・RaspberryPi PicoがUSB接続されていない。
ケーブルの接続を確認します。
スイッチ付きUSBハブではスイッチのOFF/ON状態を確認します。

・スケッチ書き込み後にCOMポート番号が変わった。
スケッチ書き込み後や、USBの挿し口を変更することで時々COMポート番号が変わることがあります。

・他のアプリケーションがCOMポートを占有している。
アプリケーションを閉じるか、COMポートの開放をしてからもう一度書き込みボタンを押す。

■シリアル認識しなくなった
スケッチ書き込み後に動作しない。
シリアル認識しなくなった。

考えられる原因にUART、I2C、SPIのピン設定を指定できる場所以外のピンに設定すると現象が発生します。

本記事で紹介した「基板の初期化とシリアル認識」を実施することで復旧できます。

基本スケッチ

ここからはRaspberryPi Picoの簡単な使い方を掲載します。
他のRP2040搭載基板でもピン番号を変更することで使用できます。

GPIO(デジタル出力)

説明

GPIO0を使ってLEDを点滅(Lチカ)させます。

#define PIN (0) の0を変更することで使用するピンを変更できます。
#define INTERVAL (100) の100を変更することで点滅間隔を変更できます。

配線

LED(色は好み)と保護抵抗に200Ωを使います。
LEDのピンの長いほう(Anode)にGPIO0、短いほう(Cathode)がGND側になるように接続します。
抵抗に方向はありません。

サンプルスケッチ

#define PIN (0)
#define INTERVAL (100)

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

void loop() {
  digitalWrite(PIN, HIGH);
  delay(INTERVAL);
  digitalWrite(PIN, LOW);
  delay(INTERVAL);
}

結果

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


GPIO(デジタル入力)

説明

GPIO1を使ってボタンの状態を読み取ります。
基板上の緑LED(GPIO25)はボタンが押されていれば点灯、放されていれば消灯します。
スケッチでは、ボタンの信号をプルダウンして使用します。
ボタンが押されている状態がHigh、放されている状態はLowです。

#define BTN_PIN (0) はボタンの入力ピン
#define LED_PIN (25) はLEDのピン(基板上のLED)

配線

LEDは基板実装のLEDを使用するため配線不要(赤〇)
タクトボタンは3V3電源を通しGPIO1に配線します。

サンプルスケッチ

#define BTN_PIN (1)
#define LED_PIN (25)

void setup() {
  pinMode(BTN_PIN, INPUT_PULLDOWN);
  pinMode(LED_PIN, OUTPUT);  
}

void loop() {

  uint8_t Status = digitalRead(BTN_PIN);

  if(Status == LOW){
    digitalWrite(LED_PIN, LOW);
  }
  else if(Status == HIGH){
    digitalWrite(LED_PIN, HIGH);
  }
}

結果

画像左はボタンを押していません。
画像右はボタンを押している間LEDが発光しました。


GPIO(PWM出力)

説明

基板上の緑LED(GPIO25)のゆっくり点灯、ゆっくり消灯をします。

setup()関数では使用するピンの指定と設定をします。
loop()関数ではLEDの輝度を0~255の256段階で変化させます。

#define LED_PIN (25) はLEDのピン(基板上のLED)
#define INTERVAL (10) LEDの輝度変化の速度です。小さくすると早く、大きくするとゆっくり変化します。
#define BRIGHTNESS (255) は最大輝度です。小さくすると暗くなります。

配線

配線不要。
基板上のLEDを使用します。

サンプルスケッチ

#define LED_PIN (25)
#define INTERVAL (10)
#define BRIGHTNESS (255)

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

void loop() {

  //ゆっくり明るくする
  for(uint32_t i = 0; i <= BRIGHTNESS; i ++){
    analogWrite(LED_PIN, i);
    delay(INTERVAL);
  }

  //ゆっくり暗くする
  for(uint32_t i = 0; i <= BRIGHTNESS; i ++){
    analogWrite(LED_PIN, BRIGHTNESS - i);
    delay(INTERVAL);
  }

}

結果

GPIO25(LED緑)からGPIO0に同じ信号を出力し、結果をオシロスコープ(OWON HDS272)で読み取りました。
PWMの周期1KHz(1ms間隔)で、HIGHとLOWの幅が連続的に変化している様子がわかります。
※分解能 : 200us/div, 1V/div


GPIO(アナログ入力)

説明

RaspberryPi PicoのGPIO26, 27, 28はアナログ入力の値を読み取ることができます。
GPIO26に三角波を入力し、読み取った値をCOMに出力します。
出力した値をExcelに貼り付けてグラフにします。

#define ADC_PIN (26) はアナログ入力に使用するピン

配線

アナログ入力にはESP32-WROOM(NODE-MCU)を使用します。
0Vから3.3Vまで2msごとに12.9mV上昇させ、3.3Vから0Vまで12.9mVずつ降下します。
(0Vから3.3Vを256階調で変化させています)

入力したアナログ信号をオシロスコープで観測しました。

サンプルスケッチ

#define ADC_PIN (26)

void setup()
{
  Serial.begin(115200);
  pinMode(ADC_PIN, INPUT);
}

void loop()
{
  int iADC = 0;

  //入力された電圧を読み取り、結果をCOMに出力します。
  iADC = analogRead(ADC_PIN);
  Serial.printf("(ADC) = %d\r\n", iADC);
  delay(2);
}

結果

読み取り結果をExcelのグラフにしました。
入力電圧に対して概ねリニアな直線が得られていると思います。
3.3Vに対する分解能10bit(0~1023)に対して若干不足が見られますが、入力電圧も完全に3.3Vに満たしていない影響も考えられます。
精度よく測定するには校正データが必要です。


UART通信

UARTで使えるピン

RP2040でのUARTは2系統使用することができます。(USBCOMとは別)
UARTに使用するピンは決められた範囲の中で任意のピンを指定できます。
UART0を使用する場合は、下図左側のマスクされていないピンをSerial1オブジェクトで指定します。
UART1を使用する場合は、下図右側のマスクされていないピンをSerial2オブジェクトで使用します。

UART0を使用する場合              UART1を使用する場合

Arduino上でUARTを使用する場合、ボードライブラリに用意されている以下のオブジェクトを使用します。
UAR0表記のピンを使用する場合はSerial1オブジェクトを、UART1表記のピンを使用する場合はSerial2オブジェクトを使用します。

表記オブジェクト名使用できるピン
COM(USB)Srial
UART0Seria1TX : 0, 12, 16, 28
RX : 1, 13, 17
UART1Serial2TX : 4, 8
RX : 5, 9
オブジェクトSerial はUSB接続したパソコンとの通信に使います。スケッチの書き込みやシリアル出力など。

OK例:UART0のTXにGPIO0 と RXにGPIO13を設定する。
NG例:UART0のTXにGPIO0 と RXにGPIO5を設定する(GPIO5は UART1のため)

使用するUARTとオブジェクトの関係が違う、またはTXとRXに非対応のピンを設定して書き込みを行うと以下のメッセージが表示されRaspberryPiはCOM認識されなくなります。

「基板の初期化とシリアル認識」で行った「RPI_blank.uf2」ファイルの書き込み作業が必要になります。

UARTピン設定例

//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);


説明

UART0とUART1を使ってパソコンと通信します。
UART0側に入力した文字をUART1側に送信します。
UART1側に入力した文字をUART0側に送信します。

マクロ定義ではUART0(Serial1オブジェクト)に使用するピンをTXはGPIO0、RXはGPIO1、UART1(Serial2オブジェクト)に使用するピンをTXはGPIO4、RXはGPIO5を使用します。

配線

パソコンとRaspberryPiPicoのUARTの変換にはFT232RLを使います。
(FT232RLのショートピンは3.3V出力にします)

サンプルスケッチ

#define SERIAL1_TX (0)
#define SERIAL1_RX (1)

#define SERIAL2_TX (4)
#define SERIAL2_RX (5)

//マクロ定義について
//命名規則的にはUART0、UART1になるが、対応するオブジェクトがSerial0, Serial1となる。
//ピン設定で Serial1.setTX(UART0_TX); となり、紛らわしいいのでSERIAL1_TXとした。

void setup() {

  Serial1.setTX(SERIAL1_TX);
  Serial1.setRX(SERIAL1_RX);
  Serial1.begin(115200, SERIAL_8N1);

  Serial2.setTX(SERIAL2_TX);
  Serial2.setRX(SERIAL2_RX);
  Serial2.begin(115200, SERIAL_8N1);
}

void loop() {

  char c;

  if(Serial1.available()){
    c = Serial1.read();
    Serial2.write(c);
  }

  if(Serial2.available()){
    c = Serial2.read();
    Serial1.write(c);
  }
}

結果

結果の確認にTeratermを2つ起動します。
今回UART0に接続したFT232RLはCOM8、UART1に接続したFT232RLはCOM11で認識されました。
COM8側のTeratermから”tamanegi”と入力すると、COM11側に”tamanegi”と表示されました。
COM11側のTeratermから”HELLO”と入力すると、COM8側に”HELLO”と表示されました。


I2Cの設定

I2Cで使えるピン

RP2040でのI2Cは2系統使用することができます。
I2Cに使用するピンは決められた範囲の中で任意のピンを指定できます。
I2C0を使用する場合は、下図左側のマスクされていないピンをWireオブジェクトで指定します。
I2C1を使用する場合は、下図右側のマスクされていないピンをWire1オブジェクトで使用します。

I2C0を使用する場合               I2C1を使用する場合

オブジェクト名表記使用できるピン
WireI2C0SDA : 0, 4, 8, 12, 16, 20, 28
SCL : 1, 5, 9, 13, 17, 21
Wire1I2C1
SDA : 2, 6, 10, 14, 18, 22, 26
SCL : 3, 7, 11, 15, 19, 27

I2C0表記のピンを使用する場合はWireオブジェクトを、I2C1表記のピンを使用する場合はWire1オブジェクトを使用します。

OK例:I2C0のSDAにGPIO0 と SCLにGPIO5を設定する。
NG例:I2C0のSDAにGPIO0 と SCLにGPIO3を設定する(GPIO3は I2C1のため)

使用するI2Cとオブジェクトの関係が違う、またはSDAとSCLに非対応のピンを設定して書き込みを行うと以下のメッセージが表示されRaspberryPiはCOM認識されなくなります。

「基板の初期化とシリアル認識」で行った「RPI_blank.uf2」ファイルの書き込み作業が必要になります。

I2Cピン設定例

#include <Wire.h>               //I2C Wireオブジェクトを使用する場合インクルードする

//I2C0に GP0(SDA)とGP1(SCL)を設定します。
  Wire.setSDA(0);
  Wire.setSCL(1);               //I2C0はWireオブジェクトを使用します。

//I2C1に GP4(SDA)とGP5(SCL)を設定します。
  Wire1.setSDA(4);
  Wire1.seTSCL(5);              //I2C1はWire1オブジェクトを使用します。

説明

小型のOLED 表示器 SSD1306 を I2Cで制御します。
画面に
“RasPico”
“OLED1306
と表示します。

I2C0に使用するピンはSDAにGPIO0、SCKにGPIO1を設定します。

配線

SSD1306のI2Cアドレスは3c(h)に設定しています。

スケッチ

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define SCREEN_WIDTH 128                //解像度 128 x 64 で使用します。
#define SCREEN_HEIGHT 64                //SCREEN_HEIGHTは 32 に設定することができます。

#define OLED_RESET     -1               //使用しないので -1を設定する。
#define SCREEN_ADDRESS 0x3C             //I2Cアドレスは 0x3C
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
                                        //表示制御にはAdafruit製 SSD1306を使用する。
                                        //初期化時には I2C0を使用する (Wire)
                                        
void setup() {
  
  Wire.setSDA(0);                       //I2C0で使用するGPは SDA = 0, SCL = 1
  Wire.setSCL(1);

  if(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
    for(;;);
  }

  display.clearDisplay();               //何か表示されている場合に備えて表示クリア

  display.setTextSize(2);               //フォントサイズは2(番目に小さい)
  display.setTextColor(SSD1306_WHITE);  //色指定はできないが必要
  display.setCursor(0, 0);              //テキストの表示開始位置
  display.println(F("RasPico"));        //表示文字列
  display.print(F("SSD1036"));

  display.display();                    //バッファ転送(表示)
}

void loop() {
}

結果

SPIの設定

SPIで使えるピン

RP2040でのSPIは2系統使用することができます。
SPIに使用するピンは決められた範囲の中で任意のピンを指定できます。
SPI0を使用する場合は、下図左側のマスクされていないピンをSPIオブジェクトで指定します。
SPI1を使用する場合は、下図右側のマスクされていないピンをSPI1オブジェクトで使用します。

オブジェクト名表記使用できるピン
SPISPIRX(MISO) : 0, 4, 16, 20
TX(MOSI) : 3, 7, 19
SCK : 2, 6, 18, 22
SPI1SPI1RX(MISO) : 8, 12, 28
TX(MOSI) : 11, 15, 27
SCK : 10, 14, 26

SPI0表記のピンを使用する場合はSPIオブジェクトを、SPI1表記のピンを使用する場合はSPI1オブジェクトを使用します。
表上はCSの位置が記載されていますが、どちらのオブジェクトにも任意のピンを設定してもよさそうです。

OK例:SPI0のTXにGPIO3 と RXにGPIO0 と SCKにGPIO2を設定する。
NG例:SPI1のTXにGPIO3 と RXにGPIO8 と SCKにGPIO10を設定する。(GPIO3は SPI0のため)

使用するSPIとオブジェクトの関係が違う、またはMOSIとMISOとSCKに非対応のピンを設定して書き込みを行うと以下のメッセージが表示されRaspberryPiはCOM認識されなくなります。

「基板の初期化とシリアル認識」で行った「RPI_blank.uf2」ファイルの書き込み作業が必要になります。

SPIピン設定例

#include <SPI.h>                  //SPIオブジェクトを使用する場合インクルードする

//SPI0に GPIO3(TX)とGPIO0(RX)とGPIO2(SCK)を設定します。
  SPI.setTX(3);
  SPI.setRX(0);
  SPI.setSCK(2);                 //SPI0はSPIオブジェクトを使用します。

//SPI1に GPIO11(TX)とGPIO12(RX)とGPIO14(SCK)を設定します。
  SPI1.setTX(11);
  SPI1.setRX(12);
  SPI1.setSCK(14);               //SPI1はSPI1オブジェクトを使用します。

説明

小型LCD表示器ST7735をSPIで制御します。
画面に
“RasPico”
“ST7735”
と表示します。

SPI0に使用するピンは
CSにGPIO17
MOSIにGPIO19
SCKにGPIO18
を設定します。

配線

スケッチ

#include <Adafruit_GFX.h> 
#include <Adafruit_ST7735.h>
#include <SPI.h>

//ピン設定
#define ST7735_DC      28  // DC
#define ST7735_CS      17  // CS
#define ST7735_SCLK    18  // Clock
#define ST7735_MOSI    19  // MOSI
#define ST7735_RST     22  // Reset 

//SPI0をコンストラクタに指定する
Adafruit_ST7735 tft = Adafruit_ST7735(&SPI, ST7735_CS, ST7735_DC, ST7735_RST);

void setup(void) 
{
  SPI.setTX(ST7735_MOSI);                    //SPI 設定
  SPI.setSCK(ST7735_SCLK);

  tft.initR(INITR_BLACKTAB);                 //Init ST7735S初期化
  
  tft.fillScreen(ST77XX_BLACK);              //背景の塗りつぶし

  //テキスト表示
  tft.setRotation(3);                        //画面回転
  tft.setTextSize(2);                        //サイズ

  tft.setCursor(0, 0);                      //カーソル位置                      
  tft.setTextColor(ST77XX_RED);              //赤
  tft.println("RasPico");

  tft.setTextColor(ST77XX_GREEN);            //緑
  tft.print("ST7735\n");
}

void loop()
{
}

結果

RP2040スケッチ

内部温度モニタ

説明

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

スケッチ

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

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

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

結果

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


HID(マウス)

説明

RP2040はHID(Human Interface Device)マウスとして動作できます。

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

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

配線

サンプルスケッチ

#include <Mouse.h>

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

#define STEP_LEFT (-1)                  //左方向の移動量 1dot
#define STEP_RIGHT (1)                  //右方向の移動量 1dot
void setup() {

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

  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);
  }
}

結果

HID(キーボード)

説明

RP2040はHID(Human Interface Device)キーボードとして動作できます。

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

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

配線

サンプルスケッチ

#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
  }
}

結果

スケッチ

//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);
}

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の点滅は目視で確認できますが、遅延は認識できません。
オシロスコープを使用し信号を確認します。

結果の画像は信号が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をコピーしました