[X]关闭

[米联客-XILINX-H3_CZ08_7100] FPGA_SDK入门篇连载-22 PL AXI-CAN实验

文档创建者:FPGA课程
浏览次数:125
最后更新:2024-09-28
文档课程分类-AMD-ZYNQ
AMD-ZYNQ: ZYNQ-SOC » 1_SDK应用方案(仅旗舰型号) » 1-SDK基础入门方案
本帖最后由 FPGA课程 于 2024-9-28 13:41 编辑

​ 软件版本:VIVADO2021.1
操作系统:WIN10 64bit
硬件平台:适用 XILINX A7/K7/Z7/ZU/KU 系列 FPGA
实验平台:米联客-MLK-H3-CZ08-7100开发板
板卡获取平台:https://milianke.tmall.com/
登录“米联客”FPGA社区 http://www.uisrc.com 视频课程、答疑解惑!



1概述
ZYNQ的PS中包含了2个CAN接口,当我们需要更多CAN接口的时候,可以使用PL的AXI-CAN IP实现扩展。在做本文之前,建议读者先了解下CAN协议,前面 “PS CAN总线实验”中,有详细描述,这里不再重复。本文的重点是对AXI-CAN IP的使用。
实验目的:
1:熟悉AXI-CAN控制的使用
2:通过VIVADO搭建axi_can的SOC工程
3:使用VITIS-SDK编写axi_can测试程序,实现类似PS-CAN测试程序功能
2系统框图
bb88b3e053684d508b354e7b7cc99093.jpg
3AXI-CAN IP概述
以下是AXI-CAN功能框图。AXI-CAN IP通过AXI-Lite Slave总线接到处理器中。IP-CORE的功能模块包括数据收发模块、寄存器配置模块、接收过流模块、发送协议引擎,其中发送协议引擎又包括了流处理器和位计时模块。
2c01d29e66424c80a304ce5c360ca704.jpg
3.1特性
-符合ISO 11898 -1、CAN 2.0A、CAN 2.0B标准
-支持标准(11位标识符)和扩展(29位标识符)帧
-支持高达1mb /s的比特率
-发送消息FIFO,深度最多可配置64条消息
-通过一个高优先级的传输缓冲区进行传输优先级
-具有错误或仲裁损失自动重传功能
-接收消息FIFO,深度最多可达64条消息
-支持最多四个接受过滤器的接受过滤
-支持睡眠与唤醒模式
-支持Loop Back模式用于诊断
-可屏蔽错误和状态中断
-可读的错误计数器
3.2寄存器概述
3237d8872bc749ba9bb32938de45b311.jpg
1:SRR软件复位寄存器
写入‘1’到软件复位寄存器(SRR)将CAN控制器置于配置模式。当处于配置模式时,CAN控制器在总线上不传输或接收消息。在上电期间,CEN和SRST位是'0',状态寄存器(SR)中的配置位是'1'。只有当SRR寄存器中的CEN位为'0 '时,才能更改传输层配置寄存器。

ac5170f150544d69bce1d872bdb3ea67.jpg
2:MSR模式选择寄存器
写入到模式选择寄存器(MSR)使CAN控制器可以进入睡眠、回循环或正常模式。在正常模式下,CAN控制器参与正常总线通信。如果SLEEP位设置为“1”,则CAN控制器进入SLEEP模式。如果LBACK位设置为'1 ',则CAN控制器进入回环模式。LBACK和SLEEP位不应该同时为“1”。在任何给定的点,CAN控制器可以处于回环模式或睡眠模式,但不能同时处于回环模式。如果两者都设置,则LBACK模式优先
8e7c11729ee340b594ea36890b460edd.jpg
3:BRPR波特率预分频器
e95e3b96935942599388fd9794454e47.jpg
4:BTR位时间寄存器
d4d97d6964c54903ba74cf1f94b2c3e7.jpg
具体如何计算波特率可与参考前面文章“13CAN总线通信实验”
5:ECR错误计数器寄存器
ee6f77ca71c74725b94ca07da0e2e136.jpg
6:ESR错误状态寄存器
f20e63a8362648eea40b6feded61f32b.jpg
7:SR状态寄存器
deac993018c249b8810641eb9e249237.jpg
8:ISR中断状态寄存器
2477bf57957147c9915a4357a03b838a.jpg
9:IER中断使能寄存器
477e72b6f220466882b9b57563573da7.jpg

3d573946b9d245d58c4cde6d44d7bfea.jpg
10:ICR中断清除寄存器
e7bcc15f9f76411ea813f57206db9384.jpg
11:IDR标识字位描述寄存器
f3bf804da1ba4529be30d93172239968.jpg
12:DLC字位描述寄存器
9fe8b715dc9446428608e5861b8b169f.jpg
13:数据字1和数据字2位描述寄存器
3fa97b03b43441558ec97eb617b5974d.jpg
14:AFR验收滤波器寄存器
a5d2e7ae3e5c403ab5581d1e7dbb3c6f.jpg
15:AFR验收滤波器掩码寄存器
cf375c93c7434f8c8575b3d8c39f30ed.jpg
16:AFIR接收过滤ID寄存器
c53c2a4970b44ac5ac5fcd3477a3644d.jpg
4硬件电路分析
7a062385bf614d789e884f637e0d41b8.jpg
配套工程的FPGA PIN脚定义路径为soc_prj/uisrc/04_pin/ fpga_pin.xdc。
5搭建SOC系统工程
详细的搭建过程这里不再重复,对于初学读者如果还不清楚如何创建SOC工程的,请学习“01Vitis Soc开发入门”这篇文章。
5.1SOC系统工程
5a38505effa645a6b357d843e8173e84.jpg
1:中断设置
6212afaf69f04951a3125c337f3bd65e.jpg
2:设置GP Master接口
3507964de06647babee3c52f18b81b4f.jpg
3:设置复位输出
7cf9fa2399c0449da5f44f8537026998.jpg
4:设置PL时钟
24e190815215458896d3fb19c7b85b25.jpg
5:CAN IP设置
c927f82cc92e4f87828b9aa8ec62252a.jpg
5.2设置AXI外设地址分配
只要添加的AXI总线外设都要正确分配地址,这一步不能遗漏
74a33995bc4e4c8d9d1097ac5e99b72a.jpg
5.3编译并导出平台文件
以下步骤简写,有不清楚的看第一篇文章。
1:单击Block文件à右键àGenerate the Output ProductsàGlobalàGenerate。
2:单击Block文件à右键à Create a HDL wrapper(生成HDL顶层文件)àLet vivado manager wrapper and auto-update(自动更新)。
3:添加配套工程路径下uisrc/04_pin/fpga_pin.xdc约束文件
4:生成Bit文件。
5:导出到硬件: FileàExport HardwareàInclude bitstream
6:导出完成后,对应工程路径的soc_hw路径下有硬件平台文件:system_wrapper.xsa的文件。根据硬件平台文件system_wrapper.xsa来创建需要Platform平台。
cd0115294bac404a8e4d11560feb287e.jpg
6搭建Vitis-sdk工程
创建soc_base sdk platform和APP工程的过程不再重复,如果不清楚请参考本章节第一个demo。
6.1创建SDK Platform工程
515f0a3ff4e8466b84dbf001e2e5bed2.jpg
右击soc_base编译,编译的时间可能会有点长
6.2创建APP工程
314bc8b4c1254b668ec52c0510aa233c.jpg
7程序分析
本文的程序从“PS CAN总线实验”稍作修改而来,主要修改下函数名和变量名,以及PL中断。说明XILINX的AXI-CAN和ZYNQ MPSOC内部的CAN IP使用上基本一样的。具体的分析可以参考“PS CAN总线实验”这篇文章。我们这里给出主要的程序源码。
7.1can_init.c函数
这个函数主要完成can的初始化,配置can的波特率参数。本文AXI-CAN波特率参数和PS-CAN都是采用100M的参考时钟,他们的配置方法相同。
  1. /********************MILIANKE**************************

  2. *Company:Liyang Milian Electronic Technology Co., Ltd
  3. *Technical forum:www.uisrc.com
  4. *Taobao: https://milianke.taobao.com
  5. *Create Date: 2020/12/01
  6. *Module Name:can_init
  7. *Copyright: Copyright (c) milianke
  8. *Revision: 1.1
  9. *Description:

  10. ****************************************************/

  11. #include "can_init.h"

  12. //CAN模块波特率时间参数
  13. can_bit_timing can_bit_timing_config[]=
  14. {
  15.         {rate_1000kbps, 4,  15,  2, 0},
  16.         {rate_800kbps,  24,  2,  0, 0},
  17.         {rate_500kbps,  24,  5,  0, 0},
  18.         {rate_250kbps,  49,  5,  0, 0},
  19.         {rate_125kbps,  99,  5,  0, 0},
  20.         {rate_100kbps,  124, 5,  0, 0},
  21.         {rate_50kbps,   124, 12, 1, 0},
  22.         {rate_20kbps,   249, 15, 2, 1}
  23. };

  24. int Can_Config(XCan *CanInstPtr ,u8 BaudRate)
  25. {
  26.         int i;
  27.         /*
  28.          * Enter Configuration Mode if the device is not currently in
  29.          * Configuration Mode.
  30. * 进入配置模式
  31.          */
  32.         XCan_EnterMode(CanInstPtr, XCAN_MODE_CONFIG);
  33.         while(XCan_GetMode(CanInstPtr) != XCAN_MODE_CONFIG);

  34.         /*
  35.          * Setup Baud Rate Prescaler Register (BRPR) and Bit Timing Register (BTR).
  36.        *  设置波特率时间参数
  37.          *
  38.          */
  39.         for(i=0; i<(sizeof(can_bit_timing_config)/sizeof(can_bit_timing)); i++)
  40.         {

  41.                 if(can_bit_timing_config[i].BaudRate == BaudRate)
  42.                 {
  43.                         XCan_SetBaudRatePrescaler(CanInstPtr, can_bit_timing_config[i].BaudRatePrescaler);
  44.                         XCan_SetBitTiming(CanInstPtr,  can_bit_timing_config[i].SyncJumpWidth,
  45.                                         can_bit_timing_config[i].TimeSegment2,
  46.                                         can_bit_timing_config[i].TimeSegment1);

  47.                         break;
  48.                 }

  49.                 if(i == ((sizeof(can_bit_timing_config)/sizeof(can_bit_timing)) - 1))
  50.                 {
  51.                         xil_printf("Baud rate not found!\r\n");
  52.                         return XST_FAILURE;
  53.                 }
  54.         }
  55. //进入普通模式
  56.         XCan_EnterMode(CanInstPtr, XCAN_MODE_NORMAL);
  57.         while(XCan_GetMode(CanInstPtr) != XCAN_MODE_NORMAL);

  58.         return XST_SUCCESS;

  59. }

  60. //初始化CAN
  61. int Init_CanPlIntr(XCan *CanInstPtr, u16 DeviceId)
  62. {

  63.         int Status;

  64.         Status = XCan_Initialize(CanInstPtr, DeviceId);
  65.         if (Status != XST_SUCCESS) {return XST_FAILURE;}

  66.         Can_Config(CanInstPtr,rate_500kbps);
  67.         /*
  68.          * Enter NORMAL Mode.
  69.          */

  70.         return XST_SUCCESS;
复制代码

7.2can_intr.c函数
这个函数完成PL中断的设置、AXI-CAN中断回调函数设置、CAN数据发送,CAN数据接收。
  1. /*
  2. * can_zynq.c
  3. *
  4. *  Created on: 2018年1月24日
  5. */

  6. #include "sys_intr.h"
  7. #include "canpl_init.h"
  8. #include "canpl_intr.h"

  9. //CAN数据发送
  10. int Can_send(XCan *InstancePtr, can_frame *msg)
  11. {
  12.         int Status;
  13.         u32 TxFrame[XCAN_MAX_FRAME_SIZE_IN_WORDS] = {0};

  14.         TxFrame[0] = (u32)XCan_CreateIdValue((u32)msg->id, (u32)msg->rtr, 0, 0, 0);
  15.         TxFrame[1] = (u32)XCan_CreateDlcValue((u32)msg->len);
  16.         TxFrame[2] = (u32)(((u32)msg->data[3] << 24) | ((u32)msg->data[2] << 16) | ((u32)msg->data[1] << 8) | ((u32)msg->data[0]));
  17.         TxFrame[3] = (u32)(((u32)msg->data[7] << 24) | ((u32)msg->data[6] << 16) | ((u32)msg->data[5] << 8) | ((u32)msg->data[4]));

  18.         /*
  19.          * Wait until TX FIFO has room.
  20.          */
  21.         while (XCan_IsTxFifoFull(InstancePtr) == TRUE);

  22.                 /*
  23.                  * Now send the frame.
  24.                  *
  25.                  * Another way to send a frame is keep calling XCanPs_Send() until it
  26.                  * returns XST_SUCCESS. No check on if TX FIFO is full is needed anymore
  27.                  * in that case.
  28.                  */
  29.         Status = XCan_Send(InstancePtr, TxFrame);
  30.         if (Status != XST_SUCCESS) {
  31.                 /*
  32.                  * The frame could not be sent successfully.
  33.                  */
  34.                 xil_printf("Can send failed\r\n");
  35.         }


  36.         return Status;
  37. }

  38. void can_SendHandler(void *CallBackRef)
  39. {
  40.         /*
  41.          * The frame was sent successfully. Notify the task context.
  42.          */
  43.         xil_printf("Can send done\r\n");
  44. }

  45. //CAN数据接收
  46. void can_RecvHandler(void *CallBackRef)
  47. {
  48.         XCan *CanPtr = (XCan *)CallBackRef;
  49.         int Status;
  50.         int i;
  51.         can_frame msg = {0};
  52.         u8 *FramePtr;

  53.         xil_printf("Can receive done\r\n");

  54.         Status = XCan_Recv(CanPtr, RxFrame);

  55.         if (Status != XST_SUCCESS) {
  56.                 xil_printf("Can receive failed\r\n");
  57.                 return;
  58.         }

  59.         /*
  60.          * Get Identifier and Data Length Code and 8 byte Data.
  61.          */
  62.         msg.id = (u16)((RxFrame[0] & XCAN_IDR_ID1_MASK )>> XCAN_IDR_ID1_SHIFT);
  63.         msg.rtr = (u8)((RxFrame[0] & XCAN_IDR_SRR_MASK) >> XCAN_IDR_SRR_SHIFT);
  64.         msg.len = (u8)((RxFrame[1] & XCAN_DLCR_DLC_MASK)>> XCAN_DLCR_DLC_SHIFT);

  65.         FramePtr = (u8 *)(&RxFrame[2]);

  66.         for(i=0; i<8; i++)
  67.         {
  68.                 msg.data[i] = *FramePtr++;
  69.         }

  70.         xil_printf("frame ID: %d\r\n", msg.id);
  71.         xil_printf("frame type: %s\r\n", msg.rtr ? "remote request frame" : "data frame");
  72.         xil_printf("frame length: %d\r\n", msg.len);

  73.         if(!msg.rtr)
  74.         {
  75.                 for(i=0; i<msg.len; i++)
  76.                 {
  77.                         xil_printf("data %d is: 0x%02x\r\n", i+1, msg.data[i]);
  78.                 }
  79.         }
  80. }
  81. //CAN错误回调函数
  82. void can_ErrorHandler(void *CallBackRef, u32 ErrorMask)
  83. {

  84.         xil_printf("Can error happen\r\n");

  85.         if(ErrorMask & XCAN_ESR_ACKER_MASK) {
  86.                 /*
  87.                  * ACK Error handling code should be put here.
  88.                  */
  89.         }

  90.         if(ErrorMask & XCAN_ESR_BERR_MASK) {
  91.                 /*
  92.                  * Bit Error handling code should be put here.
  93.                  */
  94.         }

  95.         if(ErrorMask & XCAN_ESR_STER_MASK) {
  96.                 /*
  97.                  * Stuff Error handling code should be put here.
  98.                  */
  99.         }

  100.         if(ErrorMask & XCAN_ESR_FMER_MASK) {
  101.                 /*
  102.                  * Form Error handling code should be put here.
  103.                  */
  104.         }

  105.         if(ErrorMask & XCAN_ESR_CRCER_MASK) {
  106.                 /*
  107.                  * CRC Error handling code should be put here.
  108.                  */
  109.         }

  110. }
  111. //CAN事件回调函数
  112. void can_EventHandler(void *CallBackRef, u32 IntrMask)
  113. {
  114.         XCan *CanPtr = (XCan *)CallBackRef;

  115.         xil_printf("Can event happen\r\n");

  116.         if (IntrMask & XCAN_IXR_BSOFF_MASK) {
  117.                 /*
  118.                  * Entering Bus off status interrupt requires
  119.                  * the CAN device be reset and reconfigured.
  120.                  */
  121.                 XCan_Reset(CanPtr);
  122.                 /*
  123.                  * Enter Configuration Mode if the device is not currently in
  124.                  * Configuration Mode.
  125.                  */
  126.                 Can_Config(CanPtr,rate_500kbps);
  127.                 return;
  128.         }

  129.         if(IntrMask & XCAN_IXR_RXOFLW_MASK) {
  130.                 /*
  131.                  * Code to handle RX FIFO Overflow Interrupt should be put here.
  132.                  */
  133.         }

  134.         if(IntrMask & XCAN_IXR_RXUFLW_MASK) {
  135.                 /*
  136.                  * Code to handle RX FIFO Underflow Interrupt
  137.                  * should be put here.
  138.                  */
  139.         }

  140.         if(IntrMask & XCAN_IXR_TXBFLL_MASK) {
  141.                 /*
  142.                  * Code to handle TX High Priority Buffer Full
  143.                  * Interrupt should be put here.
  144.                  */
  145.         }

  146.         if(IntrMask & XCAN_IXR_TXFLL_MASK) {
  147.                 /*
  148.                  * Code to handle TX FIFO Full Interrupt should be put here.
  149.                  */
  150.         }

  151.         if (IntrMask & XCAN_IXR_WKUP_MASK) {
  152.                 /*
  153.                  * Code to handle Wake up from sleep mode Interrupt
  154.                  * should be put here.
  155.                  */
  156.         }

  157.         if (IntrMask & XCAN_IXR_SLP_MASK) {
  158.                 /*
  159.                  * Code to handle Enter sleep mode Interrupt should be put here.
  160.                  */
  161.         }

  162.         if (IntrMask & XCAN_IXR_ARBLST_MASK) {
  163.                 /*
  164.                  * Code to handle Lost bus arbitration Interrupt
  165.                  * should be put here.
  166.                  */
  167.         }
  168. }

  169. //CAN中断设置
  170. int CanPl_Setup_IntrSystem(XScuGic *IntcInstancePtr,XCan *CanInstancePtr,u16 CanIntrId)
  171. {
  172.         int Status;

  173.         /*
  174.          * Connect the device driver handler that will be called when an
  175.          * interrupt for the device occurs, the handler defined above performs
  176.          * the specific interrupt processing for the device.
  177.          */
  178.      //CAN的中断回调函数
  179.         Status = XScuGic_Connect(IntcInstancePtr, CanIntrId,
  180.                                 (Xil_InterruptHandler)XCan_IntrHandler,
  181.                                 (void *)CanInstancePtr);
  182.         if (Status != XST_SUCCESS) {
  183.                 return Status;
  184.         }

  185.      //分配设置,发送回调函数、接收回调函数、错误回调函数、事件回调函数
  186.         XCan_SetHandler(CanInstancePtr, XCAN_HANDLER_SEND ,(void *)can_SendHandler , (void *)CanInstancePtr);
  187.         XCan_SetHandler(CanInstancePtr, XCAN_HANDLER_RECV ,(void *)can_RecvHandler , (void *)CanInstancePtr);
  188.         XCan_SetHandler(CanInstancePtr, XCAN_HANDLER_ERROR,(void *)can_ErrorHandler, (void *)CanInstancePtr);
  189.         XCan_SetHandler(CanInstancePtr, XCAN_HANDLER_EVENT,(void *)can_EventHandler, (void *)CanInstancePtr);

  190.         /*
  191.          * Enable the interrupt for the CAN device.
  192.          */
  193.         XScuGic_Enable(IntcInstancePtr, CanIntrId); //使能CAN使用的,PL中断

  194.         /*
  195.          * Enable all interrupts in CAN device.
  196.          */
  197.         XCan_InterruptEnable(CanInstancePtr, XCAN_IXR_ALL); //使能CAN的中断


  198.         return XST_SUCCESS;
  199. }
复制代码

8方案演示
8.1硬件准备
b511395c6a51415a9b5584bb46f4618e.jpg
8.2实验结果

1:软件安装
根据安装说明安装软件,CAN分析仪软件为“USB_CAN TOOLSetup(V9.05).exe”
ecfb7ab08bb6474b81accc2744824cb0.jpg
2:CAN分析仪的使用

main.c中的宏定义:#define TEST_RX_ONLY注释。重新编译工程。
CAN分析仪与电脑连接,使用CAN分析仪连接CAN模块,再将CAN模块连接开发板。
打开USB-CAN Tool,选择500kbps的波特率连接CAN分析仪。
6210334f8b3745468095cff5820ea37b.jpg
通过SDK将程序下载至开发板运行,此时,在USB-CAN Tool软件中便可抓取开发板每隔1s向外发送的CAN数据帧,如下图所示。数据帧,每个数据帧的第1个字节数据不断递增。
793d4f542735481998142156c1adc7b2.jpg
3:数据接收测试
还原main.c中的宏定义:#define TEST_RX_ONLY。重新编译工程。将程序下载到开发板中运行。
4:数据帧接收
打开USB-CAN Tool软件的数据发送功能,设置如下图所示。通过软件向开发板发送数据长度为8字节的数据帧。发送的第一个字节的数据不断递增。然后,点击立即发送。
f954ae5a99f54b0bbc1e31c63711978f.jpg
同时,开发板通过串口将每次接收到的数据帧信息在SDK中打印出来,如下图所示。可以看到第一个字节的数据在递增。
da8f6e96d3a64c948ca0988642def032.jpg
5:远程帧接收

7e8351144fe741ae90868c764154cd51.jpg
2e383e37e8a547bcab7eb081809a7577.jpg
6:数据收发同时进行
将main.c中的宏定义:#define TEST_RX_ONLY注释,重新编译工程。然后将程序下载到开发板中运行。同时,通过软件向开发板连续发送数据长度为8字节的数据帧。如下图所示。
ce7cbcd2831f4d8ebb4f25ca32d8b75d.jpg






























您需要登录后才可以回帖 登录 | 立即注册

本版积分规则