GPIO
<1>说明
· 引脚电平:0~3.3V,部分可达到5V(参考引脚定义,带FT的就可以)
同时 GPIO有两个模式
输出模式:进行端口高低电平的输出,用于驱动LED,蜂鸣器等
输入模式:读取端口高低电平的电压/电平,用于按键输入,ADC电压采集等
<2>基本结构
APB2外设时钟总线,所有的GPIO都是挂载再APB2上的,其中GPIO的名称是按照GPIO A/B/C来命名的,每个外设都有15个引脚,编号为0~15
<3>GPIO模式
GPIO库函数编码
(1)使用RCC开启GPIO的时钟
(2)使用GPIO_Init()初始化GPIO
(3)使用输出/输入函数控制GPIO口
一个涉及到RCC和GPIO两个外设
1.常用的RCC外设时钟控制函数 AHB/APB2/APB1(最主要就这三个,其他几乎不用)
void RCC_AHBPeriphClockCmd(uint32_t RCC_AHBPeriph, FunctionalState NewState);
void RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState);
void RCC_APB1PeriphClockCmd(uint32_t RCC_APB1Periph, FunctionalState NewState);
2.GPIO库函数
// 参数写入GPIOX,所指定的GPIO外设就会被复位
void GPIO_DeInit(GPIO_TypeDef* GPIOx);
//可以复位AFIO外设
void GPIO_AFIODeInit(void);
//GPIO初始化函数,我们需要先定义一个结构体变量,然后赋值,最后调用这个函数
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);
//可以把结构体变量变成一个默认值
void GPIO_StructInit(GPIO_InitTypeDef* GPIO_InitStruct);
/***********下面四个为GPIO的读取函数********/
uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
uint16_t GPIO_ReadInputData(GPIO_TypeDef* GPIOx);
uint8_t GPIO_ReadOutputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
uint16_t GPIO_ReadOutputData(GPIO_TypeDef* GPIOx);
/*************读写GPIO口的函数**************/
//设置指定的端口为高电平
void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
//设定指定的端口为低电平
void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
//前面的参数为选择端口,最后为指定写入数据值(Bit_SET / Bit_RESET)
void GPIO_WriteBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, BitAction BitVal);
void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal);
//剩下的这些我们现在还不会用到
//锁定GPIO配置
void GPIO_PinLockConfig(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
//配置AFIO的事件输出功能函数
void GPIO_EventOutputConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource);
void GPIO_EventOutputCmd(FunctionalState NewState);
//配置引脚重映射函数
void GPIO_PinRemapConfig(uint32_t GPIO_Remap, FunctionalState NewState);
//EXTI GPIO开启
void GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource);
void GPIO_ETH_MediaInterfaceConfig(uint32_t GPIO_ETH_MediaInterface);
3,开启流程(GPIO初始化)
//1.开启RCC外设
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA /*uint32_t RCC_APB2Periph*/, ENABLE);
//2.开启GPIO
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //选择模式
/*AIN模式输入,IN_FLOATING浮空输入,IPD下拉输入,IPU上拉输入
OUT_OD开漏输出(低电平有驱动力,高电平没有),PUT_PP推挽输出,AF则为复用 */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; //选择引脚
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//选择频率
GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化
EXTI中断函数
<1>中断
中断 : 在主程序运行过程中,出现特定的中断触发条件,使得CPU暂停当前正在运行的程序,而去处理中断程序,完成后,又返回原来被暂停的位置继续工作
中断优先 : 当有多个中断开始时,CPU会根据事情的轻重响应更加紧急的中断
中断嵌套 : 一个中断正常进行,又来一个更高级的中断,会先去做刚来的高级的中断,然后依次返回
<2>NVIC
68个可屏蔽中断通道,包含EXTI,TIM,ADC,USART,SPI,IIC,RTC等多个外设
NVIC:NVIC的中断优先级由优先级寄存器的4位(0~15)决定,这4位可以进行切分,分为高n位的抢占优先级和低4-n位的响应优先级
抢占优先级高的可以进行中断嵌套,响应优先级高的可以进行优先排队,抢占优先级和响应优先级均相同的按中断号排队
<3>EXTI:(Extern Interrupt)外部中断
EXTI可以检测指定GPIO口的电平信号,当其指定的GPIO口产生电平变化时,EXTI将立即向NVIC发出中断申请,经过NVIC裁决后即可中断CPU主程序,使CPU执行EXTI对应的中断程序
支持的触发方式:上升沿/下降沿/双边沿/软件触发
支持的GPIO口:所有GPIO口,但相同的Pin不能同时触发中断
通道数:16个GPIO_Pin,外加PVD输出、RTC闹钟、USB唤醒、以太网唤醒
触发响应方式:中断响应/事件响应
AFIO选择中断引脚,外部中断的9-5,15-10会触发同一个中断函数,再根据标志位来区分到底是哪个中断进来的
配置数据选择器,只有一个Pin接到EXTI
<4>配置步骤:
第一步,配置RCC,把设计到的外设时钟都打开
第二步,配置GPIO,选择端口为输入模式
第三步,配置AFIO,选择使用的一路GPIO,连接到后面的EXTI
第四步,配置EXTI,选择边沿触发方式,选择触发响应方式
第五步,配置NVIC,给中断选择一个合适的优先级
<5>库函数说明
首先,EXTI本身是不需要打开什么时钟的,所以前面正常打开GPIO就可以了
AFIO也使用RCC使能
void GPIO_EXTILineConfig(GPIO_PortSourceGPIOB /*引脚所在的源*/,GPIO_PinSource14 /*第14个中断线路);
这个函数,可以让14号引脚的电平顺利通过AFIO,进入到后级EXTI电路中
EXTI库函数:
//调用他,可以清除EXTI的配置,恢复为默认的上电状态
void EXTI_DeInit(void);
//初始化EXTI,方法与GPIO相同
void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct);
//参数传递的结构体变量赋一个默认值
void EXTI_StructInit(EXTI_InitTypeDef* EXTI_InitStruct);
//软件触发外部中断
void EXTI_GenerateSWInterrupt(uint32_t EXTI_Line);
//下面为模板函数
//读写中断标志位
FlagStatus EXTI_GetFlagStatus(uint32_t EXTI_Line);
//清楚中断标志
void EXTI_ClearFlag(uint32_t EXTI_Line);
//只能读写中断标志位
ITStatus EXTI_GetITStatus(uint32_t EXTI_Line);
void EXTI_ClearITPendingBit(uint32_t EXTI_Line);
EXTI_Init参数
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource0);//选择中断线路
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource1);///选择自己的引脚相同线路就好
EXTI_InitTypeDef EXTI_InitStructure;//定义外部中断结构体
EXTI_InitStructure.EXTI_Line = EXTI_Line0 | EXTI_Line1;//设置中断线
EXTI_InitStructure.EXTI_LineCmd = ENABLE;//开启中断线路
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;//中断模式
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;//下降沿触发
EXTI_Init(&EXTI_InitStructure);//写入参数
NVIC内核外设库函数
//中断分组,参数是中断分组的方式
void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup);
// NVIC初始化函数,根据结构体参数初始化
void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct);
void NVIC_SetVectorTable(uint32_t NVIC_VectTab, uint32_t Offset);
//系统低功耗配置
void NVIC_SystemLPConfig(uint8_t LowPowerMode, FunctionalState NewState);
void SysTick_CLKSourceConfig(uint32_t SysTick_CLKSource);
分组在整个流程中只要进行一次就可以了,如果模块化要注意选择是同一个,可以选择放在主函数里
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//中断优先级分组
NVIC结构体初始化内容
//我们选择了两个分组,那么我们就配置两个
NVIC_InitTypeDef NVIC_InitStructure;//定义NVIC结构体
NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;//设置中断通道
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//通道使能
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;//抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;//响应优先级
NVIC_Init(&NVIC_InitStructure);//写入参数
NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn;//设置中断通道
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//通道使能
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;//抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;//响应优先级
NVIC_Init(&NVIC_InitStructure);//写入参数
中断服务函数
在STM32中,每个通道的中断服务函数名字都是固定的
void EXTI0_IRQHandler(void)//线路0中断函数
void EXTI1_IRQHandler(void)//线路1中断函数
获取标志位
if (EXTI_GetITStatus(EXTI_Line0) == SET)//判断中断挂起位
服务函数结束后清除标志位
EXTI_ClearITPendingBit(EXTI_Line0);//清除中断挂起标志位
定时器
TIM(Timer)定时器
定时器可以对输入的时钟进行计数,并在计数值达到设定值时触发中断
16位计数器、预分频、自动重装寄存器的时基单元,在72M计数时钟下可以实现最大59.65s的定时
不仅具备基本的定时器中断功能,而且还包含内外时钟源选择、输入捕获、输出比较、编码器接口、主从触发模式等多种功能
根据复杂度和应用场景分为了高级定时器、通用定时器、基本定时器三种类型
对72MHz计72个数就是1MHz,也就是1us的时间,计72000个数,那就是1KHz也就是1ms的时间
59.65s =65536 · 65536 · 1/72M/(中断频率倒数),
STM32的定时器支持级联的模式:一个定时器的输出当做另一个定时器的输入最大定时时间就是59.65s · 65536 · 65536
定时器资源:
而STM32F103C8T6有TIM1/2/3/4四个定时器
· 预分频器(PSC):对输入的基准频率提前进行一个分频的操作
实际分频系数 = 预分频器的值 + 1,最大可以写65535即65536分频
· 计数器(CNT):也是16位,值可以从0~65535,当计数器的值自增(自减)到目标值时,产生中断,完成定时
· 自动重装寄存器():也是16位当计数值等于自动重装值时,就是计时的时间到了,就会产生一个中断信号,并且清零计数器,计数器自动开始下一次的计数计时,计数值等于自动重装值的中断一般叫做“更新中断”,此更新中断就会通往NVIC,再配置好NVIC的定时器通道,定时器上的更新中断就会得到CPU的响应了,对应的事件叫做“更新事件”,更新事件不会触发中断,但可以触发内部其他电路的工作
基本结构