本文是高云FPGA系列教程的第7篇文章。
本篇文章介绍片上ARM Cortex-M3硬核处理器GPIO外部的使用,演示按键中断方式来控制LED亮灭,基于TangNano 4K开发板。
参考文档:Gowin_EMPU(GW1NS-4C)软件编程 参考手册
高云GN1NSR-4C共有16个GPIO,每个GPIO可配置成输入或输出模式,支持中断输入,触发方式可选择:上升沿、下降沿、高电平、低电平触发。
typedef enum
{
GPIO_Int_Disable = 0, /* Disable : Interrupt enable=0 */
GPIO_Int_Low_Level, /* Low-level : Interrupt enable=1 */
GPIO_Int_High_Level, /* High-level : Interrupt enable=1 & polarity=1 */
GPIO_Int_Falling_Edge, /* Falling edge : Interrupt enable=1 & type=1 */
GPIO_Int_Rising_Edge /* Rising edge : Interrupt enable=1 & polarity=1 & type=1 */
}GPIOInt_TypeDef;
中断优先级可通过NVIC进行设置,支持 0-7 级可编程中断优先级。
FPGA工程中,EMPU需要使能GPIO外设。
顶层设计如下:
/***************************************************************
* Copyright(C), 2010-2022, WeChat:MCU149.
* ModuleName : top_hdl.v
* Date : 2023年9月19日
* Time : 20:19:39
* Author : WeChat:MCU149
* Function : gw1nsr-4c gpio interrupt demo
* Version : v1.0
* Version | Modify
* ----------------------------------
* v1.0 .....
***************************************************************/
module top_hdl(
//Inputs
input gclk, // 27MHz
input gresetn,
input key,
input uart_rxd,
//Outputs
output uart_txd,
output led
);
wire clk_60m;
wire arm_clk = clk_60m;
wire arm_resetn = gresetn;
wire arm_uart0_rxd = uart_rxd;
wire arm_uart0_txd;
wire [15:0] arm_gpio_in;
wire [15:0] arm_gpio_out;
wire [15:0] arm_gpio_outen;
assign uart_txd = arm_uart0_txd;
assign led = arm_gpio_out[1];
assign arm_gpio_in[0] = key;
Gowin_PLLVR pll_ut0(
.clkout(clk_60m), //output clkout
.clkin(gclk) //input clkin
);
Gowin_EMPU_Top arm_cortex_m3_core(
//Inputs
.sys_clk(arm_clk),
.reset_n(arm_resetn),
.uart0_rxd(arm_uart0_rxd),
.gpioin(arm_gpio_in[15:0]),
//Outputs
.uart0_txd(arm_uart0_txd),
.gpioout(arm_gpio_out[15:0]),
.gpioouten(arm_gpio_outen[15:0])
);
endmodule //top_hdl end
LED连接到GPIO1,按键连接到GPIO0,按键按下是低电平。
常用的GPIO驱动库函数如下:
//清除中断
void GPIO_IntClear(GPIO_TypeDef* GPIOx,uint32_t GPIO_Pin)
//获取中断触发状态
uint32_t GPIO_GetIntStatus(GPIO_TypeDef* GPIOx)
//中断使能
uint32_t GPIO_SetIntEnable(GPIO_TypeDef* GPIOx,uint32_t GPIO_Pin)
//设置高电平触发方式
void GPIO_SetIntHighLevel(GPIO_TypeDef* GPIOx,uint32_t GPIO_Pin)
//设置上升沿触发方式
void GPIO_SetIntRisingEdge(GPIO_TypeDef* GPIOx,uint32_t GPIO_Pin)
//设置低电平触发方式
void GPIO_SetIntLowLevel(GPIO_TypeDef* GPIOx,uint32_t GPIO_Pin)
//设置下降沿触发方式
void GPIO_SetIntFallingEdge(GPIO_TypeDef* GPIOx,uint32_t GPIO_Pin)
一般配置流程:
1. 配置GPIO输入输入模式,中断触发方式
2. 配置NVIC中断优先级
3. 使能NVIC和GPIO中断
4. 实现中断服务函数,并注释掉系统提供的中断服务函数
中断触发方式,可以在GPIO管脚初始化时进行设置,也可以通过单独的设定函数来设定。
首先是按键和LED对应的GPIO初始化,按键默认为高电平,按下是低电平,如果要按键按下触发中断,即从高电平到低电平,就设置成下降沿触发,如果想要按键松开触发中断,就设置为上升沿触发:
int gpio_init(void)
{
GPIO_InitTypeDef init;
NVIC_InitTypeDef InitTypeDef_NVIC;
//KEY
// init.GPIO_Int = GPIO_Int_Falling_Edge; //press trig
init.GPIO_Int = GPIO_Int_Rising_Edge; //release trig
init.GPIO_Mode = GPIO_Mode_IN;
init.GPIO_Pin = GPIO_Pin_0;
GPIO_Init(GPIO0, &init);
//LED
init.GPIO_Int = GPIO_Int_Disable;
init.GPIO_Mode = GPIO_Mode_OUT;
init.GPIO_Pin = GPIO_Pin_1;
GPIO_Init(GPIO0, &init);
// GPIO_SetIntRisingEdge(GPIO0, GPIO_Pin_0); //release trig
// GPIO_SetIntFallingEdge(GPIO0, GPIO_Pin_0); //press trig
GPIO_SetIntEnable(GPIO0, GPIO_Pin_0);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_3);
InitTypeDef_NVIC.NVIC_IRQChannel = PORT0_0_IRQn;
InitTypeDef_NVIC.NVIC_IRQChannelPreemptionPriority = 1;
InitTypeDef_NVIC.NVIC_IRQChannelSubPriority = 1;
InitTypeDef_NVIC.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&InitTypeDef_NVIC);
return 0;
}
需要注意的是,中断触发方式只能设置成一种,不支持设置成上升沿和下降沿都触发中断。
中断服务函数的实现:
void PORT0_0_Handler(void)
{
static uint16_t data = 0;
data = ~data;
gpio_write(data);
printf("GPIO0_0 Interrupt Trig\r\n");
GPIO_IntClear(GPIO0, GPIO_Pin_0);
}
需要把gw1ns4c_it.c
文件里的中断服务函数注释掉。
本文基于TangNano 4K的开发板,配套工程在以下链接,包括Keil和GMD开发环境,都可以正常使用。
本文是高云FPGA系列教程的第7篇文章。