そもそもターゲットマイコンが何であるか、開発環境がどんなものか、わからないのですが、それを示されても私が調べられるとは限りません(苦笑)。いろいろ推測を交えて書きます。
time.hをインクルードして使うclock関数を用いて
それを7セグに出力する、という形になるのでしょうか?
Linuxが動作するようなマイコンなら当然 clock() も使えるでしょう。
しかし、開発環境に依存することですが、7セグLEDが付くマイコンは小規模なことが多く、OSどころかそのような関数群は用意されていないことが多いです。私が経験したマイコンのC開発環境は、デフォルトでは printf() も使えませんでした。そのマイコンにとってどこが標準出力なのか、ということから考えなきゃいけない。
パソコンの開発環境とは違いますので、clock() 関数が用意されていない可能性は高いと思います。
ではどうするか。マイコンには様々な周辺機能が内蔵されていて、タイマ回路もそのひとつです。そのタイマを使って時間を計測するのがマイコンではスタンダードです。
どんな計算機にも一定の周波数のクロック信号がありますね。タイマの基本動作はクロックの波の数を数えることです。タイマに設定した数だけ波を数えたら目的の時間が経過した、ということですから、その時点でフラグが立ったりCPUに割込みをかけたりします。
因みに、この「フラグ」とは、プログラムの中に設けるフラグ変数ではなく、内蔵タイマが持っているレジスタの中の、ある1ビットのことです。最初から存在しています。ハードウェアマニュアルに説明があります。
C言語は初心者ですが
mbedマイコンと7セグメントLEDを用いたストップウォッチを作成しよう
7セグLEDとスイッチがあるなら、作るものはプログラムだけですが、C言語を理解するだけで作れると思ったら大間違い。ハードウェアを理解することが必須です。
ターゲットのハードウェアを理解せずに、目的の動作をするようなプログラムを書くことはできません。内蔵タイマなら、どう使えば目的の時間を測れるか、どんな値を設定すればよいか、タイマのハードウェア的な動作が見えてこないと考えようがありません。7セグLEDもスイッチも然り。
マイコンボードのハードウェアについて調べるべき基本的な資料が2種類あります。
- マイコン内部(CPU、クロック、タイマ等内蔵周辺機能)はハードウェアマニュアル
- マイコン外部(7セグLED、スイッチ、水晶発振子等)はターゲットボードの回路図
内蔵タイマを適切に初期化して、5msec. という時間を測れることがストップウォッチ実現の重要な構成要素になりそうです。そのために次の2点を明確にすることが大事です。
- タイマをどう初期化するか、各種設定値はいくつか
- 時間経過をフラグで監視するのか割込みを使うのか、おおまかに決める
で、今頃になって回答しようと思ったのは質問者が提示したコードを見て気づいたことがあるからです。
7セグLEDは4桁あるのではありませんか。推測にすぎないと言われればそれまでですが。
- a(p10)〜dp(p17) は、4桁ある7セグメントLEDにつながるポート
これは質問者が理解した通り、表示する字形を指定するものです。この8本の信号は4桁ある7セグLEDを並列につないで、全く同じ信号を伝えます。それだと、4桁とも同じ数字しか表示できないのでは、という疑問に思うでしょう。ところが
- dig1(p21)〜dig4(p24)は、4桁ある7セグメントLEDの、どの桁(digit)を光らせるかを指定するポート
であろう、これを用いて4桁それぞれ異なる数字を表示することができます。ダイナミック点灯と呼ぶ、これもスタンダードな方法です。ダイナミック点灯は
- dig1〜dig4 を全てオフにし、一旦全桁の表示を消す
- ある桁に表示する数字パターンをa(p10)〜dp(p17) にセットする
- その時点で表示する桁(dig?)だけをオンにし、その桁だけ数字を表示する
- 一定時間(この場合は5msec.以内)が経過するまで、そのまま放置
- 次の桁を表示するため 1.に戻る
という繰り返しです。一瞬だけなら一桁しか点灯していませんが、これを 20msec. 以下の周期で繰り返すと人間の目には残像として4桁の異なる数字が見えます。4桁なので5msec.毎に切り替えます。
質問者のコードは
C
1 int cs = dig4; // 0.01秒の桁?読み出し centi-second
2 int ds = dig3; // 0.1秒の桁?、読み出し deci-second
3 int s = dig2; // 秒の桁?、読み出し
4 int m = dig1; // 分の桁?、読み出し
と、全て読み出していますが、dig1〜dig4には出力しなければなりませんから、誤りだと思います。
ただし、いきなり4桁のダイナミック点灯を動かそうとするのは無謀です。ひと桁だけ表示する、全桁同じ数字を表示する、など比較的簡単な動作確認を済ませて、
タイマで5msec.を計測できることも確認したうえで、ようやくダイナミック点灯に挑戦できます。
もしかすると、開発環境にダイナミック点灯を実現しやすくするライブラリ関数が用意されているかもしれません。これも調べてみたら良いと思います。
0.001秒ごとに1ずつ値を増やしていって10や60といった決まった値になったら繰り上げる、
というのを繰り返したらいい感じのができそうだ
基本的な考え方はその通りです。ただし 0.001 秒は細かすぎます。1msec.毎に数字が変化しても人間の目では読み取れませんし4桁の7セグLEDに 0.01秒=10msec.単位で表示するのではありませんか。
なお、ある状態を一定時間保持する・一定時間毎に変化する・繰り返す、というようなプログラミングが初心者は苦手なものです。C言語の練習を兼ねて、パソコン上で clock(), sleep() 等を使って、表示が変わるような簡単なプログラムを、1秒単位のゆっくりした動作で構わないので、経験しておくと役に立ちます。
止め方がわからなくなったり
mysw(p9) につながったスイッチを読みだして、
- スイッチが押されたらカウントを開始する
- その後、スイッチが押されたらカウントアップを止める
ようにプログラムすれば良いだけだと思いますが。
スイッチの場合、チャタリング除去が必要であることを知っておきましょう。ハードウェア(スイッチ回路)がチャタリングを除去してくれる場合もありますし(その場合は読みだすだけで良いので簡単)、ソフトウェアで除去しなければならない場合もあります(やや面倒)。回路図を読めば見当がつくかもしれません。
もうひとつ、正論理・負論理について注意を。
- 「オンが1、オフが0」となるポートを正論理(のポート)
- 「オンが0、オフが1」となるポートを負論理(のポート)
と呼びます。たとえば
- スイッチを押した時、そのポートを読んだ値は0か1か
- LEDが点灯するのは0を出力したときか1を出力したときか
どちらであるかはマイコンボードの回路図があればわかります。これもハードウェアを理解できるかどうか、です。わからなければプログラムを作って確かめるという手もありますが。
ストップウォッチはマイコンで作る比較的簡単なアプリケーションですが、それでも
- いくつかの構成要素に分解し
- それぞれの動作確認を、段階を経ながら進めていき
- 最後にストップウォッチ全体として完成させる
ことになります。何かすると一発でストップウォッチが動く、なんてことはありませんから。
Enjoy!
長々書いたけど、マイコンの一般論ということで orz
罪滅ぼしに、次のような前提で、即席のストップウォッチを書いてみました。何の検証もしていませんが、ご参考まで。
- wait(0.005); で 5msec.時間待ちする
- dig1 = 1; のように、表示桁を指定できる
- if (mysw == 1) でスイッチが押されたか判定できる
- スイッチは1個、押すたび カウントアップ→停止(表示)→リセット・・・状態を繰り返す
- スイッチのチャタリング対策、というか、押しっぱなしに見えないように、delayカウンタ値が少ない期間はスイッチを見ないようにする(要調整)
- --各ポートは正論理なのだろう~~dig1〜dig4は負論理だった!
C
1// ダイナミック点灯。ひと桁表示を更新
2void out7segled(int num, int digit)
3{
4 dig1 = dig2 = dig3 = dig4 = 1; // いったん全桁消灯(負論理)
5
6 switch (num) { // 数字パターンを出力
7 case 0: a=1; b=1; c=1; d=1; e=1; f=1; g=0; break;
8 case 1: a=0; b=1; c=1; d=0; e=0; f=0; g=0; break;
9 case 2: a=1; b=1; c=0; d=1; e=1; f=0; g=1; break;
10 // (途中省略)
11 }
12
13 switch (digit) { // どの桁を点灯するか(負論理)
14 case 0: dig1 = 0; break;
15 case 1: dig2 = 0; break;
16 case 2: dig3 = 0; break;
17 case 3: dig4 = 0; break;
18 }
19}
20
21// 10msec毎に呼ばれ、タイマを10msec加算、
22void countup(int time[])
23{
24 if (++time[3] < 10) // ++cs, means "plus 10msec."
25 return;
26 time[3] = 0; // cs = 0(桁上がり)
27 if (++time[2] < 10) // ++ds, means "plus 100msec."
28 // (以下省略)
29}
30
31int main()
32{
33 int time[4]; // = { m, s, ds, cs }
34 int status = 0; // 0: reset, 1: count up, 2: stop
35 int digit = 0; // time[] の添字
36 int toggle = 0; // 2回に一回 時計を 10msec.進める
37 int delay = 0; // 遅延カウンタ、スイッチを押した直後はスイッチを見ない
38#define NDELAY 10 // スイッチを見ない期間(チャタリング対策みたいな)要調整
39 while (1) {
40 wait (0.005); // 5msec.周期で繰り返す
41
42 // 今は reset, count up, stop の、どの状態?
43 switch (status) {
44 case 0: // リセット状態
45 time[0] = time[1] = time[2] = time[3] = 0;
46 if (++delay > NDELAY && mysw == 1) {
47 toggle = 0; // 5msecカウンタをクリアして
48 status = 1; // カウントアップ動作へ
49 delay = 0; // スイッチ見ない期間開始
50 }
51 break;
52
53 case 1: // カウントアップ状態
54 if (toggle == 1)
55 countup(time); // time += 10msec. (2回に1回)
56 toggle ^= 1; // 0, 1, 0, 1, 0 ...
57
58 if (++delay > NDELAY && mysw == 1) { // スイッチが押されたら
59 status = 2; // 停止へ。時間を表示したまま
60 delay = 0; // 再びスイッチ見ない期間開始
61 }
62 break;
63
64 case 2: // 停止。時間を表示したまま
65 if (++delay > NDELAY && mysw == 1) {
66 status = 0; // リセットへ戻る
67 delay = 0; // みたびスイッチ見ない期間開始
68 }
69 break;
70 }
71
72 // 5msec.毎に必ず次の桁を表示する、ダイナミック点灯
73 out7segled(time[digit], digit);
74 digit = (digit + 1) & 3; // 0, 1, 2, 3, 0 ...
75 }
76}
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
退会済みユーザー
2020/01/14 08:29