ZYNQ:串口-CAN协议转换

前言

目前已经实现zynq的PS-CAN和PL-CAN功能。串口-CAN协议转换是实现以太网-CAN功能的过渡,通过这个流程能够减少后期以太网工程出现问题的频率。阶段性功能目标如下:

  1. 实现数据在CAN调试助手和串口调试助手之间的来回转换,从而了解中断机制和协议转换的基本流程。
  2. 实现串口信号协议解析,将数据发送到特定CAN。此时搭建的BD系统中可以添加8个CAN。
  3. 实现zynq以太网功能。此处问题较多,2021年出现大量的细节问题没有解决。如果问题较大,选择野火开发板熟悉基本流程。
  4. 按照上面的串口逻辑,实现以太网-CAN转换流程。如此,第一阶段的功能预期就完成。

这些目标是灵活的,可以依照需求中途调整。目前以上功能的实现主要是帮助梳理一些设计流程。


1串口-CAN总线

首先实现CAN中断功能,按照example提供例程学习。在中断中解析CAN帧,通过print函数打印字节数据。这个流程是比较简单。与之相对,串口数据无法通过print获取。需要学习串口上位机数据传输流程,从而提取出数据。
在这里插入图片描述
导入工程后,define中断向量号红标。原因是,BD图中CAN IP ip2bus_intrevent引脚没有连接ZYNQ的引脚IRQ_F2P,而这个引脚需要在ZYNQ中单独配置。不同的是,在microblazeip2bus_intrevent引脚是接入AXI interrupt controller

在搭建ZYNQ硬件系统时,需要认真对待每个IP的引脚。因为忽略CAN IP的中断引脚和CAN-CLK时钟引脚,都导致调试系统耗费不小精力

在这里插入图片描述在这里插入图片描述


pin property FREQ_HZ does not match between

在这里插入图片描述
此帖指出,直接删除端口再添加:结果可行。错误原因是,FCLK1的actual frequency 15.873016与设定的16Mhz不同。

尝试修改时钟源,偏差仍然存在。由于这个时钟频率偏差是软件界面显示的结果,猜想是软件稳定性造成,因此打算搁置这个问题。测试发现,此错误不影响波特率匹配。

后来,采用CAN-CLK引脚与FCK1引脚直连方式,这个问题没有再出现。
在这里插入图片描述


中断向量ID错误

更新硬件后,SDK仍然无法检测中断向量号。尝试如下四个操作:

  1. 查看hdf文件时间;
  2. 删除并重新导入CAN中断例程;
  3. RTL和wrapper图中中断相关信号;
  4. 查看注释说明。

在操作4,CAN中断向量名可能出现更新错误,没有正确命名。因为,XPAR_FABRIC_CAN_0_IP2BUS_INTREVENT_INTR宏定义与XScuGic函数中CAN_INTR_VEC_ID定义冲突,声明无法跳转。尝试代入数值61U,错误消失,可以判断是驱动更新问题。
在这里插入图片描述

/******************************************************************/

/* Definitions for Fabric interrupts connected to ps7_scugic_0 */
#define XPAR_FABRIC_CAN_0_IP2BUS_INTREVENT_INTR 61U

	XScuGic_SetPriorityTriggerType(&InterruptController, CAN_INTR_VEC_ID,
					0xA0, 0x3);

	/*
	 * Connect the interrupt handler that will be called when an
	 * interrupt occurs for the device.
	 */
	Status = XScuGic_Connect(&InterruptController, CAN_INTR_VEC_ID,
				 (Xil_ExceptionHandler)XCan_IntrHandler,
				 InstancePtr);
	if (Status != XST_SUCCESS) {
		return Status;
	}

	/*
	 * Enable the interrupt for the Can device.
	 */
	XScuGic_Enable(&InterruptController, CAN_INTR_VEC_ID);

/******************************************************************/

修改完程序,LED自动闪烁,与程序逻辑冲突。尝试检查run as,发现默认ELF文件是intr example。重新生成测试程序的debugger后,程序中断正常运行。

run as操作是常见的问题,可以习惯性点开Launch on hardware选项,避免出现默认操作。
在这里插入图片描述
在这里插入图片描述


数据顺序错乱

CAN口接收转发逻辑如下,ECanTool发送的CAN数据发送到串口调试助手。结果是,数据顺序错乱,也不是倒序。

明确"结果是什么?“和"预期是什么”,能够更加高效发现两者之间的区别。

	if (RxFrame[0] == XCan_CreateIdValue(TEST_MESSAGE_ID, 0, 0, 0, 0)) {
		print("rx id right \r\n");
	}

	FramePtr = (u8 *)(&RxFrame[2]);
	for(Index = 0; Index<8; Index++)
	{
		rxuart[Index] = *FramePtr++;
	}


		xil_printf("rx can data:%d,%d,%d,%d,%d,%d,%d,%d\r\n",rxuart[0],rxuart[1],rxuart[2],rxuart[3],rxuart[4],rxuart[5],rxuart[6],rxuart[7]);

在这里插入图片描述

在这里插入图片描述
查看CAN IP接收FIFO结构,四个字长度存在映射对应关系。从大端和小端角度出发,在指针赋值以及数据赋值时,需要注意数据存储的顺序关系。假设FIFO中的DB顺序与CAN数据是一致的。

逻辑直觉确实较慢,意识在几个概念对象中来回跳跃。此内容不是目前重点,且对大端和小端的概念比较模糊,所以后期再深入分析。

FramePtr = (u8 *)(&RxFrame[2]);
	for(Index = 0; Index<8; Index++)
	{
		rxuart[Index] = *FramePtr++;
		
	}

在这里插入图片描述


expected declaration specifiers or

打开工程时,出现expected declaration specifiers or ‘…’ before '错误,后来在数字61U前添加一个空格就好了。
在这里插入图片描述
在编程时,出现如下两个错误。原先此处正常,不知是修改什么内容影响。too few arguments不合理:因为参数已经全部添加盲点是,直接命名错误,即混淆
XScuGic
ScuGic,代入XScuGic到函数中。

在编程过程中,这种马虎的细节问题经常发生,一般需要自己细致检查才能发现这个问题。原因是,意识模糊,聚焦没有发现盲点
在这里插入图片描述

按照如下逻辑运行程序,发送CAN帧,LED没有正常闪烁。尝试如下两个操作,定位问题:

  1. 测试CAN帧发送:正常
  2. 对比例程代码:缺少exception部分内容;
//配置can波特率500K,频率为16Mhz
    XCan_EnterMode(&Can, XCAN_MODE_CONFIG);
    XCan_SetBaudRatePrescaler(&Can, 3);
    XCan_SetBitTiming(&Can, 2, 1, 4);
    XCan_EnterMode(&Can, XCAN_MODE_NORMAL);
//配置中断控制
    XCan_SetHandler(&Can, XCAN_HANDLER_RECV, (void *)RecvHandler ,(void *)&Can);

    ScuGicCfg = XScuGic_LookupConfig(XPAR_PS7_SCUGIC_0_DEVICE_ID);
    XScuGic_CfgInitialize(&ScuGic, ScuGicCfg, ScuGicCfg->CpuBaseAddress);
    XScuGic_SetPriorityTriggerType(&ScuGic, XPAR_FABRIC_CAN_0_IP2BUS_INTREVENT_INTR, 0xA0, 0x3);
    XScuGic_Connect(&ScuGic,XPAR_FABRIC_CAN_0_IP2BUS_INTREVENT_INTR, (Xil_ExceptionHandler)XCan_IntrHandler, &Can);
    XScuGic_Enable(&ScuGic, XPAR_FABRIC_CAN_0_IP2BUS_INTREVENT_INTR);

    XCan_InterruptEnable(&Can, XCAN_IXR_RXNEMP_MASK);
    while(1)
    {
    	if(flag)
    	{
        	XGpioPs_WritePin(&Gpio, 7, 1);
        	sleep(1);
        	XGpioPs_WritePin(&Gpio, 7, 0);
        	sleep(1);
    	}
    }

    cleanup_platform();
    return 0;
}

static void RecvHandler(void *CallBackRef)
{
	flag = 1;

	return ;
}

添加以上代码,再次出现too few arguments to function错误。原因是,xil是小写。删除中间参数的括号内容后,才区分xilXil

此类参数命名操作,容易忽略命名命名正确性和对应关系,需要从以上两个角度检查:大小写、命名对象和对象映射关系
在这里插入图片描述


can接收中断持续触发

因为程序能够正常发送CAN帧,尝试把接收中断修改为发送中断。LED正常闪烁,但是在中断中添加print函数后,接收中断一直处于触发状态,意味着CAN帧接收存在问题。
在这里插入图片描述
修改中断类型之后、删除所有中断触发类型,持续触发消失。这意味着,不能一次性打开所有中断。参照Uart例程,CAN外设帧发送失效,代码如下。删除新增的UART代码,程序又正常运行,猜想UART代码某个函数停留在while逻辑。可以进入debug模式,定位while逻辑点。但是在检查代码时,观察到持续进入中断会造成程序无法进入。因此,有必要先选择UART的发送模式,看看实际效果:结果可以正常发送数据,仍然无法进入发送中断

 XCan_InterruptEnable(&Can, XCAN_IXR_TXOK_MASK|XCAN_IXR_RXNEMP_MASK);
//参数初始化
	flag = 0;

    init_platform();
//配置gpio-ps
    GpioCfg = XGpioPs_LookupConfig(XPAR_PS7_GPIO_0_DEVICE_ID);
    XGpioPs_CfgInitialize(&Gpio, GpioCfg, GpioCfg->BaseAddr);
    XGpioPs_SetDirectionPin(&Gpio, 7, 1);
    XGpioPs_SetOutputEnablePin(&Gpio, 7, 1);

//初始化配置串口
    UartCfg = XUartPs_LookupConfig(XPAR_PS7_UART_1_DEVICE_ID);
    XUartPs_CfgInitialize(&Uart, UartCfg , UartCfg->BaseAddress);
    //修改点++XUartPs_SetBaudRate(&Uart, 115200);
    print("Hello World count:12\n\r");


//配置测试can-pl
    status = XCan_Initialize(&Can,XPAR_CAN_0_DEVICE_ID);
    if(status != XST_SUCCESS)
    {
    	print("can init error \r\n");
    }

//配置can波特率500K,频率为16Mhz
    XCan_EnterMode(&Can, XCAN_MODE_CONFIG);
    XCan_SetBaudRatePrescaler(&Can, 3);
    XCan_SetBitTiming(&Can, 2, 1, 4);
    XCan_EnterMode(&Can, XCAN_MODE_NORMAL);
//CAN帧发送测试
    FramePtr[0] = XCan_CreateIdValue(11, 0, 0, 0, 0);
    FramePtr[1] = XCan_CreateDlcValue(8);

    ptr = (u8 *)(&FramePtr[2]);
    for(i = 0; i<8; i++)
    {
    	*ptr++ = 10;
    }


//配置pl-cant和uart中断控制

    //pl-can
    XCan_SetHandler(&Can, XCAN_HANDLER_RECV, (void *)RecvHandler ,(void *)&Can);
    XCan_SetHandler(&Can, XCAN_HANDLER_SEND, (void *)SendHandler ,(void *)&Can);
    XCan_InterruptEnable(&Can, XCAN_IXR_TXOK_MASK);
    //ps-uart
    XUartPs_SetHandler(&Uart, (XUartPs_Handler)Handler, (void *)&Uart);
    XUartPs_SetInterruptMask(&Uart,XUARTPS_IXR_RXFULL);
    XUartPs_SetOperMode(&Uart, XUARTPS_OPER_MODE_NORMAL);
    XUartPs_SetRecvTimeout(&Uart, 8);



    //scugic配置
    ScuGicCfg = XScuGic_LookupConfig(XPAR_PS7_SCUGIC_0_DEVICE_ID);
    status = XScuGic_CfgInitialize(&ScuGic, ScuGicCfg, ScuGicCfg->CpuBaseAddress);
    if(status != XST_SUCCESS)
    {
    	print("scugic init error \r\n");
    }

    //优先级
    XScuGic_SetPriorityTriggerType(&ScuGic, XPAR_FABRIC_CAN_0_IP2BUS_INTREVENT_INTR, 0xA0, 0x3);
    //XScuGic_SetPriorityTriggerType(&ScuGic, XPAR_XUARTPS_1_INTR, 0xA0, 0x3);
    //中断控制连接
    XScuGic_Connect(&ScuGic,XPAR_FABRIC_CAN_0_IP2BUS_INTREVENT_INTR, (Xil_ExceptionHandler)XCan_IntrHandler, &Can);
    XScuGic_Connect(&ScuGic,XPAR_XUARTPS_1_INTR, (Xil_ExceptionHandler)XUartPs_InterruptHandler, &Uart);
    //使能控制
    XScuGic_Enable(&ScuGic, XPAR_FABRIC_CAN_0_IP2BUS_INTREVENT_INTR|XPAR_XUARTPS_1_INTR);

    //配置 xil exception对象
    Xil_ExceptionInit();
    Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT, (Xil_ExceptionHandler)XScuGic_InterruptHandler, &ScuGic);
    Xil_ExceptionEnable();


//can帧发送测试
    XCan_Send(&Can, FramePtr);
    while(1)
    {
    	if(1)
    	{
        	XGpioPs_WritePin(&Gpio, 7, 1);
        	sleep(1);
        	XGpioPs_WritePin(&Gpio, 7, 0);
        	sleep(1);
    	}
    }


    cleanup_platform();
    return 0;
}

static void RecvHandler(void *CallBackRef)
{
	int status;
	XCan *CanPtr = (XCan *)CallBackRef;

	status = XCan_Recv(CanPtr, RxFrame);
	if(status != XST_SUCCESS)
	{
		print("XCan_Recv\r\n");
	}

	//flag = 1;
	print("recv ok \r\n");


	return ;
}

static void SendHandler(void *CallBackRef)
{
	flag = 1;
	print("send ok \r\n");
	return ;
}

void Handler(void *CallBackRef, u32 Event, unsigned int EventData)
{

	print("uart intr ok\r\n");
	return ;
}

无法进入uart接收和发送中断

程序流程比较繁杂,回调函数使用频率较高,于是再次细致梳理程序流逻辑。

观察到,两种使能方式产生的现象不同。位与方式会导致无法发送CAN帧,修改程序为前者,程序正常工作。

XScuGic_Enable(&ScuGic, XPAR_FABRIC_CAN_0_IP2BUS_INTREVENT_INTR);
XScuGic_Enable(&ScuGic, XPAR_XUARTPS_1_INTR);XScuGic_Enable(&ScuGic, XPAR_FABRIC_CAN_0_IP2BUS_INTREVENT_INTR|XPAR_XUARTPS_1_INTR);

注意到,uart的中断特点与我预期不同。比如,CAN发送成功存在中断,uart没有这种中断分类。尝试修改中断设置为XUARTPS_IXR_MASK时,程序正常进入中断!这意味着,区分uart每种中断的特点,才能正常触发中断。

XUartPs_SetInterruptMask(&Uart,XUARTPS_IXR_MASK);

#define XCAN_IXR_TXOK_MASK	0x00000002  /**< TX Successful Interrupt Mask */

#define XUARTPS_IXR_RBRK	0x00002000U /**< Rx FIFO break detect interrupt */
#define XUARTPS_IXR_TOVR	0x00001000U /**< Tx FIFO Overflow interrupt */
#define XUARTPS_IXR_TNFUL	0x00000800U /**< Tx FIFO Nearly Full interrupt */
#define XUARTPS_IXR_TTRIG	0x00000400U /**< Tx Trig interrupt */
#define XUARTPS_IXR_DMS		0x00000200U /**< Modem status change interrupt */
#define XUARTPS_IXR_TOUT	0x00000100U /**< Timeout error interrupt */
#define XUARTPS_IXR_PARITY 	0x00000080U /**< Parity error interrupt */
#define XUARTPS_IXR_FRAMING	0x00000040U /**< Framing error interrupt */
#define XUARTPS_IXR_OVER	0x00000020U /**< Overrun error interrupt */
#define XUARTPS_IXR_TXFULL 	0x00000010U /**< TX FIFO full interrupt. */
#define XUARTPS_IXR_TXEMPTY	0x00000008U /**< TX FIFO empty interrupt. */
#define XUARTPS_IXR_RXFULL 	0x00000004U /**< RX FIFO full interrupt. */
#define XUARTPS_IXR_RXEMPTY	0x00000002U /**< RX FIFO empty interrupt. */
#define XUARTPS_IXR_RXOVR  	0x00000001U /**< RX FIFO trigger interrupt. */
#define XUARTPS_IXR_MASK	0x00003FFFU /**< Valid bit mask */

CAN帧数据转换逻辑

虽然串口数据的发送单位是字节,但是UART的数据存储单位是RxFIFO。中断和操作也是围绕这个FIFO来进行的,UART也没有所谓接收中断。与之相对,接收单个CAN帧可以触发中断,同时相关数据也存储在一组连续的地址中。
在这里插入图片描述
简单为先,采取以下转换方式:接收8个字节数据,即发送一个CAN帧。换言之,串口调试助手发送8个HEX数据,CAN端口就会发送一帧CAN。这种形式简单,逻辑表达方便。

观察到,数据接收正常,但是数据顺序和位次存在异常。发送内容是字符”3“的HEX,接收为10,13,10,13,这与ASCII表中映射关系不符合。此贴比较深入分析了如何将实数通过串口发送,满足协议要求。这提醒我,这种串口协议转发是可行的,但是需要注意其中的转换细节。
在这里插入图片描述
理想转换思路是串口调试助手发送字符’321’,十六进制数是0x31 0x32 0x33三个字节。RxFIFO接收三个字节数据,触发FIFO非空中断。XUartPs_Recv函数提取三个字节数据,并装入CAN总线帧中。按照这种逻辑,每点击一次发送数据ECanTools就能够接收到一帧CAN。结果是,可以正常接收数据,但是数据顺序存在异常。猜想是,前面发送的串口数据偏短,导致里面的数据错位。
在这里插入图片描述
猜想原因是,串口调试助手实际发送数据与发送框内的数据相关,而非上面的发送框(31-32-33那排)。这种字符顺序错位问题,应该不是当前重点,可以先考虑后面的关键内容。前期学习的重点是,通过实现主体功能来了解基本代码流程,否则就会除入到各种字符转换的繁杂逻辑中。
在这里插入图片描述


2串口信号协议解析

章节1主要实现了CAN-串口的上行和下行数据收发,了解双向数据接收中中断机制和中断类型特点。章节2主要目标是,解析串口协议判断数据发送到什么CAN口。当CAN口数量较多,串口协议数据具有指向性时,可以定向发送。比如设置串口trigger为8,前面一个字节是CAN口位,比如0x55是can0,0x66是can1等。注意,这个阶段目标需要注意协议发送的正确性,这是上个阶段没有的特征。通过这种有节奏的设定目标,可以增加每个阶段目标的吸引力。如果一个阶段目标太长,一直陷在单个问题里,动力就会大幅减少。


int main()
{
	u32 status;
	u32 FramePtr[4];
	u8 i;
	u8 *ptr;
//参数初始化
	flag = 0;
	uartRxFlag = 0;

    init_platform();
//配置gpio-ps
    GpioCfg = XGpioPs_LookupConfig(XPAR_PS7_GPIO_0_DEVICE_ID);
    XGpioPs_CfgInitialize(&Gpio, GpioCfg, GpioCfg->BaseAddr);
    XGpioPs_SetDirectionPin(&Gpio, 7, 1);
    XGpioPs_SetOutputEnablePin(&Gpio, 7, 1);

//初始化配置串口
    UartCfg = XUartPs_LookupConfig(XPAR_PS7_UART_1_DEVICE_ID);
    XUartPs_CfgInitialize(&Uart, UartCfg , UartCfg->BaseAddress);
    XUartPs_SetBaudRate(&Uart, 115200);
    print("Hello World count:13\n\r");


//配置测试can-pl
    status = XCan_Initialize(&Can,XPAR_CAN_0_DEVICE_ID);
    if(status != XST_SUCCESS)
    {
    	print("can init error \r\n");
    }

//配置can波特率500K,频率为16Mhz
    XCan_EnterMode(&Can, XCAN_MODE_CONFIG);
    XCan_SetBaudRatePrescaler(&Can, 3);
    XCan_SetBitTiming(&Can, 2, 1, 4);
    XCan_EnterMode(&Can, XCAN_MODE_NORMAL);
//CAN帧发送测试
    FramePtr[0] = XCan_CreateIdValue(11, 0, 0, 0, 0);
    FramePtr[1] = XCan_CreateDlcValue(8);

    ptr = (u8 *)(&FramePtr[2]);
    for(i = 0; i<8; i++)
    {
    	*ptr++ = 10;
    }


//配置pl-cant和uart中断控制

    //pl-can
    XCan_SetHandler(&Can, XCAN_HANDLER_RECV, (void *)RecvHandler ,(void *)&Can);
    XCan_SetHandler(&Can, XCAN_HANDLER_SEND, (void *)SendHandler ,(void *)&Can);
    XCan_InterruptEnable(&Can, XCAN_IXR_TXOK_MASK|XCAN_IXR_RXNEMP_MASK);
    //ps-uart
    XUartPs_SetHandler(&Uart, (XUartPs_Handler)Handler, (void *)&Uart);

    XUartPs_SetFifoThreshold(&Uart, 8);	//修改中断发送满以及上限值;是接收阈值
    XUartPs_SetInterruptMask(&Uart,XUARTPS_IXR_RXOVR|XUARTPS_IXR_RXFULL); //TP12 修改中断状态
    XUartPs_SetOperMode(&Uart, XUARTPS_OPER_MODE_NORMAL);
    //XUartPs_SetRecvTimeout(&Uart, 8);

//    for(i = 0; i<100; i++)
//    {
//    	TxUartBuffrt[i] = (i % 26) + 'A';
//    }



    //scugic配置
    ScuGicCfg = XScuGic_LookupConfig(XPAR_PS7_SCUGIC_0_DEVICE_ID);
    status = XScuGic_CfgInitialize(&ScuGic, ScuGicCfg, ScuGicCfg->CpuBaseAddress);
    if(status != XST_SUCCESS)
    {
    	print("scugic init error\r\n");
    }

    //优先级
    XScuGic_SetPriorityTriggerType(&ScuGic, XPAR_FABRIC_CAN_0_IP2BUS_INTREVENT_INTR, 0xA0, 0x3);
    XScuGic_SetPriorityTriggerType(&ScuGic, XPAR_XUARTPS_1_INTR, 0xA8, 0x2);
    //中断控制连接
    XScuGic_Connect(&ScuGic,XPAR_FABRIC_CAN_0_IP2BUS_INTREVENT_INTR, (Xil_ExceptionHandler)XCan_IntrHandler, &Can);
    XScuGic_Connect(&ScuGic,XPAR_XUARTPS_1_INTR, (Xil_ExceptionHandler)XUartPs_InterruptHandler, &Uart);
    //使能控制
    XScuGic_Enable(&ScuGic, XPAR_FABRIC_CAN_0_IP2BUS_INTREVENT_INTR);// |XPAR_XUARTPS_1_INTR忽略中断使能,加入uart中断,can帧都无法发送
    XScuGic_Enable(&ScuGic, XPAR_XUARTPS_1_INTR);//单独使能看看运行效果

    //配置 xil exception对象
    Xil_ExceptionInit();
    Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT, (Xil_ExceptionHandler)XScuGic_InterruptHandler, &ScuGic);
    Xil_ExceptionEnable();


//can帧发送测试
    XCan_Send(&Can, FramePtr);
//    XUartPs_Send(&Uart, TxUartBuffrt, 100);
    XUartPs_Recv(&Uart, RxUartBuffrt, 10);

    while(1)
    {
    	if(1)
    	{
        	XGpioPs_WritePin(&Gpio, 7, 1);
        	sleep(1);
        	XGpioPs_WritePin(&Gpio, 7, 0);
        	sleep(1);
    	}
    	//串口帧装入CAN帧
    	if(uartRxFlag)
    	{
    		   FramePtr[0] = XCan_CreateIdValue(10, 0, 0, 0, 0);
    		   FramePtr[1] = XCan_CreateDlcValue(8);

    		    ptr = (u8 *)(&FramePtr[2]);
    		    for(i = 0; i<8; i++)
    		    {
    		    	*ptr++ = RxUartBuffrt[i];
    		    }
    		    XCan_Send(&Can, FramePtr);
    		    uartRxFlag = 0;//注意清除标志位
    	}
    }

    cleanup_platform();
    return 0;
}

static void RecvHandler(void *CallBackRef)
{
	//中断数据接收是必要的,否则影响功能或者led闪烁
	int status;
	XCan *CanPtr = (XCan *)CallBackRef;

	status = XCan_Recv(CanPtr, RxFrame);
	if(status != XST_SUCCESS)
	{
		print("XCan_Recv\r\n");
	}

	//flag = 1;
	print("recv ok \r\n");


	return ;
}

static void SendHandler(void *CallBackRef)
{
	flag = 1;
	print("send ok \r\n");
	return ;
}

void Handler(void *CallBackRef, u32 Event, unsigned int EventData)
{
//
	XUartPs *UartPtr = (XUartPs *)CallBackRef;


	if(Event == XUARTPS_EVENT_RECV_DATA	)
	{
		print("event sent \r\n");
	}
	XUartPs_Recv(UartPtr, RxUartBuffrt,8);
	uartRxFlag = 1;//uart接收中断信号

	print("uart intr ok\r\n");
	return ;
}

多can口bd系统

在搭建此系统前,存在两个方面问题:

  1. 接口引脚映射,can-clk无法直接接入FCLK1,需要修改wrapper文件生成。后期测试表明,可以直接连接
  2. PL IO口映射,明确36通用标准IO口中能够用于CAN总线通信的引脚。从功能、bank、差分、MBG等方面考虑。因此,在RTL的IO planning中有意从这几个方面配置IO,判断那些搭配是可行的。
    在这里插入图片描述
    测试 PL-IO组合如下表:7020引脚分配表
can0
G17  IO_L16P_T2_35            2                  35    NA            NA                  HR        NA
G18  IO_L16N_T2_35            2                  35    NA            NA                  HR        NA
can1-特殊ad功能
L15  IO_L22N_T3_AD7N_35       3                  35    NA            NA                  HR        NA rx
H15  IO_L19P_T3_35            3                  35    NA            NA                  HR        NA tx
can2-期望任何接近的引脚可用,不同功能也可:dqs不是ad
E17  IO_L3P_T0_DQS_AD1P_35    0                  35    NA            NA                  HR        NA rx
E18  IO_L5P_T0_AD9P_35        0                  35    NA            NA                  HR        NA tx 
can3
D20  IO_L4N_T0_35             0                  35    NA            NA                  HR        NA rx
E19  IO_L5N_T0_AD9N_35        0                  35    NA            NA                  HR        NA tx 
can4 不同mbg
J19  IO_L10N_T1_AD11N_35      1                  35    NA            NA                  HR        NA rx
G19  IO_L18P_T2_AD13P_35      2                  35    NA            NA                  HR        NA tx 
can5 不同bank和mbg
P19  IO_L13N_T2_MRCC_34       2                  34    NA            NA                  HR        NA rx 
J16  IO_L24N_T3_AD15N_35      3                  35    NA            NA                  HR        NA tx 
can6  无MBG
K14  IO_L20P_T3_AD6P_35       3                  35    NA            NA                  HR        NA rx
G14  IO_0_35                  NA                 35    NA            NA                  HR        NA tx 
can7
F16  IO_L6P_T0_35             0                  35    NA            NA                  HR        NA rx
D18  IO_L3N_T0_DQS_AD1N_35    0                  35    NA            NA                  HR        NA tx
  1. CAN0正常,说明原先的接线方式是可行的。RTL图也是显示连接状态。区分错误CAN数据和CANID两个值
  2. CAN1没有反应
  3. CAN2没有反应【为了避免是其它问题,重新连上G17和G18,能够发送】
  4. CAN3没有反应
  5. CAN4可以,难道是AD引脚原因?不过MBG似乎不影响功能。按照这个规律can5-can6-can7都不可以,因为是不同功能引脚,或者通用引脚。
  6. CAN5没有反应【小梅哥指出emio-pscan没有出现这个问题】
  7. CAN6没有反应
  8. CAN7没有反应

总结:以上内容说明不同功能引脚可能影响CAN信号,以及EMIO不影响;为了测试这种差异,添加以下类别引脚:ps-emio引脚测试,AD功能引脚-区分P和N端,DQS功能以及MRCC功能等。表格如下:

can1  测试CAN4类型
L15  IO_L22N_T3_AD7N_35       3                  35    NA            NA                  HR        NA rx
E19  IO_L5N_T0_AD9N_35        0                  35    NA            NA                  HR        NA tx
can2 测试P和N因素
K14  IO_L20P_T3_AD6P_35       3                  35    NA            NA                  HR        NA rx
J16  IO_L24N_T3_AD15N_35      3                  35    NA            NA                  HR        NA tx 
can3 测试SRCC功能
N20  IO_L14P_T2_SRCC_34       2                  34    NA            NA                  HR        NA
P20  IO_L14N_T2_SRCC_34       2                  34    NA            NA                  HR        NA
CAN5  相同ad功能
D20  IO_L4N_T0_35             0                  35    NA            NA                  HR        NA
F16  IO_L6P_T0_35             0                  35    NA            NA                  HR        NA
CAN6 相同ad功能
J20  IO_L17P_T2_AD5P_35       2                  35    NA            NA                  HR        NA
H20  IO_L17N_T2_AD5N_35       2                  35    NA            NA                  HR        NA
can7  
L19  IO_L9P_T1_DQS_AD3P_35    1                  35    NA            NA                  HR        NA
K16  IO_L24P_T3_AD15P_35      3                  35    NA            NA                  HR        NA
ps-can0
E17  IO_L3P_T0_DQS_AD1P_35    0                  35    NA            NA                  HR        NA
H15  IO_L19P_T3_35            3                  35    NA            NA                  HR        NA
ps-can1
E18  IO_L5P_T0_AD9P_35        0                  35    NA            NA                  HR        NA
K19  IO_L10P_T1_AD11P_35      1                  35    NA            NA                  HR        NA

更新系统时,再次出现FCLK频率问题。这意味着,切换为原来的直接连接线路比较好。但是wrapper文件一直无法正常更新:vivado2018的同步问题一直存在,CAN3与其它CAN接线相同,但是只有它多余了CLK信号。
在这里插入图片描述
在这里插入图片描述

poor placement for routing错误

上个阶段,虽然已经添加了七个引脚,但是没有出现place问题。可是这次又是添加EMIO,又是更换引脚,变动不小。
在这里插入图片描述
此帖提示CLK信号问题,即CAN3的时钟信号外接普通IO。Unplace此引脚,supress unconstrained错误后,没有效果。于是,尝试断开重连BD中的所有CAN-CLK,重新生成wrapper,引脚输出正常。
在这里插入图片描述
测试结果:

  1. CAN0无法正常接收。奇怪
  2. CAN1无法接收。奇怪!
  3. 连前期正常的CAN0都无法正常工作,因此可能是其它问题!

vivado RTL图编译错误

原先猜想是,vivado编译存在问题。检查BD图后,观察到CAN-CLK错误接入到FCLK0【粗心】。这提醒我,以后编译时,通过RTL图检查关键信号的路径。
在这里插入图片描述
奇怪的是,首次出现auto-pc错误。猜想原因是,当时出现一个M08_AXI接口时直接删除。这意味着,不能直接删除BD图的模块,需要慎重调整。目前相关修改没有效果,只有选择删除所有对象、重新搭建BD系统。
在这里插入图片描述
在这里插入图片描述
与预期不同,重新配置BD图时间不多,生成的RTL中CAN-CLK的传送路径也是正常的。观察到,在比特流编译成功时,Vivado软件中的错误message仍然存在。
在这里插入图片描述
测试结果如下:

  1. CAN0测试成功
  2. CAN1测试成功
  3. CAN2测试成功
  4. CAN3测试成功
  5. CAN5测试成功,CAN4上轮测试过
  6. CAN6测试成功
  7. CAN7测试成功。这说明前期的判断失误,CAN口无法发送可能是BD系统搭建错误造成!一般的IO口都能正常使用,如果使用到特殊接口,可以再测试下。

串口协议逻辑思考

目前8CAN-PL和2CAN-PS的BD系统已经搭建完成。接下来问题是,如何设计协议来转换串口数据和CAN帧。

面对这么多的外设对象,不知如何管理:这些CAN外设是用来干什么、数据的特点是什么、准备用来干什么?如果没有具体讨论这些问题,则整个系统无的放矢,缺少具体研究对象。换言之,系统模型是残缺的。此时,又过度联想到freeRTOS或者linux等,思维头绪也越来越混乱。

协议转换的策略调度,没有任何进展,于是决定首先实现部分串口-can协议转换功能。在构思SDK软件程序逻辑过程中,意识到思维比较混乱。

3层注释逻辑和优化

以程序中的ScuGic配置为例,分三个层级说明程序注释:

  1. 说明整体逻辑,函数名基本相同,**//**处于贴最右边;
  2. 与函数左对齐,是整体逻辑的子逻辑。换言之,一个大逻辑分为几个小逻辑;
  3. 位于每段程序右边,具体说明这个函数的作用以及注意事项。

明确注释逻辑的意义在于,引入过去写作的习惯,迁移去年投入写作的状态。这种相似感,可以增强写作编程的熟悉感。同时,编程反馈比较强,写出的内容能够比较快在电脑端进行测试;与之相对,个人观点写作只是表达自己想法,很少能够获得观点反馈。这种差异能够进一步激活编程表达的动力。此外,SDK中的编程提示可以进一步创造这种流畅感,减少那种编程只是一步一步机械写出逻辑的消极感受。

//scugic配置
    
    //初始化
    ScuGicCfg = XScuGic_LookupConfig(XPAR_PS7_SCUGIC_0_DEVICE_ID);
    status = XScuGic_CfgInitialize(&ScuGic, ScuGicCfg, ScuGicCfg->CpuBaseAddress);
    if(status != XST_SUCCESS)
    {
    	print("scugic init error\r\n");
    }

    //优先级
    XScuGic_SetPriorityTriggerType(&ScuGic, XPAR_FABRIC_CAN_0_IP2BUS_INTREVENT_INTR, 0xA0, 0x3);
    XScuGic_SetPriorityTriggerType(&ScuGic, XPAR_XUARTPS_1_INTR, 0xA8, 0x2);
    //中断函数连接
    XScuGic_Connect(&ScuGic,XPAR_FABRIC_CAN_0_IP2BUS_INTREVENT_INTR, (Xil_ExceptionHandler)XCan_IntrHandler, &Can);
    XScuGic_Connect(&ScuGic,XPAR_XUARTPS_1_INTR, (Xil_ExceptionHandler)XUartPs_InterruptHandler, &Uart);
    //使能控制
    XScuGic_Enable(&ScuGic, XPAR_FABRIC_CAN_0_IP2BUS_INTREVENT_INTR);// |XPAR_XUARTPS_1_INTR忽略中断使能,加入uart中断,can帧都无法发送
    XScuGic_Enable(&ScuGic, XPAR_XUARTPS_1_INTR);//单独使能看看运行效果

    //配置 xil exception对象
    Xil_ExceptionInit();
    Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT, (Xil_ExceptionHandler)XScuGic_InterruptHandler, &ScuGic);
    Xil_ExceptionEnable();


双CAN口协议转换

为了简单,首先测试双CAN口。逻辑是用ID区分CAN口,CAN0路接收的数据取首个,CAN1数据取第二个。这个简单逻辑也容易测试。首先直接编写CAN0逻辑,然后在此基础上添加CAN1。

问题是,CAN0能够正常进入中断,但是接收的CAN帧ID IF 逻辑出现错误。结果是,**RxFrame[0]**才是存储ID的数据结构马虎
在这里插入图片描述
在这里插入图片描述
继续添加CAN1代码,实现相应逻辑。CAN结构体设定为结构体数组,方便管理。但是,程序运行之后 没有反应,led也没闪烁。

现象是,串口有输出,但是主while逻辑中的点灯逻辑没有工作。首次定位BUG如下:1,**XCan_Initialize(&Can[i],0)**初始化对象出错,没有设定为i。但是这种现象,一般是程序进入while逻辑造成。否则,什么逻辑问题会造成这种程序现象的发生?

	XCan Can[8];//修改为结构体数组

print工作,主while和CAN发送失效

存在如下现象:

  1. 主程度逻辑如下,但是程序运行后,既没有LED闪烁也没串口发送数据;
  2. 参数初始化化后,print函数正常打印。

原因猜想如下:

  1. 程序运动过程中卡在某个while逻辑中;
  2. 程序运行到某个节点路飞,这个情况曾经出现过。
    while(1)
    {
    	print("test\r\n");
    	sleep(1);
    	if(1)
    	{
        	XGpioPs_WritePin(&Gpio, 7, 1);
        	sleep(1);
        	XGpioPs_WritePin(&Gpio, 7, 0);
        	sleep(1);
    	}
    }

调试过程中,程序卡在can外设中断配置的xil_assert中,而assert中包含一个while循环。这意味着,XCan_SetHandler函数存在问题。但是在重新relaunch后,程序出现如下错误:No source available for “0x100000”

    //pl-can0
      XCan_SetHandler(&Can[0], XCAN_HANDLER_RECV, (void *)RecvHandler ,(void *)&Can[0]);
      XCan_SetHandler(&Can[0], XCAN_HANDLER_SEND, (void *)SendHandler ,(void *)&Can[0]);
      XCan_InterruptEnable(&Can[0], XCAN_IXR_RXNEMP_MASK);
    //pl-can1
      XCan_SetHandler(&Can[1], XCAN_HANDLER_RECV, (void *)RecvHandler1 ,(void *)&Can[1]);
      //XCan_SetHandler(&Can[1], XCAN_HANDLER_SEND, (void *)SendHandler ,(void *)&Can[1]);
      XCan_InterruptEnable(&Can[1], XCAN_IXR_RXNEMP_MASK);
      
int XCan_SetHandler(XCan *InstancePtr, u32 HandlerType,
		    void *CallBackFunc, void *CallBackRef)
{
	Xil_AssertNonvoid(InstancePtr != NULL);
	Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);

#define Xil_AssertNonvoid(Expression)             \
{                                                  \
    if (Expression) {                              \
        Xil_AssertStatus = XIL_ASSERT_NONE;       \
    } else {                                       \
        Xil_Assert(__FILE__, __LINE__);            \
        Xil_AssertStatus = XIL_ASSERT_OCCURRED;   \
        return 0;                                  \
    }                                              \
}
void Xil_Assert(const char8 *File, s32 Line)
{
	/* if the callback has been set then invoke it */
	if (Xil_AssertCallbackRoutine != 0) {
		(*Xil_AssertCallbackRoutine)(File, Line);
	}

	/* if specified, wait indefinitely such that the assert will show up
	 * in testing
	 */
	while (Xil_AssertWait != 0) {
	}
}

No source available for “0x100000”

问题奇怪之外在于,原本程序可以正常debug,但是在调试一遍后,就发生这样的问题。尝试点击resume后,指针直接停在_boot处。切换调试模式为system debugger模式,指针仍然停留在_boot处。

尝试直接进入行动模式,定位问题。首先,测试让两个CAN一同发送CAN帧,发现只有CAN0帧显示在ECanTool。猜想原因是,CAN1初始化问题导致中断配置中进入while的assert循环。为了测试是否是结构化数组造成初始化问题,尝试单独初始化CAN0和CAN1。

for(i = 0;i < 2;i++)
    {
		status = XCan_Initialize(&Can[i],i); //此逻辑唯一与CANx绑定,注意中断号
		if(status != XST_SUCCESS)
		{
			xil_printf("can[%d] init error \r\n",i);
		}

		//can波特率:500K,频率为16Mhz
		XCan_EnterMode(&Can[i], XCAN_MODE_CONFIG);
		XCan_SetBaudRatePrescaler(&Can[i], 5);
		XCan_SetBitTiming(&Can[i], 2, 1, 4);
		XCan_EnterMode(&Can[i], XCAN_MODE_NORMAL);
		//CAN发送:组帧  can0和can1
		FramePtr[0] = XCan_CreateIdValue(13, 0, 0, 0, 0);
		FramePtr[1] = XCan_CreateDlcValue(8);

		FramePtr1[0] = XCan_CreateIdValue(8, 0, 0, 0, 0);
		FramePtr1[1] = XCan_CreateDlcValue(8);

		ptr = (u8 *)(&FramePtr[2]);
		for(i = 0; i<8; i++)
		{
			*ptr++ = 6;
		}

		ptr = (u8 *)(&FramePtr1[2]);
		for(i = 0; i<8; i++)
		{
			*ptr++ = 7;
		}
    }

测试时候,才意识到ECanTool的界面是单独对两个通道进入测试,即为了看CAN1是否接收CAN帧,需要单独点出相应的页面。此外,此上位机无法同时接收两路信号,需要分别接收。单独测试后,CAN0和CAN1实现了正常发送。

在这里插入图片描述
尝试把所有CAN[0]命名改为CAN0,CAN[1]命名改变CAN1。如果中断程序正常运行,则说明数组结构体命名存在一定问题。命名修改后,CAN0和CAN1中断都顺利进入,基本CAN口的中断流程实现。

数组结构体使用存在盲点。面对没有实践过的内容,默认存在末知问题。

如何判断硬件系统正常更新到sdk?

因为要测试任意PL接口能够作为CAN外设的引脚,于是重新修改了PL的IO映射。在vivado进行导出hardware操作后,sdk中的创建时间与实际不符。两个wrapper文件中的system.hdf显示创建时间都为2-28,而非时间3-10。过去经验表明,这个时间应该跟文件系统显示的修改时间是一致的。显示,能够直接判断硬件更新同步到sdk端,是sdk开发的基本。

在这里插入图片描述
在这里插入图片描述
后来发现,因为没有修改BD图,只是修改了IO引脚映射,所以整个HDF文件时间没有变更。判断如下:创建新的工程和硬件平台,但是时间仍然不变。

但是,wrapper.bit虽然是乱码,但是时间仍然能够显示出来。说明vivido的硬件修改是同步的。
在这里插入图片描述

run as 错误

虽然程序能够正常build,但是无法通过run as操作正常下载。在新建应用时,发现hardware-platform和应用往往是绑定的。但是在建立应用时,总是会自动生成新的platform,这就导致了对应问题,即硬件平台与应用需要建立映射关系,但是由于随意删除wrapper的行为,导致影响run as操作。在更新了硬件系统后,wrapper1与应用匹配,可以正常run as。这提醒我,硬件平台wrapper–应用–bsp文件等需要建立 同步关系。
在这里插入图片描述
在测试两个接口后,接口类型完全不同后,就重新开始PCB的设计。

3总结

为什么考虑采用freeRTOS管理外设?
实现多路CAN口协议转换时,需要针对每个CAN口设计对应的中断函数逻辑。这些中断逻辑可能存在不同的优化级,不同的内存管理区域以及不同的复用特点。为了同时管理如此多路CAN口数据,同时提升CAN口的响应效率,需要增加DMA、内存管理和映射策略等办法。或者为了更好管理每路CAN口,有必要采用freertos管理。

为什么需要明确系统具体需求?
按照如下的测试逻辑,可以顺利8个CAN口的收发中断逻辑,无论上行还是下行。但是这只是搭建了一个基本的系统,系统没有任何需要处理的特征数据或者任务。这就需要明确自己系统的目标和具体任务,尤其是这个系统所处环境的数据特征等。如果无法明确每路CAN口的CAN帧数据周期控制特性,整个系统的建立就缺少实际意义。因此,仍然需要明确系统要解决的问题,这个有必要具体思考相关问题。

小的发现

  1. 粗心:在测试每路CAN口时,确实存在粗心问题。在此心态下,经常性混淆不同对象之间的映射关系,比如代码中的不同外设对象命名和处理区别。

  2. 测试技巧:LED闪烁是测试程序功能的一个简便方法,可以在设计数据节点板卡时,添加相应的LED闪烁功能,从而判断程序进入的状态。毕竟板子上的LED闪烁是非常直观的一种现象。

  3. 结构化表达:碰到技术问题的特殊现象,分析原因,修正问题,额外内容补充。按照一定的分析流程,能够减少犹豫,更加高效定位问题。

  • 4
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Linux系统中的串口编程涉及到对串口的调用方式和参数设置。Linux系统中的UART串口驱动框架可以通过网络ftpget、ftpput命令上传和下载文件,同时也可以通过串口进行文件的上传和下载。可以使用lrzsz工具来实现。可以使用stty命令来查看和设置串口参数,例如使用stty -F /dev/ttyS0 -a来查看串口1(/dev/ttyS0)当前的参数,使用stty -F /dev/ttyS0 ispeed 115200 ospeed 115200 cs8来设置串口1(/dev/ttyS0)的波特率为115200,数据位为8位等。使用cat命令可以打印串口数据,例如使用cat /dev/ttyS0可以在终端上显示串口数据。而使用echo命令可以发送数据,例如使用echo "HelloWorld" >/dev/ttyS0可以发送数据到串口1。在ZYNQ平台下,同样可以使用这些方法进行串口编程。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [zynq操作系统: Linux驱动开发串口篇](https://blog.csdn.net/qq_42330920/article/details/115616293)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* [Zynq-Linux移植学习笔记之51-ZYNQ通过串口上传下载文件](https://blog.csdn.net/jj12345jj198999/article/details/122203484)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值