はじめに
ここからしばらくはC言語の解説になります。まずは変数型、四則演算、16進数です。
※このページで紹介する内容はあくまでも一例です。個別の作成のご相談ご質問はお答えできませんのでご了承下さい。このページと同じ内容についてのご質問についてはロボット掲示板にてお願いいたします。
※以下の情報は2007年11月現在のものです。ご注意ください。
※このページではC言語の用語が沢山出てきますが、ビギナーの方は各自C言語の初級本なども参照しながら読み進めてください。
バス・レジスタ・メモリ
マイコンを含む、現在のほとんどのコンピュータは基本的にはONかOFF、つまりHIGHかLOW、つまり1か0の2進数で動いています。コンピュータで数値を扱う場合は実際にはバスという共通の配線を経由して値をやりとりしています。バスはCPUのビットタイプの分の幅があります。8ビットマイコンのバスは8本、16ビットマイコンは16本となります。dsPIC33Fは16ビットマイコンなので、バスは16本の幅があります。
バスは、CPU内のレジスタと呼ばれる記憶装置やメモリにつながっています。
レジスタはCPU内部にある記憶装置で、計算中の数値を一時的に保持するもので、高速に値をやりとりすることができます。dsPICには計算用に16ビットのレジスタが16個と、40ビットのレジスタが2個あります。
一部のレジスタは特殊レジスタ(SFR:Secial Function Register)と呼ばれ、マイコンの機能の設定などに使われています。dsPICは16ビットマイコンなので、各SFRも基本的には16ビットの幅があります。
メモリはCPUの中又は外に配置されており、レジスタよりは値のやりとりに時間がかかる場合が多いです。但し、容量は沢山あります(上の16ビット幅の入れ物換算で約1万5千個(30KB)分)。
2進数
バスは1本の経路でONなのか、OFFなのかしか伝えることができません。dsPIC33Fは3.3V動作ですので、ONの時に3.3Vになり、OFFの時に0Vになります。つまりマイコンは内部的には 1(3.3V:ON:HIGH)か 0(0V:OFF:LOW)の2進数で数値を扱います。
0か1かの情報1つを「ビット」という単位で表します。バスが16本あるということは16ビットを表すことができます。
私たちは10進数を使っています。2進数から10進数への変換は、各ビットへ重みをつけてそれを加算すればよいことになります。一番下位の重みは1、次は2、次は4...と、重みは2倍づつになります。
bit |
15 |
14 |
13 |
12 |
11 |
10 |
9 |
8 |
7 |
6 |
5 |
4 |
3 |
2 |
1 |
0 |
重み |
32768 |
16384 |
8192 |
4096 |
2048 |
1024 |
512 |
256 |
128 |
64 |
32 |
16 |
8 |
4 |
2 |
1 |
例えば、前の節のバスの絵で、ビット番号が0番と5番と11番だけHIGH(3.3V)で他はLOW(0V)だったとすると、バス上の数値は
2048 + 32 + 1 = 2081 となります。10進数では。
上の表から見て16ビットでは、”マイナスの数値を考えなければ”、最高で0〜 65535(上の重みを全部足した数。)までの数値を表せることになります。
但し、この数値はバス一回の計算で表せる数というだけで、レジスタやメモリを2つ以上使えばもっと大きな数を扱うこともできます。但し、1つの数値をやりとりするのにバスの動作が数回行わなければならず、計算速度は遅くなります。
<2進数の足し算>
2進数は数字が2個しかないので、1+1で桁が繰り上がります。
0+0=0
0+1=1 (1+0=1でも同じ)
1+1=0 繰り上がり1
という計算になります。1+1は桁が上がるのでもう一桁増えてその桁が1になります。つまり、
1+1=10 となります
尚、言語では基本的に2進数表記はできないので、10進数表記か16進数表記になります。2進数の説明は概念的なものだけ説明します。
マイナスの数
16ビットで表せる数は、マイナス符号を考えなければ0〜65535の数を表せます。これを65536種類の数を表せるということにして、半分をマイナスの数にすると、−32,767〜+32,768までの数を表現できます。
0〜32,767まではビット0〜14までを使えば表せるのは簡単にわかると思います。ではマイナスの値はどうすればよいでしょう?この場合はたとえば次のように考えます。
「−1は、1を足すと0になる2進数」
これを2の補数といいます。上の節で足し算で桁上がりが生じると説明しましたが、たとえば16ビット全部1の数に1を足すとどうなるでしょうか?
1111111111111111 + 1 = (1)0000000000000000
各桁で1+1が生じて全部桁上がりとなり、0番〜15番ビットまでは全部0になります。バスは16ビット幅なので、16番ビットはありません。ということで見かけ上、1111111111111111が−1になります。
同じように「−2を足して0になる数」は
1111111111111110 + 10 = (1)0000000000000000
ということで、1111111111111110が−2になります。
この方式のマイナス表示を「2の補数」と言います。マイナスの値の簡単な求め方は、プラスの数値の0と1を反転したものに1を足すとこのマイナス表記になります。
コンピュータでは、このように同じ数でもマイナスを扱うかどうかで数値の意味が違ってきます。マイナスを扱わない場合は1111111111111111は65535で、マイナスを扱う場合は−1となりますので。
16進数
プログラムでは2進数だと長すぎるので、通常は10進数か16進数で表します。16進数を表す場合は10進数を区別するために数値の頭に「0x」をつけます。
16進数は16で桁上がりします。10進数の10〜15に相当する1桁の数値はA〜Fのアルファベットで表します。
2進数 |
1111 |
1110 |
1101 |
1100 |
1011 |
1010 |
1001 |
1000 |
0111 |
0110 |
0101 |
0100 |
0011 |
0010 |
0001 |
0000 |
10進数 |
15 |
14 |
13 |
12 |
11 |
10 |
9 |
8 |
7 |
6 |
5 |
4 |
3 |
2 |
1 |
0 |
16進数 |
0xF |
0xE |
0xD |
0xC |
0xB |
0xA |
0x9 |
0x8 |
0x7 |
0x6 |
0x5 |
0x4 |
0x3 |
0x2 |
0x1 |
0x0 |
10進数の16は桁上がりして0x10になります。
16進数のメリットですが、ビットごとの値がわかりやすいというメリットがあり、SFRの設定やビット演算などで使われることが多いです。2進数のビットを追う場合も16進数だと4ビットづつに区切って表現できます。10進数だと桁がズレるので16進数の方がどの位置のビットか判定しやすい。
たとえば、dsPIC33FのSFRは16ビット幅なので、2進数で表すと 1111000010100101 などとなります。これでは見づらいので、4ビットづつに区切って
1111 0000 1010 0101 とします。その上で16進数に直すと 0xF0A5 となり、プログラム中で表現できるようになります。(2進数はC言語では表現できないので)
変数
マイコンでは計算などを行うときに「変数」というものを使います。変数は数値を入れる入れ物のようなものと考えてください。
変数には、プログラマ(プログラムを書く人をプログラマと言う)が任意(好きな)の名前をつけることができます。但し、C言語でもともと使われている言葉(予約語という)は使うことができません。たとば、後でプログラムの構文にfor文というのがありますが、変数名にforという名前はつけられません。
変数は、実際にはメモリ上かレジスタ(dsPIC33Fでは計算用が16個ある)のどちらかに配置されます。これはコンパイラーがコンパイル時(ビルド時)に自動的にどちらがよいかを判断して配置します。プログラマは変数名だけに注意すればよいことになります。
変数の型
C言語では、変数を使う前に、その変数で扱うビット数を宣言する必要があります。また、マイナスの符号を扱うかどうかも宣言します。宣言した以外の値をその変数で扱おうとすると、コンパイル時にエラーになるか、動作中に変な動きをします。(ほとんどはコンパイル中に教えてくれるはず)。
基本の変数型は整数を扱う int 型です。integer(整数)の略です。dsPICの場合は16ビットマイコンなのでintは16ビット幅の大きさになります。
マイナスの符号を扱う場合は型の前にsigned と入れます。また、マイナスの符号を扱わない場合はunsigned と入れます。これらを省略した場合は、符号ありなしどちらに判断されるかは開発環境によってことなりますので注意して下さい。
変数の型の名前はだいたい決まっていますが、マイコンや開発環境によってその大きさ(ビット幅)は違います。下はC30の変数型です。
<整数型>
型 |
ビット幅 |
最小値 |
最大値 |
char (singed char) |
8 |
-128 |
127 |
unsigned char |
8 |
0 |
255 |
short (signed short) |
16 |
-32768 |
32767 |
unsigned short |
16 |
0 |
65535 |
int (signed int) |
16 |
-32768 |
32767 |
unsinged int |
16 |
0 |
65535 |
long (signed long) |
32 |
-2の31乗 |
2の31乗-1 |
unsigned lont |
32 |
0 |
2の32乗-1 |
long long (signed long long) |
64 |
-2の63乗 |
2の63乗-1 |
unsigned long long |
64 |
0 |
2の64乗-1 |
マイコンはあまり少数点演算に向いていませんが、無理やり小数点の値を扱うこともできます。
<実数型>
型 |
ビット幅 |
E最小 |
E最大 |
N最小 |
N最大 |
float |
32 |
-126 |
127 |
2の-126乗 |
2の128乗 |
double |
32 |
-126 |
127 |
2の-126乗 |
2の128乗 |
long double |
64 |
-1022 |
1023 |
2の-1022乗 |
2の1024乗 |
実数型に関しては、C言語の初級本かインターネット情報を読んでください。
実数型を使うと、マイコンシステムでは計算速度が著しく低下しますので、通常は小数点にならないように計算します。たとえば、0.1の桁を使った計算をする場合は、全ての数を10倍で計算して最後に10で割ると近似値が計算できます。
変数宣言
変数を宣言するにはプログラムの中で、変数型の後ろにつけたい名前を書きます。(最後に;)たとえば、
int a;
と宣言すると、プログラムの中でaという名前の変数が1つ作られます。符号のありなしをつけない場合は上の表から signed int
であると解釈されます。よって、変数aは16ビット幅の符号付きの入れ物になります。
この場合は、表現できる数は−32768〜32767の範囲になりますので注意して下さい。
四則演算
C言語での足し算引き算はそれぞれ + と - の記号を使うので我々が習った数学と同じです。計算式の書き方は、はじめに計算結果を入れる変数を書いて、その後に計算式を書きます。たとえば、int型で a
b c の4つの変数を宣言したとしましょう。aとbを足してcに入れる場合には
c = a + b;
のように書きます。
a + b = c;
と書くとエラーになります。
掛け算は x だとエックスと間違えやすいので、 * を使います。割り算の記号はキーボードでは表現できないのでスラッシュ / を使います。
c = a * b;
だとaとbを掛けたものがcに入ります。
c = a / b;
だとaをbで割った値がcに入ります。割り切れない場合は切り捨てとなります。あまりを求めたい場合は%を使います。
c = a % b;
だと、aをbで割ったあまりがcに入ります。
数学と同じように、カッコをつけるとカッコ内が先に計算されます。
<演算子>
演算子 |
意味 |
+ |
加算 |
- |
減算 |
* |
乗算 |
/ |
除算 |
% |
乗余 |
それでは実際に四則演算を行うプログラムを作ってみましょう。そして、前回行ったように、PC上のハイパーターミナルでモニタしてみます。
準備
<接続>
A33Fの電源を入れるため、USBケーブルをPCへつないで下さい。
A33Fにプログラムを転送するので、ICD2経由でPCへつないで下さい。
<COMポートの確認、ハイパーターミナルの起動>
前のページのようにCOM番号を確認して、ハイパーターミナルを起動して設定してください。
<プロジェクトの作成>
てきとうなプロジェクトフォルダを作り、MPLABを起動してプロジェクトを作成し、必要なファイルを追加してください。以下、必要な5つのファイルです。
libp33FJ256GP710-coff.a
p33FJ256GP710.gld
p33FJ256GP710.h
stdio.h
uart.h
プロジェクトウィンドウを開くと次のようになっているはずです。(ちがっていたら前のページをもう一度確認してみてください。)
<ヒープの設定>
Project->Build Options->Project->MPLAB LINK30のHeap size
に128を入れます。
ソースプログラムの作成
今回、プログラムは前回作ったソースプログラムを流用してみます。
File->Open で、前回作ったHello.cを開きます。
Hello.cは開いているが、プロジェクトにはまだ追加されていません。ここで、 File->SaveAs でファイル名を変えます。たとえばここでは算術を表すArithmetic.cという名前にしました。まだこの段階ではプロジェクトには追加されていません。
プロジェクトウィンドウで、Source Files を右クリックでAdd Filesで先ほどのファイルを選択すると、プロジェクトに追加されます。
ビルド、プログラムの転送
以下、前のページなどで何回か説明していますので詳細は割愛します。
ビルドを開始するには、F10キーを押すか、「Project」−>「Make」を選択します。ビルドが成功するとOutputウィンドウに「BUILD
SUCCEEDED」と出ています。
次にIDC2をソフト的につなぎます。Debugger->MPLAB ICD2を選択します。
正常につながるとOutputウィンドウにMPLAB ICD2 Ready
と表示されます。
ICD2がつながったので書き込んでみます。書き込みはDebugger->Programを選択します。(又はProgramTargetボタンを押す)OutputウィンドウにProgramming
succeeded と出たらプログラムの転送終了です。
プログラムの変更
今のソースファイルはHello.cが原型なので、今のままではHelloが表示されるだけです。では、算術演算を実際に行って、その結果を表示させてみましょう。プログラムのメインループの部分を下のように書き換えてください。
今回はこちらで用意したソースプログラムは使いませんので、間違わないように変更してください。コメントの部分は全角(日本語)でもかまいませんが、その他のプログラム部分は、スペースも含めて半角(英数記号)となります。また、行の最後に ; をつけるのをわすれないようにしましょう。これも半角です。
コメントですが、C言語では /* と */ に囲まれた部分はコメント(注釈)を書く部分になり、プログラム的には無視されますので何を書いてもよいことになります。また、C++やC#では
// の後から改行までがコメントになります。// は書きやすいので多くのC言語のコンパイラではコメントになりますが、C30の場合も
// はコメント行になります。
コメントは実際のプログラムとは関係ないのですが、非常に重要で、プログラム上級者になるほどきちんとコメントを入れます。後でプログラムを読む、ということが多いのですが、コメントがあればしばらく経過した後でも自分がどのようなプログラムを組んでいたかを簡単に思い出せますので非常に大事です。また、一回書いたコメントは途中で不要になってもプログラム完成まではなるべく消さないのがポイントです。
Hello.cと大きく違うのは次の部分です。
行79〜80で変数を宣言しています。int型なので符号付き16ビット幅の変数となります。変数宣言は同じ型なら一行にカンマで区切って何個でも宣言できますよって、
int a;
int b;
と
int a,b;
同じ意味です。
行102〜104で計算をしています。
行106のprintf文は前回と少し違います。
printf( )は数値や変数も文字データに変換することができます。たとえば、dataという変数の中にある数値を文字に変換して送りたい場合は次の構文になります。(変数は数値を入れる入れ物のようなものです。後のページで詳しく)
printf("%d", data);
※dataの前はカンマです。
%dは与えられたデータを10進数(10進数は英語でdecimalなのでd)の文字に変換するもので、「フォーマット指定子」といいます。
変更したらもう一度ビルドして、プログラムを転送してください。そしてRUNして下さい。
結果はハイパーターミナルにこのように表示されました。
printf文の%dはこのように、” ”内に%dを入れた位置に変数の値を表示する、という機能があります。よって、表示されるのは"%d"でもなく"data"でもなく、dataの中にあった3の”文字”が表示されます。変数は複数指定することができ、カンマで区切ります。ということで行106を次のように変えてみましょう。
printf("%d+%d=%d\r\n", a,b,data);
すると結果は次のように表示されます。今度はa又はbの中身が%dがあった位置に表示されました。カンマで区切った変数を指定した順番の位置にその中身が表示されます。
フォーマット指定子にはいろいろな種類があります。たとえば、 %x は変数の値を16進数(の文字)にします。a=10 b=2に変えて行106を
printf("%x+%x=%x\r\n", a,b,data);
のように変えると結果は
となります。10進数の10は16進数の”a”、12は”c”です。
このようにして計算の内容を知ることができます。
変数型についての考察をもう少し
TIPS
この節は一見、冗長的(同じことの繰り返しで無駄)なもののように見えますが、プログラムビギナーの方は是非、読むだけでななく、実際にプログラムを書いてみて(変更してみて)実行してください。
プログラムの上達の早道は、実際にプログラムを組むことです。1回の実行(プログラミング)は10回読んだよりも覚えます。
|
例ではint型変数での計算を行いました。int型は−32768〜32767までの数しか表せません。では、これを超える数を計算させたらどうなるのでしょうか?ではプログラムを次のように変えてみてください。
行101と102のようにすると行103の結果は60000になりますのでintの計算範囲を超えます。
これをビルドしても特にエラーは出ませんでした。このような理論的な違いはコンパイル時にはエラーとみなされません。では実行してみましょう。
ハイパーターミナルでの結果はこうなりました。
これはつまり桁があふれて正確な計算ができていないことになります。C言語に慣れていないとこのように変数型でハマるということがよくあります。これは経験を重ねて気がつきやすくなるしかありません。
同じように、数が大きすぎるのではなく型が小さすぎるということもないように注意して下さい。今度はa,b,dataの変数を unsigned
char に変えて、数を次のようにしてみて下さい。
次にビルドしてみます。今度は注意してOutputの表示をよく見てみます。最後にはBUILD SUCCEEDEDと出ますが、実は途中で、
と出ています。101行と102行が warning だということです。warningは致命的なエラーではないが、なにかおかしいという場合に出るもので、プログラマに注意(warning)を促しています。英語部分を読むと大きな数値が問題であるようなことが書いてあるのが予想できます。これを実行しても、300+300の600とは出ません。(実際には255までしか計算できないので、256に桁上がりを2回繰り返して、最後には88になる。256x2+88=600。でも600の数値は扱えないのででない。)
では今度は、変数宣言をそのままunsigned int にして、数を a=-1 b=-1 にしてみましょう。
すると結果は、エラーは出ずに254になります。8ビット(char型は8ビット)の場合、254は2の補数では−2を意味します。どちらにしても、エラーが出ずに意図しない結果となります。
このように簡単な計算では注意は不要ですが、プログラムが長くなってくると計算結果が違う原因がなんなのかわかりづらくなります。これらのように変数型の指定が間違っている場合もありますし、実際に電子回路の組み方が間違っても数値が違うということもあります。
おわりに
ひきつづき、C言語でのビット演算、論理演算について解説します。
2007年11月26日
|