串口的使用
1、為什么要用串口?
這半年發(fā)生了什么?過完年就去找公司實習,在那里自我感覺進步很大。其實在公司大多都是自學,師傅基本不會給你說什么。但這并不能說明你的師傅對你不好,帶我的那個師傅只比我高一屆,但他的水平比我高的好多屆。他也是自學,也沒人告訴他該怎么做,因為老板也不太懂。所以自學能力很重要,當然有人帶你的話,這樣會更好。
在公司實習的時候,他們調(diào)試都是使用串口打印輸出信息,觀察程序從上電、初始化、運行數(shù)據(jù)什么的全部都顯示到PC機上。然后再一句一句分析它的打印信息,從而找到出錯的源頭。這使我對串口的認識有更深了一步,所以我決定在學習STM32的時候,開發(fā)流程跟在公司學的方法一樣——使用串口,觀察打印信息。
2、STM32跟PC機(也就是電腦)如何連接
解決辦法1、買一塊 MAX3232轉(zhuǎn)接板+一條USB轉(zhuǎn)串口線 +郵費=30塊左右
不過我用的是第一種,MAX3232+USB的串口線
max3232對于沒有串口的開發(fā)板可以充當電平轉(zhuǎn)換芯片,如何開發(fā)板有了電平轉(zhuǎn)換芯片,我便使用USB轉(zhuǎn)串口線經(jīng)行連接,這樣便靈活了。第二種只是用在既沒轉(zhuǎn)換芯片也沒USB轉(zhuǎn)串口的情況,不過對于最小系統(tǒng)板來說,它既可以下載程序,又可以當做串口來調(diào)試。至于臺式機就不需要USB轉(zhuǎn)串口線了,普通串口線即可。
3、代碼分析
PA9,PA10管腳要配置,
USART和uart有什么區(qū)別
#include"usart_debug.h"
void GPIO_Configuration(void)
{
GPIO_InitTypeDef
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//使能UASRT的時鐘
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//使能GPIOA的時鐘,開始的時候,我沒用這句話,調(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; //復用推挽輸出;我看網(wǎng)上有人說設置成GPIO_Mode_Out_PP普通推挽輸出也行,但實踐出真知,我試了發(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_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 ;
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ù),就需要對printf函數(shù)重定向到串口,以前工作中他們老是提重定向,什么串口重定向,USB重定向什么的,我也是云里霧里,如今給我的感覺就是將上層函數(shù)實現(xiàn)對底層硬件的操作
int
while(USART_GetFlagStatus(USART1, USART_FLAG_TC) !=SET); //網(wǎng)上的一些函數(shù)里面是沒有這一句代碼,如果不加的話,打印時第一個字符就會沒有,原因據(jù)說是硬件復位后,USART_FLAG_TC被置一了,而要發(fā)送數(shù)據(jù)必須讓其為底才可以,一表示數(shù)據(jù)發(fā)送發(fā)出的標志,也可以用這樣一句USART_ClearFlag(USART2,USART_FLAG_TC);清楚標準位。可是我沒這樣做一樣發(fā)成功了,這個疑惑以后再想明白。
USART_SendData(USART1, (uint16_t)ch);
while(USART_GetFlagStatus(USART1, USART_FLAG_TC) !=SET);
return ch;
}
void usart_debug_config(void)
{
GPIO_Configuration(); //IO口配置
USART1_config();
}
還就是usart_debug.h
#ifndef __usart_debug_H
#define
#include "stm32f10x.h"
#include
void usart_debug_config(void);
int fputc(int ch, FILE *f);
#endif
這里使用兩種方式一種是 使用普通的方式發(fā)送,另一種使用printf函數(shù)
其實還有一種USART_printf函數(shù)來實現(xiàn),這里不做介紹。有空看看區(qū)別一下printf和usart_printf,據(jù)說是支持格式多少的問題
#include"stm32f10x.h"
#include"usart_debug.h" //包含main函數(shù)里的調(diào)用函數(shù)
int main(void)
{
unsigned char TxBuf1[100] ={"發(fā)送字符串!!!\r\n"};
int
SystemInit();
usart_debug_config();
for( i = 0; TxBuf1[i] != '\0'; i++)
{
while(USART_GetFlagStatus(USART1, USART_FLAG_TC)==RESET);//這里跟分析fputc時是一樣的
USART_SendData(USART1 , TxBuf1[i]);//發(fā)送字符數(shù)組里的單個字符
while(USART_GetFlagStatus(USART1, USART_FLAG_TC)==RESET);
}
printf("hello world!
while(1)
{
;
}
}
仿真及調(diào)試
程序編好之后,在target option里要選擇 USE microLIB (keil自帶的微庫),這是使用非標準C庫,在編譯鏈接是,將我們編寫的fputc函數(shù)作為編譯的首選,否則就會編譯stdio.h里的fputc函數(shù)。
寫到這里我發(fā)現(xiàn)我這個代碼感覺不是很好,使用微庫而不用標準C庫,應該會有影響,我瞬間就明白了他們?yōu)槭裁匆约壕帉懼С州敵龈袷胶苌俚牡腢SART_printf函數(shù)了,后面目測我也會使用這個函數(shù)。
一個好的程序代碼就是結(jié)構(gòu)健全,BUG很少。把簡單做到極致
在KEIL里繼續(xù)軟件仿真
仿真和下載時要注意的是,仿真要選USE Simulation
然后點擊debug 進入調(diào)試界面
view--->serial windos -->UART 1
全速運行 其結(jié)果如下:
串口發(fā)送數(shù)據(jù)就到此為止。
串口接收數(shù)據(jù)
這兒寫的跟上面的已經(jīng)沒有關系了,不要搞混。
關于從PC機發(fā)送數(shù)據(jù)到STM32,這一部分則需要中斷來實現(xiàn),因為我們不需要時時刻刻都來檢測外部是否發(fā)出數(shù)據(jù)給STM32,因此只要有數(shù)據(jù)來,就觸發(fā)中斷。這里就需要配置NVIC了
void NVIC_Config(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPrio
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
USART_Cmd(USART1, ENABLE);
}
其他配置比如串口復用到IO配置,串口時鐘配置,和發(fā)送數(shù)據(jù)是一樣的。
串口中斷處理函數(shù):
void USART1_IRQHandler(void)
{
RX_status = USART_GetFlagStatus(USART1, USART_FLAG_RXNE);//讀取接收數(shù)據(jù)標志位,如果裝好了一幀數(shù)據(jù)則硬件將其置一。
if(RX_status == SET) {
USART_SendData(USART1 , USART_ReceiveData(USART1));//將收到的數(shù)據(jù)再由STM32發(fā)送給PC機。
while(USART_GetFlagStatus(USART1, USART_FLAG_TC)==RESET);//等待發(fā)送完成。
}
}
串口發(fā)送數(shù)據(jù)
串口發(fā)送接收數(shù)據(jù)