1. delay文件
delay 文件夾內(nèi)包含了 delay.c 和 delay.h 兩個文件,這兩個文件用來實現(xiàn)系統(tǒng)的延時功能,其中包含 7 個函數(shù):
void delay_osschedlock(void);
void delay_osschedunlock(void);
void delay_ostimedly(u32 ticks);
void SysTick_Handler(void);
前面 4 個函數(shù),僅在支持操作系統(tǒng)(OS)的時候,需要用到,而后面三個函數(shù),則不論是否支持 OS 都需要用到。
void delay_init(void);
void delay_ms(u16 nms);
void delay_us(u32 nus);
void delay_init(void);
//初始化延遲函數(shù)
//當(dāng)使用 OS 的時候,此函數(shù)會初始化 OS 的時鐘節(jié)拍
//SYSTICK 的時鐘固定為 HCLK 時鐘的 1/8
//SYSCLK:系統(tǒng)時鐘
void delay_init(u8 SYSCLK)
{
#if SYSTEM_SUPPORT_OS //如果需要支持 OS.
u32 reload;
#endif
SysTick->CTRL&=~(1<<2); //SYSTICK 使用外部時鐘源
fac_us=SYSCLK/8; //不論是否使用 OS,fac_us 都需要使用
#if SYSTEM_SUPPORT_OS //如果需要支持 OS.
reload=SYSCLK/8; //每秒鐘的計數(shù)次數(shù) 單位為 K ,所以對于晶振是9MHZ時 syclk=72000
reload*=1000000/delay_ostickspersec; //根據(jù) delay_ostickspersec 設(shè)定溢出時間
//reload 為 24 位寄存器,最大值:16777216,在 72M 下,約合 1.86s 左右
fac_ms=1000/delay_ostickspersec; //代表 OS 可以延時的最少單位
SysTick->CTRL|=1<<1; //開啟 SYSTICK 中斷
SysTick->LOAD=reload; //每 1/delay_ostickspersec 秒中斷一次
SysTick->CTRL|=1<<0; //開啟 SYSTICK
#else
fac_ms=(u16)fac_us*1000; //非 OS 下,代表每個 ms 需要的 systick 時鐘數(shù)
#endif
}
2. sys文件
-
- 共有五個文件,其中sys.c和sys.h是由ALIENTEK提供,是重點介紹的部分。在 sys.h 里面定義了 STM32F1 的 IO 口輸入讀取宏定義和輸出宏定義。sys.c 里面定義了很多STM32F1 底層硬件很相關(guān)的設(shè)置函數(shù),包括系統(tǒng)時鐘的配置、IO 配置、中斷的配置等。下面我們將分別向大家介紹這兩個文件。
- IO口的位操作
- 共有五個文件,其中sys.c和sys.h是由ALIENTEK提供,是重點介紹的部分。在 sys.h 里面定義了 STM32F1 的 IO 口輸入讀取宏定義和輸出宏定義。sys.c 里面定義了很多STM32F1 底層硬件很相關(guān)的設(shè)置函數(shù),包括系統(tǒng)時鐘的配置、IO 配置、中斷的配置等。下面我們將分別向大家介紹這兩個文件。
例如
#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)
+(bitnum<<2))
#define MEM_ADDR(addr) *((volatile unsigned long *)(addr))
#define BIT_ADDR(addr, bitnum) MEM_ADDR(BITBAND(addr, bitnum))
//IO 口地址映射
#define GPIOA_ODR_Addr (GPIOA_BASE+12) //0x4001080C
#define GPIOB_ODR_Addr (GPIOB_BASE+12) //0x40010C0C
#define GPIOC_ODR_Addr (GPIOC_BASE+12) //0x4001100C
#define GPIOD_ODR_Addr (GPIOD_BASE+12) //0x4001140C
#define GPIOE_ODR_Addr (GPIOE_BASE+12) //0x4001180C
對應(yīng):
//IO 口操作,只對單一的 IO 口!
//確保 n 的值小于 16!
#define PAout(n) BIT_ADDR(GPIOA_ODR_Addr,n) //輸出
#define PAin(n) BIT_ADDR(GPIOA_IDR_Addr,n) //輸入
#define PBout(n) BIT_ADDR(GPIOB_ODR_Addr,n) //輸出
#define PBin(n) BIT_ADDR(GPIOB_IDR_Addr,n) //輸入
//這使得調(diào)取IO口非常方便
重要函數(shù)
-
- Stm32_Clock_Init 函數(shù)
//系統(tǒng)時鐘初始化函數(shù)
//pll:選擇的倍頻數(shù),從 2 開始,最大值為 16
void Stm32_Clock_Init(u8 PLL)
{
unsigned char temp=0;
MYRCC_DeInit(); //復(fù)位并配置向量表
RCC->CR|=0x00010000; //外部高速時鐘使能 HSEON
while(!(RCC->CR>>17)); //等待外部時鐘就緒
RCC->CFGR=0X00000400; //APB1=DIV2;APB2=DIV1;AHB=DIV1;
PLL-=2; //抵消 2 個單位
RCC->CFGR|=PLL<<18; //設(shè)置 PLL 值 2~16
RCC->CFGR|=1<<16; //PLLSRC ON
FLASH->ACR|=0x32; //FLASH 2 個延時周期
RCC->CR|=0x01000000; //PLLON
while(!(RCC->CR>>25)); //等待 PLL 鎖定
RCC->CFGR|=0x00000002; //PLL 作為系統(tǒng)時鐘
while(temp!=0x02) //等待 PLL 作為系統(tǒng)時鐘設(shè)置成功
{
temp=RCC->CFGR>>2;
temp&=0x03;
}
}
//Stm32_Clock_Init 函數(shù)中,我們設(shè)置了 APB1 為 2 分頻,APB2 為 1 分頻,AHB 為 1 分頻,
同時選擇 PLLCLK 作為系統(tǒng)時鐘。該函數(shù)只有一個參數(shù) PLL,就是用來配置時鐘的倍頻數(shù)的,比如當(dāng)前所用的晶振為 8Mhz,PLL 的值設(shè)為 9,那么 STM32 將運行在 72M 的速度下。
- MYRCC_DeInit 函數(shù)
MYRCC_DeInit 函數(shù)實現(xiàn)外設(shè)的復(fù)位,并關(guān)斷所有中斷,同時調(diào)用向量表配置函數(shù)MY_NVIC_SetVectorTable,配置中斷向量表。MYRCC_DeInit 函數(shù)如下:
//把所有時鐘寄存器復(fù)位
void MYRCC_DeInit(void)
{
RCC->APB1RSTR = 0x00000000;//復(fù)位結(jié)束
RCC->APB2RSTR = 0x00000000;
RCC->AHBENR = 0x00000014; //睡眠模式閃存和 SRAM 時鐘使能.其他關(guān)閉.
RCC->APB2ENR = 0x00000000; //外設(shè)時鐘關(guān)閉.
RCC->APB1ENR = 0x00000000;
RCC->CR |= 0x00000001; //使能內(nèi)部高速時鐘 HSION
RCC->CFGR &= 0xF8FF0000;
//復(fù)位 SW[1:0],HPRE[3:0],PPRE1[2:0],PPRE2[2:0],ADCPRE[1:0],MCO[2:0]
RCC->CR &= 0xFEF6FFFF; //復(fù)位 HSEON,CSSON,PLLON
RCC->CR &= 0xFFFBFFFF; //復(fù)位 HSEBYP
RCC->CFGR &= 0xFF80FFFF;//復(fù)位 PLLSRC, PLLXTPRE, PLLMUL[3:0] and USBPRE
RCC->CIR = 0x00000000; //關(guān)閉所有中斷
//配置向量表
#ifdef VECT_TAB_RAM
MY_NVIC_SetVectorTable(0x20000000, 0x0);
#else
MY_NVIC_SetVectorTable(0x08000000,0x0);
#endif
}
- Sys_Soft_Reset 函數(shù)
該函數(shù)用來實現(xiàn) STM32 的軟復(fù)位,代碼如下:
//系統(tǒng)軟復(fù)位
void Sys_Soft_Reset(void)
{
SCB->AIRCR =0X05FA0000|(u32)0x04;
}
SCB 為 MDK 定義的一個寄存器組,里面包含了很多與內(nèi)核相關(guān)的控制器,該結(jié)構(gòu)體在
core_m3.h 里面,可以找到,具體的定義如下所示:
typedef struct
{
__I uint32_t CPUID; //CM3 內(nèi)核版本號寄存器
__IO uint32_t ICSR; //中斷控制及狀態(tài)控制寄存器
__IO uint32_t VTOR; //向量表偏移量寄存器
__IO uint32_t AIRCR; //應(yīng)用程序中斷及復(fù)位控制寄存器
__IO uint32_t SCR; //系統(tǒng)控制寄存器
__IO uint32_t CCR; //配置與控制寄存器
__IO uint8_t SHP[12]; //系統(tǒng)異常優(yōu)先級寄存器組
__IO uint32_t SHCSR; //系統(tǒng) Handler 控制及狀態(tài)寄存器
__IO uint32_t CFSR; //MFSR+BFSR+UFSR
__IO uint32_t HFSR; //硬件 fault 狀態(tài)寄存器
__IO uint32_t DFSR; //調(diào)試 fault 狀態(tài)寄存器
__IO uint32_t MMFAR; //存儲管理地址寄存器
__IO uint32_t BFAR; //硬件 fault 地址寄存器
__IO uint32_t AFSR; //輔助 fault 地址寄存器
__I uint32_t PFR[2]; //處理器功能寄存器
__I uint32_t DFR; //調(diào)試功能寄存器
__I uint32_t ADR; //輔助功能寄存器
__I uint32_t MMFR[4]; //存儲器模型功能寄存器
__I uint32_t ISAR[5]; //ISA 功能寄存器
} SCB_TypeDef;
在 Sys_Soft_Reset 函數(shù)里面,我們只是對 SCB-> AIRCR 進行了一次操作,即實現(xiàn)了 STM32的軟復(fù)位。AIRCR 寄存器的各位定義如圖 5.2.3.1 所示:
- usart 文件夾介
- usart.c 里面包含了 2 個函數(shù)
- 一個是 void USART1_IRQHandler(void);
- 另外一個是 void uart_init(u32 bound);
- 還有一段對串口 printf 的支持代碼,如果去掉,則會導(dǎo)致 printf 無法使用,雖然軟件編譯不會報錯,但是硬件上 STM32 是無法啟動的,這段代碼不要去修改。
- printf 函數(shù)向串口發(fā)送我們需要的內(nèi)容,方便開發(fā)過程中查看代碼執(zhí)行情況以及一些變量值這段代碼不需要修改,引入到 usart.h 即可。
- uart_init 函數(shù) ,這個很常見,也很重要,筆記中的串口通信和通信專欄都有詳細的講解。以下是 uart_init(u32 bound)的配置范例:
- usart.c 里面包含了 2 個函數(shù)
void uart_init(u32 bound){
//GPIO 端口設(shè)置
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA , ENABLE); //使能 USART1,GPIOA 時鐘
//USART1_TX PA.9
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9 復(fù)用推挽輸出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //復(fù)用推挽輸出
GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化 GPIOA.0 發(fā)送端
//USART1_RX PA.10 浮空輸入
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空輸入
GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化 GPIOA.10 接收端
//Usart1 NVIC 中斷配置 配置
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; //對應(yīng)中斷通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//搶占優(yōu)先級 3
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子優(yōu)先級 3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ 通道使能
NVIC_Init(&NVIC_InitStructure); //中斷優(yōu)先級配置
//USART 初始化設(shè)置
USART_InitStructure.USART_BaudRate = bound;//波特率設(shè)置;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字長為 8 位
USART_InitStructure.USART_StopBits = USART_StopBits_1;//一個停止位
USART_InitStructure.USART_Parity = USART_Parity_No; //無奇偶校驗位
USART_InitStructure.USART_HardwareFlowControl=
USART_HardwareFlowControl_None;//無硬件數(shù)據(jù)流控制
USART_InitStructure.USART_Mode = USART_Mode_Rx |USART_Mode_Tx;//收發(fā)模式
USART_Init(USART1, &USART_InitStructure); //初始化串口
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//開啟中斷
USART_Cmd(USART1, ENABLE); //使能串口
}
USART1_IRQHandler 函數(shù) 一共三個:
DCD USART1_IRQHandler ; USART1
DCD USART2_IRQHandler ; USART2
DCD USART3_IRQHandler ; USART3
void USART1_IRQHandler(void)函數(shù)是串口 1 的中斷響應(yīng)函數(shù),當(dāng)串口 1 發(fā)生了相應(yīng)的中斷后,就會跳到該函數(shù)執(zhí)行。中斷相應(yīng)函數(shù)的名字是不能隨便定義的,一般我們都遵循 MDK定義的函名。這些函數(shù)名字在啟動文件 startup_stm32f10x_hd.s 文件中可以找到。
函數(shù)體里面通過函數(shù):
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
判斷是否接受中斷,如果是串口接受中斷,則讀取串口接受到的數(shù)據(jù):
Res =USART_ReceiveData(USART1);//(USART1->DR); //讀取接收到的數(shù)據(jù)
讀到數(shù)據(jù)后接下來就對數(shù)據(jù)進行分析。
1、 PLLMUL
PLLMUL 用于設(shè)置 STM32 的 PLLCLK,STM32 支持 2~16 倍頻設(shè)置。我們常用的是 8M 外部晶振+9 倍頻設(shè)置,剛好得到 72Mhz 的 PLLCLK。從上圖可以看出,PLLMUL 的時鐘源,可以來自內(nèi)部 8M RC 振蕩/2 或者外部高速晶振(4~16Mhz)。這里切記 PLLMUL 設(shè)置后的頻率不要超過72Mhz(想超也是可以,最大 128M 也可以跑,不過一旦出問題 ST 是不負(fù)責(zé)的!)。
2、 SW
SW 是 STM32 的 SYSCLK(系統(tǒng)時鐘)切換開關(guān),從上圖可以看出,SYSCLK 的來源可以是3個:HSI、PLLCLK 和HSE。我們一般選擇PLLCLK作為SYSCLK。SYSCLK最大為 72M。這里提示一下大家:STM32 剛上電的時候,用的是系統(tǒng)內(nèi)部 8M RC 時鐘,之后運行程序才會把時鐘源設(shè)置為其他。
3、 系統(tǒng)滴答時鐘(SYSTICK)
SYSTICK 就是 CortexM3 的系統(tǒng)滴答時鐘,上圖清楚的表明 SYSTICK 的來源是AHB 分頻后再 8 分頻,因為我們一般設(shè)置 AHB 不分頻,所以 SYSTICK 的頻率就等于SYSCLK/8,如果 SYSCLK 為 72M,那么 SYSTICK 的頻率就是 9Mhz。前面介紹的延時函數(shù),就是基于 SYSTICK 來實現(xiàn)的。
4、 PCLK1
PCLK1 是 APB1 總線上外設(shè)的時鐘,最大為 36Mhz,所有掛載在 APB1 上的外設(shè),最大時鐘都是 36Mhz(定時器除外,原因見上圖),比如串口 2~5、SPI2 和 SPI3 等,在使用的時候,要特別留意。PCLK1 的時鐘可以通過 APB1 預(yù)分頻器設(shè)置,我們默認(rèn)一般設(shè)置為 2 分頻。
5、 PCLK2
PCLK2 是 APB2 總線上外設(shè)的時鐘,最大為 72Mhz,所有掛載在 APB2 上的外設(shè),最大時鐘都是 72Mhz,比如 GPIOA~G、串口 1、SPI1、ADC1~3 等。 PCLK2 的時鐘可以通過 APB2 預(yù)分頻器設(shè)置,我們默認(rèn)一般設(shè)置為 1,也就是不分頻。