STM32串口通讯初步学习
本文目錄
- 一.說明基于寄存器與基于固件庫的stm32 LED流水燈例子的編程方式有什么差異?
- 二.完成STM32的USART窗口通訊程序,要求如下:
- 1.如何進(jìn)行燒錄
- 2.代碼執(zhí)行及其運(yùn)行結(jié)果
- 三.學(xué)習(xí)C語言程序里全局變量、局部變量、堆、棧等概念,并在ubuntu系統(tǒng)中編程,輸出信息進(jìn)行驗證;
- 1.C語言在內(nèi)存中有哪幾個區(qū)域?
- 2.一個由c/C++編譯的程序占用的內(nèi)存有哪幾個部分?
- 3.例子程序如下:
- 4.關(guān)于C語言中關(guān)鍵字volatile的相關(guān)問題
- 四.在Keil中針對stm32系統(tǒng)進(jìn)行編程,調(diào)試變量,進(jìn)行驗證; 通過串口輸出信息到上位機(jī),進(jìn)行驗證。
一.說明基于寄存器與基于固件庫的stm32 LED流水燈例子的編程方式有什么差異?
1.寄存器是中央處理器內(nèi)的組成部分。寄存器是有限存貯容量的高速存貯部件,它們可用來暫存指令、數(shù)據(jù)和地址。基于寄存器來進(jìn)行流水燈的編程,它更貼近底層,對外設(shè)的工作原理和運(yùn)行機(jī)理會有更深的理解。
2.STM32標(biāo)準(zhǔn)外設(shè)庫之前的版本也稱固件函數(shù)庫或簡稱固件庫,是一個固件函數(shù)包,它由程序、數(shù)據(jù)結(jié)構(gòu)和宏組成,包括了微控制器所有外設(shè)的性能特征。基于這種方式進(jìn)行流水燈的編程,會比較簡單,易于編程,因為目前比較多的例程是使用固件庫編寫的。
二.完成STM32的USART窗口通訊程序,要求如下:
1)設(shè)置波特率為115200,1位停止位,無校驗位。
2)STM32系統(tǒng)給上位機(jī)(win10)連續(xù)發(fā)送“hello windows!”,上位機(jī)接收程序可以使用“串口調(diào)試助手“,也可自己編程。
3)當(dāng)上位機(jī)給stm32發(fā)送“Stop,stm32”后,stm32停止發(fā)送。
1.如何進(jìn)行燒錄
(1)我們使用的是野火指南針開發(fā)板,配套有相應(yīng)的教程,其板子連接電腦如下所示:
(2)打開mcuisp助手
(3)進(jìn)行如下操作即可將文件燒錄進(jìn)去
1.搜索串口
2.選擇要下載的HEX文件
3.校驗、編程后執(zhí)行
4.DTR 低電平復(fù)位,RTS 高電平進(jìn)入 bootloader
5.開始進(jìn)行串口執(zhí)行
2.代碼執(zhí)行及其運(yùn)行結(jié)果
(1)打開開發(fā)板附贈教程中的相應(yīng)工程
(2)將其中stm32f10x_it.c文件的串口中斷服務(wù)函數(shù)部分改為如下:
(3)將主函數(shù)main.c改為如下:
(4)編譯生成hex文件
(5)將上述HEX文件燒錄到助手當(dāng)中,進(jìn)行編程后,打開串口調(diào)試助手,點(diǎn)擊打開串口,即可以看到stm32發(fā)給電腦的信息了
燒錄成功:
串口助手調(diào)試:
停止發(fā)送:
三.學(xué)習(xí)C語言程序里全局變量、局部變量、堆、棧等概念,并在ubuntu系統(tǒng)中編程,輸出信息進(jìn)行驗證;
1.C語言在內(nèi)存中有哪幾個區(qū)域?
2.一個由c/C++編譯的程序占用的內(nèi)存有哪幾個部分?
1、棧區(qū)(stack)— 由編譯器自動分配釋放 ,存放函數(shù)的參數(shù)值,局部變量的值等。其操作方式類似于數(shù)據(jù)結(jié)構(gòu)中的棧。
2、堆區(qū)(heap) — 一般由程序員分配釋放, 若程序員不釋放,程序結(jié)束時可能由OS回收 。注意它與數(shù)據(jù)結(jié)構(gòu)中的堆是兩回事,分配方式倒是類似于鏈表,呵呵。
3、全局區(qū)(靜態(tài)區(qū))(static)—,全局變量和靜態(tài)變量的存儲是放在一塊的,初始化的全局變量和靜態(tài)變量在一塊區(qū)域(RW), 未初始化的全局變量和未初始化的靜態(tài)變量在相鄰的另一塊區(qū)域(ZI)。 - 程序結(jié)束后有系統(tǒng)釋放
4、文字常量區(qū) —常量字符串就是放在這里的。 程序結(jié)束后由系統(tǒng)釋放 (RO)
5、程序代碼區(qū)—存放函數(shù)體的二進(jìn)制代碼。 (RO)
3.例子程序如下:
例子程序如下:
#include <stdio.h> #include <string.h> #include <stdlib.h>void before() {}char g_buf[16]; char g_buf2[16]; char g_buf3[16]; char g_buf4[16]; char g_i_buf[]="123"; char g_i_buf2[]="123"; char g_i_buf3[]="123";void after() {}int main(int argc, char **argv) {char l_buf[16];char l_buf2[16];char l_buf3[16];static char s_buf[16];static char s_buf2[16];static char s_buf3[16];char *p_buf;char *p_buf2;char *p_buf3;p_buf = (char *)malloc(sizeof(char) * 16);p_buf2 = (char *)malloc(sizeof(char) * 16);p_buf3 = (char *)malloc(sizeof(char) * 16);printf("g_buf: 0x%x\n", g_buf);printf("g_buf2: 0x%x\n", g_buf2);printf("g_buf3: 0x%x\n", g_buf3);printf("g_buf4: 0x%x\n", g_buf4);printf("g_i_buf: 0x%x\n", g_i_buf);printf("g_i_buf2: 0x%x\n", g_i_buf2);printf("g_i_buf3: 0x%x\n", g_i_buf3);printf("l_buf: 0x%x\n", l_buf);printf("l_buf2: 0x%x\n", l_buf2);printf("l_buf3: 0x%x\n", l_buf3);printf("s_buf: 0x%x\n", s_buf);printf("s_buf2: 0x%x\n", s_buf2);printf("s_buf3: 0x%x\n", s_buf3);printf("p_buf: 0x%x\n", p_buf);printf("p_buf2: 0x%x\n", p_buf2);printf("p_buf3: 0x%x\n", p_buf3);printf("before: 0x%x\n", before);printf("after: 0x%x\n", after);printf("main: 0x%x\n", main);if (argc > 1){strcpy(l_buf, argv[1]);}return 0; }源代碼如上,其在ubantu中運(yùn)行結(jié)果如下所示:
我們可以看到從低地址到高地址,如圖:
4.關(guān)于C語言中關(guān)鍵字volatile的相關(guān)問題
Volatile這個關(guān)鍵字的必要性(真正意義之所在)
但其他程序(例如內(nèi)核程序或一個中斷)修改了內(nèi)存中該變量的值,此時寄存器R中的值并不會隨之改變而更新,由于優(yōu)化器的作用編譯器仍然去利用之前存放在寄存器R中的值,而不去尋址內(nèi)存中的值(但我們必須改變這個變量的值)。為了解決這種情況C語言就引入了volatile限定詞,讓代碼在引用該變量時多費(fèi)一點(diǎn)勁兒,再去內(nèi)存中取出該變量的值。
Volatile和register的對比
volatile 跟以前的 register 相反。 register 告訴編譯器盡量將變量放到寄存器中使用, 而volatile 強(qiáng)制將更改后的值寫回內(nèi)存。如果不寫回內(nèi)存,對于一些全局共享的變量,可能導(dǎo)致不一致問題。
Volatile關(guān)鍵字應(yīng)用的三個地方
1、 修改硬件寄存器,尤其是狀態(tài)寄存器
大家都知道,在硬件級別,如果寄存器值自動改變了,編譯器是不會主動發(fā)現(xiàn)的。經(jīng)過編譯器的自動優(yōu)化,我們讀到的都是寄存器中存儲的舊的狀態(tài)寄存器的值, 而非最新的狀態(tài)寄存器值。
2、 多線程中被幾個線程共享的變量
線程修改共享變量var是不會通知編譯器的。所以線程A堅持不懈地讀著var在寄存器或者cache中的副本,讀出來的內(nèi)容是0, 但很可惜,線程B早就把var變量給修改為1了。鑒于此,我們必須加上volatile這個關(guān)鍵字來解決這個問題。
3、 中斷服務(wù)程序ISR當(dāng)中用
ISR:中斷服務(wù)程序 (interrupt service routine)
四.在Keil中針對stm32系統(tǒng)進(jìn)行編程,調(diào)試變量,進(jìn)行驗證; 通過串口輸出信息到上位機(jī),進(jìn)行驗證。
用第二大點(diǎn)的串口通信模板,我們將main.c主函數(shù)改為如下所示:
#include "stm32f10x.h" #include "bsp_usart.h"char global1[16]; char global2[16]; char global3[16];int main(void) { char part1[16];char part2[16];char part3[16];USART_Config();printf("part1: 0x%p\n", part1);printf("part2: 0x%p\n", part2);printf("part3: 0x%p\n", part3);printf("global1: 0x%p\n", global1);printf("global2: 0x%p\n", global2);printf("global3: 0x%p\n", global3);while(1){ } }
其運(yùn)行結(jié)果如下所示:
我們可以看到:
前3個part變量為局部變量,它們儲存到了棧中,地址依次減小。
后三個global為全局變量,它們儲存到了靜態(tài)區(qū),地址依次增加。
我們用下面的主函數(shù)來定義了靜態(tài)變量和指針
運(yùn)行結(jié)果如下所示:
前三個靜態(tài)變量儲存到了靜態(tài)區(qū),地址依次增加。
后三個指針儲存到了堆中,地址依次增加。
結(jié)合兩次結(jié)果看(針對于測試的3個區(qū)域),可以大概看出棧在頂層(地址最大),然后依次是堆,靜態(tài)區(qū)。
總結(jié):
在一個STM32程序代碼中,從內(nèi)存高地址到內(nèi)存低地址,依次分布著棧區(qū)、堆區(qū)、全局區(qū)(靜態(tài)區(qū))、常量區(qū)、代碼區(qū),其中全局區(qū)中高地址分布著.bss段,低地址分布著.data段。
總的分布如下所示:
一、棧區(qū)(stack)
臨時創(chuàng)建的局部變量存放在棧區(qū)。
函數(shù)調(diào)用時,其入口參數(shù)存放在棧區(qū)。
函數(shù)返回時,其返回值存放在棧區(qū)。
const定義的局部變量存放在棧區(qū)。
2、堆區(qū)(heap)
堆區(qū)用于存放程序運(yùn)行中被動態(tài)分布的內(nèi)存段,可增可減。
可以有malloc等函數(shù)實(shí)現(xiàn)動態(tài)分布內(nèi)存。
有malloc函數(shù)分布的內(nèi)存,必須用free進(jìn)行內(nèi)存釋放,否則會造成內(nèi)存泄漏。
3、全局區(qū)(靜態(tài)區(qū))
全局區(qū)有.bss段和.data段組成,可讀可寫。
4、.bss段
未初始化的全局變量存放在.bss段。
初始化為0的全局變量和初始化為0的靜態(tài)變量存放在.bss段。
.bss段不占用可執(zhí)行文件空間,其內(nèi)容有操作系統(tǒng)初始化。
5、.data段
已經(jīng)初始化的全局變量存放在.data段。
靜態(tài)變量存放在.data段。
.data段占用可執(zhí)行文件空間,其內(nèi)容有程序初始化。
const定義的全局變量存放在.rodata段。
6、常量區(qū)
字符串存放在常量區(qū)。
常量區(qū)的內(nèi)容不可以被修改。
7、代碼區(qū)
程序執(zhí)行代碼存放在代碼區(qū)。
字符串常量也有可能存放在代碼區(qū)。
總結(jié)
以上是生活随笔為你收集整理的STM32串口通讯初步学习的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C语言之指针与数组总结
- 下一篇: 前端学习(3248):react的生命周