專(zhuān)注電子技術(shù)學(xué)習(xí)與研究
當(dāng)前位置:單片機(jī)教程網(wǎng) >> STM32 >> 瀏覽文章

STM32學(xué)習(xí)之串口的使用

作者:忙碌的小姚   來(lái)源:忙碌的小姚   點(diǎn)擊數(shù):  更新時(shí)間:2014年06月10日   【字體:

 

串口的使用

1、為什么要用串口?

     自上一篇寫(xiě)的時(shí)間是1月20號(hào),今6月7號(hào)了,半年沒(méi)更新了。

這半年發(fā)生了什么?過(guò)完年就去找公司實(shí)習(xí),在那里自我感覺(jué)進(jìn)步很大。其實(shí)在公司大多都是自學(xué),師傅基本不會(huì)給你說(shuō)什么。但這并不能說(shuō)明你的師傅對(duì)你不好,帶我的那個(gè)師傅只比我高一屆,但他的水平比我高的好多屆。他也是自學(xué),也沒(méi)人告訴他該怎么做,因?yàn)槔习逡膊惶。所以自學(xué)能力很重要,當(dāng)然有人帶你的話(huà),這樣會(huì)更好。

      不說(shuō)這些了,串口在調(diào)試的時(shí)候作用非常大。也學(xué)我們?cè)趯W(xué)51的時(shí)候,只是將程序下載到開(kāi)發(fā)板,看看是否能運(yùn)行起來(lái),通過(guò)數(shù)碼管將結(jié)果顯示出來(lái),從而就知道程序設(shè)計(jì)的正確性。以前我也是這樣做的,沒(méi)什么不好。

在公司實(shí)習(xí)的時(shí)候,他們調(diào)試都是使用串口打印輸出信息,觀察程序從上電、初始化、運(yùn)行數(shù)據(jù)什么的全部都顯示到PC機(jī)上。然后再一句一句分析它的打印信息,從而找到出錯(cuò)的源頭。這使我對(duì)串口的認(rèn)識(shí)有更深了一步,所以我決定在學(xué)習(xí)STM32的時(shí)候,開(kāi)發(fā)流程跟在公司學(xué)的方法一樣——使用串口,觀察打印信息。

2、STM32跟PC機(jī)(也就是電腦)如何連接

       我的STM32F103C8T6只是裸板,沒(méi)有串口芯片,當(dāng)然用的也是學(xué)生機(jī)——筆記本電腦,同樣也沒(méi)串口。

解決辦法1、買(mǎi)一塊 MAX3232轉(zhuǎn)接板+一條USB轉(zhuǎn)串口線(xiàn) +郵費(fèi)=30塊左右

              2、只需要買(mǎi)一塊PL2303的USB轉(zhuǎn)接板。這樣就將第一種的轉(zhuǎn)接板和連接結(jié)合到一起了。

不過(guò)我用的是第一種,MAX3232+USB的串口線(xiàn)  ,為什么不用方便的2種?

max3232對(duì)于沒(méi)有串口的開(kāi)發(fā)板可以充當(dāng)電平轉(zhuǎn)換芯片,如何開(kāi)發(fā)板有了電平轉(zhuǎn)換芯片,我便使用USB轉(zhuǎn)串口線(xiàn)經(jīng)行連接,這樣便靈活了。第二種只是用在既沒(méi)轉(zhuǎn)換芯片也沒(méi)USB轉(zhuǎn)串口的情況,不過(guò)對(duì)于最小系統(tǒng)板來(lái)說(shuō),它既可以下載程序,又可以當(dāng)做串口來(lái)調(diào)試。至于臺(tái)式機(jī)就不需要USB轉(zhuǎn)串口線(xiàn)了,普通串口線(xiàn)即可。

                連接示意如下所示:

3、代碼分析

       再寫(xiě)這里之前,應(yīng)該已經(jīng)學(xué)過(guò)模塊化編程了,STM32的每個(gè)XXX.c 和xxx.h 這都是模塊化編程。良好的程序,與其好的代碼風(fēng)格有關(guān)。你的代碼風(fēng)格跟你接觸教你寫(xiě)代碼的有很大關(guān)系。以前剛剛學(xué)習(xí)單片機(jī)編程,我還不信,現(xiàn)在我承認(rèn),跟教我單片機(jī)的老師風(fēng)格有些像。

 

       這里我做的是一個(gè)串口發(fā)送數(shù)據(jù)到PC機(jī)的例子:

       要讓STM32能夠順利發(fā)出數(shù)據(jù),要進(jìn)行如下配置

PA9,PA10管腳要配置,   USART也需要配置波特率,數(shù)據(jù)有幾位,停止位,數(shù)據(jù)流等。

USART和uart有什么區(qū)別   USART在做串口時(shí),兩者并不區(qū)別,但是USART有SPI的功能。還有串口通信為什么要配置波特率,停止位,硬件數(shù)據(jù)留這些,以前我也沒(méi)想過(guò),現(xiàn)在只覺(jué)得協(xié)議這個(gè)東西,是一個(gè)好的標(biāo)準(zhǔn)。

        建立一個(gè)usart_debug.c的文本,內(nèi)容如下

#include"usart_debug.h"

void GPIO_Configuration(void)

{

GPIO_InitTypeDef  GPIO_InitStructure;

RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//使能UASRT的時(shí)鐘

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//使能GPIOA的時(shí)鐘,開(kāi)始的時(shí)候,我沒(méi)用這句話(huà),調(diào)了兩天,跟源碼一句一句比才知道

RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);

GPIO_InitStructure.GPIO_Pin =GPIO_Pin_9;

GPIO_InitStructure.GPIO_Speed= GPIO_Speed_50MHz; //波特率較高,IO翻轉(zhuǎn)需較高頻率

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //復(fù)用推挽輸出;我看網(wǎng)上有人說(shuō)設(shè)置成GPIO_Mode_Out_PP普通推挽輸出也行,但實(shí)踐出真知,我試了發(fā)送是亂碼。;

GPIO_Init(GPIOA,&GPIO_InitStructure);

 

GPIO_InitStructure.GPIO_Pin =GPIO_Pin_10;

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //配置成浮空輸入,既然是輸入所以就不用配置IO口的頻率了

GPIO_Init(GPIOA,&GPIO_InitStructure);

}

 

void USART1_config(void)

{

USART_InitTypeDef     USART_InitStructure;

USART_InitStructure.USART_BaudRate= 115200;     //配置波特率

USART_InitStructure.USART_WordLength = USART_WordLength_8b; //配置數(shù)據(jù)位

USART_InitStructure.USART_StopBits = USART_StopBits_1;      //停止位

USART_InitStructure.USART_Parity= USART_Parity_No ;         //奇偶校驗(yàn)位

 

USART_InitStructure.USART_HardwareFlowControl= USART_HardwareFlowControl_None; //硬件流

 

USART_InitStructure.USART_Mode =USART_Mode_Rx |USART_Mode_Tx;

USART_Init(USART1, &USART_InitStructure);

 

USART_Cmd(USART1, ENABLE);

}

//這段很重要,如果要使用printf函數(shù)打印信息,需要加fputc函數(shù),就需要對(duì)printf函數(shù)重定向到串口,以前工作中他們老是提重定向,什么串口重定向,USB重定向什么的,我也是云里霧里,如今給我的感覺(jué)就是將上層函數(shù)實(shí)現(xiàn)對(duì)底層硬件的操作

int  fputc(int ch,  FILE*f){

while(USART_GetFlagStatus(USART1, USART_FLAG_TC) !=SET); //網(wǎng)上的一些函數(shù)里面是沒(méi)有這一句代碼,如果不加的話(huà),打印時(shí)第一個(gè)字符就會(huì)沒(méi)有,原因據(jù)說(shuō)是硬件復(fù)位后,USART_FLAG_TC被置一了,而要發(fā)送數(shù)據(jù)必須讓其為底才可以,一表示數(shù)據(jù)發(fā)送發(fā)出的標(biāo)志,也可以用這樣一句USART_ClearFlag(USART2,USART_FLAG_TC);清楚標(biāo)準(zhǔn)位?墒俏覜](méi)這樣做一樣發(fā)成功了,這個(gè)疑惑以后再想明白。

USART_SendData(USART1, (uint16_t)ch);

 

while(USART_GetFlagStatus(USART1, USART_FLAG_TC) !=SET);

return ch;

 

}

void usart_debug_config(void)  //提供給main函數(shù)調(diào)用的串口配置函數(shù)

{

GPIO_Configuration(); //IO口配置

USART1_config();   //串口配置

}

 

 

還就是usart_debug.h

#ifndef __usart_debug_H

#define    __usart_debug_H

 

#include "stm32f10x.h"

#include

 

void usart_debug_config(void);

int fputc(int ch, FILE *f);

 

 

#endif // __USART1_H

 

 

 

 

 

       main函數(shù):

這里使用兩種方式一種是 使用普通的方式發(fā)送,另一種使用printf函數(shù)

其實(shí)還有一種USART_printf函數(shù)來(lái)實(shí)現(xiàn),這里不做介紹。有空看看區(qū)別一下printf和usart_printf,據(jù)說(shuō)是支持格式多少的問(wèn)題

 

#include"stm32f10x.h"

#include"usart_debug.h" //包含main函數(shù)里的調(diào)用函數(shù)

int main(void)

{

 

unsigned char TxBuf1[100] ={"發(fā)送字符串!!!\r\n"};

int  i;

SystemInit();

usart_debug_config();

for( i = 0; TxBuf1[i] != '\0'; i++)

{

while(USART_GetFlagStatus(USART1, USART_FLAG_TC)==RESET);//這里跟分析fputc時(shí)是一樣的

USART_SendData(USART1 , TxBuf1[i]);//發(fā)送字符數(shù)組里的單個(gè)字符

while(USART_GetFlagStatus(USART1, USART_FLAG_TC)==RESET);

}

 

printf("hello world!  世界你好! \r\n"); //調(diào)用printf函數(shù)

while(1)

{

;

}

 

}

 

 

仿真及調(diào)試

程序編好之后,在target option里要選擇 USE microLIB (keil自帶的微庫(kù)),這是使用非標(biāo)準(zhǔn)C庫(kù),在編譯鏈接是,將我們編寫(xiě)的fputc函數(shù)作為編譯的首選,否則就會(huì)編譯stdio.h里的fputc函數(shù)。

寫(xiě)到這里我發(fā)現(xiàn)我這個(gè)代碼感覺(jué)不是很好,使用微庫(kù)而不用標(biāo)準(zhǔn)C庫(kù),應(yīng)該會(huì)有影響,我瞬間就明白了他們?yōu)槭裁匆约壕帉?xiě)支持輸出格式很少的的USART_printf函數(shù)了,后面目測(cè)我也會(huì)使用這個(gè)函數(shù)。

一個(gè)好的程序代碼就是結(jié)構(gòu)健全,BUG很少。把簡(jiǎn)單做到極致



在KEIL里繼續(xù)軟件仿真

仿真和下載時(shí)要注意的是,仿真要選USE Simulation  而下載要選右邊的JTAG。。。。

然后點(diǎn)擊debug 進(jìn)入調(diào)試界面

 

view--->serial windos -->UART 1

全速運(yùn)行 其結(jié)果如下:

串口發(fā)送數(shù)據(jù)就到此為止。

串口接收數(shù)據(jù)

這兒寫(xiě)的跟上面的已經(jīng)沒(méi)有關(guān)系了,不要搞混。

關(guān)于從PC機(jī)發(fā)送數(shù)據(jù)到STM32,這一部分則需要中斷來(lái)實(shí)現(xiàn),因?yàn)槲覀儾恍枰獣r(shí)時(shí)刻刻都來(lái)檢測(cè)外部是否發(fā)出數(shù)據(jù)給STM32,因此只要有數(shù)據(jù)來(lái),就觸發(fā)中斷。這里就需要配置NVIC了

void NVIC_Config(void)

{

NVIC_InitTypeDef NVIC_InitStructure;

 

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);      //優(yōu)先組為2

NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;  //打開(kāi)USART中斷通道

NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3; //搶占優(yōu)先級(jí)

NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;          //子優(yōu)先級(jí)

NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;     //中斷通道使能

NVIC_Init(&NVIC_InitStructure);

 

USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);    //串口接收中斷使能

 

USART_Cmd(USART1, ENABLE);

}

 

 

其他配置比如串口復(fù)用到IO配置,串口時(shí)鐘配置,和發(fā)送數(shù)據(jù)是一樣的。

 

 

串口中斷處理函數(shù):

 

 

void USART1_IRQHandler(void)

 int RX_status;  //自己定義一個(gè)標(biāo)志位

RX_status = USART_GetFlagStatus(USART1, USART_FLAG_RXNE);//讀取接收數(shù)據(jù)標(biāo)志位,如果裝好了一幀數(shù)據(jù)則硬件將其置一。

if(RX_status == SET) {

USART_SendData(USART1 , USART_ReceiveData(USART1));//將收到的數(shù)據(jù)再由STM32發(fā)送給PC機(jī)。

while(USART_GetFlagStatus(USART1, USART_FLAG_TC)==RESET);//等待發(fā)送完成。

 

}

}

       串口的發(fā)送接收大致如此,還是要搞懂什么數(shù)據(jù)位,硬件流,停止位,校驗(yàn)位什么,一個(gè)東西既然出現(xiàn)了就要好好分析出現(xiàn)的意義。想到了今天給電腦拆機(jī)清灰,電腦最好兩年清一次。反正每次上螺絲都會(huì)多出那么一兩個(gè)來(lái),是不是可以不用要?

       肯定不是,生產(chǎn)商肯定也知道節(jié)約成本什么,他們的結(jié)構(gòu)工程師也知道PCB上開(kāi)一個(gè)螺絲孔也是要收錢(qián)的。所以每一顆螺絲都有它的意義,所以什么校驗(yàn)位,停止位也有他的意義,即便我們不使用。其實(shí)剩的螺絲我也扔了,完全不知道上在哪兒!

        差不多就到這里了,有問(wèn)題或者需要STM32的學(xué)習(xí)資料,關(guān)注我的新浪微博@忙碌的小姚,私信我即可,

  學(xué)習(xí)知識(shí)一定要主動(dòng),這是多么痛的領(lǐng)悟啊。!

                                                                                                                                        記于2014年6月8日

 

串口發(fā)送數(shù)據(jù)     百度云盤(pán):http://pan.baidu.com/s/1i3kj31N

串口發(fā)送接收數(shù)據(jù)  百度云盤(pán):http://pan.baidu.com/s/1o6r0OIY

關(guān)閉窗口

相關(guān)文章