日本黄色一级经典视频|伊人久久精品视频|亚洲黄色色周成人视频九九九|av免费网址黄色小短片|黄色Av无码亚洲成年人|亚洲1区2区3区无码|真人黄片免费观看|无码一级小说欧美日免费三级|日韩中文字幕91在线看|精品久久久无码中文字幕边打电话

當前位置:首頁 > 嵌入式 > 嵌入式微處理器
[導讀]01 前言 STM32上hello world,說白了就是使用串口向PC上的上位機軟件或者串口調(diào)試助手發(fā)送字符串。 串口的使用方法百度一下就能知道了,簡單來說就是下面這樣。 uint8_t buff[BUFF_SIZE];//定義一個緩存數(shù)組 HAL_UART_Receive_IT(&huart1, (uint8_t *)buff, B


01

前言


STM32STM32 world,說白了就是使用串口向PC上的上位機軟件或者串口調(diào)試助手發(fā)送字符串。


串口的使用方法百度一下就能知道了,簡單來說就是下面這樣。

uint8_t buff[BUFF_SIZE];//定義一個緩存數(shù)組
HAL_UART_Receive_IT(&huart1, (uint8_t *)buff, BUFF_SIZE);//打開串口接收中斷

串口中斷打開之后,當接收到BUFF_SIZE個數(shù)據(jù)后就會進入

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);

然后我們就可以在上面這個函數(shù)下操作收到的數(shù)據(jù)啦,簡單方便快捷。當然實際操作一遍后大家就會發(fā)現(xiàn),這個程序只能進入一次中斷,之后就再也收不到數(shù)據(jù)了,這是因為HAL庫在每次進入串口中斷時都會把這個中斷關閉,所以我們處理完數(shù)據(jù)之后,要重新打開中斷。

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart){ //處理數(shù)據(jù)...
HAL_UART_Receive_IT(&huart1, (uint8_t *)buff, BUFF_SIZE);}

而發(fā)送數(shù)據(jù)呢,就用

HAL_UART_Transmit(&huart1, (uint8_t *)buff, BUFF_SIZE,0xffff);

知道串口怎么用了,我們就可以想辦法hello world。重定向printf的方法百度一搜一大片,fputc這個函數(shù)是_weak定義的,自己寫一個就可以覆蓋過去了。

int fputc(int ch, FILE *f){ HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1,0xffff); return ch;}

然后寫下終極代碼,完美。

printf("hello world\r\n");

上面的內(nèi)容百度上可以找到很多很多文章,而且講的又詳細又生動,這里我只是帶大家復習一下,如果你能夠熟練掌握上面的內(nèi)容了,那接下來就可以進入正題,看看如何變得更優(yōu)雅。

02

變優(yōu)雅第一步

我們實際運行這個代碼,發(fā)現(xiàn)在串口接收幾次數(shù)據(jù)之后,又突然會再也接收不到數(shù)據(jù)了。因為即使你記得在處理完數(shù)據(jù)之后及時打開了接收中斷,開啟中斷的的函數(shù)也不一定總是能正確開啟,我一直覺得這是HAL庫的一個坑。我翻了很久的百度,終于找到一種解決方案。

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart){ //處理數(shù)據(jù)...
int i=0; while(HAL_UART_Receive_IT(&huart1,(uint8_t *)&buff,1) != HAL_OK ) { i++; if(i>10000) { huart6.RxState=HAL_UART_STATE_READY; __HAL_UNLOCK(&huart1); i=0; } }}

事情就是這么神奇,單單執(zhí)行一句開中斷不一定能成功的,開完還要檢查一下是不是真的開成功了,不行的話再打開一下試試,試了10000次還不行,生氣了,強制開。

03

變優(yōu)雅第二步


百度上看到的串口教程,大家都是用下面這個函數(shù)

HAL_UART_Transmit(&huart1, (uint8_t *)buff, BUFF_SIZE,0xffff);

就是說,CPU找到串口,給串口huart1安排好任務:

從這個buff的地址開始,挨個發(fā)BUFF_SIZE個數(shù)據(jù),我就在邊上守著,等你0XFFFF的時間,干不完就別給我干了!


其實看到這么個代碼我是很生氣的,CPU作為大領導,員工干活的時候不去喝咖啡?在邊上守著?這成何體統(tǒng)?


優(yōu)雅的辦法肯定是CPU交代好任務之后,轉(zhuǎn)身去忙自己的事情了,而串口接到命令之后,默默完成任務,然后再跟CPU匯報一下。所以我們不光接收要用中斷,發(fā)送也要用中斷。


所以我們要用下面這個串口中斷發(fā)送的函數(shù)

HAL_UART_Transmit_IT(&huart1, (uint8_t *)buff, BUFF_SIZE);

這個函數(shù)會使能發(fā)送中斷,然后挨個發(fā)數(shù)據(jù),發(fā)完之后執(zhí)行一個回調(diào)函數(shù),然后自己關掉發(fā)送中斷。一條龍服務,用戶什么都不用管。如果用戶想多管閑事,可以把代碼寫在回調(diào)函數(shù)里。


于是乎,我們優(yōu)雅地把串口發(fā)送改用中斷的方式,那我們重定向的fputc可以寫成

int fputc(int ch, FILE *f){ HAL_UART_Transmit_IT(&huart1, (uint8_t *)&ch, 1); return ch;}

這樣我們可以用先前的方法快樂地hello world了。

printf("hello world\r\n");

愛動手的小伙伴一旦嘗試一下就會發(fā)現(xiàn),這傻逼的文章里的代碼都沒法跑,hello world發(fā)了個h就不發(fā)了???

那么這是為什么呢?我們來分析一下這個程序執(zhí)行的過程。printf里是把格式化好的字符一個一個交給fputc發(fā)送的,當發(fā)送第一個字符'h'時,串口處于空閑狀態(tài),能夠正確地使能串口發(fā)送中斷。因此,字符'h'正確發(fā)送。


但我們注意到,CPU給串口安排好工作后,并沒有在原地等待,而是去執(zhí)行后續(xù)的任務了,那后續(xù)的任務就是發(fā)送字符'e'。CPU再次來到fputc函數(shù)內(nèi),再次執(zhí)行

HAL_UART_Transmit_IT(&huart1, (uint8_t *)&ch, 1);

由于CPU的運行速度比串口快得多,此時串口還沒有完成先前的字符'h'的發(fā)送任務,串口處于忙碌的狀態(tài),因此現(xiàn)在是無法正確打開串口發(fā)送中斷的,因此字符'e'發(fā)送失敗。后續(xù)的字符也是同樣的情況。


這就說明,采用中斷發(fā)送方式時,連續(xù)發(fā)送是會發(fā)送失敗的,串口就只有這點速度,你槍頂著他腦袋他也快不起來。在每次使用串口發(fā)送中斷時,都要檢查一下是否正確打開了中斷,和先前提到的串口接收中斷一樣,打開中斷并不是總能成功的。于是,我們修改fputc函數(shù)成下面的樣子。

int fputc(int ch, FILE *f){ while(HAL_UART_Transmit_IT(&huart1, (uint8_t *)&ch, 1)!=HAL_OK); return ch;}

這樣子,CPU不斷地嘗試打開串口中斷直至成功為止。由于串口要完成先前的任務后才會由BUSY狀態(tài)變成READY狀態(tài),所以這里際就是在等待串口發(fā)送。

仔細一想這個過程我們會發(fā)現(xiàn),這不傻逼嗎?用中斷發(fā)送就是為了不堵塞CPU的工作,結(jié)果搞了半天,還是在這兒堵著?


那我們還是要進一步改進一下。如果使用了多線程的話,那我們可以進行任務切換,讓CPU切換到別的線程工作一會兒。

int fputc(int ch, FILE *f){ while(HAL_UART_Transmit_IT(&huart1, (uint8_t *)&ch, 1)!=HAL_OK) { osDelay(1);//ARM CMSIS的API,相當于一般理解的sleep_ms(1); } return ch;}

但是這也有一個很明顯的問題,CPU雖然釋放出來了,但是串口堵了啊。當我們連續(xù)發(fā)送字符的時候,CPU總是會在前一個字符發(fā)送完成之前嘗試發(fā)送下一個字符,然后中斷打開失敗,進入osDelay(1),要等1ms之后才會回來。這其實是非常慢的,STM32 world要大約10ms才能發(fā)送完畢,串口以1ms一個數(shù)據(jù)的速度發(fā)送,這依然不優(yōu)雅。

要么CPU堵,要么串口堵,總有一個要等待,這可怎么辦呢?我們回顧一下串口中斷的API

AL_StatusTypeDef HAL_UART_Transmit_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)

明顯看到這個函數(shù)的第三個參數(shù)是一個size,它可以一次設置發(fā)送很多個數(shù)據(jù),但我們在fputc中使用時,由于fputc是單個字符發(fā)送的,因此我們只能把size設置成1。如果能夠一口氣把所有要發(fā)送的數(shù)據(jù)都設置好,我們就不用重復地打開中斷了,也就避免了前述的尷尬。

所以接下來要做的便是避開fputc這個令人尷尬的單個字符發(fā)送的函數(shù),可是printf就是用這個函數(shù)的呀,要避開fputc,就不能用printf。我們自己搞一個更加優(yōu)雅的!起一個好聽的名字叫做debug,當然你喜歡的話叫別的也可以。

void debug(const char *format, ...){ static char tmpStr[64]; /*在靜態(tài)區(qū)申請一塊緩存,因為CPU開啟中斷之后不會原地等待,而是退出這個函數(shù), 如果在棧上申請空間,函數(shù)退出時緩存區(qū)會直接釋放掉,導致串口發(fā)送數(shù)據(jù)錯誤。 因此把緩存區(qū)申請在靜態(tài)區(qū) */
/*等待串口發(fā)送完畢,在串口忙于發(fā)送先前的數(shù)據(jù)時,不能修改緩存區(qū)內(nèi)的數(shù)據(jù), 否則數(shù)據(jù)會出錯。等串口發(fā)送完成后,再把新的數(shù)據(jù)放進緩存區(qū)。 */ while(huart6.gState!=HAL_UART_STATE_READY);
//把數(shù)據(jù)放進緩存區(qū)里 va_list list; va_start(list, format); vsprintf(tmpStr,format, list);//這一部分不懂得同學請百度這個API,<stdio.h>里的 va_end(list); while(HAL_UART_Transmit_IT(&huart6,(uint8_t*)tmpStr,strlen(tmpStr))!=HAL_OK); //開啟中斷發(fā)送,由于先前已經(jīng)等待過串口發(fā)送完成了,按理說串口肯定是可以打開的 //但為了避免多線程或者中斷等原因在別的地方打開了這個中斷,依然用while嘗試打開直到成功為止}

上面的函數(shù)接收不定長的輸入,這個輸入和printf的格式化是一模一樣的,使用方法和printf完全一樣。這樣的話,每執(zhí)行一次debug,只會開啟一次中斷,只要等待一次中斷開啟就可以了,不必像先前重映射fputc那樣每發(fā)送一個字符都要開一次中斷。

重映射fputc時,必然會產(chǎn)生連續(xù)發(fā)送的情形,而用后面這種方法的話,如果你沒有連續(xù)地調(diào)用debug,很少會出現(xiàn)想發(fā)送卻串口忙碌的情況,while的等待時間是比較少的。


當然如果你使用了多線程,并且串口發(fā)送數(shù)據(jù)沒有那么多,但是又希望CPU一點兒也不堵塞。那就更可以稍微修改一下。

void debug(const char *format, ...){ static char tmpStr[64]; while(huart6.gState!=HAL_UART_STATE_READY) { osDelay(1); }  va_list list; va_start(list, format); vsprintf(tmpStr,format, list); va_end(list);  while(HAL_UART_Transmit_IT(&huart6,(uint8_t*)tmpStr,strlen(tmpStr))) { osDelay(1); }}

這樣在連續(xù)執(zhí)行debug時,會進行線程切換,好處是CPU不堵了,壞處是串口要延后1ms才能發(fā)送下一個字符串,但也遠好于每個字符都延后1ms的方式,況且連續(xù)地debug也不是特別常見。在實際應用中,根據(jù)需求來使用吧。

-END-




推薦閱讀



【01】stm32幾種低功耗模式的實現(xiàn)和差別
【02】使用 Python 來開發(fā) STM32F411 !
【03】基于STM32CUBE的USB鍵盤例程.docx
【04】長見識了:STM8、STM32可以超頻嗎?
【05】Arm中國學堂:新手應該這樣學Cortex-M4和STM32" target="_blank">Arm中國學堂:新手應該這樣學Cortex-M4和STM32


免責聲明:整理文章為傳播相關技術,版權歸原作者所有,如有侵權,請聯(lián)系刪除

免責聲明:本文內(nèi)容由21ic獲得授權后發(fā)布,版權歸原作者所有,本平臺僅提供信息存儲服務。文章僅代表作者個人觀點,不代表本平臺立場,如有問題,請聯(lián)系我們,謝謝!

嵌入式ARM

掃描二維碼,關注更多精彩內(nèi)容

本站聲明: 本文章由作者或相關機構授權發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點,本站亦不保證或承諾內(nèi)容真實性等。需要轉(zhuǎn)載請聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權益,請及時聯(lián)系本站刪除。
換一批
延伸閱讀

LED驅(qū)動電源的輸入包括高壓工頻交流(即市電)、低壓直流、高壓直流、低壓高頻交流(如電子變壓器的輸出)等。

關鍵字: 驅(qū)動電源

在工業(yè)自動化蓬勃發(fā)展的當下,工業(yè)電機作為核心動力設備,其驅(qū)動電源的性能直接關系到整個系統(tǒng)的穩(wěn)定性和可靠性。其中,反電動勢抑制與過流保護是驅(qū)動電源設計中至關重要的兩個環(huán)節(jié),集成化方案的設計成為提升電機驅(qū)動性能的關鍵。

關鍵字: 工業(yè)電機 驅(qū)動電源

LED 驅(qū)動電源作為 LED 照明系統(tǒng)的 “心臟”,其穩(wěn)定性直接決定了整個照明設備的使用壽命。然而,在實際應用中,LED 驅(qū)動電源易損壞的問題卻十分常見,不僅增加了維護成本,還影響了用戶體驗。要解決這一問題,需從設計、生...

關鍵字: 驅(qū)動電源 照明系統(tǒng) 散熱

根據(jù)LED驅(qū)動電源的公式,電感內(nèi)電流波動大小和電感值成反比,輸出紋波和輸出電容值成反比。所以加大電感值和輸出電容值可以減小紋波。

關鍵字: LED 設計 驅(qū)動電源

電動汽車(EV)作為新能源汽車的重要代表,正逐漸成為全球汽車產(chǎn)業(yè)的重要發(fā)展方向。電動汽車的核心技術之一是電機驅(qū)動控制系統(tǒng),而絕緣柵雙極型晶體管(IGBT)作為電機驅(qū)動系統(tǒng)中的關鍵元件,其性能直接影響到電動汽車的動力性能和...

關鍵字: 電動汽車 新能源 驅(qū)動電源

在現(xiàn)代城市建設中,街道及停車場照明作為基礎設施的重要組成部分,其質(zhì)量和效率直接關系到城市的公共安全、居民生活質(zhì)量和能源利用效率。隨著科技的進步,高亮度白光發(fā)光二極管(LED)因其獨特的優(yōu)勢逐漸取代傳統(tǒng)光源,成為大功率區(qū)域...

關鍵字: 發(fā)光二極管 驅(qū)動電源 LED

LED通用照明設計工程師會遇到許多挑戰(zhàn),如功率密度、功率因數(shù)校正(PFC)、空間受限和可靠性等。

關鍵字: LED 驅(qū)動電源 功率因數(shù)校正

在LED照明技術日益普及的今天,LED驅(qū)動電源的電磁干擾(EMI)問題成為了一個不可忽視的挑戰(zhàn)。電磁干擾不僅會影響LED燈具的正常工作,還可能對周圍電子設備造成不利影響,甚至引發(fā)系統(tǒng)故障。因此,采取有效的硬件措施來解決L...

關鍵字: LED照明技術 電磁干擾 驅(qū)動電源

開關電源具有效率高的特性,而且開關電源的變壓器體積比串聯(lián)穩(wěn)壓型電源的要小得多,電源電路比較整潔,整機重量也有所下降,所以,現(xiàn)在的LED驅(qū)動電源

關鍵字: LED 驅(qū)動電源 開關電源

LED驅(qū)動電源是把電源供應轉(zhuǎn)換為特定的電壓電流以驅(qū)動LED發(fā)光的電壓轉(zhuǎn)換器,通常情況下:LED驅(qū)動電源的輸入包括高壓工頻交流(即市電)、低壓直流、高壓直流、低壓高頻交流(如電子變壓器的輸出)等。

關鍵字: LED 隧道燈 驅(qū)動電源
關閉