STM32F030 Nucleo-開(kāi)發(fā)調(diào)試的經(jīng)驗(yàn)USART的重要性
先聲明一點(diǎn),我自己不是高手,也不是大神,只是積累了一點(diǎn)點(diǎn),想分享一下罷了!
還記得那會(huì)我在初學(xué)51單片機(jī)時(shí),當(dāng)?shù)弥狿89V51系列單片機(jī)支持在線(xiàn)仿真、跟蹤代碼時(shí),那是一個(gè)興奮啊,無(wú)論如何都要弄一個(gè)來(lái)玩玩,進(jìn)行代碼跟蹤!
當(dāng)在開(kāi)始接觸和學(xué)習(xí)STM32是,那時(shí)候知道了J-Link的存在,它出了燒錄,也能代碼跟蹤,單步執(zhí)行。最后有知道了St-Link的存在,它針對(duì)意法半導(dǎo)體的MCU作調(diào)試和燒錄!當(dāng)然了,還有ST-Link和J-Link的各種針對(duì)于STM32的兼容用法。但知道當(dāng)我開(kāi)始使用別人的代碼進(jìn)行開(kāi)發(fā)的時(shí)候,無(wú)可想象,使用J-link或者ST-Link進(jìn)行在線(xiàn)仿真調(diào)試(代碼跟蹤)顯得矯情了!
對(duì)于最底層的硬件驅(qū)動(dòng)調(diào)試來(lái)說(shuō),使用J-link或者ST-Link進(jìn)行代碼跟蹤效果是比較可觀(guān)的,因?yàn)橹灰驗(yàn)槲覀兛梢钥吹郊拇嫫鞯闹颠M(jìn)行邏輯的判斷和配置正確與否的判斷。當(dāng)然,也可以在某些特殊的情況的要求下,進(jìn)行代碼的優(yōu)化,也可以使用。至于其他的情況,自我感覺(jué)使用J-Link/ST-Link進(jìn)行代碼跟蹤顯得很矯情了!
通常一個(gè)大的項(xiàng)目或者一個(gè)產(chǎn)品項(xiàng)目中,整一個(gè)軟件程序基本上不可能是同一個(gè)人寫(xiě)的,可能同事寫(xiě)的,也有可能是芯片原廠(chǎng)提供的方案,而且各個(gè)程序員的風(fēng)格各異(對(duì)于對(duì)編程風(fēng)格有要求的公司,情況可能會(huì)好一點(diǎn),總之有些程序員的程序風(fēng)格可以叫做慘不忍睹,總之,在調(diào)試程序一天,你就會(huì)罵他娘一天,直到罵到公司不再使用這個(gè)方案或者你辭職,也不知道這類(lèi)程序員是咋想的,為毛原意讓人家罵他娘,他都不愿意修正或者學(xué)習(xí)一下風(fēng)格),除了這些還有這項(xiàng)技術(shù)的難度、算法的復(fù)雜程度等等,所以通常會(huì)將軟件進(jìn)行分層,最底層就是啟動(dòng)之后硬件驅(qū)動(dòng)了,然后就是與硬件無(wú)關(guān)的功能代碼了(當(dāng)然,我只是隨便舉個(gè)例子,比如Linux、Android這些程序就分成了好幾層,而且非常復(fù)雜),還有就是,有些技術(shù)是原廠(chǎng)或者方案公司不方便外漏的技術(shù),所以他們所提供的二次開(kāi)發(fā)包SDK通常關(guān)鍵技術(shù)已經(jīng)封裝成庫(kù),那么使用J-Link/ST-Link來(lái)調(diào)試跟蹤代碼已經(jīng)不現(xiàn)實(shí)了,因?yàn)樵谝粋€(gè)項(xiàng)目中我們不可能了解到全部的代碼,也不可能去看全部的代碼,只因?yàn)闆](méi)有時(shí)間。通??赡芪覀冎恍枰雷约贺?fù)責(zé)的這部分的邏輯流程和進(jìn)入接口和向外輸出接口即可,也就是說(shuō),我們自己只能在小小的空間里面做事,萬(wàn)萬(wàn)不能越界。這時(shí)候,UART/USART同步/異步串行口通信將起到了巨大的作用。很簡(jiǎn)單,只需在其接口Tx和Rx與PC機(jī)建立串口通信,使用串口調(diào)試助手與其通信(打印或者輸入標(biāo)志到MCU),即可通過(guò)串口調(diào)試助手的打印現(xiàn)象來(lái)進(jìn)行代碼的跟蹤。說(shuō)白了,就是在我們代碼的某處(需要的地方)將某些標(biāo)志或者數(shù)據(jù)打印出來(lái),既可以輕易的對(duì)代碼進(jìn)行跟蹤。就可以知道代碼的執(zhí)行邏輯和步驟。我現(xiàn)在這可比J-Link/ST-Link簡(jiǎn)單多了。
所以,基于這樣的一個(gè)思想,每當(dāng)我進(jìn)行新的硬件代碼調(diào)試時(shí)(不管是自己寫(xiě)驅(qū)動(dòng)還是使用SDK包),只要硬件支持UART/USART,第一件事就是點(diǎn)燈(能夠控制GPIO口)和調(diào)通UART/USART(以便進(jìn)行代碼的調(diào)試),這兩點(diǎn)自我覺(jué)得是非常重要的。
到這里,基本上經(jīng)驗(yàn)之談已經(jīng)結(jié)束,下面就記錄一下STM32F030 Nucleo板卡的學(xué)習(xí) 。
首先,有必要搞清楚幾點(diǎn):
(1)UART和USART之間的區(qū)別:
UART:Universal Asynchronous Receiver and Transmitter,通用異步收發(fā)器,[Bus Signal] Tx , Rx
51單片機(jī)上面的就是這個(gè)了,ARM架構(gòu)的MCU/CPU部分也還支持。
USART:Universal Synchronous Asynchronous Receiver and Transmitter,通用同步異步收發(fā)器,[Bus Signal]Tx , Rx , CK
從名字上,就可以看出了,USART比UART高大上多了,只是在UART之上增強(qiáng)了通信協(xié)議。
USART支持同步模式,因此USART需要同步信號(hào)USART_CK(仔細(xì)的觀(guān)察STM32單片機(jī),就可以發(fā)現(xiàn)這樣的引腳),通常同步信號(hào)通信相對(duì)而言是比較少用的,所以通常的調(diào)試中,UART和USART的使用方式是一樣的,都使用異步模式。
(2)STM32 USART通信的各種模式:
不用多說(shuō),我相信看到這個(gè)表就一目了然了!
當(dāng)然,通過(guò)MAX485或者RS485等芯片,UART/USART接口可以作為458通信接口。
那么現(xiàn)在就要把牛客板卡的USART1調(diào)通,與PC機(jī)進(jìn)行串口通信,
(1)找到使用的USART1引腳。
查看Datasheet,得知如下圖:
STM32F030 USATU1的復(fù)用第一功能引腳就如上了,其中有GPIOA8作為USART1_CK,同步模式時(shí)作為USART同步通信的同步時(shí)鐘引腳;GPIOA9腳為USART1通信時(shí)的發(fā)送引腳;GPIOA10腳作為USART1通信時(shí)的接收引腳;GPIOA11和GPIOA12引腳作為USART1通信當(dāng)使用硬件流控時(shí),作為流控控制引腳。然而,在這里咱不玩什么同步模式,也不玩流控,所以只需要配置GPIOA9和GPIOA10引腳即可。
(2)找到??桶蹇ǖ腢SART1的引腳位置。
查看牛客板卡的用戶(hù)手冊(cè)《STM32 Nucleo-64 boards》,找到下圖:
(3)在庫(kù)中找到USART相關(guān)的接口。
先確定要調(diào)試功能:
打開(kāi)GPIO時(shí)鐘和USART1時(shí)鐘,選擇時(shí)鐘源,配置復(fù)用IO模式:
void RCC_AHBPeriphClockCmd(uint32_t RCC_AHBPeriph, FunctionalState NewState); //GPIO時(shí)鐘使能函數(shù)
void RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState); //USART1時(shí)鐘使能函數(shù)
void RCC_USARTCLKConfig(uint32_t RCC_USARTCLK);//USART1時(shí)鐘源選擇函數(shù)
void GPIO_PinAFConfig(GPIO_TypeDef* GPIOx, uint16_t GPIO_PinSource, uint8_t GPIO_AF);//IO口復(fù)用配置函數(shù)。
配置GPIO口:
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct)
USART初始化并啟動(dòng)USART通信:
void USART_Init(USART_TypeDef* USARTx, USART_InitTypeDef* USART_InitStruct);//USART初始化函數(shù)
void USART_Cmd(USART_TypeDef* USARTx, FunctionalState NewState);//USART使能函數(shù)
void USART_ClearFlag(USART_TypeDef* USARTx, uint32_t USART_FLAG);//USART清標(biāo)志函數(shù)
配置中斷:
對(duì)于USART的接收功能來(lái)說(shuō),可以使用兩種方式,分別是循環(huán)檢測(cè)接收方式和中斷方式接收數(shù)據(jù),前一種方式會(huì)阻塞占用MCU,導(dǎo)致效率低下,而中斷方式接收數(shù)據(jù)則不會(huì)阻塞,所以這里使用中斷方式接收數(shù)據(jù)。
void USART_ITConfig(USART_TypeDef* USARTx, uint32_t USART_IT, FunctionalState NewState);//USART中斷使能函數(shù)
void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct);//嵌套向量中斷控制器初始化配置函數(shù)
接收和發(fā)送數(shù)據(jù):
FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint32_t USART_FLAG);//獲取USART狀態(tài)標(biāo)識(shí)函數(shù)
uint16_t USART_ReceiveData(USART_TypeDef* USARTx);//USART讀取數(shù)據(jù)函數(shù)
void USART_SendData(USART_TypeDef* USARTx, uint16_t Data);//USART發(fā)送數(shù)據(jù)函數(shù)
那么這么多函數(shù)是從哪里找的呢??答案是,在keil上搜索得到的,所以這種開(kāi)發(fā)的方式就是,當(dāng)調(diào)試某個(gè)功能時(shí),找到與之相關(guān)的文件比如:stm32f0xx_usart.c和stm32f0xx_usart.h文件,其由于GPIO相關(guān),又去找stm32f0xx_gpio.c和stm32f0xx_gpio.h文件,其時(shí)鐘還與RCC相關(guān),就去找stm32f0xx_rcc.c和stm32f0xx_rcc.h文件,又還與NVIC相關(guān),所以又去找stm32f0xx_misc.c和stm32f0xx_misc.h文件??傊褪且痪湓?huà),它需要什么就給它什么。
還有個(gè)問(wèn)題就是,你咋知道先配置什么,再配置什么的???答案是:其實(shí)我也不知道,是參考手冊(cè)或者編程手冊(cè)告訴我的,比如下圖:
圖已經(jīng)告訴咱數(shù)據(jù)是怎么傳輸?shù)牧?,?yīng)該配置啥寄存器等等,那咱不就是知道怎么配置了么》??就是這樣的。
(4)配上COMS電平轉(zhuǎn)TTL電平的模塊,比如MAX232,MAX3232,RS232,PL2303等。與PC機(jī)連接通信。
我用的就是上圖這種模塊了,連接是:
MCU_Tx---------模塊Rx
MCU_Rx---------模塊Tx
然后就與PC機(jī)連接,再連上串口調(diào)試助手。
OK!到這里就還有一點(diǎn)要講的了!那就是波特率,其實(shí)就是單片機(jī)或計(jì)算機(jī)串口通信時(shí)的速率。其實(shí)在手冊(cè)當(dāng)中也給咱講的一清二楚了,
人家講的很清楚,還給咱舉了例子,如何計(jì)算,如何配置。其實(shí)如上圖的計(jì)算過(guò)程只是對(duì)于玩操作寄存器的人才需要考慮的計(jì)算,如果直接用庫(kù)函數(shù)開(kāi)發(fā),直接指定波特率就好了。
還有就是,普通的通信應(yīng)該配置成什么呢???三個(gè)字“8N1,無(wú)奇偶”,啥意思呢??8個(gè)數(shù)據(jù)為,無(wú)流控,1個(gè)停止位,無(wú)奇偶校驗(yàn),就是這么簡(jiǎn)單。
且看庫(kù)的配置結(jié)構(gòu)體:
指定波特率,設(shè)置數(shù)據(jù)位長(zhǎng)度8位,1個(gè)停止位,無(wú)奇偶校驗(yàn),輸入和輸出模式,無(wú)流控。如下圖:
具體初始化如下:
USART初始化:
NVIC初始化:
初始化就如上了。
那么,咱要發(fā)送數(shù)據(jù)哇!所以,咋就寫(xiě)寫(xiě):
發(fā)送一個(gè)字節(jié):
發(fā)送字符串:
發(fā)送十進(jìn)制數(shù)據(jù):
OK!發(fā)送的就是這樣,沒(méi)什么好解釋的!哈哈!
但是,如果用來(lái)進(jìn)行調(diào)試的話(huà),以上方法好像不太給力哦,為毛呢??比如所咱想發(fā)送字符串和數(shù)據(jù)混雜呢》》按照上面的方法,那可得寫(xiě)好幾句打印函數(shù)呢!嘿嘿!那咱就把ANSI標(biāo)準(zhǔn)C的printf移植過(guò)來(lái)用吧!腫么玩呢??其實(shí),兩步就好:
(1)包含頭文件#include
(2)如下圖:
這幾個(gè)意思呢??而且,明眼人一看就能看見(jiàn),在咱的工程中,壓根就沒(méi)有調(diào)用int fputc(int ch, FILE *f)這個(gè)函數(shù),只是寫(xiě)在那里了而已,哈哈!其實(shí)呢,int fputc(int ch, FILE *f)函數(shù)是printf函數(shù)開(kāi)放的一個(gè)從硬件讀取數(shù)據(jù)的接口,那么在哪里調(diào)用呢??肯定在C標(biāo)志庫(kù)調(diào)用啦!只是咱看不到罷了。所以,不用管它,寫(xiě)上就好!哈哈!
這樣,咱就能在工程中直接使用printf函數(shù)了,至于怎么使用,不會(huì)的話(huà),自己好好的去學(xué)習(xí)C吧。
發(fā)送數(shù)據(jù)講完了,咱就說(shuō)說(shuō)接收數(shù)據(jù)了,我在這里就簡(jiǎn)單的表示一下,具體的還要看實(shí)際應(yīng)用的需要修改。
首先咱得找到stm32f0xx_it.c文件,然后再文件中任意位置寫(xiě)函數(shù)
void USART1_IRQHandler(void)
{
}
那么這個(gè)函數(shù)名從哪來(lái)的呢??又是干啥的呢??
還記得前面提到的在啟動(dòng)文件建立的中斷向量表嗎?打開(kāi)startup_stm32f030.s文件,中斷向量表如下:
沒(méi)錯(cuò),當(dāng)發(fā)生中斷時(shí),MCU會(huì):
(1)將現(xiàn)有數(shù)據(jù)保存在相應(yīng)寄存器中,即保存現(xiàn)場(chǎng)
(2)跳轉(zhuǎn)到中斷向量表中查詢(xún)發(fā)生中斷的外設(shè),并找到中斷入口地址
(3)執(zhí)行中斷功能
(4)跳出中斷,從相應(yīng)寄存器中讀取數(shù)據(jù),即恢復(fù)現(xiàn)場(chǎng)
中斷的過(guò)程就是上面這幾個(gè)了,那么void USART1_IRQHandler(void)函數(shù)就是USART1的中斷入口地址了,就是這么簡(jiǎn)單。再多說(shuō)一點(diǎn)就是,有些人說(shuō),看見(jiàn)別人在函數(shù)的任意位置填寫(xiě)任意的函數(shù),他就直接成了中斷函數(shù)了,為毛這里要有ST規(guī)定了名字啊???其實(shí)我想說(shuō),只要你開(kāi)心,想怎么樣都可以;首先,void USART1_IRQHandler(void)函數(shù)可以存在于工程中的任意C文件,再就是,如果想自己命名,那就修改一下中斷向量表的名字為你想要的名字即可,只要你開(kāi)心。
OK!實(shí)現(xiàn)就如下圖了:
上圖首先檢測(cè)USART1讀標(biāo)志,然后讀取數(shù)據(jù),再然后將其打印出來(lái)個(gè)咱看,數(shù)據(jù)是否發(fā)送成功。然后情況標(biāo)志位。在這里只是驗(yàn)證通信的成功。
所以當(dāng)我們從串口調(diào)試助手發(fā)送數(shù)據(jù)后,發(fā)送的數(shù)據(jù)有會(huì)在串口調(diào)試助手上面打印出來(lái),有點(diǎn)像回顯。哈哈!就是這么簡(jiǎn)單了!
具體的主程序調(diào)用如下:
很簡(jiǎn)單!一直在輸出!哈哈!OK了!
這些只是個(gè)人調(diào)試和理解,如若有誤,請(qǐng)諒解!