STM32F1xx 系統時鍾

來源:STM32F1中文參考手冊 6.2時鍾
時鍾的作用
決定了程序執行的速度,給芯片提供一個穩定的執行頻率
- STM32F103R8 最高速率是多少??
72 MHz maximum frequency
如果采用最高頻率:執行一條指令 1/72M s ==> 1/72us
精簡指令集:幾乎所有的指令都是消耗一個時鍾節拍(1/72 us)執行
R8的時鍾來源
-
高速外部時鍾信號(HSE) 4 – 16M 給系統時鍾提供時鍾信號
-
高速內部時鍾信號(HSI) 內部RC振盪器
-
低速外部時鍾信號(LSE) 32.768 給RTC實時時鍾提供時鍾信號
-
低速內部時鍾信號(LSI)
系統時鍾來源
-
HSI振盪器時鍾
-
HSE振盪器時鍾
-
PLL時鍾
R8的時鍾樹和外設分布

來自:STM32F103R8數據手冊 2.1 Device overview
STM32時鍾的配置
- PLL的倍頻因子:HSE * PLLMUL = 72M Hz
- AHB的頻率:72M Hz
- APB2的頻率:72M Hz
- APB1的頻率:36M Hz
修改HSE_VALUE
設置函數static void SetSysClockTo72(void)
中的值

GPIO的配置
GPIO的作用
GPIO通用的輸入輸出外設
數字接口:0/1
0 -- TTL電平:0v~1.5v
1 -- TTL電平:2.5v~5v
STM32F103C8 : 0 => 0v ± 0.1 1 => 3.3v ± 0.3v
STM32 中GPIO口如何表示(理解)
PA0 PA1 PA2..... PA15 ; PB0 .....
P port 端口
A B C.... 端口號
0 1 ..... 15 端口位
每個端口最多有16個端口位
PA1 端口A 的第1位
PA0 端口A 的第0位
GPIO的相關模式
- 輸入:4種
模擬輸入:輸入的模擬量,用於ADC轉化。
浮空輸入:輸入的數字量,
上拉輸入:輸入的數字量,向上驅動能力以及穩定信號。
下拉輸入:輸入的數字量,向下驅動能力以及穩定信號。
配置成那種模式要結合外部電路,不能導致IO口的狀態不確定。
比如說:外部是上拉,如果IO口配置成 “下拉模式”,就會導致當外部為高的時候IO口的電平不確定是高還是低,主要原因在於電阻的分壓大小。
-
輸出:4種
通用:GPIO口功能
復用:除了GPIO口功能
推挽:既能輸出低電平,又能輸出高電平
開漏:只能輸出低電平。 -- 用於總線通信,外部具有上拉功能。
通用推挽輸出、通用開漏輸出、復用推挽輸出、復用開漏輸出。

寄存器配置GPIO的模式
寄存器概念和作用
“寄存器” 是 “地址” 的別名。
操作寄存器就是操作地址。
在stm32f10x.h(宏定義)文件中,就定義了很多的地址
#define APB1PERIPH_BASE PERIPH_BASE
#define APB2PERIPH_BASE (PERIPH_BASE + 0x10000)
#define AHBPERIPH_BASE (PERIPH_BASE + 0x20000)
//三條總線 AHB APB2 APB1
---------------------------------------------------------
#define GPIOA_BASE (APB2PERIPH_BASE + 0x0800)
#define GPIOB_BASE (APB2PERIPH_BASE + 0x0C00)
#define GPIOC_BASE (APB2PERIPH_BASE + 0x1000)
//APB2PERIPH_BASE:說明GPIO是在APB2總線上的外設。
----------------------------------------------------------
typedef struct
{
__IO uint32_t CRL; //32 bit
__IO uint32_t CRH; //32 bit
__IO uint32_t IDR; //32 bit
__IO uint32_t ODR; //32 bit
__IO uint32_t BSRR; //32 bit
__IO uint32_t BRR; //32 bit
__IO uint32_t LCKR; //32 bit
} GPIO_TypeDef;
----------------------------------------------------------
#define GPIOA ((GPIO_TypeDef *) GPIOA_BASE)
#define GPIOB ((GPIO_TypeDef *) GPIOB_BASE)
#define GPIOC ((GPIO_TypeDef *) GPIOC_BASE)
//將一個地址,強制轉換為 GPIO_TypeDef 類型,
//就是將這一片空間按照 GPIO_TypeDef 分配,
//通過操作 結構體的成員 達到操作對應的地址的內容。
---------------------------------------------------------

上述的結構體,可以通過操作 “結構體中的成員” ,從而達到對 “地址內容” 的更改,也就是完成了對 “寄存器內容” 的修改。
配置GPIO輸入 / 輸出模式
配置 7 :0 位(低8位)

4bite 控制一個 “GPIO端口位 ” , 相關位,對應的功能如下圖所示。

配置 15 :8(高8位)

位 清零 與 置一
不影響其他位的值
位 清零: &= ~( );
位 置一: |= ( );
將想要 清零 或者 置一 的位 的值 對應為 1 ,帶入上面的方法中就行了
將GPIOB5配置為 “通用推挽輸出” 對應的4個位為 0011
先清零: GPIOB->CRL &= ~( 0xF << 20 ); //將4個位都清零 4*5=20。
后置位: GPIOB->CRL |= ( 0x3 << 20 ); //將對應的位,設置為所需的功能。
讀取輸入數據
出現在I/O腳上的數據在每個APB2時鍾被采樣到輸入數據寄存器
端口輸入數據寄存器(GPIOx_IDR) (x=A..E)

如何判斷固定的位的值??
& ( );
判斷 GOIOA5 的輸入數據是1/0
( GPIOA->IDR & (0x1<<5) ) == 0
就說明外部輸入的是0
( GPIOA->IDR & (0x1<<5) ) != 0
就說明外部輸入的是1
也就是說,&1 對應的值不改變,否則就會對應位被置零;
所以說,當外部輸入0,整體的就是0;外部輸入1,整體不為0;
也可以判斷多個位,將想要判斷的位 寫成1,
然后 (對象) & (對應位),判斷結果,就可以知道多個位的狀態。
輸出數據
開漏模式:輸出寄存器上的 ’0’ 激活N-MOS,而輸出寄存器上的 ’1’ 將端口置於高阻狀態(PMOS從不被激活)。
推挽模式:輸出寄存器上的 ’0’ 激活N-MOS,而輸出寄存器上的 ’1’ 將激活P-MOS。

想要控制某個位輸出 0/1 就在 寄存器對應的位寫 0/1。
輸出要求:不改變其他位的前提下,改變寄存器的值。
清零:&= ~( );
置一:|= ( );
函數代碼編寫
參考別人寫的代碼,一步步讓自己的代碼優秀起來。
//延時1us, 頻率:1/72us ; => 72個_NOP(); 就是1us
#define Delay_1us(){\
__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();\
__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();\
__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();\
__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();\
__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();\
__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();\
__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();\
__NOP();__NOP();\
}//沒有使用函數來定義,用宏定義,就是一個替代效果;盡可能讓1us延時更精准,后續的調用誤差會更小。
//延時n us , 函數調用,肯定有誤差,盡可能的減少
void Delay_us(uint32_t time)
{
while(time--)
Delay_1us();
}
//延時 n ms, 沒有調用 Delay_us(); 函數,就是為了減少函數調用和返回時消耗的時間。
void Delay_ms(uint32_t time)
{
uint64_t ms = time*1000;
while(ms--)
Delay_1us();
}
//Led_On:首字母大寫,其余小寫;1、能知道是宏定義; 2、用起來像是函數
#define Led_On(port, pin) (port->ODR &= ~(0x1<<pin))
#define Led_Off(port, pin) (port->ODR |= (0x1<<pin))
//LED0_PORT 全部大寫,就是這個量就只是一個宏定義的常量。
#define LED0_PORT GPIOB
#define LED0_PIN 5
#define LED1_PORT GPIOEc
#define LED1_PIN 5
......
int main(void)
{
....
Led_On(LED0_PORT, LED0_PIN); //一眼就能看出來是,打開LED0
Delay_ms(200);
Led_Off(LED0_PORT, LED0_PIN); //關閉LED0
Delay_ms(200);
....
}