STM32F103寄存器方式點亮LED流水燈


 

一、關於STM32

STM32F103C8T6是一款由意法半導體公司(ST)推出的基於Cortex-M3內核的32位微控制器,硬件采用LQFP48封裝,屬於ST公司微控制器中的STM32系列。

主要有三種類型的MCU:主流級別MCU、高性能MCU、低功耗MCU。再詳細一點,我們可以具體到STM32的命名規則,比如STM32F103C8T6中的“F”,代表的就是通用型,另外還有,比如S代表的是簡單型、L代表的是低功耗、H代表高性能、AL是汽車應用低功耗型、AF是汽車應用通用型。

二、STM32F103系列芯片的地址映射和寄存器映射原理

2.1 GPIO(general porpose intput output)

    GPIO(general porpose intput output):通用輸入輸出端口的簡稱。可以通過軟件控制其輸出和輸入。stm32芯片的GPIO引腳與外部設備連接起來,從而實現與外部通信,控制以及數據采集的功能。

2.2 寄存器映射與寄存器空間

  • Cortex‐M3 支持4GB 存儲空間。整塊4G存儲器開始地址標為0x0000_0000,結束地址為0xFFFF_FFFF,地址的位數是32位,那么2^32=4,294,967,296。
  • 由於一個基本的存儲單元是8bits即1Byte(每個地址對應一個存儲單元,這樣如果只是訪問某一bit就要使用位操作,或者使用位帶操作),因此4,294,967,296/1024=4,194,304KB,4,194,304/1024=4096MB,4094/1024=4GB。
    這4GB的存儲空間被划分成8個塊,每一塊用來與特定功能完成映射。映射關系如圖所示。

    每個寄存器都是32bit,占用4個Byte即4個存儲單元。可以把寄存器看作一個特殊的單元,一個這樣的單元占32bit,只要找到這個單元的起始地址就可以對其進行操作。

    其映射地址 = 外設總基地址(塊基地址)+ 總線相對於外設總基地址的偏移 + 具體外設基地址相對於總線基地址的偏移 + 寄存器相對於具體外設基地址的偏移。

    2.3 寄存器訪問

    以GPIOE_ODR寄存器為例:查芯片手冊知:ODR寄存器地址相對於GPIOE起始地址的偏移為:0Ch

    /*GPIOE_ODR = GPIOE_BASE+0x0C
    
    GPIOE_BASE = APB2PERIPH_BASE + 0x1800
    
    APB2PERIPH_BASE = PERIPH_BASE + 0x10000
    
    PERIPH_BASE = 0x40000000
    
    所以:
    
    GPIOE_ODR = 0x4001180C(寄存器的起始地址)*/
    /*****直接地址操作,改變寄存器的值****/
    *(unsigned int *)(0x4001180C)&= 0x00;    //初始化port5賦值為1,要拉低電平使用&操作
    delay_ms(300);
    *(unsigned int *)(0x4001180C)|= 0x20;
    delay_ms(300);

     

三、初始化設置

3.1 打開GPIO口時鍾



 

 

 

 

 

 

 

 

 

 

 

 

 

 

將GPIOA GPIOB GPIOC三個時鍾打開
#define RCC_APB2ENR (*(unsigned int *)0x40021018)

// 打開時鍾
RCC_APB2ENR |= (1<<2);  // 打開 GPIOA 時鍾
RCC_APB2ENR |= (1<<3);  // 打開 GPIOB 時鍾
RCC_APB2ENR |= (1<<4);  // 打開 GPIOC 時鍾

3.2 采用推挽輸出模式

 

 

 

 

 

 端口1-7為低,端口8-15為高。每個引腳由四個位控制。
以GPIOB和0號引腳(B0)為例,將其設置為推挽輸出,並設置最大速度為10MHz,則將控制B0的四個位設置為0001

 

 

 

 

#define GPIOB_CRL (*(unsigned int *)0x40010c00)

// 最后四位變為0001
GPIOB_CRL |= (1<<0);  // 最后一位變1
GPIOB_CRL &= ~(0xE<<0);  // 倒數2、3、4位變0

 

#define GPIOB_CRL (*(unsigned int *)0x40010C00)
#define GPIOC_CRH (*(unsigned int *)0x40011004)
#define GPIOA_CRL (*(unsigned int *)0x40010800)

// 配置 GPIO 口為推免輸出
// GPIOB----最后四位為0001
GPIOB_CRL |= (1<<0);  // 最后一位變1
GPIOB_CRL &= ~(0xE<<0);  // 倒數2、3、4位變0
// GPIOC----前四位為0001
GPIOC_CRH |= (1<<28);  //  第四位變1
GPIOC_CRH &= ~(0xE0000000);  // 前三位變0
// GPIOA----最后四位為0001
GPIOA_CRL |= (1<<0);  // 最后一位變1
GPIOA_CRL &= ~(0xE<<0);  // 倒數2、3、4位變0

3.3 設置低電平

 

1為高電平 0為低電平
以GPIOB和0號引腳(B0)為例,將其設置為低電平

#define GPIOB_ODR (*(unsigned int *)0x40010C0C)
GPIOB_ODR &= ~(1<<0);  // 最后一位變0
#define GPIOB_ODR (*(unsigned int *)0x40010C0C)
#define GPIOC_ODR (*(unsigned int *)0x4001100C)
#define GPIOA_ODR (*(unsigned int *)0x4001080C)

GPIOB_ODR &= ~(1<<0);  //最后一位變為0
GPIOC_ODR &= ~(1<<15); //倒數16位變為0
GPIOA_ODR &= ~(1<<0);  //最后一位變為

四、一些細節以及結果展示(First)

以下是一些我搭建電路過程中遇到的一些問題,希望能對大家有幫助

4.1需要准備的器件

 

 

 

  • USB轉TTL轉接口一個
  • 面包板一個
  • 杜邦線若干
  • STM32單片機一個

     

     

    4.2如何燒錄

這是32的單片機芯片引腳,我們可以看到A9口和A10口分別是TX和RX,我們需要將USB轉TTL上的TX接到單片機上的A10,RX接到單片機上的A9。方便我們使用MCUISP軟件燒錄HEX文件。

 

 

 這是我們剛剛拿到芯片時的樣子,我們可以從圖中看到黃色部分,上面是BOOT0,下面是BOOT1,我們需要將BOOT0置1,BOOT1置0,才能燒錄進程序。

 

 

 將將BOOT0置1,BOOT1置0后,我們按上述接上A9和A10,然后接3.3v和GND,安裝完CH340驅動,我們就可以燒錄程序。

否則,燒錄不成功,MCUISP程序會自增到401然后結束顯示無法燒錄。

關於Project的建立以及燒錄過程

 

 

 

 

 

 

 

 

 

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

 

 

 燒錄的過程選擇你要的HEX文件,然后保證上述過程正確,單片機已經可以被識別,然后點擊開始編程

 

 就會有如上圖的提示,如果開始編程后跳到401自行中斷,請檢查是否沒有置正確的BOOT0/1,A9/A10是否正確連接,如果都正確,可以按下單片機黃色下方的RESET鍵即可成功燒錄!

 

下面展示一下流水燈的結果

五、代碼結果

 流水燈代碼C語言實現

//--------------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;  
 GPIOB_ORD=0x1<<9;  
 GPIOC_ORD=0x1<<15;  
}
void B_LED_LIGHT(){
 GPIOA_ORD=0x1<<7;  
 GPIOB_ORD=0x0<<9;  
 GPIOC_ORD=0x1<<15;  
}
void C_LED_LIGHT(){
 GPIOA_ORD=0x1<<7;  
 GPIOB_ORD=0x1<<9;  
 GPIOC_ORD=0x0<<15;  
}
int main()
{
 int j=100;
 RCC_AP2ENR|=1<<2;   
 RCC_AP2ENR|=1<<3;   
 RCC_AP2ENR|=1<<4;   
 //????????? RCC_APB2ENR|=1<<3|1<<4;
 GPIOA_CRL&=0x0FFFFFFF;  
 GPIOA_CRL|=0x20000000; 
 GPIOA_ORD|=1<<7;  
 
 GPIOB_CRH&=0xFFFFFF0F;  
 GPIOB_CRH|=0x00000020;  
 GPIOB_ORD|=1<<9;   
 
 GPIOC_CRH&=0x0FFFFFFF;  
 GPIOC_CRH|=0x30000000;    
 GPIOC_ORD|=0x1<<15;  
 while(j)
 { 
  A_LED_LIGHT(); 
  Delay_ms(10000000);
  B_LED_LIGHT();
  Delay_ms(10000000);
  C_LED_LIGHT();
  Delay_ms(10000000);
 }
}

流水燈代碼匯編實現

RCC_APB2ENR EQU 0x40021018;配置RCC寄存器,時鍾,0x40021018為時鍾地址

GPIOB_BASE EQU 0x40010C00
GPIOC_BASE EQU 0x40011000
GPIOA_BASE EQU 0x40010800
    
GPIOB_CRL EQU 0x40010C00
GPIOC_CRH EQU 0x40011004
GPIOA_CRL EQU 0x40010800
    
GPIOB_ODR EQU 0x40010C0C
GPIOC_ODR EQU 0x4001100C
GPIOA_ODR EQU 0x4001080C

Stack_Size EQU  0x00000400;棧的大小
    
                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
                LDR R1,=RCC_APB2ENR
                STR R0,[R1]
                
                
                ;初始化GPIOA_CRL
                LDR R0,=GPIOA_CRL
                BIC R0,R0,#0x0fffffff;BIC 先把立即數取反,再按位與
                LDR R1,=GPIOA_CRL
                STR R0,[R1]
                
                LDR R0,=GPIOA_CRL
                ORR R0,#0x00000001
                LDR R1,=GPIOA_CRL
                STR R0,[R1]
                ;將PA0置1
                MOV R0,#0x01
                LDR R1,=GPIOA_ORD
                STR R0,[R1]
                
                
                ;初始化GPIOB_CRL
                LDR R0,=GPIOB_CRL
                BIC R0,R0,#0x0fffffff;BIC 先把立即數取反,再按位與
                LDR R1,=GPIOB_CRL
                STR R0,[R1]
                
                LDR R0,=GPIOB_CRL
                ORR R0,#0x00000001
                LDR R1,=GPIOB_CRL
                STR R0,[R1]
                ;將PB0置1
                MOV R0,#0x01
                LDR R1,=GPIOA_ORD
                STR R0,[R1]
                
                
                 ;初始化GPIOC
                LDR R0,=GPIOC_CRH
                BIC R0,R0,#0x0fffffff
                LDR R1,=GPIOC_CRH
                STR R0,[R1]
                
                LDR R0,=GPIOC_CRH
                ORR R0,#0x01000000
                LDR R1,=GPIOC_CRH
                STR R0,[R1]
                ;將PC15置1
                MOV R0,#0x8000
                LDR R1,=GPIOC_ORD
                STR R0,[R1]
             
                POP {R0,R1,PC};將棧中之前存的R0,R1,LR的值返還給R0,R1,PC
LED_ON_A
                PUSH {R0,R1, LR}    
                
                MOV R0,#0x00
                LDR R1,=GPIOA_ORD 
                STR R0,[R1]
             
                POP {R0,R1,PC}
             
LED_OFF_A
                PUSH {R0,R1, LR}    
                
                MOV R0,#0x01
                LDR R1,=GPIOA_ORD 
                STR R0,[R1]
             
                POP {R0,R1,PC}  
LED_ON_B;亮燈
                PUSH {R0,R1, LR}    
                
                MOV R0,#0x00
                LDR R1,=GPIOB_ORD
                STR R0,[R1]
             
                POP {R0,R1,PC}
             
LED_OFF_B;熄燈
                PUSH {R0,R1, LR}    
                
                MOV R0,#0x01
                LDR R1,=GPIOB_ORD
                STR R0,[R1]
             
                POP {R0,R1,PC}  
LED_ON_C;亮燈
                PUSH {R0,R1, LR}    
                
                MOV R0,#0x00
                LDR R1,=GPIOC_ORD
                STR R0,[R1]
             
                POP {R0,R1,PC}
             
LED_OFF_C;熄燈
                PUSH {R0,R1, LR}    
                
                MOV R0,#0x0100
                LDR R1,=GPIOC_ORD
                STR R0,[R1]
             
                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

六、參考鏈接

https://blog.csdn.net/NiceBabyaaa/article/details/120834837?spm=1001.2014.3001.5501

https://blog.csdn.net/junseven164/article/details/120804940

https://blog.csdn.net/geek_monkey/article/details/86291377

https://blog.csdn.net/geek_monkey/article/details/86293880

七、心得體會

之前從未接觸過STM32的我經過這次的學習過程,粗略了解了STM32的使用過程和理解了STM32的部分工作原理,希望今后的學習過程,能夠理解掌握STM32的使用知識,身為新手的我一開始對單片機的這些使用細節並不了解,在老師和同學的幫助下成功完成並且實現了流水燈的效果。希望今后自己的水平能夠提高到不僅僅只是模仿代碼以及電路的地步,而是提高到能夠獨立完成任務,甚至設計實現一些自己想要的東西。這次的學習是重新撿起單片機使用的一個過程,讓我了解到了利用硬件實現軟件端的困難和艱辛,同時克服困難的快樂也是無與倫比的,希望今后能夠更認真的學習單片機和嵌入式開發。

 


免責聲明!

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



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