想走嵌入式系統開發這行必經的路,直接了解最底層怎麼運作的,Arduino底層也是這樣運作的,只是Arduino把這些操作都包起來寫成一個函式給你使用,叫出那函式輸入對的值就能直接使用。
我在DAY3,DAY4的時候有先教學如何安裝環境,在第四天的最後先創了兩個空檔(main.c、stm32f103.h),還有從標準庫複製了1個.s檔,這個.s檔是用組合語言所寫的,MCU一上電第1個執行的地方,是1個啟動的檔案。
首先,先打stm32f103.h,這裡面是要配置暫存器位置,如下圖
stm32f103.h 程式碼:
//外設基本地址,在datasheet的Memory map(內存映射)裡看的到
#define PERIPA_BASE ((unsigned int)0x40000000)
//基本地址加上20000是因為所有GPIO口在這地址上,( APB2PERIPH_BASE=0x4000 0000+0x0001 0000=0x4001 0000)
#define APB2PERIPH_BASE (PERIPA_BASE + 0x00010000)
//GPIOA這個IO口的地址,"A"的,專屬於A的!!
#define GPIOA_BASE (APB2PERIPH_BASE+0x0800)
//這一部份是Reference manual(參考手冊)上的GPIO章節所介紹的所有能配置功能的暫存器
//全部基本位置加上偏移位置就到單1那個GPIO口的實際位置了
#define GPIOA_CRL *(unsigned int*)(GPIOA_BASE+0x00)
#define GPIOA_CRH *(unsigned int*)(GPIOA_BASE+0x04)
#define GPIOA_IDR *(unsigned int*)(GPIOA_BASE+0x08)
#define GPIOA_ODR *(unsigned int*)(GPIOA_BASE+0x0C)
#define GPIOA_BSRR *(unsigned int*)(GPIOA_BASE+0x10)
#define GPIOA_BRR *(unsigned int*)(GPIOA_BASE+0x14)
#define GPIOA_LCKR *(unsigned int*)(GPIOA_BASE+0x18)
//RCC外設基本地址
#define RCC_BASE (0x40021000)
//RCC_控制GPIO口時鐘的實際地址
#define RCC_APB2ENR *(unsigned int*)(RCC_BASE+0x18)
逐行解釋,#define是C語言的定義,我打左邊的暫存器名稱會等於右邊的指向地址
#define PERIPA_BASE,這是指我總線的地址,先來看看Reference manual裡提供的記憶體的地圖:
看到上土左半邊的位置範圍,對上右邊每個位置的名稱,這樣就可以查詢你要的功能在哪個範圍,將偏移位置加上去,來舉#define GPIOA_BSRR為例:
先開Reference manua找對應的暫存器位置說明:
看到左半邊的目錄位置,點選BSRR後會跳進來,在看右邊上方有個Address offser:0x10再去看看我的程式碼,這就是實際的位置啦~~
同理再來設置RCC時鐘,有時中GPIO口才會開始工作,可以先看我上上個記憶體地圖,我有框起來RCC的基本位置從0x4002 1000 開始
再來是實際的位置,加上0x18
這樣都配置完地址了~~,這邊要花時間自己看一下,看懂就會發現其實這原理很簡單,但查位置真的挺花時間的。
再來就是要去main.c裡去賦予值給這些暫存器瞜,首先天上我的程式碼:
//main.c
#include "stm32f103.h"
int main(void)
{
int i=0;
RCC_APB2ENR |= (1<<2);//開GPIOA的時鐘
GPIOA_CRL &= ~( 0xf<< (4)); //對要使用位置清0
GPIOA_CRL |= ( 0x1<< (4)); //賦予值
while(1)
{
GPIOA_BSRR |= ( 1<< 1); //GPIOA Pin1_Hi,開發版上的A1腳位
for(i=0;i<=480000;i++)
{
}
i=0;
GPIOA_BSRR |= ~( 1<< 1);//GPIOA Pin1_LOW,開發版上的A1腳位
for(i=0;i<=480000;i++)
{
}
}
}
//打這個函式是為了讓編譯器不報錯,實際的原因是.s啟動檔那有宣告執行這函式
//,所以讓他實現空空的的函示就好
void SystemInit(void)
{
}
我這邊就當作大家都懂c語言跟基本的數位邏輯觀念了,不懂的話再去搜尋一下吧。
首先RCC開啟時鐘的部份
可以看到上圖我紅框部份,再去對應我main.c的開啟時鐘看,到這邊看懂的話你就會操作了~
RCC_APB2ENR |= (1<<2); 講解一下這行,1左移兩位(1 => 100)或上RCC_APB2ENR這個暫存器。就這樣,這個不會的話要去看一下基本數位邏輯喔
下面程式我就不解釋,一樣的道理,去查看CRL和BSRR暫存器就可以啦,CRL主要是在配置IO口模式,我用簡單計數來當delay,BSRR一邊控制HI,一邊控制LOW,完成啦
驗證過了可以動作~,這套查詢的方式套用到哪顆MCU都可以!,方法都一樣
講一下燒錄的方式
確定編譯沒錯才能燒錄喔,燒入後記得按Reset,把LED接到 A1 的腳位就會看到LED在閃爍了喔~
底層就是這樣動作了,有什麼問題可以在下面提出來~
我預計明天來講暫存器的查找方式吧