pic16F818 での自前シリアル(RS232C)通信(HITECH-C)
+80000
昨日
今日


PIC16F819で温度計を作りました。せっかくなのでPCでロギングしようと思ったのですが、このマイコンはシリアル機能がついてないのです ね。。。
もう回路も出来上がってピンも制約があるためC言語で自前のソースを書きました。ネット上にサンプルも見当たらないので。。。

デバッグに土日丸々かかる一大プロジェクトになってしまい、この労力を誰かの為にと思い(それをエゴだと言えば自己満足ですが)、アップします。

今回の回路はこちら。


そしてrs232cのプロトコル(タイムチャート)はこちらです。

簡単ですが時間がかかりました。。。

そしてそしてソースは以下です。この後温度計に組み込むのでライブラリ化しました。ご自分のプロジェクトにソースファイル、ヘッダファイルを追加 して ヘッダファイルをインクルードして下さい。
自己流の稚拙なコードです。マチガイ指摘などご指導やご質問は掲 示板までお願いします。

//C言語によるシリアル通信の雛形
//(c)ahotcho
// rs232test.h
// for HITECH-C
void SendChar(signed char);
void SendWord(signed char*);
signed char ReceiveChar();
void ReceiveWord(unsigned char*);
//C言語によるシリアル通信の雛形
//(c)ahotcho
// rs232test.c
// for HITECH-C
#include "rs232test.h"
#include <pic.h>
#include <string.h>
#define TXD RB1
#define RXD RB0
#define SEPCHAR ' '
#define TIMERSET 0xf3

bit NextBit,rxCome;

//文字の出力
void SendChar(signed char a){
    //カウンタの宣言
    unsigned char i=0;

    //タイマーのクリア
    TMR0=TIMERSET-1;

    //スタートビット
    TXD = 0;

    //タイマフラグのクリア
    NextBit=0;
    //1ビット待ち   
    while(!NextBit){;}
    NextBit = 0;

    //8ビット繰り返し
    for(i = 0; i<=7; i++){
    //1ビット目から順に出力
    TXD=(a>>i)&1;
    //1ビット待ち
    while(!NextBit){;}
    NextBit = 0;
    }
    //ストップビット
    TXD =1;   
    //1ビット待ち
    while(!NextBit){;}
    NextBit = 0;
    return;
}   

//文字列の出力
void SendWord(signed char *a){
    //カウンタの宣言
    unsigned char i;
    //文字列の長さ-1終端文字の前まで出力
    for(i=0;i<strlen(a);i++)SendChar(a[i]);
    return;
}

//1文字の入力
signed char ReceiveChar(){
    //バッファの宣言
    signed char buf=0;
    //カウンタの宣言
    unsigned char i;
    //スタートビット待ち
    while(RXD){}
    //タイマーリセット&タイマーフラグリセット
    TMR0=TIMERSET;
    NextBit = 0;
    for(i = 0;i <= 7; i++){
        //1ビット待ち
        while(!NextBit){;}
        NextBit = 0;
        //8ビット代入し続け
        buf += ((unsigned char)RXD<<i);
    }
    //ストップビット待ち
    while(!NextBit){;}
    NextBit = 0;
    //ストップビット待ち
    while(!RXD){}
    return buf;
}

void ReceiveWord(unsigned char *buf){
    //バッファの宣言
    signed char a=0;
    //カウンタの宣言
    unsigned char i=0;

    //区切り文字が来るまでループ
    while(a != SEPCHAR){
    //文字の入力関数
        a = ReceiveChar();
        //文字列のi番目に代入
        buf[i] = a;
        //カウントアップ
        i++;
    }
    buf[i-1]='\0';
    //区切り文字を除去&終端文字代入
    return;
}
//C言語によるシリアル通信の雛形〜呼び出すmain関数と割 り込 み 〜
//(c)ahotcho
// rs232test_main.c
// for HITECH-C
#define _LEGACY_HEADERS
#define _XTAL_FREQ 8000000


#include "rs232test.h"
#include <pic.h>
#define TXD RB1
#define RXD RB0
#define TIMERSET 0xf3
__CONFIG(LVPDIS & UNPROTECT & MCLRDIS & BORDIS & PWRTEN & WDTDIS & INTIO) ;

extern bit rxCome,NextBit;


static void interrupt intr(){
    //タイマ0なら
    if(TMR0IF){
        //タイマ0のフラグをクリア
        TMR0IF = 0;
        //タイマのカウンタをセットしなおし
        TMR0=TIMERSET;
        //タイマが来たことのフラグをセット
        NextBit=1;

    }
    return;
}   


//メイン関数
main(){
    //バッファ文字列の定義
    signed char receivedWord[32]={0};
    //セッティングのおまじない
    OSCCON = 0b01110000;
    OPTION = 0b10000110;
    ADCON1 = 0b00000110;
    TRISA = 0b01000100;
    TRISB = 0b00000001;
    //タイマー0有効
    TMR0IE=1;
    GIE=1;
    RB2=0;

    //入力の初期化
    RXD=1;
    TXD=1;
    while(1){
    //無限ループ
        if(!RXD){
            //入力
            RB2=0;

            ReceiveWord(receivedWord);
            //出力
            RB2=1;
            SendWord(receivedWord);
            RB2=0;
        }

    }
    return;
}


このルーチンで内蔵クロック8MHzではビットレートが1200bps迄しか上げられませんでした。タイマが早過ぎてオーバーフローするようで す。

1/(1200Hz)~=833.333uSecはdelay関数でやるとサイクルが処理内容によって変わってしまうのでのでイニシャルテスト時 に使 いま したが、機能を積むに当たってタイマ0を使いました。もし 使うなら他のタイマを使うなどして下さい。

ここでこの周期がクロックで割り切れないのでちょっと工夫が必要です。
欲しいウエイト時間の長さの両脇に実際設定出来る長めのウエイト
1/(2MHz) x 128サイクルx14=896uS、短めのウエイト1/(2MHz) x 128サイクルx13=832uSecがあるわけです 。
アウトプットのときは 短めに、インプットのときは最初のビットを長めに、あとは短めに設定してあります。短め((833.3333・・・uSec-832uSec)- (833.333・・・uSec-896uSec)/(833.3333・・・uSec-832)~=47回に1回長めを使う等上手く使えば8 ビット 以上 の送受信も出来そうです。

このタイマは13回(10進)でオーバーフローするようにしてフラグを立てます。長くしたい場合14回
(10 進)にします。
これをウエイトに使います。

処理の概要は

スタートビット検出で文字列インプットモード(ReceiveWord関数)に入ります。これは区切り文字(文頭でスペースが割り当てられてい る)が 送ら れるまで続きます。このとき送られてくる文字 (8bit)をReceiveChar関数を使って受信します。この関数は呼ばれた後、

1. 本当にスタートビット(L)になっているか(これで同期をとる)
一定時間(長め)待ち、0ビット目をRXDフラグにて設定

2. 一定時間(短め)待つ
1〜7ビット目も同様

3. 7ビット目受信直後はウエイトせずストップビット(H)を確認 これも同期のためです

4. リターン

スペース受信後NULL文字(\0)を付け加え、引数のポインタの番地へ文字列を格納してリターンします。

文字列受信後そのままSendWord関数を使ってPCに文字列を返します。

この関数もReceiveWord関数と同じで一文字ずつ引数のポインタの番地の文字を読み込んでSendChar関数を使って送信します。

このSendChar関数は

1. スタートビット(L)送信

2. 一定時間(短め)待って0bit目設定

3. 1〜7ビット目も同様

3. 7ビット目送信直後ストップビット(L)送信後長めのウエイト

4. リターン

となっています。
区切り文字は文頭でスペースにしましたが書き換えてPC側と上手く通信して下さい。




一 応・・・ このサイトの内容は個人利用のみコピーを許可します。商用利用される際は事前にお問い合わせ下さい。
2012/9/23