之前我從宏觀上介紹了CM3架構和STM32的相關內容,現在我們就開始正式進行STM32的靈魂塑造---程序編寫。爲什麼說是塑造,因爲整個代碼是從無到有的而非利用官方庫的開發,就好像新生的嬰兒呱呱墜地。。。羅嗦止於此,切入正題!
在我的一篇文章《ARM寄存器C操作探討》裏,我曾詳細的介紹了ARM通常採用的兩種寄存器C操作的方式,現在我們就實際應用一下第二種方法。爲了幫初學者形成ARM編程的一般思路和思想,我會從解讀Datasheet到實際編程都說明的非常詳細。
首先,我們要讀Datasheet,這裏我選用的芯片是STM32F103RB,大家可以到ST的官網尋找下載,讀Datasheet讀什麼?就像物質是由原子組成,程序是由寄存器的配置組成,尤其對ARM來說,靈活的配置寄存器是基礎中的基礎,說來容易,可至此還是有種無從下手的感覺,下面我就從寄存器操作開始講講構建自己的ARM編程模式。
打開KEIL,新建一個工程,device選擇STM32F103RB,詳細的我這裏不再展開,相信大家在51或其它微處理器的學習中都用的熟的不能再熟,不贅述,直奔核心---寄存器的操作。要操作一個寄存器我們得知道這個寄存器的地址,如何尋址?這就是重點。我們需要查看Datasheet來確定STM32F103RB這個芯片或者說STM32F103系列芯片的寄存器映射。依據這個內存映射,我們可以找到芯片外設的首地址,而這正是我們想要的、需要的。那麼具體怎麼寫?我們新建一個名爲MYstm32f10x_map.h的文件,內容可以這麼寫:
#ifndef MYstm32f10x_map_h_h_
#define MYstm32f10x_map_h_h_
//數據類型定義
typedef unsigned int uint32;
typedef unsigned short int uint16;
//####################################################################################
//****RCC REGS******//
//####################################################################################
typedef struct tagRCC_REGS{
uint32 CR; //時鐘控制寄存器
uint32 CFGR; //時鐘配置寄存器
uint32 CIR; //時鐘中斷寄存器
uint32 APB2RSTR; //APB2外設復位寄存器
uint32 APB1RSTR; //APB1外設復位寄存器
uint32 AHBENR; //AHB外設時鐘使能寄存器
uint32 APB2ENR; //APB2外設時鐘使能寄存器
uint32 APB1ENR; //APB1外設時鐘使能寄存器
uint32 BDCR; //備份域控制寄存器
uint32 CSR; //控制/狀態寄存器
}RCC_REGS;
#define RCC ((volatile RCC_REGS *)0x40021000) //RCC寄存器組首地址
//####################################################################################
//****GPIO REGS****//
//####################################################################################
typedef struct tagGPI0X_REGS{ //X=A\B\C
uint32 CRL; //端口配置低寄存器
uint32 CRH; //端口配置高寄存器
uint32 IDR; //端口輸入數據寄存器
uint32 ODR; //端口輸出數據寄存器
uint32 BSRR; //端口位設置/清除寄存器
uint32 BRR; //端口位清除寄存器
uint32 LCKR; //端口配置鎖定寄存器
}GPIOX_REGS;
#define GPIOA ((volatile GPIOX_REGS *)0x40010800) //端口A寄存器組首地址
#define GPIOB ((volatile GPIOX_REGS *)0x40010C00) //端口B寄存器組首地址
#define GPIOC ((volatile GPIOX_REGS *)0x40011000) //端口C寄存器組首地址
//####################################################################################
//*****TIME REGS******//
//####################################################################################
typedef struct tagADVANCED_TIMX_REGS{ //高級控制定時器(X)寄存器組
uint32 CR1; //控制寄存器1
uint32 CR2; //控制寄存器2
uint32 SMCR; //從模式控制寄存器
uint32 DIER; //中斷使能寄存器
uint32 SR; //狀態寄存器
uint32 EGR; //事件產生寄存器
uint32 CCMR1; //捕獲/比較模式寄存器1
uint32 CCMR2; //捕獲/比較模式寄存器2
uint32 CCER; //捕獲/比較使能寄存器
uint32 CNT; //計數器
uint32 PSC; //預分頻器
uint32 ARR; //自動裝載寄存器
uint32 RCR; //重複計數寄存器
uint32 CCR1; //捕獲/比較寄存器1
uint32 CCR2; //捕獲/比較寄存器2
uint32 CCR3; //捕獲/比較寄存器3
uint32 CCR4; //捕獲/比較寄存器4
uint32 BDTR; //剎車和死區寄存器
uint32 DCR; //DMA控制寄存器
uint32 DMAR; //連續模式的DMA地址
}ADVANCED_TIMX_REGS;
typedef struct tagGENERAL_TIMX_REGS{ //X=2-4
uint32 CR1; //控制寄存器1
uint32 CR2; //控制寄存器2
uint32 SMCR; //從模式控制寄存器
uint32 DIER; //DMA/中斷使能寄存器
uint32 SR; //狀態寄存器
uint32 EGR; //事件產生寄存器
uint32 CCMR1; //捕獲/比較模式寄存器1
uint32 CCMR2; //捕獲/比較模式寄存器2
uint32 CCER; //捕獲/比較使能寄存器
uint32 CNT; //計數器
uint32 PSC; //預分頻器
uint32 ARR; //自動裝載寄存器
uint32 CCR1; //捕獲/比較寄存器1
uint32 CCR2; //捕獲/比較寄存器2
uint32 CCR3; //捕獲/比較寄存器3
uint32 CCR4; //捕獲/比較寄存器4
uint32 DCR; //DMA控制寄存器
uint32 DMAR; //連續模式的DMA地址
}GENERAL_TIMX_REGS;
#define TIM1 ((volatile ADVANCED_TIMX_REGS *) 0x40012C00) //高級控制定時器1寄存器組首地址
#define TIM2 ((volatile GENERAL_TIMX_REGS *) 0x40000000) //通用控制定時器2寄存器組首地址
#define TIM3 ((volatile GENERAL_TIMX_REGS *) 0x40000400) //通用控制定時器3寄存器組首地址
#define TIM4 ((volatile GENERAL_TIMX_REGS *) 0x40000800) //通用控制定時器4寄存器組首地址
//####################################################################################
//*****ADC REGS******//
//####################################################################################
typedef struct tagADCX_REGS{
uint32 SR; //ADC狀態寄存器
uint32 CR1; //ADC控制寄存器1
uint32 CR2; //ADC控制寄存器2
uint32 SMPR1; //ADC採樣時間寄存器1
uint32 SMPR2; //ADC採樣時間寄存器2
uint32 JOFR1; //ADC注入通道數據偏移寄存器1
uint32 JOFR2; //ADC注入通道數據偏移寄存器2
uint32 JOFR3; //ADC注入通道數據偏移寄存器3
uint32 JOFR4; //ADC注入通道數據偏移寄存器4
uint32 HTR; //ADC看門狗高閥值寄存器
uint32 LTR; //ADC看門狗低閥值寄存器
uint32 SQR1; //ADC規則序列寄存器1
uint32 SQR2; //ADC規則序列寄存器2
uint32 SQR3; //ADC規則序列寄存器3
uint32 JSQR; //ADC注入序列寄存器
uint32 JDR1; //ADC 注入數據寄存器1
uint32 JDR2; //ADC 注入數據寄存器2
uint32 JDR3; //ADC 注入數據寄存器3
uint32 JDR4; //ADC 注入數據寄存器4
uint32 DR; //ADC規則數據寄存器
}ADCX_REGS;
#define ADC1 ((volatile ADCX_REGS *) 0x40012400) //ADC1寄存器組首地址
#define ADC2 ((volatile ADCX_REGS *) 0x40012800) //ADC2寄存器組首地址
//等等。。。。。。
#endif
如上,然後我們就可以在.c文件裏包含這個頭文件,並根據技術手冊裏的各外設的寄存器描述來讀寫相應的寄存器,完成想要實現的功能。
×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××
注意:
可能你在編譯過程中會報錯,大體意思是說你沒有定義某結構體,可我們明明定義了,我想這可能是KEIL的一個BUG(當然也有可能是盜版的緣故~),經過幾次猜想驗證之後,我發現在typedef struct...這個結構體定義時,在最後的結構體名一定要是你一個字一個字敲進去的,而不是從tagXXX中拷貝而來的,如果是從tagXXX拷貝而來的話就會報上述的錯誤(真是什麼奇怪的事都可能存在~)。
×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××
歡迎大家掃描下方二維碼關注我的個人微信公衆號,一起交流學習,謝謝。