*=============================================================================
* Supported STM32F42xxx/43xxx devices
*-----------------------------------------------------------------------------
* System Clock source | PLL (HSE)
*-----------------------------------------------------------------------------
* SYSCLK(Hz) | 180000000
*-----------------------------------------------------------------------------
* HCLK(Hz) | 180000000
*-----------------------------------------------------------------------------
* AHB Prescaler | 1
*-----------------------------------------------------------------------------
* APB1 Prescaler | 4
*-----------------------------------------------------------------------------
* APB2 Prescaler | 2
*-----------------------------------------------------------------------------
* HSE Frequency(Hz) | 25000000
*-----------------------------------------------------------------------------
* PLL_M | 25
*-----------------------------------------------------------------------------
* PLL_N | 360
*-----------------------------------------------------------------------------
* PLL_P | 2
*-----------------------------------------------------------------------------
* PLL_Q | 7
*-----------------------------------------------------------------------------
* PLLI2S_N | NA
*-----------------------------------------------------------------------------
* PLLI2S_R | NA
*-----------------------------------------------------------------------------
* I2S input clock | NA
*-----------------------------------------------------------------------------
* VDD(V) | 3.3
*-----------------------------------------------------------------------------
* Main regulator output voltage | Scale1 mode
*-----------------------------------------------------------------------------
* Flash Latency(WS) | 5
*-----------------------------------------------------------------------------
* Prefetch Buffer | ON
*-----------------------------------------------------------------------------
* Instruction cache | ON
*-----------------------------------------------------------------------------
* Data cache | ON
*-----------------------------------------------------------------------------
* Require 48MHz for USB OTG FS, | Disabled
* SDIO and RNG clock |
*-----------------------------------------------------------------------------
*=============================================================================
STM32芯片(所有型号)的时钟包含4类:
HSE(High Speed External)高速外部时钟
来源:有源晶振(1-50M)、无源晶振(4-26M)
控制:RCC_CR 时钟控制寄存器的位16:HSEON控制
HSI(High Speed Internal)高速内部时钟
来源:芯片内部,大小为16M,当HSE故障时,系统时钟会自
动切换到HSI,直到HSE启动成功。
控制: RCC_CR 时钟控制寄存器的位0:HSION控制
LSE(Low Speed External)低速外部时钟
LSI(Low Speed Internal)低速内部时钟
锁相环时钟:PLLCLK
来源:HSI、HSE。由PLLSRC位配置。
HSE或者HSI先经过一个分频因子M进行分频,然后再经过一个倍频因子N,然后再经过一个分频因子P,最后成为锁相环时钟PLLCLK = (HSE/M) * N / P = 25/25 * 360 / 2 = 180M
F407:(8/8)*336/2=168M
控制: RCC_PLLCFGR :RCC PLL 配置寄存器
系统时钟:SYSCLK
缩写:SYSCLK,F429最高为180M,F407最高为168M。
来源:HSI、HSE,PLLCLK。
控制: RCC_CFGR 时钟配置寄存器的SW位
AHB,是Advanced High performance Bus的缩写,译作高级高性能总线,这是一种“系统总线”。
AHB主要用于高性能模块(如CPU、DMA和DSP等)之间的连接。AHB 系统由主模块、从模块和基础结构(Infrastructure)3部分组成,整个AHB总线上的传输都由主模块发出,由从模块负责回应。APB,是Advanced
Peripheral Bus的缩写,这是一种外围总线。
APB主要用于低带宽的周边外设之间的连接,例如UART、1284等,它的总线架构不像AHB支持多个主模块,在APB里面唯一的主模块就是APB 桥。再往下,APB2负责AD,I/O,高级TIM,串口1;APB1负责DA,USB,SPI,I2C,CAN,串口2345,普通TIM。
HCLK:AHB高速总线时钟,最高为180M。为AHB总线的外设提供时钟、为Cortex系统定时器提供时钟(SysTick)、为内核提供时钟(FCLK)。
AHB为advanced high-performance bus。
来源:系统时钟分频得到。
控制: RCC_CFGR 时钟配置寄存器的HPRE位
PCLK1时钟
PCLK1:APB低速总线时钟,最高为45M。为APB1总线的外设提供时钟。2倍频之后则为APB1总线的定时器提供时钟,最大为90M。
来源:HCLK分频得到。
控制: RCC_CFGR 时钟配置寄存器的PPRE1位
PCLK2时钟
PCLK2:APB高速总线时钟,最高为90M。为APB2总线的外设提供时钟。2倍频之后则为APB2总线的定时器提供时钟,最大为180M。
来源:HCLK分频得到。
控制: RCC_CFGR 时钟配置寄存器的PPRE2位
APB1 定时器有 TIM2, TIM3 ,TIM4, TIM5, TIM6, TIM7, TIM12, TIM13,TIM14-90M
APB2 定时器有 TIM1, TIM8 ,TIM9, TIM10, TIM11—180M
RTC时钟
RTC:为芯片内部的RTC提供时钟。
来源:HSE_RTC(HSE分频得到)、LSE(外部32.768KHZ的晶体提供)、LSI(32KHZ)。
控制: RCC备份域控制寄存器RCC_BDCR:RTCSEL位控制
独立看门狗时钟:IWDGCLK,由LSI提供
I2S时钟:由外部的引脚I2S_CKIN或者PLLI2SCLK提供。
以太网PHY时钟:429没有集成PHY,只能外接PHY芯片,比如LAN8720,那PHY时钟就由外部的PHY芯片提供,大小为50M。
USB PHY时钟:429的USB没有集成PHY,要想实现USB高速传输,只能外接PHY芯片,比如USB33000。那USB PHY时钟就由外部的PHY芯片提供。
SysTick:系统定时器(滴答定时器),24位,只能递减,存在于内核,嵌套在NVIC中,所有的Cortex-M内核的单片机都具有这个定时器。
它是从load的值开始减数,减到0时COUNTFLAG置为1,产生中断。,然后重装载load的值,val是当前值
Systick涉及到四个寄存器
SysTick_CTRL 控制寄存器
SysTick_LOAD 重载寄存器
SysTick_VAL 当前值寄存器
SysTick_CALRB 校准值寄存器
对四个寄存器及位的解释:
SysTick_CRTL
[0] : ENABLE systick使能位 0=关闭systick功能,1=开启systick功能 使能位
[1] : TICKINT systick中断使能位 0=关闭systick中断,1=开启systick中断
[2] : CLKSOURCE systick时钟源选择位 0=使用HCLK/8时钟源 1=使用HCLK时钟源
[16]: COUNTFLAG systick计数比较标志,如果上次读取本寄存器后,systick已经到0了,则该位为1,如果 读取该位,该位自动清零; 状态位
[其它位] : 保留;
在不同的开发模式里,SysTick_CRTL有可能会被表示成STK_CRS,这里留意一下就好了;
SysTick_LOAD
[23:0] : systick定时器递减到0后,从SysTick_LOAD的值装载给SysTick_VAL(当前值寄存器),从而实现和C51单片机定时器的8位自动重装模式一样的效果; 重装载
[其它位]:保留
SysTick_VAL
[23:0] : 读取时返回当前倒计数的值,写它则使之清零,同时还会清除SysTick控制寄存器和状态寄存器中的COUNTFLAG标志; 计数位
[其它位]:保留
SysTick_CALRB(不做重要介绍因为不常用)
[31] :NOREF 外部参考时钟有无标志位 1=没有外部参考时钟(STCLK不可用); 0=外部参考时钟可用
[30] : SKEW 校准值是否为准确1ms标志位 1=准确 ; 0=不准确;
[23:0] : 校准值;(STM3210x使用手册中指出校准值固定为9000,而且当系统滴答时钟设定为9MHz(HCLK/8的最大值),将会产生1ms时间基准)
SysTick配置函数: SysTick_Config(SystemCoreClock / 1000);
函数参数的含义是:两次中断之间的tick次数 ,
那么根据 SystemCoreClock =180M=180000000,可以得出
中断之间间隔为:(180M / 1000) * (1 / 180M) (秒) = 1ms
函数调用:
SysTick_Config(SystemCoreClock / 1000);
函数定义:
__STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks)
{
if ((ticks - 1UL) > SysTick_LOAD_RELOAD_Msk) { return (1UL); }
/* Reload value impossible */ //如果赋值不对,返回
SysTick->LOAD = (uint32_t)(ticks - 1UL);
/* set reload register */ //设置重装载值
NVIC_SetPriority (SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL);
/* set Priority for Systick Interrupt */ //设置优先级
SysTick->VAL = 0UL;
/* Load the SysTick Counter Value */ //计数器当前值
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk |
SysTick_CTRL_TICKINT_Msk |
SysTick_CTRL_ENABLE_Msk;
/* Enable SysTick IRQ and SysTick Timer */ //选用HCLK时钟, 开启中断,使能定时器
return (0UL);
/* Function successful */
}
利用systick实现最基础的delay_ms:
#include "bsp.h"
uint32_t timedelay = 0;
void delay_us(uint8_t us)
{
}
void delay_ms(uint8_t ms)
{
timedelay = ms;
while(timedelay != 0);//循环挂机,直到timedelay == 0
}
void delay_s(uint8_t s)
{
delay_ms(250);
delay_ms(250);
delay_ms(250);
delay_ms(250);
}
void Systick_Init(void)
{
SysTick_Config(SystemCoreClock / 1000);//主频180M 打开中断 中断时间为1ms
//计算方法为 180M / 1000 / 180M (单位秒)
}
void SysTick_Handler(void)//中断服务函数
{
TimingDelay_Decrement();
}
void TimingDelay_Decrement(void)
{
if (timedelay != 0)
{
timedelay--;
}
}
进阶delay_ms原理:
1. 函数SysTick_Config()是属于CMSIS里面的一个函数,实现配置如下
- 函数的参数是重载寄存器reload要赋予的初值
- 配置嘀嗒定时器的优先级是最低优先级15
- 配置HCLK作为系统的时钟源
- 使能嘀嗒定时器中断
- 启动嘀嗒定时器计数
2. 用户可以在调用函数SysTick_Config()后通过函数 SysTick_CLKSourceConfig()
更改嘀嗒定时器的时钟源为HCLK/8。SysTick_CLKSourceConfig()在文件misc.c里面
3. 用户可以在调用函数SysTick_Config()后通过函数 NVIC_SetPriority()修改优先级,
函数NVIC_SetPriority()在文件core_cm4.h文件里面
4. 通过下面的公式调整时基
Reload Value = SysTick Counter Clock (Hz) x Desired Time base (s)
Reload Value 是函数SysTick_Config()的参数,但是不能超过0xFFFFFF
delay_us原理:
reload = SysTick->LOAD;
ticks = n * (SystemCoreClock / 1000000); /* 需要的节拍数 */
tcnt = 0;
told = SysTick->VAL; /* 刚进入时的计数器值 */
while (1)
{
tnow = SysTick->VAL;
if (tnow != told)
{ /* SYSTICK是一个递减的计数器 */
if (tnow < told) { tcnt += told - tnow; }
/* 重新装载递减 */
else { tcnt += reload - tnow + told; }
told = tnow;
/* 时间超过/等于要延迟的时间,则退出 */
if (tcnt >= ticks) {break;}
}
}
NVIC中断基础
STM32F4系列包含91个可屏蔽中断(407系列是86个)
具体可以参考英文参考手册P377 表62中的内容.
STM32中有两个优先级的概念—抢占式优先级和响应优先级,有人把响应优先级称作'亚优先级'或'副优先级',每个中断源都需要被指定这两种优先级。
具有高抢占式优先级的中断可以在具有低抢占式优先级的中断处理过程中被响应,即中断嵌套,或者说高抢占式优先级的中断可以嵌套在低抢占式优先级的中断中。
当两个中断源的抢占式优先级相同时,这两个中断将没有嵌套关系,当一个中断到来后,如果正在处理另一个中断,这个后到来的中断就要等到前一个中断处理完之后才能被处理。如果这两个中断同时到达,则中断控制器根据他们的响应优先级高低来决定先处理哪一个;如果他们的抢占式优先级和响应优先级都相等,则根据他们在中断表中的排位顺序决定先处理哪一个。
利用函数 NVIC_PriorityGroupConfig 来配置这两个中断优先级 参数有下面5种
* @arg NVIC_PriorityGroup_0: 0 bits for pre-emption priority 抢占优先级
* 4 bits for subpriority 响应优先级(次优先级)
* @arg NVIC_PriorityGroup_1: 1 bits for pre-emption priority
* 3 bits for subpriority
* @arg NVIC_PriorityGroup_2: 2 bits for pre-emption priority
* 2 bits for subpriority
* @arg NVIC_PriorityGroup_3: 3 bits for pre-emption priority
* 1 bits for subpriority
* @arg NVIC_PriorityGroup_4: 4 bits for pre-emption priority
* 0 bits for subpriority
NVIC称之为“嵌套向量中断控制器:Nested Vectored Interrupt Controller (NVIC)“。
CM4内核支持256个中断,其中包含了16个内核中断和240个外部中断,并且具有256级的可编程中断设置。但STM32F4并没有使用CM4内核的全部东西,而是只用了它的一部分
STM32F40xx/STM32F41xx总共有92个中断,STM32F42xx/STM32F43xx则总共有96个中断,以下仅以STM32F40xx/41xx为例讲解。
STM32F40xx/STM32F41xx的92个中断里面,包括10个内核中断和82个可屏蔽中断,具有16级可编程的中断优先级,而我们常用的就是这82个可屏蔽中断。在MDK内,与NVIC相关的寄存器,MDK为其定义了如下的结构体:
typedefstruct
{
__IOuint32_t ISER[8]; /*!< 中断使能寄存器组 */
uint32_tRESERVED0[24];
__IOuint32_t ICER[8]; /*!< 中断除能寄存器组 */
uint32_tRSERVED1[24];
__IOuint32_t ISPR[8]; /*!< 中断挂起控制寄存器组 */
uint32_tRESERVED2[24];
__IOuint32_t ICPR[8]; /*!< 中断解挂控制寄存器组 */
uint32_tRESERVED3[24];
__IOuint32_t IABR[8]; /*!<中断激活标志位寄存器组 */
uint32_tRESERVED4[56];
__IOuint8_t IP[240]; /*!< 中断优先级控制的寄存器组*/
uint32_tRESERVED5[644];
__Ouint32_t STIR;? /*!< 软件触发中断寄存器 */
}NVIC_Type;
STM32F4的中断在这些寄存器的控制下有序的执行的。只有了解这些中断寄存器,才能方便的使用STM32F4的中断。下面重点介绍这几个寄存器:
ISER[8]:ISER全称是:Interrupt Set-Enable Registers,这是一个中断使能寄存器组。上面说了CM4内核支持256个中断,这里用8个32位寄存器来控制,每个位控制一个中断。但是STM32F4的可屏蔽中断最多只有82个,所以对我们来说,有用的就是三个(ISER[0~2]),总共可以表示96个中断。而STM32F4只用了其中的前82个。ISER[0]的bit0~31分别对应中断0~31;ISER[1]的bit0~32对应中断32~63;ISER[2]的bit0~17对应中断64~81;这样总共82个中断就分别对应上了。你要使能某个中断,必须设置相应的ISER 位为1,使该中断被使能(这里仅仅是使能,还要配合中断分组、屏蔽、IO 口映射等设置才算是一个完整的中断设置)。
ICER[8]:全称是:Interrupt Clear-Enable Registers,是一个中断除能寄存器组。该寄存器组与ISER的作用恰好相反,是用来清除某个中断的使能的。其对应位的功能,也和ICER一样。这里要专门设置一个ICER来清除中断位,而不是向ISER写0来清除,是因为NVIC的这些寄存器都是写1有效的,写0是无效的。
ISPR[8]:全称是:Interrupt Set-Pending Registers,是一个中断挂起控制寄存器组。每个位对应的中断和ISER是一样的。通过置1,可以将正在进行的中断挂起,而执行同级或更高级别的中断。写0是无效的。
ICPR[8]:全称是:Interrupt Clear-Pending Registers,是一个中断解挂控制寄存器组。其作用与ISPR相反,对应位也和ISER是一样的。通过设置1,可以将挂起的中断接挂。写0无效。
IABR[8]:全称是:Interrupt Active Bit Registers,是一个中断激活标志位寄存器组。对应位所代表的中断和ISER一样,如果为1,则表示该位所对应的中断正在被执行。这是一个只读寄存器,通过它可以知道当前在执行的中断是哪一个。在中断执行完了由硬件自动清零。
IP[240]:全称是:Interrupt Priority Registers,是一个中断优先级控制的寄存器组。这个寄存器组相当重要!STM32F4的中断分组与这个寄存器组密切相关。IP 寄存器组由240个8bit的寄存器组成,每个可屏蔽中断占用8bit,这样总共可以表示240个可屏蔽中断。而STM32F4只用到了其中的82个。IP[81]~IP[0]分别对应中断81~0。而每个可屏蔽中断占用的8bit并没有全部使用,而是只用了高4位。这4位,又分为抢占优先级和响应优先级。抢占优先级在前,响应优先级在后。而这两个优先级各占几个位又要根据SCB->AIRCR中的中断分组设置来决定。
这里简单介绍一下STM32F4的中断分组:STM32F4将中断分为5个组,组0~4。该分组的设置是由SCB->AIRCR寄存器的bit10~8来定义的。如图所示:
通过这个表,我们就可以清楚的看到组0~4对应的配置关系,例如组设置为3,那么此时所有的82个中断,每个中断的中断优先寄存器的高四位中的最高3位是抢占优先级,低1位是响应优先级。每个中断,你可以设置抢占优先级为0~7,响应优先级为1或0。抢占优先级的级别高于响应优先级。而数值越小所代表的优先级就越高。
这里需要注意两点:
第一,如果两个中断的抢占优先级和响应优先级都是一样的话,则看哪个中断先发生就先执行;
第二,高优先级的抢占优先级是可以打断正在进行的低抢占优先级中断的。而抢占优先级相同的中断,高优先级的响应优先级不可以打断低响应优先级的中断。
结合实例说明一下:假定设置中断优先级组为2,然后设置中断3(RTC_WKUP中断)的抢占优先级为2,响应优先级为1。中断6(外部中断0)的抢占优先级为3,响应优先级为0。中断7(外部中断1)的抢占优先级为2,响应优先级为0。那么这3个中断的优先级顺序为:中断7>中断3>中断6。
上面例子中的中断3和中断7都可以打断中断6的中断。而中断7和中断3却不可以相互打断!
通过以上介绍,我们熟悉了STM32F4中断设置的大致过程。
在来个例子:
抢占优先级和响应优先级STM32 的中断向量具有两个属性,一个为抢占属性,另一个为响应属性,其属性编号越小,表明它的优先级别越高。
抢占,是指打断其它中断的属性,即因为具有这个属性,会出现嵌套中断
(在执行中断服务函数 A 的过程中被中断 B 打断,执行完中断服务函数 B 再继续
执行中断服务函数 A),抢占属性由 NVIC_IRQChannelPreemptionPriority 的参
数配置。而响应属性则应用在抢占属性相同的情况下,当两个中断向量的抢占优先
级相同时,如果两个中断同时到达,则先处理响应优先级高的中断,响应属性由NVIC_IRQChannelSubPriority 的参数配置。例如,现在有三个中断向量:
中断向量 抢占优先级 响应优先级
A 0 0
B 1 0
C 1 1
若内核正在执行 C 的中断服务函数,则它能被抢占优先级更高的中断 A 打
断,由于 B 和 C 的抢占优先级相同,所以 C 不能被 B 打断。但如果 B 和 C 中断
是同时到达的,内核就会首先响应响应优先级别更高的 B 中断。
接下来我们介绍如何使用库函数实现以上中断分组设置以及中断优先级管理,使得我们以后的中断设置简单化。NVIC中断管理函数主要在misc.c文件里面。
首先要讲解的是中断优先级分组函数NVIC_PriorityGroupConfig,其函数申明如下
void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup);
这个函数的作用是对中断的优先级进行分组,这个函数在系统中只能被调用一次,一旦分组确定就最好不要更改。这个函数我们可以找到其实现:
void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup)
{
assert_param(IS_NVIC_PRIORITY_GROUP(NVIC_PriorityGroup));
SCB->AIRCR= AIRCR_VECTKEY_MASK | NVIC_PriorityGroup;
}
从函数体可以看出,这个函数唯一目的就是通过设置SCB->AIRCR寄存器来设置中断优先级分组,这在前面寄存器讲解的过程中已经讲到。而其入口参数通过双击选中函数体里面的“IS_NVIC_PRIORITY_GROUP”然后右键“Go to defitionof …”可以查看到为
这也是我们上面提到的,分组范围为0-4。比如我们设置整个系统的中断优先级分组值为2,那么方法是:
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
这样就确定了一共为“2位抢占优先级,2位响应优先级”。
设置好了系统中断分组,那么对于每个中断我们又怎么确定他的抢占优先级和响应优先级呢?下面我们讲解一个重要的函数为中断初始化函数NVIC_Init,其函数申明为:
void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct)
其中NVIC_InitTypeDef是一个结构体,我们可以看看结构体的成员变量:
typedefstruct
{
uint8_tNVIC_IRQChannel; //定义初始化的是哪个中断 中断名
uint8_tNVIC_IRQChannelPreemptionPriority; //定义这个中断的抢占优先级别
uint8_tNVIC_IRQChannelSubPriority; //定义这个中断的抢占优先级别
FunctionalStateNVIC_IRQChannelCmd; //中断通道是否使能
} NVIC_InitTypeDef;
NVIC_InitTypeDef结构体中间有四个成员变量,接下来我们一一来看看这些成员变量的含义。
NVIC_IRQChannel:定义初始化的是哪个中断,这个我们可以在stm32f4xx.h中找到每个中断对应的名字。例如USART1_IRQn。
NVIC_IRQChannelPreemptionPriority:定义这个中断的抢占优先级别。
NVIC_IRQChannelSubPriority:定义这个中断的子优先级别,也叫响应优先级。
NVIC_IRQChannelCmd:该中断通道是否使能。
比如我们要使能串口1的中断,同时设置抢占优先级为1,响 应优先级位2,初始化的方法是:
NVIC_InitTypeDef? NVIC_InitStructure;;
NVIC_InitStructure.NVIC_IRQChannel= USART1_IRQn;//串口1中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;// 抢占优先级为1
NVIC_InitStructure.NVIC_IRQChannelSubPriority= 2;// 响应优先级位2
NVIC_InitStructure.NVIC_IRQChannelCmd= ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //根据上面指定的参数初始化NVIC寄存器
这里我们讲解了中断分组的概念以及设置单个中断优先级的方法。对于每个中断,还有一些类似清除中断,查看中断状态的操作,这在后面我们讲解每个中断的时候会详细讲解怎么使用。
最后我们总结一下中断优先级设置的步骤:
串口初始化:
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
/* 第1步: 配置GPIO */ /* 打开 GPIO 时钟 */
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
/* 打开 UART 时钟 */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
/* 将 PA9 映射为 USART1_TX */
GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1) ;
/* 将 PA10 映射为 USART1_RX */
GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_USART1);
/* 配置 USART Tx 为复用功能 */
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
/* 配置 USART Rx 为复用功能 */
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* 第2步: 配置串口硬件参数 */
USART_InitStructure.USART_BaudRate = 115200;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx|USART_Mode_Tx;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_Init(USART1,&USART_InitStructure);
/*使能串口*/
USART_Cmd(USART1,ENABLE);
/*清楚标志,防止出现bug*/
USART_ClearFlag(USART1,USART_FLAG_TC);
GPIO-通用功能I/O
每个GPI/O 端口有
两个32 位配置寄存器(GPIOx_CRL,GPIOx_CRH),两个32位数据寄存器(GPIOx_IDR,GPIOx_ODR),
一个32 位置位/复位寄存器(GPIOx_BSRR),
一个16 位复位寄存器(GPIOx_BRR)和
一个32 位锁定寄存器(GPIOx_LCKR)。
GPIO 端口的每个位可以由软件分别配置成多种模式。每个I/O 端口位可以自由编程,然而I/0 端口寄存器必须按32 位字被访问(不允许半字或字节访问)。GPIOx_BSRR 和GPIOx_BRR 寄存器允许对任何GPIO 寄存器的读/更改的独立访问
1.通用I/O(GPIO):最最基本的功能,可以驱动LED、可以产生PWM、可以驱动蜂鸣器等等;
2.单独的位设置或位清除:方便软体作业,程序简单。端口配置好以后只需
GPIO_SetBits(GPIOx,GPIO_Pin_x); 可以实现对GPIOx的pinx位为高电平 置高
GPIO_ResetBits(GPIOx, GPIO_Pin); 可以实现对GPIOx的pinx位为低电平 置低
3.外部中断/唤醒线:端口必须配置成输入模式时,所有端口都有外部中断能力;
4.复用功能(AF):复用功能的端口兼有IO功能等。复位期间和刚复位后,复用功能未开启,I/O 端口被配置成浮空输入模式:(CNFx[1:0]=01b,MODEx[1:0]=00b)。
5.软件重新映射I/O复用功能:为了使不同器件封装的外设I/O 功能的数量达到最优,可以把一些复用功能重新映射到其他一些脚上。这可以通过软件配置相应的寄存器来完成。这时,复用功能就不再映射到它们的原始引脚上了;
6.GPIO锁定机制:主要针对复位设定的,当某端口位lock后,复位后将不改变的此端口的位配置。
I/O配置的步骤-输出模式
//1.定义一个端口结构体变量
GPIO_InitTypeDef GPIO_InitStructure;
//2.开启端口对应的时钟
RCC_AHB1PeriphClockCmd(RCC_ALL_LED, ENABLE);
//3.填充结构体数据
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; /* 设为输出口 */
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; /* 设为推挽模式 */
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; /* 上下拉电阻不使能 */
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; /* IO口最大速度 */
GPIO_InitStructure.GPIO_Pin = GPIO_PIN_LED1;
//4.完成初始化
GPIO_Init(GPIO_PORT_LED1, &GPIO_InitStructure);
输出控制
//库函数:
GPIO_SetBits (GPIOx, GPIO_Pin)
GPIO_ResetBits (GPIOx, GPIO_Pin)
GPIO_ToggleBits(GPIOx, GPIO_Pin)
//寄存器模式:
GPIOx->BSRRL = GPIO_Pin; //置高
GPIOx->BSRRH = GPIO_Pin; //置低
GPIOx->ODR ^= GPIO_Pin; //取反
输出控制-位带操作
// 把“位带地址+位序号”转换成别名地址的宏
#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x02000000+((addr & 0x000FFFFF)<<5)+(bitnum<<2))
// 把一个地址转换成一个指针
#define MEM_ADDR(addr) *((volatile unsigned long *)(addr))
// 把位带别名区地址转换成指针
#define BIT_ADDR(addr, bitnum) MEM_ADDR(BITBAND(addr, bitnum))
#define GPIOA_ODR_Addr (GPIOA_BASE+20)
#define GPIOA_IDR_Addr (GPIOA_BASE+16)
// 单独操作 GPIO的某一个IO口,n(0,1,2...16),n表示具体是哪一个IO口
#define PAout(n) BIT_ADDR(GPIOA_ODR_Addr,n) //输出
#define PAin(n) BIT_ADDR(GPIOA_IDR_Addr,n) //输入
位带操作的原理概括讲就是讲一位膨胀为32位
也即4个字节,其中有效的位为最低位。
操作时输出:
PAout(1) = 1;
输入:
re = PAin(1) ;
GPIO初始化
GPIO_InitTypeDef GPIO_InitStructure;
/* 第1步:打开GPIO时钟 */
RCC_AHB1PeriphClockCmd(RCC_ALL_KEY, ENABLE);
/* 第2步:配置所有的按键GPIO为浮动输入模式*/
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN; /* 设为输入口 */
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;/* 设为推挽模式 */
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;/* 无需上下拉电阻 */
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;/* IO口最大速度 */
GPIO_InitStructure.GPIO_Pin = GPIO_PIN_K1;
GPIO_Init(GPIO_PORT_K1, &GPIO_InitStructure);
//库函数:
GPIO_ReadInputDataBit
(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
//寄存器:
if((GPIOx->IDR & GPIO_Pin) == 0)
//按键对应端口==低电平