こんにちは。Yukiです。

今回は、CH32V003F4P6でUART通信をしてみました。

 

 

 

  UART通信ついて

 

CH32V003F4P6にはUSARTが搭載されています。

USARTは、同期通信もできますが、今回は、非同期通信(UART)でやりたいと思います。

(RS232CやCH340 IC等でPCと通信する場合には、UARTの方が楽なため)

 

今回は、送信・受信のどっちもやってみますが、

送信では、まず、’a’等などの、一文字を送信してみた後に、関数化し、文字列を送信してみたいと思います。

 

受信では、小文字を送信したら、大文字を返すようなプログラムにしてみたいと思います。

 

 

 

  回路図と写真

 

 

(久しぶりにRS232C変換基板使いました…)

 

 

 

  UART 初期化をしてみる

 

今回はPLLを有効にして、メインクロックを48MHzにします。

 

    //PLL有効

    RCC->CTLR |= (1 << 24);

    //PLL安定動作まで待機

    while(RCC->CTLR & (1 << 25) == 0);

 

    //ADCプリスケーラ 16 (ADCPRE)

    RCC->CFGR0 = bit_replace(RCC->CFGR0, 0b11100, 5, 11);

 

    //クロックソース PLLに変更 (SW)

    RCC->CFGR0 = bit_replace(RCC->CFGR0, 0b10, 2, 0);

 

    //HCLKプリスケーラ 0 (HPRE)

    RCC->CFGR0 = bit_replace(RCC->CFGR0, 0b0000, 4, 4);

 

 

次に、USART1モジュールを有効化します。

 

    //USART1 有効

    RCC->APB2PCENR |= RCC_USART1EN;

 

 

次に、UARTを115200bpsに調整します。

ボーレートを調整する式は下で求められます。

 

 

つまり、式変形して、次のようになります。

 

 

 

今回は、クロック周波数が48MHz

ボーレートが115200bpsなので、

48MHz ÷ (16 × 115200) = 26.04166… となります。

 

このマイコンは、ボーレートレジスタが小数点まで調整できるタイプです。

15 ~ 4 ビットは整数部 3~0ビットは小数部です。

なので、 26をそのまま4シフトして、代入します。

 

 

USART1->BRR = (26 << 4);

 

 

次に、小数部です。

0.041666は2進数に変換すると、0b 0000 1010 1010…となります。

ということで、小数部は0のままでOKです。

 

 

次に、UARTの送信・受信を有効にします。

 

    //UART送信有効

    USART1->CTLR1 |= USART_CTLR1_TE;

    //UART受信有効

    USART1->CTLR1 |= USART_CTLR1_RE;

 

 

最後にUSART1モジュールを有効化します。

 

    //USART1 有効

    USART1->CTLR1 |= USART_CTLR1_UE;

 

 

 

 

  送信してみる

 

DATARレジスタに突っ込むと、自動で送信が開始されます。

 

今回は、送信が終了するまで、待機したいと思います。

 

まずは文字列を送信します。

 

    //dataを送信

    USART1->DATAR = data;

 

 

 

送信が完了すると、ハードウェアで1になります。

 

    //TCフラグを0にして、1になるまで待機

    USART1->STATR &= ~USART_FLAG_TC;

 

    while((USART1->STATR & USART_FLAG_TC) == 0);

 

 

 

  送信の結果

 

ちゃんとTera termに受信できました。

 

 

  関数化1

 

送信はよく行うので、関数化します。

 

//UART 送信用関数 (1byteのみ)

//data:送信データ(1byte)

void UART1_write(uint8_t data) {

    //dataを送信

    USART1->DATAR = data;

    //TCフラグを0にして、1になるまで待機

    USART1->STATR &= ~USART_FLAG_TC;

    while((USART1->STATR & USART_FLAG_TC) == 0);

}

 

 

 

 

  文字列を送信する

 

先ほど作った、関数を使って、文字列を送信できるようにします。

 

//UART 文字列送信用関数 (改行なし)

//data:送信する文字列

void UART1_print(uint8_t data[]) {

    for (int i = 0; data[i] != '\0'; i++) {

        UART1_write(data[i]);

    }

}

 

 

Tera termに問題なく送信できていますね。

 

 

 

 

  受信もやる

 

受信は、Tera term上で小文字を打ったら、大文字が返ってくるような仕様にします。

 

受信は、STATRジレスタのRXNEビットを確認します。0であれば、未受信(または受信中) 1であれば、受信完了(レジスタから取得可能)となります。

なので、条件分岐を使います。

 

//UART 受信関数 (1byteのみ)

//受信できるデータがない場合、-1を返す

int8_t UART1_read(void){

    //受信するデータが存在する場合

    if(USART1->STATR & USART_FLAG_RXNE){

        //フラグを0にして、次の受信に備える

        USART1->STATR &= ~USART_FLAG_RXNE;

        return USART1->DATAR; //受信データを返し、終了

    }

    //受信データがない場合、-1を返して、終了

    return -1;

}

 

 

参考例です。

 

こんな感じで書いてみました。

 

        int8_t send_data;

        send_data = UART1_read();

 

        //受信データが-1ではなかったら

        if(send_data != -1){

            UART1_write(send_data);

            UART1_write(send_data + ('A' - 'a'));

        }

 

正常に動作してるっぽいですね。良かった。

 

 

 

  余談

 

日本語も送受信できます。ただし、MounRiverの謎仕様で、初期の文字コードがGBKになるので、UTF-8にすると、正常に送信できます。

 

 

(クッキーの食べすぎて太りそう…)