色哟哟视频在线观看-色哟哟视频在线-色哟哟欧美15最新在线-色哟哟免费在线观看-国产l精品国产亚洲区在线观看-国产l精品国产亚洲区久久

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

如何讓STM32優雅地“說”hello world?

GReq_mcu168 ? 來源:嵌入式ARM ? 2020-06-28 17:18 ? 次閱讀

01

前言

STM32上hello world,說白了就是使用串口向PC上的上位機軟件或者串口調試助手發送字符串。

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

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

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

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);

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

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

而發送數據呢,就用

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

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

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

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

printf("hello world ");

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

02

變優雅第一步

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

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart){ //處理數據... 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; } }}

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

03

變優雅第二步

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

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

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

“從這個buff的地址開始,挨個發BUFF_SIZE個數據,我就在邊上守著,等你0XFFFF的時間,干不完就別給我干了!”

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

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

所以我們要用下面這個串口中斷發送的函數

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

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

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

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

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

printf("hello world ");

愛動手的小伙伴一旦嘗試一下就會發現,這***的文章里的代碼都沒法跑,hello world發了個h就不發了???

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

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

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

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

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

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

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

仔細一想這個過程我們會發現,這不***嗎?用中斷發送就是為了不堵塞CPU的工作,結果搞了半天,還是在這兒堵著?

那我們還是要進一步改進一下。如果使用了多線程的話,那我們可以進行任務切換,讓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雖然釋放出來了,但是串口堵了啊。當我們連續發送字符的時候,CPU總是會在前一個字符發送完成之前嘗試發送下一個字符,然后中斷打開失敗,進入osDelay(1),要等1ms之后才會回來。這其實是非常慢的,hello world要大約10ms才能發送完畢,串口以1ms一個數據的速度發送,這依然不優雅。

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

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

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

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

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

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

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

當然如果你使用了多線程,并且串口發送數據沒有那么多,但是又希望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); }}

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

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • STM32
    +關注

    關注

    2270

    文章

    10921

    瀏覽量

    356965
  • 數組
    +關注

    關注

    1

    文章

    417

    瀏覽量

    25997

原文標題:如何讓STM32優雅地“說”hello world?

文章出處:【微信號:mcu168,微信公眾號:硬件攻城獅】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    使用MCUXpresso for VS Code插件開發Zephyr的hello world

    本期來到Zephyr實戰經驗演練,小編帶著大家一起使用MCUXpresso for VS Code插件來開發一個屬于Zephyr的hello world
    的頭像 發表于 01-03 09:21 ?577次閱讀
    使用MCUXpresso for VS Code插件開發Zephyr的<b class='flag-5'>hello</b> <b class='flag-5'>world</b>

    如何在i2c中將hello world發送到LCD屏幕?

    有誰知道如何在 i2c 中告訴這個以將 hello world 發送到 LCD 屏幕?當我查找我的 4BIT 引腳時,我可以很好地做到這一點,但是當使用 i2c 時,我似乎在任何地方都找不到協議的任何細節,每個人都只想談論天哪的 ardunio,這對杰克有幫助。 謝謝!!
    發表于 07-11 06:10

    編譯IDF example/hello_world例程時,編譯報錯找不到lwip inet.h頭文件,為什么?

    大家好,我在編譯IDF example/hello_world例程時,編譯報錯找不到lwip inet.h頭文件,但是我到該路徑下發現了該頭文件,這是為什么?還有組件lwip并沒有源碼,該如何更新
    發表于 06-26 07:33

    esp32運行make flash燒寫hello world遇到的疑問求解

    串口驅動安裝正常,連接正常 在demo目錄里hello world程序下,運行make all,編譯正常, 然后運行 make flash 出現
    發表于 06-26 06:25

    使用make flash命令燒錄Hello_world程式失敗了,為什么?

    請問我使用 make flash 命令要燒錄 Hello_world 程式失敗了 看起來像是COM3 Port有連上,但找不到開發板上芯片,無法寫入 狀況如下e3.JPG (119.44 KiB
    發表于 06-26 06:10

    hello_world例程里面CONFIG_FREERTOS_HZ沒定義,但又能編譯燒錄,為什么?

    hello_world例程里面CONFIG_FREERTOS_HZ沒定義,但又能編譯燒錄,這是什么情況?
    發表于 06-19 07:25

    ESP8266筆記-03.ESP8266 RTOS的Hello world

    Hello world 由于我也是在邊學習邊寫文章,所以疏漏是難免的,我發的也不是教程貼,只不過是筆記,如果有大佬發現問題,歡迎指正! 在上一篇文章的最后,其實已經可以正常運行Hello wo
    發表于 05-30 11:51

    鴻蒙OpenHarmony【輕量系統 編寫“Hello World”程序】 (基于Hi3861開發板)

    下方將通過修改源碼的方式展示如何編寫簡單程序,輸出“Hello world”。請在下載的源碼目錄中進行下述操作。
    的頭像 發表于 05-16 18:15 ?1022次閱讀
    鴻蒙OpenHarmony【輕量系統 編寫“<b class='flag-5'>Hello</b> <b class='flag-5'>World</b>”程序】 (基于Hi3861開發板)

    鴻蒙OpenHarmony【小型系統 編寫“Hello World”程序】 (基于Hi3516開發板)

    展示如何在單板上運行第一個應用程序,其中包括新建應用程序、編譯、燒寫、運行等步驟,最終輸出“Hello World!”。
    的頭像 發表于 05-10 16:26 ?733次閱讀
    鴻蒙OpenHarmony【小型系統 編寫“<b class='flag-5'>Hello</b> <b class='flag-5'>World</b>”程序】 (基于Hi3516開發板)

    鴻蒙OpenHarmony【標準系統 編寫“Hello World”程序】(基于RK3568開發板)

    下方將展示如何在單板上運行第一個應用程序,其中包括新建應用程序、編譯、燒寫、運行等步驟,最終輸出“Hello World!”。
    的頭像 發表于 05-09 17:58 ?934次閱讀
    鴻蒙OpenHarmony【標準系統 編寫“<b class='flag-5'>Hello</b> <b class='flag-5'>World</b>”程序】(基于RK3568開發板)

    鴻蒙OpenHarmony【標準系統編寫“Hello World”程序】 (基于RK3568開發板)

    編寫“Hello World”程序 下方將展示如何在單板上運行第一個應用程序,其中包括新建應用程序、編譯、燒寫、運行等步驟,最終輸出“Hello World!”。 前提條件 已參考[創
    的頭像 發表于 04-24 17:32 ?906次閱讀
    鴻蒙OpenHarmony【標準系統編寫“<b class='flag-5'>Hello</b> <b class='flag-5'>World</b>”程序】 (基于RK3568開發板)

    鴻蒙OpenHarmony【小型系統編寫“Hello World”程序】 (基于Hi3516開發板)

    下方將展示如何在單板上運行第一個應用程序,其中包括新建應用程序、編譯、燒寫、運行等步驟,最終輸出“Hello World!”。
    的頭像 發表于 04-22 21:55 ?385次閱讀
    鴻蒙OpenHarmony【小型系統編寫“<b class='flag-5'>Hello</b> <b class='flag-5'>World</b>”程序】 (基于Hi3516開發板)

    鴻蒙OpenHarmony【輕量系統編寫“Hello World”程序】 (基于Hi3861開發板)

    下方將通過修改源碼的方式展示如何編寫簡單程序,輸出“Hello world”。請在下載的源碼目錄中進行下述操作。
    的頭像 發表于 04-21 21:44 ?371次閱讀
    鴻蒙OpenHarmony【輕量系統編寫“<b class='flag-5'>Hello</b> <b class='flag-5'>World</b>”程序】 (基于Hi3861開發板)

    STM32F401使用USART6時不正常,發送HELLO WORLD為亂碼怎么解決?

    各位大神: 我用STM32F401 進行串口通信,UASRT1跟UASRT2使用正常,使用USART6時不正常,發送HELLO WORLD為亂碼怎么破? 代碼如下: main
    發表于 03-22 06:18

    用于EK-RA8D1 MIPI LCD顯示器的GUIX Hello World

    電子發燒友網站提供《用于EK-RA8D1 MIPI LCD顯示器的GUIX Hello World.pdf》資料免費下載
    發表于 02-20 09:48 ?0次下載
    用于EK-RA8D1 MIPI LCD顯示器的GUIX <b class='flag-5'>Hello</b> <b class='flag-5'>World</b>
    主站蜘蛛池模板: 久久国产36精品色熟妇| 67194免费入口| 亚洲精品无码国产爽快A片| 91系列在线观看免费| 国产网站免费观看| 暖暖视频免费观看高清完整版 | www.青青草.com| 久久精品成人免费网站| 午夜一区二区三区| 大香网伊人久久综合观看| 免费国产网站| 亚洲男人天堂网| 俄罗斯性孕妇孕交| 欧美三级aaa| 7723日本高清完整版在线观看| 寂寞夜晚看免费视频| 色呦呦人人视频| 动漫美女被吸奶| 日本大尺码喷液过程视频| 97国产揄拍国产精品人妻| 九九热在线观看视频| 小SB几天没做SAO死了H| 工口肉肉彩色不遮挡| 日韩国产精品欧美一区二区| 99re6在线视频国产精品欧美| 久久久精品免费免费直播| 亚洲成人在线免费观看| 国产精品一区二区免费| 天天躁夜夜踩很很踩2022| 二色AV天堂在线| 日日做夜夜欢狠狠免费软件| 爱看吧孕妇网| 日本高清免费在线| 成年人国产视频| 热久久免费频精品99热| 把腿张开JI巴CAO死你H教室| 彭丹吃奶门| 第四色播日韩AV第一页| 手机看片国产日韩欧美| 国产精品久久人妻无码网站一区无| 天天爽夜夜爽夜夜爽|