本帖最后由 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系统框图
3AXI-CAN IP概述以下是AXI-CAN功能框图。AXI-CAN IP通过AXI-Lite Slave总线接到处理器中。IP-CORE的功能模块包括数据收发模块、寄存器配置模块、接收过流模块、发送协议引擎,其中发送协议引擎又包括了流处理器和位计时模块。
3.1特性-符合ISO 11898 -1、CAN 2.0A、CAN 2.0B标准 -支持标准(11位标识符)和扩展(29位标识符)帧 -支持高达1mb /s的比特率 -发送消息FIFO,深度最多可配置64条消息 -通过一个高优先级的传输缓冲区进行传输优先级 -具有错误或仲裁损失自动重传功能 -接收消息FIFO,深度最多可达64条消息 -支持最多四个接受过滤器的接受过滤 -支持睡眠与唤醒模式 -支持Loop Back模式用于诊断 -可屏蔽错误和状态中断 -可读的错误计数器 3.2寄存器概述
1:SRR软件复位寄存器写入‘1’到软件复位寄存器(SRR)将CAN控制器置于配置模式。当处于配置模式时,CAN控制器在总线上不传输或接收消息。在上电期间,CEN和SRST位是'0',状态寄存器(SR)中的配置位是'1'。只有当SRR寄存器中的CEN位为'0 '时,才能更改传输层配置寄存器。
2:MSR模式选择寄存器写入到模式选择寄存器(MSR)使CAN控制器可以进入睡眠、回循环或正常模式。在正常模式下,CAN控制器参与正常总线通信。如果SLEEP位设置为“1”,则CAN控制器进入SLEEP模式。如果LBACK位设置为'1 ',则CAN控制器进入回环模式。LBACK和SLEEP位不应该同时为“1”。在任何给定的点,CAN控制器可以处于回环模式或睡眠模式,但不能同时处于回环模式。如果两者都设置,则LBACK模式优先
3:BRPR波特率预分频器
4:BTR位时间寄存器
具体如何计算波特率可与参考前面文章“13CAN总线通信实验” 5:ECR错误计数器寄存器
6:ESR错误状态寄存器
7:SR状态寄存器
8:ISR中断状态寄存器
9:IER中断使能寄存器
10:ICR中断清除寄存器
11:IDR标识字位描述寄存器
12:DLC字位描述寄存器
13:数据字1和数据字2位描述寄存器
14:AFR验收滤波器寄存器
15:AFR验收滤波器掩码寄存器
16:AFIR接收过滤ID寄存器
4硬件电路分析
配套工程的FPGA PIN脚定义路径为soc_prj/uisrc/04_pin/ fpga_pin.xdc。 5搭建SOC系统工程详细的搭建过程这里不再重复,对于初学读者如果还不清楚如何创建SOC工程的,请学习“01Vitis Soc开发入门”这篇文章。 5.1SOC系统工程
1:中断设置
2:设置GP Master接口
3:设置复位输出
4:设置PL时钟
5:CAN IP设置
5.2设置AXI外设地址分配只要添加的AXI总线外设都要正确分配地址,这一步不能遗漏
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平台。
6搭建Vitis-sdk工程创建soc_base sdk platform和APP工程的过程不再重复,如果不清楚请参考本章节第一个demo。 6.1创建SDK Platform工程
右击soc_base编译,编译的时间可能会有点长 6.2创建APP工程
7程序分析本文的程序从“PS CAN总线实验”稍作修改而来,主要修改下函数名和变量名,以及PL中断。说明XILINX的AXI-CAN和ZYNQ MPSOC内部的CAN IP使用上基本一样的。具体的分析可以参考“PS CAN总线实验”这篇文章。我们这里给出主要的程序源码。 7.1can_init.c函数这个函数主要完成can的初始化,配置can的波特率参数。本文AXI-CAN波特率参数和PS-CAN都是采用100M的参考时钟,他们的配置方法相同。 - /********************MILIANKE**************************
- *Company:Liyang Milian Electronic Technology Co., Ltd
- *Technical forum:www.uisrc.com
- *Taobao: https://milianke.taobao.com
- *Create Date: 2020/12/01
- *Module Name:can_init
- *Copyright: Copyright (c) milianke
- *Revision: 1.1
- *Description:
- ****************************************************/
- #include "can_init.h"
- //CAN模块波特率时间参数
- can_bit_timing can_bit_timing_config[]=
- {
- {rate_1000kbps, 4, 15, 2, 0},
- {rate_800kbps, 24, 2, 0, 0},
- {rate_500kbps, 24, 5, 0, 0},
- {rate_250kbps, 49, 5, 0, 0},
- {rate_125kbps, 99, 5, 0, 0},
- {rate_100kbps, 124, 5, 0, 0},
- {rate_50kbps, 124, 12, 1, 0},
- {rate_20kbps, 249, 15, 2, 1}
- };
- int Can_Config(XCan *CanInstPtr ,u8 BaudRate)
- {
- int i;
- /*
- * Enter Configuration Mode if the device is not currently in
- * Configuration Mode.
- * 进入配置模式
- */
- XCan_EnterMode(CanInstPtr, XCAN_MODE_CONFIG);
- while(XCan_GetMode(CanInstPtr) != XCAN_MODE_CONFIG);
- /*
- * Setup Baud Rate Prescaler Register (BRPR) and Bit Timing Register (BTR).
- * 设置波特率时间参数
- *
- */
- for(i=0; i<(sizeof(can_bit_timing_config)/sizeof(can_bit_timing)); i++)
- {
- if(can_bit_timing_config[i].BaudRate == BaudRate)
- {
- XCan_SetBaudRatePrescaler(CanInstPtr, can_bit_timing_config[i].BaudRatePrescaler);
- XCan_SetBitTiming(CanInstPtr, can_bit_timing_config[i].SyncJumpWidth,
- can_bit_timing_config[i].TimeSegment2,
- can_bit_timing_config[i].TimeSegment1);
- break;
- }
- if(i == ((sizeof(can_bit_timing_config)/sizeof(can_bit_timing)) - 1))
- {
- xil_printf("Baud rate not found!\r\n");
- return XST_FAILURE;
- }
- }
- //进入普通模式
- XCan_EnterMode(CanInstPtr, XCAN_MODE_NORMAL);
- while(XCan_GetMode(CanInstPtr) != XCAN_MODE_NORMAL);
- return XST_SUCCESS;
- }
- //初始化CAN
- int Init_CanPlIntr(XCan *CanInstPtr, u16 DeviceId)
- {
- int Status;
- Status = XCan_Initialize(CanInstPtr, DeviceId);
- if (Status != XST_SUCCESS) {return XST_FAILURE;}
- Can_Config(CanInstPtr,rate_500kbps);
- /*
- * Enter NORMAL Mode.
- */
- return XST_SUCCESS;
复制代码
7.2can_intr.c函数这个函数完成PL中断的设置、AXI-CAN中断回调函数设置、CAN数据发送,CAN数据接收。 - /*
- * can_zynq.c
- *
- * Created on: 2018年1月24日
- */
- #include "sys_intr.h"
- #include "canpl_init.h"
- #include "canpl_intr.h"
- //CAN数据发送
- int Can_send(XCan *InstancePtr, can_frame *msg)
- {
- int Status;
- u32 TxFrame[XCAN_MAX_FRAME_SIZE_IN_WORDS] = {0};
- TxFrame[0] = (u32)XCan_CreateIdValue((u32)msg->id, (u32)msg->rtr, 0, 0, 0);
- TxFrame[1] = (u32)XCan_CreateDlcValue((u32)msg->len);
- TxFrame[2] = (u32)(((u32)msg->data[3] << 24) | ((u32)msg->data[2] << 16) | ((u32)msg->data[1] << 8) | ((u32)msg->data[0]));
- TxFrame[3] = (u32)(((u32)msg->data[7] << 24) | ((u32)msg->data[6] << 16) | ((u32)msg->data[5] << 8) | ((u32)msg->data[4]));
- /*
- * Wait until TX FIFO has room.
- */
- while (XCan_IsTxFifoFull(InstancePtr) == TRUE);
- /*
- * Now send the frame.
- *
- * Another way to send a frame is keep calling XCanPs_Send() until it
- * returns XST_SUCCESS. No check on if TX FIFO is full is needed anymore
- * in that case.
- */
- Status = XCan_Send(InstancePtr, TxFrame);
- if (Status != XST_SUCCESS) {
- /*
- * The frame could not be sent successfully.
- */
- xil_printf("Can send failed\r\n");
- }
- return Status;
- }
- void can_SendHandler(void *CallBackRef)
- {
- /*
- * The frame was sent successfully. Notify the task context.
- */
- xil_printf("Can send done\r\n");
- }
- //CAN数据接收
- void can_RecvHandler(void *CallBackRef)
- {
- XCan *CanPtr = (XCan *)CallBackRef;
- int Status;
- int i;
- can_frame msg = {0};
- u8 *FramePtr;
- xil_printf("Can receive done\r\n");
- Status = XCan_Recv(CanPtr, RxFrame);
- if (Status != XST_SUCCESS) {
- xil_printf("Can receive failed\r\n");
- return;
- }
- /*
- * Get Identifier and Data Length Code and 8 byte Data.
- */
- msg.id = (u16)((RxFrame[0] & XCAN_IDR_ID1_MASK )>> XCAN_IDR_ID1_SHIFT);
- msg.rtr = (u8)((RxFrame[0] & XCAN_IDR_SRR_MASK) >> XCAN_IDR_SRR_SHIFT);
- msg.len = (u8)((RxFrame[1] & XCAN_DLCR_DLC_MASK)>> XCAN_DLCR_DLC_SHIFT);
- FramePtr = (u8 *)(&RxFrame[2]);
- for(i=0; i<8; i++)
- {
- msg.data[i] = *FramePtr++;
- }
- xil_printf("frame ID: %d\r\n", msg.id);
- xil_printf("frame type: %s\r\n", msg.rtr ? "remote request frame" : "data frame");
- xil_printf("frame length: %d\r\n", msg.len);
- if(!msg.rtr)
- {
- for(i=0; i<msg.len; i++)
- {
- xil_printf("data %d is: 0x%02x\r\n", i+1, msg.data[i]);
- }
- }
- }
- //CAN错误回调函数
- void can_ErrorHandler(void *CallBackRef, u32 ErrorMask)
- {
- xil_printf("Can error happen\r\n");
- if(ErrorMask & XCAN_ESR_ACKER_MASK) {
- /*
- * ACK Error handling code should be put here.
- */
- }
- if(ErrorMask & XCAN_ESR_BERR_MASK) {
- /*
- * Bit Error handling code should be put here.
- */
- }
- if(ErrorMask & XCAN_ESR_STER_MASK) {
- /*
- * Stuff Error handling code should be put here.
- */
- }
- if(ErrorMask & XCAN_ESR_FMER_MASK) {
- /*
- * Form Error handling code should be put here.
- */
- }
- if(ErrorMask & XCAN_ESR_CRCER_MASK) {
- /*
- * CRC Error handling code should be put here.
- */
- }
- }
- //CAN事件回调函数
- void can_EventHandler(void *CallBackRef, u32 IntrMask)
- {
- XCan *CanPtr = (XCan *)CallBackRef;
- xil_printf("Can event happen\r\n");
- if (IntrMask & XCAN_IXR_BSOFF_MASK) {
- /*
- * Entering Bus off status interrupt requires
- * the CAN device be reset and reconfigured.
- */
- XCan_Reset(CanPtr);
- /*
- * Enter Configuration Mode if the device is not currently in
- * Configuration Mode.
- */
- Can_Config(CanPtr,rate_500kbps);
- return;
- }
- if(IntrMask & XCAN_IXR_RXOFLW_MASK) {
- /*
- * Code to handle RX FIFO Overflow Interrupt should be put here.
- */
- }
- if(IntrMask & XCAN_IXR_RXUFLW_MASK) {
- /*
- * Code to handle RX FIFO Underflow Interrupt
- * should be put here.
- */
- }
- if(IntrMask & XCAN_IXR_TXBFLL_MASK) {
- /*
- * Code to handle TX High Priority Buffer Full
- * Interrupt should be put here.
- */
- }
- if(IntrMask & XCAN_IXR_TXFLL_MASK) {
- /*
- * Code to handle TX FIFO Full Interrupt should be put here.
- */
- }
- if (IntrMask & XCAN_IXR_WKUP_MASK) {
- /*
- * Code to handle Wake up from sleep mode Interrupt
- * should be put here.
- */
- }
- if (IntrMask & XCAN_IXR_SLP_MASK) {
- /*
- * Code to handle Enter sleep mode Interrupt should be put here.
- */
- }
- if (IntrMask & XCAN_IXR_ARBLST_MASK) {
- /*
- * Code to handle Lost bus arbitration Interrupt
- * should be put here.
- */
- }
- }
- //CAN中断设置
- int CanPl_Setup_IntrSystem(XScuGic *IntcInstancePtr,XCan *CanInstancePtr,u16 CanIntrId)
- {
- int Status;
- /*
- * Connect the device driver handler that will be called when an
- * interrupt for the device occurs, the handler defined above performs
- * the specific interrupt processing for the device.
- */
- //CAN的中断回调函数
- Status = XScuGic_Connect(IntcInstancePtr, CanIntrId,
- (Xil_InterruptHandler)XCan_IntrHandler,
- (void *)CanInstancePtr);
- if (Status != XST_SUCCESS) {
- return Status;
- }
- //分配设置,发送回调函数、接收回调函数、错误回调函数、事件回调函数
- XCan_SetHandler(CanInstancePtr, XCAN_HANDLER_SEND ,(void *)can_SendHandler , (void *)CanInstancePtr);
- XCan_SetHandler(CanInstancePtr, XCAN_HANDLER_RECV ,(void *)can_RecvHandler , (void *)CanInstancePtr);
- XCan_SetHandler(CanInstancePtr, XCAN_HANDLER_ERROR,(void *)can_ErrorHandler, (void *)CanInstancePtr);
- XCan_SetHandler(CanInstancePtr, XCAN_HANDLER_EVENT,(void *)can_EventHandler, (void *)CanInstancePtr);
- /*
- * Enable the interrupt for the CAN device.
- */
- XScuGic_Enable(IntcInstancePtr, CanIntrId); //使能CAN使用的,PL中断
- /*
- * Enable all interrupts in CAN device.
- */
- XCan_InterruptEnable(CanInstancePtr, XCAN_IXR_ALL); //使能CAN的中断
- return XST_SUCCESS;
- }
复制代码
8方案演示
8.1硬件准备
8.2实验结果
1:软件安装根据安装说明安装软件,CAN分析仪软件为“USB_CAN TOOLSetup(V9.05).exe”
2:CAN分析仪的使用
将main.c中的宏定义:#define TEST_RX_ONLY注释。重新编译工程。
将CAN分析仪与电脑连接,使用CAN分析仪连接CAN模块,再将CAN模块连接开发板。
打开USB-CAN Tool,选择500kbps的波特率连接CAN分析仪。
通过SDK将程序下载至开发板运行,此时,在USB-CAN Tool软件中便可抓取开发板每隔1s向外发送的CAN数据帧,如下图所示。数据帧,每个数据帧的第1个字节数据不断递增。
3:数据接收测试 还原main.c中的宏定义:#define TEST_RX_ONLY。重新编译工程。将程序下载到开发板中运行。 4:数据帧接收 打开USB-CAN Tool软件的数据发送功能,设置如下图所示。通过软件向开发板发送数据长度为8字节的数据帧。发送的第一个字节的数据不断递增。然后,点击立即发送。
同时,开发板通过串口将每次接收到的数据帧信息在SDK中打印出来,如下图所示。可以看到第一个字节的数据在递增。
5:远程帧接收
6:数据收发同时进行 将main.c中的宏定义:#define TEST_RX_ONLY注释,重新编译工程。然后将程序下载到开发板中运行。同时,通过软件向开发板连续发送数据长度为8字节的数据帧。如下图所示。
|