Ultrascale+ ZYNQ UART(二) 接收不定长数据实例

大家好,这里是程序员杰克。一名平平无奇的嵌入式软件工程师。

上篇推文对Ultrascale+ ZYNQ PS端uart controller进行了介绍和描述。对于开发者而言,最重要的是实际的工程应用。对于PS端的UART的串口回环通信使用,网上资料一搜一大把,此处不进行总结和分享。

本篇推文主要是分享实际应用场景下,PS端接收不定长数据的实例。


下面正式进入本章推送的内容。

01 实现原理和描述

STM32串口接收不定长数据有一种是通过接收中断接收空闲中断来实现。本篇PS端实现串口接收不定长数据的思路类似(ZYNQ等皆适用)。本示例通过设置rxFIFO的缓存阈值触发中断接收超时中断来实现串口接收不定长数据。


串口FIFO中断

在UG1085技术参考手册里面,对uart controller的FIFO中断描述如下图所示:

62edd741abdaa58260321994b6394928.png

上图中,我们需要用到的知识点如红框所示:

  • RxFIFO的缓存阈值由Rcvr_FIFO_trigger_level[RTRIG]位设置

  • Rcvr_FIFO_trigger_level[RTRIG]为6bit,触发阈值最大可设置64-1


在UG1085技术参考手册(MPSOC)中并没有uart寄存器的详细信息,本篇推文对接收触发阈值、接收超时的寄存器配置参考UG585技术参考手册的内容(ZYNQ)进行描述,两者的寄存器内容是一致的。

接收触发阈值寄存器

设置RxFIFO接收阈值触发中断配置寄存器如下图所示:

0909c4ddf61be65f1a4fe3a821d0c92e.png

特别说明:

从寄存器的描述中RTRIG位宽为6bit,即RxFIFO接收触发阈值最大为64-1;


接收超时寄存器

设置接收超时配置寄存器如下图所示:

8623d368b9b09378c636a42709555606.png

特别说明:

从寄存器的描述中RTO位宽为8bit,即RxFIFO接收触发阈值最大为256-1;


02 代码实现

前面已对实现PS端串口接收不定长数据所使用的寄存器进行了描述,本章节开始对使用的代码进行描述。

实现思路

示例的串口接收不定长数据的实现思路如下:

  • 设置RxFIFO触发阈值,用于接收数据到缓存buffer

  • 设置接收空闲超时阈值,用于表示当前帧传输完成


串口接收不定长数据配置过程

对于Ultrascale+ ZYNQ而言,实现PS端串口接收不定长数据的配置实现步骤如下:

    • 初始化串口设备

    • 设置串口工作模式

    • 设置串口通信的数据格式

    • 配置RxFIFO触发中断阈值

    • 配置接收超时阈值

    • 使能接收FIFO阈值中断、接收超时中断中断

    • 中断接收处理

    • 用户逻辑处理


官方API描述

XLINX提供了相关的uart controller的API函数供使用。下表罗列了本示例使用的API函数(仅描述功能, 具体函数自行查看)。

  • uart相关API

API名称类型功能说明
XUartPs_LookupConfig()函数在uart设备表中获取如基地址、设备号等外设配置参数. 
XUartPs_CfgInitialize()配置uart设备结构体的初始化参数内容.
XUartPs_SetOperMode()设置uart的运行模式(正常/自动回传等).
XUartPs_SetDataFormat()设置uart的数据格式. 如波特率、数据位、奇偶位、停止位等参数.
XUartPs_SetFifoThreshold()设置RxFIFO触发中断的阈值.
XUartPs_SetRecvTimeout()设置接收空闲中断的时间阈值(以波特率时钟为节拍).
XUartPs_SetInterruptMask()设置uart的中断掩码寄存器的值, 控制中断使能.
XUartPs_ReceiveBuffer()读取RxFIFO的值到设定的缓存buffer中.
XUartPs_Recv()

RxFIFO有数据时,从FIFO中读取数据;

RxFIFO无数据时,初始化自定义接收buffer.

XUartPs_WriteReg()宏定义向uart设备的寄存器写入数据
XUartPs_ReadReg()读取uart设备的寄存器数据
  • GIC相关API

API名称类型功能说明
XScuGic_LookupConfig()函数在gic设备表中获取如基地址、设备号等外设配置参数. 
XScuGic_CfgInitialize()配置gic设备结构体的初始化参数内容.
Xil_ExceptionInit()通用API, 用于出书哈异常向量处理程序函数. 在ARM Cortex-A53/R5/A9等硬核无任何操作.
Xil_ExceptionRegisterHandler()用于gic设备的全局中断处理回调函数的注册.
XScuGic_Connect()用于向gic注册设备的中断回调函数.
XScuGic_Enable()使能设备中断.
Xil_ExceptionEnableMask()宏定义使能全局中断.

代码描述

在本示例中,杰克设置RxFIFO触发阈值为1,即每接收到一个字节便发起一次中断;设置接收空间超时阈值为8,即接收到数据后、经过8个波特率时钟无数据接收便发起空闲超时中断。具体的C语言代码如下所示:

  • 串口初始化函数

s32 uart_init(XUartPs *uartPtr, u16 deviceId)
{
  s32 status = XST_SUCCESS;
  XUartPs_Config *config;


  //初始化串口设备
  config = XUartPs_LookupConfig(deviceId);
  if(config == NULL)
  {
    return XST_FAILURE;
  }
  status = XUartPs_CfgInitialize(uartPtr, config, config->BaseAddress);
  if(status != XST_SUCCESS)
  {
    return XST_FAILURE;
  }


  XUartPs_SetOperMode(uartPtr, XUARTPS_OPER_MODE_NORMAL);  //设置串口工作模式为正常收发模式
  XUartPs_SetDataFormat(uartPtr, &UartFormat0);            //设置通信数据格式
  XUartPs_SetFifoThreshold(uartPtr, 1);                    //设置RxFIFO中断阈值
  XUartPs_SetRecvTimeout(uartPtr, 8);                      //设置接收超时阈值


  XUartPs_Recv(&uart0, recvBuffer, sizeof(recvBuffer));    //初始化接收buffer相关参数


  return status;
}
  • 中断初始化函数

s32 uart_interrupt_init(XScuGic *intcPtr, XUartPs *uartPtr, u32 uartInteruptID)
{
  s32 status = XST_SUCCESS;


  //向GIC注册串口0的中断回调函数 uart_interupt_handler
  status = XScuGic_Connect(intcPtr,
          uartInteruptID,
          (Xil_InterruptHandler) uart_interupt_handler,
          (void *)uartPtr);


  //设置UART0的中断触发方式: 接收FIFO触发、接收超时
  XUartPs_SetInterruptMask(uartPtr, XUARTPS_IXR_RXOVR | XUARTPS_IXR_TOUT);


  //使能GIC的串口中断
  XScuGic_Enable(intcPtr, uartInteruptID);


  return status;
}
  • 中断处理函数

void uart_interupt_handler(XUartPs *uartPtr)
{
  u32 isr_status;


  Xil_AssertVoid(uartPtr != NULL);
  Xil_AssertVoid(uartPtr->IsReady == XIL_COMPONENT_IS_READY);


  isr_status = XUartPs_ReadReg(uartPtr->Config.BaseAddress, XUARTPS_IMR_OFFSET);
  isr_status &= XUartPs_ReadReg(uartPtr->Config.BaseAddress, XUARTPS_ISR_OFFSET);


  if(isr_status & (u32)XUARTPS_IXR_RXOVR)    //RxFIFO接收阈值触发中断
  {
    if (uartPtr->ReceiveBuffer.RemainingBytes != (u32)0)
    {
      (void)XUartPs_ReceiveBuffer(uartPtr);
    }
    XUartPs_WriteReg(uartPtr->Config.BaseAddress, XUARTPS_ISR_OFFSET, XUARTPS_IXR_RXOVR);
  }
  else if(isr_status & (u32)XUARTPS_IXR_TOUT)  //接收超时(空闲)
  {
    recvFlag = 1;
    recvLen = uartPtr->ReceiveBuffer.RequestedBytes - uartPtr->ReceiveBuffer.RemainingBytes;
    XUartPs_WriteReg(uartPtr->Config.BaseAddress, XUARTPS_ISR_OFFSET, XUARTPS_IXR_TOUT);
  }
}
  • 全局中断相关函数

int interrupt_init(XScuGic *intcPtr, u16 DeviceId)
{
  s32 status;
  XScuGic_Config *intc_cfg;


  //初始化中断控制器GIC
  intc_cfg = XScuGic_LookupConfig(DeviceId);
  if(intc_cfg == NULL)
  {
    return XST_FAILURE;
  }
  status = XScuGic_CfgInitialize(intcPtr, intc_cfg, intc_cfg->CpuBaseAddress);
  if(status != XST_SUCCESS)
  {
    return XST_FAILURE;
  }


  return status;
}


void Interrupt_Setup_Exception(XScuGic *intcPtr)
{
  //打开中断异常处理
  Xil_ExceptionInit();


  Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
          (Xil_ExceptionHandler)XScuGic_InterruptHandler,
          (void *)intcPtr);


  Xil_ExceptionEnableMask(XIL_EXCEPTION_IRQ);
}
  • 主逻辑函数

int main()
{
  s32 status;


  //中断GIC初始化
  interrupt_init(&intc, XPAR_PSU_ACPU_GIC_DEVICE_ID);


  //串口0初始化
  status = uart_init(&uart0, XPAR_PSU_UART_0_DEVICE_ID);
  if(status == XST_FAILURE)
  {
    return 0;
  }
  uart_interrupt_init(&intc, &uart0, UART0_INT_IRQ_ID); //使能uart接收中断和接收超时中断
  
  //全局中断使能
  Interrupt_Setup_Exception(&intc);


  while(1)
  {
    if(recvFlag)
    {
      recvFlag = 0;
      /*
       * 业务逻辑
       */
      XUartPs_Recv(&uart0, recvBuffer, sizeof(recvBuffer)); //初始化接收buffer相关参数
    }
  }


    return 0;
}

特别说明:

以上内容基于官方提供的demo,在此基础上进行二次开发。由于公众号篇幅原因,杰克仅是对官方API进行了简单的功能描述,并未对上面所使用的一些官方API函数进行深入说明,若需要深入了解可参考官方例程。


03 文章总结

XILINX的SOC芯片,无论ZYNQ、还是杰克使用的ZYNQ Ultrascale+ MPSOC皆适用于该实现方法。对于开发者而言,最重要的是实现思路以及相关API的灵活使用。由于篇幅原因,相关官方API函数杰克并未进行说明,杰克强烈建议大家深入到具体代码中了解。

参考文档:

ug585-Zynq-7000-TRM.pdf

ug1085-zynq-ultrascale-trm.pdf

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

杰克拉力船长

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值