第二章MicroBlaze软核设计--UART(中断)

       这篇文章主要介绍串口中断,使用上位机下发数据,使用FPGA串口接收消息,并通过判断接收到的数据与预定数据是否相同控制led灯的亮灭,在主函数中使用延迟的方式定时向上位机发送数据。

一、平台搭建

  1. Microblaze软核的配置可以看我之前的文章——《第一章MicroBlaze软核设计--GPIO(中断)》,写的非常详细,也可参考正点原子的Microblaze开发指南V2.0开发文档,这里不多赘述。
  2. GPIO核与之前一样,只需要配置一个输出端口用来控制led就可以。
  3. 在上一节基础上,需要配置一个UART模块用来接收、发送消息。对于串口IP核的工作原理,可以参考下面这位博主写的文章。【【FPGA 之Micro Blaze的串口中断实验】】_microblaze 中断-CSDN博客
  4. 对于串口IP核设置只需要配置波特率就可以,也可以使用默认的波特率,这里我使用的115200。与上位机通信时,注意波特率的选择。
  5. 最后是整个工程的Block Design的布局
  6. 为了统一接口,在创建一个顶层文件,最后的工程便是这样
  7. 接下来就是绑定管脚,生成bit流文件,导出xsa平台,可参考《第一章MicroBlaze软核设计--GPIO(中断)》文章中第一、6节内容。

二、Vitis工程搭建

  1. 参考我之前写的《第一章MicroBlaze软核设计--GPIO(中断)》文章中第二节,Vitis工程搭建。

三、代码编写

  1. gpio.c
    #include 	"gpio.h"
    #include 	"xgpio.h"
    
    #define 	GPIO_DEVICE_ID  XPAR_GPIO_0_DEVICE_ID 	// GPIO设备ID
    
    XGpio 		Gpio; 			// 实例化XGpio名为GPIO
    #define 	channel1  1     // 第一组端口
    
    /*------------------------------------初始化------------------------------------*/
    uint8_t gpio_init()
    {
    	uint8_t status = XGpio_Initialize(&Gpio, GPIO_DEVICE_ID);
    	if(status == XST_FAILURE){
    		xil_printf("gpio init error!\n");
    	}
    
    	// 设置通道1为输出
    	XGpio_SetDataDirection(&Gpio, channel1, 0x0);
    	XGpio_DiscreteWrite(&Gpio, channel1, 0x0);		// 输出默认为0
    
    	return XST_SUCCESS;
    }
    
    // --------------------------------------反转通道1特定位----------------------------------------//
    void toggle_channel1_bit(uint8_t bit)
    {
    	uint32_t current = XGpio_DiscreteRead(&Gpio, channel1);
    	current ^= (1 << bit);
    	XGpio_DiscreteWrite(&Gpio, channel1, current);
    }
    // --------------------------------------设置特定位----------------------------------------//
    void set_channel1_bit(uint8_t bit, uint8_t flag)
    {
    	uint32_t current = XGpio_DiscreteRead(&Gpio, channel1);  // 读取当前输出状态
    	if(flag == GPIO_SET){
    		current |= (1 << bit);
    	} else if (flag == GPIO_RESET){
    		current &= ~(1 << bit);
    	} else {
    		xil_printf("Error: Wrong flag\n");
    	}
    	XGpio_DiscreteWrite(&Gpio, channel1, current);      // 写回新值
    }
    
  2. gpio.h
    #ifndef __GPIO_H
    #define	__GPIO_H
    
    #include <stdint.h>
    
    #define     O_DEBUG_LED     0
    
    // 电平定义
    #define     GPIO_SET        1
    #define     GPIO_RESET      0
    
    uint8_t 	gpio_init();
    void 		toggle_channel1_bit(uint8_t bit);
    void 		set_channel1_bit(uint8_t bit, uint8_t flag);
    #endif
  3. uart.c
    #include "uart.h"
    #include "main.h"
    #include "xuartlite.h"		// 串口驱动API
    #include "xuartlite_l.h"	// 包含寄存器偏移量宏
    #include "xil_exception.h"	// 异常处理
    #include "xintc.h"			// 中断驱动API
    #include "gpio.h"
    
    /*------------------------------------串口------------------------------------*/
    #define 	UART_DEVICE_ID 	XPAR_AXI_UARTLITE_0_DEVICE_ID  		// 串口器件ID
    #define		UART_INTR_ID	XPAR_INTC_0_UARTLITE_0_VEC_ID		// 串口中断ID
    #define		INTC_DEVICE_ID	XPAR_INTC_0_DEVICE_ID 				// 中断控制器ID
    
    #define 	RX_NOEMPTY 		XUL_SR_RX_FIFO_VALID_DATA 			// 接收 FIFO 非空
    
    XUartLite 	Uart;	// 串口实例
    XIntc 		Intc; 	// 中断控制器实例
    
    #define 	RX_BUFFER_SIZE 128
    uint8_t 	rx_buff[RX_BUFFER_SIZE] = {0};
    uint16_t 	rx_index = 0;
    uint8_t 	rx_data = 0;
    void uartIntr_handle(void *CallbackRef){
    	XUartLite *InstancePtr= (XUartLite *)CallbackRef;
    
    	// 读取状态寄存器
    	uint32_t isr_status = XUartLite_ReadReg(InstancePtr->RegBaseAddress ,XUL_STATUS_REG_OFFSET);
    	if(isr_status & RX_NOEMPTY){ //接收 FIFO 中有数据
    	    // 读取数据
    		rx_data = XUartLite_ReadReg(InstancePtr->RegBaseAddress ,XUL_RX_FIFO_OFFSET);
    		rx_buff[rx_index++] = rx_data;
    		// 发送数据
    //		XUartLite_WriteReg(InstancePtr->RegBaseAddress ,XUL_TX_FIFO_OFFSET, rx_data);
    	}
    	if (rx_index > RX_BUFFER_SIZE) {
    		rx_index = 0;
    	}
    	if (rx_data == '\n') {  // 以换行符为结束标志
    		rx_buff[rx_index] = '\0';
    		if(strcmp((const char *)rx_buff, "led_on\r\n") == 0)
    //			xil_printf("led_on\n");
    			set_channel1_bit(O_DEBUG_LED, GPIO_SET);
    		else if(strcmp((const char *)rx_buff, "led_off\r\n") == 0)
    //			xil_printf("led_off\n");
    			set_channel1_bit(O_DEBUG_LED, GPIO_RESET);
    		xil_printf("Data: %s", rx_buff);
    		rx_data = 0;
    		rx_index = 0;
    	}
    }
    uint8_t uart_init(void){
    	// 初始化串口
        uint8_t status = XUartLite_Initialize(&Uart, UART_DEVICE_ID);
        if(status != XST_SUCCESS) return XST_FAILURE;
    
        // 初始化中断控制器
        status = XIntc_Initialize(&Intc, INTC_DEVICE_ID);
        if (status != XST_SUCCESS) return XST_FAILURE;
    
        // 关联中断处理函数
        XIntc_Connect(&Intc, UART_INTR_ID,(XInterruptHandler)uartIntr_handle,&Uart);
    
        // 使能中断控制器
        XIntc_Enable(&Intc,UART_INTR_ID);
    
        // 启用中断控制器	XIN_REAL_MODE:硬件中断模式
        XIntc_Start(&Intc, XIN_REAL_MODE);
    
        // 异常处理
        Xil_ExceptionInit();
        Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,(Xil_ExceptionHandler)XIntc_InterruptHandler , &Intc);
        Xil_ExceptionEnable();
    
        // 使能串口
        XUartLite_EnableInterrupt(&Uart);
        return XST_SUCCESS;
    }
  4. uart.h
    #ifndef	__UART_H
    #define	__UART_H
    
    #include <stdint.h>
    
    uint8_t  	uart_init(void);
    
    #endif
    
  5. main.c
    #include	"main.h"
    #include 	"uart.h"
    #include	"gpio.h"
    
    int main()
    {
        if (uart_init() != XST_SUCCESS) return -1;
        if (gpio_init() != XST_SUCCESS) return -1;
        while(1){
        	xil_printf("hello word!!\n");
    //    	toggle_channel1_bit(O_DEBUG_LED);
        	usleep(2000000);
        }
    }
    
  6. main.h
    #ifndef	__MAIN_H
    #define	__MAIN_H
    
    #include 	"xil_printf.h"      // 轻量级打印函数
    #include 	"xparameters.h"     // 硬件参数宏定义
    #include    "sleep.h"           // 延时函数
    
    #endif
    
  7. 上面代码已经过测试,完全没有问题。
  8. 在上面的串口中断处理函数中,感觉写的比较累赘,大家有什么好的处理方式,可以分享一下,共同探讨、进步。

欢迎各位大佬阅读文章、指正错误、探讨技术(欢迎评论区留言)!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值