STM32-GPIO_EXTI_TIM
一、GPIO
1、GPIO基本结构
2、GPIO模式
通过配置GPIO的端口配置寄存器,端口可以配置成以下8种模式
模式名称 |
性质 |
特征 |
浮空输入 |
数字输入 |
可读取引脚电平,若引脚悬空,则电平不确定 |
上拉输入 |
数字输入 |
可读取引脚电平,内部连接上拉电阻,悬空时默认高电平 |
下拉输入 |
数字输入 |
可读取引脚电平,内部连接下拉电阻,悬空时默认低电平 |
模拟输入 |
模拟输入 |
GPIO无效,引脚直接接入内部ADC |
开漏输出 |
数字输出 |
可输出引脚电平,高电平为高阻态,低电平接VSS |
推挽输出 |
数字输出 |
可输出引脚电平,高电平接VDD,低电平接VSS |
复用开漏输出 |
数字输出 |
由片上外设控制,高电平为高阻态,低电平接VSS |
复用推挽输出 |
数字输出 |
由片上外设控制,高电平接VDD,低电平接VSS |
3、配置流程(点亮LED)
- 初始化GPIO对应的外设时钟RCC
- 初始化GPIO的基础配置(端口复用、对应引脚,传输速度、输入输出模式等等)
- 根据原理图,决定引脚的电平状态
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| /* GPIOD Periph clock enable */ RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
/*Configure PB0 、PB1and PB5 in output pushpull mode*/ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_5; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_Init(GPIOB, &GPIO_InitStructure);
while (1) { printf("Green Led is coming!\n"); GPIO_ResetBits(GPIOB, GPIO_Pin_0); //Ì Delay(10); GPIO_SetBits(GPIOB, GPIO_Pin_0); Delay(10);
printf("Blue Led is coming!\n"); GPIO_ResetBits(GPIOB, GPIO_Pin_1); //6 Delay(10); GPIO_SetBits(GPIOB, GPIO_Pin_1); Delay(10); printf("Red Led is coming!\n"); GPIO_ResetBits(GPIOB, GPIO_Pin_5); //۬ Delay(10); GPIO_SetBits(GPIOB, GPIO_Pin_5); Delay(10); }
//LED状态翻转 // R-红色 #define LED1_GPIO_PORT GPIOB #define LED1_GPIO_CLK RCC_APB2Periph_GPIOB #define LED1_GPIO_PIN GPIO_Pin_5
// G-绿色 #define LED2_GPIO_PORT GPIOB #define LED2_GPIO_CLK RCC_APB2Periph_GPIOB #define LED2_GPIO_PIN GPIO_Pin_0
// B-蓝色 #define LED3_GPIO_PORT GPIOB #define LED3_GPIO_CLK RCC_APB2Periph_GPIOB #define LED3_GPIO_PIN GPIO_Pin_1
/* 直接操作寄存器的方法控制 IO */ #define digitalToggle(p,i) {p->ODR ^=i;} //输出反转状态
/* 定义控制 IO 的宏 */ #define LED1_TOGGLE digitalToggle(LED1_GPIO_PORT,LED1_GPIO_PIN) #define LED2_TOGGLE digitalToggle(LED2_GPIO_PORT,LED2_GPIO_PIN) #define LED3_TOGGLE digitalToggle(LED3_GPIO_PORT,LED3_GPIO_PIN)
|
4、复用功能AFIO
二、EXTI
1、中断系统
中断:在主程序运行过程中,出现了特定的中断触发条件(中断源),使得CPU暂停当前正在运行的程序,转而去处理中断程序,处理完成后又返回原来被暂停的位置继续运行
中断优先级:当有多个中断源同时申请中断时,CPU会根据中断源的轻重缓急进行裁决,优先响应更加紧急的中断源
中断嵌套:当一个中断程序正在运行时,又有新的更高优先级的中断源申请中断,CPU再次暂停当前中断程序,转而去处理新的中断程序,处理完成后依次进行返回
中断数量:68个可屏蔽中断通道,包含EXTI、TIM、ADC、USART、SPI、I2C、RTC等多个外设
中断管理:使用NVIC统一管理中断,每个中断通道都拥有16个可编程的优先等级,可对优先级进行分组,进一步设置抢占优先级和响应优先级
2、中断控制器(NVIC)
1、NVIC基本结构
2、NVIC的作用
- NVIC是用来统一分配中断优先级和管理中断的
- NVIC是内核外设(不需要初始化外设时钟RCC)
3、外部中断/事件控制器(EXTI)
1、EXTI的作用:EXTI可以监测指定GPIO口的电平信号,当其指定的GPIO口产生电平变化时,EXTI将立即向NVIC发出中断申请,经过NVIC裁决后即可中断CPU主程序,使CPU执行EXTI对应的中断程序
2、EXTI支持的触发方式:上升沿/下降沿/双边沿/软件触发
3、通道数:16个GPIO_Pin,外加PVD输出、RTC闹钟、USB唤醒、以太网唤醒
4、EXTI基本结构
- 每个GPIOX都有16个引脚
- 通过AFIO中断引脚选择功能,将GPIOA触发中断的引脚对应到EXTI的GPIO_pin通道(GPIOA_PIn1—>EXTI GPIO_pin1)
- 其中,中断5-9和10-15属于同一根触发线
4、配置流程
按键作为中断源,触发一次LED灯状态转变
- 开启案按键对应的GPIO的RCC时钟
- 配置NVIC中断控制器结构体NVIC_InitTypeDef
- 配置按键对应的GPIO的输入模式
- 配置EXTI结构体EXTI_InitTypeDef
- 注册EXTI中断服务函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
| // 引脚定义
// R-红色 #define LED1_GPIO_PORT GPIOB #define LED1_GPIO_CLK RCC_APB2Periph_GPIOB #define LED1_GPIO_PIN GPIO_Pin_5
// G-绿色 #define LED2_GPIO_PORT GPIOB #define LED2_GPIO_CLK RCC_APB2Periph_GPIOB #define LED2_GPIO_PIN GPIO_Pin_0
// B-蓝色 #define LED3_GPIO_PORT GPIOB #define LED3_GPIO_CLK RCC_APB2Periph_GPIOB #define LED3_GPIO_PIN GPIO_Pin_1
/* 直接操作寄存器的方法控制 IO */ #define digitalToggle(p,i) {p->ODR ^=i;} //输出反转状态
/* 定义控制 IO 的宏 */ #define LED1_TOGGLE digitalToggle(LED1_GPIO_PORT,LED1_GPIO_PIN) #define LED2_TOGGLE digitalToggle(LED2_GPIO_PORT,LED2_GPIO_PIN) #define LED3_TOGGLE digitalToggle(LED3_GPIO_PORT,LED3_GPIO_PIN)
//初始化中断控制器,配置优先级 static void NVIC_Configuration(void) { NVIC_InitTypeDef NVIC_InitStructure;
/* 配置 NVIC 为优先级组 1 */ NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
/* 配置中断源:按键 1 */ NVIC_InitStructure.NVIC_IRQChannel = KEY1_INT_EXTI_IRQ; /* 配置抢占优先级:1 */ NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; /* 配置子优先级:1 */ NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; /* 使能中断通道 */ NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); }
//初始化EXTI中断配置 void EXTI_Key_Config(void) { GPIO_InitTypeDef GPIO_InitStructure; EXTI_InitTypeDef EXTI_InitStructure;
/* 开启按键 GPIO 口的时钟 */ RCC_APB2PeriphClockCmd(KEY1_INT_GPIO_CLK,ENABLE);
/* 配置 NVIC 中断 */ NVIC_Configuration();
/*--------------------------KEY1 配置---------------------*/ /* 选择按键用到的 GPIO */ GPIO_InitStructure.GPIO_Pin = KEY1_INT_GPIO_PIN; /* 配置为浮空输入 */ GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(KEY1_INT_GPIO_PORT, &GPIO_InitStructure);
/* 选择 EXTI 的信号源 */ GPIO_EXTILineConfig(KEY1_INT_EXTI_PORTSOURCE, \ KEY1_INT_EXTI_PINSOURCE); EXTI_InitStructure.EXTI_Line = KEY1_INT_EXTI_LINE;
/* EXTI 为中断模式 */ EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; /* 上升沿中断 */ EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising; /* 使能中断 */ EXTI_InitStructure.EXTI_LineCmd = ENABLE; EXTI_Init(&EXTI_InitStructure);
}
//注册中断服务函数 void KEY1_IRQHandler(void) { //确保是否产生了 EXTI Line 中断 if (EXTI_GetITStatus(KEY1_INT_EXTI_LINE) != RESET) { // LED1 取反 LED1_TOGGLE; //清除中断标志位 EXTI_ClearITPendingBit(KEY1_INT_EXTI_LINE); } }
//主函数 int main(void) { /* LED 端口初始化 */ LED_GPIO_Config();
/* 初始化 EXTI 中断,按下按键会触发中断, * 触发中断会进入 stm32f10x_it.c 文件中的函数 * KEY1_IRQHandler 和 KEY2_IRQHandler,处理中断,反转 LED 灯。 */ EXTI_Key_Config();
/* 等待中断,由于使用中断方式,CPU 不用轮询按键 */ while (1) { } }
|
三、TIM
1、定时器简介
- 可以对输入的时钟进行计数,并在计数值达到设定值时触发中断
- 16位计数器、预分频器、自动重装寄存器的时基单元,在72MHz计数时钟下可以实现最大59.65s的定时(计数器:执行计数定时的寄存器,每来一个时钟,计数器加一,预分频器:对计数的时钟进行分频,让计数更加灵活,自动重装寄存器:计数的目标值,就是记多少个数产生中断)
2、定时器分类
类型 |
编号 |
总线 |
功能 |
高级定时器 |
TIM1、TIM8 |
APB2 |
拥有通用定时器全部功能,并额外具有重复计数器、死区生成、互补输出、刹车输入等功能 |
通用定时器 |
TIM2、TIM3、TIM4、TIM5 |
APB1 |
拥有基本定时器全部功能,并额外具有内外时钟源选择、输入捕获、输出比较、编码器接口、主从触发模式等功能 |
基本定时器 |
TIM6、TIM7 |
APB1 |
拥有定时中断、主模式触发DAC的功能 |
3、定时中断基本结构
- RCC、ETR、ITRx和TIx:是定时器的可选时钟源
- 时基单元:决定了计数的次数、时间和重装值
- 运行控制:决定了定时器的触发方式
- 中断输出控制:表示的是定时中断触发的流程
4、定时LED翻转
4.1、配置流程
- 开启定时器TIM3外设时钟APB1
- 初始化时基单元TIM_TimeBaseStructure结构体(TIM_TimeBaseInitTypeDef)
- 定时器中断优先级配置
- 编写定时器中断服务函数
4.2、代码实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138
| // 引脚定义
// R-红色 #define LED1_GPIO_PORT GPIOB #define LED1_GPIO_CLK RCC_APB2Periph_GPIOB #define LED1_GPIO_PIN GPIO_Pin_5
// G-绿色 #define LED2_GPIO_PORT GPIOB #define LED2_GPIO_CLK RCC_APB2Periph_GPIOB #define LED2_GPIO_PIN GPIO_Pin_0
// B-蓝色 #define LED3_GPIO_PORT GPIOB #define LED3_GPIO_CLK RCC_APB2Periph_GPIOB #define LED3_GPIO_PIN GPIO_Pin_1
/* 直接操作寄存器的方法控制 IO */ #define digitalToggle(p,i) {p->ODR ^=i;} //输出反转状态
/* 定义控制 IO 的宏 */ #define LED1_TOGGLE digitalToggle(LED1_GPIO_PORT,LED1_GPIO_PIN) #define LED2_TOGGLE digitalToggle(LED2_GPIO_PORT,LED2_GPIO_PIN) #define LED3_TOGGLE digitalToggle(LED3_GPIO_PORT,LED3_GPIO_PIN)
//定时器宏定义 #define BASIC_TIM TIM3 #define BASIC_TIM_APBxClock_FUN RCC_APB1PeriphClockCmd #define BASIC_TIM_CLK RCC_APB1Periph_TIM3 #define BASIC_TIM_IRQ TIM3_IRQn #define BASIC_TIM_IRQHandler TIM3_IRQHandler
//基本定时器模式配置 void BASIC_TIM_Config(void) { TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
// 开启定时器时钟, 即内部时钟 CK_INT=72M BASIC_TIM_APBxClock_FUN(BASIC_TIM_CLK, ENABLE);
// 自动重装载寄存器周的值 (计数值) TIM_TimeBaseStructure.TIM_Period=1000;
// 累计 TIM_Period 个频率后产生一个更新或者中断 // 时钟预分频数为 71,则驱动计数器的时钟 CK_CNT = CK_INT / (71+1)=1M TIM_TimeBaseStructure.TIM_Prescaler= 71;
// 时钟分频因子 ,基本定时器没有,不用管 //TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;
// 计数器计数模式,基本定时器只能向上计数,没有计数模式的设置 //TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;
// 重复计数器的值,基本定时器没有,不用管 //TIM_TimeBaseStructure.TIM_RepetitionCounter=0;
// 初始化定时器 TIM_TimeBaseInit(BASIC_TIM, &TIM_TimeBaseStructure);
// 清除计数器中断标志位 TIM_ClearFlag(BASIC_TIM, TIM_FLAG_Update);
// 开启计数器中断 TIM_ITConfig(BASIC_TIM,TIM_IT_Update,ENABLE); // 使能计数器 TIM_Cmd(BASIC_TIM, ENABLE); // 暂时关闭定时器的时钟,等待使用 //BASIC_TIM_APBxClock_FUN(BASIC_TIM_CLK, DISABLE); }
// 中断优先级配置 void BASIC_TIM_NVIC_Config(void) { NVIC_InitTypeDef NVIC_InitStructure; // 设置中断组为 0 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0); // 设置中断来源 NVIC_InitStructure.NVIC_IRQChannel = BASIC_TIM_IRQ ; // 设置主优先级为 0 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; // 设置抢占优先级为 3 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); }
static uint32_t time=0; static uint32_t LED_FLAG=0;
//定时器中断服务程序 void BASIC_TIM_IRQHandler (void) { if ( TIM_GetITStatus( BASIC_TIM, TIM_IT_Update) != RESET ) { if ( time == 100 ) { /* 1000 * 1 ms = 1s 时间到 */ time = 0; /* LED1 取反 */ LED_FLAG=!LED_FLAG; } time++; printf("current time=%d\n",time); TIM_ClearITPendingBit(BASIC_TIM , TIM_FLAG_Update); } }
int main(void) { //GPIO初始化 /* GPIOD Periph clock enable */ RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
/* Configure PD0 and PD2 in output pushpull mode */ GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;// | GPIO_Pin_1 | GPIO_Pin_5; //0(绿),1(蓝),5(红) GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_Init(GPIOB, &GPIO_InitStructure); /* 基本定时器TIM3定时配置 */ BASIC_TIM_Config(); /* 配置基本定时器TIM3的中断优先级 */ BASIC_TIM_NVIC_Config(); while (1) { if (LED_FLAG) { /* LED1 取反 */ LED1_TOGGLE; } printf("current time=%d\n",time); } }
|
5、