1. delay文件
delay 文件夹内包含了 delay.c 和 delay.h 两个文件,这两个文件用来实现系统的延时功能,其中包含 7 个函数:
void delay_osschedlock(void);
void delay_osschedunlock(void);
void delay_ostimedly(u32 ticks);
void SysTick_Handler(void);
前面 4 个函数,仅在支持操作系统(OS)的时候,需要用到,而后面三个函数,则不论是否支持 OS 都需要用到。
void delay_init(void);
void delay_ms(u16 nms);
void delay_us(u32 nus);
void delay_init(void);
//初始化延迟函数
//当使用 OS 的时候,此函数会初始化 OS 的时钟节拍
//SYSTICK 的时钟固定为 HCLK 时钟的 1/8
//SYSCLK:系统时钟
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; //每秒钟的计数次数 单位为 K ,所以对于晶振是9MHZ时 syclk=72000
reload*=1000000/delay_ostickspersec; //根据 delay_ostickspersec 设定溢出时间
//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 时钟数
#endif
}
2. sys文件
-
- 共有五个文件,其中sys.c和sys.h是由ALIENTEK提供,是重点介绍的部分。在 sys.h 里面定义了 STM32F1 的 IO 口输入读取宏定义和输出宏定义。sys.c 里面定义了很多STM32F1 底层硬件很相关的设置函数,包括系统时钟的配置、IO 配置、中断的配置等。下面我们将分别向大家介绍这两个文件。
- IO口的位操作
- 共有五个文件,其中sys.c和sys.h是由ALIENTEK提供,是重点介绍的部分。在 sys.h 里面定义了 STM32F1 的 IO 口输入读取宏定义和输出宏定义。sys.c 里面定义了很多STM32F1 底层硬件很相关的设置函数,包括系统时钟的配置、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
对应:
//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) //输入
//这使得调取IO口非常方便
重要函数
-
- Stm32_Clock_Init 函数
//系统时钟初始化函数
//pll:选择的倍频数,从 2 开始,最大值为 16
void Stm32_Clock_Init(u8 PLL)
{
unsigned char temp=0;
MYRCC_DeInit(); //复位并配置向量表
RCC->CR|=0x00010000; //外部高速时钟使能 HSEON
while(!(RCC->CR>>17)); //等待外部时钟就绪
RCC->CFGR=0X00000400; //APB1=DIV2;APB2=DIV1;AHB=DIV1;
PLL-=2; //抵消 2 个单位
RCC->CFGR|=PLL<<18; //设置 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 作为系统时钟
while(temp!=0x02) //等待 PLL 作为系统时钟设置成功
{
temp=RCC->CFGR>>2;
temp&=0x03;
}
}
//Stm32_Clock_Init 函数中,我们设置了 APB1 为 2 分频,APB2 为 1 分频,AHB 为 1 分频,
同时选择 PLLCLK 作为系统时钟。该函数只有一个参数 PLL,就是用来配置时钟的倍频数的,比如当前所用的晶振为 8Mhz,PLL 的值设为 9,那么 STM32 将运行在 72M 的速度下。
- MYRCC_DeInit 函数
MYRCC_DeInit 函数实现外设的复位,并关断所有中断,同时调用向量表配置函数MY_NVIC_SetVectorTable,配置中断向量表。MYRCC_DeInit 函数如下:
//把所有时钟寄存器复位
void MYRCC_DeInit(void)
{
RCC->APB1RSTR = 0x00000000;//复位结束
RCC->APB2RSTR = 0x00000000;
RCC->AHBENR = 0x00000014; //睡眠模式闪存和 SRAM 时钟使能.其他关闭.
RCC->APB2ENR = 0x00000000; //外设时钟关闭.
RCC->APB1ENR = 0x00000000;
RCC->CR |= 0x00000001; //使能内部高速时钟 HSION
RCC->CFGR &= 0xF8FF0000;
//复位 SW[1:0],HPRE[3:0],PPRE1[2:0],PPRE2[2:0],ADCPRE[1:0],MCO[2:0]
RCC->CR &= 0xFEF6FFFF; //复位 HSEON,CSSON,PLLON
RCC->CR &= 0xFFFBFFFF; //复位 HSEBYP
RCC->CFGR &= 0xFF80FFFF;//复位 PLLSRC, PLLXTPRE, PLLMUL[3:0] and USBPRE
RCC->CIR = 0x00000000; //关闭所有中断
//配置向量表
#ifdef VECT_TAB_RAM
MY_NVIC_SetVectorTable(0x20000000, 0x0);
#else
MY_NVIC_SetVectorTable(0x08000000,0x0);
#endif
}
- Sys_Soft_Reset 函数
该函数用来实现 STM32 的软复位,代码如下:
//系统软复位
void Sys_Soft_Reset(void)
{
SCB->AIRCR =0X05FA0000|(u32)0x04;
}
SCB 为 MDK 定义的一个寄存器组,里面包含了很多与内核相关的控制器,该结构体在
core_m3.h 里面,可以找到,具体的定义如下所示:
typedef struct
{
__I uint32_t CPUID; //CM3 内核版本号寄存器
__IO uint32_t ICSR; //中断控制及状态控制寄存器
__IO uint32_t VTOR; //向量表偏移量寄存器
__IO uint32_t AIRCR; //应用程序中断及复位控制寄存器
__IO uint32_t SCR; //系统控制寄存器
__IO uint32_t CCR; //配置与控制寄存器
__IO uint8_t SHP[12]; //系统异常优先级寄存器组
__IO uint32_t SHCSR; //系统 Handler 控制及状态寄存器
__IO uint32_t CFSR; //MFSR+BFSR+UFSR
__IO uint32_t HFSR; //硬件 fault 状态寄存器
__IO uint32_t DFSR; //调试 fault 状态寄存器
__IO uint32_t MMFAR; //存储管理地址寄存器
__IO uint32_t BFAR; //硬件 fault 地址寄存器
__IO uint32_t AFSR; //辅助 fault 地址寄存器
__I uint32_t PFR[2]; //处理器功能寄存器
__I uint32_t DFR; //调试功能寄存器
__I uint32_t ADR; //辅助功能寄存器
__I uint32_t MMFR[4]; //存储器模型功能寄存器
__I uint32_t ISAR[5]; //ISA 功能寄存器
} SCB_TypeDef;
在 Sys_Soft_Reset 函数里面,我们只是对 SCB-> AIRCR 进行了一次操作,即实现了 STM32的软复位。AIRCR 寄存器的各位定义如图 5.2.3.1 所示:
- usart 文件夹介
- usart.c 里面包含了 2 个函数
- 一个是 void USART1_IRQHandler(void);
- 另外一个是 void uart_init(u32 bound);
- 还有一段对串口 printf 的支持代码,如果去掉,则会导致 printf 无法使用,虽然软件编译不会报错,但是硬件上 STM32 是无法启动的,这段代码不要去修改。
- printf 函数向串口发送我们需要的内容,方便开发过程中查看代码执行情况以及一些变量值这段代码不需要修改,引入到 usart.h 即可。
- uart_init 函数 ,这个很常见,也很重要,笔记中的串口通信和通信专栏都有详细的讲解。以下是 uart_init(u32 bound)的配置范例:
- usart.c 里面包含了 2 个函数
void uart_init(u32 bound){
//GPIO 端口设置
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 复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化 GPIOA.0 发送端
//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; //对应中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级 3
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子优先级 3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ 通道使能
NVIC_Init(&NVIC_InitStructure); //中断优先级配置
//USART 初始化设置
USART_InitStructure.USART_BaudRate = bound;//波特率设置;
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;//无硬件数据流控制
USART_InitStructure.USART_Mode = USART_Mode_Rx |USART_Mode_Tx;//收发模式
USART_Init(USART1, &USART_InitStructure); //初始化串口
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启中断
USART_Cmd(USART1, ENABLE); //使能串口
}
USART1_IRQHandler 函数 一共三个:
DCD USART1_IRQHandler ; USART1
DCD USART2_IRQHandler ; USART2
DCD USART3_IRQHandler ; USART3
void USART1_IRQHandler(void)函数是串口 1 的中断响应函数,当串口 1 发生了相应的中断后,就会跳到该函数执行。中断相应函数的名字是不能随便定义的,一般我们都遵循 MDK定义的函名。这些函数名字在启动文件 startup_stm32f10x_hd.s 文件中可以找到。
函数体里面通过函数:
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
判断是否接受中断,如果是串口接受中断,则读取串口接受到的数据:
Res =USART_ReceiveData(USART1);//(USART1->DR); //读取接收到的数据
读到数据后接下来就对数据进行分析。
1、 PLLMUL
PLLMUL 用于设置 STM32 的 PLLCLK,STM32 支持 2~16 倍频设置。我们常用的是 8M 外部晶振+9 倍频设置,刚好得到 72Mhz 的 PLLCLK。从上图可以看出,PLLMUL 的时钟源,可以来自内部 8M RC 振荡/2 或者外部高速晶振(4~16Mhz)。这里切记 PLLMUL 设置后的频率不要超过72Mhz(想超也是可以,最大 128M 也可以跑,不过一旦出问题 ST 是不负责的!)。
2、 SW
SW 是 STM32 的 SYSCLK(系统时钟)切换开关,从上图可以看出,SYSCLK 的来源可以是3个:HSI、PLLCLK 和HSE。我们一般选择PLLCLK作为SYSCLK。SYSCLK最大为 72M。这里提示一下大家:STM32 刚上电的时候,用的是系统内部 8M RC 时钟,之后运行程序才会把时钟源设置为其他。
3、 系统滴答时钟(SYSTICK)
SYSTICK 就是 CortexM3 的系统滴答时钟,上图清楚的表明 SYSTICK 的来源是AHB 分频后再 8 分频,因为我们一般设置 AHB 不分频,所以 SYSTICK 的频率就等于SYSCLK/8,如果 SYSCLK 为 72M,那么 SYSTICK 的频率就是 9Mhz。前面介绍的延时函数,就是基于 SYSTICK 来实现的。
4、 PCLK1
PCLK1 是 APB1 总线上外设的时钟,最大为 36Mhz,所有挂载在 APB1 上的外设,最大时钟都是 36Mhz(定时器除外,原因见上图),比如串口 2~5、SPI2 和 SPI3 等,在使用的时候,要特别留意。PCLK1 的时钟可以通过 APB1 预分频器设置,我们默认一般设置为 2 分频。
5、 PCLK2
PCLK2 是 APB2 总线上外设的时钟,最大为 72Mhz,所有挂载在 APB2 上的外设,最大时钟都是 72Mhz,比如 GPIOA~G、串口 1、SPI1、ADC1~3 等。 PCLK2 的时钟可以通过 APB2 预分频器设置,我们默认一般设置为 1,也就是不分频。