15.1 實(shí)驗(yàn)內(nèi)容
通過(guò)本實(shí)驗(yàn)主要學(xué)習(xí)以下內(nèi)容:
? 串口簡(jiǎn)介
? GD32F303串口工作原理
? 使用printf打印信息
15.2 實(shí)驗(yàn)原理
15.2.1 串口簡(jiǎn)介
串口,從廣義上看,指所有串行通信接口,比如RS232、RS422、RS485、SPI、IIC等。串行通訊是指僅用一根接受線和一根發(fā)送線就能將數(shù)據(jù)以位進(jìn)行傳輸?shù)耐ㄓ嵎绞?。和串行通訊相?duì)應(yīng)的是并行通訊,并行通信指一個(gè)傳輸接口可以傳輸8個(gè)bit即一個(gè)byte(有時(shí)甚至更多),雖然串行通信比并行通信慢,但是串口可以在僅僅使用兩根線的情況下就能實(shí)現(xiàn)數(shù)據(jù)的傳輸。
對(duì)于GD32F303來(lái)說(shuō),串口一般特指USART(通用同步異步收發(fā)器 ?)和UART(通用異步收發(fā)器 ?)。USART/UART提供了一個(gè)靈活方便的串行數(shù)據(jù)交換接口,數(shù)據(jù)幀可以通過(guò)全雙工或半雙工,同步或異步的方式進(jìn)行傳輸。紅楓派開(kāi)發(fā)板搭載的GD32F303ZET6總共擁有5個(gè)串口,(USART+UART) 對(duì)于一般應(yīng)用來(lái)說(shuō)足夠使用了。
15.2.2 串口通信幀介紹
GD32F303的串口通信只需要3條線組成,分別為T(mén)X(發(fā)送線)、RX(接收線)和GND,對(duì)于兩個(gè)通信結(jié)點(diǎn),TX和RX需要交叉連接,如下示例:
下面來(lái)介紹下串口數(shù)據(jù)幀組成。
以下為一個(gè)標(biāo)準(zhǔn)的串口通信幀:
一個(gè)串口幀由空閑、起始位、數(shù)據(jù)位、校驗(yàn)位以及停止位組成,傳輸?shù)臄?shù)據(jù)地位在前,高位再后。
空閑:串口TX或RX數(shù)據(jù)線上沒(méi)有傳輸任何數(shù)據(jù)時(shí),則該線處于為空閑狀態(tài)。空閑是TX和RX都是處于高電平。
起始位:占一個(gè)bit時(shí)間,標(biāo)志數(shù)據(jù)起始,由一個(gè)邏輯0(低電平)的數(shù)據(jù)位表示。當(dāng)發(fā)送方開(kāi)始發(fā)送一幀數(shù)據(jù)時(shí),起始位會(huì)最先發(fā)送,而對(duì)于接收方來(lái)說(shuō),檢測(cè)到起始位后,即使自己的接收時(shí)鐘與發(fā)送方的數(shù)據(jù)同步。
數(shù)據(jù)位:數(shù)據(jù)位緊跟在起始位之后,是通信中的真正有效信息。數(shù)據(jù)位的位數(shù)可以由通信雙方共同約定,對(duì)于GD32F303來(lái)說(shuō),數(shù)據(jù)位一般只有8位。
校驗(yàn)位:校驗(yàn)位占一bit時(shí)間,GD32F303可以設(shè)置校驗(yàn)位為:奇校驗(yàn)、偶校驗(yàn)或無(wú)校驗(yàn)。校驗(yàn)位是為了保證通信的可靠性,如果是奇校驗(yàn),需要保證傳輸?shù)臄?shù)據(jù)總共有奇數(shù)個(gè)邏輯高位,如果是偶校驗(yàn),需要保證傳輸?shù)臄?shù)據(jù)總共有偶數(shù)個(gè)邏輯高位。以傳輸傳輸數(shù)據(jù)A:0x01000001為例,如果設(shè)置了奇校驗(yàn),則需要在校驗(yàn)位傳輸“1”,如果是偶檢驗(yàn),則傳輸“0”。奇偶校驗(yàn)是由硬件處理的,當(dāng)設(shè)置好校驗(yàn)位后,硬件會(huì)自動(dòng)根據(jù)需要傳輸?shù)臄?shù)據(jù)自動(dòng)插入校驗(yàn)位。
注意:GD32F303的數(shù)據(jù)位可設(shè)置為8bit和9bit兩種方式,當(dāng)設(shè)置了奇校驗(yàn)或偶校驗(yàn),一定要將數(shù)據(jù)位設(shè)置為9bit;而設(shè)置了無(wú)校驗(yàn)時(shí),需要將數(shù)據(jù)位設(shè)置為8bit。
停止位:它是一幀數(shù)據(jù)的結(jié)束標(biāo)志,可以是1bit、1.5bit、2bit個(gè)邏輯“1”。
15.2.3 串口波特率
波特率是串口通信中一個(gè)非常重要的參數(shù),串口通信傳輸雙方必須要設(shè)置一樣的串口波特率,否則通訊就會(huì)出錯(cuò)。波特率可以認(rèn)為是比特率,即每秒傳輸?shù)奈粩?shù)。一般波特率可以是9600、19200、115200等等,如果設(shè)置波特率為9600,設(shè)置通信幀為1bit起始位+8bit數(shù)據(jù)幀+無(wú)校驗(yàn)+1bit停止位,那么每秒鐘最多可以傳輸9600bit/10bit = 960個(gè)字節(jié)。
現(xiàn)在重點(diǎn)介紹下GD32F303串口接收器的工作原理。GD32F303串口接收器采用的是16倍過(guò)采樣,即發(fā)送方發(fā)送數(shù)據(jù)后,GD32F303串口接受器都會(huì)將每個(gè)bit采樣16次:
在默認(rèn)情況下,接收器通過(guò)獲取三個(gè)采樣點(diǎn)的值來(lái)估計(jì)該位的值。如果在3個(gè)采樣點(diǎn)中有2個(gè)或3個(gè)為0,該數(shù)據(jù)位被視為0,否則為1。如果3個(gè)采樣點(diǎn)中有一個(gè)采樣點(diǎn)的值與其他兩個(gè)不同,不管是起始位,數(shù)據(jù)位,奇偶校驗(yàn)位或者停止位,都將產(chǎn)生噪聲錯(cuò)誤(NERR)。
15.2.4 GD32F303串口設(shè)置步驟
串口設(shè)置的一般步驟為:
15.3 硬件設(shè)計(jì)
紅楓派開(kāi)發(fā)板設(shè)計(jì)了一個(gè)USB轉(zhuǎn)UART功能,通過(guò)USB TypeC線將電腦和開(kāi)發(fā)板連接起來(lái),就可以進(jìn)行電腦和開(kāi)發(fā)板之間的串口通訊了。
15.4 代碼解析
15.4.1 在driver_uart.c中定義了串口初始化函數(shù)driver_uart_init。
C
void driver_uart_init(typdef_uart_struct *uartx)
{
rcu_periph_clock_enable(uartx->rcu_uart_x);
usart_deinit(uartx->uart_x);
driver_gpio_general_init(uartx->uart_rx_gpio);
driver_gpio_general_init(uartx->uart_tx_gpio);
if(uartx->uart_mode_rx==MODE_DMA)
{
if(uartx->uart_rx_dma!=NULL)
{
driver_dma_com_init(uartx->uart_rx_dma,(uint32_t)&USART_DATA(uartx->uart_x),NULL,DMA_Width_8BIT,DMA_PERIPHERAL_TO_MEMORY);
usart_interrupt_enable(uartx->uart_x,USART_INT_IDLE);
}
}
if(uartx->uart_mode_tx==MODE_DMA)
{
if(uartx->uart_tx_dma!=NULL)
{
driver_dma_com_init(uartx->uart_tx_dma,(uint32_t)&USART_DATA(uartx->uart_x),NULL,DMA_Width_8BIT,DMA_MEMORY_TO_PERIPHERAL);
}
}
usart_baudrate_set(uartx->uart_x, uartx->baudrate);
usart_receive_config(uartx->uart_x, USART_RECEIVE_ENABLE);
usart_transmit_config(uartx->uart_x, USART_TRANSMIT_ENABLE);
usart_word_length_set(uartx->uart_x, uartx->data_length);
usart_parity_config(uartx->uart_x, uartx->parity);
usart_enable(uartx->uart_x);
}
15.4.2 重定向函數(shù)int fputc(int ch, FILE *f)
要使用Printf,重定向函數(shù)fputc 是必須的。在C 語(yǔ)言標(biāo)準(zhǔn)庫(kù)中,fputc 函數(shù)是printf 函數(shù)內(nèi)部的一個(gè)函數(shù),功能是將字符ch 寫(xiě)入到文件指針file所指向文件的當(dāng)前寫(xiě)指針位置,簡(jiǎn)單理解就是把字符寫(xiě)入到特定文件中。我們使用USART 函數(shù)重新修改fputc 函數(shù)內(nèi)容,達(dá)到類(lèi)似“寫(xiě)入”的功能。
fputc定義在bsp_uart.c中
C
int fputc(int ch, FILE *f)
{
driver_uart_transmit_byte(&BOARD_UART,(uint8_t)ch);
return ch;
}
這個(gè)函數(shù)比較簡(jiǎn)單,就是調(diào)用了接口driver_uart_transmit_byte,該接口定義在driver_uart.c中:
C
Drv_Err driver_uart_transmit_byte(typdef_uart_struct *uartx,uint8_t data)
{
uint32_t timeout = driver_tick;
while(uartx->uart_control.Com_Flag.Bits.SendState==1)
{
if((timeout+UART_TIMEOUT_MS) <= driver_tick)
{
uartx->uart_control.Com_Flag.Bits.SendState=0;
return DRV_ERROR;
}
}
Drv_Err uart_state=DRV_SUCCESS;
uartx->uart_control.Com_Flag.Bits.SendSucess=0;
uartx->uart_control.Com_Flag.Bits.SendState=1;
uart_state=driver_uart_flag_wait_timeout(uartx,USART_FLAG_TBE,SET);
usart_data_transmit(uartx->uart_x,data);
uartx->uart_control.Com_Flag.Bits.SendSucess=1;
uartx->uart_control.Com_Flag.Bits.SendState=0;
return uart_state;
}
這段代碼作用是,循環(huán)去讀串口的TBE標(biāo)志位,并且將待發(fā)送的數(shù)據(jù)寫(xiě)到串口寄存器中。
15.4.3 main函數(shù)實(shí)現(xiàn)
串口初始化完成并定義好fputc重定向函數(shù)后,就可以通過(guò)printf函數(shù)往電腦上打印數(shù)據(jù)了。以下main函數(shù):
C
int main(void)
{
delay_init();
bsp_uart_init(&BOARD_UART);
bsp_led_init(&LED0);
while(1)
{
delay_ms(1000);
bsp_led_toggle(&LED0);
printf("printf:system driver_tick is %lld \r\n",driver_tick);
}
}
本例程main函數(shù)首先進(jìn)行了延時(shí)函數(shù)初始化,再配置開(kāi)發(fā)板上的USB串口,并設(shè)置了一個(gè)LED燈用來(lái)提示代碼運(yùn)行。while(1)循環(huán)中先延時(shí)1s,再翻轉(zhuǎn)一次LED狀態(tài),接著使用printf函數(shù)打印系統(tǒng)運(yùn)行tick時(shí)間。
15.5 實(shí)驗(yàn)結(jié)果
使用USB-TypeC線,連接電腦和板上USB to UART口后,配置好串口調(diào)試助手,即可看到每秒鐘串口打印的數(shù)據(jù)了。
紅楓派開(kāi)發(fā)板使用手冊(cè):??????????????????????????????????????????????????GD32F303紅楓派使用手冊(cè) - 飛書(shū)云文檔 (feishu.cn)