本文介紹了:外設寄存器查找 ① 名稱 ② 偏移地址 ③ 寄存器位表 ④ 位功能說明 寄存器基本操作 C語言的置位和清零 具體方法 設置GPIO流程 給寄存器賦值 帶參數巨集 STM32F1xx 晶元識別 存儲器映射 寄存器映射 讓GPIOB埠的16個引腳輸出高電平,要怎麼實現? STM32寄存器映射... ...
目錄2024年7月18日 發佈於博客園, 本文涉及到STM32F4XX和STM32F1XX系列
外設寄存器查找
見<<野火STM32庫開髮指南P64>>
在XX 外設的地址範圍內,分佈著的就是該外設的寄存器。以GPIO 外設為例,GPIO 是通用輸入輸出埠的簡稱,簡單來說就是STM32 可控制的引腳,基本功能是控制引腳輸出高電平或者低電平。最簡單的應用就是把GPIO 的引腳連接到LED 燈的陰極,LED 燈的陽極接電源,然後通過STM32 控制該引腳的電平,從而實現控制LED 燈的亮滅。
GPIO 有很多個寄存器,每一個都有特定的功能。每個寄存器為32bit,占四個位元組,在該外設的基地址上按照順序排列,寄存器的位置都以相對該外設基地址的偏移地址來描述。
① 名稱
寄存器說明中首先列出了該寄存器中的名稱,“(GPIOx_BSRR)(x=A⋯I)”
這段的意思是該寄存器名為“GPIOx_BSRR”其中的“x”可以為A-I,也就是說這個寄存器說明適用於GPIOA、GPIOB 至GPIOI,這些GPIO 埠都有這樣的一個寄存器。
② 偏移地址
偏移地址是指本寄存器相對於這個外設的基地址的偏移。本寄存器的偏移地址是0x18,從參考手冊中我們可以查到GPIOA 外設的基地址為0x4002 0000 ,我們就可以算出GPIOA 的這個GPIOA_BSRR 寄存器的地址為:0x4002 0000+0x18;同理,由於GPIOB 的外設基地址
為0x4002 0400,可算出GPIOB_BSRR 寄存器的地址為:0x4002 0400+0x18 。其他GPIO 埠以此類推即可。
③ 寄存器位表
緊接著的是本寄存器的位表,表中列出它的0-31 位的名稱及許可權。表上方的數字為位編號,中間為位名稱,最下方為讀寫許可權,其中w 表示只寫,r 表示只讀,rw 表示可讀寫。本寄存器中的位許可權都是w,所以只能寫,如果讀本寄存器,是無法保證讀取到它真正內容的。而有的寄存器位只讀,一般是用於表示STM32 外設的某種工作狀態的,由STM32 硬體自動更改,程式通過讀取那些寄存器位來判斷外設的工作狀態。
④ 位功能說明
位功能是寄存器說明中最重要的部分,它詳細介紹了寄存器每一個位的功能。例如本寄存器中有兩種寄存器位,分別為BRy 及BSy,其中的y 數值可以是0-15,這裡的0-15 表示埠的引腳號,如BR0、BS0 用於控制GPIOx 的第0 個引腳,若x 表示GPIOA,那就是控制GPIOA 的第0 引腳,而BR1、BS1 就是控制GPIOA 第1 個引腳。
其中BRy 引腳的說明是“0:不會對相應的ODRx 位執行任何操作;1:對相應ODRx 位進行複位”。
這裡的“複位”是將該位設置為0 的意思,而“置位”表示將該位設置為1;說明中的ODRx 是另一個寄存器的寄存器位,我們只需要知道ODRx 位為1 的時候,對應的引腳x 輸出高電平,為0 的時候對應的引腳輸出低電平即可(感興趣的讀者可以查詢該寄存器GPIOx_ODR 的說明瞭解)。所以,如果對BR0 寫入“1”的話,那麼GPIOx 的第0 個引腳就會輸出“低電平”,但是對BR0 寫入“0”的話,卻不會影響ODR0 位,所以引腳電平不會改變。要想該引腳輸出“高電平”,就需要對“BS0”位寫入“1”,寄存器位BSy 與BRy 是相反的操作。
具體查對應的手冊和系統庫函數的封裝, 註意, 要看數據手冊, 查找對應的資源才能知道具體的位置. 重點是掌握方法.
寄存器基本操作
C語言的置位和清零
若直接賦值0或1, 會將所有位都變為0或1
例如:*(unsigned int*)(0x40010C0C)=1;
或 *(unsigned int*)(0x40010C0C)=0;
任何數&1,值不變 任何數&0,均為0
故而只對目標位操作
//清零 &=~
//置位 |=
具體方法
/*對某寄存器某些位清零*/
// 配置IO口為輸出
GPIOB_CRL &= ~( (0x0f) << (4*0) );//對寄存器清零. 0x0f是0000 1111, 因為要取反, 要把哪一位清零就用1去清零
/*對某寄存器某1位置位*/
//寫入值
GPIOB_CRL |= ( (1) << (4*0) );
/*對某寄存器某1位置零*/
// 控制 ODR 寄存器
GPIOB_ODR &= ~(1<<0);
/* 置位操作 */
a|=(1<<?);
/* 清零操作 */
a&=~(1<<?);
設置GPIO流程
打開GPIOB埠的時鐘-->配置埠配置低寄存器的埠模式和速率-->控制ODR寄存器的高低電平
//打開GPIOB埠的時鐘(看2.3和第六章6.3.7)
//RCC外設地址0x4002 1000位於AHB匯流排, 該外設下的APB2寄存器,APB2外設時鐘使能寄存器偏移地址為0x18
//讓該寄存器的對應IOPB埠時鐘開啟, 位於該寄存器的第3位, 即左移3位
*(unsigned int *)0x40021018 |= ((1)<<3);
//配置IO口位輸出
//配置8.2.1埠配置低寄存器的埠模式和速率
//因為4個為1組,所以控制PB0則為第0組,需要左移0組
*(unsigned int *)0x40010C00 |= ((1)<<(4*0));
//控制ODR寄存器的高低電平,此處為置零
*(unsigned int *)0x40010C0C &= ~((1)<<0);
給寄存器賦值
方法1:巨集定義直接賦值
/*通過絕對地址訪問記憶體單元*/
//GPIOB的埠全部輸出為高電平
*(unsigned int*)(0x40010C0C)=0xFFFF;
/*
(0x40010C0C)是GPIOB輸出數據寄存器ODR的地址, 是一個立即數
(unsigned int*)(0x40010C0C) 將立即數強制類型轉換為指針, 指向地址
*(unsigned int*)(0x40010C0C) 對該地址所指向的區域進行操作
*(unsigned int*)(0x40010C0C)=0xFFFF; 從該地址開始往後數32位,均為1(十六進位0xffff)
*/
/*通過巨集定義,寄存器別名的方式訪問記憶體單元*/
// GPIOB 埠全部輸出 高電平
#define GPIOB_ODR (unsignedint*)(0x40010C0C)//給該地址定義個巨集
* GPIOB_ODR = 0xFF;//對巨集進行操作
/*為了方便操作,我們乾脆把指針操作“*”也定義到寄存器別名裡面*/
// GPIOB 埠全部輸出 高電平
#define GPIOB_ODR *(unsignedint*)(0x40010C0C)
GPIOB_ODR = 0xFF;
方法2: 採用移位的方式
* (unsigned int * )0x40010c0c &=~(1<<0);
#define PERIPH_BASE ((unsigned int)0x40000000)
#define APB2PERIPH_BASE (PERIPH_BASE + 0x00010000)
#define GPIOB_BASE (APB2PERIPH_BASE + 0x0C00)
#define GPIOB_ODR *(unsignedint*)(GPIOB_BASE+0x0C)//ODR的絕對地址
// PB0輸出輸出低電平(清零) 將目標位清零 &= ~(1<<目標位)
GPIOB_ODR &= ~(1<<0);
/*
等效於 GPIOB_ODR = GPIOB_ODR & ~(1<<0);
根據優先順序, 先計算括弧內(1<<0)
0001<<0--->0001
取反~
~0001--->1110
計算按位與
GPIOB_ODR & 1110 若原來值為1000
則 1000 & 1110
得 1000
*/
// PB0輸出輸出高電平 將目標位拉高 |= ~(1<<目標位)
GPIOB_ODR |= (1<<0);
按位邏輯運算符: A &= B
等效於 A = A&B
移位運算符:
左移 1<<2
- 把十進位數1轉換為二進位 0000 0001
- 整體左移2位 0000 0100 , 左側移除的丟失, 空位補0
右移需要區分符號位, 結果看機器 (1000 1010)>>2
- //轉換為二進位
- 無符號位 整體右移2位 0010 0010 , 右側移除的丟失, 空位補0
- 有符號位 整體右移2位 0010 0010或1110 0010 , 右側移除的丟失, 空位補符號位
或不用巨集定義
* (unsigned int *)0x40010C0C &=~(1<<0);//將第一位置零
帶參數巨集
#define ON 1 //代參數巨集
#define OFF 0
// \ C語言裡面叫續行符,後面不能有任何的東西
//帶參巨集的主體
#define LED_G(a) if(a) \
GPIO_ResetBits(LED_G_GPIO_PORT, LED_G_GPIO_PIN); \
else GPIO_SetBits(LED_G_GPIO_PORT, LED_G_GPIO_PIN);
void LED_GPIO_Config(void);
STM32F1xx
寄存器 需要反覆看書籍第六章
晶元識別
1、如何看晶元絲印
2、懂得如何辨別正方向
若有2個點,則看小的點,逆時針。
若沒有點,那麼正看絲印,左上方為第一腳,逆時針數。
STM32晶元架構簡圖
flash存儲程式,SRAM存儲變數
IP廠商(內核廠商):ARM
SOC廠商(晶元廠商):ST
STM32F10xx系統框圖
在參考手冊的存儲架構章節中。
驅動單元(CPU,內核):ARM公司設計
被動單元:外設:ST公司設計
ICode 中的I 表示Instruction,即指令。我們寫好的程式編譯之後都是一條條指令,存放在FLASH中,內核要讀取這些指令來執行程式就必須通過ICode 匯流排,它幾乎每時每刻都需要被使用,它是專門用來取指的。
DCode 匯流排:DCode 中的D 表示Data,即數據,那說明這條匯流排是用來取數的。我們在寫程式的時候,數據有常量和變數兩種,常量就是固定不變的,用C 語言中的const 關鍵字修飾,是放到內部的FLASH當中的,變數是可變的,不管是全局變數還是局部變數都放在內部的SRAM。因為數據可以被Dcode 匯流排和DMA 匯流排訪問,所以為了避免訪問衝突,在取數的時候需要經過一個匯流排矩陣來仲裁,決定哪個匯流排在取數。
系統匯流排system 系統匯流排主要是訪問外設的寄存器,我們通常說的寄存器編程,即讀寫寄存器都是通過這根系統匯流排來完成的。
DMA 匯流排:DMA 匯流排也主要是用來傳輸數據,這個數據可以是在某個外設的數據寄存器,可以在SRAM,可以在內部的FLASH。因為數據可以被Dcode 匯流排和DMA 匯流排訪問,所以為了避免訪問衝突,在取數的時候需要經過一個匯流排矩陣來仲裁,決定哪個匯流排在取數
APB1低速匯流排,APB2高速匯流排
存儲器映射
存儲器本身不具有地址信息,它的地址是由晶元廠商或用戶分配,給存儲器分配地址的過程就稱為存儲器映射,具體見圖存儲器映射。如果給存儲器再分配一個地址就叫存儲器重映射。
2的32次方是4G。
圖在Datasheet search site的Memory mapping章節
重點學習Block2
寄存器映射
看STM32參考手冊中文版第八章
地址偏移是相對於埠基地址偏移,見第二章存儲器組織。
讓GPIOB埠的16個引腳輸出高電平,要怎麼實現?
/*通過絕對地址訪問記憶體單元*/
//GPIOB的埠全部輸出為高電平
*(unsigned int*)(0x40010C0C)=0xFFFF;
/*
(0x40010C0C)是GPIOB輸出數據寄存器ODR的地址, 是一個立即數
(unsigned int*)(0x40010C0C) 將立即數強制類型轉換為指針, 指向地址
*(unsigned int*)(0x40010C0C) 對該地址所指向的區域進行操作
*(unsigned int*)(0x40010C0C)=0xFFFF; 從該地址開始往後數32位,均為1(十六進位0xffff)
*/
/*通過巨集定義,寄存器別名的方式訪問記憶體單元*/
// GPIOB 埠全部輸出 高電平
#define GPIOB_ODR (unsignedint*)(0x40010C0C)//給該地址定義個巨集
* GPIOB_ODR = 0xFF;//對巨集進行操作
/*為了方便操作,我們乾脆把指針操作“*”也定義到寄存器別名裡面*/
// GPIOB 埠全部輸出 高電平
#define GPIOB_ODR *(unsignedint*)(0x40010C0C)
GPIOB_ODR = 0xFF;
什麼是寄存器?
給有特定功能的記憶體單元取一個別名,這個別名就是我們經常說的寄存器,這個給已經分配好地址的有特定功能的記憶體單元取別名的過程就叫寄存器映射。
什麼叫存儲器映射?
給存儲器分配地址的過程叫存儲器映射,再分配一個地址叫重映射。
STM32寄存器映射
如何計算?以GPIOB為例
GPIOB的外設基地址為:0x4001 0C00
因為埠輸入數據寄存器(GPIOx_IDR) 地址偏移為:0x08
所以(GPIOx_IDR) 的絕對地址為 0x4001 0C08
所以我們只需要+4就能得到下一個寄存器的地址, 一個位元組有8位, 4個位元組即32位
CRL 埠配置低寄存器
CRH 埠配置高寄存器
IDR 輸入數據寄存器
ODR 數據輸出寄存器
BSRR和BRR是配置某埠的某一個位的
LCKR 鎖定寄存器
重點看第85頁
C語言對寄存器的封裝
在編程上為了方便理解和記憶,我們把匯流排基地址和外設基地址都以相應的巨集定義起來,匯流排或
者外設都以他們的名字作為巨集名
代碼清單: 寄存器-4 匯流排和外設基址巨集定義
/* 外設基地址*/
#define PERIPH_BASE ((unsigned int)0x40000000)
/* 匯流排基地址*/
#define APB1PERIPH_BASE PERIPH_BASE
#define APB2PERIPH_BASE (PERIPH_BASE + 0x00010000)
#define AHBPERIPH_BASE (PERIPH_BASE + 0x00020000)
/* 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)
/* 寄存器基地址,以GPIOB 為例*/
#define GPIOB_CRL (GPIOB_BASE+0x00)
#define GPIOB_CRH (GPIOB_BASE+0x04)
#define GPIOB_IDR (GPIOB_BASE+0x08)
#define GPIOB_ODR (GPIOB_BASE+0x0C)
#define GPIOB_BSRR (GPIOB_BASE+0x10)
#define GPIOB_BRR (GPIOB_BASE+0x14)
#define GPIOB_LCKR (GPIOB_BASE+0x18)
首先定義了“片上外設”基地址PERIPH_BASE,接著在PERIPH_BASE 上加入各個匯流排的地址偏移,得到APB1、APB2 匯流排的地址APB1PERIPH_BASE、APB2PERIPH_BASE,在其之上加入外設地址的偏移,得到GPIOA-G 的外設地址,最後在外設地址上加入各寄存器的地址偏移,得到特定寄存器的地址。一旦有了具體地址,就可以用指針讀寫
重點看64頁
讓PB0輸出低/高電平,要怎麼實現?
#define PERIPH_BASE ((unsigned int)0x40000000)
#define APB2PERIPH_BASE (PERIPH_BASE + 0x00010000)
#define GPIOB_BASE (APB2PERIPH_BASE + 0x0C00)
#define GPIOB_ODR *(unsignedint*)(GPIOB_BASE+0x0C)//ODR的絕對地址
// PB0輸出輸出低電平(清零)
GPIOB_ODR &= ~(1<<0);//(1<<0)將1左移0位 0000 0000變成0000 0001,別的為0
/*
先計算左邊 0000 0001 取反後 1111 1110
再與原來的值0000 1000 按位與
結果 0000 0000
*/
// PB0輸出輸出高電平
GPIOB_ODR |= (1<<0);
/*
|=是相加的意思
原來0000 1000
相加0000 0001 先計算出1<<0的結果,再位相加
結果0000 1001
*/
使用結構體封裝寄存器列表?
-> 和結構體的. 的作用是類似的, 都是對結構體成員的訪問, 可以見C結構體
這部分工作都由固件庫幫我們完成了
新建寄存器(REG)模板
創建工程
工程命名--不要中文
選擇對應晶元型號
跳過軟體包
導入啟動文件,文件路徑,在固件庫中:
C:\MYDATA~1\網課資料\野火指~1\A盤(~1\1-程式~1\1-_野~1\1-書籍~1\0【固~1.0\STM32F~1.0\【固件~1.0\LIBRAR~1\CMSIS\CM3\DEVICE~1\ST\STM32F~1\startup\arm\
根據Flash大小選擇對應啟動文件:
拷貝啟動文件到工程項目文件夾中
雙擊導入文件
記事本新建一個main.c文件並導入
設置代碼大小
#include "stm32f10x.h"//<>是表示頭文件在軟體安裝根目錄下, ""表示先在工程目錄下尋找,再去軟體安裝根目錄尋找
int main (void)
{
}
void SystemInit(void)
{
//編譯時, 會先在啟動文件的彙編中初始化時鐘, 採用的插入方式. 函數體為空,目的是為了騙過編譯器不報錯
}
修改名稱
間斷雙擊target,重命名
修改生成的目標工程文件名稱,存放在項目的objects文件夾中。
hex是通過串口下載的可執行文件,
axf是通過編譯器下載的可執行文件
**燒錄設置 ** 連接模擬器和單片機 上電
然後編譯後,選擇download下載
這個是可以上相容的,即在同一內核版本下,F103,引腳少的工程項目在引腳多的晶元上可以直接燒錄,無需修改。因為引腳少的晶元的所有引腳在引腳多的晶元上都有。
移植工程,需要修改晶元型號,然後重新配置模擬器:
關閉C語言語法的動態檢查:
本文來自博客園,作者:舟清颺,轉載請註明原文鏈接:https://www.cnblogs.com/zqingyang/p/18308826