株式会社インデペンデンスシステムズ横浜

システム開発エンジニアの西田五郎が運営しております。新規開発案件のご依頼をお待ちしております。

*

Raspberry PiでC言語版Lチカを試す(その2)レジスタを操作する

      2015/05/29

Raspberry PiでC言語を使ったLEDの点灯、消灯(いわいるLチカ)を試してみました。今回は2回目です。前回デバイスドライバを利用する方法を書きました。今回はレジスタを直接操作する方法を使います。今回の方法は組み込み系のプログラミングでは一般的な方法だと思います。前回の方法に比べて自由度が高くアクセス速度も期待出来る方法といえると思います。但し、データシートを読んで間違いがないようにレジスタにアクセスするという意味では前回より難易度は高いと思います。

今回の記事とプログラム作成は以下のサイトと書籍を参考にしながら作成しました。ありがとうございます。より詳細についてはこちらを参照して頂ければと思います。

こじ研(宮城大学 小嶋研究室様) Raspberry Pi 応用編
お手軽ARMコンピュータ ラズベリー・パイでI/O – CQ出版社(※Amazonへのリンクはページ最後にあります。)

あと、組み込みLinux開発の視点からの補足をページ最後に書きました。

ここから本題です。テスト用の回路は以下のような前回と同じ回路です。ここでは、Raspberry PI B+でGPIO21からLEDを制御します。プログラムでも固定で21という数字を使っていますので、お手数ですが必要な場合は読み換えて下さい。
IMG_0286

レジスタのアドレスについて
レジスタにアクセスするための仕様となるデータシートは今現在以下のPDFファイルにあります。
BCM2835 ARM Peripherals

このデータシートで今現在89ページあたりからGPIOのレジスタの仕様があります。但し間違えてはいけないのがアドレスです。ここでは、0x 7E20 0000 から始まっていますが、これはBCM2835のバス上のアドレスです。CPUのARMから見た場合は、0x 2020 0000です。プログラムではこのアドレスを使います。データシートの5ページあたりを参照して確認して下さい。

このアドレスを考慮したマッピングは上記のこじ研(宮城大学 小嶋研究室様) Raspberry Pi 応用編のページの図が分かりやすいと思います。

あと、ユーザプログラムからは仮想メモリへのアクセスとなるため目的の物理アドレスへのマッピングが必要となります。システムコールを利用してこのマッピングを行います。

プログラムについて
以上を踏まえて今回のプログラムは以下です。動作としては前回と同様に3秒間LEDを点灯します。但しですが、今回は実行にはルート権限が必要です。

コンパイルはソースファイルをgpioreg1.cとすると以下のように出来ます。
$ gcc gpioreg1.c -o gpioreg1

コンパイルエラーがなければ、gpioreg1が生成されますので、sudo gpioreg1 で実行出来ると思います。回路が正しく接続されているとLEDの3秒間点灯が確認出来ると思います。

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>

//  レジスタの物理アドレス
#define GPIO_BASE     (0x20000000 + 0x200000)
#define BLOCK_SIZE    4096

//  GPIO 関連レジスタ
static volatile unsigned int *Gpio;

int main(int argc, char **argv)
{
        int pin = 21;//pin21を指定(※必要な場合は書き換えて下さい。)
        int mode = 1;//出力に設定

        int fd;
        void *gpio_map;

        //仮想メモリアドレスの取得
        fd = open("/dev/mem", O_RDWR | O_SYNC);
        if (fd == -1) {
                printf("/dev/mem オープンエラー sudoが必要\n");
                exit(-1);
        }

        gpio_map = mmap(NULL, BLOCK_SIZE,
                    PROT_READ | PROT_WRITE, MAP_SHARED,
                    fd, GPIO_BASE );

        if ((int) gpio_map == -1) {
                printf("/dev/mem マップエラー\n");
                exit(-1);
        }
        close(fd);

        // uint32 の配列としてレジスタへのアクセスが可能となる
        Gpio = (unsigned int *) gpio_map;

        // 21ピンを出力に設定する処理
        int index = pin / 10;
        unsigned int mask = ~(0x7 << ((pin % 10) * 3)); //maskの値を生成

        // GPFSEL0 - GPFSEL3 の該当する FSEL (3bit) のみを書き換えて21ピンを出力に設定する
        Gpio[index] = (Gpio[index] & mask) | ((mode & 0x7) << ((pin % 10) * 3));

        //LEDの処理
        // 3秒間点灯
        Gpio[7] = 0x1 << pin;   //  GPSET0
        usleep(3 * 1000 * 1000);

        //消灯
        Gpio[10] = 0x1 << pin;   //  GPCLR0

        return 0;
}

プログラムの説明です。

#define GPIO_BASE でARMから見た既定のアドレスを定義しています。mmap()システムコールで仮想アドレスに変換してアドレス空間を取得します。

取得したアドレス空間を32ビット単位の配列として扱い、目的のGPFSEL0 から GPFSEL3の位置で21ピンを出力に設定して、GPSET0の目的の位置をセットしてLED点灯、3秒後に、GPCLR0の目的の位置をセットしてLED消灯を実行しています。値の設定には論理演算でビット操作を行っています。

以上がこのプログラムの説明です。このプログラムでは簡単な操作のみですが今回のレジスタを操作する方法ではデータシートを見ながら必要なビット操作を行っていくと流れになると思います。より詳細は今回参考させて頂いた以下のサイトや書籍等を参照して頂ければと思います。

こじ研(宮城大学 小嶋研究室様) Raspberry Pi 応用編

組み込みLinux開発の視点からの補足
今回はレジスタを直接操作する方法で書きましたがこれはやはり組み込みLinux的な視点になっていると思います。C言語を使ったことがあっても組み込み関連は経験がないといった場合は、馴染みのない用語が出てきたかもしれないです。(※私も特に組み込み専門の開発エンジニアではありませんが)そういった意味で組み込みLinux開発という視点で補足を書きます。

CPUとSoC
CPU(Central Processing Unit)は中央処理装置や中央演算処理装置と日本語で表現されると思いますが、まさに算術演算(計算)を行うための装置ということであり、他のチップ(周辺回路)と共にコンピュータとして機能します。パソコンではCPUがあって、マザーボードがあって記憶装置があってという構成が一般的です。

一方、SoC(System on a Chip)というと、CPUと共に必要な周辺機能(ペリフェラル)がシステムとして1つのチップにまとまっています。強引な表現かもしれないですが、パソコンのCPUとマザーボードを一つにまとめて限りなく小さくしたものと表現出来るかもしれないです。

Raspberry Piの場合は小型なのでもちろん SoCとして実装されていますが、SoCと表現する場合は、Broadcom BCM2835であり、CPUとしては、ARM11 ファミリ(700 MHz)です。(※詳細はデータシート等を参照して下さい。今現在以下のPDFファイルにあります。)
BCM2835 ARM Peripherals

ARMファミリのCPUは組み込み系、スマホ系では今現在で圧倒的なシェアでこの分野でデファクトスタンダードになっていると思います。日本にはもちろんルネサス エレクトロニクスがあります。

レジスタ操作での制御
レジスタもCPUとSoCで考えると概念が変わってくると思います。一般的な情報処理の講義的な内容では、CPUでのレジスタというとCPU内の記憶装置でありプログラム実行の手順でその役割が説明されることが多いと思います。組み込み以外のプログラミングでは特に意識することはないでしょう。

組み込みの場合は、「メモリ・マップド・レジスタ」(※ここからは単にレジスタと書きます。)という表現になり、プログラマは番地を指定することでメモリと同じようにアクセスできます。このレジスタを操作(ビットでのON/OFF)して周辺機器を制御します。組み込みの場合は、SoCとなると特にですが、周辺機器が多くなり、それらを制御するためのレジスタの領域が大きくなります。CPU、SoCごとに仕様が決まっており、それを理解して、レジスタとの値の読み書きがプログラミングの中心になってくると思います。(今回のRaspberry Piでは、GPIOの操作に必要なアドレスにアクセスしています。但し、Linux上なので、物理メモリと仮想メモリの変換があります。)

Linuxとベアメタル
組み込みではOSを使う場合もあれば、使わない場合もあります。OSを使わない場合は私の場合は「スタンドアロン」という表現をよく聞いた記憶があるのですが、「ベアメタル」なる表現があることを最近知りました。Linuxを使えば、今回のようにデバイスドライバやユーザ管理、メモリ管理等でLinux配下での実装が基本になります。一方「ベアメタル」の考えではハードウェアに直接アクセスすることになり、プロセッサの性能を追求することも出来ます。もちろん、どちらが正しいとかいうことではなく、Raspbery Piではベアメタルでもプログラミングが出来るということです。より詳細は以下等を参照して下さい。
ベアメタルで遊ぶRaspberry Pi

今回はここまでです。今後も関連する内容について出来る範囲で書いていきたいと思います。

 - Raspberry Pi

AdSense

AdSense

  関連記事

Raspberry PiでのNode.jsの導入(その1)インストールと動作確認

Node.jsとは、Node.js 日本ユーザグループのサイトから引用させて頂き …

Raspberry PiでAC100V(ソリッド・ステート・リレー)制御

Raspberry PiでAC100VのON/OFF制御を試してみました。今回そ …

Raspberry PiでIoT(MQTTで遠隔操作編 その3)Raspberry PiのGPIOとHeroku連動Sub編

Raspberry PiでIoT MQTTで遠隔操作編の3回目です。2台のRas …

Raspberry Piでも温度センサADT7310をつかってみる

以下の記事でArduino Unoで温度センサのADT7310を使ってみました。 …

Raspberry PiでIoT(MQTTで遠隔操作編 その4)Raspberry PiのGPIOとHeroku連動Pub編

Raspberry PiでIoT MQTTで遠隔操作編の4回目です。2台のRas …

Raspberry PiでIoT(温度・湿度・気圧データ編 その1)BME280でデータ取得

Raspberry PiでIoTに取り組んでみるという内容で実際に動作させながら …

Raspberry PiでIoT(MQTTで遠隔操作編 その2)MQTT Brokerの構築とPythonでのpub/sub

Raspberry PiでIoT MQTTで遠隔操作編の2回目です。2台のRas …

温度センサADT7410(その3)Raspbery Piでの利用

温度センサADT7410の3回目です。前回までの内容は以下です。 温度センサAD …

Raspberry Piでタッチアプリ開発(その1)ハードウェア構成・開発環境等

Raspberry Piで簡単なタッチアプリを開発する手順について書きます。もち …

Raspberry PiでIoT(温度・湿度・気圧データ編 その3)データ表示とグラフ表示

Raspberry PiでIoT 温度・湿度・気圧データ編の3回目です。前回まで …