見出し画像

6.新規プロジェクトの開発(外部LED)-Raspberry Pi Pico Windows C言語プログラミング入門

8.新規プロジェクトの開発
8.1. Pico Project Generatorによる新規プロジェクト作成
これまでは、サンプルのプログラムのビルド、修正をしましたが、今回からは、ゼロから自分自身のオリジナルPicoのプログラムを作成します。新規プロジェクトのCMakeLists.txtを新規に作成するのは、大変ですが、先の記事”1.開発環境セット”でインストールしました”Pico Project Generator"を使用します。

Picoインストール先のフォルダの"Pico Project Generator"をクリックし、起動します。

図8-1
図8-2

新規プロジェクトの構成設定画面が表示されます。Project Nameにプロジェクト名を入力します。例えば"ExternalLedBlink"と入力します。Locationに保存先を入力します。"pico-examples”があるPicoインストール先を選択します。BoardTypeは”pico”を選択します。PicoWの場合は、リストから"pico_w”を選択します。その他、Raspberry Pi財団からのボード以外のサードパーティーのボードも対応しています。

Console Optionsの選択は、このPico Project Generator確認の作業では、"Console over UART"のみにチェックを入れます。このため第7章でのWindowsPCとPicoのUART接続を参考にUART0をWindowsPCに接続してください。

他の設定は、初期状態にします。ここでの設定内容は、CMakeLists.txtに反映されます。また、手動で、このCMakeLists.txtは編集可能です。OKボタンを押します。

図8-3

上記のように新規プロジェクトの構成が作成されます。例えばこのメッセージ中でプロジェクトのターゲットボードが”pico”であることがわかります。

8.2.作成された空の新規プロジェクトの動作確認
Visual Studio Codeを起動します。過去のプロジェクトが表示される場合、「ファイル」>「フォルダのクローズ」を選択し、閉じます。次に先ほど新規作成したExcternalLedBlinkのフォルダを「ファイル」>「ホルダのオープン」で開きます。このとき、以下画面のように、このフォルダのファイルは信頼できるかどうかのダイアログ画面が表示されるのでYESボタンを押します。このメッセ―ジは、どこで作成されたかにかかわらず、新規ファイルを読み込むとき、一度は表示されます。

図8-4

以下、画面となります。

図8-5

Visual Studio Codeで、上記画面のようにExternalLedBlinkフォルダ内の一覧が表示されます。

図8-6

ここで、実際に編集するのは
CMake構成ファイル CMakeLists.txt
Cソースリスト ExternalLedBlink.c
の2つです。
初期状態のCMakeLists.txtは以下です。

# Generated Cmake Pico project file
cmake_minimum_required(VERSION 3.13)

# Pull in Raspberry Pi Pico SDK (must be before project)
include(pico_sdk_import.cmake)
project(ExternalLedBlink C CXX ASM)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)

# Initialise pico_sdk from installed location
# (note this can come from environment, CMake cache etc)
set(PICO_SDK_PATH "C:/PiPicoC/Pico/pico-sdk")
set(PICO_BOARD pico CACHE STRING "Board type")
if (PICO_SDK_VERSION_STRING VERSION_LESS "1.4.0")
	 message(FATAL_ERROR "Raspberry Pi Pico SDK version 1.3.0 (or later) required. Your version is 	${PICO_SDK_VERSION_STRING}")
endif()

# Initialise the Raspberry Pi Pico SDK
pico_sdk_init()

# Add executable. Default name is the project name, version 0.1
add_executable(ExternalLedBlink ExternalLedBlink.c )
pico_set_program_name(ExternalLedBlink "ExternalLedBlink")
pico_set_program_version(ExternalLedBlink "0.1")
pico_enable_stdio_uart(ExternalLedBlink 0)
pico_enable_stdio_usb(ExternalLedBlink 1)

# Add the standard library to the build
target_link_libraries(ExternalLedBlink  pico_stdlib)

# Add the standard include files to the build
target_include_directories(ExternalLedBlink PRIVATE
  ${CMAKE_CURRENT_LIST_DIR}
  ${CMAKE_CURRENT_LIST_DIR}/.. # for our common lwipopts or any other standard includes, if required
)

# Add any user requested libraries
target_link_libraries(ExternalLedBlink )

pico_add_extra_outputs(ExternalLedBlink)

ここで、よく編集が必要になる箇所は、まずstdioの定義部分です。
pico_enable_stdio_uart(ExternalLedBlink 1)
pico_enable_stdio_usb(ExternalLedBlink 0)

stdioのuart出力を1として、有効にしておりstdioのusb出力を0として無効にしています。デフォルトのstdioの出力はUART0なので、このピンが標準出力となり、usb標準出力は使用できません。このため、UARTシリアルUSB変換ケーブルでWindowsPCと接続する必要があります。

Cソースリストは以下のシンプルなものです。

#include <stdio.h>

#include "pico/stdlib.h"

int main()
{
    stdio_init_all();
    puts("Hello, world!");
    return 0;
}

この初期状態でビルドしてみます。まず、画面下ブルーのバーで
Not Kit Selected
となっているので、この部分をクリックし、コンパイラを選択します。


図8-7

"GCC 10.3.1 arm-none-eabi"を選択します。選択すると、cmakeが起動され、以下、Build ファイルが作成された、とのメッセージが表示され完了します。

図8-8

この以下のステップでの動作確認は、飛ばしても問題ありません。次の章で実際の自分自身のプログラムに入ることができます。ただ、空の新規プロジェクトがどのようなものか理解はできます。第7章のWindowsPCとRaspberry Pi PicoとのUART接続にしたがって、UART0をUSBシリアルケーブルでPCに接続します。次に、Picoのインストール先ファルダのExternalLedBlink>build内に作成されたExternalLedBlink.uf2をPicoに書き込み、実行します。

Tera TermVTを起動し、Adafruitsのケーブルを選択します。

図8-9
図8-10

新規プロジェクト作成で作成された基本のプログラムはソースリストから、おわかりのように、標準出力に”Hello World!"を一度出力して終了します。このため、再度、USBケーブルを抜き、リセット後、実行します。以下、Tera Term VTに1行だけ、”Hello World!"が表示されます。


9.LEDプロジェクト
この章以降のプロジェクトは、前第8章の新規プロジェクト作成手順に従って、同様にプロジェクトを作成します。ここまでのサンプルプログラムは、Picoに外部回路を追加することなく、ビルド、実行してきました。以降新規プロジェクトでは、LED,スイッチ、センサー等を追加し、新しいプログラムを作成します。

9.1.外部LEDの点滅
9.1.1.外部LED点滅の空の新規プロジェクト作成
Picoインストール先のフォルダの"Pico Project Generator"をクリックし、起動します。

図9-1

新規プロジェクトの構成設定画面が表示されます。Project Nameにプロジェクト名を入力します。"ExternalLedBlink"と入力します。Locationに保存先を入力します。"pico-examples”があるPicoインストール先を選択します。BoardTypeは使用ボードに合わせて”pico”、または"pico_w"を選択します。今後、本書の新規プロジェクト作成では"Console Options"を先の8章と異なり、UARTを使用せず、USBを使用することとします。UARTとPCとの接続が不要となります。ただUSBの標準出力は、Picoにロードされたプログラムが起動時に、PC側で認識されることとなり、PC側のターミナルソフトはPicoプログラムが起動後に立ち上げる必要があります。ソースリストにこの対応する関数を呼びだしています。詳細はソースリストを参照してください。あとの設定は、初期状態のままとします。この構成は、CMakeLists.txtに反映されます。また、手動で、このCMakeLists.txtは編集可能です。OKボタンを押します。

Picoインストール先のフォルダにある”Visual Studio Code for Pico”をクリックし、起動し、新規作成したExcternalLedBlinkのフォルダを「ファイル」>「ホルダのオープン」で開きます。

図9-2

Visual Studio Codeで、上記画面のようにExternalLedBlinkフォルダ内の一覧が表示されます。CMakeLists.txtは第7章と異なり、以下stdioの定義部分が以下になっています。
pico_enable_stdio_uart(ExternalLedBlink 0)
pico_enable_stdio_usb(ExternalLedBlink 1)
stdioのuart出力を0にして無効とします。stdioのusb出力を1として有効にしています。次に"GCC 10.3.1 arm-none-eabi"を選択します。選択すると、cmakeが起動され、以下、Build ファイルが作成された、とのメッセージが表示され完了します。

図9-3
図9-4

これで、空の新規プロジェクトが作成できました。ただ、標準出力USBに1度だけ、"Hello world!"を出力だけのプログラムです。このままでは、標準出力をUARTからUSBに変更しているため動作しません。

9.1.2.外部LED点滅のプログラム作成
プロジェクト名:ExternalLedBlink
プロジェクト概要
ここから、空のプロジェクトを編集し、実際のプログラムを作成します。このプログラムでは、赤、黄色、青の3つのLEDを使用して、信号機のプログラムを作成します。GPIOのドライブ電流は、SDK,およびデータシートによると、設定できる値は2,4,8,12 mAで初期値は4mAです。またRP2040が同時に出力できる電流値はMax50mAです。この範囲内で使用することが必要です。LEDの順方向電圧は2.1Vです。したがって、電流制限抵抗を330Ωとすると、LED電流 = (3.3V - 2.1V)/330 = 3.6mA
となり、GPIOドライブ電流4mA以内となります。プラス側アノード、マイナス側カソード、足の長い方がアノード端子です。


図9-5

部品リスト
3mm赤色LED 1
3mm黄緑色LED 1
3mm黄色LED 1
1/4Wカーボン抵抗 330Ω 3

回路図

図9-6

ソースリスト

#include <stdio.h>

#include "pico/stdlib.h"

#define LED_RED_PIN 2

#define LED_YELLOW_PIN 3

#define LED_BLUE_PIN 10

int main()
{
  int     stage;

    stdio_init_all();
    gpio_init(LED_RED_PIN);
    gpio_set_dir(LED_RED_PIN, GPIO_OUT);
    gpio_init(LED_YELLOW_PIN);
    gpio_set_dir(LED_YELLOW_PIN, GPIO_OUT);
    gpio_init(LED_BLUE_PIN);
    gpio_set_dir(LED_BLUE_PIN, GPIO_OUT);

 	stage = 0;
    while(true)
    {
    	switch (stage)
    	{
    	  case 0: //Go
	        	gpio_put(LED_BLUE_PIN, 1);
       	    	gpio_put(LED_YELLOW_PIN, 0);
           		gpio_put(LED_RED_PIN, 0);
           		puts("Go");
           		sleep_ms(5000);
           		break;
	       	case 1: //Caution
		      gpio_put(LED_BLUE_PIN, 0);
           	 	gpio_put(LED_YELLOW_PIN, 1);
           		gpio_put(LED_RED_PIN, 0);
           		puts("Caution");
           		sleep_ms(1000);
           		break;
        	case 2: //Stop
		       	gpio_put(LED_BLUE_PIN, 0);
			    gpio_put(LED_YELLOW_PIN, 0);
			    gpio_put(LED_RED_PIN, 1);
		        puts("Stop");
		        sleep_ms(5000);
		        break;
		    default:
		      break;
	   }
	    stage++;
	    if(stage >= 3)
	    {
	       	stage = 0;
	    }
	}
	return 0;
}

Stage変数が、現在の信号機の状態を保持し、0:赤、1:黄色、2:青と順に、切り替わり、指定時間点灯します。各色の点灯時間は、sleep_msで変更できます。

Picoでプログラム実行後、PC側でターミナルソフトTera Termを起動し、Pico接続のCOMポートを選択し、待機します。C言語には一般的な標準出力の関数として、printf関数、およびputs関数があります。違いは、printfが書式付出力関数に対して、putsは文字列専用の関数です。文字のみを出力したい場合はputsがおすすめです。putsは文字列に改行コードが不要で、自動的に改行します。実行するとPicoを接続先のコンピュータに信号が変化するごとにメッセージが表示されます。

9.2.LED点灯に対するスッチ反応速度計測
プロジェクト名:LedReactionTime
プロジェクト概要
人は、ゲーム、自動車運転等で、画面、信号機の変化に対して反応し行動を起こしますが、この反応時間は、どの程度でしょうか、また環境、年齢での反応時間は異なるのでしょうか。この疑問に答えるため、このプロジェクトを作成します。

図9-7

部品リスト
3mm赤色LED 1
1/4Wカーボン抵抗 330Ω 1
タクトスイッチ 2

配線図

図9-8

このプロジェクトではLEDと2つのスイッチを使用します。このプロジェクトでは、信号の正論理、負論理を学習します。上記回路で、スイッチSW2は、正論理です。正論理とは、信号がオンのとき、GPIOピンがハイレベル、プログラムで読み取る値が1になることを意味します。逆にSW3は負論理です。信号がオンのとき、GPIOピンがローレベル、プログラムで読み取る値が0になります。このためには、SW2のスイッチの片方を3.3Vに接続し、他方をGPIOピンに接続し、プルダウンに設定します。SW3はスイッチの片方をGNDに接続し、他方をGPIOピンに接続し、プルアップに設定します。

乱数生成関数rand()を使用します。このrand()を組み込むためには、C++の標準ライブラリの組み込みが必要です。このため、以下の
#include <stdlib.h>
の記述が必要です。stdlib.hがインクルードされていない場合、ビルドの時、warningメッセージがでます。これは、#include "pico/stdlib.h"とは別であり、このpico/stdlib.hのインクルードも必要です。ランダムな時間間隔でLEDが点灯します。このあと、2つのスッチの状態を読み取り、LEDオンからの経過時間を表示します。

ソースリスト

#include <stdio.h>

#include <stdlib.h>

#include "pico/stdlib.h"

#define LED_RED_PIN 2

#define BUTTON_ACTIVE_HIGH_PIN 3

#define BUTTON_ACTIVE_LOW_PIN 10

int main()
{
    stdio_init_all();

    gpio_init(PICO_DEFAULT_LED_PIN);
    gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT);
    gpio_init(LED_RED_PIN);
    gpio_set_dir(LED_RED_PIN, GPIO_OUT);
    gpio_init(BUTTON_ACTIVE_HIGH_PIN);
    gpio_set_dir(BUTTON_ACTIVE_HIGH_PIN, GPIO_IN);
    gpio_pull_down(BUTTON_ACTIVE_HIGH_PIN);
    gpio_init(BUTTON_ACTIVE_LOW_PIN);
    gpio_set_dir(BUTTON_ACTIVE_LOW_PIN, GPIO_IN);
    gpio_pull_up(BUTTON_ACTIVE_LOW_PIN);
    gpio_put(PICO_DEFAULT_LED_PIN, 0);
    gpio_put(LED_RED_PIN, 0);

    while(true){
        int rval = rand()%10;
        int wait = (rval + 1)*1000; 
        int curUs, span,startUs;

        sleep_ms(wait);
        gpio_put(LED_RED_PIN, 1);
        startUs = time_us_32 ();

        while(true)
        {
            curUs = time_us_32 ();
            span = curUs - startUs;

            if(span > 1000*1000*10){
                break;
            }if (gpio_get(BUTTON_ACTIVE_HIGH_PIN)){
                printf("%s%7.3f%s\n", "Reaction time=", (double)span/(1000.0*1000.0), "sec");
                puts("Push on Active high button");
                break;
            }if (!gpio_get(BUTTON_ACTIVE_LOW_PIN)){
                printf("%s%7.3f%s\n", "Reaction time=", (double)span/(1000.0*1000.0), "sec");
                puts("Push on Active low button");
                break;
            }
        }
        gpio_put(LED_RED_PIN, 0);
    }
    return 0;
}

9.3.LED点灯,ブザーに対するスッチ反応速度計測
LedBuzzerReactionTime

プロジェクト概要
前章ではLED、光の目に対する反応を計測、観測しましたが、このプロジェクトでは電子ブザーを使い、音に対する反応速度を測定します。電子ブザーは、電磁ブザー(アクティブブザー)と圧電ブザー(パッシブブザー)の2つに大きく分かれます。電磁ブザーは、オンオフのみで、オンで規定のブザー音がなります。圧電ブザーは、発振回路がなく、外部から入力された波形に応じて、発音します。今回は、通常のブザー音のみが必要なため、電磁ブザーを使用します。村田製作所の電子ブザーPKB24SPCH3601を使用します。以下部品リストにある秋月電子通商で入手できます。
動作電圧範囲はDC 3.0~15Vで消費電流は3V時2.8mAとあり、GPIO初期値mA以下で問題なく使用可能です。

部品リスト
3mm赤色LED 1
1/4Wカーボン抵抗 330Ω 1
タクトスイッチ 2
電子ブザーPKB24SPCH3601 1 秋月電子通商

配線図

図9-9

ソースリスト

#include <stdio.h>

#include <stdlib.h>

#include "pico/stdlib.h"

//#define PICO_DEFAULT_LED_PIN 25

#define LED_PIN 2

#define BUZZER_PIN 3

#define BUTTON_PIN 10


int ReactionMode = 0;
int main()
{
    stdio_init_all();

    gpio_init(PICO_DEFAULT_LED_PIN);
    gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT);
    gpio_init(LED_PIN);
    gpio_set_dir(LED_PIN, GPIO_OUT);
    gpio_init(BUZZER_PIN);
    gpio_set_dir(BUZZER_PIN, GPIO_OUT);
    gpio_init(BUTTON_PIN);
    gpio_set_dir(BUTTON_PIN, GPIO_IN);
    gpio_pull_down(BUTTON_PIN);
    gpio_put(PICO_DEFAULT_LED_PIN, 0);
    gpio_put(LED_PIN, 0);
    gpio_put(BUZZER_PIN, 0);

    while(true){
        int rval = rand()%10;
        int wait = (rval + 1)*1000; 
        int curUs, span,startUs;

        sleep_ms(wait);
        if(ReactionMode == 0){
            gpio_put(LED_PIN, 1);
        } else {
            gpio_put(BUZZER_PIN, 1);
        }     

        startUs = time_us_32 ();
        while(true)
        {
            curUs = time_us_32 ();
            span = curUs - startUs;
            if(span > 1000*1000*10){
                break;
            }

            if(gpio_get(BUTTON_PIN)){
                if(ReactionMode == 0){
                    printf("%s%7.3f%s\n", "Led Reaction time=", (double)span/(1000.0*1000.0), "sec");
                }else{
                    printf("%s%7.3f%s\n", "Buzzer Reaction time=", (double)span/(1000.0*1000.0), "sec");
                }
                puts("Push on button");
                break;
            }
        }

        gpio_put(LED_PIN, 0);
        gpio_put(BUZZER_PIN, 0);
        if(ReactionMode == 0){
            ReactionMode = 1;
        }else{
            ReactionMode = 0;
        }
    }
    return 0;
}

9.4.LED点灯,ブザーリアクションゲームもぐらたたき
プロジェクト名:WhackAmoleGame

プロジェクトの概要
このプロジェクトでは4個のLEDをランダムに指定時間点灯させ、そのLEDに対応するスイッチが指定時間内にオンされば、叩かれたと判定します。このゲームはリセットボタンスタート後、指定時間、実行されます。

図9-10

部品リスト
3mm赤色LED 4
1/4Wカーボン抵抗 330Ω 4
タクトスイッチ 4
電子ブザーPKB24SPCH3601 1 秋月電子通商

配線図

図9ー11

ソースリスト

#include <stdio.h>

#include <stdlib.h>

#include "pico/stdlib.h"


//#define PICO_DEFAULT_LED_PIN 25

#define BUZZER_PIN 2

#define LED1_PIN 3

#define LED2_PIN 10

#define LED3_PIN 11

#define LED4_PIN 12

#define BUTTON1_PIN 13

#define BUTTON2_PIN 14

#define BUTTON3_PIN 15

#define BUTTON4_PIN 16

int CurLedIndex;
int Score;
int Value;

int main()
{
    stdio_init_all();

    gpio_init(PICO_DEFAULT_LED_PIN);
    gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT);
    gpio_init(BUZZER_PIN);
    gpio_set_dir(BUZZER_PIN, GPIO_OUT);

    gpio_init(LED1_PIN);
    gpio_set_dir(LED1_PIN, GPIO_OUT);
    gpio_init(LED2_PIN);
    gpio_set_dir(LED2_PIN, GPIO_OUT);
    gpio_init(LED3_PIN);
    gpio_set_dir(LED3_PIN, GPIO_OUT);
    gpio_init(LED4_PIN);
    gpio_set_dir(LED4_PIN, GPIO_OUT);

    gpio_init(BUTTON1_PIN);
    gpio_set_dir(BUTTON1_PIN, GPIO_IN);
    gpio_pull_down(BUTTON1_PIN);
    gpio_init(BUTTON2_PIN);
    gpio_set_dir(BUTTON2_PIN, GPIO_IN);
    gpio_pull_down(BUTTON2_PIN);

    gpio_init(BUTTON3_PIN);
    gpio_set_dir(BUTTON3_PIN, GPIO_IN);
    gpio_pull_down(BUTTON3_PIN);
    gpio_init(BUTTON4_PIN);
    gpio_set_dir(BUTTON4_PIN, GPIO_IN);
    gpio_pull_down(BUTTON4_PIN);

    gpio_put(PICO_DEFAULT_LED_PIN, 0);
    gpio_put(BUZZER_PIN, 0);
    gpio_put(LED1_PIN, 0);
    gpio_put(LED2_PIN, 0);
    gpio_put(LED3_PIN, 0);
    gpio_put(LED4_PIN, 0);

    Score = 0;
    srand(time_us_32());
    while(true){
        int rval = rand()%10;
        int wait = (rval + 1)*300; 
        int curUs, span,startUs;
        sleep_ms(wait);

        CurLedIndex = rand()%4;
        switch (CurLedIndex)
        {
            case 0:
                gpio_put(LED1_PIN, 1);
                break;
            case 1:
                gpio_put(LED2_PIN, 1);
                break;
            case 2:
                gpio_put(LED3_PIN, 1);
                break;
            case 3:
                gpio_put(LED4_PIN, 1);
                break;
            default:
                break;
        }

        startUs = time_us_32 ();
        while(true)
        {
            curUs = time_us_32 ();
            span = curUs - startUs;

            if(span >= 1000*800){
                break;
            }

            switch (CurLedIndex)
            {
                case 0:
                    Value = gpio_get(BUTTON1_PIN);
                    break;
                case 1:
                    Value = gpio_get(BUTTON2_PIN);
                    break;
                case 2:
                    Value = gpio_get(BUTTON3_PIN);
                    break;
                case 3:
                    Value = gpio_get(BUTTON4_PIN);
                    break;
                default:
                    break;
            }

            if(Value){
                gpio_put(BUZZER_PIN, 1);
                sleep_ms(50);
                gpio_put(BUZZER_PIN, 0);
                Score++;
                printf("%s%d%s%d\n", "Index =", CurLedIndex, " Score =", Score);
                break;
            }
        }

        switch (CurLedIndex)
        {
            case 0:
                gpio_put(LED1_PIN, 0);
                break;
            case 1:
                gpio_put(LED2_PIN, 0);
                break;
            case 2:
                gpio_put(LED3_PIN, 0);
                break;
            case 3:
                gpio_put(LED4_PIN, 0);
                break;
            default:
                break;
        }
    }
    return 0;
}

このプロジェクトでは乱数の生成前に 乱数のseed、種を設定する srand()関数を使用します。この関数を使用しない場合、生成乱数に変化がなく、ランダムなゲームになりません。Srand関数は以下のように、seedに時間usを与えて、初期化します。
srand(time_us_32());
ターミナルソフトTera TermがPC側で起動されていれば、叩かれる毎に、標準出力に現在のスコアが表示されます。
if(span >= 1000*800){
にあるように、800msec以内に対応するスイッチがオンされれば、叩かれたと判定し、ブザーが鳴りスコアがアップします。簡単にたたくことができる場合、この値を小さくし、また叩くことができない場合、値を大きくします。

この内容、”Raspberry Pi Pico Windows C言語プログラミング入門”は 以下のサイトでPDFでご購入できます。
www.elabnet.jp ショップ
https://www.elabnet.jp/shopdata/#cc-m-product-14710382635

この記事が気に入ったらサポートをしてみませんか?