PIC32でUART通信

UART communication by PIC32
 
古い時代からあるシリアル通信のひとつであるUARTは双方の機器においてデータのやりとりの方法を事前に知っておかなければならない、現在の基準から見ると通信速度が遅いなどの理由により、今では別の手段、とくにパソコンではUSBに取って代わられました。一方で、最低2本の信号線があればやりとりが可能というシンプルさより、特別なデバッグツールなしにマイコンの内部処理をパソコンで調べたいときなどに、この通信方法が使われています。

PIC32ではいくつかの決められたピンの中から通信用に割り当てることができます。これはデータシートの「Peripheral Pin Select」で確認でき、「UnRXR」「UnRTX」のレジスタで指定できます。28ピン版では「A2,B6,A4,B13,B2」のいずれかをUART1のRXに、「A1,B5,B1,B11,B8」をUART2のRXに指定できます。
rxpin.c
U1RXRbits.U1RXR = 0b0100; // B2ピンをUART1・RXに割り当て
/*
0000 = RPA2
0001 = RPB6
0010 = RPA4
0011 = RPB13
0100 = RPB2
*/
TXでは「A0,B3,B4,B15,B7」をUART1に、「A3,B14,B0,B10,B9」をUART2に割り振れます。

出力であるTXは「RXに○ピンを割り当てる」という先の指定方法とは逆で、「○ピンにTXを割り当てる」という命令を記述します。
txpin.c
RPB3Rbits.RPB3R = 0b0010; // B3にTXコードを割り当てる
速度を決めるためのボーレート設定レジスタ「UnBRG」の値は、周辺クロック数に基づいて「PBCLK/(16*baud)-1」という式で計算します。たとえば周辺クロックが20MHz、通常ボーレートモード(UxMODEbits.BRGH=0)においてボーレートを115200にしたい場合は、「20000000/(16*115200)-1=9.8」となります。プログラムでは近似値の10を指定することになるので、実行時には若干の誤差が発生します(データの送信ごとに同期し直されるので、誤差が5%未満であれば、データへの影響はありません)。

「UxSTAbits.UTXEN=1」にすると送信が、「UxSTAbits.URXEN = 1」にすると受信が受け付けられます。「UxSTAbits.UTXBA=0」であれば、送信バッファに空きがあるので「UxTXREG」にデータを代入して送信します。受け取ったデータが存在する場合、「UxSTAbits.URXDA=1」になっているので、「UxRXREG」から取得します。

こちらはUARTシリアル通信のサンプルです。この例ではArduino UnoをUSBシリアル変換ケーブルの代わりに使用しています(スケッチは不要。やりかたはこちら)。PIC32のRB3をArduinoのRXに、RB4をTXピンに接続してください。
main.c
#include <p32xxxx.h>
#include <sys/attribs.h>

// 外部の8MHz水晶発振器から40MHzのクロックを生成するための設定
#pragma config FNOSC = PRIPLL // 発信源 = 主外部発振器
#pragma config POSCMOD = XT // 主発振の方法 = 外部高精度発振器を使う
#pragma config FPLLIDIV = DIV_2 // PLL入力分数 = x1/2
#pragma config FPLLMUL = MUL_20 // PLL逓倍比 = x20
#pragma config FPLLODIV = DIV_2 // PLL出力分数 = x1/2
#pragma config FPBDIV = DIV_2 //周辺モジュールクロック倍数 : x1/2

char str[51]; int sp = 0;

void sendString(const char *s, int slen)
{
    int i;
     for(i = 0; i < slen; i++){
        U1TXREG = s[i];
        while(U1STAbits.UTXBF) ; // バッファに空きが出るまで待機
    }
}

void __ISR(_UART_1_VECTOR, IPL4AUTO) OnUartReceiving(void) 
{
    IFS1bits.U1RXIF = 0; // UART1受信フラグのリセット

    if(U1STAbits.URXDA == 0) return; // バッファが空の状態なら何もしない
    str[sp] = (char)U1RXREG; // 受信データバッファのデータを格納
    
    // 改行コードを受信するかバッファがいっぱいになったら
    if(str[sp] == '\n' || sp > 50){
        // 受け取ったデータをそのまま返す
        sendString(str, sp + 1);
        sp = 0;
    }else{
        sp++;
    }
}

int main(void)
{
    ANSELB = 0x00;        // ポートBをすべてデジタルモードにする

    U1RXRbits.U1RXR = 0b0100; // UART1/RXにB2を
    RPB3Rbits.RPB3R = 0b0001; // B3をUART1/TXに
    
    U1STAbits.UTXEN = 1;  // UART1の送信を有効
    U1STAbits.URXEN = 1;  // UART1の受信を有効
    U1BRG = 129;          // ボーレート9600 [20M/(16*9600)-1]
    
    U1MODEbits.ON = 1;   // UART1を有効
    IPC8bits.U1IP = 4;   // UART1優先割り込み度
    IPC8bits.U1IS = 3;   // UART1副優先度
    
    IFS1bits.U1RXIF = 0; // UART1受信フラグのリセット
    IFS1bits.U1TXIF = 0; // UART1送信フラグのリセット
    
    IEC1bits.U1RXIE = 1; // UART1受信完了割り込みを有効
    IEC1bits.U1TXIE = 0; // UART1送信完了割り込みを無効
    
    INTCONbits.MVEC = 1;  // マルチベクタ割り込みを有効にする
       
    __builtin_enable_interrupts();  // マイコンにおける割り込みの有効

    sendString("Hello!\n", 7);

    while(1) ;
}
Arduino IDEを開いて、シリアルモニタよりArduinoのCOMポートを開きボーレートに「9600 bps」を指定してPICよりプログラムを実行すると、はじめに文字列が表示されます。あとは、Arduino IDEから任意の文字列を送信すると、全く同じ文字列がPICから返され、シリアルモニタにその内容が表示されます。
また、XC32コンパイラではprintf()を使うことができ、「__XC_UART」で出力先のUARTピンを指定すると、マイコン側で文字列のフォーマットと送信を一度に行えます。
printf.c
#include <stdio.h>

int main(void)
{
    __XC_UART = 1;
    U1RXRbits.U1RXR = 0b0100;
    RPB3Rbits.RPB3R = 0b0001;
    
    U1STAbits.UTXEN = 1;
    U1STAbits.URXEN = 1;
    U1BRG = 129;
    
    U1MODEbits.ON = 1;
    
    printf("Hello, world!\n");
    while(1);
}
2018/09/26