こんにちは。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にすると、正常に送信できます。
(クッキーの食べすぎて太りそう…)