以下の記事で紹介しているTiのマイコンはC言語で書かれています。

というわけでC言語の勉強もしているわけですが、せっかくなのでわかった内容も確認のための書いてみようと思います。

今回はPCとシリアル通信する際に、アスキーコード変換が必要だったので、そのことについて記載します(^^)

これはCに限った話ではないですが、Cの勉強の一環として。。。

環境

  • Windows10 Pro バージョン1809 64bit
  • Tera Term v4.105(19/12/07)

やったこと

Tera TermにCPU温度を表示させる際に、アスキーコード変換した。

ことの発端

TiのこのマイコンにはADコンバータの一つが内部の温度センサーにつながっていて、CPUのジャンクション温度を測定することができます。

このCPU温度をPCのシリアルモニター(Tera Term)に表示させようとしたところ、アスキーコード変換されて、よくわからない文字になってしまった。ということがきっかけです。

プログラム

// 宣言
unsigned short DEG;         // shortは16bit 温度は正の値しか無いとしてunsignedとしています
unsigned short DEGTen;      // DEGの10の位 整数型で定義
unsigned short DEGone;      // DEGの1の位
unsigned short DEGTenASCII; // DEGの10の位をアスキーコード変換した値
unsigned short DEGoneASCII; // DEGの1の位をアスキーコード変換した値

DEG = 47; // DEGはCPU温度で仮に47℃とします

// 各桁の値の抽出
DEGTen = DEG / 10;
DEGone = DEG % 10;

// アスキーコード変換
DEGTenASCII = DEGTen + 0x30; // これをTera Termでみると「4」と表示される 
DEGoneASCII = DEGone + 0x30; // これをTera Termでみると「7」と表示される

説明

Tera Termにはアスキーコード表の文字が表示されます。

例えば、CPU温度47℃とすると、47は10進数なので、アスキーコード表だと、「/」という文字に相当します。

ですので、マイコンから数値の47(10進数)をシリアル送信しても、Tera Termには「/」が表示されます。でも、「/」と表示されていては直感的に温度がわからず不便なので、Tera Termでも「47」と表示されるようにします。つまり、「47」という文字に相当する数値をマイコンから送信します。

シリアル通信は、1文字ずつ(1byte=8bitずつ)送信のため、「4」と「7」という2文字を分けて送信する必要があります。ですので、47を10の位と1の位に分解します。

10の位の抽出

まず、10の位を抽出します。

10の位は47を10で割ります。

割った値はDEGTenとしてunsigned short(整数型)で定義します。

47 / 10=4.7ですが、整数型で定義しているので、小数は扱えません。すると、小数以下は切り捨てられます。ですので「4」のみが残ります。

これで10の位が抽出できました。

DEGTen 		= DEG / 10;

unsignedで定義しているのは、温度は正の値しか無いとしているからです。shortで定義しても問題ありません。また、温度は常識的に100℃程度までと考えると、short=16bit=216=-32768 ~ 32767(unsigned shortなら0 ~ 65535)の値を扱えるので、範囲内で全然問題ありませんし、char=8bit=28=-128~127(unsigned charなら0~255)でも良いです。

1の位の抽出

次に1の位を抽出します。

DEGone 		= DEG % 10;

「%」は余りを求める演算子です。

47 / 10 = 4あまり7なので、「7」が残ります。

これで1の位が抽出できました。

アスキー文字コードに変換

ここまででは、まだ10進数の数値ですのでTera Termには「47」の文字を表示することができません。

これらをアスキー文字コードに変換します。(欲しいアスキー文字コードに相当する数値に変換します)

下の表はアスキー文字コードに相当する10進数と16進数ですが、アスキー文字コードが0〜1について、16進数に注目するとすべて0x30台となっています。ですのでこれを利用します。数値に0x30を足せば、0〜1の「文字」を表示させられそうです。

10進数にも規則性(ここで言う0x30を足す)があれば10進数を使いましたが、16進数のほうが規則性がわかりやすかったので16進数としました。

ASCIIコード表(一部だけ抜粋)

10進数(0d)16進数(0x)アスキー文字コード
470x29/
480x300
490x311
500x322
510x333
520x344
530x355
540x366
550x377
560x388
570x399
  • 0d4(10進)=0x04(16進)
  • 0d7(10進)=0x07(16進)

ですので、

  • 「4」(アスキー文字コード)=0x04 + 0x30 = 0x34 = 0d52
  • 「7」(アスキー文字コード)=0x07 + 0x30 = 0x37 = 0d55

これをPCに送信すれば、Tera Termで「47」を表示することができます。

DEGTenASCII = DEGTen + 0x30; // これをTera Termでみると「4」と表示される
DEGoneASCII = DEGone + 0x30; // これをTera Termでみると「7」と表示される

2桁以上の場合と他の方法(2020/03/07追記)

2桁だと分かりづらいので、4桁の場合も載せたいと思います。桁が増えても同じようにやれば良いですね。DEG = 1234 とします。

unsigned short DEG = 1234;
unsigned short sen,hyaku,jyu,iti;

sen = (DEG / 1000) + 0x30; // 1234 / 1000 = 1.234 整数型なので1が残る
hyaku = ((DEG % 1000) / 100) + 0x30; // 1234 % 1000 = 1あまり234 → 234 / 100 = 2.34 整数型なので2が残る
jyu = ((DEG % 100) / 10) + 0x30; // 1234 % 100 = 12あまり34 → 34 / 10 = 3.4 整数型なので3が残る
iti = (DEG % 10) + 0x30; // 1234 % 10 = 123あまり4 → 4が残る

泥臭く全部書いてますが、最初の理解にはわかりやすいと思います。こうやって書き出さないと自分もわからない(笑)

また、もうちょっとコンパクトに書いた別の方法を挙げると、

unsigned short DEG = 1234;
unsigned short keta[4];

for(i=0;i<4;i++){
 keta[i] = (DEG % 10) + 0x30;
 DEG /= 10;
}

forループの中身を書くと、以下でも同じことです。

unsigned short DEG = 1234;
unsigned short keta[4];

keta[0] = (DEG % 10) + 0x30; // 1桁目抽出 '4'
DEG /= 10; // DEGを10で割って、DEGに代入(DEGは一桁小さくなる)
keta[1] = (DEG % 10) + 0x30; // 2桁目抽出 '3'
DEG /= 10;
keta[2] = (DEG % 10) + 0x30; // 3桁目抽出 '2'
DEG /= 10;
keta[3] = (DEG % 10) + 0x30; // 4桁目抽出 '1'
DEG /= 10;

keta[0](forループのi=0の時)は1234を10で割ったあまりを出しています。つまり、123あまり4なので、4が残ります。これで1桁目が抽出できました。これは桁が増えても同じことです。

次に、DEG /= 10; で、DEGを10でわって、DEGに代入しています。つまり、DEG=123になります。(整数型なので123.4の.4は消えて123です)

そして、keta[1]で今度は123を10で割ったあまりを出しています。つまり、12あまり3なので、3が残ります。これで2桁目が抽出できました。

のように繰り返せば、全桁が抽出できます。

注意としては、配列0から中身を並べる(keta[0]からketa[3])と、最初と順番が逆になってしまうことです。つまり1234なら4321になる。

どこかの桁を抽出するだけなら良いですが、シリアル通信で各桁を分けて送って、同じように並べ直したい(つまり1234と送って、1234と表示したい(4321ではなく))時は別途考える必要があります。

以上(^^)