这篇文章主要介绍串口中断,使用上位机下发数据,使用FPGA串口接收消息,并通过判断接收到的数据与预定数据是否相同控制led灯的亮灭,在主函数中使用延迟的方式定时向上位机发送数据。
一、平台搭建
- Microblaze软核的配置可以看我之前的文章——《第一章MicroBlaze软核设计--GPIO(中断)》,写的非常详细,也可参考正点原子的Microblaze开发指南V2.0开发文档,这里不多赘述。
- GPIO核与之前一样,只需要配置一个输出端口用来控制led就可以。
- 在上一节基础上,需要配置一个UART模块用来接收、发送消息。对于串口IP核的工作原理,可以参考下面这位博主写的文章。【【FPGA 之Micro Blaze的串口中断实验】】_microblaze 中断-CSDN博客
- 对于串口IP核设置只需要配置波特率就可以,也可以使用默认的波特率,这里我使用的115200。与上位机通信时,注意波特率的选择。
- 最后是整个工程的Block Design的布局
- 为了统一接口,在创建一个顶层文件,最后的工程便是这样
- 接下来就是绑定管脚,生成bit流文件,导出xsa平台,可参考《第一章MicroBlaze软核设计--GPIO(中断)》文章中第一、6节内容。
二、Vitis工程搭建
- 参考我之前写的《第一章MicroBlaze软核设计--GPIO(中断)》文章中第二节,Vitis工程搭建。
三、代码编写
- 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); // 写回新值 }
- 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
- 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; }
- uart.h
#ifndef __UART_H #define __UART_H #include <stdint.h> uint8_t uart_init(void); #endif
- 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); } }
- main.h
#ifndef __MAIN_H #define __MAIN_H #include "xil_printf.h" // 轻量级打印函数 #include "xparameters.h" // 硬件参数宏定义 #include "sleep.h" // 延时函数 #endif
- 上面代码已经过测试,完全没有问题。
- 在上面的串口中断处理函数中,感觉写的比较累赘,大家有什么好的处理方式,可以分享一下,共同探讨、进步。
欢迎各位大佬阅读文章、指正错误、探讨技术(欢迎评论区留言)!