<p><iframe name="ifd" src="https://mnifdv.cn/resource/cnblogs/LearnCH32V307VCT6" frameborder="0" scrolling="auto" width="100%" height="1500"></iframe></p>
<iframe frameborder="0" height="1500" name="ifd" scrolling="auto" src="https://mnifdv.cn/resource/cnblogs/LearnCH32V307VCT6" width="100%"></iframe>
#include "debug.h" #include "ch32v30x.h" #define GPIO_PORT (GPIOD) #define GPIO_PIN (GPIO_Pin_3) #define GPIO_SET (GPIO_SetBits(GPIO_PORT, GPIO_PIN)) //输出高电平 #define GPIO_RESET (GPIO_ResetBits(GPIO_PORT, GPIO_PIN)) //输出低电平 #define GPIO_INPUT (GPIO_ReadOutputDataBit(GPIO_PORT, GPIO_PIN)) //获取输入电平 #define GPIO_TOGGLE (GPIO_WriteBit(GPIO_PORT, GPIO_PIN, 1-GPIO_INPUT)) //输出翻转 #define GPIO_RCC_ENADLE (RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD, ENABLE)) //打开时钟线 /** * @brief init * @param * @param None * @param None * @retval None * @example **/ void gpio_init(void) { GPIO_InitTypeDef GPIO_InitStructure; GPIO_RCC_ENADLE; //启动GPIO的时钟线,让时钟进去以驱动其GPIO GPIO_InitStructure.GPIO_Pin = GPIO_PIN;//pin GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出(最大驱动能力输出) GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//频率越高,切换GPIO高低电平时间越短 GPIO_Init(GPIO_PORT, &GPIO_InitStructure); } int main(void) { NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); USART_Printf_Init(115200); Delay_Init(); gpio_init(); while(1) { GPIO_SET;//设置GPIO输出高电平 printf("GPIO_SET:%d\r\n", GPIO_INPUT);//打印GPIO电平状态 Delay_Ms(500); GPIO_RESET;//设置GPIO输出低电平 printf("GPIO_RESET:%d\r\n", GPIO_INPUT);//打印GPIO电平状态 Delay_Ms(500); } }
#include "debug.h" #include "ch32v30x.h" #define GPIO_PORT (GPIOA) #define GPIO_PIN (GPIO_Pin_0) #define GPIO_INPUT (GPIO_ReadInputDataBit(GPIO_PORT, GPIO_PIN)) //获取输入电平 #define GPIO_RCC_ENADLE (RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE)) //打开时钟线 /** * @brief init * @param * @param None * @param None * @retval None * @example **/ void gpio_init(void) { GPIO_InitTypeDef GPIO_InitStructure; GPIO_RCC_ENADLE; //启动GPIO的时钟线,让时钟进去以驱动其GPIO GPIO_InitStructure.GPIO_Pin = GPIO_PIN;//pin GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //上拉输入 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//设不设置都可以 GPIO_Init(GPIO_PORT, &GPIO_InitStructure); } int main(void) { NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); USART_Printf_Init(115200); Delay_Init(); gpio_init(); while(1) { printf("GPIO_INPUT:%d\r\n", GPIO_INPUT);//打印GPIO电平状态 Delay_Ms(500); } }
#include "debug.h" #include "ch32v30x.h" /*在中断里面调用的变量需要使用 volatile 修饰*/ volatile uint8_t gpio_interrupt_flag=0; /** * @brief init * @param None * @retval None * @example **/ void gpio_init(void) { GPIO_InitTypeDef GPIO_InitStructure = {0}; EXTI_InitTypeDef EXTI_InitStructure = {0}; NVIC_InitTypeDef NVIC_InitStructure = {0}; /*打开挂载GPIO总线APB2的复用时钟(GPIO除了输入输出的其它功能都叫做复用功能,所以需要打开复用时钟); 打开GPIOA时钟线*/ RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOA, ENABLE); /*设置GPIO*/ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;//pin GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //上拉输入 GPIO_Init(GPIOA, &GPIO_InitStructure); /*设置PA0作为中断线的GPIO引脚*/ GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0); /*设置GPIO中断*/ EXTI_InitStructure.EXTI_Line = EXTI_Line0;//中断线0 EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;//中断模式 EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;//下降沿触发 EXTI_InitStructure.EXTI_LineCmd = ENABLE;//使能 EXTI_Init(&EXTI_InitStructure); /*配置中断优先等级*/ NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;//外部中断0 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;//抢占式优先级 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;//响应式优先级 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//使能 NVIC_Init(&NVIC_InitStructure); } /** * @brief 中断函数 **/ __attribute__((interrupt("WCH-Interrupt-fast"))) //中断函数前加这上这句,告诉编译器这个是中断函数 void EXTI0_IRQHandler(void) { if(EXTI_GetITStatus(EXTI_Line0)!=RESET)//产生中断 { gpio_interrupt_flag=1; EXTI_ClearITPendingBit(EXTI_Line0);//清除中断标志 } } int main(void) { NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置优先级分组为2 USART_Printf_Init(115200); Delay_Init(); gpio_init(); while(1) { if (gpio_interrupt_flag==1) {//有中断产生 gpio_interrupt_flag=0; printf("gpio_interrupt_flag\r\n"); } } }
前面有提到单片机有抢占式和响应式优先级, 优先级是由一个四位的二进制数保存的,
四位的二进制数共有 2^4 = 16种, 也就是 0000 - 1111 , 就是有 0 - 15 等级
但是呢这是只存在一种优先等级的情况下, 可以有0 - 15 等级
上面的四位共同代表了抢占式和响应式优先级; 具体怎么分抢占式和响应式各有多少个;
就是下面的优先级分组来设置
假设设置优先级分组为 0 (NVIC_PriorityGroup_0) 那么就是没有抢占式,上面的四位全部作为响应式
那么咱在设置中断的时候,抢占式就不用设置了 , 响应式就是有(0-15) 选择
假设设置优先级分组为 1 (NVIC_PriorityGroup_1) 那么就是其中一位给抢占式,剩余3位作为响应式
那么咱在设置中断的时候,抢占式就是0 - 1 选择 , 响应式就是有(0-8) 选择
假设设置优先级分组为 2 (NVIC_PriorityGroup_2) 那么就是其中两位给抢占式, 其中两位作为响应式
那么咱在设置中断的时候,抢占式就是0 - 3 选择 , 响应式就是有(0-3) 选择
假设设置优先级分组为 3 (NVIC_PriorityGroup_3) 那么就是其中三位给抢占式, 其中一位作为响应式
那么咱在设置中断的时候,抢占式就是0 - 8 选择 , 响应式就是有(0-1) 选择
假设设置优先级分组为 4 (NVIC_PriorityGroup_4) 那么就是其中四位给抢占式, 没有响应式
那么咱在设置中断的时候,抢占式就是0 - 15 选择 , 响应式就不用设置了
#include "debug.h" #include "ch32v30x.h" /*在中断里面调用的变量需要使用 volatile 修饰*/ volatile uint8_t gpio_interrupt_flag=0; /** * @brief init * @param None * @retval None * @example **/ void gpio_init(void) { GPIO_InitTypeDef GPIO_InitStructure = {0}; EXTI_InitTypeDef EXTI_InitStructure = {0}; NVIC_InitTypeDef NVIC_InitStructure = {0}; /*打开挂载GPIO总线的复用时钟(GPIO除了输入输出的其它功能都叫做复用功能,所以需要打开复用时钟); 打开GPIOA时钟线*/ RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOA, ENABLE); /*设置GPIO*/ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;//pin GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //上拉输入 GPIO_Init(GPIOA, &GPIO_InitStructure); /*设置PA0作为中断线的GPIO引脚*/ GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0); /*设置GPIO中断*/ EXTI_InitStructure.EXTI_Line = EXTI_Line0;//中断线0 EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;//中断模式 EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;//下降沿触发 EXTI_InitStructure.EXTI_LineCmd = ENABLE;//使能 EXTI_Init(&EXTI_InitStructure); /*配置中断优先等级*/ NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;//外部中断0 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;//抢占式优先级 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;//响应式优先级 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//使能 NVIC_Init(&NVIC_InitStructure); /*打开挂载GPIO总线的复用时钟(GPIO除了输入输出的其它功能都叫做复用功能,所以需要打开复用时钟); 打开GPIOA时钟线*/ RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOB, ENABLE); /*设置GPIO*/ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;//pin GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //下拉 GPIO_Init(GPIOB, &GPIO_InitStructure); /*设置PB2作为中断线的GPIO引脚*/ GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource2); /*设置GPIO中断*/ EXTI_InitStructure.EXTI_Line = EXTI_Line2;//中断线 EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;//中断模式 EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;//上升沿触发 EXTI_InitStructure.EXTI_LineCmd = ENABLE;//使能 EXTI_Init(&EXTI_InitStructure); /*配置中断优先等级*/ NVIC_InitStructure.NVIC_IRQChannel = EXTI2_IRQn;//外部中断 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;//抢占式优先级 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;//响应式优先级 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//使能 NVIC_Init(&NVIC_InitStructure); } /** * @brief 中断函数 **/ __attribute__((interrupt("WCH-Interrupt-fast"))) //中断函数前加这上这句,告诉编译器这个是中断函数 void EXTI2_IRQHandler(void) { if(EXTI_GetITStatus(EXTI_Line2)!=RESET)//产生中断 { EXTI_ClearITPendingBit(EXTI_Line2);//清除中断标志 } } /** * @brief 中断函数 **/ __attribute__((interrupt("WCH-Interrupt-fast"))) //中断函数前加这上这句,告诉编译器这个是中断函数 void EXTI0_IRQHandler(void) { if(EXTI_GetITStatus(EXTI_Line0)!=RESET)//产生中断 { gpio_interrupt_flag=1; EXTI_ClearITPendingBit(EXTI_Line0);//清除中断标志 } } int main(void) { NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置优先级分组为2 USART_Printf_Init(115200); Delay_Init(); gpio_init(); while(1) { if (gpio_interrupt_flag==1) {//有中断产生 gpio_interrupt_flag=0; printf("gpio_interrupt_flag\r\n"); } } }
外部中断5-9共用一个中断;
#include "debug.h" #include "ch32v30x.h" /** * @brief init * @param None * @retval None * @example **/ void gpio_init(void) { GPIO_InitTypeDef GPIO_InitStructure = {0}; EXTI_InitTypeDef EXTI_InitStructure = {0}; NVIC_InitTypeDef NVIC_InitStructure = {0}; /*打开挂载GPIO总线的复用时钟(GPIO除了输入输出的其它功能都叫做复用功能,所以需要打开复用时钟); 打开GPIOA时钟线*/ RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOB, ENABLE); /*设置GPIO*/ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;//pin GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //下拉 GPIO_Init(GPIOB, &GPIO_InitStructure); /*设置GPIO*/ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;//pin GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //下拉 GPIO_Init(GPIOB, &GPIO_InitStructure); /*设置作为中断线的GPIO引脚*/ GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource6); GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource7); /*设置GPIO中断*/ EXTI_InitStructure.EXTI_Line = EXTI_Line6;//中断线 EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;//中断模式 EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;//上升沿触发 EXTI_InitStructure.EXTI_LineCmd = ENABLE;//使能 EXTI_Init(&EXTI_InitStructure); /*设置GPIO中断*/ EXTI_InitStructure.EXTI_Line = EXTI_Line7;//中断线 EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;//中断模式 EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;//上升沿触发 EXTI_InitStructure.EXTI_LineCmd = ENABLE;//使能 EXTI_Init(&EXTI_InitStructure); /*配置中断优先等级*/ NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQn;//外部中断 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;//抢占式优先级 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;//响应式优先级 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//使能 NVIC_Init(&NVIC_InitStructure); } /** * @brief 中断函数 **/ __attribute__((interrupt("WCH-Interrupt-fast"))) //中断函数前加这上这句,告诉编译器这个是中断函数 void EXTI9_5_IRQHandler(void) { if(EXTI_GetITStatus(EXTI_Line6)!=RESET)//产生中断 { printf("666666666666\r\n"); EXTI_ClearITPendingBit(EXTI_Line6);//清除中断标志 } else if(EXTI_GetITStatus(EXTI_Line7)!=RESET)//产生中断 { printf("777777777777\r\n"); EXTI_ClearITPendingBit(EXTI_Line7);//清除中断标志 } } int main(void) { NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置优先级分组为2 USART_Printf_Init(115200); Delay_Init(); gpio_init(); while(1) { } }
外部中断10-15共用一个中断;
#include "debug.h" #include "ch32v30x.h" /** * @brief init * @param None * @retval None * @example **/ void gpio_init(void) { GPIO_InitTypeDef GPIO_InitStructure = {0}; EXTI_InitTypeDef EXTI_InitStructure = {0}; NVIC_InitTypeDef NVIC_InitStructure = {0}; /*打开挂载GPIO总线的复用时钟(GPIO除了输入输出的其它功能都叫做复用功能,所以需要打开复用时钟); 打开GPIOA时钟线*/ RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOB, ENABLE); /*设置GPIO*/ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//pin GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //下拉 GPIO_Init(GPIOB, &GPIO_InitStructure); /*设置GPIO*/ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;//pin GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //上拉 GPIO_Init(GPIOB, &GPIO_InitStructure); /*设置作为中断线的GPIO引脚*/ GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource10); GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource11); /*设置GPIO中断*/ EXTI_InitStructure.EXTI_Line = EXTI_Line10;//中断线 EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;//中断模式 EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;//上升沿触发 EXTI_InitStructure.EXTI_LineCmd = ENABLE;//使能 EXTI_Init(&EXTI_InitStructure); /*设置GPIO中断*/ EXTI_InitStructure.EXTI_Line = EXTI_Line11;//中断线 EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;//中断模式 EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;//下降沿触发 EXTI_InitStructure.EXTI_LineCmd = ENABLE;//使能 EXTI_Init(&EXTI_InitStructure); /*配置中断优先等级*/ NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn;//外部中断 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;//抢占式优先级 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;//响应式优先级 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//使能 NVIC_Init(&NVIC_InitStructure); } /** * @brief 中断函数 **/ __attribute__((interrupt("WCH-Interrupt-fast"))) //中断函数前加这上这句,告诉编译器这个是中断函数 void EXTI15_10_IRQHandler(void) { if(EXTI_GetITStatus(EXTI_Line10)!=RESET)//产生中断 { printf("00000000000000000\r\n"); EXTI_ClearITPendingBit(EXTI_Line10);//清除中断标志 } else if(EXTI_GetITStatus(EXTI_Line11)!=RESET)//产生中断 { printf("1111111111\r\n"); EXTI_ClearITPendingBit(EXTI_Line11);//清除中断标志 } } int main(void) { NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置优先级分组为2 USART_Printf_Init(115200); Delay_Init(); gpio_init(); while(1) { } }