STM32F103入門---點亮流水燈教程


一、STM32簡介

STM32,從字面上來理解,ST 是意法半導體,M 是 Microelectronics 的縮寫,32 表示32 位,合起來理解,STM32 就是指 ST 公司開發的 32 位微控制器。在如今的 32 位控制器當中,STM32 可以說是最璀璨的新星,它受寵若嬌,大受工程師和市場的青睞,無芯能出其右。

STM32 屬於一個微控制器,自帶了各種常用通信接口,比如 USART、I2C、SPI 等,可接非常多的傳感器,可以控制很多的設備。現實生活中,我們接觸到的很多電器產品都有 STM32 的身影,比如智能手環,微型四軸飛行器,平衡車、移動 POST 機,智能電飯鍋,3D 打印機等等。

STM32 有很多系列,可以滿足市場的各種需求,從內核上分有 Cortex-M0、M3、M4和 M7 這幾種,每個內核又大概分為主流、高性能和低功耗。

單純從學習的角度出發,可以選擇 F1和 F4,F1代表了基礎型,基於 Cortex-M3內核,主頻為 72MHZ,F4 代表了高性能,基於 Cortex-M4 內核,主頻 180M。本文則選擇的F1下的stm32f103c8t6。

二.使用通過寄存器點燈原理介紹。

(ps : stm32f1中文文檔我在網上找到在這里下載的:https://blog.csdn.net/qq_39758638/article/details/96423525)

1.配置時鍾使能。

因為流水燈要操作的引腳都是在GPIO端口的,所以根據系統結構圖,屬於AHB總線,所以所要用的端口的復位和時間控制都受RCC控制。

1634642935614

再看寄存器組起始地址表,可以看到RCC的地址范圍,且可以看到要控制的寄存器都是在APB2總。

從上面發現復位和時鍾控制的起始地址為0x4002 1000,

1634643668557

再翻到這里發下偏移量為0x18,所以該寄存器的地址為0x4002 1018

上圖表格表示的寄存器里各位的含義,比如第三位也就是2那個位置為1時,就是GPIOA的時鍾開啟了。這時我們就可以用代碼表達出來了。

#define RCC_AP2ENR	*((unsigned volatile int*)0x40021018) #時鍾使能寄存器
RCC_AP2ENR|=1<<2;			//開啟APB2-GPIOA外設時鍾使能

接下來就是配置端口配置寄存器了,

這個就比較關鍵了,可以發現上面的時鍾使能寄存器開啟時鍾是針對一個區域的,並不能確定引腳,

而這個寄存器就是確定引腳的,端口配置寄存器有兩個,分別為端口配置低寄存器(CRL)和端口配置高寄存器(CRH)。

每四位配置一個端口,如11 01,11就是選擇開啟功能,01就是選擇模式和確定最大速度,但有一點不一樣,低寄存器的偏移地址為0x00,高寄存器的偏移地址為0x04。

我們翻到這里

1634644330144

以PA7為示例,相應端口配置器GPIOA_CRL地址為GPIOA的基址+上偏移量

1634644696916

在開頭第二張圖的那個位置可以找到GPIOA的地址為0x40010800, 低寄存器偏移如上圖所示為0

1634645006618

以PA7口為輸出口為例,就是要改變的是紅色方框里的內容,

根據它下方表格里的內容得知要將他設置為推挽輸出,輸出模式最大速度為2MHz就是將它賦為0010,轉換進制就是2(ps:我個人理解,錯了的話希望大佬指正)

所以即將地址0x40010800的最前頭一位賦為2,代碼如下

#define GPIOA_CRL	*((unsigned volatile int*)0x40010800)
GPIOA_CRL=0x20000000;		//PA7推挽輸出,2Mhz

7.接下來就是配置端口輸出寄存器(ORD),可以看到偏移量為0xc,所以該寄存器的地址等於端口的基址加上偏移量,在相應的位賦值可以控制輸出電壓,0為低電壓,1為高電壓,以pa7引腳為例子,想要輸出高電壓,就需要在第八位賦1。

1634645322996

#define	GPIOA_ORD	*((unsigned volatile int*)0x4001080C)
GPIOA_ORD|=1<<7;			//設置初始燈為亮

這里就可以控制led亮或者滅了,實現流水燈只需增加燈的數量和增加一些延時就行了。

三.使用C語言實現。

1.創建一個新的項目。選擇自己對應的板子,注意后面需要勾選上core 以及startup

1634647930969

1634647965117

添加一個main.c並寫入代碼,注意項目結構,使用的引腳是PA7,PB9,PC15,同時如果燈不閃爍,程序沒有正常運行,可以先試試仿真調試,仿真調試正常了一般在板子上運行就正常了

//--------------APB2使能時鍾寄存器------------------------
#define RCC_AP2ENR	*((unsigned volatile int*)0x40021018)
	//----------------GPIOA配置寄存器 ------------------------
#define GPIOA_CRL	*((unsigned volatile int*)0x40010800)
#define	GPIOA_ORD	*((unsigned volatile int*)0x4001080C)
//----------------GPIOB配置寄存器 ------------------------
#define GPIOB_CRH	*((unsigned volatile int*)0x40010C04)
#define	GPIOB_ORD	*((unsigned volatile int*)0x40010C0C)
//----------------GPIOC配置寄存器 ------------------------
#define GPIOC_CRH	*((unsigned volatile int*)0x40011004)
#define	GPIOC_ORD	*((unsigned volatile int*)0x4001100C)
//-------------------簡單的延時函數-----------------------
void  Delay_ms( volatile  unsigned  int  t)
{
     unsigned  int  i;
     while(t--)
         for (i=0;i<800;i++);
}
void A_LED_LIGHT(){
	GPIOA_ORD=0x0<<7;		//PA7低電平
	GPIOB_ORD=0x1<<9;		//PB9高電平
	GPIOC_ORD=0x1<<15;		//PC15高電平
}
void B_LED_LIGHT(){
	GPIOA_ORD=0x1<<7;		//PA7高電平
	GPIOB_ORD=0x0<<9;		//PB9低電平
	GPIOC_ORD=0x1<<15;		//PC15高電平
}
void C_LED_LIGHT(){
	GPIOA_ORD=0x1<<7;		//PA7高電平
	GPIOB_ORD=0x1<<9;		//PB9高電平
	GPIOC_ORD=0x0<<15;		//PC15低電平	
}
//------------------------主函數--------------------------
int main()
{
	int j=100;
	RCC_AP2ENR|=1<<2;			//APB2-GPIOA外設時鍾使能
	RCC_AP2ENR|=1<<3;			//APB2-GPIOB外設時鍾使能	
	RCC_AP2ENR|=1<<4;			//APB2-GPIOC外設時鍾使能
	//這兩行代碼可以合為 RCC_APB2ENR|=1<<3|1<<4;
	GPIOA_CRL&=0x0FFFFFFF;		//設置位 清零	
	GPIOA_CRL|=0x20000000;		//PA7推挽輸出
	GPIOA_ORD|=1<<7;			//設置PA7初始燈為滅
	
	GPIOB_CRH&=0xFFFFFF0F;		//設置位 清零	
	GPIOB_CRH|=0x00000020;		//PB9推挽輸出
	GPIOB_ORD|=1<<9;			//設置初始燈為滅
	
	GPIOC_CRH&=0x0FFFFFFF;		//設置位 清零
	GPIOC_CRH|=0x20000000;   	//PC15推挽輸出
	GPIOC_ORD|=0x1<<15;			//設置初始燈為滅	
	while(j)
	{	
		A_LED_LIGHT();	
		Delay_ms(10000000);
		B_LED_LIGHT();
		Delay_ms(10000000);
		C_LED_LIGHT();
		Delay_ms(10000000);
	}
}

將三個led燈接到PC15,PB9,PA7上,通過keil軟件以及STlink將程序燒入其中。結果如下所示

四.通過匯編語言實現

1.新建一個工程,步驟和上面一個差不多,不過不選擇startup,選了會有錯誤,燒錄過程也是一致

RCC_APB2ENR EQU 0x40021018;配置RCC寄存器,時鍾,0x40021018為時鍾地址
GPIOC_CRH EQU 0x40011004;配置GPIOC_CRH寄存器,是端口配置高寄存器,高位的偏移地址為0x04 
GPIOC_ORD EQU 0x4001100c;配置GPIOC_ORD寄存器,是端口輸出寄存器,輸出由這里控制
GPIOA_CRL EQU 0x40010800;配置GPIOC_CRH寄存器,是端口配置高寄存器,高位的偏移地址為0x04 
GPIOA_ORD EQU 0x4001080C;配置GPIOC_ORD寄存器,是端口輸出寄存器,輸出由這里控制
GPIOB_CRH EQU 0x40010C04;配置GPIOC_CRH寄存器,是端口配置高寄存器,高位的偏移地址為0x04 
GPIOB_ORD EQU 0x40010C0C;配置GPIOC_ORD寄存器,是端口輸出寄存器,輸出由這里控制
Stack_Size EQU  0x00000400;棧的大小
;分配一個stack段,該段不初始化,可讀寫,按8字節對齊。分配一個大小為Stack_Size的存儲空間,並使棧頂的地址為__initial_sp。
                AREA    STACK, NOINIT, READWRITE, ALIGN=3 ;NOINIT: = NO Init,不初始化。READWRITE : 可讀,可寫。ALIGN =3 : 2^3 對齊,即8字節對齊。
Stack_Mem       SPACE   Stack_Size

__initial_sp



​```
            AREA    RESET, DATA, READONLY
​```

__Vectors       DCD     __initial_sp               ; Top of Stack
                DCD     Reset_Handler              ; Reset Handler
                    
                    

​```
            AREA    |.text|, CODE, READONLY
                
            THUMB
            REQUIRE8
            PRESERVE8
                
            ENTRY
​```

Reset_Handler 
				bl LED_Init;bl:帶鏈接的跳轉指令。當使用該指令跳轉時,當前地址(PC)會自動送入LR寄存器
MainLoop        BL LED_ON_C
                BL Delay
                BL LED_OFF_C
                BL Delay
				BL LED_ON_A
                BL Delay
                BL LED_OFF_A
                BL Delay
				BL LED_ON_B
                BL Delay
                BL LED_OFF_B
                BL Delay
                

​```
            B MainLoop;B:無條件跳轉。
​```

LED_Init;LED初始化
                PUSH {R0,R1, LR};R0,R1,LR中的值放入堆棧
                

​```
            LDR R0,=RCC_APB2ENR;LDR是把地址裝載到寄存器中(比如R0)。
            ORR R0,R0,#0x1c;ORR 按位或操作,11100將R0的第二位置1,其他位不變
            LDR R1,=RCC_APB2ENR
            STR R0,[R1];STR是把值存儲到寄存器所指的地址中,將r0里存儲的值給rcc寄存器
			;上面一部分匯編代碼是控制時鍾的
​```

​				

​```
            ;初始化GPIOA部分
            LDR R0,=GPIOA_CRL
            BIC R0,R0,#0x0fffffff;BIC 先把立即數取反,再按位與
            LDR R1,=GPIOA_CRL
            STR R0,[R1]
            ;上面的代碼是初始化CPIOC_CRH
            LDR R0,=GPIOA_CRL
            ORR R0,#0x20000000;開啟的是pc15,所以是2,為0100,是推挽輸出模式,最大速度為2mhz
            LDR R1,=GPIOA_CRL
            STR R0,[R1]
			;GPIOC的端口配置高寄存器配置完畢,也就是CPIOA_CRH配置完成,端口的輸出模式確定,不使用的都設為復位后的狀態,為0100,所以上面處理輸出為都是4
            ;將PC15置1
            MOV R0,#0x80; 二進制為0b1000 0000 ,第7位就是a7引腳的輸出電壓
            LDR R1,=GPIOA_ORD ;由r1控制ord寄存器
            STR R0,[R1] ;將ord寄存器的值變為r0的值
			
			 ;初始化GPIOB部分
            LDR R0,=GPIOB_CRH
            BIC R0,R0,#0xffffff0f;BIC 先把立即數取反,再按位與,用的是b9,所以把第二位置零
            LDR R1,=GPIOB_CRH
            STR R0,[R1]
            ;上面的代碼是初始化CPIOC_CRH
            LDR R0,=GPIOB_CRH
            ORR R0,#0x00000020;開啟的是pc15,所以是2,為0100,是推挽輸出模式,最大速度為2mhz
            LDR R1,=GPIOB_CRH
            STR R0,[R1]
			;GPIOC的端口配置高寄存器配置完畢,也就是CPIOA_CRH配置完成,端口的輸出模式確定,不使用的都設為復位后的狀態,為0100,所以上面處理輸出為都是4
            ;將PC15置1
            MOV R0,#0x200; 二進制為0b10 0000 0000,第16位就是b9引腳的輸出電壓
            LDR R1,=GPIOB_ORD ;由r1控制ord寄存器
            STR R0,[R1] ;將ord寄存器的值變為r0的值
			
			 ;初始化GPIOC部分
            LDR R0,=GPIOC_CRH
            BIC R0,R0,#0x0fffffff;BIC 先把立即數取反,再按位與,就是將三十二位全部置零
            LDR R1,=GPIOC_CRH
            STR R0,[R1]
            ;上面的代碼是初始化CPIOC_CRH
            LDR R0,=GPIOC_CRH
            ORR R0,#0x20000000;開啟的是pc15,所以是2,為0100,是推挽輸出模式,最大速度為2mhz
            LDR R1,=GPIOC_CRH
            STR R0,[R1]
			;GPIOC的端口配置高寄存器配置完畢,也就是CPIOA_CRH配置完成,端口的輸出模式確定,不使用的都設為復位后的狀態,為0100,所以上面處理輸出為都是4
            ;將PC15置1
            MOV R0,#0x8000; 二進制為0b1000 0000 0000 0000,第16位就是c15引腳的輸出電壓
            LDR R1,=GPIOC_ORD ;由r1控制ord寄存器
            STR R0,[R1] ;將ord寄存器的值變為r0的值
         
            POP {R0,R1,PC};將棧中之前存的R0,R1,LR的值返還給R0,R1,PC
​```

LED_ON_A;亮燈
                PUSH {R0,R1, LR}    
                

​```
            MOV R0,#0x00 ;二進制為0b0000 0000 0000 0000,第16位為0,后面將作為pc15的輸出電壓
            LDR R1,=GPIOA_ORD ;將GPIOC的地址賦予r1
            STR R0,[R1];將r0的值賦予在GPIOC_ORD中
         
            POP {R0,R1,PC}
​```

LED_OFF_A;熄燈
                PUSH {R0,R1, LR}    
                

​```
            MOV R0,#0x80 ;二進制為0b 1000 0000 0000 0000,第16位為1,后面將作為pc15的輸出電壓
            LDR R1,=GPIOA_ORD ;將GPIOC的地址賦予r1
            STR R0,[R1] ;[]是指對里面的地址操作,所以是將r0的值賦予GPIOC_ORD
         
            POP {R0,R1,PC}  
​```

LED_ON_B;亮燈
                PUSH {R0,R1, LR}    
                

​```
            MOV R0,#0x000 ;二進制為0b0000 0000 0000 0000,第16位為0,后面將作為pc15的輸出電壓
            LDR R1,=GPIOB_ORD ;將GPIOC的地址賦予r1
            STR R0,[R1];將r0的值賦予在GPIOC_ORD中
         
            POP {R0,R1,PC}
​```

LED_OFF_B;熄燈
                PUSH {R0,R1, LR}    
                

​```
            MOV R0,#0x200 ;二進制為0b 1000 0000 0000 0000,第16位為1,后面將作為pc15的輸出電壓
            LDR R1,=GPIOB_ORD ;將GPIOC的地址賦予r1
            STR R0,[R1] ;[]是指對里面的地址操作,所以是將r0的值賦予GPIOC_ORD
         
            POP {R0,R1,PC}  
​```

LED_ON_C;亮燈
                PUSH {R0,R1, LR}    
                

​```
            MOV R0,#0x0000 ;二進制為0b0000 0000 0000 0000,第16位為0,后面將作為pc15的輸出電壓
            LDR R1,=GPIOC_ORD ;將GPIOC的地址賦予r1
            STR R0,[R1];將r0的值賦予在GPIOC_ORD中
         
            POP {R0,R1,PC}
​```

LED_OFF_C;熄燈
                PUSH {R0,R1, LR}    
                

​```
            MOV R0,#0x8000 ;二進制為0b 1000 0000 0000 0000,第16位為1,后面將作為pc15的輸出電壓
            LDR R1,=GPIOC_ORD ;將GPIOC的地址賦予r1
            STR R0,[R1] ;[]是指對里面的地址操作,所以是將r0的值賦予GPIOC_ORD
         
            POP {R0,R1,PC}             
​```

Delay
                PUSH {R0,R1, LR}
                

​```
            MOVS R0,#0
            MOVS R1,#0
            MOVS R2,#0
​```

DelayLoop0        
                ADDS R0,R0,#1

​```
            CMP R0,#330
            BCC DelayLoop0
            
            MOVS R0,#0
            ADDS R1,R1,#1
            CMP R1,#330
            BCC DelayLoop0 ;無進位

            MOVS R0,#0
            MOVS R1,#0
            ADDS R2,R2,#1
            CMP R2,#15
            BCC DelayLoop0
​```    	

POP {R0,R1,PC}    
            NOP
			END             

燒錄過程一致,小燈展示結果也同上方一樣


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM