STM32資料 発展編4

便利な機能や書き方を使ってみよう

前回までの説明でクラスをかけるようになった

クラスやライブラリを作成すると、自分以外の人が使う機会も増えるので

読みやすさ(可読性)と使いやすさ(汎用性)の高いコードをかけるようになりたい

そこで今回は、クラスで使える便利な機能たちを紹介します!

列挙型(enum)

クラスを作成することで、使用者は内部の処理を知らなくても動かすことができるようになった

しかし、修正することもあるので内部の処理も分かりやすく書いた方がよい

前回のクラスで書いたWIAの関数では"0x72"が急に出てきてなんのことかよくわからない

そこで、WIAレジスタのアドレスであることを列挙型(enum)を使って分かりやすく書いてみよう

//WIAを取得する関数
uint8_t ICM45686::WIA(){
        
    uint8_t read_value = 0; //WIAの値を格納する変数
        
    //0x72 という値のみが急に出てくるコードになる 
    Read(0x72, &read_value, 1);
        
    //WIAの値が正しいか確認する
    if(read_value != 0xE9){
        
        return 1; //正しくない場合は1を返す
    }
        
    return 0; //正しい場合は0を返す
}

列挙型(enum)の定義

列挙型はこのように書くことができる

変数名と値を1対1対応で書いていくことができる

また、変数型をあらかじめ決めることができ、入力ミスにはコンパイルエラーを出してくれる

レジスタアドレスの列挙型
enum class REGISTER: uint8_t{
    
    ACCEL_DATA_X = 0x00,
    PWR_MGMT_0 = 0x0f,
    WIA = 0x72
};

実際に使用する際にはuint8_t型に明示的に変換した上で、REGISTERの中のWIAの値を使用すればよい

//WIAを取得する関数
uint8_t ICM45686::WIA(){
            
    uint8_t read_value = 0; //WIAの値を格納する変数
            
    //WIAレジスタであることがわかりやすい 
    Read((uint8_t)ICM45686::REGISTER::WIA, &read_value, 1);
            
    //WIAの値が正しいか確認する
    if(read_value != 0xE9){
            
        return 1; //正しくない場合は1を返す
    }
            
    return 0; //正しい場合は0を返す
}

このように書くことで、引数が示していることを明確にすることができた

実際にセンサーのライブラリを書く時には、この後説明する理由でヘッダーファイルのpublicに定義することが多い

クラスの継承について

クラスの継承は、すでにあるクラスに追加の機能を乗せた新しいクラスを作ることである

このように聞くと最初から1つにまとめればいい気がするが、通信形式をI2CとSPIから選びたい場合など

特定の部分(通信形式)のみが違い、他の部分(レジスタアドレスや操作)が共通の場合にとても役に立つ

このような場合、共通部分のみを実装するクラス(親クラス)を作成し、異なる部分のみを実装するクラス(子クラス)で

継承をすることで、子クラスを付け替える(インクルードで選べる)だけ通信形式を変更できる

継承のやり方

継承自体はとても簡単で、クラスを作成するときに継承したいクラス名を書くだけでよい

ここでもpublicかprivateか選ぶことができるが、これは子クラスから親クラスへのアクセス制限の設定になっている

publicにすると親クラスのメンバ変数や関数にアクセスできるようになる

privateにすると親クラスのメンバ変数にアクセスできなくなる

//子クラスの作成
class ICM45686_I2C: public ICM45686{
                        
    //あとはいつも通り

}

仮想関数

ここからは、仮想関数を使って通信形式を簡単に変える方法を紹介する

仮想関数は、関数の宣言のみをしておき後から実装をするというものである

前回は、ヘッダーファイルで通信関数の定義をして、同じクラスのソースファイルですぐに実装をした

仮想環境を使うことで、ヘッダーファイルで通信関数の定義をするが、継承した別のクラスでその中身を実装することができる

前回の関数宣言

//通信用の関数の定義

//書き込みと読み取りの関数
void Read(uint8_t reg, uint8_t* rx_buffer, uint8_t len); //Read関数の宣言
void Write(uint8_t reg, uint8_t* tx_buffer, uint8_t len); //Write関数の宣言

仮想環境の関数宣言

仮想関数を定義するときはvirtualを先頭につける

また、= 0をつけておくことで、実装を忘れたまま実行しようとした時にエラーがでるようになる

//通信用の関数の定義

//書き込みと読み取りの関数
virtual void Read(uint8_t reg, uint8_t* rx_buffer, uint8_t len) = 0; //Read関数の宣言
virtual void Write(uint8_t reg, uint8_t* tx_buffer, uint8_t len) = 0; //Write関数の宣言

子クラスで実装するときは、これと同じ名前の関数を定義し、実装すると自動で上書き(オーバーライド)してくれる

この時、引数は同じでなくてはいけない

前回のクラスにRead/Write関数を作ってあるので、それを置き換えるだけで仮想関数の実装ができる(次回やります)

リンク

・メインページ