LED点灯パターンのヘッダファイルの作成

ヘッダファイルとは

それでは、ソースコードの分割例として、まずはドットマトリクスLEDの点灯パターン部分のコードを別ファイルに分割することにしましょう。

点灯パターン部分のコードの分割先は、ここでは「ヘッダファイル」とします。

ヘッダファイルは、プログラム中に「#include」で指定され、ビルド時のコンパイルの前処理の段階で読み込まれる(インクルードされる)ファイルです。

今までのプログラムでも、Arduinoフレームワークの提供する「Arduino.h」というヘッダファイルをインクルードしてきました。今回はヘッダファイルを自作し、インクルードして使うことを目指します。

ヘッダファイルの追加とインクルードガード

エクスプローラでプロジェクト内のincludeを右クリックして「新しいファイル」を選択し、ヘッダファイルを追加しましょう。ここでは「char8x8.h」というファイルを追加することにします。

図10-2 ヘッダファイルの追加

ファイルの追加が完了したら、まずは「インクルードガード」を行いましょう。

インクルードガイドは、ヘッダファイルの読み込み(インクルード)が複数行われるのを防止するためのもので、以下のように書かれます。

#ifndef CHAR8X8_H_
#define CHAR8X8_H_

変数等の宣言

#endifCode language: Arduino (arduino)

ここで、「#ifndef」「#define」の後ろには他の定義等と重複しないそのヘッダファイル固有の「定義」を設定します。上記の例では「CHAR8X8_H_」という「定義」を設定しています。

インクルードガードには、以下のようなマクロ(ソースコードがコンパイルされる前に行われる「前処理用」の命令)が使われています。

//「定義値」が設定されていない時,「#ifndef」〜「#ifdef」を処理に含める
#ifndef 定義値#endifCode language: Arduino (arduino)
//「定義値」を定義する
#define 定義値Code language: Arduino (arduino)

つまり、上記のコード例では「CHAR8X8_」が定義されていない時は「#ifndef」〜「#endif」を処理に含めるとともに「CHAR8X8_」を定義し、「CHAR8X8_」が定義されている時は「#ifndef」〜「#endif」を処理に含めません。

インクルードガードは、ヘッダファイルが複数回読み込まれた場合に重複した定義や宣言が発生しコンパイルエラーになることを防ぐためのものです。よくわからなければ、とりあえずは「ヘッダファイルを書く時のおまじない」と思っておいても良いでしょう。

「定義」は、他の定義等と重複しなければ何でも良いですが、「CHAR8X8_H_」ような「ヘッダファイル名を大文字にしてコロンをアンダーバーに置き換え、後ろにもアンダーバーを加えたもの」が比較的よく使われています。

ヘッダファイルへのコードの移行

それでは、main.cppの点灯パターン部分を char8x8.h にコピー&ペーストしましょう。以下はコピー&ペースト後の char8x8.h の例です。

char8x8.h 10-1

#ifndef CHAR8X8_H_
#define CHAR8X8_H_

// 文字Aの点灯パターン
byte ledPatternA[8] = {
  0b00000000,
  0b00001000,
  0b00010100,
  0b00100010,
  0b00100010,
  0b00111110,
  0b00100010,
  0b00100010,
};

// ドットマトリクスLEDの点灯パターン(カソード側=1行ずつ表示)
byte ledCathodePattern[8] = {
  0b00000001,
  0b00000010,
  0b00000100,
  0b00001000,
  0b00010000,
  0b00100000,
  0b01000000,
  0b10000000,
};

#endif
Code language: Arduino (arduino)

このままでは「byte」が構文エラーになりますが、これはArduinoフレームワーク用の関数や定数が定義されていないためで、main.cppと同様にソースコードの冒頭に

#include <Arduino.h>Code language: Arduino (arduino)

を追加することで解消できます。以下は、char8x8.cppの作成例です。冒頭にこのソースコードに関するコメントも追加しています。

char8x8.h 10-2

/**********************************************************
  製作者  アドウィン
  解説   8x8ドットマトリクスLED用の点灯パターン
**********************************************************/
#ifndef CHAR8X8_H_
#define CHAR8X8_H_
#include <Arduino.h>

// 文字Aの点灯パターン
(以下略)
Code language: Arduino (arduino)

main.cppからchar8x8.hにコピーした部分を削除し、#includeでchar8x8.hの読み込みを追加しましょう。

main.cpp 10-1

#include <Arduino.h> // Arduino.h の読み込み
#include "char8x8.h" // char8x8.h の読み込み

// ドットマトリクスLEDアノード側ピンの配列
int ledAnodePins[8] = {0, 2, 4, 5, 16, 17, 18, 19};
// ドットマトリクスLEDカソード側ピンの配列
int ledCathodePins[8] = {21, 22, 23, 25, 26, 27, 32, 33};

/*
 * ドットマトリクスLED初期設定関数
 */
(以下略)
Code language: Arduino (arduino)

プログラムのビルドはできましたか? また、プログラムは想定通り動いていますか?

インクルード時のファイル名の囲い

ところで、今までのコードではArduino.hのインクルードは <Arduino.h> のようにファイル名を「<」〜「>」で囲ってきました(上記コードでもそのようにしています)。それに対し、char8x8.hのインクルードは "char8x8.h" のように「”」〜「”」でファイル名を囲んでいます。

これは、char8x8.hが現在のプロジェクト内に作成されたヘッダファイルだからです。

Arduino.hは開発環境共通のファイルであり、開発環境共通のインクルードファイルが置かれている場所に存在しています。インクルードするファイル名を「<」〜「>」で囲うと、コンパイラ(プリプロセッサ)はそのような「インクルードファイルが置かれている場所」から指定のヘッダファイル(Arduino.h)を探してきます。

一方、「”」〜「”」でファイル名を囲った場合、コンパイラはまずは現在のディレクトリから指定のヘッダファイルを探し、その後「インクルードファイルが置かれている場所」を探します。「<」〜「>」は「インクルードファイルが置かれている場所」しか探さないため、ユーザ(プログラマ)が独自に作ったヘッダファイルのインクルードには「”」〜「”」を使う必要がある場合があります。

本コースの開発環境では、プロジェクトの「include」ディレクトリは「インクルードファイルが置かれている場所」のひとつになっており、さらにはmain.cppが置かれている「src」ディレクトリも「インクルードファイルが置かれている場所」のひとつになっています。したがって、「<」〜「>」と「”」〜「”」とで振る舞いに違いはなく、ファイル名を「<」〜「>」で囲んで「#include <char8x8.h>」のようにしてもインクルードは正常に行われます。

ここでは、開発環境提供ではなく独自に作ったヘッダファイルだということを示す意味も含めて、char8x8.hのインクルードに「”」〜「”」を使ってみました。

前の記事

関数の修正

次の記事

関数の宣言と定義の分離