2020-12-3

天空&落葉發表於2020-12-02

1、基於暫存器與基於韌體庫的stm32 LED流水燈例子的程式設計方的差異

1、基於暫存器方法
缺點
(1)開發速度慢
(2)程式可讀性差
(3)維護複雜
優點:
(1)具體引數更直觀
(2)程式執行佔用資源
 

2、基於韌體庫方式
(1)外設交流方便;
(2)查錯簡單;
(3)對主控制器STM32上手簡單。

外設庫函式的呼叫與直接配置暫存器相比,從執行效率上看會有額外的消耗:初始化變數賦值的過程、庫函式在被呼叫的時候要耗費呼叫時間;在函式內部,對輸入引數轉換所需要的額外運算也消耗一些時間。

 


 

2、STM32的USART視窗通訊程式

本人使用的是野火SRM32F103指南者的開發板,有關的資料、源程式程式碼和軟體皆來自野火的官網。

野火STM32F103指南者開發板資料

在這裡插入圖片描述

在這裡插入圖片描述
在這裡插入圖片描述

所需的資料下載完全後,將其解壓出來

在這裡插入圖片描述
安裝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的堆、棧、全域性變數的分配地址。總的來說,還是收穫頗豐。

 


 

6、參考資料

STM32串列埠通訊USART學習筆記

C語言中,區域性變數、全域性變數、靜態變數、堆、棧的記憶體地址

C程式記憶體的各種變數儲存區域和各個區域詳解

基於STM32分析棧、堆、全域性區、常量區、程式碼區、RAM、ROM

STM32 KEIL下的堆疊設定