目次
前
次
マイコン選定
PLCを実現する関数が揃ったので、マイコンを選定します。
入出力は、各8ビットを利用するとして合計16ビット
なので、28ピン以上のマイコンが必要です。
手元にはPIC、AVR、H8、ARM、78K等の40ピン以上のマイコン
があります。どれも一長一短がありますが、ひとつのマイコン
で動作するファームウエアを作成すれば、高レベル言語である
Cで記述しているので、移植は簡単です。
マイコンの選定条件を与えて、ひとつに絞ります。
- 40ピンDIP
- EEPROM内蔵
- シリアルインタフェース回路内蔵
- アルカリ乾電池2本で動作可能
- I/O数は多いほどよい
- GCCでファームウエアを開発可能
これらの条件をすべて満たす手元のマイコンは、AVRだけでした。
マイコン決定後、パーツボックスの中にあるAVRを
調べてみると、次の種類がありました。
- AT90S8515
- AT90S8535
- ATMega644
それぞれ特徴がありますが、PLC動作は単純なので
現在主流ではないAT90Sシリーズを利用することに
しました。
AT90S8515、AT90S8535ともにテストボードは作成して
あるので、AT90S8535で開発してみます。
AT90S8535の最高動作クロック周波数は、8MHzなので
8MHzで動作させています。
インタプリタは作成済みなので、通信処理とタイマー割込み
に分けて、動作を考えます。
通信処理
通信処理は、次の2つのフェーズで必要になります。
- ラダー図情報のEEPROMへの転送(save)
- EEPROMからラダー図情報の取得(load)
通信にはプロトコルが必要なので、次のように決めました。
- データ転送速度 38400bps
- 同期方式 調歩同期
- フロー制御 なし
- パリティ なし
- ストップビット 1ビット
- saveコマンド S
- loadコマンド L
コマンドをS、Lにしたので、フォーマットを規定します。
Sコマンド
ラダー図情報は、16ビットデータでEEPROMに保存する
ので、アドレスとデータが必要です。
アドレス、データともに、16進4けたで指定します。
EEPROMのアドレス0100hに、9001hを設定する場合は
次のように記述します。
S01009001'\r'
通信内容をモニタできるように、文字で送信します。
Lコマンド
EEPROMに格納されている、ラダー図情報を取得します。
コマンドを示す1文字’L’を送信します。
L'\r'
マイコン側は、このコマンドを受信すると、0から
EEPROMの最終アドレスまで、内容を16進4けたで返信
します。
コマンドを決めたので、PLCファームウエア処理を修正します。
if ( mode == IDLE ) {
if ( SFLAG == ON ) {
SFLAG = OFF ;
/* get command */
cmd = *(rbuf+0) ;
/* set circuit infomation */
if ( cmd == 'S' ) {
/* get address */
cadr = get_ad(OFF) ;
/* get data */
cdat = get_ad(ON) ;
/* save */
put_dat_eeprom( cadr , cdat );
}
/* get circuit infomation */
if ( cmd == 'L' ) {
*(xdat+2) = 0 ;
for ( rom_address = 0 ; rom_address < 512 ; rom_address++ ) {
tmp = read_eeprom( rom_address ) ;
*(xdat+0) = asc_cha[ (tmp >> 4) & MASK0F ];
*(xdat+1) = asc_cha[ tmp & MASK0F ];
rs_puts( xdat );
}
}
}
}
受信割込みから与えられたトリガーを使います。
トリガーが着たら、saveとloadを判断し、EEPROMの
アクセスを切り分けます。
プロトコルが確定したので、通信ブロック初期化と
受信割込みの内容を定義します。
通信ブロック初期化
データ転送速度を設定します。
#define FOSC 8000000
#define BAUD 38400
#define MYUBRR (FOSC/16/BAUD)-1
/* set Baud Rate Registers */
UBRR = MYUBRR ;
/* Enable receive interrupt , receive module and transmit module */
UCR = (1 << TXEN) | (1 << RXEN) | (1 << RXCIE);
受信バッファが必要なので、配列で指定します。
また、格納データの位置を指定する変数を宣言します。
#define BUFSIZE 10
volatile UBYTE rbuf[BUFSIZE] ;
volatile UBYTE rindex;
受信割込み
受信バッファに1文字ずつ格納し、デリミタで
あったなら、フラグSFLAGをセットします。
ISR(SIG_UART_RECV)
{
volatile UBYTE ch ;
/* get 1 charactoer */
ch = UDR ;
/* store */
*(rbuf+rindex) = ch ;
rindex++ ;
/* judge */
if ( ch == '\r' ) {
SFLAG = ON ;
rindex = 0 ;
}
}
タイマー割込み
PLCでは、スキャン処理周期を10msとします。
10msごとに、スキャン処理開始のトリガーを
与えるため、タイマー割込みを使います。
10msごとにフラグTFLAGをセットします。
仕様を決めたので、初期設定と割込み処理を定義します。
初期設定
タイマー1を、コンペアマッチで動かします。
/* clear timer/counter */
TCNT1 = 0 ;
/* set counter */
OCR1A = 9999 ;
/* Control Register 1-B */
TCCR1B = (1 << CS10) ;
カウンタ用クロックは、8MHzを8分周して1MHzにします。
0〜9999まで、カウントとすると10msになります。
割込み処理
割込みでカウンタクリアとフラグ設定します。
ISR(SIG_OUTPUT_COMPARE1A)
{
/* clear timer/counter */
TCNT1 = 0 ;
/* set flag */
TFLAG = ON ;
}
シーケンス処理動作をさせるか、通信処理をさせるかを
トグルスイッチを利用して切り替えます。
スイッチを使うので、チャタリングが発生します。
チャタリング除去のために、20ms程度ごとにスイッチ状態
をシフトレジスタに読み込みます。
シフトレジスタに、スイッチ状態を入力するトリガーを
タイマー割込みで発生させます。
仕様を決めたので、初期設定と割込み処理を定義します。
初期設定
タイマー0を、オーバーフローで動かします。
/* clear timer/counter */
TCNT0 = 56 ;
/* Control Register */
TCCR0 = (5 << CS00) ;
カウンタ用クロックは、8MHzを1024分周して入力します。
さらに、56〜256までカウントして25.6msごとに割込みが
発生するカラクリを作ります。
割込み処理
割込みでカウンタクリアとフラグ設定します。
ISR(SIG_OVERFLOW0)
{
/* clear timer/counter */
TCNT0 = 56 ;
/* set flag */
MFLAG = ON ;
}
2つのタイマーを動かすため、初期化後、次の命令を与えます。
TIMSK = (1 << OCIE1A) | (1 << TOIE0);
目次
前
次