前言
学习嵌入式的时候不可避免会接触到中断这个概念,我们平时接触也是最多的,在刚开始学习stm32单片机的时候,听到中断这个概念一头雾水,慢慢接触多了后发现这个是真的好用,也非常简单。在学习国产GD32系列单片机的时候,中断的学习也是基本适用,所以很快上手。
`
一、EXTI-外部中断
GD32F303的Cortex-M4集成了嵌套式矢量型中断控制器(Nested Vectored Interrupt Controller (NVIC))来实现高效的异常和中断处理。NVIC实现了低延迟的异常和中断处理,以及电源管理控制。它和内核是紧密耦合的。更多关于NVIC 的说明请参考《Cortex-M4 技术参考手册》。EXTI(中断/事件控制器)包括20个相互独立的边沿检测电路并且能够向处理器内核产生中断请求或唤醒事件。EXTI有三种触发类型:上升沿触发、下降沿触发和任意沿触发。EXTI中的每一个边沿检测电路都可以独立配置和屏蔽。
外部中断的控制逻辑如下图,检测外部中断线上的电平信号,设置成上升沿触发、下降沿触发和任意沿触发,任意沿触发就是发生上升沿或下降沿都会触发,在不确定外部初始电平的情况下使用。当发生了一个边沿跳变,信号就会到NVIC中断控制器,然后交给内核处理是否响应中断,除了外部中断外,其它所有的中断都要经过NVIC中断控制寄存器处理,每个中断源都可以在中断向量表中找到。
下图就是外部中断线在向量表中的位置。还有一些其它重要的中断请参考数据手册。
上图就详细说明了外部中断线对应的引脚情况,跟32单片机类似,GPIO_PIN_X相同的脚对应相同的EXTI_X。所以在使用两个外部中断以上时,引脚的PIN序号不能一样。
二、程序说明
1.主函数
例如,使用EXTI1作为外部中断源,所以PA/PB/PC/PD_1任选一个引脚作为外部输入,如PA_1 下面一步步分析。
//配置PA1外部中断
rcu_periph_clock_enable(RCU_GPIOA);//GPIOA时钟使能
gpio_init(GPIOA, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_1);//PA1配置成浮空输入
gpio_exti_source_select(GPIO_PORT_SOURCE_GPIOA, GPIO_PIN_SOURCE_1);//connect key EXTI line to key GPIO pin
exti_init(EXTI_1, EXTI_INTERRUPT, EXTI_TRIG_FALLING);//下降沿中断
exti_interrupt_flag_clear(EXTI_1);//清中断标志
nvic_irq_enable(EXTI1_IRQn, 2U, 0U);//enable and set key EXTI interrupt to the lowest priority
首先打开GPIOA的时钟,这个很好理解,运行之前,“心脏”肯定得打开,接着是初始化配置GPIO,设置成浮空输入模式,速度50Mhz,GPIO的1引脚。此脚连接了按键,可以实现边沿跳变。
第三步是将GPIOA_1设置成外部中断触发源,我们看下函数
/*!
\brief select GPIO pin exti sources
\param[in] output_port: gpio event output port
only one parameter can be selected which are shown as below:
\arg GPIO_PORT_SOURCE_GPIOA: output port source A
\arg GPIO_PORT_SOURCE_GPIOB: output port source B
\arg GPIO_PORT_SOURCE_GPIOC: output port source C
\arg GPIO_PORT_SOURCE_GPIOD: output port source D
\arg GPIO_PORT_SOURCE_GPIOE: output port source E
\arg GPIO_PORT_SOURCE_GPIOF: output port source F
\arg GPIO_PORT_SOURCE_GPIOG: output port source G
\param[in] output_pin: GPIO output pin source
only one parameter can be selected which are shown as below:
\arg GPIO_PIN_SOURCE_x(x=0..15)
\param[out] none
\retval none
*/
void gpio_exti_source_select(uint8_t output_port, uint8_t output_pin)
{
uint32_t source = 0U;
source = ((uint32_t)0x0FU) << (AFIO_EXTI_SOURCE_FIELDS * (output_pin & AFIO_EXTI_SOURCE_MASK));
/* select EXTI sources */
if(GPIO_PIN_SOURCE_4 > output_pin){
/* select EXTI0/EXTI1/EXTI2/EXTI3 */
AFIO_EXTISS0 &= ~source;
AFIO_EXTISS0 |= (((uint32_t)output_port) << (AFIO_EXTI_SOURCE_FIELDS * (output_pin & AFIO_EXTI_SOURCE_MASK)));
}else if(GPIO_PIN_SOURCE_8 > output_pin){
/* select EXTI4/EXTI5/EXTI6/EXTI7 */
AFIO_EXTISS1 &= ~source;
AFIO_EXTISS1 |= (((uint32_t)output_port) << (AFIO_EXTI_SOURCE_FIELDS * (output_pin & AFIO_EXTI_SOURCE_MASK)));
}else if(GPIO_PIN_SOURCE_12 > output_pin){
/* select EXTI8/EXTI9/EXTI10/EXTI11 */
AFIO_EXTISS2 &= ~source;
AFIO_EXTISS2 |= (((uint32_t)output_port) << (AFIO_EXTI_SOURCE_FIELDS * (output_pin & AFIO_EXTI_SOURCE_MASK)));
}else{
/* select EXTI12/EXTI13/EXTI14/EXTI15 */
AFIO_EXTISS3 &= ~source;
AFIO_EXTISS3 |= (((uint32_t)output_port) << (AFIO_EXTI_SOURCE_FIELDS * (output_pin & AFIO_EXTI_SOURCE_MASK)));
}
}
可以看到操作的是AFIO_EXTISS0/1/2/3寄存器,也就是EXTI源选择寄存器,AFIO也就是使用普通IO口复用功能,复用为EXTI功能。这个也可以从数据手册中查到,每个io口都有默认的io功能,复用功能以及重映射的功能。
第四步是设置EXTI1为下降沿中断。
第五步是清除EXTI1线上的中断标志。
第六步是打开NVIC的外部中断使能,打开EXTI1_IRQn的中断请求,并设置主优先级为2,子优先级为0,函数体为:
/*!
\brief enable NVIC request
\param[in] nvic_irq: the NVIC interrupt request, detailed in IRQn_Type
\param[in] nvic_irq_pre_priority: the pre-emption priority needed to set
\param[in] nvic_irq_sub_priority: the subpriority needed to set
\param[out] none
\retval none
*/
void nvic_irq_enable(uint8_t nvic_irq, uint8_t nvic_irq_pre_priority,
uint8_t nvic_irq_sub_priority)
{
uint32_t temp_priority = 0x00U, temp_pre = 0x00U, temp_sub = 0x00U;
/* use the priority group value to get the temp_pre and the temp_sub */
if(((SCB->AIRCR) & (uint32_t)0x700U)==NVIC_PRIGROUP_PRE0_SUB4){
temp_pre=0U;
temp_sub=0x4U;
}else if(((SCB->AIRCR) & (uint32_t)0x700U)==NVIC_PRIGROUP_PRE1_SUB3){
temp_pre=1U;
temp_sub=0x3U;
}else if(((SCB->AIRCR) & (uint32_t)0x700U)==NVIC_PRIGROUP_PRE2_SUB2){
temp_pre=2U;
temp_sub=0x2U;
}else if(((SCB->AIRCR) & (uint32_t)0x700U)==NVIC_PRIGROUP_PRE3_SUB1){
temp_pre=3U;
temp_sub=0x1U;
}else if(((SCB->AIRCR) & (uint32_t)0x700U)==NVIC_PRIGROUP_PRE4_SUB0){
temp_pre=4U;
temp_sub=0x0U;
}else{
nvic_priority_group_set(NVIC_PRIGROUP_PRE2_SUB2);
temp_pre=2U;
temp_sub=0x2U;
}
/* get the temp_priority to fill the NVIC->IP register */
temp_priority = (uint32_t)nvic_irq_pre_priority << (0x4U - temp_pre);
temp_priority |= nvic_irq_sub_priority &(0x0FU >> (0x4U - temp_sub));
temp_priority = temp_priority << 0x04U;
NVIC->IP[nvic_irq] = (uint8_t)temp_priority;
/* enable the selected IRQ */
NVIC->ISER[nvic_irq >> 0x05U] = (uint32_t)0x01U << (nvic_irq & (uint8_t)0x1FU);
}
2.中断函数
代码如下(示例):
void EXTI1_IRQHandler(void)
{
exti_interrupt_flag_clear(EXTI_1);
IntFlag=0x55;
}
进入这个中断函数,要先清除中断标志,然后可以根据代码需要设置一些进入中断的标志,方便主程序中用到。
如果感觉有用就请留下言或是有什么问题指出,一定做出优化和改正,方便大家一起学习交流。谢谢
总结
提示:这里对文章进行总结:
例如:以上就是今天要讲的内容,本文仅仅简单介绍了pandas的使用,而pandas提供了大量能使我们快速便捷地处理数据的函数和方法。