プログラム

mbedの画面です。日本語が使えるようにBetaモードで使っています。方法は「mbed 日本語」でググると見つかります。
http://developer.mbed.org/users/weed/notebook/how_to_use_mbed_site_in_Japanese/
インターネットエクスプローラでは日本語は使えないのでgoogle Chromeを使用しています。
Proguramu workspaceウィンドウに表示されているScaleSpeedが今回のプロジェクトです。
TextLCDはライブラリからインポートした物です。インポートのやり方は製作例:液晶表示器とスイッチをご覧ください。

アセンブラ言語でプログラムをコーディングしていた頃はメインルーチンとサブルーチンでしたが、C言語ではメインルーチンはメイン関数、サブルーチンは単に関数となります。
関数を呼び出す(飛ぶ)時には引数と戻り値という値が用いられています。
書式は戻り値 = 関数名(引数);です。
引数は呼び出す関数に値を渡す事が出来ます。
昔々は変数がメモリのどこに記憶されているかで扱いが変わり、非常に大変でしたが、引数により相手の関数に必要な値を渡す事でメモリの配置を意識する必要が無くなりました。
戻り値は関数で処理された結果を値で返す値という事になります。
グローバル変数は割込みや複数の関数で判断などに必要な変数のみとなり、大変読み安くなりました。
C++は簡単に言えばC言語に名前空間を加えた物です。
名前空間とは沢山の関数をグループにまとめて名前を付けたような物でしょう。
今回のプログラムではSerialという名前空間でPCのCOMポート通信に必要な関数を利用しています。
更にTextLCDという名前空間から液晶ディスプレイに文字を表示する関数を利用しています。
これらはライブラリと呼ぶ沢山の関数を記述したファイルに収められています。
私たちは自分で液晶表示器に文字を表示するプログラムを記述しなくても、ライブラリを利用する事で簡単に文字表示が可能になりました。
mbedにも沢山のライブラリが用意され自由に使用する事が出来ます。
putcという関数名はSerialのライブラリにもTextLCDのライブラリにも存在します。このように同じ関数名が複数のライブラリに存在しても困らないように
名前空間という考え方が生まれたようです。名前空間を指定すれば同じ関数名でも問題は起こりません。

C言語やC++について詳しく記述しませんが書籍が豊富に出版されていますので興味がある方は手に取ってみては如何でしょうか。
私は「ネコでもわかるC言語プログラミング」や「ネコでもわかるC++プログラミング」を読んで理解しましたが、人により参考になる本は違うと思います。
また、C言語はパソコンのアプリケーション開発に使われてる言語ですから、マイコンボードをプログラミングするC言語とは少し違います。
これら組込用のC言語入門は「C言語によるPICプログラミング」が参考になりました。マイコンが違うので20%程度しか参考になりませんが、組込C言語を理解するには十分でした。
以下にプログラムを記します。

 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
//***********************************************************************
//
//    スケールスピードメータプログラム
//    
//    Projyect name     : ScaleSpeed
//    Date                      : 2015.04.08
//    Target                   : FRDM-KL25Z
//   
//    仕様
//    Start swich           : 計測開始ボタン
//    Break switch        : 計測中止ボタン
//    Distance switch   : 計測距離設定ボタン
//    Scale switch         : 縮尺切替ボタン
//
//    Start : LED on green
//    Senser1 : LED on Yelow
//    Senser2 : LED on Blue
//   
//************************************************************************
#include "mbed.h"
#include "TextLCD.h"
#define ON 0
#define OFF 1

Serial pc(USBTX, USBRX);                    
// USB COMポート設定
TextLCD lcd(A0, A1, A2, A3, A4, A5);   
// TextLCD設定
Timer timer;                                                
// タイマー計測初期化
InterruptIn int1(D12);                               
// 割込みピン設定(センサー1)
InterruptIn int2(D11);                               
// 割込みピン設定(センサー2)

/* 三色LEDポート設定 */

DigitalOut greenLed(LED_GREEN);   
 // 緑
DigitalOut redLed(LED_RED);              
// 赤
DigitalOut blueLed(LED_BLUE);        
 // 青


/* スイッチポート設定 */
DigitalIn startSW(D2);
DigitalIn breakSW(D3);
DigitalIn distanceSW(D4);
DigitalIn scaleSW(D5);
DigitalIn sence1(D12);
DigitalIn sence2(D11);

/* グローバル定義 */
bool countFlag;      // 計測中フラッグ
bool endFlag;         // 計測完了フラッグ
bool changeFlag;   // 変化フラッグ

uint8_t charCounter;     // 割込み文字カウンタ
mbed 日本語リファレンス(私家版)を参考にプログラムを書いてみました。
 https://developer.mbed.org/users/okini3939/notebook/ref_jp/

















20:インクルードするヘッダファイルはmbed.hとインポートしたTextLCD.hです。
  mbedのライブラリは全てヘッダファイルとプログラムファイルで構成されています。
22:#defineはLEDのON/OFFが分かりやすいように定義しました。

24:PCのCOMポートと通信する為の設定です。名前はpcです。
25:TextLCDライブラリの初期設定になります。名前はlcdとしましたのでlcd..と関数名の前に記された関数はTextLCDライブラリの関数を意味します。引数のA0〜A5は液晶ディスプレイに
  接続するポートを指定しています。RS,E,D4,D5,D6,D7
   

26:Timer計測関数を使用するので初期化します。名前はtimerです。
27:光センサ入力ポートを割込みポートに設定します。名前はint1とint2です。
31:LED接続ポートを出力に設定し、名前をつけます。


37:タクトスイッチを接続したポートを入力に設定し、名前をつけます。




41:光センサを接続したポートを入力に設定し、名前をつけます。



45:グローバル変数はタイマ計測中1となるcountFlag、計測終了すると1になるendFlag、
    タクトスイッチを押すなど表示書替が必要な時に1になるchangeFlagです。

49:デバッグ用にPCからのキーボード入力で割込みが発生するようにしているので、
    入力された文字バッファの数を格納するレジスタです。
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
 //*******************************************************
// Serial受信割り込みベクタ関数(キーボード割込み)
//*******************************************************

void
pcRx(){
         char c;
         c = pc.getc();
         
if (c == 0xa0) charCounter = 0;
         
else{
             lcd.locate(charCounter,1);
             lcd.putc(c);
             charCounter++;
             if (charCounter > 10) charCounter = 0;
         }
}
PCとマイコンボード間でCOM通信を設定するとプログラムの特定の場所から変数値をPCに送ったり、
プログラムを途中で止める事が出来ると思い、入れてあります。まだ使っていません。
PCのキーボードを叩くと液晶に文字が表示されるようにコーディングしてあります。

54:割り込みが発生したらこの関数を呼び出すように設定してあります。

56:COMポートバッファの文字を取得します。
57:改行だったら文字カウンタをクリアします。
58:それ以外は
59:液晶表示器の2行目の文字カウンタの位置に移動し、
60:1文字表示します。
61:文字カウンタのインクリメント。
62:文字カウンタが10文字を超えたら文字カウンタをリセットします。
 
66
67
68
69
70
71
72
73
74
75
 //*******************************************************
// センサー1割込み関数
//*******************************************************
void senser1(){
       
 if (countFlag){
             timer.start();
             redLed =
0;
        }
}



69:光センサ1の入力ポートに割込みが発生したらこの関数を呼び出すように設定ししてあります。
70:スタートボタンが押され、計測中であれば
71:タイマー計測を開始します。
72:赤LEDを点灯します。

77
78
79
80
81
82
83
84
85
86
87
88
//*******************************************************
// センサー2割込み関数
//*******************************************************

void
senser2(){
      
if (countFlag){
          timer.stop();
          redLed =
1;
          countFlag =
0;
          endFlag =
1;
          changeFlag =
1;
      }
}



80:光センサ1の入力ポートに割込みが発生したらこの関数を呼び出すように設定ししてあります。
81:計測中であれば
82:タイマー計測を停止し、
83:赤LEDを消灯し、
84:計測中フラグをクリアし、
85:終了フラグをセットし、
86:変化フラグをセットします。
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
//*******************************************************
// Distance設定関数
//*******************************************************

uint32_t
distanceConfig(int counter){

        
/* Print distance */
        lcd.locate(
2,0);
        
switch(counter){
       
 case 0:
                lcd.printf(
"10mm");
                
return 10000;
        
case 1:
               lcd.printf(
"50mm");
               
return 50000;
        
case 2:
               lcd.printf(
"10cm");
               
return 100000;
        
case 3:
               lcd.printf(
"50cm");
               
return 500000;
        
case 4:
              lcd.printf(
"1.0m");
              
return 1000000;
        
case 5:
              lcd.printf(
"5.0m");
              
return 5000000;
        
default:
              
return 0;
        }
}
計測距離設定関数です。引数は計測距離設定番号。戻り値は距離値です。





95:液晶の文字位置をイ行目の3カラム目に設定します。
96:計測距離設定番号で分岐します。
97:0の時は、
98:10mmと表示し、
99:10000を戻り値として戻る。
100:の時は、
101:50mmと表示し、
102:50000を戻り値として戻る。
103:2の時は、
104:10cmと表示し、
105:100000を戻り値として戻る。
106:3の時は、
107:50cmと表示し、
108:500000を戻り値として戻る。
109:4の時は、
110:1.0mと表示し、
111:1000000を戻り値として戻る。
112:5の時は、
113:5.0mと表示し、
114:5000000を戻り値として戻る。
115:それ以外は
116:0を戻り値としてもどる。
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
//*******************************************************
// LCD表示関数
//*******************************************************

uint16_t
scaleConfig(int counter){

       
 /* Print scale */
        lcd.locate
(11,0);
       
switch(counter){
        
case 0:
                lcd.printf(
"1/150");
                
return 150;
        
case 1:
                lcd.printf(
"1/120");
                
return 120;
        
case 2:
                lcd.printf(
"1/100");
                
return 100;
        
case 3:
                lcd.printf(
"1/87 ");
                
return 87;
        
case 4:
                lcd.printf(
"1/76 ");
                
return 76;
        
case 5:
                lcd.printf(
"1/48 ");
                
return 48;
        
case 6:
                lcd.printf(
"1/35 ");
                
return 35;
        
case 7:
                lcd.printf(
"1/30 ");
                
return 30;
       
 case 8:
                lcd.printf(
"1/24 ");
                
return 24;
        
case 9:
                lcd.printf(
"1/20 ");
                
return 20;
        
case 10:
                lcd.printf(
"1/16 ");
                
return 16;
        
case 11:
                lcd.printf(
"1/12 ");
                
return 12;
        
case 12:
                lcd.printf(
"1/10 ");
                
return 10;
        
case 13:
                lcd.printf(
"1/8 ");
                
return 8;
       
 case 14:
                lcd.printf(
"1/6 ");
                
return 6;
        
case 15:
                lcd.printf(
"1/4 ");
                
return 4;
        
case 16:
                lcd.printf(
"1/2 ");
                
return 2;
        
case 17:
                lcd.printf(
"1/1 ");
                
return 1;
        
default:
                
return 0;
        }
}
縮尺設定関数です。引数は縮尺設定番号。戻り値は縮尺値です。
引数に対応した縮尺を表示して縮尺値を戻り値にして戻ります。
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
//*******************************************************
// メイン関数
//*******************************************************
int main() {
        
bool startFlag = 0;
       
 bool breakFlag = 0;
        
bool distanceFlag = 0;
        
bool scaleFlag = 0;

        
uint8_t scaleCounter = 0;         // 縮尺設定番号
        
uint8_t distanceCounter = 0;   // 計測距離設定番号

        
uint32_t distance = 0;     // 距離(mm)
        
uint16_t scale = 0;            // 縮尺比
        
uint32_t speedSec;           // 秒速値
        
uint32_t speedScale;       // 縮尺変換秒速値
        
uint16_t speed;                 // 速度データ(km/h)

        redLed =
1;
        greenLed =
1;
        blueLed =
1;
メイン関数です。
処理の流れは以下です。
        初期設置
        メインループ
        待機モード
        計測中モード
        計時中モード
        計時完了モード
        繰り返し

191:メイン関数で使用する変数を設定します。
205:三色LEDをOFFに設定します。

countFlagが0なので待機モードではじまります。
209
210
211
212
213
214
215
216
        /* Print start */
        wait(
0.5);
        lcd.printf(
" FRDM-KL25Z");
        wait(0.5);
        pc.printf(
"Start!\r\n");
        charCounter =
0;
        wait(
1);
液晶に初期画面を表示します。
210:0.5秒待機します。wait関数はmbedライブラリの関数です。
211:液晶にFRDM-KL25Zと表示します。
212:0.5秒待機します。
213:PCのCOMポートにSTARTを送信し、改行して先頭にカーソルを移動します。
214:受信文字カウンタをクリアします。
215:1秒間待機します。
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
         /* Print LCD fixed charactor */
        lcd.cls();
        lcd.locate(
0,0);
        lcd.printf(
"D=");
        lcd.locate(
7,0);
        lcd.printf(
"SCL=");
        lcd.locate(
12,1);
        lcd.printf(
"km/h");
        lcd.locate(
0,1);
        lcd.printf(
"1: 2:");
        changeFlag =
1;

        
/* Enable interrupt */
        int1.fall(&senser1);
        int2.fall(&senser2);
初期画面をクリアして固定文字を表示します。
218:液晶全画面消去。
219:カーソルを1行目の先頭に移動。
220:文字列を表示








ポート入力割込みを有効にします。
230:int1はD12ポートです。Fallで立下り割込み発生を許可し、senser1関数に飛びます。
231:int2はD11ぽーとです。同様にsenser2関数に飛びます。
233
234
235
236
237
238
239
240
        //*******************************************************
        // メインループ
        //*******************************************************

        while
(1) {

                /* Recieve serial */
                pc.attach(pcRx,Serial::RxIrq);
メインループ
リセットされるまで永久に繰り返すプログラムです。

236:引数が0になるまで繰り返す。

238:受信割り込み設定
239:PCからのCOM通信で受信したらRxIrq関数に飛ぶ。
241
242
243
244
245
246
247
248
249
250
251
252
253
254
                /* Push start switch */
                
if (!startSW && !startFlag){
                        wait_ms(10); // チャタリング防止
                        
if (!startSW){
                                changeFlag = 1;
                                startFlag = 1;
                                endFlag = 0;
                                breakFlag = 0;
                                countFlag = 1;
                                timer.reset();
                        }
                }
                
else if (startFlag) startFlag = 0;
タクトスイッチについて処理します。チャタリング防止のため10mS後にもう一度スイッチの状態を確認し、まだ押されていれば受け付けます。
受け付けたらstartフラグが1になるので、スイッチを一度離すまで受けつかないようになります。
こうしないとスイッチを押している間中スタート受付処理を繰り返してしまう事になります。
changeフラグは変化フラグでモードが変化した事を示すフラグです。
countフラグを1にセッとしたので計測中モードに移行します。
breakフラッグはbreakスイッチを受け付け可能にする為クリアします。
250:時間計時用タイマをリセットします。
253:スイッチが一度離されたらstartフラグをクリアしてstartスイッチを受付可能にします。
255
256
257
258
259
260
261
262
263
264
265
266
267
268
        /* Push break switch */
              
if (!breakSW && !breakFlag){
                      wait_ms(
10);
                      
if (!breakSW){
                              changeFlag =
1;
                              breakFlag =
1;
                              countFlag =
0;
                              timer.stop();
                              speed =
0;
                              redLed =
1;
                      }
              }
             
 else if (breakSW) breakFlag = 0;
ブレークスイッチについて処理します。スイッチの動作は上記と同様です。
ブレークスイッチを受け付けるとcountフラグをクリアしますので、計測中モードから
待機モードに移行します。
プログラムの冒頭で#define OFF 1と記していますので
264:        redLed = OFF;
と記述しても赤LEDをOFFにする事が出来ます。
269
270
271
272
273
274
275
276
277
278
279
280
                /* Push scale switch */
                
if(!scaleSW && !scaleFlag){
                        wait_ms(
10);
                        
if (!scaleSW){
                                changeFlag =
1;
                                scaleFlag =
1;
                                scaleCounter++;
                                
if (scaleCounter > 17) scaleCounter = 0;
                        }
                }
                
else if (scaleSW) scaleFlag = 0;
スケールスイッチについて処理します。スイッチの動作は上記と同じです。
縮尺を選択します。押す度にscaleounterがインクリメントされます。scaleConfig関数でscaleCounterの値により尺度が設定され、SCL=1/150などと液晶に表示されます。
281
282
283
284
285
286
287
288
289
290
291
292
                /* Push distance switch */
                
if(!distanceSW && !distanceFlag){
                        wait_ms(
10);
                        
if (!distanceSW){
                                changeFlag =
1;
                                distanceFlag =
1;
                                distanceCounter++;
                                if (distanceCounter >
5) distanceCounter = 0;
}                        
                }
                
else if (distanceSW) distanceFlag = 0;
ディスタンススイッチについて処理します。スイッチ動作は上記と同じです。
計測センサー間の距離を設定します。押す度にdistanceCounterがインクリメントされます。distanceConfig関数でdistanceCounterの値により距離が設定され、D=10mmなどと液晶に表示されます。
293
294
295
596
297
298
299
300
301
302
                /* sence1 */
                lcd.locate(
2,1);
                
if (sence1) lcd.printf("H");
                
else lcd.printf("L");

                
/* sence2 */
                lcd.locate(
6,1);
                
if (sence2) lcd.printf("H");
                
else lcd.printf("L");

光センサー1,2のそれぞれの状態を表示します。
光センサーに光が当たっていればHと表示、当たっていなければLと表示します。
光センサーとレーザー発光モジュールを設置する際、適切に受光している事を確認する為の表示です。
303
304
305
306
307
308
309
                /* Set LED */
                
if (countFlag) greenLed =0;
                
else greenLed = 1;

                
if (endFlag) blueLed = 0;
                
else blueLed = 1;

LEDをモードにより点灯を切り替えます。
304:計測中モードでは緑LEDが点灯します。
307:計測終了モードでは青LEDが点灯します。
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
                /* 速度計算と表示 */
                
if (changeFlag){
                        changeFlag =
0;

                        
/* 距離設定 */
                        distance = distanceConfig(distanceCounter);

                        
/* 縮尺設定 */
                        scale = scaleConfig(scaleCounter);

                        
/* 速度計算 */
                        speedSec = distance / timer.read_ms();         
// 速度(mm/sec) = 距離 / 時間
                        speedScale = speedSec * scale;                       
// 縮尺速度 = 秒速 × 縮尺
                        speed = (speedScale *
36) / 10000; // 時速km/h = 縮尺速度 × 3600sec(1h) / 1000000

                        
/* 速度表示 */
                        lcd.locate(
8,1);
                        
if (speed < 10000) lcd.printf("%4d",speed);
                        
else lcd.printf("----");
                        pc.printf(
"%d",speed);         // For debug
                }
        }
}
/* End of main */
変化が1になったら表示を更新します。
315:距離設定、縮尺設定を表示します。
321:速度を計算します。

327:printf関数はC言語標準ライブラリに準拠していますので変換形式を指定できます。
 %d:10進数で出力する。
 %c:1文字として出力する。
 %x:16進数で出力する。
 %o:8進数で出力する。
 %f:±000.00など浮動小数点型で出力します。
 %e:指数形式で出力します。
 %s:文字列として出力します。
この他エスケープシーケンスも使用する事が出来ます。

BACK NEXT