※Arduinoやmbed・ラズパイを使う記事ではありません。
CPUはRL78/G13を直に使います。
INA260・INA226・LM61からデータを取得して無線でパソコンに送信します。
RSSIを読み出し、電波が強ければ高速通信、弱いときは低速で確実に。
センサーとの通信に使用する無線モジュールの使い方の詳細です。
距離(RSSI)に応じて通信速度を変えるようにしました(近距離は高速、遠距離では低速)。
INA260、INA226、LM61などからデータを集めIM920で送受信。
結構過酷な環境で5年ほど使ってますが、故障知らずで順調に動作しています。
興味があるところを読む
CPUとの接続
R5F100V1とUARTで接続してます。
IM920のBUSYとRESET用にI/Oを2bit使います。
CPU側の回路図へジャンプ
IM920
ペアリング
アプリケーション・ノート(中段)
IM920取扱説明書(ハードウェア編)
IM920取扱説明書(ソフトウェア編)
使っているパラメータの一覧表です。
デフォルト値 ・STCH:通信チャンネル設定:初期値=1(920.6MHz) ・STPO:送信出力設定:初期値=3(10mW) ・STRT:無線通信速度設定:初期値=2(長距離モード) ・消費電流:送信時40mA、受信時27mA、スリープ時:7uA ・パケット送信時間 長距離モード 94.4ms+6.4ms×バイト数 ← 送信時間が3.8秒(約550byte)を越えると60msの休止が入る 高速モード 3.2ms+160μs×バイト数
ペアリング
RPRM 全パラメータを読み出してみる(自分のIDや設定状態が分かる) ENWR パラメータの書込みを許可し SRIDnnnn ペアリング相手のIDを設定し RRID 設定したIDを確認し DSWR パラメータの書込みを禁止する 以上でペアリングしたので、TXDAで送受信が可能だ。
送受信の実験
・一度に送信できるのは63byte+0xd(0xaは無視される)。 ・BUSY端子がLの期間中のみコマンド入力が可能。 ・コマンド処理中およびデータ処理中はSTATUSにHを出力(STATUSを見ない場合は、0xdを送信後に”OK\r\n”の返信待ちが必要)。 ・遠距離モードでは6400byte送信に約60秒かかる(1byte送信に9.4ms、64byteで600ms)。 <ENWR,DCIO,DSWR> DCIO状態で2F86 → 2F87に以下を送信すると・・・ 2F86が送信:TXDA 00112233445566778899AABBCCDDEEFF<CR><LF> 2F87が受信:00,2F86,D0:00,11,22,33,44,55,66,77,88,99,AA,BB,CC,DD,EE,FF<CR><LF>
<ENWR,ECIO,DSWR> ECIOだと送信した文字がそのまま受信される。0xdと0xa以外は送信可能だ。 こっちの方が送信データ数が減らせるんで良いかも(0xdと0xaはESC処理する)。 00,2F86,CC: 11223344556677889900aabbccddeeffgg ※RPRMでパラメータを読み出してみた ID:2F86 自分のID STNN:00 ノード番号 STCH:01 通信チャンネル=01(920.6MHz、01~15で指定) STPO:03 送信出力=03(10mW=最大) STRT:02 通信速度=02(長距離モード1.25Kbps、01=高速通信モード50Kbps) SSTM:0000 スリープ時間=0(連続スリープ) SWTM:0000 間欠動作時間=0(10ms 単位で最大656秒、16 進数で指定) ENRX スリープ停止(開始はDSRX) DCIO キャラクタモード1 DABK アンサーバック解除 DRPT 簡易中継解除
スリープの実験
2F87を間欠受信に設定し、2F86から送信データを受け取れるか実験する。 失敗したときはPCLRでペアリングID以外のパラメータを初期化可能だ。 ENWR パラメータ書き込み許可 SWTMnnnn 間欠動作時間設定 DSRX:スリープ開始 ENRXでスリープを停止できるが使わないで行けるはず DSWR:パラメータ書き込み禁止 DSRX:スリープ開始 自分がスリープ中に送信する時は頭に?が必要。「?TXDA 123」など。 起きるには「?ENRX」 スリープ中は受信できない
間欠受信の実験
SSTMnnnn スリープ時間を設定し(1 秒単位で最大65535秒を16進で。0=連続スリープ) SWTMnnnn 間欠動作時間を設定した後(10ms 単位で最大656秒を16 進。0=動作しない) DSRX スリ ープ状態に設定すると間欠受信を開始する ※nnnnの4桁で指定すること ※スリープ中でも「?RPRM」でパラメータ読み出し可能だ(2秒以内に打ち込む必要あり)。 間欠動作時は確かに消費電流が減るのは確認済み 2018/11/24 SSTM=0001(スリープ時間1秒)+SWTM=0064(動作時間1秒) ← 時々しくじる SSTM=0001(スリープ時間1秒)+SWTM=0032(動作時間0.5秒) ← 殆どダメ スリープ中にTXDAは使用可能だ(2秒以内に打ち込む)。「?TXDA 123456」「?」と「T」の間に待ちが必要 まずは送受信だけを実験する 1:センサは5秒スリープして1000mS起きる。 2:PCは15秒間連続送信して返事を待つ。 3:センサは受信したら間欠動作を止めてバッファ内容を送信する。 4:PCは正常受信できたら「センサは寝て良いよ」を送信する。
温度センサ(LM61)
温度センサはLM61BIZ(動作電圧2.7~10V)を使ってます。
出力は温度0℃で600mV、1℃につき10mV増加する(-25~+85°)
これをCPUのADコンバータで取得(3.3V動作)。
CPUのメイン動作フロー
IM920の自家スリープは使わず、全ての管理はCPUが行うこととする(IM920のスリープも起動もCPUが行う)。 ※なるべく消費電力をケチる。 1,CPUはスリープから目覚めたらIM920を起こす。 2,CPUはセンサ値を取得する。 3,500mS以内にLF迄を受信しなければ、IM920をスリープさせ、CPUもスリープして1へ。 4,LFを受信した場合はCPUはCMDを解析して、センサ値をPCに送信する。
_main.c ソースコード(クリックで開きます)
その他のソースが必要であれば請求して下さい。
//----------------------------------------------------------------------------------------------------------
// <<V1.0>> 2018/09/07
// R5F100LG (RL78/G13) RAM12,288byte 64PIN
// http://akizukidenshi.com/download/ds/renesas/r01uh0146jj0200_rl78g13.pdf
// RTCの1秒間隔の周期割込みで目覚めて、センサ値を取得してIM920で送信する(保存データ数は_blkfifo.cを参照)。
// CPUの消費電流はSTOP時50uA程度(STOPしないと500uA)。
// I2CはIICA0、UARTはUART0、SPIはCSI10を使用している。
// ・動作電圧 CPU=1.6~5.5V、IM920=2~3.6V(40mA)、INA260=3~5V(0.3mA)、LM61BIZ=2.7~10V(0.12mA)
// ・電源は外部バッテリーで供給している
// <<V1.3>> 2021/02/17
// 無線通信速度の自動切り替えを追加(アプリとファーム両方の変更)
// 1,最初の接続は低速で行う
// 2,PCからのCMDにより高速通信を試し
// 3,OKなら高速でデータ通信を行い
// 4,オフラインおよびSLEEP前には低速通信に戻す
//----------------------------------------------------------------------------------------------------------
#include "r_cg_macrodriver.h"
#include "r_cg_cgc.h"
#include "r_cg_port.h"
#include "r_cg_serial.h"
#include "r_cg_adc.h"
#include "r_cg_timer.h"
#include "r_cg_rtc.h"
#include "r_cg_wdt.h"
#include "r_cg_userdefine.h"
#include <stdio.h>
#include <string.h>
static void Ina260Init(DCH ch);
static uint16_t Ina260Getvolt(DCH ch);
static uint16_t Ina260Getcurt(DCH ch);
static uint16_t Lm61Get(void);
static void MainInit(void);
static void OnlineProc(void);
static void OfflineProc(void);
static void OnlineLed(bool sw);
static void TestLed(bool sw);
static void ErrorLed(void);
static void StopOj3(void);
static void SaveData(void);
static void PcCmdProc(uint8_t pccmd[]);
static bool RecvFromPc(uint8_t rxd[], uint16_t len, uint16_t tmout);
static bool SendToPcRes(uint8_t d);
static void ChgSampleTime(uint8_t cmd2);
static bool InitIm920(void);
static bool IsIm920Busy(void);
static bool SleepIm920(void);
static bool WakeupIm920(void);
static void SpdIm920(SPDSEL sl); // V1.3
static bool SendFifo(void);
static bool SendNowData(void);
static bool SendOneLine(uint8_t txd[], uint8_t len);
static void RtcAccs(uint8_t pccmd[]);
static void RtcSet(uint8_t d[]);
static void RtcGet(void);
static bool G_Online = FALSE; // オンライン中フラグ
static uint32_t G_SmplIntv = 1UL; // デフォルトのサンプリング・インターバル=1秒
static uint8_t G_SmplIntvCh = 'A'; // " 'A'
static SPDSEL G_Spdsel = LSPD; // V1.3 通信速度の状態(LSPD=低速モード)
void MF_main(void)
{
MainInit();
while (1) {
if (G_Online == TRUE) OnlineProc(); // オンライン中なら・・・
else OfflineProc(); // オフライン中なら・・・
OnlineLed(G_Online);
}
}
//----------------------------------------------------------------------------------------------------------
// オンライン・オフライン処理
//----------------------------------------------------------------------------------------------------------
#define ONLCHKINTV 60UL // オンライン要求を確認するインターバル間隔(秒)
#define RESCNT 15UL // 自分自身でオフラインにする時間(15×2秒)
// オンライン処理
static void OnlineProc(void)
{
uint8_t rxd[30];
uint32_t resCnt = 0UL;
while (TRUE == G_Online) {
// PCからCMDが来たら処理する
if (TRUE == RecvFromPc(rxd, 30, 2000)) { // timeout=2秒
PcCmdProc(rxd);
resCnt = 0UL;
if (FALSE == G_Online) SleepIm920(); // オフラインCMDが来た V1.3
}
// 一定時間PCからのCMDが来ないようなら自分でオフラインにする
else {
if (++resCnt >= RESCNT) {
resCnt = 0UL;
G_Online = FALSE;
SleepIm920();
}
}
R_WDT_Restart();
}
}
// オフライン処理(RTCにより1秒毎に起動する)
static void OfflineProc(void)
{
uint8_t rxd[50];
uint32_t sensCnt = 0UL;
uint32_t wkupCnt = 0UL;
while (FALSE == G_Online) {
// 指定のサンプリング間隔になっていたら、温度・電圧・電流を読み出してFiFoに保存する
//G_SmplIntv=10; // デバッグ用に覚醒間隔=10秒
if (++sensCnt >= G_SmplIntv) {
OnlineLed(ON);
sensCnt = 0UL;
SaveData();
OnlineLed(OFF);
}
// 一定間隔で電波を出してPCからのオンライン要求を確認する
if (++wkupCnt == ONLCHKINTV) {
wkupCnt = 0UL;
if (TRUE == WakeupIm920()) { // IM920を起こし・・・
SendToPcRes('1'); // 適当な文字をPCに送信する
if (TRUE == RecvFromPc(rxd,50,400)) { // PCからCMDを受信したら(timeout=400mS)
PcCmdProc(rxd); // 処理する
if (TRUE == G_Online) break; // オンラインになったら抜ける
}
SleepIm920(); // オンラインにならなければIM920をSleepする
}
}
StopOj3(); // CPUをSleepする(1秒後に起動する)
R_WDT_Restart();
}
}
//----------------------------------------------------------------------------------------------------------
// main初期化
//----------------------------------------------------------------------------------------------------------
static void MainInit(void)
{
RPECTL_bit.no7 = 1; // RAMパリティーエラー無効
R_UART0_Start(); // 無線機との通信用
//R_CSI10_Start(); // 使ってない
R_TAU0_Channel0_Start(); // delay用だ
R_RTC_Start(); // RTC割込みでCPUは目覚める
EI();
Delay(100); // 必須
InitIm920(); // 何もしてない
if (FALSE == SleepIm920()) ErrorLed(); // 無線機をスリープする
Ina260Init(CH1); // 電圧・電流の取得用
Ina260Init(CH2);
FifoInit();
}
//----------------------------------------------------------------------------------------------------------
// 省電力化したSTOP:RTCの定周期INTRで生き返る
// STOP中は50uAほど消費する(STOPしないと500uA)
//----------------------------------------------------------------------------------------------------------
static void StopOj3(void)
{
// 寝る前に電気食いを止める
R_UART0_Stop();
//R_IICA0_Stop(); // I2Cは止めちゃダメ
//R_CSI10_Stop(); // SPIは使ってない
R_TAU0_Channel0_Stop();
STOP(); // STOPして
// 目覚めたら電気食いを起こす
R_UART0_Start();
//R_CSI10_Start();
R_TAU0_Channel0_Start();
}
//----------------------------------------------------------------------------------------------------------
// PCから'\n'までを受信する
// IM920からは'00,2F86,D0: xx\r\n'の16byteが出力される
//----------------------------------------------------------------------------------------------------------
static bool RecvFromPc(uint8_t rxd[], uint16_t len, uint16_t tmout)
{
uint16_t i;
MF_R_UART0_Receive(rxd, len);
for (i=0; i<tmout && FALSE==MF_IsRxEnd(); ++i) Delay(1);
return MF_IsRxEnd();
}
//----------------------------------------------------------------------------------------------------------
// PCに1文字を送信する
//----------------------------------------------------------------------------------------------------------
static bool SendToPcRes(uint8_t d)
{
uint8_t txd[10];
strcpy(txd, "TXDA x\r");
txd[5] = d;
return SendOneLine(txd, 7);
}
//----------------------------------------------------------------------------------------------------------
// PCに配列を送信する
//----------------------------------------------------------------------------------------------------------
static bool SendOneLine(uint8_t txd[], uint8_t len)
{
uint8_t rxd[10];
while (TRUE == IsIm920Busy()); // BUSYをチェックし
MF_R_UART0_Receive(rxd, 4); // 先に結果取得を始めておく
MF_R_UART0_Send(txd, len); // IM920へ文字列を渡し
while (FALSE == MF_IsTxEnd()); // IM920へ渡ったら
while (FALSE == MF_IsRxEnd()); // IM920からの返事を待つ('\n'で終了)
return (!strncmp(rxd, "NG\r\n", 4))? FALSE: TRUE;
}
//----------------------------------------------------------------------------------------------------------
// PCからのCMD('00,2F86,D0: xx\r\n')に応じた処理
//----------------------------------------------------------------------------------------------------------
static void PcCmdProc(uint8_t pccmd[])
{
uint8_t cmd1, cmd2;
cmd1 = pccmd[12]; // PCからのCMD[12][13]をセット
cmd2 = pccmd[13];
TestLed(ON);
// cmd1が'S'の処理
if (cmd1 == 'S') {
// cmd2の処理分岐
switch (cmd2) {
// SN:オンラインにする
case 'N':
SendToPcRes('1'); // PCへCMD受信確認を送信
G_Online = TRUE;
break;
// SF:オフラインにする
case 'F':
SendToPcRes('1'); // PCへCMD受信確認を送信
SpdIm920(LSPD); // 本機を低速モードにする
G_Online = FALSE;
break;
// S0:PCとの送受信テスト用(何もせずに返信する)
case '0':
SendToPcRes('1'); // PCへCMD受信確認を送信
break;
// S1:PCにFiFo内容を送信する
case '1':
SendFifo(); // PCへは受信確認の送信をせずにデータを送信する
break;
// S2:Fifoをクリアする
case '2':
SendToPcRes('1'); // PCへCMD受信確認を送信
FifoInit();
break;
// S3:現時点のセンサ値を送信する
case '3':
SendNowData(); // PCへは受信確認の送信をせずにデータを送信する
break;
}
// cmd1が'C'ならサンプリング間隔の変更
} else if (cmd1 == 'C') {
SendToPcRes('1'); // PCへCMD受信確認を送信
ChgSampleTime(cmd2);
// cmd1が'R'なら日時の設定と取得
} else if (cmd1 == 'R') {
RtcAccs(pccmd); // PCへは受信確認の送信をせずにデータを送信する
// cmd1が'P'なら通信速度を高速化する V1.3
} else if (cmd1 == 'P') {
SendToPcRes('1'); // PCへCMD受信確認を送信
SpdIm920(HSPD); // 本機を高速モードにする
}
TestLed(OFF);
}
//----------------------------------------------------------------------------------------------------------
// リングバッファからセンサ値を全て取り出して送信する(0xaと0xdは含まれていないはず)
// "TXDA xx"+60byteのデータを生成する
//----------------------------------------------------------------------------------------------------------
static bool SendFifo(void)
{
bool res = TRUE;
uint16_t t1, v1, c1, t2, v2, c2, i;
uint8_t txd[80], c, blkcnt, csum;
FifoRpSave(); // 現時点での読み出し位置の保存
//for (i=0; i<900; ++i) FifoPut(1,1,1,1,1,1); // debug
// 全バッファデータを送信する
strcpy(txd, "TXDA "); // txd[5]=有効ブロック数, txd[6]=csum,
while (1) {
c = 7; // txd[7~73]データ,[74]='\r'
blkcnt = csum = 0;
for (i=0; i<5; ++i) {
if (0 == FifoCnt()) break;
FifoGet(&t1, &v1, &c1, &t2, &v2, &c2);
++blkcnt;
// CH1送信データ生成
txd[c] = GetMsb(t1); csum += txd[c++];// temp
txd[c] = GetLsb(t1); csum += txd[c++];
txd[c] = GetMsb(v1); csum += txd[c++];// Volt
txd[c] = GetLsb(v1); csum += txd[c++];
txd[c] = GetMsb(c1); csum += txd[c++];// Curt
txd[c] = GetLsb(c1); csum += txd[c++];
// CH2送信データ生成
txd[c] = GetMsb(t2); csum += txd[c++];// temp
txd[c] = GetLsb(t2); csum += txd[c++];
txd[c] = GetMsb(v2); csum += txd[c++];// Volt
txd[c] = GetLsb(v2); csum += txd[c++];
txd[c] = GetMsb(c2); csum += txd[c++];// Curt
txd[c] = GetLsb(c2); csum += txd[c++];
R_WDT_Restart();
}
if (0 == blkcnt) break; // Fifoが空
txd[5] = blkcnt + '0'; // txd[5]に有効ブロック数をセット
if (csum==0xa || csum==0xd) ++csum; // csumが0xa/0xdの場合は+1する
txd[6] = csum; // txd[6]にcsumをセット
txd[c++] = '\r'; // デリミタは'\r'だけでOK
res = SendOneLine(txd, c); // PCに送信する
if (FALSE == res) Delay(200); // 300blk程度でエラーが返りDelayするが、この時に1blk送信ミスする
Delay(5); // これを追加することで送信ミスをしなくなる
}
FifoRpReset(); // 読み出し位置の復元
return res;
}
//----------------------------------------------------------------------------------------------------------
// 現在のセンサ値を送信する
// "TXDA 1x"+12byteのデータを生成する
//----------------------------------------------------------------------------------------------------------
static bool SendNowData(void)
{
uint16_t t, v, r, c;
uint8_t txd[80], csum;
strcpy(txd, "TXDA 1x"); // txd[5]=1(有効ブロック数), txd[6]=csum
c = 7; // txd[7~18]データ,[19]='\r'
csum = 0;
// CH1送信データ生成(CH1とCH2のデータ個数は同じ)
t = Lm61Get(); // 温度
v = Ina260Getvolt(CH1); // 電圧
r = Ina260Getcurt(CH1); // 電流
txd[c] = GetMsb(t); csum += txd[c++]; // Temp
txd[c] = GetLsb(t); csum += txd[c++];
txd[c] = GetMsb(v); csum += txd[c++]; // Volt
txd[c] = GetLsb(v); csum += txd[c++];
txd[c] = GetMsb(r); csum += txd[c++]; // Curt
txd[c] = GetLsb(r); csum += txd[c++];
// CH2送信データ生成
t = 0; // 温度
v = Ina260Getvolt(CH2); // 電圧
r = Ina260Getcurt(CH2); // 電流
txd[c] = GetMsb(t); csum += txd[c++]; // Temp
txd[c] = GetLsb(t); csum += txd[c++];
txd[c] = GetMsb(v); csum += txd[c++]; // Volt
txd[c] = GetLsb(v); csum += txd[c++];
txd[c] = GetMsb(r); csum += txd[c++]; // Curt
txd[c] = GetLsb(r); csum += txd[c++];
// 送信開始
if (csum==0xa || csum==0xd) ++csum; // csumが0xa/0xdの場合は+1する
txd[6] = csum; // txd[5]にcsumをセット
txd[c++] = '\r'; // デリミタは'\r'だけでOK
return SendOneLine(txd, c); // PCに送信する
}
//----------------------------------------------------------------------------------------------------------
// サンプリング間隔の変更
// A:1秒、B:10秒、C:30秒、D:1分、E:5分、F:10分、G:15分、H:30分
// I:1時間、J:2時間、K:4時間、L:6時間、M:8時間、N:12時間
//----------------------------------------------------------------------------------------------------------
static void ChgSampleTime(uint8_t cmd2)
{
G_SmplIntvCh = cmd2;
switch (cmd2) {
case 'A':G_SmplIntv = 1UL; break;
case 'B':G_SmplIntv = 10UL; break;
case 'C':G_SmplIntv = 30UL; break;
case 'D':G_SmplIntv = 60UL; break;
case 'E':G_SmplIntv = 60UL*5UL; break;
case 'F':G_SmplIntv = 60UL*10UL; break;
case 'G':G_SmplIntv = 60UL*15UL; break;
case 'H':G_SmplIntv = 60UL*30UL; break;
case 'I':G_SmplIntv = 60UL*60UL*1UL; break;
case 'J':G_SmplIntv = 60UL*60UL*2UL; break;
case 'K':G_SmplIntv = 60UL*60UL*4UL; break;
case 'L':G_SmplIntv = 60UL*60UL*6UL; break;
case 'M':G_SmplIntv = 60UL*60UL*8UL; break;
case 'N':G_SmplIntv = 60UL*60UL*12UL; break;
default:break;
}
}
//----------------------------------------------------------------------------------------------------------
// センサのBIN値をそのままリングバッファに追加していく
//----------------------------------------------------------------------------------------------------------
static void SaveData(void)
{
uint16_t t1, v1, c1, t2, v2, c2;
// #1
t1 = Lm61Get(); // 温度
v1 = Ina260Getvolt(CH1); // 電圧 0x3840 半分の電圧
c1 = Ina260Getcurt(CH1); // 電流 0x7dff +側の最高電流
// #2
t2 = 0x0; // 温度
v2 = Ina260Getvolt(CH2); // 電圧 0x3840 半分の電圧
c2 = Ina260Getcurt(CH2); // 電流 0x7dff +側の最高電流
FifoPut(t1, v1, c1, t2, v2, c2);
}
//----------------------------------------------------------------------------------------------------------
//
// 細かい下請け関数
//
//----------------------------------------------------------------------------------------------------------
static void OnlineLed(bool sw)
{
P4_bit.no3 = (sw==ON)? 1: 0;
}
static void TestLed(bool sw)
{
P4_bit.no2 = (sw==ON)? 1: 0;
}
static void ErrorLed(void)
{
while (1) {
P4_bit.no3 = !P4_bit.no3;
P4_bit.no2 = !P4_bit.no2;
Delay(50);
}
}
//----------------------------------------------------------------------------------------------------------
// IM920:無線通信
//
// 初期化(このボード搭載のIM920のIDは2F86で、PCアプリ側のIDは2F87である)
// このシステムに組み込む前に「ECIO,SRID 2F87」を指定しておくこと(UARTのボーレートは19200)
// <出荷時のdefault値>
// "DCIO" HEX入出力モード
// "STCH 01" 通信チャンネル設定(01)
// "STRT 2" 通信速度設定(1=高速/短距離,2=低速/長距離)
// "STPO 3" 送信出力設定(10mW最大出力)
// ソフトマニュアル https://www.interplan.co.jp/support/solution/IM315/manual/IM920_SW_manual.pdf
// ハードマニュアル https://www.interplan.co.jp/support/solution/IM315/manual/IM920_HW_manual.pdf
// ペアリング https://www.interplan.co.jp/support/solution/IM315/app_note/AN01.pdf
//----------------------------------------------------------------------------------------------------------
static bool InitIm920(void)
{
return TRUE; // 初期化はTeratermで事前に行っておく(ここでは何もしない)
}
// IM920がスリープ中(BUSY=1)ならTRUEを返す
static bool IsIm920Busy(void)
{
return (P5_bit.no3 == 1)? TRUE:FALSE;
}
// IM920をSLEEPさせる
static bool SleepIm920(void)
{
uint8_t rxd[64];
if (TRUE == IsIm920Busy()) return TRUE; // 既にSleep中なら終わり
SpdIm920(LSPD); // V1.3 低速通信モードにする(デフォルト状態)
MF_R_UART0_Receive(rxd, 64); // 先に受信を始めておく
MF_R_UART0_Send("DSRX\r\n", 6); // SleepCMD発行
while (FALSE == MF_IsTxEnd()); // CMDの設定終了を待つ
while (FALSE == MF_IsRxEnd()); // '\xa'を受信するまで待つ
if (!strncmp(rxd, "OK\r\n", 4)) { // "OK"なら・・・
while (FALSE == IsIm920Busy()); // SLEEPするのを待つ
return TRUE;
}
return FALSE;
}
// IM920をSLEEPから起こす
static bool WakeupIm920(void)
{
uint8_t rxd[64];
if (FALSE == IsIm920Busy()) return TRUE; // 既に起きてるなら終わり
MF_R_UART0_Receive(rxd, 64); // 先に受信を始めておく
MF_R_UART0_Send("?", 1); // 目覚めマークを送信
while (TRUE == IsIm920Busy()); // 目覚めるのを待つ
MF_R_UART0_Send("ENRX\r\n", 6); // 目覚めCMDを送信
while (FALSE == MF_IsTxEnd()); // CMDの設定終了を待つ
while (FALSE == MF_IsRxEnd()); // '\xa'を受信するまで待つ
return (!strncmp(rxd, "OK\r\n", 4))? TRUE:FALSE; // "OK"が来ればTRUE
}
// IM920の通信速度を変更する V1.3(失敗したら永久Loopで終わり)
static void SpdIm920(SPDSEL sl)
{
uint8_t rxd[10], txd[10];
if (G_Spdsel == sl) return; // 速度が変更済みならなにもしない
if (TRUE == IsIm920Busy()) return; // 念のために起きてることを確認
MF_R_UART0_Receive(rxd, 10); // 先に受信を始めておく
strcpy(txd, "STRT x\r\n"); // 速度CMDセット
txd[5] = (sl == HSPD)? '1': '2'; // '1'=高速, '2'=低速
MF_R_UART0_Send(txd, 8); // 通信速度の変更CMD
while (FALSE == MF_IsTxEnd()); // CMDの設定終了を待つ
while (FALSE == MF_IsRxEnd()); // '\xa'を受信するまで待つ
if (!strncmp(rxd, "OK\r\n", 4)) G_Spdsel = sl; // "OK"が来ることを確認
else ErrorLed(); // 失敗したら永久Loopで終わり
}
//--------------------------------------------------------------------------------
// INA260:電圧・電流を取得する
//--------------------------------------------------------------------------------
// IC2での送受信
static bool Ina260_wr(DCH ch, uint8_t dat[], uint8_t len)
{
bool res;
uint8_t wadrs = (ch==CH1)? INA260_CH1W: INA260_CH2W; // I2C write adrs
uint8_t radrs = (ch==CH1)? INA260_CH1R: INA260_CH2R; // I2C read adrs
res = I2C_Rw(wadrs, TX, len, dat); // 送信
if (TRUE == res) { // OKなら・・・
DelayMsec(1); // ちょい待ってから
res = I2C_Rw(radrs, RX, 2, dat); // 結果を受信する
}
return res;
}
// 初期化
static void Ina260Init(DCH ch)
{
char cmd[3];
// 16回の平均値を取得する
cmd[0]=0x00; // config reg.
cmd[1]=0x45; // average set & number=16
cmd[2]=0x27;
Ina260_wr(ch, cmd, 3);
}
// 電圧・電流・電力読みだしsub
// 無線機には0xd/0xaを送信できないので一工夫あり
static bool Ina260Get(DCH ch, uint8_t cmd, uint8_t dat[])
{
dat[0]=cmd;
if (TRUE == Ina260_wr(ch, dat, 1)) {
// 下位byteは0xa->0xb,0xd->0xcとする
if (dat[1] == 0xa) dat[1]=0xb;
else if (dat[1] == 0xd) dat[1]=0xc;
return TRUE;
}
return FALSE;
}
// 電流読みだし
static uint16_t Ina260Getcurt(DCH ch)
{
uint8_t dat[3];
if (TRUE == Ina260Get(ch, 0x1, dat)) {
// 上位byteは0xa->0x7E,0xd->0x7Fとする
if (dat[0] == 0xa) dat[0]=0x7e;
else if (dat[0] == 0xd) dat[0]=0x7f;
return (dat[0]<<8 | dat[1]);
}
return 0;
//return Ina260Getcurt(ch) * 1.25; // mA値 -40.9600A~+40.9600A
}
// 電圧読み出し
static uint16_t Ina260Getvolt(DCH ch)
{
uint8_t dat[3];
if (TRUE == Ina260Get(ch, 0x2, dat)) {
// 上位byteは0xa->0xFE,0xd->0xFFとする
if (dat[0] == 0xa) dat[0]=0xfe;
else if (dat[0] == 0xd) dat[0]=0xff;
return (dat[0]<<8 | dat[1]);
}
return 0;
//return Ina260Getvolt(ch) * 1.25; // mV値 0~36.000V
}
/*
// 電力読みだし
static uint16_t Ina260Getpwr(DCH ch)
{
uint8_t dat[3];
return (Ina260Get(ch, 0x3, dat)==TRUE)? (dat[0]<<8 | dat[1]): 0;
//return Ina260Getpwr(ch) * 1000.0; // mW値
}
*/
//--------------------------------------------------------------------------------
// LM61BIZ(動作電圧2.7~10V)温度を取得する
// 出力は温度0℃で600mV、1℃につき10mV増加する(-25~+85°)
//--------------------------------------------------------------------------------
static uint16_t Lm61Get(void)
{
uint16_t r;
uint8_t h8, l8;
R_ADC_Set_OperationOn();
DelayMsec(1);
R_ADC_Start();
DelayMsec(1);
R_ADC_Set_OperationOff();
r = MF_AdGet();
// 上位byteは0xa->0xFE,0xd->0xFFとする
h8 = GetMsb(r);
if (h8 == 0xa) h8 = 0xfe;
else if (h8 == 0xd) h8 = 0xff;
// 下位byteは0xa->0xb,0xd->0xcとする
l8 = GetLsb(r);
if (l8 == 0xa) l8 = 0xb;
else if (l8 == 0xd) l8 = 0xc;
return (h8 << 8) + l8;
}
//--------------------------------------------------------------------------------
// パラメータの設定と読み出し
// PCとの日時同期用だが日時部分は使ってない
// もしPCでセンサ側のパラメータが必要になったらここに追加すれば良い
//--------------------------------------------------------------------------------
static void RtcAccs(uint8_t pccmd[])
{
uint8_t d[10];
if (pccmd[13] == '1') { // RTCセット
memcpy(d, &pccmd[14], 6); // [14]から時刻データがBCDコードで入ってくる
RtcSet(d);
} else RtcGet(); // RTC読み出し
}
static void RtcSet(uint8_t d[])
{
// RTCはXtalを積んでないので動作不可
RTCE = 0;
YEAR = d[0]; // BCDコードなので0xd0xaはあり得ない
MONTH = d[1];
DAY = d[2];
HOUR = d[3];
MIN = d[4];
SEC = d[5];
RTCE = 1;
SendToPcRes('1'); // PCにOKを返信
}
static void RtcGet(void)
{
uint16_t fcnt;
uint32_t si;
uint8_t txd[80], csum, c;
strcpy(txd, "TXDA 1x"); // txd[5]=1(有効ブロック数), txd[6]=csum
c = 7;
csum = 0;
// 日時は6byte(BCDコードなので0xd/0xaはあり得ない)
txd[c] = YEAR; csum += txd[c++];
txd[c] = MONTH; csum += txd[c++];
txd[c] = DAY; csum += txd[c++];
txd[c] = HOUR; csum += txd[c++];
txd[c] = MIN; csum += txd[c++];
txd[c] = SEC; csum += txd[c++];
// Fifo個数はint16
// 0xd/0xaを送信しない処理
fcnt = FifoCnt();
if (fcnt == 0xa) fcnt = 0xfffe;
if (fcnt == 0xd) fcnt = 0xffff;
txd[c] = GetMsb(fcnt); csum += txd[c++];
txd[c] = GetLsb(fcnt); csum += txd[c++];
// サンプリング間隔の指定文字は1byte
txd[c] = G_SmplIntvCh; csum += txd[c++];
// オマケをint32(0xd/0xaは送れないので注意)
si = 0x12345678UL;
txd[c] = si >> 24; csum += txd[c++];
txd[c] = si >> 16; csum += txd[c++];
txd[c] = si >> 8; csum += txd[c++];
txd[c] = si & 0xff; csum += txd[c++];
txd[c++] = '\r';
if (csum==0xa || csum==0xd) ++csum; // csumが0xa/0xdの場合は+1する
txd[6] = csum;
SendOneLine(txd, c);
}
バージョン経過
V1.1
<2021/1/21> INA260は低電流時の電流誤差が大きいので(150mAを120mAと表示)、CH1をINA226PRC(3.2A計測タイプ)に変更した。 接続はINA260とコンパチなので、C#表示アプリのCH1だけの変更である(表示値を1/10にした)。
V1.2
<2021/2/15> センサ側のバッテリーを26550に変更した(大きくした)。 また被測定物からも電源を取れるように改造した。 外部電源はMAX20V迄である(XC6202の限界電圧) バッテリー交換時はスライドSWを外部側にしておけばCPUは停止しない。
V1.3
<2021/2/17> 無線通信速度の自動切り替えを追加した(アプリとファーム両方を変更) 1,低速通信モードでオンラインにして 2,アプリ側でRSSIを見て140以上なら、アプリの指示で高速通信モードに切換え 3,データ通信を行い 4,オフラインおよびファーム側がSLEEPするときにアプリ・ファームとも低速通信モードに戻す ・その他のアプリのバグ修正 MIN値が0にならないように、データ個数分だけの最小値を得るように修正した
V1.31
<2021/3/19> ・リングバッファを改造して確実な物にした ・V1.3で出たファームのバグ修正 IM920へのデータセット速度が上がった事による送信不良に対処 ・V1.1で出たアプリのバグ修正 INA226では電流値のMAX制限が余計だったので削除 <2021/5/28> ・INA226のI2Cアドレス(0x80:A1=A0=GND)のジャンパを半田付けしてなかった
V2(案)
V2では追加で以下を提案 ・CPUをこれに変更(RAMが12 → 20K) ・指定電圧や電流でブザーを鳴らす ・SWでオンオフ可能な電圧計と電流計を付ける
コメント