Tanuki_Bayashin’s diary

電子工作を趣味としています。最近はラズベリーパイPicoというマイコンを使って楽しんでいます

傾斜計の試作機の製作 C言語バージョン

※なにか気になる点がありましたらコメント欄にご記入ください。また、工作や回路を製作する場合には、細かい作業などに対して、細心の注意を払われるようお願いいたします。

目次

1.はじめに

 以前書いた記事で(2022/05/19)、Raspberry Pi Pico(以下Pico と記述) を用いて、倒立振子を製作するのに欠かせない傾斜計の試作を行いました。そのときはMicro Python を使ってコードを記述しましたが、今回はC言語を用いてコードを書いています。

 ハードの部分は、以前の記事を参考にしてください。また、加速度センサー(MPU-6050)からのデータを読み込む部分のコードは、Pico-SDK のサンプルの中に偶然入っていたので、そのまま利用しました。(ラズベリーパイ財団の著作物みたいなので、ここでは公表は控えました。悪しからず)

 前回の記事では(2022/06/22)、同一環境でPico からUART により、PCに数値のデータを送信する手法を示すところまでできているので、今回の記事では、加速度センサーからデータを読み込む処理を付け加えた形となっています。
※今回は新たに、小数点以下の数値も表示できるようにした関数も用意しました。

以前書いた記事:
傾斜計の試作機の製作 - Tanuki_Bayashin’s diary

前回書いた記事:
ラズパイPico によるUART接続~Pico-SDK C/C++開発環境にて - Tanuki_Bayashin’s diary

ご参考:
pico-sdk C/C++ での開発の進め方 - Tanuki_Bayashin’s diary

図1 全体構成図

主な仕様

出典:トランジスタ技術 2019年7月号 第1章第2節(P.39~)のあたり(再掲)

※ここでは自分なりに考えた処理を紹介していますが、後日、以下のやり方を他の方のブログにて見つけました。こちらの方がPC側に送信した後でもデータの扱いがしやすいことから、コードに書くには適していると思われます。(2022/08/11追記)

        snprintf(s, sizeof(s), "%f", angle);
        uart_puts(UART_ID, s);

解説:1行目により、angleというfloatの変数をsという文字列に変換したのち、uart_puts()にてデータを送信しています。

2.傾斜計のソフトウェア

 コードをリスト1に示します。
 今回は加速度センサーからのデータはUARTによりPCに送り、Terataermにより表示しています。
 加速度センサーの初期化と値を読み取る関数は、Pico-SDKのサンプル・プログラムを引用しました。(省略してありますが)
 UARTに関する処理は、同様にPico-SDKのサンプル・プログラムを引用し、また独自に自作したものもあります。(1章 前回書いた記事参照)

※何の気なしにコードを載せておりますが、安全性への確固たる責任は保証できるものではありません。恥ずかしい話ですが、危険性が少しでもはらむと判断される用途には、利用されないことを強くお願い申し上げます。

リスト1 mpu6050_uart_2nd.c

/**
 * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

#include <stdio.h>
#include <string.h>
#include <math.h>
#include "pico/stdlib.h"
#include "hardware/uart.h"
#include "pico/binary_info.h"
#include "hardware/i2c.h"

#define UART_ID uart1
#define BAUD_RATE 2400
#define UART_TX_PIN 8
#define UART_RX_PIN 9
#define PI 3.1415926535898

// By default these devices  are on bus address 0x68
static int addr = 0x68;

#ifdef i2c_default
static void mpu6050_reset() {
//  省略(mpu6050を初期化します)

}

static void mpu6050_read_raw(int16_t accel[3], int16_t gyro[3], int16_t* temp) {
//  省略(mpu6050からの生データを読み取ります)

}
#endif

void uart_integer_to_string(int16_t a) {  // 数値を文字に変換する関数です

    if (a == 0) {
        uart_putc(UART_ID, '0');
    }
    else if (a == 1) {
        uart_putc(UART_ID, '1');
    }
    else if (a == 2) {
        uart_putc(UART_ID, '2');
    }
    else if (a == 3) {
        uart_putc(UART_ID, '3');
    }
    else if (a == 4) {
        uart_putc(UART_ID, '4');
    }
    else if (a == 5) {
        uart_putc(UART_ID, '5');
    }
    else if (a == 6) {
        uart_putc(UART_ID, '6');
    }
    else if (a == 7) {
        uart_putc(UART_ID, '7');
    }
    else if (a == 8) {
        uart_putc(UART_ID, '8');
    }
    else if (a == 9) {
        uart_putc(UART_ID, '9');
    }
    else {
        uart_putc(UART_ID, 'X');
    }

}

void sendDataByUART(int16_t x) {  //  int16_t の型の変数をUARTにて送信します
    int16_t digit, a;

    if (x < 0) {
        x = -1 * x;
        uart_putc(UART_ID, '-');
    }
    
    digit = floor(log10((double)x));
    for (int j = digit; j > 0; j--) {
        a = floor(x / pow(10.0, j));
        x = x - a * pow(10.0, j);

        uart_integer_to_string(a);
    }
    uart_integer_to_string(x);

    uart_puts(UART_ID, ", ");
    sleep_ms(50);

}

void sendData_double(double x) {  //  倍精度実数の変数をUARTにて送信します
    int16_t digit, a;

    if (x < 0) {
        x = -1 * x;
        uart_putc(UART_ID, '-');
    }

    digit = floor(log10(x));

//  数値が0で始まる場合の処理です
    if (digit < 0) {
        uart_puts(UART_ID, "0.");
        for (int i = -1; i > digit; i--) {
            uart_puts(UART_ID, "0");
        }

    }

//  小数第4位まで表示します
    for (int j = digit; j >= -4; j--) {
        a = floor(x / pow(10.0, j));
        x = x - a * pow(10.0, j);

        uart_integer_to_string(a);

        if (j == 0) {
            uart_puts(UART_ID, ".");
        }
    }

    uart_puts(UART_ID, ", ");
    sleep_ms(50);

}

int main() {

    // Set up our UART with the required speed.
    uart_init(UART_ID, BAUD_RATE);
    //  省略(UARTの機能を初期化しています)

#if !defined(i2c_default) || !defined(PICO_DEFAULT_I2C_SDA_PIN) || //以下省略
    //  省略(mpu6050を使えるようにします)

    int16_t acceleration[3], gyro[3];
    int16_t x;
    int16_t* temp;

    while (1) {

        mpu6050_read_raw(acceleration, gyro, temp);

        uart_puts(UART_ID, "Accel:");
        for (int16_t i = 0; i < 3; i++) {
            x = acceleration[i];
            sendDataByUART(x);
        }

        double angle;
        angle = atan2((double)acceleration[1], (double)acceleration[2])*180.0/PI;
        sendData_double(double x) 

        uart_puts(UART_ID, "\r\n");

        sleep_ms(10);
    }
#endif
    return 0;
}

解説
 7 ~ 13行目: 必要なヘッダファイルを取り込んでいます。
15~19行目: UART(シリアル通信)に関するマクロを設定しています。
      (uart1を選択,,ボーレート2400、TX=GPIO8(11番ピン)、RX=GPIO9(12番ピン))
21~22行目: 加速度センサーMPU6050のI2Cアドレスを指定しています。

24~34行目 : mpu6050に関する関数の定義です。

36~72行目 : int16_t の型を持つ数値に応じて、対応する文字をUARTにより送信しています。
74~94行目 : int16_t型の数値xをPC側に送信する関数です。(UART)
96~130行目 : 倍精度実数型の数値を送信する関数です。

132~162行目 : main() 関数です。
134~136行目 : UARTを初期化します。
138,139行目  : 加速度センサーMPU6050を動作可能としているようです。
141~143行目 : 変数の型を指定します。

145~162行目 : 無限ループの部分です。
147行目   : 加速度センサーMPU6050からのデータを読み取っています。
149~153行目 : 加速度のデータをPCに送信しています。
155~157行目 : 傾きの角度を計算し、データをPC側に送信します。

159行目 : 改行と、文字位置の復帰のエスケープ・シーケンスです。
161行目 : 10ms、待機しています。
164行目 : main( ) 関数の戻り値 0 を返しています

解説は以上です。

3.今回のコードをビルドするときに使用したCMakeLists.txt

 以下の通りです。(ディレクトリ構造については1章にある「ご参考」の記事を参照してください)

スト2 ディレクトリpico/works の中に保存してあるCMakeLists.txt

cmake_minimum_required(VERSION 3.16)

# Pull in SDK (must be before project)
include(pico_sdk_import.cmake)

project(pico_examples C CXX ASM)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)

set(PICO_EXAMPLES_PATH ${PROJECT_SOURCE_DIR})

# Initialize the SDK
pico_sdk_init()

include(example_auto_set_url.cmake)
# Add blink example
add_subdirectory(mpu6050_uart_2nd)

リスト3 pico/works/mpu6050_uart_2nd の中に保存しているCMakeLists.txt

add_executable(mpu6050_uart_2nd
        mpu6050_uart_2nd.c
        )

# Pull in our (to be renamed) simple get you started dependencies
target_link_libraries(mpu6050_uart_2nd pico_stdlib hardware_i2c)

# create map/bin/hex file etc.
pico_add_extra_outputs(mpu6050_uart_2nd)

# add url via pico_set_program_url
example_auto_set_url(mpu6050_uart_2nd)

※ライブラリに関する記述(6行目)では、2章のコード内でインクルードされているヘッダファイルで、ダブルコーテーションマークで囲まれているもののうち、一番上と一番下を記述すればよいようです。

4.傾斜角度の表示(動画)

 加速度センサーからのデータを表示している場面の動画です。

www.youtube.com

5.まとめ

 ラズベリーパイPicoを用い、Pico-SDK環境にて、加速度センサーの値を読み取り、その値をPC側に送り、Terataermに表示しました。

 今後は実際に傾いている角度と、計測したデータを照らし合わせ、どれくらいズレがあるのか調べていきたいです。
 また、MicroPythonとC言語のそれぞれのバージョンで、処理速度の比較にも取り組みたいと思います。