串行IO接口设计
文章目录
1 实验目的
-
理解UART串行通信协议以及接口设计;
-
掌握UART串行接口设计。
2 实验任务与要求
- 串行IO要求:所有实验任务要求采用中断方式实现,中断方式时,GPIO输入、延时、UART\SPI接口通信都采用中断方式实现。每位同学仅完成其中的一个任务,且需完成的实验任务编号与座位号模3的取值一致。
- 实验任务0要求:采用UART IP 核,实现Nexys4 或Nexys4 DDR 实验板UART接口之间的通信。要求当拨动开关时,将开关对应的值通过UART1发送到UART2,同时利用LED 灯指示UART2接收到的当前开关的值;当按下按键时,将按键对应的值通过UART2发送到UART1,同时利用数码管指示UART1接收到的当前按下的按键位置码(C,U,d,L,r)。UART 波特率为9600bps。
3 实验环境
- Vivado 2019.1
- Nexys4 DDR™ FPGA Board
- Xilinx SDK
4 实验内容与步骤
4.1 设计思路
4.1.1 中断源
本次实验的中断源为GPIO_0、GPIO_2和UART;
4.1.2 中断服务程序设计
- 对于中断源为开关的中断服务程序,函数内读取并更新当前的8位开关状态(搭建平台时之定义了低八位开关与低八位LED灯的引脚);
- 对于中断源为按键的中断服务程序,函数内读取并更新5个按键的状态值;
- 对于中断源为uart1的中断服务程序,函数内判断此时是接收中断还是发送中断,接收满中断则将接收到的键值输出到定义的一位数码管上(最右),如果是发送空中断则将当前的开关状态发送到uart2;
- 对于中断源为uart2的中断服务程序,函数内判断是接收满中断还是发送中断,如果是接收满中断,则将接收到的开关状态输出到8位LED灯上,如果是发送空中断,则将当前的按键状态值发送给uart1。
4.2 硬件电路
4.2.1 Nexys4 DDR开发板的外设
4.2.2 GPIO与外设的连接
- 本实验使用到了GPIO_0的SW70和LED70、GPIO_1的最右边一位数码管、GPIO_2的BTND、U、L、R、C。
4.2.3 UART接口
- 本实验使用到了UART1和UART2模块,引脚也自定义到相应外设上。
4.3 硬件平台
- 根据左东红老师的学习通视频,基于Vivado2019.1版本搭建了串行的IO中断系统。
4.4 软件流程图
4.5 设计源代码及注释
- 快速中断
/*fast interrupt*/
#include "xil_io.h"
#include "stdio.h"
#include "xgpio_l.h"
#include "xintc_l.h"
#include "xuartlite_l.h"
void UART1_RECV()__attribute__((fast_interrupt));
void UART2_RECV()__attribute__((fast_interrupt));
void BtnHandler()__attribute__((fast_interrupt));
void SwtHandler()__attribute__((fast_interrupt));
//中断服务程序
char scancode[5][2]={{0x1,0xc6},{0x2,0xc1},{0x4,0xc7},{0x8,0x88},{0x10,0xa1}};
int main()
{
/*GPIO*/
/*GPIO_0*/
Xil_Out16(XPAR_AXI_GPIO_0_BASEADDR + XGPIO_TRI_OFFSET, 0xffff);//switch
Xil_Out16(XPAR_AXI_GPIO_0_BASEADDR + XGPIO_TRI2_OFFSET, 0x0);//LED
Xil_Out32(XPAR_AXI_GPIO_0_BASEADDR+XGPIO_IER_OFFSET,XGPIO_IR_CH1_MASK);//通道1允许中断
Xil_Out32(XPAR_AXI_GPIO_0_BASEADDR+XGPIO_GIE_OFFSET,XGPIO_GIE_GINTR_ENABLE_MASK);//允许GPIO_0中断输出
/*GPIO_1*/
Xil_Out8(XPAR_AXI_GPIO_1_BASEADDR + XGPIO_TRI_OFFSET, 0x0);//设定位码为输出
Xil_Out8(XPAR_AXI_GPIO_1_BASEADDR + XGPIO_TRI2_OFFSET, 0x0);//设定段码为输出
Xil_Out8(XPAR_AXI_GPIO_1_BASEADDR + XGPIO_DATA_OFFSET, 0xfe);//segment初值,DATA位选
/*GPIO_2*/
Xil_Out8(XPAR_AXI_GPIO_2_BASEADDR + XGPIO_TRI_OFFSET, 0x1f);//设定5个按键为输入
Xil_Out32(XPAR_AXI_GPIO_2_BASEADDR+XGPIO_IER_OFFSET,XGPIO_IR_CH1_MASK); CH1允许 中断,CH1对应的是按键
Xil_Out32(XPAR_AXI_GPIO_2_BASEADDR+XGPIO_GIE_OFFSET,XGPIO_GIE_GINTR_ENABLE_MASK);//允许 按键和LD所对应的GPIO IP核 GPIO_2 中断输出
/*UART*/
Xil_Out32(XPAR_AXI_UARTLITE_1_BASEADDR+XUL_CONTROL_REG_OFFSET,
XUL_CR_ENABLE_INTR|XUL_CR_FIFO_RX_RESET|XUL_CR_FIFO_TX_RESET);
Xil_Out32(XPAR_AXI_UARTLITE_2_BASEADDR+XUL_CONTROL_REG_OFFSET,
XUL_CR_ENABLE_INTR|XUL_CR_FIFO_RX_RESET|XUL_CR_FIFO_TX_RESET);
/*INTC*/
Xil_Out32(XPAR_INTC_0_BASEADDR+XIN_IAR_OFFSET,
Xil_In32(XPAR_INTC_0_BASEADDR+XIN_ISR_OFFSET));
Xil_Out32(XPAR_INTC_0_BASEADDR+XIN_IER_OFFSET,XPAR_AXI_GPIO_0_IP2INTC_IRPT_MASK|XPAR_AXI_GPIO_2_IP2INTC_IRPT_MASK|
XPAR_AXI_UARTLITE_1_INTERRUPT_MASK|XPAR_AXI_UARTLITE_2_INTERRUPT_MASK);//使能
Xil_Out32(XPAR_INTC_0_BASEADDR+XIN_IMR_OFFSET,XPAR_AXI_GPIO_2_IP2INTC_IRPT_MASK|XPAR_AXI_GPIO_0_IP2INTC_IRPT_MASK|
XPAR_AXI_UARTLITE_1_INTERRUPT_MASK|XPAR_AXI_UARTLITE_2_INTERRUPT_MASK);//工作模式
Xil_Out32(XPAR_AXI_INTC_0_BASEADDR+XIN_MER_OFFSET,XIN_INT_MASTER_ENABLE_MASK|XIN_INT_HARDWARE_ENABLE_MASK);//允许中断输出
Xil_Out32(XPAR_INTC_0_BASEADDR+XIN_IVAR_OFFSET+4*XPAR_INTC_0_GPIO_2_VEC_ID,(u32)BtnHandler);
Xil_Out32(XPAR_INTC_0_BASEADDR+XIN_IVAR_OFFSET+4*XPAR_INTC_0_GPIO_0_VEC_ID,(u32)SwtHandler);
Xil_Out32(XPAR_INTC_0_BASEADDR+XIN_IVAR_OFFSET+4*XPAR_INTC_0_UARTLITE_1_VEC_ID,(u32)UART1_RECV);
Xil_Out32(XPAR_INTC_0_BASEADDR+XIN_IVAR_OFFSET+4*XPAR_INTC_0_UARTLITE_2_VEC_ID,(u32)UART2_RECV);
Xil_Out32(XPAR_INTC_0_BASEADDR+XIN_MER_OFFSET,XIN_INT_MASTER_ENABLE_MASK|XIN_INT_HARDWARE_ENABLE_MASK);
//填写中断控制器维护的中断向量表 写IVAR寄存器
/*MicroBlaze*/
microblaze_enable_interrupts();
//microblaze中断开放
return 0;
}
void BtnHandler()
{
u8 btncode=Xil_In8(XPAR_AXI_GPIO_2_BASEADDR+XGPIO_DATA_OFFSET);
if(btncode)
{
//将按键值写入UART2的发送FIFO到UART1
Xil_Out32(XPAR_AXI_UARTLITE_2_BASEADDR+XUL_TX_FIFO_OFFSET,btncode);
}
Xil_Out32(XPAR_AXI_GPIO_2_BASEADDR+XGPIO_ISR_OFFSET,
Xil_In32(XPAR_AXI_GPIO_2_BASEADDR+XGPIO_ISR_OFFSET));//清除中断
}
void SwtHandler(){
//将开关写入UART1的发送FIFO到UART2
Xil_Out32(XPAR_AXI_UARTLITE_1_BASEADDR+XUL_TX_FIFO_OFFSET,Xil_In32(XPAR_AXI_GPIO_0_BASEADDR+XGPIO_DATA_OFFSET));
Xil_Out32(XPAR_AXI_GPIO_0_BASEADDR+XGPIO_ISR_OFFSET,Xil_In32(XPAR_AXI_GPIO_0_BASEADDR+XGPIO_ISR_OFFSET)); //清除中断
}
void UART2_RECV()
{
if((Xil_In32(XPAR_AXI_UARTLITE_2_BASEADDR+XUL_STATUS_REG_OFFSET)
&XUL_SR_RX_FIFO_VALID_DATA)==XUL_SR_RX_FIFO_VALID_DATA)
Xil_Out32(XPAR_AXI_GPIO_0_BASEADDR+XGPIO_DATA2_OFFSET,
Xil_In32(XPAR_AXI_UARTLITE_2_BASEADDR+XUL_RX_FIFO_OFFSET));
}
void UART1_RECV()
{
if((Xil_In32(XPAR_AXI_UARTLITE_1_BASEADDR+XUL_STATUS_REG_OFFSET)
&XUL_SR_RX_FIFO_VALID_DATA)==XUL_SR_RX_FIFO_VALID_DATA)
{
u8 btncode = Xil_In8(XPAR_AXI_UARTLITE_1_BASEADDR+XUL_RX_FIFO_OFFSET);
for(int i=0;i<5;i++)
{
if(btncode==scancode[i][0])
{
btncode=scancode[i][1];
Xil_Out32(XPAR_AXI_GPIO_1_BASEADDR+XGPIO_DATA2_OFFSET,btncode);
break;
}
}
}
4.5 下载及功能验证
在经过调试和修改代码后,下载到实验FPGA上,能够实现要求的功能,验收,功能验证图片如下:
-
在连接时,LED和一位数码管能够正常相应中断
-
在断开时,LED和一位数码管不能正常相应中断
-
再次连接上,LED和一位数码管又能正常相应中断
5 实验心得、体会
深入学习了硬件平台的搭建,中断的原理,在SDK上以c语言实现各种中断的功能以及GPIO,TIMER等的具体功能,除此之外还学习到了串行接口的原理,UART的使用方法等。