2020-12-3
STM32串列埠通訊
1、基於暫存器與基於韌體庫的stm32 LED流水燈例子的程式設計方的差異
1、基於暫存器方法
缺點
(1)開發速度慢
(2)程式可讀性差
(3)維護複雜
優點:
(1)具體引數更直觀
(2)程式執行佔用資源
2、基於韌體庫方式
(1)外設交流方便;
(2)查錯簡單;
(3)對主控制器STM32上手簡單。
外設庫函式的呼叫與直接配置暫存器相比,從執行效率上看會有額外的消耗:初始化變數賦值的過程、庫函式在被呼叫的時候要耗費呼叫時間;在函式內部,對輸入引數轉換所需要的額外運算也消耗一些時間。
2、STM32的USART視窗通訊程式
本人使用的是野火SRM32F103指南者的開發板,有關的資料、源程式程式碼和軟體皆來自野火的官網。
所需的資料下載完全後,將其解壓出來
安裝CH340驅動
用usb線把stm32開發板中的usb轉串列埠和電腦相連線
將ST-Link中 的GND、 SWCLK 、RST、 SWDIO這幾個引腳用杜邦線與開發板中SWD區域的相應引腳相連線。
選擇串列埠通訊中的USART1接發
注意要將該資料夾放在無中文目錄中,否則工程編譯可能會出現問題。
開啟其工程檔案
修改stm32f10x_it.c檔案的串列埠中斷服務函式:
int i=0;
uint8_t ucTemp[50];
void DEBUG_USART_IRQHandler(void)
{
if(USART_GetITStatus(DEBUG_USARTx,USART_IT_RXNE)!=RESET)
{
ucTemp[i] = USART_ReceiveData(USART1);
}
if(ucTemp[i] == '!')
{
if(ucTemp[i-1] == '2'&&ucTemp[i-2] == '3'&&ucTemp[i-3] == 'm'&&ucTemp[i-4] == 't'&&ucTemp[i-5] == 's'&&ucTemp[i-6] == ' ')
if(ucTemp[i-7] == 'p'&&ucTemp[i-8] == 'o'&&ucTemp[i-9] == 't'&&ucTemp[i-10] == 's')
{
printf("ok");
while(1);
}
}
i++;
}
main.c修改如下:
#include "stm32f10x.h"
#include "bsp_usart.h"
void delay(uint32_t count)
{
while(count--);
}
int main(void)
{
USART_Config();
while(1)
{
printf("hello windows 10!\n");
delay(5000000);
}
}
完成程式碼修改後進如下等操作
(本人使用的是ST-Link連線線,需要預先安裝其驅動程式)
接著進行編譯等操作
結果如下
輸入“stop stm32!”時,stm32停止傳送訊息。
3、ubuntu中的全域性變數、區域性變數、堆、棧等概念
棧區(stack):由編譯器自動分配釋放,存放函式的引數值,區域性變數的值等。其操作方式類似於資料結構中的棧。
堆區(heap):一般由程式設計師分配釋放,若程式設計師不釋放,程式結束時可能由OS回收 。它與資料結構中的堆不同,分配方式類似於連結串列。
全域性區(靜態區)(static):全域性變數和靜態變數的儲存是放在一塊的,初始化的全域性變數和靜態變數在一塊區域, 未初始化的全域性變數、未初始化的靜態變數在相鄰的另一塊區域。當程式結束後,變數由系統釋放 。
文字常量區:存放常量字串。當程式結束後,常量字串由系統釋放 。
程式程式碼區:存放函式體的二進位制程式碼。
在Ubuntu中寫一個程式,程式碼如下:
#include <stdio.h>
#include <stdlib.h>
int k1 = 1;
int k2;
static int k3 = 2;
static int k4;
int main( )
{ static int m1=2, m2;
int i = 1;
char *p;
char str[10] = "hello";
char *var1 = "123456";
char *var2 = "abcdef";
int *p1=malloc(4);
int *p2=malloc(4);
free(p1);
free(p2);
printf("棧區-變數地址\n");
printf(" i:%p\n", &i);
printf(" p:%p\n", &p);
printf(" str:%p\n", str);
printf("\n堆區-動態申請地址\n");
printf(" %p\n", p1);
printf(" %p\n", p2);
printf("\n.bss段\n");
printf("全域性外部無初值 k2:%p\n", &k2);
printf("靜態外部無初值 k4:%p\n", &k4);
printf("靜態內部無初值 m2:%p\n", &m2);
printf("\n.data段\n");
printf("全域性外部有初值 k1:%p\n", &k1);
printf("靜態外部有初值 k3:%p\n", &k3);
printf("靜態內部有初值 m1:%p\n", &m1);
printf("\n常量區\n");
printf("文字常量地址 :%p\n",var1);
printf("文字常量地址 :%p\n",var2);
printf("\n程式碼區\n");
printf("程式區地址 :%p\n",&main);
return 0;
}
執行程式之後的結果如下圖所示:
可以大概看出棧在頂層(地址最大),然後依次是堆,靜態區。
4、STM32中的全域性變數、區域性變數、堆、棧等概念
在之前的串列埠通訊工程檔案下進行main.c檔案修改
#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)
{
}
}
接著進行編譯等操作,再開啟串列埠除錯助手,點選開啟串列埠
如上圖所示:
part1、part2、part3為棧中的區域性變數,地址逐漸減小。
global1、global2、global3為靜態區中的全域性變數,地址逐漸增加。
之後再對於main.c程式碼進行修改
#include "stm32f10x.h"
#include "bsp_usart.h"
#include <stdlib.h>
int main(void)
{
static char st1[16];
static char st2[16];
static char st3[16];
char *p1;
char *p2;
char *p3;
USART_Config();
printf("st1: 0x%p\n", st1);
printf("st2: 0x%p\n", st2);
printf("st3: 0x%p\n", st3);
p1 = (char *)malloc(sizeof(char) * 16);
p2 = (char *)malloc(sizeof(char) * 16);
p3 = (char *)malloc(sizeof(char) * 16);
printf("p1: 0x%p\n", p1);
printf("p2: 0x%p\n", p2);
printf("p3: 0x%p\n", p3);
while(1)
{
}
}
再進行stm32的燒錄
如上圖所示:
st1、st2、st3都是靜態變數,他們的地址依次增加。
p1、p2、p3是堆中的指標,他們的地址也是依次增加。
結合兩次結果看,Ubuntu在棧區和堆區的地址值都是從上到下增長的,而stm32的棧區的地址值是從上到下減小的,堆區則是從上到下增長的。
檢視stm32地址的分配
從圖片中可以看出ROM的地址分配是從0x8000000開始,整個大小為0x40000,這個部分用於存放程式碼區和文字常量區。RAM的地址分配是從0x20000000開始,其大小是0xC000,這個區域用來存放棧、堆、全域性區(.bss段、.data段)。與程式碼結果顯示進行對比,也可以看出對應得部分得地址與設定的是相對應的。
5、小結
本次學習是對stm32基於暫存器與基於韌體庫的的程式設計方式的差異化的瞭解,並且重溫了C語言程式裡全域性變數、區域性變數、堆、棧等概念,完成了在Ubuntu和stm32下的測試,並歸納出stm32的堆、棧、全域性變數的分配地址。總的來說,還是收穫頗豐。