受信部は、赤外線で送られてきたデータを赤外受信モジュール(OSRB38C9AA)で電気信号に変換しPIC16F15325のEUSART1で受信する。受信するだけでは何にもならないので受信したデータはEUSART2を使って送信し、これをPCで受けてTeraTermで内容を確認する。
メインプログラムは先ずEUSART1の受信割込みを起動する。EUSART1がデータを1バイト受信し割込みが発生すると受信レジスタから受信データを取出し、受信バッファに格納する。受信バッファが満杯になるか改行コードを受信すると受信を停止し受信完了をメインプログラムに通知する。
メインプログラムは受信状況を監視しており、受信が完了すると受信バッファを送信処理に渡し(これを送信バッファとする)EUSART2の送信割込みを起動する。USART2の送信割込みが発生すると送信バッファからデータを1バイト取出し送信レジスタに書き込む。このとき最後のデータを書き込んだ場合は送信割込みを停止し送信処理完了をメインプログラムに通知する。しかし実際にはこのとき最大2バイトが物理的に未送信で送信完了迄には更に最大8msが必要である事に注意が必要である。
メインプログラムは3つのバッファを用意し、それらを順に使って処理を行う。つまり最初のバッファの受信が完了すると直ぐ送信に回し、同時に2つ目のバッファで受信処理を起動する。これを全3つのバッファを使って繰り返すことで疑似マルチタスク処理を行う。
送信スピードより受信スピードが速ければいずれバッファが不足し受信の取りこぼしが発生するが、受信赤外線回線は赤外線受信モジュールの特性で休止時間があるため最大効率75%だから今回は送受同速の通信でもデータが溢れることは無い筈である。
次図はPIC16F18326の割り込みロジックで、送信部で使ったPIC16F18326と同じと考えてよい。
受信部では2つのEUSARTを(同時に)使うため複数の要因で割込みが起こるが、割込みが発生した場合、1つのエントリーポイントが入口となるため、割込み処理ではどの要因で割込みが発生した識別してそれぞれ対応する処理を呼び出す必要がある。そのため割込みは次の様にする。
void __interrupt() isr(){ //割込み処理の入り口
if(PIR1bits.RX1IF && PIE3bits.RC1IE)
recvISR(); //UART1が受信済で割込み可なら受信処理
if(PIR1bits.TX2IF && PIE3bits.TX2IE)
sendISR(); //UART2が送信可で割込み可なら送信処理
}
今回の様なフロー制御の効かない通信方式の場合、送信より受信を優先して処理するのは通信の基本である。また割込み処理中の割込み(多重割込み)は禁止されるが、送信処理の割込みでも受信データの有無が先にチェックされ、もし受信データが存在すると望まない受信処理が実行される事になるので割込み可かどうかのチェックは必須である(最初はこれが無くて上手く行かなかった)。
割り込み処理では次のような状態(state)を導入する。RxSTOPは待機状態、RxBUSYは受信中、RxCMPLTは受信完了を表す。
typedef enum{ RxSTOP, RxBUSY, RxCMPLT } rxstate;
作業領域は次の様にする。
rxstate r1state = RxSTOP; // RX1 state
char *r1bufp; // RX Buffer
int r1buf_siz; // remaining RX buffer size
上位プログラムは受信部がRxIDLEにある時受信を起動する事が出来る。この時パラメータとして受信データを格納するバッファの場所とその大きさを指定する。そして状態を受信中(RxBUSY)とし、割込みを許可する。
void recvUART1(char *buf, int siz){
r1bufp = buf;
r1buf_siz = siz;
r1state = RxBUSY;
PIE3bits.RC1IE = ON;
}
データが受信され割込みが起こると次の受信処理が実行される。
void recvISR1(void){
byte st = RC1STA; // read status 1st
char c = RC1REG; // read data
if(st & 0x02){ // if overrun error
RC1STAbits.CREN = OFF; // clear CREN bit once
RC1STAbits.CREN = ON;
}
*r1bufp++ = (st & 0x06) ? '?' : c; //if any error chg char to '?'
r1buf_siz--;
if((r1buf_siz<=0)||(c=='\n')){ // completion
PIE3bits.RC1IE = OFF;
*r1bufp = 0;
r1state = RxCMPLT;
LED1(OFF);
}
}
先ず受信状態(RxBUSY)である事を確認した後、受信データと受信ステータスを取り込み、エラーがあれば然るべく回復処理を行い、正常であればバッファにデータを格納する(エラーの場合は'?'を入れる)。もし改行を受信するかバッファが満杯になれば割り込みを停止しバッファに文字列の終端(0)を書き込み、状態を受信完了(RxCMPLT)として上位プログラムに知らせる。
プログラムを頻繁にいじってPICに書き込むが、外から中身が分からないのでスタート時にバージョン等を出力するようにした。次は先に受信部を起動し、次に送信部を起動したときのTeraTermの表示である。
次の写真は赤外通信状態にある送信部(左)、受信部(右)である。受信部では4つのLEDで内部状態を示すようにしてデバッグを行った。
LED1 - 受信がBUSY状態にある
LED2 - 割り込み処理を実施中
LED3 - 2つ以上のバッファに受信済データが入っている
LED4 - 送信がBUSY状態にある
参考までに3月7日時点のソースコード(約300行)を以下に示す。
/*
* File: main.c
* Author: MYcrosLip
* Created on 2023/02/24
*
* Recieve Message on IR using interrupt
*
* * Programmed for PIC16F15325
*/
/*
* PIC16F15325 pin configuration
* PIN & PORT Allocation
* 5:RC5 - RX1 in
* 6:RC4 - Tx1 oout
* 2:RA5 - Rx2 in
* 3:RA4 - Tx2 out
* 10:RC0 - LED1
* 9:RC1 - LED2
* 8:RC2 - LED3
* 7:RC3 - LED4
*/
#include <stdio.h>
#include <stdlib.h>
#include <xc.h>
#define _XTAL_FREQ 32000000
// config word 1
#pragma config RSTOSC = HFINT32 //** Power-up Default Value for COSC
#pragma config FEXTOSC = OFF // FEXTOSC External Oscillator Mode Selection
#pragma config FCMEN = OFF // Fail-Safe Clock Monitor Enable
#pragma config CSWEN = ON // Clock Switch Enable
#pragma config CLKOUTEN = OFF // Clock Out Enable
#pragma config PWRTE = OFF // Power-up Timer Enable
#pragma config WDTE = OFF //** Watchdog Timer Enable
#pragma config LVP = OFF //** Low Voltage Programming
#pragma config CP = OFF // Program Memory Code Protection
#define MYNAME "IR-Rx-Intrpt-Basic"
#define VERSION " 0.03"
#define DATE " 2023/03/07"
#define COPYRIGHT1 " Copyright(C)2023"
#define COPYRIGHT2 " by MYcrosLip"
#define NL "\n"
#define NBLINK 2 // startup blink sign
#define BUFLEN 30 // Buffer Length in byte
#define NBUF 3 // Number of Buffer
#define ON 1
#define OFF 0
typedef unsigned char byte;
typedef unsigned int uint;
typedef enum{TRUE=1, FALSE=0} boolean;
// Syntax Sugar; Dialect Adaptation Codes
#define RX1PPS RX1DTPPS
#define RX2PPS RX2DTPPS
// LED Macros
#define LED4(x) LATCbits.LATC3 = x
#define LED3(x) LATCbits.LATC2 = x
#define LED2(x) LATCbits.LATC1 = x
#define LED1(x) LATCbits.LATC0 = x
typedef enum{ RxSTOP, RxBUSY, RxCMPLT } rxstate;
typedef enum{ TxSTOP, TxBUSY, TxCMPLT } txstate;
// PROTOTYPES
void Init_Clk(void);
void Initialize(void);
void blink(int);
void LEDs(byte);
void __interrupt() isr(void);
void resetUART1(void);
void recvUART1(char*, int);
void recvISR1(void);
void resetUART2(void);
void sendUART2(char*);
void sendISR2(void);
//========================================================
// Interrupt Dispatcher
//========================================================
void __interrupt() isr(){
LED2(ON);
if(PIR3bits.RC1IF && PIE3bits.RC1IE) recvISR1();
if(PIR3bits.TX2IF && PIE3bits.TX2IE) sendISR2();
LED2(OFF);
}
//========================================================
// UART1 Handler
//========================================================
rxstate r1state = RxSTOP; // RX1 state
char *r1bufp; // RX Buffer
int r1buf_siz; // remaining RX buffer size
txstate t1state = TxSTOP; // TX1 state
char *t1bufp; // TX Buffer
void resetUART1(){
byte b;
while(PIR3bits.RC1IF) b = RC1REG;
RC1STAbits.CREN = OFF; // clear CREN bit once
RC1STAbits.CREN = ON;
r1state = RxSTOP;
}
//========================================================
void recvUART1(char *buf, int siz){
r1bufp = buf;
r1buf_siz = siz;
r1state = RxBUSY;
LED1(ON);
PIE3bits.RC1IE = ON;
}
//========================================================
void recvISR1(void){
byte st = RC1STA; // read status 1st
char c = RC1REG; // read data
if(st & 0x02){ // if overrun error
RC1STAbits.CREN = OFF; // clear CREN bit once
RC1STAbits.CREN = ON;
}
*r1bufp++ = (st & 0x06) ? '?' : c ; //if any error chg char to '?'
r1buf_siz--;
if((r1buf_siz<=0)||(c=='\n')){ // completion
PIE3bits.RC1IE = OFF;
*r1bufp = 0;
r1state = RxCMPLT;
LED1(OFF);
}
}
//========================================================
// UART2 Handler
//========================================================
rxstate r2state = RxSTOP; // RX2 state
char *r2bufp; // Recieve Buffer
int r2buf_siz; // remaining buffer size
txstate t2state = TxSTOP; // TX2 state
char *t2bufp; // Send Buffer
void resetUART2(){
byte b;
while(PIR3bits.RC2IF) b = RC2REG;
RC2STAbits.CREN = OFF; // clear CREN bit once
RC2STAbits.CREN = ON;
t2state = TxSTOP;
}
//========================================================
// Initiator
void sendUART2(char *bufr){
t2bufp = bufr;
t2state = TxBUSY;
LED4(ON);
PIE3bits.TX2IE = ON;
}
//========================================================
// Interrupt Service
void sendISR2(void){
TX2REG = *t2bufp++;
if(*t2bufp == 0){
LED4(OFF);
PIE3bits.TX2IE = OFF; // Dsiable Interrupt
t2state = TxCMPLT; // Mission Completed
}
}
//=======================================================
// CONSTANTS
static char id[] = NL NL MYNAME VERSION DATE COPYRIGHT1 COPYRIGHT2 NL;
// VARIABLES
char buf[NBUF][BUFLEN+1];
int rbufn = 0, tbufn = 0; // recieve buffer & send buffer
int nbuf = 0; // number of filled buffers
//========================================================
// MAIN
//
int main() {
__delay_ms(1000); // 1 sec delay
Init_Clk();
#undef _XTAL_FREQ
#define _XTAL_FREQ 8000000 //chg Fosc to 8MHz dor __delay_ms()
Initialize();
blink(NBLINK); // Startup Salute
// Reset serial ports
resetUART1();
resetUART2();
rbufn = 0; tbufn = 0; nbuf = 0;
PIE3 = 0;
INTCONbits.PEIE = ON;
INTCONbits.GIE = ON;
sendUART2(id);
while(t2state!=TxCMPLT){};
t2state = TxSTOP;
//MAIN LOOP
while(TRUE){
if((r1state==RxSTOP)&&(nbuf < NBUF)){
recvUART1(buf[rbufn],BUFLEN);
}
if(r1state==RxCMPLT){
nbuf++;
rbufn++; rbufn %= NBUF; // next buffer
r1state = RxSTOP;
}
LED3( nbuf>1 );
if((t2state==TxSTOP) && (nbuf>0)){
sendUART2(buf[tbufn]);
}
if(t2state==TxCMPLT){
tbufn++; tbufn %= NBUF;
nbuf--;
t2state = TxSTOP;
}
}
return (EXIT_SUCCESS);
}
void Init_Clk(){
// OSC: HFINTOSC=16MHz, Fosc=8MHz
OSCCON1 = 0x61; // New ClockSource is HIFNTOSC, Div by 2
OSCEN = 0x40; // HFINTOSC OSC Manual Req Enable
OSCFRQ = 0x05; // HFINTOSC is set to 16MHz
}
void Initialize(void){
// PIN-Periphral Selection
// 5:RC5 - RX1 in
TRISCbits.TRISC5 = 1; // set Cbit5 to intput
ANSELCbits.ANSC5 = 0; // set Cbit5 to digital
RX1PPS = 0x15; // RC5 EUSART1
// 6:RC4 - Tx1 oout
TRISCbits.TRISC4 = 0; // set Cbit4 to output
ANSELCbits.ANSC4 = 0; // set Cbit4 to digital
RC4PPS = 0x0F; // TX1 EUSART
// 2:RA5 - Rx2 in
TRISAbits.TRISA5 = 1; // set Abit5 to intput
ANSELAbits.ANSA5 = 0; // set Abit5 to digital
RX2PPS = 0x05; // RA5 EUSART2
// 3:RA4 - Tx2 out
TRISAbits.TRISA4 = 0; // set Abit4 to output
ANSELAbits.ANSA4 = 0; // set Abit4 to digital
RA4PPS = 0x11; // TX2 UASRT2
//10:RC0 - LED1
TRISCbits.TRISC0 = 0; // set Cbit0 to output
ANSELCbits.ANSC0 = 0; // set Cbit0 to digital
// 9:RC1 - LED2
TRISCbits.TRISC1 = 0; // set Cbit1 to output
ANSELCbits.ANSC1 = 0; // set Cbit1 to digital
// 9:RC1 - LED3
TRISCbits.TRISC2 = 0; // set Cbit2 to output
ANSELCbits.ANSC2 = 0; // set Cbit2 to digital
// 7:RC3 - LED4
TRISCbits.TRISC3 = 0; // set Cbit3 to output
ANSELCbits.ANSC3 = 0; // set Cbit3 to digital
//
//EUART1 Tx : Fosc=8MHz
TX1STA = 0x24; // Async mode, Tx enable, high speed
RC1STA = 0x90; //
// Baud Rate Generator
BAUD1CON = 0x08; // BRG16
SP1BRGH = 0x03; // 1200: 1666(682) 2400:832(340) on SYNC=0,BRGH=1,BRG16=1
SP1BRGL = 0x40;
//EUART2 Tx : Fosc=8MHz
TX2STA = 0x24; // Async mode, Tx enable, high speed
RC2STA = 0x90; //
// Baud Rate Generator
BAUD2CON = 0x08; // BRG16
SP2BRGH = 0x03; // 1200: 1666(682) 2400:832(340) on SYNC=0,BRGH=1,BRG16=1
SP2BRGL = 0x40;
}
void blink(int n){
for(int i=n; i>0; i--){
LEDs(ON); __delay_ms(300);
LEDs(OFF); __delay_ms(300);
}
__delay_ms(300);
}
void LEDs(byte f){
LED1(f); LED2(f); LED3(f); LED4(f);
}
0 件のコメント:
コメントを投稿