1、GPIO中断描述
DA14580的GPIO中断有6个,1个 键盘接口中断(KBRD)、5个独立中断线(IRQ0~IRQ4)。
1.1、IRQ0~IRQ4中断线
一共有5组独立的中断线,每组中断线支持所有的IO管脚配置为中断输入源,一个中断线只能配置1个IO管脚,一个IO管脚可以配置多个中断线。因此最大只能配置5个管脚的独立中断。(每个中断线的消抖时间是共用的)
1.2、KBRD键盘接口中断
键盘接口中断的中断输入源可以配置为所有的IO管脚,所有的管脚可以同时使能键盘中断。但不能独立配置每个管脚的中断方式(按下中断/松开中断、高电平中断/低电平中断)。
DA14580内部有一个键盘控制器,键盘控制器可以扫描GPIO管脚的状态。通过配置可以让它产生一个中断给CPU(KEYBR_IRQ)。
2、GPIO中断寄存器
2.1、GPIO_IRQ0_IN_SEL_REG 中断线0,中断源选择
位 | 符号 | 描述 |
15:6 | Reserved | |
5:0 | KBRD_IRQ0_SE | 0: no input selected 1: P0[0] is selected 2: P0[1] is selected 3: P0[2] is selected 4: P0[3] is selected 5: P0[4] is selected 6: P0[5] is selected 7: P0[6] is selected 8: P0[7] is selected 9: P1[0] is selected 10: P1[1] is selected 11: P1[2] is selected 12: P1[3] is selected 13: P1[4] is selected 14: P1[5] is selected 15: P2[0] is selected 16: P2[1] is selected 17: P2[2] is selected 18: P2[3] is selected 19: P2[4] is selected 20: P2[5] is selected 21: P2[6] is selected 22: P2[7] is selected 23: P2[8] is selected 24: P2[9] is selected 25: P3[0] is selected 26: P3[1] is selected 27: P3[2] is selected 28: P3[3] is selected 29: P3[4] is selected 30: P3[5] is selected 31: P3[6] is selected 32: P3[7] is selected 其它值,不选择任何管脚 |
2.2、GPIO_IRQ1_IN_SEL_REG 中断线1,中断源选择(参考GPIO_IRQ0_IN_SEL_REG )
2.3、GPIO_IRQ2_IN_SEL_REG 中断线2,中断源选择(参考GPIO_IRQ0_IN_SEL_REG )
2.4、GPIO_IRQ3_IN_SEL_REG 中断线3,中断源选择(参考GPIO_IRQ0_IN_SEL_REG )
2.5、GPIO_IRQ4_IN_SEL_REG 中断线4,中断源选择(参考GPIO_IRQ0_IN_SEL_REG )
2.6、GPIO_DEBOUNCE_REG 消抖寄存器
位 | 符号 | 描述 |
15:14 | Reserved | |
13 | DEB_ENABLE_KBRD | 1: 使能键盘(KBRD)消抖计数器 |
12 | DEB_ENABLE4 | 1: 使能中断线4(IRQ4)消抖计数器 |
11 | DEB_ENABLE3 | 1: 使能中断线3(IRQ3)消抖计数器 |
10 | DEB_ENABLE2 | 1: 使能中断线2(IRQ2)消抖计数器 |
9 | DEB_ENABLE1 | 1: 使能中断线1(IRQ1)消抖计数器 |
8 | DEB_ENABLE0 | 1: 使能中断线0(IRQ0)消抖计数器 |
7:6 | Reserved | |
5:0 | DEB_VALUE | 消抖时间,0~63ms (IRQ0~IRQ4共用) |
2.7、 GPIO_RESET_IRQ_REG 中断复位寄存器(处理完中断后要复位中断,否则一直进入中断)
位 | 符号 | 描述 |
15:6 | Reserved | |
5 | RESET_KBRD_IRQ | 写1复位键盘(KBRD)中断, 读返回0 |
4 | RESET_GPIO4_IRQ | 写1复位中断线4(IRQ4)中断,读返回0 |
3 | RESET_GPIO3_IRQ | 写1复位中断线3(IRQ3)中断,读返回0 |
2 | RESET_GPIO2_IRQ | 写1复位中断线2(IRQ2)中断,读返回0 |
1 | RESET_GPIO1_IRQ | 写1复位中断线1(IRQ1)中断,读返回0 |
0 | RESET_GPIO0_IRQ | 写1复位中断线0(IRQ0)中断,读返回0 |
2.8、GPIO_INT_LEVEL_CTRL_REG IRQx中断电平控制寄存器
位 | 符号 | 描述 |
15:13 | Reserved | |
12 | EDGE_LEVELN4 | 同 EDGE_LEVELN0 |
11 | EDGE_LEVELN3 | 同 EDGE_LEVELN0 |
10 | EDGE_LEVELN2 | 同 EDGE_LEVELNO |
9 | EDGE_LEVELN1 | 同 EDGE_LEVELNO |
8 | EDGE_LEVELN0 | 中断线0的中断方式: 0:产生中断时,不用等待按键松开,复位中断后(GPIO_RESET_IRQ_REG对应位置1),继续产生新的中断。
1:产生中断时,等待按键松开,复位中断后,直到下次按下按键才会产生中断。
这里面的按键按下,可以是高电平,也可以是低电平,由INPUT_LEVEL0位决定。
个人理解: |
4 | INPUT_LEVEL4 | |
3 | INPUT_LEVEL3 | |
2 | INPUT_LEVEL2 | |
1 | INPUT_LEVEL1 | |
0 | INPUT_LEVEL0 | 中断线0的中断电平: 0:高电平中断 1:低电平中断 这个位和EDGE_LEVELN0配合用 |
2.9、KBRD_IRQ_IN_SEL0_REG 键盘控制寄存器0
位 | 符号 | 描述 |
15 | KBRD_REL | 0:按下产生中断,松开不中断 1:按下和松开都产生中断 按下是高电平还是低电平由KBRD_LEVEL决定 |
14 | KBRD_LEVEL | 0:高电平中断 1:低电平中断 |
13:8 | KEY_REPEAT | 可设置值N = 0~63,单位ms。 N = 0: 按下时只中断一次。 N > 0: 一直按下时,每N毫秒就中断一次。 |
7 | KBRD_P07_EN | 使能P0[7] 键盘中断 |
6 | KBRD_P06_EN | 使能P0[6] 键盘中断 |
5 | KBRD_P05_EN | 使能P0[5] 键盘中断 |
4 | KBRD_P04_EN | 使能P0[4] 键盘中断 |
3 | KBRD_P03_EN | 使能P0[3] 键盘中断 |
2 | KBRD_P02_EN | 使能P0[2] 键盘中断 |
1 | KBRD_P01_EN | 使能P0[1] 键盘中断 |
0 | KBRD_P00_EN | 使能P0[0] 键盘中断 |
2.10、KBRD_IRQ_IN_SEL1_REG 键盘控制寄存器1
位 | 符号 | 描述 |
15 | KBRD_P15_EN | 使能P1[5] 键盘中断 |
14 | KBRD_P14_EN | 使能P1[4] 键盘中断 |
13 | KBRD_P13_EN | 使能P1[3] 键盘中断 |
12 | KBRD_P12_EN | 使能P1[2] 键盘中断 |
11 | KBRD_P11_EN | 使能P1[1] 键盘中断 |
10 | KBRD_P10_EN | 使能P1[0] 键盘中断 |
9 | KBRD_P29_EN | 使能P2[9] 键盘中断 |
8 | KBRD_P28_EN | 使能P2[8] 键盘中断 |
7 | KBRD_P27_EN | 使能P2[7] 键盘中断 |
6 | KBRD_P26_EN | 使能P2[6] 键盘中断 |
5 | KBRD_P25_EN | 使能P2[5] 键盘中断 |
4 | KBRD_P24_EN | 使能P2[4] 键盘中断 |
3 | KBRD_P23_EN | 使能P2[3] 键盘中断 |
2 | KBRD_P22_EN | 使能P2[2] 键盘中断 |
1 | KBRD_P21_EN | 使能P2[1] 键盘中断 |
0 | KBRD_P20_EN | 使能P2[0] 键盘中断 |
2.11、KBRD_IRQ_IN_SEL2_REG 键盘控制寄存器2
位 | 符号 | 描述 |
15:8 | Reserved | |
7 | KBRD_P37_EN | 使能P3[7] 键盘中断 |
6 | KBRD_P36_EN | 使能P3[6] 键盘中断 |
5 | KBRD_P35_EN | 使能P3[5] 键盘中断 |
4 | KBRD_P34_EN | 使能P3[4] 键盘中断 |
3 | KBRD_P33_EN | 使能P3[3] 键盘中断 |
2 | KBRD_P32_EN | 使能P3[2] 键盘中断 |
1 | KBRD_P31_EN | 使能P3[1] 键盘中断 |
0 | KBRD_P30_EN | 使能P3[0] 键盘中断 |
3、中断函数介绍
3.1、GPIO_EnableIRQ
使能管脚中断
GPIO_EnableIRQ( GPIO_PORT port, GPIO_PIN pin, IRQn_Type irq, bool low_input,
bool release_wait, uint8_t debounce_ms )
参数 | 描述 |
port | 端口:GPIO_PORT_0 ~ GPIO_PORT_3 |
pin | 管脚:GPIO_PIN_0 ~ GPIO_PIN_15 |
irq | 5个中断线 GPIO0_IRQn GPIO1_IRQn GPIO2_IRQn GPIO3_IRQn GPIO4_IRQn |
low_input | 为true时低电平产生中断,为false时高电平产生中断 |
release_wait | 为true时为边沿中断,为false时为电平中断 如: low_input = true release_wait = true 下降沿中断 low_input = false release_wait = true 上升沿中断
low_input = true release_wait = false 低电平中断(只要是低就一直触发中断) low_input = false release_wait = false 高电平中断(只要是高就一直触发中断) |
debounce_ms | 按键消抖,单位为ms, 最大63 |
3.2、GPIO_ResetIRQ
复位中断标志
GPIO_ResetIRQ( IRQn_Type irq )
参数 | 描述 |
irq | 中断类型:GPIO0_IRQn、GPIO1_IRQn、GPIO2_IRQn、GPIO3_IRQn、GPIO4_IRQn |
3.3、GPIO_RegisterCallback
注册中断回调函数
GPIO_RegisterCallback(IRQn_Type irq, GPIO_handler_function_t callback)
参数 | 描述 |
irq | 中断类型:GPIO0_IRQn、GPIO1_IRQn、GPIO2_IRQn、GPIO3_IRQn、GPIO4_IRQn |
callback | 回调函数,中断时会自动调用这个函数。 |
3.4、GPIO_SetIRQInputLevel
设置中断电平,高电平中断还是低电平中断,和前面的low_input作用一样。
GPIO_SetIRQInputLevel(IRQn_Type irq, GPIO_IRQ_INPUT_LEVEL level)
参数 | 描述 |
irq | 中断类型:GPIO0_IRQn、GPIO1_IRQn、GPIO2_IRQn、GPIO3_IRQn、GPIO4_IRQn |
level | 中断电平 GPIO_IRQ_INPUT_LEVEL_HIGH GPIO_IRQ_INPUT_LEVEL_LOW |
3.5、GPIO_GetIRQInputLevel
获取中断电平,返回GPIO_IRQ_INPUT_LEVEL_HIGH 或 GPIO_IRQ_INPUT_LEVEL_LOW
3.6、GPIO0_Handler
GPIO0_IRQn中断时会调用该函数。
3.7、GPIO1_Handler
GPIO1_IRQn中断时会调用该函数。
3.8、GPIO2_Handler
GPIO2_IRQn中断时会调用该函数。
3.9、GPIO3_Handler
GPIO3_IRQn中断时会调用该函数。
3.10、GPIO4_Handler
GPIO4_IRQn中断时会调用该函数。
3.11、KEYBRD_Handler
KEYBRD中断时会调用该函数。(该函数在gpio.c里面没有写出,可以自己添加)
void KEYBRD_Handler(void)
{
GPIOSetBits16(GPIO_RESET_IRQ_REG ,RESET_KBRD_IRQ,1);
}
复位KEYBRD中断时,不能调用GPIO_ResetIRQ,因为这个函数值针对IRQ0~IRQ4的中断复位,可以写成下面这样
GPIOSetBits16(GPIO_RESET_IRQ_REG ,RESET_KBRD_IRQ,1);
4、使用示例
4.1中断线IRQ0中断:
实现功能:1个按键、1个LED,按键按一次LED取反。
1)在user_periph_setup.h中定义要用到的管脚
#define KEY_PORT GPIO_PORT_2
#define KEY_PIN GPIO_PIN_4
#define LED_PORT GPIO_PORT_2
#define LED_PIN GPIO_PIN_3
2)在GPIO_reservations函数里面添加预留管脚。
void GPIO_reservations(void)
{
RESERVE_GPIO(LED, LED_PORT,LED_PIN, PID_GPIO);
RESERVE_GPIO(KEY, KEY_PORT,KEY_PIN, PID_GPIO);
}
3)在set_pad_functions函数里配置管脚工作模式
void set_pad_functions(void)
{
//配置LED管脚为输出,初始化为低电平
GPIO_ConfigurePin(LED_PORT,LED_PIN,OUTPUT,PID_GPIO,false);
//配置KEY管脚为上拉输入,初始化为高电平
GPIO_ConfigurePin(KEY_PORT,KEY_PIN,INPUT_PULLUP,PID_GPIO,true);
//使能按键中断,中断类型为GPIO0_IRQn,下降沿中断,消抖20ms
GPIO_EnableIRQ(KEY_PORT,KEY_PIN,GPIO0_IRQn,true,true,20);
//注册GPIO0_IRQn中断回调函数
GPIO_RegisterCallback(GPIO0_IRQn,GPIO_IRQ_Handle);
}
4)定义中断回调函数
void GPIO_IRQ_Handle(void)
{
if(GPIO_GetPinStatus(LED_PORT,LED_PIN) == false)
{
GPIO_SetActive(LED_PORT,LED_PIN);
}
else
{
GPIO_SetInactive(LED_PORT,LED_PIN);
}
}
4.2、键盘中断(KEYBRD)
实现功能:1个按键、1个LED,按键按一次LED取反。
1)在user_periph_setup.h中定义要用到的管脚
#define KEY_PORT GPIO_PORT_2
#define KEY_PIN GPIO_PIN_4
2)在GPIO_reservations函数里面添加预留管脚。
void GPIO_reservations(void)
{
RESERVE_GPIO(LED, LED_PORT,LED_PIN, PID_GPIO);
RESERVE_GPIO(KEY, KEY_PORT,KEY_PIN, PID_GPIO);
}
3)由于键盘中断官方并没有给出相关的函数,所以我们要自己实现
a、在gpio.c里面添加2个变量,KBRD_GPIO、KBRD_status,用来标志需要使能键盘中断的管脚。
int KBRD_GPIO[NO_OF_PORTS][NO_OF_MAX_PINS_PER_PORT];
volatile uint64_t KBRD_status;
b、编写KBRD_SELECT_GPIO函数,用来设置需要使能键盘中断的管脚。(设置KBRD_status 的值)
void KBRD_SELECT_GPIO(GPIO_PORT port, GPIO_PIN pin)
{
KBRD_GPIO[port][pin] = (KBRD_GPIO[port][pin] != 0) ? (-1) : 1;
KBRD_status |= ((uint64_t)KBRD_GPIO[port][pin] << pin) << (port * 16);
}
c、编写键盘中断使能函数
/**
****************************************************************************************
* @brief 使能键盘中断
*
* @param[in] low_input true:低电平中断 false:高电平中断
* @param[in] release_wait true:按下和松开都中断 false:按下中断、松开不中断
* @param[in] debounce_ms 消抖时间(看DEB_VALUE位)
* @param[in] repeat_ms 重复时间(看KEY_REPEAT位)
*
* @return void
****************************************************************************************
*/
void KBRD_Enable_IRQ(bool low_input,bool release_wait, uint8_t debounce_ms,uint8_t repeat_ms )
{
//是否使能键盘消抖
GPIOSetBits16(GPIO_DEBOUNCE_REG, DEB_ENABLE_KBRD, (debounce_ms > 0) );
//给出消抖时间最大63ms
GPIOSetBits16(GPIO_DEBOUNCE_REG, DEB_VALUE, debounce_ms);
GPIOSetBits16(KBRD_IRQ_IN_SEL0_REG, KBRD_REL, release_wait);
GPIOSetBits16(KBRD_IRQ_IN_SEL0_REG, KBRD_LEVEL , low_input);
GPIOSetBits16(KBRD_IRQ_IN_SEL0_REG, KEY_REPEAT , repeat_ms);
//配置P0端口
GPIOSetBits16(KBRD_IRQ_IN_SEL0_REG, 0xff , KBRD_status&0xff);
//配置P1端口
GPIOSetBits16(KBRD_IRQ_IN_SEL1_REG, 0xfc00 , ((KBRD_status>>16)&0x3f));
//配置P2端口
GPIOSetBits16(KBRD_IRQ_IN_SEL1_REG, 0x3ff , ((KBRD_status>>32)&0x3ff));
//配置P3端口
GPIOSetBits16(KBRD_IRQ_IN_SEL1_REG+2, 0xff , ((KBRD_status>>48)&0xff));
//中断优先级
NVIC_SetPriority(KEYBRD_IRQn, 2);
//使能中断
NVIC_EnableIRQ(KEYBRD_IRQn);
}
d、编写中断处理函数
void KEYBRD_Handler(void)
{
GPIOSetBits16(GPIO_RESET_IRQ_REG ,RESET_KBRD_IRQ,1);
if(GPIO_GetPinStatus(LED_PORT,LED_PIN) == false)
{
GPIO_SetActive(LED_PORT,LED_PIN);
}
else
{
GPIO_SetInactive(LED_PORT,LED_PIN);
}
}
3)在set_pad_functions函数里配置管脚工作模式
void set_pad_functions(void)
{
//配置LED管脚为输出,初始化为低电平
GPIO_ConfigurePin(LED_PORT,LED_PIN,OUTPUT,PID_GPIO,false);
//配置KEY管脚为上拉输入,初始化为高电平
GPIO_ConfigurePin(KEY_PORT,KEY_PIN,INPUT_PULLUP,PID_GPIO,true);
KBRD_SELECT_GPIO(KEY_PORT,KEY_PIN); //需要使能键盘中断的管脚
KBRD_Enable_IRQ(true,false,20,0); //使能键盘中断
}
到这里,键盘中断相关的代码就编写完成了,可以运行程序看结果。
把KBRD_Enable_IRQ的参数改为下面这样
KBRD_Enable_IRQ(true,false,20,30);
一直按下按键时,每30ms就会进入1次中断。