LECTURE 5 A/D 変換で加速度センサ値を読む

○ はじめに

前回 は、ATmega8535 の A/D 変換機能の使い方を説明しました。せっかくなので今回はその A/D 変換機能を利用して、3 軸加速度センサの出力を読んでみます。

○ 加速度センサについて

加速度センサとは、その名前の通り加速度を検出するためのセンサです。1 軸〜 3 軸対応のセンサが有り、それぞれの軸に対して働く加速度に応じた電圧を出力してくれます。センサを傾けると各軸に働く重力加速度の大きさも変化するため、姿勢変化の検出にも使えます。

本実験回路では、KXM52-1050 というチップ型 3 軸加速度センサを使いやすい様に基板形式にまとめてくれている、秋月電子 の KXM52-1050 モジュールを使っています。800円くらいだった様な気が…

↓赤枠内が KXM52-1050 モジュールです。

KXM52-1050 モジュール

X 軸と Y 軸については上図に記載した矢印の方向になる様に取り付けました。Z 軸については、基板の裏(図で見えない方)から表(図に見える方)に貫く方向となっています。

データシートによると Z 軸は + が基板の裏から表の方向になっているはずなので、通常の 1G がかかっている状況では -1G となるはずなのですが…筆者の回路ではどうやっても通常状態で 1G、逆さまの時に -1G が出てしまいました。KXM52-1050 の端子をテスターで直接計測してもその様な電圧が出ていたため、接続ミスとか考え違いではないと思うのですが…というわけですので、方向の正誤はみなさん各自でご確認ください m(__)m

XY 軸について水平から下図の様に θ だけ傾けたとします。θ = 0°の時が水平位置とします。

XY軸の傾き

この場合、下向きの重力加速度を g とすると XY 軸にかかる重力加速度 g ' は g ' = g sinθ となります。θ を増減した場合の XY 軸の重力加速度 g ' の変化は下のグラフの様になります。横軸が θ(単位は度)を表し、縦軸が g '/g を表しています。

XY軸の重力加速度の変化

今度は Z 軸について垂直から下図の様に θ だけ傾けたとします。θ = 0°の時を、基板の表側を上に向けた位置とします(先ほどの写真の状態。KXM52-1050 モジュールではこの場合、Z 軸の方向が下図と逆になるはずですが、説明上この様に記述しています)。

Z軸の傾き

この場合、下向きの重力加速度を g とすると Z 軸にかかる重力加速度 g ' は g ' = g cosθ となります。θ を増減した場合の Z 軸の重力加速度 g ' の変化は下のグラフの様になります。横軸が θ(単位は度)を表し、縦軸が g '/g を表しています。

Z軸の重力加速度の変化

KXM52-1050 モジュールでは Z 軸は基板の裏から表の方向(上図だと下から上)が正のため、実際には上図の θ = 0°の時に g ' = -g、θ = 180°の時に g ' = g となるはずなのですが、前述の通り筆者の回路では逆になってしまいました…方向の正誤については、みなさん各自でご確認ください m(__)m

○ 回路図

LECTURE 1 ATmega8535 および使用環境について に掲載してある全体回路図に対して、ATMega8535 の PA1 - PA3 および KXM52-1050 モジュール部分を赤枠で囲んだ図を こちら に置いておきます。

PORTA の PA1 - PA3 ピンに KXM52-1050 モジュールの Z/Y/X 出力をそれぞれ接続しています。これにより、PINA1/PINA2/PINA3 の値を A/D 変換器で読み込めば、Z/Y/X 軸に作用している重力加速度成分を得ることができます。

KXM52-1050 加速度センサは

0g(重力加速度0)の場合の出力電圧 = Vcc/2
1g の変化に対応する電圧感度 = Vcc/5

という特性を持っています。よって各軸の出力電圧は Vcc = 5V とすると

最小値     = Vcc/2 - Vcc/5 = 1.5V
オフセット = Vcc/2         = 2.5V
最大値     = Vcc/2 + Vcc/5 = 3.5V

となります。ATmega8535 の A/D 変換結果の最大値が 0x3ff であることから、それぞれの場合の A/D 変換結果の予測値は

最小値     = 0x3ff x 1.5V / 5V = 約 306.9(0x132)
オフセット = 0x3ff x 2.5V / 5V = 約 511.5(0x1ff)
最大値     = 0x3ff x 3.5V / 5V = 約 716.1(0x2cc)

と計算できます。実際にはセンサの誤差やノイズ等で値は異なるはずですが、目安にはなるでしょう。

○ サンプルプログラムと説明

A/D 変換を使って加速度センサ値を読むサンプルプログラムを 05acc.tar.gz からダウンロードできます。はじめに のページから、全てのサンプルプログラムを 固めた tar ファイルをダウンロードすることもできます。

中には

  • Makefile
  • adc.h
  • adc.c
  • util.h
  • util.c
  • usart.h
  • usart.c
  • main.c

が格納されています。main.c と Makefile 以外は LECTURE 4 A/D 変換でボリューム(可変抵抗)値を読む の内容と同じで Makefile もほとんど変わらないので、main.c について以下に載せます。なお、以下に掲載しているソースコードには修正 BSD ライセンス条項を含めていませんが、ソースコード配布は修正 BSD ライセンスに従います(はじめに のページの免責事項を参照してください)。

これまでにヒューズビットの設定を行ったことが一度も無い場合は、まず最初に make fuse を実行してください。fuse ターゲットについては、LECTURE 2 LED を点滅させる のサンプルプログラムの項を参照してください。

make 実行後、プログラムをアップロードする場合は make uisp を実行してください。

本サンプルプログラムは 500ms に 1 回の割合で、3 軸の加速度センサ出力値を A/D 変換機能で読み込んだ後に、値を "(X,Y,Z)=(...,...,...)" という形式の文字列に加工してシリアル通信で送信します。A/D 変換結果部分は 10進数表示です。文字列を受信するためには、実験用回路と PC のシリアルポート(D-SUB 9ピン)をストレートケーブルで接続し、Windows であればハイパーターミナル等の通信ソフトで以下のパラメータを設定してフロー制御無しで接続してください。

  • 非同期動作
  • ボーレート 9600 bps
  • パリティ無し
  • ストップビット 1ビット
  • 8 ビットデータ転送

"(X,Y,Z)=(...,...,...)" という形式の文字列が、ターミナルに連続的に表示されると思います。この間に加速度センサを載せた基板を傾けると、"(X,Y,Z)=(...,...,...)" の ... の部分に表示される数字が変化します。

↓通信ソフトにはこの様に表示されます。
加速度センサ値の読み込み結果表示画面

  • main.c
        1    #include <avr/io.h>
        2    #include <inttypes.h>
        3    
        4    #include "adc.h"
        5    #include "usart.h"
        6    #include "util.h"
        7    
        8    
        9    #define INDEX_TO_INSERT_X  9
       10    #define INDEX_TO_INSERT_Y  14
       11    #define INDEX_TO_INSERT_Z  19
       12    
       13    
       14    static uint16_t get_data(char ch);
       15    static void fill(char *buf, int index, int data);
       16    
       17    
       18    int
       19    main(void)
       20    {
       21            char buf[32] = "(X,Y,Z)=(    ,    ,    )\r\n";
       22    
       23            uint16_t x;
       24            uint16_t y;
       25            uint16_t z;
       26    
       27            pullup();
       28            /* PA1-3 プルアップ無効 */
       29            PORTA &= ~(_BV(PINA1) | _BV(PINA2) | _BV(PINA3));
       30    
       31            /* (8MHz/64) = 125KHz */
       32            adc_init(ADC_CK_64, ADC_NO_INT);
       33            usart_init(USART_NO_INT);
       34    
       35            while (1){
       36                    x = get_data(PINA3);
       37                    y = get_data(PINA2);
       38                    z = get_data(PINA1);
       39    
       40                    fill(buf, INDEX_TO_INSERT_X, x);
       41                    fill(buf, INDEX_TO_INSERT_Y, y);
       42                    fill(buf, INDEX_TO_INSERT_Z, z);
       43    
       44                    usart_tx_string(buf);
       45                    delay_500ms();
       46            }
       47    
       48            return 0; /* 警告を避けるため */
       49    }
       50    
       51    
       52    static uint16_t
       53    get_data(char ch)
       54    {
       55            adc_set_ch(ch);
       56            adc_start();
       57            adc_wait();
       58            return adc_get();
       59    }
       60    
       61    
       62    static void
       63    fill(char *buf, int index, int data)
       64    {
       65            char abuf[5];
       66            char *pos = abuf;
       67            char ch;
       68    
       69            itoa(abuf, data, 10, 4, '0');
       70    
       71            while ((ch = *(pos ++))) {
       72                    buf[index ++] = ch;
       73            }
       74    }
       75    
       76    /* End of main.c */
    
    
    • 18 〜 49 行目 (main())

      • pullup() (pullup.h、全入力ポートの内部プルアップを有効化)
      • PINA1 - PINA3 ピンは入力として使用するので、プルアップを無効化
      • adc_init() (adc.h、A/D 変換器初期化)
      • usart_init() (usart.h、USART 初期化)

      を行った後、while ループに入ります。ループ内ではまず

      x = get_data(PINA3);
      y = get_data(PINA2);
      z = get_data(PINA1);
      
      

      を実行して、加速度センサの X/Y/Z 出力の A/D 変換結果を取得します。 その後、後述の fill() によって USART 送信用の文字列を作成します。

      送信用の文字列を作成したら、usart_tx_string() によって USART 送信バッファに文字列を設定し、500ms 待って while() ループによる次の A/D 変換を行います。

    • 52 〜 59 行目 (get_data())

      以下の処理をまとめて行うための関数です。

      • 指定されたチャネルを adc_set_ch() でアナログ変換器に接続
      • adc_start() で A/D 変換開始
      • adc_wait() で A/D 変換終了待ち
      • adc_get() で A/D 変換結果を返す

    • 62 〜 74 行目 (fill())

      例によって文字列加工用の自作関数です。data で指定した数値を itoa()(util.c) で 4 文字幅の数字に変換し、"(X,Y,Z)=( , , )" の文字列バッファに 直接文字を埋め込みます。

以上で解説は終わりです。本プログラムでは加速度センサ出力値の A/D 変換結果をそのまま表示しましたが、値から何度傾いているのか?を計算して表示したり、ゲーム機のコントローラの様に使って PC 上のアプリケーションの表示を変えたり等いろいろな使い途があると思います。