STM8S-DISCOVERYをBLUETOOTHでPCにつないでみる

BLUETOOTHを使う.これ、わたし的には夢的なものがあります.やってみたらできました.

BLUETOOTHをどこで買ったか?
わたしはUSAのサイトから、写真のBLUETOOTH moduleを買いました.
1ヶ$6ぐらいでした.大きさは500円玉ぐらい.BLACK BOXなので回路図は判りません.
ただし、この基板を日本国内で運用することは電波法に違反していますので、下記の実験だけにとどめて恒久的な使用はしていません.
日本国内で運用できるように技的マークを取得したBLUETOOTH moduleは、値段は高いですが売られてはいるようです.



BLUETOOTH moduleでなにができるのか?
このWEBサイトではUSBを使って疑似COM PORT接続によりPCと通信する記事をたくさん書いてきましたが、それがUSBではなくBLUETOOTHに置き換わるのだというのが技術的な要件です.
つまり、TeraTermのようなterminal softからBLUETOOTHを通じてSTM8S-DISCOVERYを制御できるようになるっていうことです.


なにをするか?
USBでPCと接続する多ch温度ロガー という記事を以前書きました.詳細は左記のページを参照してください.
その製作物を、BLUETOOTHでPCと接続するように改造します.
ゆえに、ソフトウエアは同じで済むはずでしたが、BLUETOOTH moduleの都合で、UARTの設定を少々変更する必要が生じました.



ハードウエアの製作

BLUETOOTH基板のとりつけ
BLUETOOTH基板を、STM8S-DISCOVERYにどうやって取り付けるかを説明します.
回路図は下記です.配線は下記回路図の4本です.
STM8SのPD5とPD6には、UARTの送信信号と受信信号が出てきますので、それをBLUETOOTH基板に配線しています.



STM8S-DISCOVERYの配線

電源は、かならず3.3Vを使います.BLUETOOTH基板の電源が3.3Vだからです.
せっかくBLUETOOTHをつけるのですからUSBケーブル経由の電源供給はしないことにします.なにか専用電源で3.3Vを供給してください.
●STM8S-DISCOVERYのJP1のジャンパーピンは、かならず外しておきます.
●リセットSWはRSTピンをGNDに落とすようにプッシュSWをつけます.プルアップ抵抗はなくてかまいません.
●温度センサは秋月電子で売られている LM61CZ というセンサを使いました.  (LM61CZのpdf)
STM8Sには10chのADC入力ピンがありますけど、ここではそのうち3つだけにLM61CZをつけました.全ch付けたければお好みでどうぞ.




FLASH焼き基板の切り離し
BLOETOOTHですからUSBケーブルを繋がずに使ってみたいわけです.
ですが、program FLASHを焼くには、USBケーブルを繋がなくちゃいけないです.これはジレンマですが仕方ないです.
program FLASH焼きは、USB接続でいつもどおり行います.
program FLASHを焼いた後に、FLASH焼き基板を電気的に切り離さないとSTM8Sにresetがかかりっぱなしになってしまい、動きません.
どうやってFLASH焼き基板を電気的に切り離すかというと、下の写真のSB1,SB2をハンダゴテで切断します.


余談:FLASH焼き基板切断後のFLASH焼きをどうするか?
SB1,SB2を再びハンダブリッジすればOKです.またFLASHを焼けるようになります.
ですが、他の手段もあります.こちらに書いたとおり、疑似COM PORTからFLASHを焼くこともできるんです.
BLUETOOTH経由でできるのかと疑問でしたが、やってみたらできました.



BLUETOOTH接続のしかた

わたしの環境での話なので一般的ではありませんが、いちおう書きます.

PCにはBLUETOOTHがないので、USB接続型のBLUETOOTHアダプタをつけました.
PCI社のこちらの製品です.デバイスドライバのインストール方法は省略します.


BLUETOOTH設定を開きます.すでに使っているマウスが見えています.


ヘルプでバージョン情報を表示すると、このBLUETOOTHアダプタが東芝のOEMだとわかります.


BLUETOOTH設定の新しい接続をクリック.


ウィザードが開きます.次へをクリック.


新しいBLUETOOTHデバイスをサーチした結果が表示されます.HRP-BT1というのが今回製作した回路です.次へをクリック.


接続できそうです.そのまま待ちます.


自動的に画面が切り替わります.次へをクリック.


BLUETOOTH設定画面をみると、HRP-BT1が増えてます.しかしまだ接続された絵になってません.どうやら、登録されただけの状態であるようです.


接続するためにHRP-BT1の絵をダブルクリックしますと、 パスワードを入力せよと促されます.
BLUETOOTHアダプタのパスワードは1234か1111らしいので1234を入力すると正解でした.



HRP-BT1が接続されました.


HRP-BT1を右クリックして、プルダウンメニューの詳細をクリックします.


HRP-BT01が、仮想COM PORTのCOM40に接続されたことがわかります.


Terminal softのTeraTermを起動し、シリアルポート設定を開きます.
設定を下図のようにします.OKをクリック.


以後は、USBによる仮想COM PORTと同じように使えます.



BLUETOOTH基板を使ってみた感想

●USAから輸入したプリント基板を使うのは電波法違反なので実験だけにしておきましょう

●STM8SのUARTのボーレートは115200bpsじゃないと動きませんでした.
これはBLUETOOTH基板の固定仕様であるようです.
したがって、BLUETOOTH基板によってはSTM8Sが9600bpsじゃないとダメっていうこともあると思います.

●BLUETOOTH基板の仕様に合わせて、STM8SのUARTの設定を、8bitのparityナシに変更しました.
これ以外の設定では動きませんでした.

●terminal softのボーレートは、115200でも9600でもなんでもOKでした.BLUETOOTHドライバがボーレートをシカトしているようです.

●STM8SのCOM PORT経由でFLASHを焼く機能はちゃんと使えました.
ただし、あらかじめBLUETOOTHを繋ぎっぱなしにしておく必要がありました(やってみないと意味がかもですが).

●どのくらいの距離まで電波が届くかは実験してません.


ソースコードの解説

サンプルプログラムをこちらに置きます

今回は、ソースコードサイズが大きいので、STVDをこの設定でbuildしました.
ADCのライブラリを使うとサイズが大きくなるようです.
Global Variables = in Data
Program model = STM8 Large model


main.c

#include "stdio.h"
#include "stdlib.h"
#include "string.h"
文字列操作の関数をいろいろ使うので、Cの標準ライブラリをインクルードしておきます.

#include "stm8s.h"
STMicroのライブラリを使うためのインクルードです.

#include "usrlib-uart.h"
UARTを使うための自作のライブラリのためのインクルードです.

void UART_Menu(void);
メニュールーチンのプロトタイプ宣言です.

// address of data on EEPROM  (offset calinration values)
char* EEPROM_OFFSET_AIN0 = (char*)0x4000;
char* EEPROM_OFFSET_AIN1 = (char*)0x4010;
char* EEPROM_OFFSET_AIN2 = (char*)0x4020;
char* EEPROM_OFFSET_AIN3 = (char*)0x4030;
char* EEPROM_OFFSET_AIN4 = (char*)0x4040;
char* EEPROM_OFFSET_AIN5 = (char*)0x4050;
char* EEPROM_OFFSET_AIN6 = (char*)0x4060;
char* EEPROM_OFFSET_AIN7 = (char*)0x4070;
char* EEPROM_OFFSET_AIN8 = (char*)0x4080;
char* EEPROM_OFFSET_AIN9 = (char*)0x4090;
これらは補正値を格納する不揮発性データのアドレスです.
不揮発性データ名をキャラクタポインタで宣言し、それにEEPROMアドレスをあてはめています.

EEPROMは0x4000以降です.
各変数に16BYTEを割り当てます.

// work area
char UARTstrTX[80];
char dump_ascii[4];
char dump_char[17];
Terminal Softへ表示するための文字列をつくるためのワークエリアです.

float offset_AIN[10];
AINxのoffset値のワークエリアです.

int i,k;

void main(void)
{
メイン関数の始まりです.

CLK_ClockSwitchConfig(CLK_SWITCHMODE_AUTO, CLK_SOURCE_HSE, DISABLE, CLK_CURRENTCLOCKSTATE_DISABLE);
外部水晶発振子の16MHzを使います.
時刻をカウントするので周波数精度が高い方がこのましいからです.

TIM3_DeInit();
TIM3_TimeBaseInit(TIM3_PRESCALER_512,3124); // 0.1sec timer
TIM3_ITConfig(TIM3_IT_UPDATE, ENABLE);
TIM3_Cmd(ENABLE);
インジケータLEDをチカチカさせるためのタイマ割り込みの設定です.約0.1Secで割り込みします.
なんで0.1Secになるかというと、16MHz/512/3125=0.1だからです.

 ADC1_DeInit();
一応ADCを初期化してから.

 ADC1_Init(@ADC1_CONVERSIONMODE_CONTINUOUS, AADC1_CHANNEL_9, BADC1_PRESSEL_FCPU_D4, CADC1_EXTTRIG_TIM, DDISABLE, EADC1_ALIGN_RIGHT, FADC1_SCHMITTTRIG_ALL, GDISABLE);
ADCを初期化する引数がたくさんある関数です.
@ADCを連続変換モードにするか、一発変換モードにするかの選択.
_CONTINUOUSは連続変換、_SINGLEは一発変換.
この用途では、_CONTINUOUSにすること.
AADCの入力AINxのどれを変換するかを設定する.
_9だと10個全部._8だとAIN0〜AIN8までの9個を変換._0だとAIN0だけを変換するようになります.
変換しないピンは、普通のデジタルIOピンとして使えると思いますが、未確認です.
BADCへ供給するclock周波数を選択する.下記のどれか.
ADCのclockは1〜4MHzという制限がありますので、適切なfADC1になるように設定します.
ここでは16MHz÷4=4MHzにしています.

ちなみに、FCPUという名前でdefineされているのでfCPUを分周するかのごとく誤解してしまいますが、正しくはfMASTERを分周します.これはSTM8Sのライブラリを書いた人の誤解による誤記と思われます.
fMASTERはこのサンプルプログラムでは16MHzですから、ADC clockを8MHz〜888kHzの範囲で選択できます.
ただし、AD変換レートが8MHz〜888kHzになるわけではありません.
なぜなら、1回のAD変換に14clock必要だからです.
    ADC1_PRESSEL_FCPU_D2  Prescaler selection fADC1 = fMASTER/2.  
    ADC1_PRESSEL_FCPU_D3  Prescaler selection fADC1 = fMASTER/3.  
    ADC1_PRESSEL_FCPU_D4  Prescaler selection fADC1 = fMASTER/4.  
    ADC1_PRESSEL_FCPU_D6  Prescaler selection fADC1 = fMASTER/6.  
    ADC1_PRESSEL_FCPU_D8  Prescaler selection fADC1 = fMASTER/8.  
    ADC1_PRESSEL_FCPU_D10  Prescaler selection fADC1 = fMASTER/10.  
    ADC1_PRESSEL_FCPU_D12  Prescaler selection fADC1 = fMASTER/12.  
    ADC1_PRESSEL_FCPU_D18  Prescaler selection fADC1 = fMASTER/18.  

CDはADC変換のトリガ信号を選択する引数ですが、DがDISABLEになっているので、Cはドントケアでいいでしょう.
EADCは10bitですが、変換結果は16bitで返されます.
なので、左詰にするか右詰にするかをこの引数で選択します.
普通は_RIGHTの右詰でつかいます.
FGはAINxのシュミット特性をオンオフするために使います.
わたしの感触では、シュミット特性がオンでもオフでもAD変換動作には影響ないようです.
しかし問題は、シュミット回路にアナログ信号が入力されると消費電力が増えてしまうことだそうです.
ですから、この機能は、アナログ入力ピンのシュミット特性をオフし、デジタル入力ピンにはシュミット特性をオンしておくべきだという正しい運用をするための引数です.

ADC1_ScanModeCmd(ENABLE);
AINxを一気にスキャンするモードにするおまじないです.
AINxのどこをスキャンするかは、
ADC1_Init()のAADC1_CHANNEL_xで決めます.

ADC1_DataBufferCmd(ENABLE);
AINxを一気にスキャンするモードで、スキャン結果を保存するためのレジスタをオンにするおまじないです.

ADC1_StartConversion();
ここでADCを変換スタートすればOKです.
(実例31 10ch電圧計では割り込みルーチンで毎回この関数をコールしていますが、あれは冗長な操作だったようです.orz)

UART2_DeInit();
UART2_Init((u32)115200, UART2_WORDLENGTH_8D, UART2_STOPBITS_1, UART2_PARITY_NO, UART2_SYNCMODE_CLOCK_DISABLE, UART2_MODE_TXRX_ENABLE);    
UART2_ITConfig(UART2_IT_RXNE_OR, ENABLE);
UART2_Cmd(ENABLE);
UARTを使うための設定です.
BLUETOOTH基板に合わせて、8bitにしてparityナシにしなくちゃ動きませんでした.

GPIO_Init(GPIOD, GPIO_PIN_0, GPIO_MODE_OUT_PP_HIGH_FAST); // LED
LEDが接続されたポートを出力に設定しています.

offset_AIN[0]=atof(EEPROM_OFFSET_AIN0);
offset_AIN[1]=atof(EEPROM_OFFSET_AIN1);
offset_AIN[2]=atof(EEPROM_OFFSET_AIN2);
offset_AIN[3]=atof(EEPROM_OFFSET_AIN3);
offset_AIN[4]=atof(EEPROM_OFFSET_AIN4);
offset_AIN[5]=atof(EEPROM_OFFSET_AIN5);
offset_AIN[6]=atof(EEPROM_OFFSET_AIN6);
offset_AIN[7]=atof(EEPROM_OFFSET_AIN7);
offset_AIN[8]=atof(EEPROM_OFFSET_AIN8);
offset_AIN[9]=atof(EEPROM_OFFSET_AIN9);
EEPROMの補正値は文字列なのでfloatに変換します.あとで使います.

enableInterrupts();
割り込みイネーブル.

SerialPutString("=====start temperature log=====\r\n");
オープニングメッセージを表示します.

while (1){ UART_Menu();    } // Main loop
}
メニュールーチンを永久ループしてメインルーチンを終えます.


u16 adc[10]; // ADC0-9 values
u32 adc_sum[10];  // ADC0-9 sum values
float mV[10]; // ADC0-9 mV values
float temperature[10]; // calculated temperature
char VERBOSE='s';   // silent or verbose
float INTERVAL=1;     // display interval second
u16 cnt=0; // 0-9 to counte 1sec
u16 adc_sum_cnt; // AINx summation times
u32 count_01sec;    // interrupt counter (0.1sec)
u8 now_hour, now_min, now_sec;
ワークエリアをいろいろ宣言します.
スタックメモリ容量が小さなマイコンなので、わたしの好みで変数の多くをこのようにglobal変数定義しています.
一般にソフトウエアテクニックとしてどうなんでしょうか?

void Tim3Update(void) interrupt 15 // 0.1Sec interrupt
{
タイマ3割り込みルーチンです.0.1Secごとにここに飛んできます.

    GPIO_WriteReverse(GPIOD,GPIO_PIN_0); // LED flash
LEDをチカチカさせます.

    count_01sec++;
時刻表示のためのカウンタです.0.1Sec刻みで時刻をカウントする変数です.

    cnt++;
表示インターバルをカウントする0.1Sec刻みのカウンタです.

    if(cnt>(10*INTERVAL)){
        cnt=0; // 1sec counter
INTERVALはデフォルトで1Secになっています.
なので、cnt==10になったら1Sec経過したことになりますので、以下の表示ルーチンに入ってゆきます.


        // calculations of voltage and temperature
        for(i=0;i<=9;i++)
        {
            mV[i] = (float)adc_sum[i]/(float)adc_sum_cnt/1024.0*3333;
            temperature[i] = ( mV[i] - 850 ) / 10 + 25 + offset_AIN[i];
        }
AD変換値を、電圧変数であるmVに変換し、さらに温度変数であるtemperatureに変換します.
配列はAIN0〜AIN9を意味します.
ここでadc_sumという変数が唐突に出てきていますが、裏で0.1Secの割り込みごとにAD変換値を積算しているんです.
積算する理由は、ノイズを減らすためです.
adc_sumが積算値で、adc_sum_cntが積算回数です.
ADCは10bitです.かつフルスケールで3.3Vです.なのでmV=ADC/1024x3333で電圧値に変換します.
3300でも良かったかもですが....
温度に変換するのは、25度で850mVで、10mV/度ですので、上の数式になっています.

        // current time to display
        now_hour= (u8)((count_01sec/36000) % 24);
        now_min = (u8)((count_01sec/600) % 60);
        now_sec = (u8)((count_01sec/10) % 60);
        sprintf(UARTstrTX,"%02d:%02d:%02d ",(int)now_hour,(int)now_min,(int)now_sec);
        SerialPutString(UARTstrTX);
時刻を表示します.
0.1Sec刻みでcount_01secがカウントしていますので、時分秒に変換してからUARTに送信します.

        for(i=0;i<=9;i++)
        {
            sprintf(UARTstrTX,"%d:%2.1f ",i,temperature[i]);
            SerialPutString(UARTstrTX);
        }
        SerialPutString("\r\n");
温度を10チャンネル分表示します.

        if(VERBOSE=='v')
        {
            SerialPutString("ADC(0-9)=");
            for(i=0;i<=9;i++)
            {
                sprintf(UARTstrTX,"%d:%d ",i,(int)((float)adc_sum[i]/(float)adc_sum_cnt));
                SerialPutString(UARTstrTX);
            }
            SerialPutString("\r\n");
            SerialPutString("mV(0-9)=");
            for(i=0;i<=9;i++)
            {
                sprintf(UARTstrTX,"%d:%d ",i,(int)mV[i]);
                SerialPutString(UARTstrTX);
            }
            SerialPutString("\r\n");
        }
verboseモードの場合はVERBOSEという変数に”v”の文字が入っています.
ADC値とmV値を表示します.


        // prepair for next turn
        adc_sum_cnt=0;
        for(i=0;i<=9;i++){ adc_sum[i]=0; }
    }
表示インターバルごとの表示処理はこれで終わりです.
次の表示インターバルの準備のために、変数を初期化しています.


    else {
        adc_sum_cnt++;
        for(i=0;i<=9;i++)
        {
            adc[i]=ADC1_GetBufferValue(i);
            adc_sum[i]+=adc[i];
        }
        if(VERBOSE=='v'){
            SerialPutString("ADC(0-9)=");
            for(i=0;i<=9;i++){
                sprintf(UARTstrTX,"%d:%4d ",i,adc[i]);
                SerialPutString(UARTstrTX);
            }
            SerialPutString("\r\n");
        }
    }
このelse以降は、表示インターバルではない、裏の処理です.
裏の処理では、0.1Secごとに、ADC値を積算しています.
ADC1_GetBufferValue(i) がADC値を取ってくる関数です.
verboseモードではADC値も表示します.


    TIM3_ClearFlag(TIM3_FLAG_UPDATE);
}
次回のタイマ3割り込みのために、割り込みフラグをクリアしておきます.
これで、タイマ3割り込み処理ルーチンはおしまいです.



void UART_Menu(void)
{
メニュー処理ルーチンの始まり.

int fieldnumber;
Terminalへ打ち込まれたコマンドラインの単語の数が入る変数です.

if(UART_STR_EXIST==1) // there is a command line
  {
UARTの処理ルーチンから、コマンドを1行受信するとUART_STR_EXISTフラグがセットされます.

     fieldnumber = separate_line(command);
separate_line()を呼ぶと、コマンドラインを単語ごとに分離してくれます.
単語が2つだったら、2を返します.

単語はField[]に入ります.


        // verbose mode or not
        if(@strcmp(Field[0],"v")==0 && Afieldnumber==1)
        {
            A(VERBOSE=='v') ? (VERBOSE='s') : (VERBOSE='v');
        }

コマンドvを処理するところです.
@最初の単語=vで、単語の数が1つであることが条件です.
A変数VERBOSEはvとsを交互にスイッチさせます.
ここでvがセットさせると、タイマ3割り込みルーチンで詳細に表示します.

        // set interval second
        else if(strcmp(Field[0],"int")==0 && fieldnumber==2)
        {
            if(atof(Field[1])<0.1)goto NG;
            else if(atof(Field[1])>60)goto NG;
            else INTERVAL = atof(Field[1]);
        }
表示インターバル時間をセットするコマンドintの処理をするところです.
コマンド名=intで、単語数=2であることが条件です.
さらに、2nd引数が0.1未満ならNGとします.60超ならNGとします.
最期に、変数INTERVALに引数をfloat変換して代入します.

        // set date and time
        else if(strcmp(Field[0],"time")==0 && fieldnumber==3)
        {
            u8 hour, min;
            hour = (int)atof(Field[1]);
            min  = (int)atof(Field[2]);
            count_01sec = (u32)hour*60*60*10 + (u32)min*60*10;
        }
時刻をセットするコマンドtimeの処理をするところです.
コマンド名=timeで、単語数=3であることが条件です.
0.1Sec刻みのカウンタに時刻を代入します.
カウント操作はタイマ3割り込みで実行されます.

        // set offset values
        else if(strcmp(Field[0],"ofs")==0 && fieldnumber==3)
        {
            int AINx;
            AINx = (int)atof(Field[1]);
            if(AINx<0 || 9<AINx) goto NG; // check AINx
            else
            {
                @FLASH_Unlock(FLASH_MEMTYPE_DATA);
                for(i=0;i<=15;i++)
                {
                    AFLASH_EraseByte(0x4000+AINx*16+i);
                    BFLASH_ProgramByte(0x4000+AINx*16+i,(u8)Field[2][i]);
                }
                CFLASH_Lock(FLASH_MEMTYPE_DATA);
                offset_AIN[AINx]=atof(Field[2]);
            }
        }
補正値を設定するコマンドofsを処理するところです.
コマンド名=ofsで、単語数=3であることが条件です.
2nd引数=AINxを指定し、3rd引数=補正値です.
AINxが0〜9でなければNGです.
@EEPROMへの書き込みロックを解除します.
AEEPROMを1BYTE消します.
BEEPROMに1BYTE書きます.
CEEPROMを書き込みロックします.
最後にoffset_AIN変数に3rd引数を代入します.

EEPROM上の格納のされ方は、次のように文字列の15文字です.
領域は16文字確保してありますが、末尾は00ですので、文字数は15文字です.
+0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +A +B +C +D +E +F
41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 00  | ABCDEFGHIJKLMNO.

        // dump EEPROM
        else if(strcmp(Field[0],"dump")==0 && fieldnumber==1)
        {
            @disableInterrupts();
            SerialPutString("\r\n");
            for(i=0;i<=9;i++)
            {
                UARTstrTX[0]=0; // null string
                dump_char[16]=0;
                for(k=0;k<=15;k++)
                {
                    uint8_t a;
                    Aa=FLASH_ReadByte(0x4000+16*i+k);
                    sprintf(dump_ascii,"%02x ",a);
                    strcat(UARTstrTX,dump_ascii);
                    if(a<32)a=46;
                    if(a==0x7F)a=46;
                    if((a&0xF0)==0x80)a=46;
                    if((a&0xF0)==0x90)a=46;
                    if((a&0xF0)==0xE0)a=46;
                    if((a&0xF0)==0xF0)a=46;
                    dump_char[k]=a;
                }
                SerialPutString(UARTstrTX);
                SerialPutString(" | ");
                SerialPutString(dump_char);
                SerialPutString("\r\n");
            }
          BenableInterrupts();
        }
EEPROMを表示するコマンドdumpを処理するところです.
@dump中にタイマ3割り込みによる表示が邪魔するので、一旦割り込みを禁止します.
AEEPROMから1BYTE読むには、
FLASH_ReadByte() 関数を使います.
表示の詳細は省きます.
Bdumpが終わったら割り込みを許可します.

        // clear EEPROM
        else if(strcmp(Field[0],"clr")==0 && fieldnumber==1)
        {
            disableInterrupts();
            SerialPutString("\r\n");
            FLASH_Unlock(FLASH_MEMTYPE_DATA);
            for(i=0;i<=0x9f;i++) FLASH_EraseByte(0x4000+i);
            FLASH_Lock(FLASH_MEMTYPE_DATA);
            for(i=0;i<=9;i++)offset_AIN[i]=0;
          enableInterrupts();
        }
EEPROMをクリアするコマンドclrを処理するところです.
0x4000〜0x409FまでをFLASH_Erase()しています.

        else
        {
            NG:
            SerialPutString("\r\nCOMMAND NG !!!\r\n\n");
            SerialPutString("time 21 22 --> set time hour/min\r\n");
            SerialPutString("int 3      --> interval time 3 seconds\r\n");
            SerialPutString("ofs 4 -0.5 --> set AIN4 offset to -0.5\r\n");
            SerialPutString("v          --> switch verbose <--> silent\r\n");
            SerialPutString("dump       --> dump EEPROM\r\n");
            SerialPutString("clr        --> clear EEPROM\r\n");
        }
該当するコマンドがなかった場合は、コマンドNGと表示します.メニューを表示します.

        UART_STR_EXIST=0;
フラグをクリアします.
    }
}



usrlib_uart.h

UARTを使うための自作ライブラリです.
普段はあまりいじらないのですが、今回は、単語の文字数を15文字に制限しないと不揮発性データ領域の内容を破壊しますので、文字数を15文字に制限する役目をここで担っています.
下記の、FIELD_CHAR 16というところが制限です.末尾のNULL文字を含めて16BYTEの文字列領域を確保します.
もしも16文字以上だと、その単語は無視されますので、トラブルを防いでいます.

void SerialPutChar(char c);
void SerialPutString(char *s);
int separate_line(char *tmp);
void UART2RX_isr(void);

#define FIELD_NUM 4   // 4 words in a line
#define FIELD_CHAR 16  // 15 characters in a word

extern char Field[FIELD_NUM][FIELD_CHAR];
extern char command[FIELD_NUM*FIELD_CHAR];
extern u8 UART_STR_EXIST;
extern int bytes_read;




inserted by FC2 system