FLIS-UNOホストプログラムのご紹介(EFM32-DK) | 組込みとともに

FLIS-UNOホストプログラムのご紹介(EFM32-DK)

前回の2線式コントロールLEDのブログの続きです。

今回は、ホストプログラムについて解説します。
2線式コントロールLED(FLIS-UNO-001)の使用についてはスーの道具箱さんに記載がありますのでそちらをご確認ください。

バス仕様 ・・・ http://su-u.jp/juju/FLIS/BUS.html
コマンド仕様 ・・・ http://su-u.jp/juju/FLIS/COMMAND.html
その他 ・・・ http://su-u.jp/juju/FLIS/UNO.html

本題に入る前に2線式インターフェイスについてまとめますと次のような物があります。

・電流2線インターフェース
 一般に電流ループとか2線伝送式とかいうインターフェイスで4-20mAのアナログ出力です。
 スレーブは4mA以内で動作することにより内部電源を確保し、出力を4-20mAまで変化させる。電源(プラス側)は一般的に24Vが使用される。

・電源重畳通信
 主にテレビアンテナのブースターなどに使われている通信
 電源をDCレベルで重畳させて、信号は交流分として抽出する
 最近一切話題に上がらないPoEも原理的には同じ
 インバータノイズに弱い

・1Wireインターフェイス
 MAXIMの温度計などに使用されているプロトコル(FLISとは若干異なるが、原理的には同じ)
 スレーブが省電力ということが前提

電源と信号を運ぶ2線式のインターフェイスと言ったら上記の3つが一般的だと思います(他にもあるかもしれませんが)

さてさて、では早速スーの道具箱さんから移植・改変したプログラムをご紹介します
※EFMのサンプルプログラムおよびスーの道具箱さんのサンプルプログラムを使用していますが、コード中に特にライセンス表示などはしていません。

#include // memcpy

#include "chip.h"
#include "efm32.h"
#include "efm32_cmu.h"
#include "efm32_gpio.h" // Port B-0制御
#include "dvk.h"
#include "core_cm3.h" // __NOP

static volatile uint32_t msTicks; /* counts 1ms timeTicks */

/**************************************************************************//**
* @brief SysTick_Handler
* Interrupt Service Routine for system tick counter
*****************************************************************************/
void SysTick_Handler(void)
{
msTicks++; /* increment counter necessary in Delay()*/
}

/**************************************************************************//**
* @brief Delays number of msTick Systicks (typically 1 ms)
* @param dlyTicks Number of ticks to delay
*****************************************************************************/
void Delay(uint32_t dlyTicks)
{
uint32_t curTicks;

curTicks = msTicks;
while ((msTicks - curTicks) < dlyTicks) ;
}

void loop();
void send(unsigned char node, void *data);
void sendaddr(unsigned char *address, void *data);
void sendByte(unsigned char data);
void sendBytes(unsigned char *data, uint8_t size);
void delayMicroseconds(int us);

/**************************************************************************//**
* @brief Main function
*****************************************************************************/
int main(void)
{
/* Chip errata */
CHIP_init();

/* Initialize DVK board register access */
DVK_init();
CMU->HFCORECLKDIV = _CMU_HFCORECLKDIV_HFCORECLKDIV_DEFAULT; // 32MHzクロック

// LED初期設定
GPIO_PinModeSet(gpioPortB, 12, gpioModePushPull, 0 ); // プッシュプル出力 & デフォルト出力L PortB-12が接続ピン
GPIO_PinOutClear(gpioPortB, 12); // 出力をL

/* Setup SysTick Timer for 1 msec interrupts */
if (SysTick_Config(CMU_ClockFreqGet(cmuClock_CORE) / 1000))
{
while (1) ;
}

/* Blink forever */
while (1)
{
/* Blink user leds on DVK board */
DVK_setLEDs(0xAA55); // ボードのLED(動作確認用でしかない)
Delay(400);

loop(); // FLIS駆動回路

/* Blink user leds on DVK board */
DVK_setLEDs(0x55AA); // ボードのLED(動作確認用でしかない)
Delay(400);
}
}


void loop()
{
static unsigned char c = 0;

#if 1 // LED個別コントロール
if(++c >= 6) c = 0;
switch(c)
{
case 0:
sendaddr("\x01\x00\x00\x3d","\x28\xc8\x00\x00");
sendaddr("\x01\x00\x00\x3e","\x28\xc8\xc8\x00");
sendaddr("\x01\x00\x00\x3f","\x28\x00\xc8\x00");
sendaddr("\x01\x00\x00\x40","\x28\x00\xc8\xc8");
sendaddr("\x01\x00\x00\x41","\x28\x00\x00\xc8");
sendaddr("\x01\x00\x00\x42","\x28\xc8\x00\xc8");
break;
case 1:
sendaddr("\x01\x00\x00\x3d","\x28\x8d\x8d\x00");
sendaddr("\x01\x00\x00\x3e","\x28\x00\xc8\x00");
sendaddr("\x01\x00\x00\x3f","\x28\x00\x8d\x8d");
sendaddr("\x01\x00\x00\x40","\x28\x00\x00\xc8");
sendaddr("\x01\x00\x00\x41","\x28\x8d\x00\x8d");
sendaddr("\x01\x00\x00\x42","\x28\x8d\x00\x00");
break;
case 2:
sendaddr("\x01\x00\x00\x3d","\x28\x00\xc8\x00");
sendaddr("\x01\x00\x00\x3e","\x28\x00\x8d\x8d");
sendaddr("\x01\x00\x00\x3f","\x28\x00\x00\xc8");
sendaddr("\x01\x00\x00\x40","\x28\x8d\x00\x8d");
sendaddr("\x01\x00\x00\x41","\x28\xc8\x00\x00");
sendaddr("\x01\x00\x00\x42","\x28\x8d\x8d\x00");
break;
case 3:
sendaddr("\x01\x00\x00\x3d","\x28\x00\x8d\x8d");
sendaddr("\x01\x00\x00\x3e","\x28\x00\x00\xc8");
sendaddr("\x01\x00\x00\x3f","\x28\x8d\x00\x8d");
sendaddr("\x01\x00\x00\x40","\x28\xc8\x00\x00");
sendaddr("\x01\x00\x00\x41","\x28\x8d\x8d\x00");
sendaddr("\x01\x00\x00\x42","\x28\x00\xc8\x00");
break;
case 4:
sendaddr("\x01\x00\x00\x3d","\x28\x00\x00\xc8");
sendaddr("\x01\x00\x00\x3e","\x28\x8d\x00\x8d");
sendaddr("\x01\x00\x00\x3f","\x28\xc8\x00\x00");
sendaddr("\x01\x00\x00\x40","\x28\x8d\x8d\x00");
sendaddr("\x01\x00\x00\x41","\x28\x00\xc8\x00");
sendaddr("\x01\x00\x00\x42","\x28\x00\x8d\x8d");
break;
case 5:
sendaddr("\x01\x00\x00\x3d","\x28\x8d\x00\x8d");
sendaddr("\x01\x00\x00\x3e","\x28\xc8\x00\x00");
sendaddr("\x01\x00\x00\x3f","\x28\x8d\x8d\x00");
sendaddr("\x01\x00\x00\x40","\x28\x00\xc8\x00");
sendaddr("\x01\x00\x00\x41","\x28\x00\x8d\x8d");
sendaddr("\x01\x00\x00\x42","\x28\x00\x00\xc8");
break;
}
#endif
#if 0 // 全LED同時コントロール
if(++c >= 6) c = 0;
switch(c)
{
case 0: send(0xff, "\x14\xc8\x00\x00"); break; // 赤(1s)
case 1: send(0xff, "\x14\xc8\xc8\x00"); break; // 黄(1s)
case 2: send(0xff, "\x14\x00\xc8\x00"); break; // 緑(1s)
case 3: send(0xff, "\x14\x00\xc8\xc8"); break; // シアン(1s)
case 4: send(0xff, "\x14\x00\x00\xc8"); break; // 青(1s)
case 5: send(0xff, "\x14\xc8\x00\xc8"); break; // 紫(1s)
}
#endif
}


void send(unsigned char node, void *data)
{
if(node == 0x00)
return;
uint8_t buffer[6], sum = 0;
buffer[0] = node;
memcpy(&buffer[1], data, 4);
for(uint8_t i = 0; i < 5; i++)
sum += buffer[i];
buffer[5] = -sum;
sendBytes(buffer, 6);
}



void sendaddr(unsigned char *address, void *data)
{
unsigned char buffer[10], sum = 0;
buffer[0] = 0x00;
memcpy(&buffer[1], address, 4);
memcpy(&buffer[5], data, 4);
for(unsigned char i = 0; i < 9; i++)
sum += buffer[i];
buffer[9] = -sum;
sendBytes(buffer, 10);
}


void sendByte(unsigned char data)
{
for(unsigned char mask = 0x80; mask; mask >>= 1)
{
// 割込み禁止
__disable_irq();
if(data & mask)
{
// 1
GPIO_PinOutSet(gpioPortB, 12);
delayMicroseconds(6); // 6マイクロ秒停止
GPIO_PinOutClear(gpioPortB, 12);
}
else
{
// 0
GPIO_PinOutSet(gpioPortB, 12);
__NOP(); // 2マイクロ秒停止
GPIO_PinOutClear(gpioPortB, 12);
delayMicroseconds(22); // 22マイクロ秒停止
}
// 割込み再開
__enable_irq();
delayMicroseconds(20); // 20マイクロ秒停止
}
}

void sendBytes(unsigned char *data, uint8_t size)
{
while(size--)
sendByte(*data++);
for(uint8_t i = 0; i < 10; i++)
delayMicroseconds(10); // 10マイクロ秒停止
}


__INLINE void delayMicroseconds(int us){

for(int i=0; i {
}
}

=================================================
Toff1は仕様的には10マイクロ秒以上となっていますが、私の環境(3.3V系)では短くしないと、FLLISがブラックアウト(?)してしまうため(おそらく電源電圧低下)6,7マイクロ秒としました。
Toff0は使用的には0.5~1.5マイクロ秒となっていますが、2マイクロ秒程度に設定しました。本来センターをとって1マイクロ秒が仕様的に行けそうですが、駆動用のFETドライブをしようするとFETの動作遅延の影響でパルス幅が短くなってしまいます。そのため少し長くする必要があります。
余談ですが、私の環境ではパルスは径が歪んでしまいましたが、駆動用のFETの動作遅延によりLED側へはこの歪が伝達されずに済んでいます(下の写真参照)

CPU出力直後の波形(写真は90度回転)↓
$組込みとともに-1u ボード出力

FET通過後の波形(写真は90度回転)↓
$組込みとともに-1u FET出力

CPU出力直後の波形(拡大)(写真は90度回転)↓
$組込みとともに-リンギング

スーの道具箱さんのソースはもともとArduino用のC++ですが、大きな変更を加えずとも簡単に移植できました。
バイト配列は文字列で代用しました。終端のNULL文字分もったいないですが、今回は気にしないことにします。

それにしても、このバス仕様はホント素晴らしい!!
スーの道具箱さんよいキットをありがとうございました。

繰り返しにもなりますが、今回の記事でご紹介した関連サイトを以下に記載します。
スーの道具箱さん ・・・ http://su-u.jp/juju/
EnergyMicro(EFM32) ・・・ http://www.energymicro.com/
MTM06 ・・・ http://jp.makezine.com/blog/2010/11/mtm06_announce.html