如果不會暫存器開發而陷入瓶頸,那麼本文將會有較大幫助

如果

不會暫存器開發而陷入瓶

那麼本文將會有較大幫助

如果不會暫存器開發而陷入瓶頸,那麼本文將會有較大幫助

//

/

插播一條:我自己在今年年初錄製了一套還比較系統的入門微控制器教程,想要的同學找我拿就行了免費的,私信我就可以

~

點我頭像黑色字型加我地球呺也能領取哦。最近比較閒,帶做畢設,帶學生參加省級或以上比

///

如果不會暫存器開發而陷入瓶頸,那麼本文將會有較大幫助

0。

緒論

對於經過系統培訓的開發

微控制器

So

C

的驅動開

不管是使用各種庫還是直接上寄存

都不成問

可是很多非專業但是需要幹嵌入式或微控制器工程的

比如機

車輛工

或是其他的一些專

有時候這些學生需要搞比

做項

不可避免的要用到一些單片

由於種種原

現在幾乎都會首

STM32)。

但是缺乏系統訓練的學生往往無法看懂暫存器版本的例

或是別人的開源項

自己寫的話更是不知道如何開

或者編譯完成後總是出現莫名的問

這給大家帶來了極大的困

同時網際網路上大部分教程都是轉載來轉載

各種差異和版本都

很多人即使看過了也是一頭霧

所以應一個同學要

大概寫一下暫存器的一些操

本教程將會使

學生使用者體量龐

STM32F1x

x

系列的微控制器作為例

但是不必擔

所有的暫存器操作都是共通

並不會有本質的差

希望你能快速閱讀完並且理解

然後查閱自己需要的資

不管是什麼東

西

這種方式都是通用

嵌入式軟體開發具有一個比較龐大的知識體

限於作者的時間和水

本文不可能講太多東

西

但是如果你只是

因為不會暫存器開發而陷入瓶

那麼本文將會有較大幫助

畢竟這個是寫文章的目

)。

理解本文需要一些基

大概包

: c/c++,

一些數

計算機基礎知

大多數理工類專業都會有相關的課程

如果確實存疑可以即時搜

讓我們先記住開發的正確方

摘抄借

修改糅

以實現功能為目

以別人的程式碼為基

只要不涉及什麼智慧財產權的

這樣做是完全沒有問題

畢竟不需要把很多基礎性的東西寫來寫

所以請大膽的找開源項

儘可能在基礎上

而不是從零開

不廢話

開始正

比如十進位制下

0。

1

就是二進位制的無限不迴圈小

上面那個例子也

著名

IEEE754 floa

t

浮點數標準導致

bug:

在很多語言

, 0。1+0。

2

0。3

如果不會暫存器開發而陷入瓶頸,那麼本文將會有較大幫助

就是因

0。

1

是二進位制無限迴圈小數的原

但是儲存器位寬不能是無窮

所以產生舍入誤

如果不會暫存器開發而陷入瓶頸,那麼本文將會有較大幫助

在瀏覽器中按

F1

2

進入開發者模

JavaScrip

t

下的浮點數精

bug

所以在很多專案

為了實現當兩個值相等時觸發什麼函

往往不會直接寫相

而是兩者的差值小於多少時即生效

float

a,b;……。。

if

a

==

b){

/

/

這種寫法不建議

}……。。

if

abs(a

-

b)

0。00001

){

/

/

一般這麼寫

}

二進位制與

/

十六進位制的轉換

剛才那個方法就可以直接

還有其他演算法這裡不

先讓我貼一個表

BIN

DEC

HEX

0000

0001

1

1

0010

2

2

0011

3

3

0100

4

4

0101

5

5

0110

6

6

0111

7

7

1000

8

8

1001

9

9

1010

10

A

1011

11

B

1100

12

C

1101

13

D

1110

14

E

1111

15

F

BI

N

是二進

, DE

C

是十進

, HE

X

是十六進

都是英語簡

十六進位制和二進位制可以直接換

方法是每四位二進位制看作一個十六進位制

比如

0110 1101 1011 1001

6 D B 9

轉換表背過的話讀程式碼快

因為一般情況

為了讓一行程式碼看的不至於太

人們會用十六進位制代表二進

尤其是對於擁

3

2

cortex-M

3

核心

STM32F

1

系列單片

一次寫一個三十二位數屬實太冗

c/c+

+

預設寫的數字都是十進

二進位制應該

0

b

0b00101100,

而十六進位制

0

x

0x3C。

/

/

一般這麼寫

GPIOB

->

CRL

&=

0x00440000

/

/

這麼寫就不太美觀了

GPIOB

->

CRL

&=

0b00000000010001000000000000000000;

數學差不多

開始正

2。 c/c+

+

語言基礎

a+b; /

/

加法

a-b; /

/

減法

a*b; /

/

乘法

a/b; /

/

所有計算需要注意整

和浮點

的運算區

如有疑問自行搜尋

a%b; /

/

就是小學學的餘

。 14÷4=3 。。。 2

14mod4=2, 14/4=3。 float(14)/4=3。5f

a<

/

a

看為二進位制

整個向左移

b

00000110<

00011000。

當溢位的時候會發生什麼

a>>b; /

/

和上面一樣的功

a&b; /

/

按位求

(AND),

11010010

// & 01010011

// ——————

// 01010010

a|b; /

/

按位取

OR)

~a; /

/

按位取

NOT)

!a; /

/

邏輯

注意和上面的區

c+=a; /

/

相當

c=c+a;

其他算符同

重點看左右移和位操

)。

3。

我們配寄存

(register

到底是在配置什麼

你的最終目的都是使用微控制器

GPIO(general pin input & output

/

輸出一個高電平還是低電

不管是諸

I2C, SP

I

的通

還是按鍵讀

亮燈報

說到底都是高低電平的控制或探

AD

C

輸入的是模

(Analog

但是會被轉化為數

(Digital

一樣是高低電

這裡暫且不談

或者一些功能的使能及配置一樣是透過暫存器

原理相

畢竟晶片積體電路也是電

而且是數字電

暫時不深

為了讓某一

Pi

n

輸出電

或者使能一個通

我們可以

0

1

但是程式碼終歸是代

不是魔

為了使需求生

微控制器將每一個需要控制的

賦予一個

地址

在電路層面上實現相關的功能綁

使用者只需透過給這個地址去寫一個

就相當於控制了需要控制的東

西

易於計

我們

3

2

位處理器最大可以尋

4G

B

的記憶體空

並不是每一個地址都是有真實物理地址對應

換而言

一個地址可能指向的是真實的內

也可能並不是真實存在的內

不過訪問這個地址相當於控制了被控量一

此時該地址可以看作控制量的一個

handler)

使能

的意思是

enable

反義詞

禁用

disable

)”。

計算機相關的詞彙總是這麼的看不懂字面意

看英語就明白

記憶體是儲存資料的地

任何電子資訊資料都可以看

0

1

。 3

2

位機的最小儲存單元

DWORD

, Double Word),

3

2

位元

。 3

2

位機的記憶體單元可以看作一個個存

3

2

位元位的小倉

為了找到所需要的數

需要給這些倉庫編

這個所謂的編號就是

地址

(Address)。

儲存的值是地址值的變數叫做

pointer)

比如一個倉庫放著一個記錄某個貨物的多個存放倉庫的編

那麼這個記憶體裡的資料就是一個指

計算機無法分辨哪寫是指

哪些是數

這需要人去完

庫函式是暫存器的封裝

不管

S

T

HA

L

硬體抽象層

還是標準

STL,

都是封裝而

本質上是宏定義替換和一些函

宏定義在編

(compile

階段完

不佔用寶貴的儲存空

函式本身會被編譯為代

所以會佔用空

儘量避免在庫中使用函

當然使用者程式碼肯定無所

為什麼要用庫函式

比如

GPIO_InitTypeDef GPIO_Initure;

GPIO_Initure。Pin=GPIO_PIN_13;

GPIO_Initure。Mode=GPIO_MODE_OUTPUT_PP;

HAL_GPIO_Init(GPIOB,&GPIO_Initure);

HAL_GPIO_WritePin(GPIOB, GPIO_PIN_13, GPIO_PIN_SET);

這一段代

不加註釋你也知道他的意思是初始

PB1

3

為推輓輸

但是如果寫成了暫存器版本的你可能就看不懂

GPIOB->CRH&=0XFF0FFFFF;

GPIOB->CRH|=0X00300000;

GPIOB->ODR|=1<

現在會簡要介紹暫存器的配置方

你能搜到這個文章肯定是因為

你找見的開原始碼使用的是暫存器版

正常人的

能找見庫函式絕對是不會用暫存器版本的程式碼

暫存器版本的程式碼實際上也是封

比如上述程式碼實際上也可以直接寫成

uint32_t *)(((((uint32_t)0x40000000) + 0x10000) + 0x0C00)+0x01)&=0XFF0FFFFF;

uint32_t *)(((((uint32_t)0x40000000) + 0x10000) + 0x0C00)+0x01)|=0X00300000;

uint32_t *)(((((uint32_t)0x40000000) + 0x10000) + 0x0C00)+0x04)|=1<

/

/

這一段程式碼可能不對

上面就是直接透過指標操作暫存器的方

暫存器宏定義和結構體相當

先找

GPIO

B

的開頭地

然後加上結構體的附加地址偏

去操作某一個功

CR

H

寄存

)。

最後給這個地址寫入一串二進位制值去控制相應的東

西

說到底還是去給某些位元上寫高低電

類似

GPIOB->CR

H

實際上是定義的一個結構

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_TypeDef;

透過結構體的位關係以及各種宏定義替

可以實現地址的編譯時自動確

暫存器的封裝好在對於實際記憶體位置不同的芯

移植只需更改暫存器宏定義對映即

而且已經很容易讀

4。

常見的暫存器配置初始

使

STM32F1x

x

系列舉例

我們寫驅

最常用的就

GPI

O

的配置

我們就舉一些例

RC

C

暫存器

RC

C

暫存器一般用來配

RC

C

時鐘相關的代

比如我要使

PA

0

我必須開

P

A

的時鐘通道才

時鐘相當於處理器的心

沒有心跳當然是死

但是沒理由的開啟時鐘會帶來額外

EM

I

干擾和整機功

原理暫且不

實際危害就是干擾大

耗電多

尤其針對於一些對功耗要求嚴格的場

例如手環手

應該儘可能在待機時關閉能關閉的所有時

用來省

比如有的智慧手錶升級系統後待機變久

可能就是這個原

我們說的

firmware)

升級實際上指的就是把新寫的程式碼下載到了老裝置

)。

舉個例

, RC

C

時鐘的配置如下

RCC->APB2ENR|=1<

/

使

PORT

C

時鐘

RCC->APB2ENR|=1<

/

使

PORT

B

時鐘

RCC->APB2ENR|=1<

/

使

PORT

A

時鐘

具體使能哪一個位是控制

需要查

除非你背過

)。

如果是需要快速出項

不管其他

可以省

直接使能所有時

但是不建議這麼