|
舞鶴技術研究会・技術講演会より(2004/2/28(土)) |
舞鶴技術研究会・技術講演会
参官学協同の技術力向上への取り組み事例発表会
日時:2月28日(土) 午後1時30分から
場所:ポリテクカレッジ京都(6号館2階を予定しています)舞鶴市上安1922
ものづくりプロジェクト:
「1チップ・マイコンPIC&AVRの応用技術 (応用編)」
1チップマイコンPICとRTC-ICのI2C通信によるラップカウンタの製作
舞鶴工業高等専門学校 電子制御工学科
町田秀和(machida@maizuru-ct.ac.jp)
1.はじめに
舞鶴技術研究会の情報システム分科会では、2002年〜2003年にかけて
「ものづくりプロジェクト:1チップ・マイコンPIC&AVRの応用技術」
を立ち上げ、以下の公開講座などを実施し、会員および舞鶴高専学生の
多くの参加を得ました。
- 2002年6月 勉強会
- 2002年11月 公開講座(基礎編):トレースカー
- 2003年11月 公開講座(応用編):ラップカウンタ
その結果、以下のような問い合わせや製作例などを寄せていただきました。
- 医療福祉センターから身体障害者介助機器
- 音声録音再生器、超音波融雪器
- PICによる多足歩行ロボット制御器の開発
- 体育運動解析のための携帯記録装置
本プロジェクトは2003年度で終了しますが、1チップ・マイコンPICは応用範囲が
広い割に、気軽に取り組むことができることが、ご理解いただけてきたと実感して
います。今後とも、1チップ・マイコンに関するお問い合わせがありましたら、町田
までお気軽にメール(machida@maizuru-ct.ac.jp)でご連絡ください。
本稿では、2003年度に公開講座(応用編)のネタとして取り上げた、
写真1に示す「1チップマイコンPICとRTC-ICのI2C通信によるラップカウンタの製作」
を紹介します。
キーポイントは以下のとおりです。
- RTC-IC(EPSON RTC8564)の採用による正確な計時
- PICとRTC-IC間のI2C通信
- 液晶表示
- PICのCプログラミング

写真1 ラップ・カウンタ全景(実物大)
2.1チップマイコンPIC
マイクロ・コンピュータは電子制御機器の中核として、例えば炊飯器、ミシン、洗濯機、
自動車、などなどに使われています。もはや必要不可欠な存在と言ってよいでしょう。しかし
ながら電子制御工学科でも最近は3年生の「計算機工学1」で初めて触れられるだけで、それも
「基本情報処理技術者試験」で出題されるCASL2/COMET2という仮想のアセンブラ/マイコンを
取り扱っているだけです(今後カリキュラムは改善されていきます)。また、以前は実際にシス
テムを実現するにはCPU(Z80)のほかにメモリ(S-RAM)や並列入出力LSI8255を搭載したワンボード
マイコンを使用していて、とても気軽に使える物ではなく、それなりの値段がしました。
しかし最近になってごく小規模な組み込み用途向けのコントローラ として、8〜40ピンの
1チップ・マイコンが流行しています。例えば、米国Microchip Technology社のPICや、
米国Atmel社のAVRなどです。これらの1チップマイコンは高速(20MHz)、低消費電力(最小30μA)、
十分なメモリ(数キロバイト)、強力な入出力ピン(RS232C,I2Cも可能)、最小の周辺回路、
低価格(320円程度)で、そして開発ツール(ソフトウェア:ほとんどフリー)が豊富でパソコン
で簡単に開発できるところが最大の魅力です。
ということで現実的なマイコン応用の実現手段として、大変人気があります。実際、ロボコン
ではほとんどのチームがPICを用いています。また、ベンチャー企業でも盛んに用いられています。
その理由の一つとしては、PICが大変タフなことが上げられるでしょう。つまり、何度でも手軽に
プログラムが可能なことや、すくそばでトランジスタが破裂してもPIC自体は無事なことなどです。
S研では年度初めに学生どおしで新入部員対象に部内講習会を行い、また例年秋頃には筆者らが
一般市民相手に公開講座を開いています。本校学生の参加も問題ありませんのでお問い合わせて
ください。
PICの開発手順は詳しくは参考文献1)
や2)にありますが、簡単な流れは次の通りです。
- ハードウェア回路を設計制作する。ここでは写真2、図1,2の回路を用いました。
- 開発ツールMPLAB上で、アセンブラもしくはC言語でプログラムを制作する。
- 書込み器をパソコンのシリアルポートに接続し、
参考文献3)のic-progを用いて書込む。

写真2 PICのハードウェア例(実物大)
写真2の右側がライタ部分(左端のコネクタでPCのシリアルポートに接続)、左側が汎用評価
ボード部分(入力スイッチx3,リセットスイッチx1,出力LEDx2,液晶LCDユニット用コネクタ,
I2Cリアリタムクロック用ソケット付)です。

図1 実体配線図(プリント基板CAD「PCBE」で作成)

図2 全回路図(参考文献4)Bschによる)
写真2の右側がライタ部分(左端のコネクタでPCのシリアルポートに接続)、左側が汎用評価
ボード部分(入力スイッチx3,リセットスイッチx1,出力LEDx2,液晶LCDユニット用コネクタ,
I2Cリアリタムクロック用ソケット付)です。
以上のうち、Cコンパイラのみは有料(25,000円程度)ですが、その他はいっさいフリー
(無料)です。筆者の感想としては、アセンブリ言語はとっつきにくくはありますが、すでに
参考文献2)
などに膨大なプログラム資産があるので、それを参照すれば目的はすぐに達成
できるというところです。一方、C言語プログラムはライブラリ(RS232C,I2C通信やタイマ
など)がそろっているので、高級言語らしく大変楽にプログラミングができますが、なんと
いっても有料であること、そしてやはりアセンブリ言語に対しては非効率なことと、
わずかに予期しない動作をすることがあります。この理由から、ロボコンでは全て
アセンブリ言語で記述しているそうです。
3.RTC-IC(EPSON RTC-8564)とI2C通信
(1チップ)マイコンで時計(ラップ・カウンタ)を実現するには、
何らかの方法で時間を計測するわけだが、それには次の3つの方法が考えられます。
- 命令数と実行時間をかけて時間を計る。
- 周辺装置(1チップマイコンでは内臓)のカウンタの割り込みで時間を計る。
- 専用のRTC(Real Time Clock)-ICを外付けして時間を読み書きする。
1,2の方法は外付け回路が不要なので安価にはできますが、一番の問題は
命令実行時間(クロック周波数)の精度に依存し、また割り込みを用いると
往々にして狂ってしまうことがあります。
これに対して、3のRTC-IC外付けの方法は専用ICだけあって正確であるし、
アクセス方法さえ判明すれば、プログラミングも難しくはありません。
そこで、ここではEPSON社のRTC-ICであるRTC8564を採用しました。
( 秋月電子通商で通販しています。
約500円程度)
問題のアクセス方法ですが、2線式シリアル通信方式であるI2Cが使われています。
I2Cはピン数が少ない1チップマイコン(PIC)には大変適した方式であり、PIC16F87*シリーズ
には専用ハードウェア回路が内蔵されているし、最も普及しているPより小型のPIC16F84
でもソフトウェアでインターフェースすることができます。
I2C(Inter-Integrated Circuit)とは、フィリップス社が提唱した周辺デバイスとの
シリアル通信方式で、マイコン間はもちろんEEPROMやA/D変換ICそしてRTC-IC
のインターフェースが実現されています。このような目的のため、同じ基板内などの
ように近距離で直結したデバイスと最大400kbpsの通信に用いられるのが主で、
離れた装置間の通信には向いていません。
Cコンパイラ(参考文献6)
のCCS社のPCM)にはI2Cの基本ライブラリ関数が用意されていて大変簡単にアクセス
することができます。
さらに、付録に町田が自作したRTC8564用の通信ライブラリを示しますが、
読み書きシーケンスは図3に示すとおりです。
気を付けないといけないのは、RTC(I2C)から読み出す場合には、最後の読み出し
ではack(返答)を返さない(0)とすることです。そうしないと、RTC(I2C)が
読み出しモードから抜け出せずに固まってしまいます。

図3 I2C通信によるRTCの読み書きシーケンス
4.液晶表示
時間の表示には液晶を用いています。ここでは1チップマイコンPICでは
デファクトスタンダードになっているサンライク社のSC1602Bを採用しました。
SC1602Bは4bitのデータの他3本の制御信号だけで駆動できるので、ピン数の
少ないPICには適しています。付録に液晶を駆動するためのC言語ライブラリを示します。
5.ソフトウェアの制作
ソフトウェアの製作にはCコンパイラ(参考文献6)
のCCS社のPCM)を用いました。全プログラムを付録に示します。
使い方は以下のとおりで、写真3のようにラップ計時が行えます。
- 電源投入あるいはPICをリセットするとRTCをリセットして計時を開始すします。
- 1周目に入るときに押ボタンスイッチ(RA4)を押すと、
RTCを再リセットし1周目の計時が始まる(スタート)
液晶の上段(1行目)には経過時間が表示されます。
- 2周目に入るときに押ボタンスイッチ(RA4)を押すと、
1周目の記録が液晶の下段(2行目)に表示されます(ラップ)。
- n周目に入るたびに押ボタンスイッチ(RA4)を押すと、
n-1周目の記録が液晶の下段(2行目)に表示されます(ラップ)。
電源投入時

スタート

1周目計時

2周目計時

写真3 ラップカウンタの使い方
(RA4の押ボタンスイッチを押すだけ)
(MCLRの押ボタンスイッチはリセット)
Cプログラミングは非常に記述しやすいですが、やはりPICの特性を良く把握しないと
思わぬ不具合に泣くことになります。たとえば、押しボタンスイッチが押されたことを
確認するには、以下のように20msec待って再確認してチャタリングを除去する必要が
あるます。
if(input(PIN_A4)==0) {
delay_ms(20);
if(input(PIN_A4)==0) {
6.おわりに
ものづくりプロジェクトの応用編の課題として、ラップカウンタを製作しました。
外付け回路として液晶とRTC-ICを採用したましたが、それをアクセスするライブラリ関数
を記述さえしてしまえば、プログラミングは大変簡単で様々な応用が考えられます。
今後は、加速度センサや歩数計そして記録用のI2C-EEPROMを外付けして、
実用的なラップカウンタに仕上げていく予定です。
参考文献(リンク集)
- 町田、S研:「ものづくりプロジェクト:PIC&AVRによる1チップマイコンの応用技術」
の勉強会(2002/6/20)
- 後閑: 電子工作の実験室
- IC-Prog Prototype Programmer
- 岡田:水魚堂オンライン、回路図エディタBsch
- CCS社: PIC用C言語コンパイラ
- PIC,RTC通販: 秋月電子通商 (SSRで検索するとよい。1個250円)
付録:PICとRTC-ICのI2C通信SSRによるラップカウンラのCプログラム
付録1:メイン・プログラム(rctlap01.c)
///// rctcap01 RTC , lap counter/////
#include <16f84.h>
#fuses HS,NOWDT,NOPROTECT,PUT
#use delay(CLOCK = 20000000)
#use fast_io(A)
#use fast_io(B)
#use i2c(MASTER, SDA=PIN_A0, SCL=PIN_A1, ADDRESS=0xa0, FORCE_HW) // I2C使用宣言
#define mode 0 ////// 液晶表示ライブラリ設定
#define input_x input_B //ポートB使用
#define output_x output_B
#define set_tris_x set_tris_B
#define stb PIN_B3
#define rs PIN_B2
#byte port_a=5
#include "lcd_lib.c" // 液晶ライブラリ
#include "rtc_lib.c" // RTCライブラリ
#include "rtc_lap.c" // ラップ計算
///// メイン関数
void main()
{
int pa;
int lap=-1;
set_tris_a(0x3F); // A-port all input
output_float(PIN_A1); //SCLピン定義
output_float(PIN_A0); //SDAピン定義
lcd_init(); //LCD初期化
lcd_cmd(0x0C); // cursor off
lcd_clear();
year=0x03; month=0x90; week=0x00; day=0x12; hour=0x00; min=0x00; sec=0x00;
rtc_date_set();
rtc_date_read();
rtc_lap_init();
while(1) { //永久ループ
rtc_date_read();
lcd_cmd(0x00); //1行目の先頭へ
lcd_cmd(0x02);
printf(lcd_data," Now %c%c:%c%c:%c%c",h_hour,l_hour,h_min,l_min,h_sec,l_sec);
if(input(PIN_A4)==0) {
delay_ms(20);
if(input(PIN_A4)==0) {
lap++;
if(lap==0) {
year=0x03; month=0x90; week=0x00; day=0x12; hour=0x00; min=0x00; sec=0x00;
rtc_date_set();
rtc_date_read();
}
rtc_lap_calc();
lcd_cmd(0xC0); //2行目の先頭へ
printf(lcd_data," Lap%2d %c%c:%c%c:%c%c",lap,hl_hour,ll_hour,hl_min,ll_min,hl_sec,ll_sec);
do {} while(input(PIN_A4)==0);
}
}
}
}
// LCD表示 16文字×2
// 0123456789ABCDEF
// Now 12:34:56
// Lap01 00:00:12
付録2:液晶ライブラリ(lcd_lib.c)
///////////////////////////////////////////////
// 液晶表示器制御ライブラリ
// 内蔵関数は以下
// lcd_init() ----- 初期化
// lcd_cmd(cmd) ----- コマンド出力
// lcd_data(chr) ----- 1文字表示出力
// lcd_clear() ----- 全消去
//////////////////////////////////////////////
//////// データ出力サブ関数
void lcd_out(int code, int flag)
{
output_x((code & 0xF0) | (input_x() & 0x0F));
if (flag == 0)
output_high(rs); //表示データの場合
else
output_low(rs); //コマンドデータの場合
delay_cycles(1); //NOP
output_high(stb); //strobe out
delay_cycles(2); //NOP
output_low(stb); //reset strobe
}
//////// 1文字表示関数
void lcd_data(int asci)
{
lcd_out(asci, 0); //上位4ビット出力
lcd_out(asci<<4, 0); //下位4ビット出力
delay_us(50); //50μsec待ち
}
/////// コマンド出力関数
void lcd_cmd(int cmd)
{
lcd_out(cmd, 1); //上位4ビット出力
lcd_out(cmd<<4, 1); //下位4ビット出力
delay_ms(2); //2msec待ち
}
/////// 全消去関数
void lcd_clear()
{
lcd_cmd(0x01); //初期化コマンド出力
delay_ms(15); //15msec待ち
}
/////// 初期化関数
void lcd_init()
{
set_tris_x(mode); //モードセット
delay_ms(15);
lcd_out(0x30, 1); //8bit mode set
delay_ms(5);
lcd_out(0x30, 1); //8bit mode set
delay_ms(1);
lcd_out(0x30, 1); //8bit mode set
delay_ms(1);
lcd_out(0x20, 1); //4bit mode set
delay_ms(1);
lcd_cmd(0x2E); //DL=0 4bit mode
lcd_cmd(0x08); //display off C=D=B=0
lcd_cmd(0x0D); //display on C=D=1 B=0
lcd_cmd(0x06); //entry I/D=1 S=0
lcd_cmd(0x02); //cursor home
}
付録3:RTCライブラリ(rtc_lib.c)
// I2C RTC EPSON RTC-8564 functions By H.machida@MNCT-S 2003/10/9
// rtc_lib.c
// global variables
int year,month,week,day,hour,min,sec; // 現時刻
char h_year,h_month,h_week,h_day,h_hour,h_min,h_sec; // 現時刻文字2桁め
char l_year,l_month,l_week,l_day,l_hour,l_min,l_sec; // 現時刻文字1桁め
// 【<1>日付時刻設定】
void rtc_date_set()
{
i2c_start();
i2c_write(0xa2); // 書き込みモード
i2c_write(0x02); // 秒のアドレス
i2c_write(sec); // 秒の値 0-59
i2c_write(min); // 分の値 0-59
i2c_write(hour); // 時の値 0-23
i2c_write(day); // 日の値 1-31
i2c_write(week); // 曜の値 日月火水木金土 0123456 上位5bitはDon't Car
i2c_write(month);// 月の値 (C:MSB)1-12 Cは1のとき21世紀
i2c_write(year); // 年の値 00-99
i2c_stop();
}
// 【<2>日付時刻読み出し】
void rtc_date_read()
{
i2c_start();
i2c_write(0xa2); // 書き込みモード
i2c_write(0x02); // 秒のアドレス
i2c_start();
i2c_write(0xa3); // 読み込みモード
sec= i2c_read(1); // 秒の値
min= i2c_read(1); // 分の値
hour= i2c_read(1); // 時の値
day= i2c_read(1); // 日の値
week= i2c_read(1); // 曜の値
month=i2c_read(1); // 月の値
year= i2c_read(0); // 年の値
i2c_stop();
h_sec= (( sec>>4)&0x07)|0x30; l_sec= ( sec&0x0f)|0x30;
h_min= (( min>>4)&0x07)|0x30; l_min= ( min&0x0f)|0x30;
h_hour= (( hour>>4)&0x03)|0x30; l_hour= ( hour&0x0f)|0x30;
h_day= (( day>>4)&0x03)|0x30; l_day= ( day&0x0f)|0x30;
h_week= 0; l_week= ( week&0x0f)|0x30;
h_month=((month>>4)&0x01)|0x30; l_month=(month&0x0f)|0x30;
h_year= (( year>>4)&0x0f)|0x30; l_year= ( year&0x0f)|0x30;
}
// 【<3>アラームの設定】毎時10分にアラーム出力する。
void rtc_alarm_set(int aweek,int aday,int ahour,int amin)
{
i2c_start();
i2c_write(0xa2); // 書き込みモード
i2c_write(0x09); // アラーム分のアドレス
i2c_write(amin); // アラーム分の値 0-59 MSBはAE
i2c_write(ahour); // アラーム時の値 0-23
i2c_write(aday); // アラーム日の値 1-31
i2c_write(aweek); // アラーム曜の値 日月火水木金土 0123456
i2c_stop(); // 上位5bitは Don't Care
}
// 【<4>タイマ、周波数の設定】周波数1Hz,タイマ10秒設定
void rtc_timer_set(int tfreq,int tcont,int timer)
{
i2c_start();
i2c_write(0xa2); // 書き込みモード
i2c_write(0x0D); // クロック出力周波数のアドレス
i2c_write(tfreq); // クロック出力周波数の値
i2c_write(tcont); // タイマ制御値
i2c_write(timer); // タイマ値
i2c_stop();
}
付録4:ラップ計算(rtc_lap.c)
// I2C RTC EPSON RTC-8564 functions By H.machida@MNCT-S 2003/10/9
// rtc_lap.c
// global variables
// int year,month,week,day,hour,min,sec; // 現時刻
// char h_year,h_month,h_week,h_day,h_hour,h_min,h_sec; // 現時刻文字2桁め
// char l_year,l_month,l_week,l_day,l_hour,l_min,l_sec; // 現時刻文字1桁め
char hb_hour,hb_min,hb_sec; // 前時刻文字2桁め
char lb_hour,lb_min,lb_sec; // 前時刻文字1桁め
char hl_hour,hl_min,hl_sec; // ラップ時間2桁め
char ll_hour,ll_min,ll_sec; // ラップ時間文字1桁め
char ht_hour,ht_min,ht_sec; // 待避用時刻文字2桁め
char lt_hour,lt_min,lt_sec; // 待避用時刻文字1桁め
//
void rtc_lap_init()
{
hb_hour=h_hour; hb_min=h_min; hb_sec=h_sec;
lb_hour=l_hour; lb_min=l_min; lb_sec=l_sec;
}
// 【ラップ時間=現在時刻-前時刻】
void rtc_lap_calc()
{
ht_hour=h_hour; ht_min=h_min; ht_sec=h_sec;
lt_hour=l_hour; lt_min=l_min; lt_sec=l_sec;
if(lb_sec > l_sec) { l_sec+=10; hb_sec++; }
ll_sec=(l_sec-lb_sec)|0x30;
if(hb_sec > h_sec) { h_sec+=6; lb_min++; }
hl_sec=(h_sec-hb_sec)|0x30;
if(lb_min > l_min) { l_min+=10; hb_min++; }
ll_min=(l_min-lb_min)|0x30;
if(hb_min > h_min) { h_min+=6; lb_hour++; }
hl_min=(h_min-hb_min)|0x30;
if(lb_hour > l_hour) { l_hour+=10; hb_hour++; }
ll_hour=(l_hour-lb_hour)|0x30;
if(hb_hour > h_hour) h_hour+=3;
hl_hour=(h_hour-hb_hour)|0x30;
hb_hour=ht_hour; hb_min=ht_min; hb_sec=ht_sec;
lb_hour=lt_hour; lb_min=lt_min; lb_sec=lt_sec;
}
ご意見ご感想はこちらまで!!