IM920の使い方|ペアリングやコマンドとRSSIの読み出し|マイコン入門

当サイトにはアフィリエイト広告が表示されます。

※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でオンオフ可能な電圧計と電流計を付ける
スポンサーリンク
  • URLをコピーしました!

コメント

コメントする

CAPTCHA


興味があるところを読む