100-CH32V307(WCH单片机)学习开发-GPIO电平检测,引脚中断

<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>

 

在GPIO设置为输出的状态下读取GPIO电平

1,控制PD3 输出高低电平,并打印其引脚状态(把以下程序直接拷贝到自己工程运行)

#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);     } }

 

2.使用数据线连接开发板

 

 

 

 

 

设置PA0为输入上拉状态, 读取PA0状态

#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);     } }

 

注意:设置为输入状态时, 需要使用 GPIO_ReadInputDataBit 函数获取

 

 

 

 

把PA0口接低电平可以看到打印

 

 

 

 

关于中断优先级

在51单片机中只有一种优先等级,默认是(外部中断0, 定时器0, 外部中断1, 定时器1, 串口中断), 优先等级左面最高

当来了外部中断和定时器0中断的时候,优先处理外部中断0;

当定时器0执行的时候来了外部中断0,那么回去执行外部中断0, 然后再接着回来执行定时器0

-------------------------------------------------------------------------

对于CH32V307单片机, 中断有抢占式和响应式两种优先级(数越小越优先)

抢占式是指中断嵌套; 响应式是指中断同时来先执行谁;

抢占式等级相同,谁的响应式高先执行谁; 抢占式等级不同,谁的抢占式等级高先执行谁

假设有两个GPIO中断 PA0 和 PA1

PA0 的抢占式优先等级设置为 0; 响应式优先等级设置为 2;

PA1 的抢占式优先等级设置为 0; 响应式优先等级设置为 1;

假设正在执行的PA0中断, 现在来了PA1中断, 因为二者抢占式等级一样,所以等执行完了PA0 再执行PA1

假设PA0和PA1同时来了中断, 因为PA1的响应式比PA0高,所以先PA1 再执行PA0

----------------------------------------------------------------------------------------------------------

PA0 的抢占式优先等级设置为 1; 响应式优先等级设置为 1;

PA1 的抢占式优先等级设置为 0; 响应式优先等级设置为 2;

假设正在执行的PA0中断, 现在来了PA1中断, 因为PA1抢占式比PA0高,所以会去执行PA1,然后再回来执行PA0

假设PA0和PA1同时来了中断, 因为PA1抢占式比PA0高,所以会去执行PA1,然后再回来执行PA0

 

所以记住上面的一句话就可以: 

抢占式等级相同,谁的响应式高先执行谁; 抢占式等级不同,谁的抢占式等级高先执行谁

 

 

设置PA0为下降沿中断

1,把以下程序拷贝到工程,并下载到开发板

#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);         }     } }

 

每次把PA0接到低电平串口就会打印

 

 

 

 

2,程序说明

1,关于优先级分组

前面有提到单片机有抢占式和响应式优先级,  优先级是由一个四位的二进制数保存的,

四位的二进制数共有 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 选择 , 响应式就不用设置了

 

 

 

 

 

 

2,在中断里面赋值,在主轮训判断使用的变量需要使用 volatile 修饰

 

 

 

 

假设设置PB2为上升沿中断

#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);         }     } }

 

 

 

 

假设设置PB6,PB7为上升沿中断

外部中断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)     {      } }

 

 

假设设置PB10上升沿中断, PB11下降沿中断

外部中断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)     {      } }