高云FPGA系列教程(8):ARM串口数据接收(中断和轮询方式)

文章目录

    • @[toc]
      • 1. GW1NSR-4C串口外设简介
      • 2. FPGA配置
      • 3. 常用函数
      • 4. 轮询方式接收数据
      • 5. 中断方式接收数据

本文是高云FPGA系列教程的第8篇文章。

本篇文章介绍片上ARM Cortex-M3硬核处理器串口外设的使用,演示轮询方式和中断方式接收串口数据,并进行回环测试,基于TangNano 4K开发板。

参考文档:Gowin_EMPU(GW1NS-4C)软件编程 参考手册

1. GW1NSR-4C串口外设简介

GW1NSR-4C ARM部分共有2个串口外设,都挂载在APB1总线上,最高支持波特率921.6Kbit/s,无奇偶校验位,8位数据位,1位停止位,支持高速测试模式 HSTM(High Speed Test Mode),即每个时钟周期输出1位数据,可以在短时间内传输大量数据。

官方手册上没有描述发送和接收缓存FIFO的深度,所以不确定是否支持缓存。

高云FPGA系列教程(8):ARM串口数据接收(中断和轮询方式)_第1张图片

2. FPGA配置

FPGA部分需要在云源软件中手动使能EMPU串口外设,如下图所示。

高云FPGA系列教程(8):ARM串口数据接收(中断和轮询方式)_第2张图片
不需要其他配置,使用起来非常简单。

3. 常用函数

高云串口驱动函数常用的有以下几个:

//串口初始化,指定波特率和中断使能,高速测试模式等
ErrorStatus UART_Init(UART_TypeDef* UARTx, UART_InitTypeDef* UART_InitStruct)
//获取接收缓存区状态,当接收到数据时,返回SET
FlagStatus UART_GetRxBufferFull(UART_TypeDef* UARTx)
//获取发送缓存区状态
FlagStatus UART_GetTxBufferFull(UART_TypeDef* UARTx)
//发送一个字节
void UART_SendChar(UART_TypeDef* UARTx,char txchar)
//发送字符串
void UART_SendString(UART_TypeDef* pUARTx, char *str)
//接收一个字节,轮询或接收中断时调用,自动
char UART_ReceiveChar(UART_TypeDef* UARTx)
//获取接收中断的状态,当被触发时返回SET
ITStatus UART_GetRxIRQStatus(UART_TypeDef* UARTx)
//获取发送中断的状态
ITStatus UART_GetTxIRQStatus(UART_TypeDef* UARTx)
//清除接收中断
void UART_ClearRxIRQ(UART_TypeDef* UARTx)
//清除发送中断
void UART_ClearTxIRQ(UART_TypeDef* UARTx)

下面来介绍串口接收数据的两种方式:轮询方式和中断方式。

4. 轮询方式接收数据

初始化时不使能接收中断:

void uart0_init(uint32_t BaudRate)
{
	UART_InitTypeDef UART_InitStruct;

	UART_InitStruct.UART_Mode.UARTMode_Tx = ENABLE;
	UART_InitStruct.UART_Mode.UARTMode_Rx = ENABLE;
	UART_InitStruct.UART_Int.UARTInt_Tx = DISABLE;
	UART_InitStruct.UART_Int.UARTInt_Rx = DISABLE;
	UART_InitStruct.UART_Ovr.UARTOvr_Tx = DISABLE;
	UART_InitStruct.UART_Ovr.UARTOvr_Rx = DISABLE;
	UART_InitStruct.UART_Hstm = DISABLE;
	UART_InitStruct.UART_BaudRate = BaudRate;//Baud Rate

	UART_Init(UART0, &UART_InitStruct);
}

主循环中直接把收到的数据通过串口发送出去:

while(1)
{
    if(UART_GetRxBufferFull(UART0))
    {
        cnt_idle = 0;
        rx = UART_ReceiveChar(UART0);
        printf("rec data: %c\r\n", rx);
    }
}

这种简单粗暴的方式,会导致数据丢失,可能是串口接收部分没有FIFO导致:

高云FPGA系列教程(8):ARM串口数据接收(中断和轮询方式)_第3张图片

我们可以采用缓冲区配合超时空闲的方式来处理,首先定义一个缓冲数组用来存储收到的数据,并通过一个计时器来判断当前是否空闲,若空闲则把数据返回:

uint8_t rx = 0;
uint8_t buf[256];
uint16_t buf_idx = 0;
uint32_t cnt_idle = 0;

//空闲超时方式接收不丢失数据
while(1)
{
    //空闲时间计数器
    if(buf_idx != 0)
    {
        cnt_idle++;
    }
    else 
    {
        cnt_idle = 0;
    }
    //数据缓存到数组中
    if(UART_GetRxBufferFull(UART0))
    {
        cnt_idle = 0;
        buf[buf_idx] = UART_ReceiveChar(UART0);
        buf_idx++;
    }
    //长时间没有接收到串口数据,把缓冲区数据返回
    if(cnt_idle > 5000)   //明显感觉=500000
    {
        UART_SendString(UART0, buf);
        cnt_idle = 0;
        buf_idx = 0;
        memset(buf, 0, sizeof(buf)/sizeof(buf[0]));
    }
}

实际测试效果很不错,数据没有任何丢失:

高云FPGA系列教程(8):ARM串口数据接收(中断和轮询方式)_第4张图片

下面来介绍通过串口接收中断的方式来缓存数据。

5. 中断方式接收数据

初始化时使能串口接收中断,并通过NVIC开启串口中断请求。

void uart0_init(uint32_t BaudRate)
{
	UART_InitTypeDef UART_InitStruct;
	NVIC_InitTypeDef InitTypeDef_NVIC;

	UART_InitStruct.UART_Mode.UARTMode_Tx = ENABLE;
	UART_InitStruct.UART_Mode.UARTMode_Rx = ENABLE;
	UART_InitStruct.UART_Int.UARTInt_Tx = DISABLE;
	UART_InitStruct.UART_Int.UARTInt_Rx = ENABLE;   //开启接收中断
	UART_InitStruct.UART_Ovr.UARTOvr_Tx = DISABLE;
	UART_InitStruct.UART_Ovr.UARTOvr_Rx = DISABLE;
	UART_InitStruct.UART_Hstm = DISABLE;
	UART_InitStruct.UART_BaudRate = BaudRate;//Baud Rate

	UART_Init(UART0, &UART_InitStruct);

    //Enable UART0 interrupt handler
    InitTypeDef_NVIC.NVIC_IRQChannel = UART0_IRQn;
    InitTypeDef_NVIC.NVIC_IRQChannelPreemptionPriority = 1;
    InitTypeDef_NVIC.NVIC_IRQChannelSubPriority = 1;
    InitTypeDef_NVIC.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&InitTypeDef_NVIC);
}

串口中断服务函数,数据缓存到数组中,并清零空闲计数器:

void UART0_Handler(void)
{
	char rx = 0;
	
	if(UART_GetRxIRQStatus(UART0) == SET)
	{
		rx = UART_ReceiveChar(UART0);
        buf[buf_idx] = rx;
        buf_idx++;
        cnt_idle = 0;
	}
	
	UART_ClearRxIRQ(UART0);
}

需要注释掉系统默认提供的串口中断服务函数,否则编译会报错。

高云FPGA系列教程(8):ARM串口数据接收(中断和轮询方式)_第5张图片

主循环中通过一个计数器来判断串口是否空闲,当超时没有收到新的数据时,认为串口空闲,把缓冲区的数据返回:

uint8_t rx = 0;
uint8_t buf[256];
uint16_t buf_idx = 0;
uint32_t cnt_idle = 0;

while(1)
{
    //长时间没有接收到串口数据
    if(buf_idx != 0)
        cnt_idle++;
    else 
        cnt_idle = 0;

    if(cnt_idle > 5000)   //明显感觉=500000
    {
        printf("rx: %s", buf);
        cnt_idle = 0;
        buf_idx = 0;
        memset(buf, 0, sizeof(buf)/sizeof(buf[0]));
    }
}

下载,运行,数据完整:

高云FPGA系列教程(8):ARM串口数据接收(中断和轮询方式)_第6张图片


本文是高云FPGA系列教程的第8篇文章。

你可能感兴趣的:(高云,FPGA,小蜜蜂,ARM,串口,中断,轮询)