12 August 2012

PIC12F675/690でソフトウエア方式RS232C出力

UART機能を持たないPIC12F675,PIC12F690などで、ソフトウエアUARTを用いてRS232C経由でコンピュータにデータを送る方法。

■ 検証環境
・PIC12F675
・通信方式は2400bps, ストップビット=1bit, パリティ無し
・Microchip MPLAB X IDE ver 1.30
・Microchip MPLAB XC8 ver 1.01

20120812-test-circuit.jpg
ブレッドボード上に作成したテスト回路

20120812-circuit.png
回路図 (MAX232よりPC側の回路は省略)


■ タイミング微調整前後の受信状態の例

2400bpsでの1ビットの信号持続時間は 1/2400 = 0.0004166 → 416マイクロ秒 となる。 プログラム中でこの時間を出すために、__delay_usマクロやタイマーを使うが、その場合の時間設定は416usecではなく、微妙な調整が必要。(コンパイラが吐き出すアセンブラコードの最適化度によっても、あるいはOSCCALがずれてしまっている場合など、個別にあわせこんでいく必要がある)

20120812-pc-receive.png
タイミング調整後(上)と、調整前(下)の受信状況

■ 共通部分


#include <stdio.h>
#include <stdlib.h>
#include<xc.h>

#include "functions.h"

__CONFIG(CP_OFF & CPD_OFF & BOREN_OFF & MCLRE_OFF & WDTE_OFF & PWRTE_ON & FOSC_INTRCIO);

/* Clock Frequency (use by __delay_ms function) */
#define _XTAL_FREQ 4000000

/* printf関数の出力先のスタブ関数を定義する */
void putch(unsigned char ch){
rs232c_putchar(ch);
return;
}


int main(int argc, char** argv) {
TRISIO = 0b00001000; /* IOポートGP0...GP5を出力モードに設定(GP3は常に1,入力専用) */
CMCON = 0x07; /* コンパレータを使わない設定 */
INTCONbits.PEIE = 0; /* 割り込み機能をOFF */
GPIO = 0b00000010; /* RA0...RA5をV-low, TXに接続したGP1はプルアップ */
ANSEL = 0b00000000; /* A/Dポートを無効化(A/Dは初期化後は有効であるため、必ず無効化する) */

for(;;){
rs232c_puts("Test String from PIC12F675\n");
for(unsigned char i=0x20; i<=0xd0; i+=0x10){
if(i==0x80||i==0x90) continue;
printf("%02X : ", i); /* 出力はputch()スタブ関数 */
for(unsigned int j=0; j<=0xf; j++){
rs232c_putchar(i+j);
}
rs232c_puts("\n");
}
rs232c_puts("\n");
__delay_ms(500);
}

}

■ __delay_us (遅延マクロ)を使ったソフトウエアUART


/* RS-232C送信:1文字(1バイト)送信する */
/* 2400bps, stopbit=1bit, parity=off */
/* __delay でタイミングを取る方式 */
void rs232c_send_char_delay(unsigned char ch){
/* start bit */
GPIObits.GP1 = 0; /* TX */
__delay_us(370); /* 2400bpsのタイミングは416マイクロ秒。この関数内でのwaitは360から380の間で確認済み */

for(unsigned int i=0; i<8; i++){
if(ch>>i & 0x01){
GPIObits.GP1 = 1; /* TX */
}
else{
GPIObits.GP1 = 0; /* TX */
}
__delay_us(370);
}

/* stop bit */
GPIObits.GP1 = 1; /* TX */
__delay_us(370);
__delay_us(10);
}


■ プロセッサタイマーを使ったソフトウエアUART


/* RS-232C送信:1文字(1バイト)送信する */
/* 2400bps, stopbit=1bit, parity=off */
/* Timer1でタイミングを取る方式 */
void rs232c_send_char_timer(unsigned char ch){
T1CON = 0b00010001; /* 00-01-000-1 : Timer1設定を1:2プリスケール, Timer ON */
/* (1/4MHz)*4*2PreScale = 2 micro-sec */
/* 2400bpsのタイミングは416マイクロ秒。416/2 = 208タイマーカウント */
/* 65536 - 208 = 65328 = 0xff30 この値が理論値だが、この関数内では190から205カウント(0xff42から0xff33)が許容値 */
TMR1H = 0xff; /* 0xff3a >> 8 */
TMR1L = 0x3a; /* 0xff3a & 0x00ff */
PIE1bits.TMR1IE = 0; /* Timer1割り込みを無効化 */
PIR1bits.TMR1IF = 0; /* Timer1カウンターオーバーフローフラグをクリア */

/* start bit */
GPIObits.GP1 = 0; /* TX */
while(!PIR1bits.TMR1IF) ; /* Timer1のカウントが完了するのを待つ(416マイクロ秒) */

for(unsigned int i=0; i<8; i++){
TMR1H = 0xff; /* 0xff3a >> 8 */
TMR1L = 0x3a; /* 0xff3a & 0x00ff */
PIR1bits.TMR1IF = 0; /* Timer1再スタート */
if(ch>>i & 0x01){
GPIObits.GP1 = 1; /* TX */
}
else{
GPIObits.GP1 = 0; /* TX */
}
while(!PIR1bits.TMR1IF) ; /* Timer1のカウントが完了するのを待つ(416マイクロ秒) */
}

TMR1H = 0xff; /* 0xff3a >> 8 */
TMR1L = 0x3a; /* 0xff3a & 0x00ff */
PIR1bits.TMR1IF = 0; /* Timer1再スタート */
/* stop bit */
GPIObits.GP1 = 1; /* TX */
while(!PIR1bits.TMR1IF) ; /* Timer1のカウントが完了するのを待つ(416ミリ秒) */

T1CONbits.TMR1ON = 0; /* Timer1停止 */
__delay_us(10);

}


■ 参考にしたウエブページ
実験2: ソフトウェア・シリアル通信
12F675覚書
12F629、12F675でシリアル通信