欧美极品高清xxxxhd,国产日产欧美最新,无码AV国产东京热AV无码,国产精品人与动性XXX,国产传媒亚洲综合一区二区,四库影院永久国产精品,毛片免费免费高清视频,福利所导航夜趣136

 找回密碼
 立即注冊

QQ登錄

只需一步,快速開始

搜索
查看: 1642|回復(fù): 1
收起左側(cè)

STM32初學(xué)者,寫博客只是為了記錄學(xué)習(xí),希望能有大神指點一下

[復(fù)制鏈接]
ID:430402 發(fā)表于 2019-7-6 10:52 | 顯示全部樓層 |閱讀模式


# 初學(xué)者入門書有沒有推薦的
  大學(xué)學(xué)的是單片機89C51,基礎(chǔ)不是很好,也就一知半解,能在百度的幫助下修改代碼,實現(xiàn)程序功能,下學(xué)期大四,因為現(xiàn)在很多公司都用的是嵌入式STM32 招聘要求上寫的也是要熟悉STM32和Linux,所以我覺得去認真學(xué)習(xí)一下STM32,在一開始的時候,很懵圈,直到看到了一本嵌入式入門文章 《零死角玩轉(zhuǎn)STM32》 才有一些了解STM32,下面是書中的語段,想把一些學(xué)習(xí)到的東西記錄下來。
  很感謝在網(wǎng)絡(luò)上無私奉獻,分享經(jīng)驗的前輩,雖然我感覺我還沒入門,但如果只靠一些書籍,我很難自己理解,下面的文章都是文庫里的資料,我只想再學(xué)習(xí)一次,記錄下來,如果誰知道這本書的,和我說一下哪里能買,淘寶上找不到這本書
  ![在這里插入圖片描述](?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM3NjIwNzIw,size_16,color_FFFFFF,t_70)
#  一開始通過51來認識STM32
  51是嵌入式的入門級的經(jīng)典MCU,結(jié)構(gòu)簡單,易于教學(xué),但是現(xiàn)在市場產(chǎn)品競爭愈發(fā)激烈,對成本及其敏感,相應(yīng)地對MCU的要求也更高了,所以STM32的出現(xiàn),不是偶然是一種趨勢,
    本章要結(jié)合 《STM32中文參考手冊》和《CM3權(quán)威指南CnR2》 一起閱讀,效果更佳
#  用寄存器點亮LED
##  51點亮LED燈
如何用51來點亮一個LED
在硬件上,我們假設(shè)在51單片機的P0口的第0位接一個LED,負邏輯亮,代碼上,我們會這樣寫

    P0=0XFE;

關(guān)掉LED則是


     P0=0XFE;

這里面我們用的是總線的操作方法,即是對P0口的8個IO口同時操作,但起作用的只是P0^0,除了總線操作,我們還學(xué)習(xí)過位操作,利用51編譯器的關(guān)鍵字sbit,我們可以定義一個位變量:

```
Sbit LED=P0^0;
```

  那么給0亮, 給1滅
  *

    *LED=0; //亮
        LED=1;//滅**

###   寄存器
![在這里插入圖片描述](?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM3NjIwNzIw,size_16,color_FFFFFF,t_70)
在點亮LED的時候,我們都是用操作寄存器的方式來實現(xiàn)的,那這個寄存器到底是什么?為什么我們可以直接操作P0口?
   以**STC89C51**為例,該單片機主要由51內(nèi)核,外設(shè)IP,總線三大部分組成,內(nèi)核由Inter公司生產(chǎn)的,總線是用來連接內(nèi)核和外設(shè)的接口單元
    **寄存器則是內(nèi)置于各個IP外設(shè)中,是一種用于配置外設(shè)功能的存儲器,就是一種內(nèi)存,并且有相對應(yīng)的地址**。學(xué)過C語言我們就知道,要操作這些內(nèi)存就可以使用C語言中的指針,通過尋址的方式,來操作這些具有特殊功能的內(nèi)存-存儲器,比如 P0口對應(yīng)的地址是0X80 ,那么我們需要修改0X80這個地址對應(yīng)的內(nèi)存的內(nèi)容的話,按常理可以這樣操作:


```
*(0X80) =0XFE
```
可我們這樣編譯的時候,編譯器會報錯,在51里面只能通過Sfr和sbit這倆個關(guān)鍵字來是實現(xiàn)寄存器的映像,不能直接操作寄存器對應(yīng)的地址,這是51不同于STM32的地方
  51單片機的這些寄存器位于地址80H—FFH中,對應(yīng)128個地址,但不是每個地址都是有效的,51有21個,52系列則有26個,其他的都是保留區(qū)。
###   寄存器映射
  實際上我們在編程的時候并不是通過指針來操作寄存器的,二十直接給P0,P1這些端口寄存器賦值,那么這些外設(shè)資源是如何與地址一一對應(yīng)的關(guān)系,這也得益與51**特有**的倆個關(guān)鍵字 SFR和SBIT ,其他單片機沒有,只能用其他的方式來實現(xiàn)寄存器映射,這倆個關(guān)鍵字幫我們實現(xiàn)了所有寄存器的定義,所以我們才可以像操作普通變量來控制寄存器,所以,我們一開始的點燈LED的代碼,全貌應(yīng)該是


     sfr P0 =0X80;
      P0=0XFE;

為了方便起見,我們可以把寄存器映射全部寫好封裝在一個頭文件里面,不用每用一個寄存器就定義一次,其實這方面的工作,不用我們做,我們在編程的時候都會在開始的地方添加一個頭文件:`#include<reg51.h>`這個頭文件已經(jīng)實現(xiàn)了全部寄存器的定義,該文件是keil自帶的
在安裝目錄Keil \C51\INC 下面可以找到,這個文件實現(xiàn)了字節(jié)寄存器和位寄存器的定義
![在這里插入圖片描述](?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM3NjIwNzIw,size_16,color_FFFFFF,t_70)
###   啟動文件-STARTUP.A51
   還有一個就是啟動代碼,這是大多數(shù)人容易忽略的地方,我們主要總結(jié)一下它的功能,不詳細講解里面的代碼
   單片機在上電之后,首先執(zhí)行的是啟動文件,而不是我們通常看到的main函數(shù),我們新建51工程的時候會有一個提示,
   ![是否添加啟動代碼](?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM3NjIwNzIw,size_16,color_FFFFFF,t_70)
是否添加啟動代碼,代碼主要實現(xiàn)了以下功能:清除內(nèi)部數(shù)據(jù)存儲器,清除外部數(shù)據(jù)存儲器,清除外部頁儲存器,初始化Small模式下的可重入棧和指針,初始化large模式下可重入棧和指針,初始化compact模式下的可沖入棧和指針,初始化8051硬件棧指針,傳遞初始化全局變量的控制命令或者在沒有初始化全局變量時給main函數(shù)傳遞命令,然后程序就跳轉(zhuǎn)到main函數(shù),來到我們熟悉的C語言世界

##  STM32
  現(xiàn)在我們對比51點亮LED的方法,我們先用操作寄存器的方法用STM32點亮一個LED,然后再一步步完善代碼,構(gòu)建最簡單的庫函數(shù),讓我們知道庫函數(shù)是怎么建立起來的
1. 啟動文件
新建工程,把工程放入事先建好的文件夾中,然后在工程目錄下添加startup_stm32f10x_hd.s
   STM32的啟動文件主要實現(xiàn)了:
   1.設(shè)置初始SP。
   2.設(shè)置初始PC=Reset_Handler  
   3.設(shè)置向量表入口地址,并初始化向量表。
   4.調(diào)用庫函數(shù)SystemInit ,把系統(tǒng)時鐘配置成72M,SystemInit 在庫文件system_STM32F10.C定義.
   5.跳轉(zhuǎn)到標號_mian,最終來到C的世界,這里我們先去除繁枝細節(jié),挑重點的講,主要理解第四和第五點,在啟動文件的147—155行,是復(fù)位處理函數(shù),代碼如下


```
1 ;Reset handler
2 Reset_Handler   PROC
3 EXPORT Reset_Handler
4 IMPORT _main
5 IMPORT  SystemInit
6 LDR        R0,=SystemInit
7 BLX        R0
8 LDR          R0,=main
9  BX R0
10 ENDP

```
簡單解釋一下這10行代碼
第一行 是程序注釋,在匯編中注釋是“ ;”

第二行,是定義了一個子程序 Reset_Handler ,而 PROC和最后的ENDP 配合使用,是子程序定義偽指令
一般用法
![在這里插入圖片描述]()
第三行 EXPORT  表示 該子程序可供其他模塊調(diào)用
關(guān)鍵字,【W(wǎng)EAK】表示弱定義,如果編譯器發(fā)現(xiàn)在別處定義了同名的函數(shù),則在連接時用別處的地址進行鏈接,如果其他地方?jīng)]有定義,編譯器也不報錯,以此處地址進行鏈接

第四行 和 第五行IMPORT 說明SystemInit和_main 這倆個標號在其他文件,在鏈接的時候需要到其他文件去尋找

SystemInit 在庫文件 system_stm32f10x.c實現(xiàn),用來初始化,STM32的一系列時鐘,把系統(tǒng)時鐘設(shè)置為72MHZ,STM32的時鐘比51單片機復(fù)雜,需要經(jīng)過一系列的配置才能達到穩(wěn)定運行的狀態(tài)
_main 其實不是我們定義的,當編譯器編譯時,只要遇到這個標號就會定義這個函數(shù),該函數(shù)的主要功能是:負責(zé)初始化棧,堆,配置系統(tǒng)環(huán)境,并在最后跳轉(zhuǎn)到用戶自定義的main函數(shù)

第六行  把SystemInit的地址加載到寄存器 R0
第七行 程序跳轉(zhuǎn)到R0中的地址執(zhí)行程序,之后系統(tǒng)的時鐘被設(shè)置為72MHZ
第八行把_main 的地址加載到寄存器R0
第九行程序跳轉(zhuǎn)到R0中的地址執(zhí)行程序,執(zhí)行完畢之后就去我們熟悉的C世界
第十行表示子程序結(jié)束

總結(jié)一下,Reset_Handler  這個函數(shù)執(zhí)行了倆個函數(shù)的調(diào)用,一個是SystemInit ,把系統(tǒng)的時鐘設(shè)置成72M,令一個_main,初始化系統(tǒng)環(huán)境,最終調(diào)用C的main
**等會我們的主要任務(wù)還是點亮 LED 的時候采用最簡單的方法,直接使用內(nèi)部的LSI時鐘,(8MHZ)作為主時鐘即可,不使用外部時鐘LSE **
  _main 函數(shù)由編譯器生成,負責(zé)初始化棧,堆等,,并在最后跳轉(zhuǎn)到用戶自定義的main函數(shù)

3. mian.c
先編寫一個 main 函數(shù)
![在這里插入圖片描述]()
這時候出現(xiàn)了一個錯誤


```
Undefined symbol SystemInit (referred from startup_stm32f10x_hd.o)
```
SystemInit 沒有定義,從分析啟動文件時,我們知道,Reset_Handler 調(diào)用了該函數(shù)用來初始化系統(tǒng)時鐘,而該函數(shù)實在庫文件 System_stm32f10x.c中實現(xiàn)的,我們重新寫一個函這個的函數(shù)也可以,把功能完整的實現(xiàn)一邊,但為了方便起見,我們在main文件里面定義一個SystemInit空函數(shù),為的是騙過編譯器,把這個錯誤去掉,關(guān)于配置系統(tǒng)時鐘我們在后面再寫簡單的代碼
![在這里插入圖片描述]()
這時候編譯沒有錯了,還有一個方法是再啟動文件中把關(guān)于SystemInit函數(shù)的代碼注釋掉也可以,代碼如下:
![在這里插入圖片描述]()
4. 控制IO口
  下面我們從三個方面來講解STM32的IO口在控制LED時,和51的區(qū)別,關(guān)于STM32DE IO寄存器的介紹,我們可以看《STM32中文參考手冊》第八章即可,下面涉及的IO寄存器均來自這一章的第二小節(jié)


###   寄存器
####    1.電平控制
51單片機的IO口如果要輸出1和0,可以直接賦值,不用控制其他寄存器
而STM32的IO口比較復(fù)雜,如果要輸出1和0,則要通過控制;端口輸出數(shù)據(jù)寄存器ODR來實現(xiàn) ,ODR 是 Output data register 的簡寫,在STM32里面,其寄存器的命名都是英文的簡寫,很容易記住,從手冊上我們知道ODR是一個32位的寄存器,低16位有效,高16位保留,低16位對應(yīng)著IO 0- IO 16 ,只要往對應(yīng)的位置寫入0或者1 就可以輸出低或者高電平

![在這里插入圖片描述](?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM3NjIwNzIw,size_16,color_FFFFFF,t_70)
PB0輸出低電平,代碼如下


```
GPIOB_ODR  = 0<<0
```
這時候編譯,會有一個錯誤,說 GPIOB_ODR 沒有定義,不過我們確實沒有定義,在51單片機中,我們可以直接往P0口賦值,那是因為在reg51.h這個頭文件里面實現(xiàn)了P0口這個寄存器的映像,用的是51特有的SFR來實現(xiàn)的,但是STM32不一樣沒有,sfr,所以只能用其他的方式來實現(xiàn)寄存器映像,因為寄存器實際上就是具有特殊功能的內(nèi)存,那么我們可以通過宏定義來實現(xiàn)寄存器映像,其實ST的庫函數(shù)中用的也是這種方法,從手冊中我們看到ODR寄存器的偏移是:0CH,(上圖左上角),這個偏移地址是基于端口的起始地址而言的,在STM32中,每個外設(shè)都有一個起始地址,叫外設(shè)基地址,外設(shè)的寄存器,就以這個基地址位標準按照順序排列,跟結(jié)構(gòu)體里的成員差不多。
在手冊的第二張,2.3 存儲器映像中,可以查找所有外設(shè)的基地址,如下![在這里插入圖片描述](?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM3NjIwNzIw,size_16,color_FFFFFF,t_70)
其中 GPIOB的起始地址位 : 0X40010C00   ,從這個地址,我們可以算出GPIOB_ODR寄存器的地址位:0x40010C0C
  0X4001 0C00 + (地址偏移)0X 0C =(寄存器地址)
  所有定義代碼如下:


```
#define GPIO_ODR  *(volatile unsigned long *) 0x40010C0C
```
**long是32位整型,unsigned指無符號數(shù),左邊的*表示取內(nèi)容
volatile表示易變的,告訴編譯器不要優(yōu)化,這個地址的內(nèi)容不一定是在程序中改變的。
volatile unsigned long * 表示將后面跟的內(nèi)容轉(zhuǎn)化成一個指針,并且是指向一個易變的無符號整數(shù)。
左邊再加個 * ,表示取該指針指向地址的內(nèi)容。
總的意思是取那個內(nèi)存單元(內(nèi)存地址0x40010C0C)里存的數(shù),并將這個數(shù)轉(zhuǎn)化為無符號整數(shù)**

所以有了這個寄存器定義,我們就可以直接控制操作FPIOB_ODR了
####    2.方向控制
![在這里插入圖片描述](?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM3NjIwNzIw,size_16,color_FFFFFF,t_70)
  雖然配置了ODR,但這個時候還不能點亮LED,因為STM32的IO口還要配置方向,這個由端口配置寄存器來控制,端口配置寄存器分為高低倆個,每4bit控制一個IO口,所以端口配置寄存器:CRL控制這個IO口的低8位,端口配置高寄存器: CRH控制這個IO口的高8bit,在4位一組的控制位中,CNFy【1:0】用來控制端口的輸入輸出,MODEy【1:0】用來控制輸出模式的速率,即輸出時,IO電平翻轉(zhuǎn)的速度
   輸入有三種模式。輸出有四種模式,我們在控制LED的時候,選擇通用推挽輸出。
   輸出速率有三種模式:2M,10M,50M,這里我們選擇2M
   同GPIOB_ODR 一樣,我們也可以算出FPIO_CRL的地址為:0X40010C00 那么設(shè)置PB0為通用推挽輸出,輸出速率為2M的代碼則如下
   `#define GPIOB_CRL  *(volatile unsigned long *) 0x40010C00
    //配置PB0位通用推挽輸出,輸出速率為2M
    GPIOB_CRL = 2<<0 | 0<<0`
  這個代碼我又查了了一下

      GPIOB->CRL = 0x00000002;
       //PB0:CNF0[1:0]=00, MODE0[1:0]=10
這樣就好理解多了,主要還是要學(xué)會看手冊
####    3.時鐘控制
![A]()
![在這里插入圖片描述]()
當我們設(shè)置了IO口的方向,并在相應(yīng)的輸出寄存器里輸入了值得時候,覺得現(xiàn)在總算可以點亮LED了吧,其實還差最后一步
  STM32外設(shè)很多,未來降低功耗,每個外設(shè)都對應(yīng)了一個時鐘,在系統(tǒng)復(fù)位的時候這些時鐘都是關(guān)閉的,想要這些外設(shè)工作,必須把相應(yīng)的時鐘打開
   而STM32的所有外設(shè)的時鐘由一個專門的外設(shè)來管理叫RCC(reset and clock contrlo) 在手冊第六章
  STM32的外設(shè)因為速率的不同,分別掛載在三條總線上,AHB,APB2,APB1,AHB位高速總線,APB2次之,APB1再次之,所有的IO口都掛載在APB2總線上,屬于高速外設(shè),時鐘有APB2外設(shè)時鐘使能寄存器 (RCC_APB2ENR )來控制,其中PB端口的時鐘由該寄存器的位3寫1使能。
   同ODR 和CRL ,我們可以算出RCC_APB2ENR的地址
   0x40021018


```
#define RCC_APB2ENR  *(volatile unsigned long *) 0x40021018
   //開啟端口B
    GPIOB_CRL = 1<<3;
手冊上位3為 B端口使能端 給1 使能
```
如果你足夠細心,你會發(fā)現(xiàn)我們雖然開了端口時鐘,那么這個時鐘到底多大呢?又是用哪里來的呢
如果我們用的是庫,那么會有一個庫函數(shù) SystemInit,會幫我們把系統(tǒng)時鐘設(shè)置位72M 但現(xiàn)在我們沒有使用庫,那現(xiàn)在時鐘是多少呢?答案是8M,當外部HSE沒有開啟或者出現(xiàn)故障的時候,系統(tǒng)時鐘由內(nèi)部低速時鐘LSI提高,現(xiàn)在我們確實沒開啟HSE,所有系統(tǒng)默認的時鐘位LSI=8M,至于更深入的細節(jié),在后面的RCC時鐘樹中在詳細分析,如果你想自己先嘗嘗鮮,那么可以看RCC外設(shè)中的:時鐘控制寄存器 (RCC_CR)和時鐘配置寄存器(RCC_CFGR)這倆個寄存器即可
####    4.水到渠成
我們現(xiàn)在控制了電平,配置了方向,開啟了時鐘,經(jīng)過這三部,我們總算可以控制一個LED了,比起51直接輸出點評,控制STM32的IO多了倆步:即配置方向可開啟時鐘,比起AVR和PIC這倆種單片機則多了開啟時鐘這一步

下面是一個完整的用STM32控制一個LED代碼

```
#define RCC_APB2ENR *(volatile unsigned long *)0x40021018
#define GPIOB_CRL   *(volatile unsigned long *)0x40010C00
#define GPIOB_ODR   *(volatile unsigned long *)0x40010C0C
int main (void)
{
    //開啟端口B 的時鐘
        RCC_APB2ENR |= 1<<3;
        //配置PB0位輸出速率為2M,通用推挽輸出
        GIOPB_CRL  |=(2<<0)|(0<<2);           
        //PB0輸出低電平,點亮LED
        GIOPB_ODR = 0<<0 ;
}
void SystemInit (void)
{

}
```
###    再接再厲
學(xué)習(xí)STM32存在一個用庫好還是用寄存器好的爭議,就好像編程用匯編好還是用C好一月,其實孰優(yōu)孰劣,在市場上自有定論,用戶群說明一切
雖然我們上面用寄存器點亮了LED,乍一看好像代碼也很姜丹,但是我們別僥幸以后就一直用寄存器開發(fā)了,在用寄存器點亮LED的時候,我們是否發(fā)生STM32的寄存器都是32位的,在配置時非常容易出錯,而且代碼還很不好理解,所以學(xué)習(xí)STM32最好的方法是用庫,然后在庫的基礎(chǔ)上了解底層,看遍所有寄存器
這里我們講一下,關(guān)于GPIO庫,其他的外設(shè)我們直接參考庫學(xué)習(xí)即可,不必自己寫
#### 定義外設(shè)寄存器結(jié)構(gòu)體
   上面我們在操作寄存器的時候,操作的是寄存器的絕對地址,如果每個寄存器都這樣操作的話,那將非常麻煩
    我們考慮到外設(shè)寄存器的地址都是基于外設(shè)地址的偏移地址,都是在外設(shè)基地址上逐個增加的,每個寄存器占了32個或16個字節(jié),這種方式和結(jié)構(gòu)體里面的成員類似,所以我們可以定義一種外設(shè)結(jié)構(gòu)體,結(jié)構(gòu)體的地址等于外設(shè)的基地址,結(jié)構(gòu)體的成員等于寄存器,成員的排列順序跟寄存器的順序一月,這樣我們操作寄存器的時候,就不用每次都找到絕對地址,只要找到外設(shè)的基地址就可以操作外設(shè)的全部寄存了
    下面我先定義一個GPIO 寄存器結(jié)構(gòu)體,結(jié)構(gòu)體里的成員全是GPIO的寄存器,按寄存器的偏移地址從低到高排列


    //GPIO 寄存器結(jié)構(gòu)體
     typedef struct(
      _IO uint32_t CRL;
      _IO uint32_t CRH;
      _IO uint32_t IDR;
      _IO uint32_t ODR;
      _IO uint32_t BSRR;
      _IO uint32_t BRR;
      _IO uint32_t LCKR;
     )          GPIO_TypDef;


在手冊 第8.2我們可以找到7個寄存器描述,在點亮LED的時候,我們只用了CRL和ODR這倆個寄存器,至于其他寄存器功能,自行看手冊
在GPIO結(jié)構(gòu)體中,我們用來倆個數(shù)據(jù)類型,一個是 uint32_t ,表示無符號的32位整型,因為GPIO的寄存器都是32位的,這個類型聲明在標準頭文件stdint.h里面,我們在程序上只要包含這個頭文件即可
另外一個是_IO,這個是我們自己定義的,原型是volatile ,作用是高速編譯器不要因優(yōu)化而省略此指令,必須每次都直接讀寫其值,這樣就可以確保每次讀寫或者寫寄存器都真正執(zhí)行到位
為了這倆個數(shù)據(jù)類型,我們添加一下代碼

```
#includu <stdint.h>
#define _IO  volatile

```
#### 外設(shè)聲明
   寄存器結(jié)構(gòu)體已經(jīng)定義好了,GPIO端口分為A-G,每個端口都含有GPIO_TypDef 類型的指針,然后我們就可以根據(jù)端口名(實際上現(xiàn)在是結(jié)構(gòu)體指針)來操作各個端口的寄存器,代碼實現(xiàn)如下:

```
//GPIO端口及地址
#define GPIOA_BASE  (APB2PERIPH_BASE+0X0800)
#define GPIOB_BASE  (APB2PERIPH_BASE+0X0C00)
#define GPIOC_BASE  (APB2PERIPH_BASE+0X1000)
#define GPIOD_BASE  (APB2PERIPH_BASE+0X1400)
#define GPIOE_BASE  (APB2PERIPH_BASE+0X1800)
#define GPIOF_BASE  (APB2PERIPH_BASE+0X1C00)
#define GPIOG_BASE  (APB2PERIPH_BASE+0X2000)
```
   對于其他外設(shè),我們也可以這樣將外設(shè)的名字定義為一個外設(shè)寄存器結(jié)構(gòu)體類型的指針,這里我們只講GPIO

![在這里插入圖片描述](?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM3NjIwNzIw,size_16,color_FFFFFF,t_70)

這里我蠻奇怪的第一個地方,還在探索,為什么GPIO端口F和G的起始位置一樣

#### APB1,APB2,AHB 總線基地址
![在這里插入圖片描述]()
這張圖在之前有,只是我拿出來說一下,總線的位置在這里然后現(xiàn)在先定義基地址,然后在基地址上加入偏移地址就可以

   ![在這里插入圖片描述]()
直接上圖
![在這里插入圖片描述](?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM3NjIwNzIw,size_16,color_FFFFFF,t_70)
![在這里插入圖片描述](?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM3NjIwNzIw,size_16,color_FFFFFF,t_70)


    #include <stdint.h>          //點一個LED燈
    #include <stm32f10.h>
    #define _IO volatile
    //GPIO 寄存器結(jié)構(gòu)體
     typedef struct(
      _IO uint32_t CRL;
      _IO uint32_t CRH;
      _IO uint32_t IDR;
      _IO uint32_t ODR;
      _IO uint32_t BSRR;
      _IO uint32_t BRR;
      _IO uint32_t LCKR;
     )          GPIO_TypDef;
     //RCC 寄存器結(jié)構(gòu)體
     typedef struct(
      _IO uint32_t CR;
      _IO uint32_t CFGR;
      _IO uint32_t CLR;
      _IO uint32_t APB2RSTR;
      _IO uint32_t APB1RSTR;
      _IO uint32_t AHBENR;
      _IO uint32_t APB2ENR;
      _IO uint32_t APB1ENR;
      _IO uint32_t BDCR;
      _IO uint32_t CSR;
     ) Rcc_TypDef;

      //總線基地址
     #define PERIPH BASE  ((uint32_t)0x40000000)                   //外設(shè)基地址
     #define APB1PERIPH_BASE  PERIPH_BASE
     #define APB2PERIPH_BASE  (PEPIPH_BASE+0X10000)
     #define AHBPERIPH_BASE  (PEPIPH_BASE+0X20000)
    //GPIO端口及地址
     #define GPIOA_BASE  (APB2PERIPH_BASE+0X0800)
     #define GPIOB_BASE  (APB2PERIPH_BASE+0X0C00)
     #define GPIOC_BASE  (APB2PERIPH_BASE+0X1000)
     #define GPIOD_BASE  (APB2PERIPH_BASE+0X1400)
     #define GPIOE_BASE  (APB2PERIPH_BASE+0X1800)
     #define GPIOF_BASE  (APB2PERIPH_BASE+0X1C00)
     #define GPIOG_BASE  (APB2PERIPH_BASE+0X2000)
     //RCC
     #define RCC_BASE ( AHBPERIPH_BASE+0X1000)
    //外設(shè)聲明
     #define GPIOA ((GPIO_TypeDef*) GPIOA_BASE)
     #define GPIOB ((GPIO_TypeDef*) GPIOB_BASE)
     #define GPIOC ((GPIO_TypeDef*) GPIOC_BASE)
     #define GPIOD ((GPIO_TypeDef*) GPIOD_BASE)
     #define GPIOE ((GPIO_TypeDef*) GPIOE_BASE)
     #define GPIOF ((GPIO_TypeDef*) GPIOF_BASE)
     #define GPIOG ((GPIO_TypeDef*) GPIOG_BASE)
     #define RCC   ((RCC_TypeDef*)    RCC_BASE)

     //
     #define RCC_APB2ENR *(volatile unsigned long *)0x40021018
     #define GPIOB_CRL   *(volatile unsigned long *)0x40010C00
     #define GPIOB_ODR   *(volatile unsigned long *)0x40010C0C

     int main (void)
     {
        //開啟端口B 的時鐘
            RCC->APB2ENR |= 1<<3;
            //配置PB0位輸出速率為2M,通用推挽輸出
            GIOPB->CRL  |=(2<<0)|(0<<2);           
            //PB0輸出低電平,點亮LED
            GIOPB->ODR = 0<<0 ;
     }
     void SystemInit (void)
     {

     }

這里我們用的是宏定義,之前我們用的是結(jié)構(gòu)體定義,所以有沒有看出來在main函數(shù)的代碼有一些不一樣

用結(jié)構(gòu)體可以直接操作,用宏定義還要一個個的找到寄存器的絕對地址進行重新定義

比如開時鐘
RCC->APB2ENR |= 1<<3;
這章節(jié)本來后面還有一些是關(guān)于軟件的實際操作的,但是因為剛剛學(xué)還沒買開發(fā)板,學(xué)生一名,想多看看,對比一下,找一下性價比高的開發(fā)板,如果有這本書的,電子版的或者實體銷售店的··留言一下, 這本書第一次看的時候感覺剛好把我學(xué)的51和想學(xué)的STM32結(jié)合在一起,更好理解了


上面是我自己選重點給自己看的
https://wenku.baidu.com/view/193e26de900ef12d2af90242a8956bec0975a58c.html  原文件
指針的概念理解
https://www.cnblogs.com/Waming-zhen/p/4353963.html


回復(fù)

使用道具 舉報

ID:579046 發(fā)表于 2019-7-7 10:35 | 顯示全部樓層
自學(xué)過一點51想學(xué)32,感覺32資料好多 我了解的信息也不太多,有點不好上手。樓主有好的入門方式嗎?主要想先了解32都要學(xué)啥,
回復(fù)

使用道具 舉報

您需要登錄后才可以回帖 登錄 | 立即注冊

本版積分規(guī)則

小黑屋|51黑電子論壇 |51黑電子論壇6群 QQ 管理員QQ:125739409;技術(shù)交流QQ群281945664

Powered by 單片機教程網(wǎng)

快速回復(fù) 返回頂部 返回列表