nRF : MPU6050 DMP pedometer
MUP6050:三軸加速度センサ・三軸ジャイロセンサモジュール
Amazon等で安価に購入できるため、慣性計測装置(IMU:Inertial Measurement Unit)としてはよく使われ情報を多く目にする。ただ、マイコンからの命令で、ジャイロの値を読んだり、加速度センサの値を読み込んだりするのは難易度的に低く、必要なレジスタへの書き込み、読み込みさえできれば値を得る事は難しくない。
一方で、MUP6050の本当の真価を得るためには、DMP(Digital Motion Processor)を使わないとその価値は得られない。DMPは、内部処理機能に必要なプログラム(これはTDKが用意していてバイナリーで配布されている)を送り込みする事で動作させる事ができる。その間マイコンによる監視が不要にできる
しかし以外と情報は少ない。ArduinoでDMP対応のライブラリーが提供されているので参考にはなるが、メモリの都合上、UNO,NANOではそのままでは試す事できない事もあり、情報が少ない。特にnRF系でというと情報がそこそこ少ないので、ここでは、NordicのnRFに移植し、万歩計を実装する手順を記載する。
1.必要ファイルの準備
MUP6050の公式サイト
https://invensense.tdk.com/developers/software-downloads/#smartmotion
MPU eMD6.12をクリック
(アカウントがない場合は、アカウントの作成をする)
zipファイルのダウンロードが終わったら適当に解凍する。
エクスプローラーから
\motion_driver_6.12\arm\STM32F4_MD6\Projects\eMD6\core\driver\eMPL
を開いておく。元にするファイルは、
を開いておく。元にするファイルは、
に記載されているように、I2C関係、時間待ち、時間計測、ログの出力に関する関数を使うプラットフォームにあわせて用意してやる。例題として、TI社のMSP430やSTM社のSTMF4が収録されているので、それを参考にしながら、NordicのnRFでも動作をするようするというのが実施する方向性。
nRF52832向けはここから。nRF52840では試していないがそのままでも動くとは思う。
2.基本の書き込み、読み込み関数の作成
I2Cでの通信になるので、
#include "nrf_drv_twi.h"
がインクルードされていて、mainの外で基本的なI2Cの初期化をする。(テンプレートには記載済み)
/* ****************/
/* TWI
/* ****************/
/* TWI instance ID. */
#if TWI0_ENABLED
#define TWI_INSTANCE_ID 0
#elif TWI1_ENABLED
#define TWI_INSTANCE_ID 1
#endif
/* TWI instance. */
const nrf_drv_twi_t m_twi = NRF_DRV_TWI_INSTANCE(0);
void twi_init(void)
{
const nrf_drv_twi_config_t twi_config = {
.scl = ARDUINO_SCL_PIN,
.sda = ARDUINO_SDA_PIN,
.frequency = NRF_DRV_TWI_FREQ_400K,
.interrupt_priority = APP_IRQ_PRIORITY_HIGH,
.clear_bus_init = false
};
nrf_drv_twi_init(&m_twi, &twi_config, NULL, NULL);
nrf_drv_twi_enable(&m_twi);
}
components\drivers_ext\の下に、mpu6050dmpというフォルダを作る。
そこに、先ほどTDKのホームページからダウンロードした6つのファイルをコピー
さらに、このフォルダで右クリックをして新規作成ーテキストドキュメント選択し、拡張子も変更してしまう形で、mpu6050dmp.cとmpu6050dmp.hを作る。
SEGGER Embedded Studioでプロジェクトを右クリックし、mpu6050という名で新しいフォルダを作る。
まず、3行の記載をする。
#include <string.h>
#include "nrf_drv_twi.h"
extern const nrf_drv_twi_t m_twi;
後で、memcpyを使うので、#include <string.h>をインクルードしておき、I2CのnRFライブラリーもインクルードしておく。extern const nrf_drv_twi_t m_twi;とI2Cのインスタンスの部分は、mainファイルで他のI2C機器と共有する事を前提に外部参照する形とした。
本題の読み書きの関数を記述する。ラッピングする事を前提に引数の個数と順を
この関数は、引数をinv_mpu.cに記載の
* i2c_write(unsigned char slave_addr, unsigned char reg_addr,
* unsigned char length, unsigned char const *data)
* i2c_read(unsigned char slave_addr, unsigned char reg_addr,
* unsigned char length, unsigned char *data)
内容にあわせる。基本的な動作については前回確認はしている。
int drv_mpu6050_write(unsigned char slave_addr, unsigned char reg_addr, unsigned char length, unsigned char const *data)
{
uint32_t err_code;
uint8_t buffer[length+1];
buffer[0] = reg_addr;
memcpy(&buffer[1], data, length);
err_code = nrf_drv_twi_tx( &m_twi,
slave_addr,
buffer,
length + 1,
false);
APP_ERROR_CHECK(err_code);
return 0;
}
int drv_mpu6050_read(unsigned char slave_addr, unsigned char reg_addr, unsigned char length, unsigned char *data)
{
uint32_t err_code;
err_code = nrf_drv_twi_tx( &m_twi,
slave_addr,
®_addr,
1,
true );
APP_ERROR_CHECK(err_code);
err_code = nrf_drv_twi_rx( &m_twi,
slave_addr,
data,
length );
APP_ERROR_CHECK(err_code);
return 0;
}
基本的な、nrf_drv_twi_tx, nrf_drv_twi_rxの使い方なので詳細は割愛。ポイントは、nrf_drv_twi_txの引数がfalseか、trueか。no stopなのでtrueの時止めずに次の転送ができる。設定された場合、転送が正常に完了した後、バス上で停止条件が生成されない(次の転送で繰り返し開始することができる)。
mpu6050dmp.h側には、簡単に2行、関数定義を記載。
int drv_mpu6050_write(unsigned char slave_addr, unsigned char reg_addr, unsigned char length, unsigned char const *data);
int drv_mpu6050_read(unsigned char slave_addr, unsigned char reg_addr, unsigned char length, unsigned char *data);
3.基本の書き込み、読み込み関数の動作確認
mpu6050dmp.hを見つけられるように、プロジェクトを右クリックしOption Commonを選択、Preprocessor、User include directories を選択し、drivers_extの下に作ったmpu6050dmpのフォルダを追加する。
#include "mpu6050dmp.h"
を追加する。
続いて、whileの外の方で、
uint8_t *mpu6050_ID;
drv_mpu6050_read(0x68, 0x75, 1, mpu6050_ID);
NRF_LOG_INFO("ID=0x%x",mpu6050_ID);
NRF_LOG_FLUSH();
を記載して実行してやる。ビルト、エラーがあれば修正してエラーフリーになったら書き込み動作を確認してみる。
続いて、drv_mpu6050_writeの方をチェックする。
レジスタの0x6Bを一度読んで、書き換え、再度読み込み値が書き込んだ値になっているかを確認する。
whileの外の方、先ほどのdrv_mpu6050_readのテストの記載の下に、
uint8_t TEMP_DIS;
TEMP_DIS=1;
drv_mpu6050_write(0x68, 0x6B, 1, &TEMP_DIS);
uint8_t PWR_MGMT_1;
drv_mpu6050_read(0x68, 0x6B, 1, &PWR_MGMT_1);
NRF_LOG_INFO("PWR_MGMT_1=0x%02x",PWR_MGMT_1);
NRF_LOG_FLUSH();
TEMP_DIS=9;
drv_mpu6050_write(0x68, 0x6B, 1, &TEMP_DIS);
drv_mpu6050_read(0x68, 0x6B, 1, &PWR_MGMT_1);
NRF_LOG_INFO("PWR_MGMT_1=0x%02x",PWR_MGMT_1);
NRF_LOG_FLUSH();
これで実行してみる。
もう一つ関数を用意する。時間を計測する関数。
mpu6050dmp.c
に、
上の方に
//To use application timer
#include "app_timer.h"
#include "nrf_drv_clock.h"
とタイマー関係のヘッダーをインクルードしておき、末尾に、関数を追記する
int drv_mpu6050_get_clock_ms(unsigned long *count)
{
//*count = millis();
*count = app_timer_cnt_get()/32.768;
return 0;
}
また、mpu6050dmp.hにも関数定義を1行ついかしておく。
int drv_mpu6050_write(unsigned char slave_addr, unsigned char reg_addr, unsigned char length, unsigned char const *data);
int drv_mpu6050_read(unsigned char slave_addr, unsigned char reg_addr, unsigned char length, unsigned char *data);
int drv_mpu6050_get_clock_ms(unsigned long *count);
4.inv_mpu.c、inv_mpu_dmp_motion_driver.c編集
大まかには使う関数が準備できたので、inv_mpu内でラッピング作業をする。
作成したmpu6050のフォルダを右クリック、Add Existing File...を選択する。ダイヤログが開くのでcomponents\drivers_ext\の下に作成したmpu6050dmpにコピーした、inv_mpu.cとinv_mpu_dmp_motion_driver.cを追加する。対応する.hはすでに対処済みなのでここでは
の2行を付け足す。プラットフォーム(マイコン)をどれにするか?という事でSTM32F4やMSP430で選択できるようになっていて、EMPL_TARGET_MSP430をdefineすればMSP430向けにif文で分岐されるようになっている。今回は、新たにEMPL_TARGET_NRF52を付け足す。MPU6050も姉妹品のMPU9150なのか? MPU6050なのか?また磁気センサのAK8975を併用するのか?というのをこの #defineでスイッチするように記載されているので、今回MPU6050を単独で使う事を想定して、この1行を記載する。
120行あたりで
#else
#error Gyro driver is missing the system layer implementations.
#endif
と記述がある直上に、
#elif defined EMPL_TARGET_NRF52
#include "nrf_delay.h"
#include "mpu6050dmp.h"
#define i2c_write drv_mpu6050_write
#define i2c_read drv_mpu6050_read
#define delay_ms nrf_delay_ms
#define get_ms drv_mpu6050_get_clock_ms
#define log_i MPL_LOGI
#define log_e MPL_LOGE
#define min(a,b) ((a<b)?a:b)
を記載する。
同じ要領で、inv_mpu_dmp_motion_driver.c側も編集をする。スクショだけ残しておきます。同じ。
inv_mpu.cファイルに定義されている関数で
int mpu_init(struct int_param_s *int_param)
をmpu_initの文字列を検索して探す。編集前をコメントアウトして
引数の中が空(void)の関数に変更しておく。
//int mpu_init(struct int_param_s *int_param)
int mpu_init(void)
また、この関数の中で、
#ifndef EMPL_TARGET_STM32F4
if (int_param)
reg_int_cb(int_param);
#endif
EMPL_TARGET_STM32F4がターゲットでなければ実行するように記載されいるこの4行をコメントアウトする。int_paramという引数、あまり意味がないのでなくて大丈夫。
この編集に合わせて、inv_mpu.hのファイルも編集をする。
これらの編集で、inv_mpu.cとinv_mpu_dmp_motion_driver.cの関数がnRF52で呼び出す事ができるはずである。
テストをしていく。
whileの上の部分で、
int res;
res= mpu_init();
NRF_LOG_INFO("mpu_init=%d",res);
NRF_LOG_FLUSH();
とmpu_init()が完了するかを見てみる。
調子に乗って、いくつかの設定と、肝心なDMPのファームを流し込む命令、そしてDMPを有効にする命令なんかを送ってやる。
res= mpu_init();
NRF_LOG_INFO("mpu_init=%d",res);
NRF_LOG_FLUSH();
res=mpu_set_sensors(INV_XYZ_GYRO|INV_XYZ_ACCEL);
NRF_LOG_INFO("mpu_set_sensors=%d",res);
NRF_LOG_FLUSH();
res=mpu_configure_fifo(INV_XYZ_GYRO | INV_XYZ_ACCEL);
NRF_LOG_INFO("mpu_configure_fifo=%d",res);
NRF_LOG_FLUSH();
res=mpu_set_sample_rate(50);
NRF_LOG_INFO("mpu_set_sample_rate=%d",res);
NRF_LOG_FLUSH();
res=dmp_load_motion_driver_firmware();
NRF_LOG_INFO("dmp_load_motion_driver_firmware=%d",res);
NRF_LOG_FLUSH();
res=dmp_set_fifo_rate(50);
NRF_LOG_INFO("dmp_set_fifo_rate=%d",res);
NRF_LOG_FLUSH();
res=mpu_set_dmp_state(1);
NRF_LOG_INFO("mpu_set_dmp_state=%d",res);
NRF_LOG_FLUSH();
ここで、順番にmpu_init();で初期化して、その次に使うセンサを指定して、FIFOの設定、サンプリングレートを設定して、ファームを送り込んで・・・とやっているが、これは参考にしたコードがある。
nrf inv_mpu.cというようなキーワードで検索すると、10件(2023年5月時点)程度しか検索にかからない。
その多くが、中国のサイト。中国の方は、正点原子 http://www.alientek.com/ という会社が出しているMPU6050のブレイクアウトボードを多く利用するようで、それをターゲットとしている記事が多い。正点原子さんは、TDKのオフィシャルなinv_mpu.cに独自の部分を末尾に付け足しているものを提供しているらしく、多くの中国の方の記事は、それを元にNordicのnRFへのinv_mpuの移植方法を解説してくれいる。
ただ、多くのサイトが、csdnなので詳細を知る事ができない。しかも、正点原子さんのWEBサイトを訪問してドライバーとしてDLできる?と思いきやできなかった・・・。
githubを放浪していると
等で正点原子さんが追記をした inv_mpu.cを見る事ができる。追記している関数等を知ることができる。
ここでは万歩計が作りたいという事でシンプルにしているが、角度(ヨー、ピッチ、ロール)、加速度を得たい場合は参考にするとよい。
(ここでは示さないが、ピッチ、ロール、ヨーをFIFO経由でデータを得る事はできいた)
//mpu_dmp_get_data(&pitch,&roll,&yaw);
// NRF_LOG_RAW_INFO("pitch: " NRF_LOG_FLOAT_MARKER "; ", pitch);
// NRF_LOG_RAW_INFO("roll: " NRF_LOG_FLOAT_MARKER "; ", roll);
// NRF_LOG_RAW_INFO("yaw: " NRF_LOG_FLOAT_MARKER "\r\n", yaw);
// NRF_LOG_FLUSH();
5.万歩計:Pedometer
今回の目的の万歩計の実装を進める。
main関数の外、すぐ上に、2つ関数を用意する。
unsigned long dmpGetPedometerSteps(void)
{
unsigned long steps;
if (dmp_get_pedometer_step_count(&steps) == 0)
{
return steps;
}
return 0;
}
int dmpSetPedometerSteps(unsigned long steps)
{
return dmp_set_pedometer_step_count(steps);
}
その後、
mainの中で、whileの外の部分で、
//PEDOMeter setup
unsigned long stepCount = 0;
res=dmpSetPedometerSteps(stepCount);
NRF_LOG_INFO("dmpSetPedometerSteps=%d",res);
NRF_LOG_FLUSH();
変数の定義と、セットアップをしてやる。
whileの中でテストとして、1秒おきに現在の歩数を表示してやる。
stepCount = dmpGetPedometerSteps();
NRF_LOG_INFO("Walked %d steps",stepCount) ;
NRF_LOG_FLUSH();
nrf_delay_ms(1000);手にもってゆりかごをふるようにしてやると、歩数のカウントががっていく。どうも単なる振動を与えるだけではカウントは増えない模様。歩るいていると判断するアルゴリズムが入っているのか8回ぐらいはカウントがあがらないので辛抱良く振る!と数字がゼロから8となってその後は数字が次々をあがり、無事にDMPを使って万歩計ができる見通しがついた。
→歩数とみなす感度、閾値の調整はどうするんだろう・・・わからない・・・。
TDKさんが出しているバイナリーをリバースしている方がいるのでそれを見て変更するような事をするんだろうか・・・・
/**
* Copyright (c) 2009 - 2021, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/** @file
* @brief Example template project.
* @defgroup nrf_templates_example Example Template
*
*/
#include <stdbool.h>
#include <stdint.h>
#include "nrf.h"
#include "nordic_common.h"
#include "boards.h"
// for using nrf_log
#include "nrf_log.h"
#include "nrf_log_ctrl.h"
#include "nrf_log_default_backends.h"
// for using nrf_delay
#include "nrf_delay.h"
// for using I2C
#include "nrf_drv_twi.h"
// for using OLED display with SSD1306
#include "ssd1306.h"
#include "fun_logo.h"
//To use application timer
#include "app_timer.h"
#include "nrf_drv_clock.h"
#include "mpu6050dmp.h"
#include "inv_mpu.h"
#include "inv_mpu_dmp_motion_driver.h"
/* ****************/
/* TWI
/* ****************/
/* TWI instance ID. */
#if TWI0_ENABLED
#define TWI_INSTANCE_ID 0
#elif TWI1_ENABLED
#define TWI_INSTANCE_ID 1
#endif
/* TWI instance. */
const nrf_drv_twi_t m_twi = NRF_DRV_TWI_INSTANCE(0);
void twi_init(void)
{
const nrf_drv_twi_config_t twi_config = {
.scl = ARDUINO_SCL_PIN,
.sda = ARDUINO_SDA_PIN,
.frequency = NRF_DRV_TWI_FREQ_400K,
.interrupt_priority = APP_IRQ_PRIORITY_HIGH,
.clear_bus_init = false
};
nrf_drv_twi_init(&m_twi, &twi_config, NULL, NULL);
nrf_drv_twi_enable(&m_twi);
}
/* ****************/
/* APP TIMER
/* ****************/
#define LED_INTERVAL1 APP_TIMER_TICKS(100)
#define LED_INTERVAL2 APP_TIMER_TICKS(200)
#define LED1_PIN 17
#define LED2_PIN 18
APP_TIMER_DEF(timer_id1);
APP_TIMER_DEF(timer_id2);
static void lfclk_config(void)
{
ret_code_t err_code =nrf_drv_clock_init();
APP_ERROR_CHECK(err_code);
nrf_drv_clock_lfclk_request(NULL);
}
static void app_timer_handler1(void * p_context)
{
nrf_gpio_pin_toggle(LED1_PIN);
}
static void app_timer_handler2(void * p_context)
{
nrf_gpio_pin_toggle(LED2_PIN);
}
static void app_timers_init(void)
{
ret_code_t err_code;
err_code=app_timer_init();
APP_ERROR_CHECK(err_code);
err_code=app_timer_create(&timer_id1, APP_TIMER_MODE_REPEATED, app_timer_handler1);
APP_ERROR_CHECK(err_code);
err_code=app_timer_create(&timer_id2, APP_TIMER_MODE_REPEATED, app_timer_handler2);
APP_ERROR_CHECK(err_code);
}
unsigned long dmpGetPedometerSteps(void)
{
unsigned long steps;
if (dmp_get_pedometer_step_count(&steps) == 0)
{
return steps;
}
return 0;
}
int dmpSetPedometerSteps(unsigned long steps)
{
return dmp_set_pedometer_step_count(steps);
}
/**
* @brief Function for application main entry.
*/
int main(void)
{
//Initialization of TWI
twi_init();
//configration GPIP pin
nrf_gpio_cfg_output(LED1_PIN);
nrf_gpio_cfg_output(LED2_PIN);
//Initilization of APP_timer
lfclk_config();
app_timers_init();
//timer start
uint32_t err_code;
err_code =app_timer_start(timer_id1, LED_INTERVAL1,NULL);
APP_ERROR_CHECK(err_code);
err_code =app_timer_start(timer_id2, LED_INTERVAL2,NULL);
APP_ERROR_CHECK(err_code);
//Initialization of SSD1306
SSD1306_Init();
SSD1306_DrawBitmap(0,0,fun_logo_mono128x64, 128, 64, 1);
SSD1306_UpdateScreen();
nrf_delay_ms(1000);
//Initialization of NRF_LOG
APP_ERROR_CHECK(NRF_LOG_INIT(NULL));
NRF_LOG_DEFAULT_BACKENDS_INIT();
uint8_t mpu6050_ID;
drv_mpu6050_read(0x68, 0x75, 1, &mpu6050_ID);
NRF_LOG_INFO("ID=0x%x",mpu6050_ID);
NRF_LOG_FLUSH();
uint8_t TEMP_DIS;
TEMP_DIS=1;
drv_mpu6050_write(0x68, 0x6B, 1, &TEMP_DIS);
uint8_t PWR_MGMT_1;
drv_mpu6050_read(0x68, 0x6B, 1, &PWR_MGMT_1);
NRF_LOG_INFO("PWR_MGMT_1=0x%02x",PWR_MGMT_1);
NRF_LOG_FLUSH();
TEMP_DIS=9;
drv_mpu6050_write(0x68, 0x6B, 1, &TEMP_DIS);
drv_mpu6050_read(0x68, 0x6B, 1, &PWR_MGMT_1);
NRF_LOG_INFO("PWR_MGMT_1=0x%02x",PWR_MGMT_1);
NRF_LOG_FLUSH();
int res;
res= mpu_init();
NRF_LOG_INFO("mpu_init=%d",res);
NRF_LOG_FLUSH();
res=mpu_set_sensors(INV_XYZ_GYRO|INV_XYZ_ACCEL);
NRF_LOG_INFO("mpu_set_sensors=%d",res);
NRF_LOG_FLUSH();
res=mpu_configure_fifo(INV_XYZ_GYRO | INV_XYZ_ACCEL);
NRF_LOG_INFO("mpu_configure_fifo=%d",res);
NRF_LOG_FLUSH();
res=mpu_set_sample_rate(50);
NRF_LOG_INFO("mpu_set_sample_rate=%d",res);
NRF_LOG_FLUSH();
res=dmp_load_motion_driver_firmware();
NRF_LOG_INFO("dmp_load_motion_driver_firmware=%d",res);
NRF_LOG_FLUSH();
res=dmp_set_fifo_rate(50);
NRF_LOG_INFO("dmp_set_fifo_rate=%d",res);
NRF_LOG_FLUSH();
res=mpu_set_dmp_state(1);
NRF_LOG_INFO("mpu_set_dmp_state=%d",res);
NRF_LOG_FLUSH();
//PEDOMeter setup
unsigned long stepCount = 0;
unsigned long lastStepCount = 0;
res=dmpSetPedometerSteps(stepCount);
NRF_LOG_INFO("dmpSetPedometerSteps=%d",res);
NRF_LOG_FLUSH();
//uint32_t p_ticks;
while (true)
{
stepCount = dmpGetPedometerSteps();
lastStepCount = stepCount;
NRF_LOG_INFO("Walked %d steps",stepCount) ;
NRF_LOG_FLUSH();
//p_ticks=app_timer_cnt_get();
//NRF_LOG_INFO("Count=%d",p_ticks);
//NRF_LOG_FLUSH();
nrf_delay_ms(1000);
//SSD1306_Clear();
//SSD1306_GotoXY (0,0 );
//SSD1306_Puts ("Hello world.", &Font_11x18, 1);
//SSD1306_UpdateScreen();
//nrf_delay_ms(750);
//SSD1306_Clear();
// Do nothing.
}
}
/** @} */
コメント
コメントを投稿