目前已经实现zynq的PS-CAN和PL-CAN功能。串口-CAN协议转换是实现以太网-CAN功能的过渡,通过这个流程能够减少后期以太网工程出现问题的频率。阶段性功能目标如下:
这些目标是灵活的,可以依照需求中途调整。目前以上功能的实现主要是帮助梳理一些设计流程。
首先实现CAN中断功能,按照example提供例程学习。在中断中解析CAN帧,通过print函数打印字节数据。这个流程是比较简单。与之相对,串口数据无法通过print获取。需要学习串口上位机数据传输流程,从而提取出数据。

导入工程后,define中断向量号红标。原因是,BD图中CAN IP ip2bus_intrevent引脚没有连接ZYNQ的引脚IRQ_F2P,而这个引脚需要在ZYNQ中单独配置。不同的是,在microblaze中ip2bus_intrevent引脚是接入AXI interrupt controller。
在搭建ZYNQ硬件系统时,需要认真对待每个IP的引脚。因为忽略CAN IP的中断引脚和CAN-CLK时钟引脚,都导致调试系统耗费不小精力



此帖指出,直接删除端口再添加:结果可行。错误原因是,FCLK1的actual frequency 15.873016与设定的16Mhz不同。
尝试修改时钟源,偏差仍然存在。由于这个时钟频率偏差是软件界面显示的结果,猜想是软件稳定性造成,因此打算搁置这个问题。测试发现,此错误不影响波特率匹配。
后来,采用CAN-CLK引脚与FCK1引脚直连方式,这个问题没有再出现。

更新硬件后,SDK仍然无法检测中断向量号。尝试如下四个操作:
在操作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 ‘…’ before '错误,后来在数字61U前添加一个空格就好了。

在编程时,出现如下两个错误。原先此处正常,不知是修改什么内容影响。too few arguments不合理:因为参数已经全部添加。盲点是,直接命名错误,即混淆XScuGic和ScuGic,代入XScuGic到函数中。
在编程过程中,这种马虎的细节问题经常发生,一般需要自己细致检查才能发现这个问题。原因是,意识模糊,聚焦没有发现盲点。
按照如下逻辑运行程序,发送CAN帧,LED没有正常闪烁。尝试如下两个操作,定位问题:
//配置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是小写。删除中间参数的括号内容后,才区分xil与Xil。
此类参数命名操作,容易忽略命名命名正确性和对应关系,需要从以上两个角度检查:大小写、命名对象和对象映射关系
因为程序能够正常发送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 ;
}
程序流程比较繁杂,回调函数使用频率较高,于是再次细致梳理程序流逻辑。
观察到,两种使能方式产生的现象不同。位与方式会导致无法发送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 */
虽然串口数据的发送单位是字节,但是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那排)。这种字符顺序错位问题,应该不是当前重点,可以先考虑后面的关键内容。前期学习的重点是,通过实现主体功能来了解基本代码流程,否则就会除入到各种字符转换的繁杂逻辑中。

章节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 ;
}
在搭建此系统前,存在两个方面问题:

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
总结:以上内容说明不同功能引脚可能影响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信号。


上个阶段,虽然已经添加了七个引脚,但是没有出现place问题。可是这次又是添加EMIO,又是更换引脚,变动不小。

此帖提示CLK信号问题,即CAN3的时钟信号外接普通IO。Unplace此引脚,supress unconstrained错误后,没有效果。于是,尝试断开重连BD中的所有CAN-CLK,重新生成wrapper,引脚输出正常。

测试结果:
原先猜想是,vivado编译存在问题。检查BD图后,观察到CAN-CLK错误接入到FCLK0【粗心】。这提醒我,以后编译时,通过RTL图检查关键信号的路径。

奇怪的是,首次出现auto-pc错误。猜想原因是,当时出现一个M08_AXI接口时直接删除。这意味着,不能直接删除BD图的模块,需要慎重调整。目前相关修改没有效果,只有选择删除所有对象、重新搭建BD系统。


与预期不同,重新配置BD图时间不多,生成的RTL中CAN-CLK的传送路径也是正常的。观察到,在比特流编译成功时,Vivado软件中的错误message仍然存在。

测试结果如下:
目前8CAN-PL和2CAN-PS的BD系统已经搭建完成。接下来问题是,如何设计协议来转换串口数据和CAN帧。
面对这么多的外设对象,不知如何管理:这些CAN外设是用来干什么、数据的特点是什么、准备用来干什么?如果没有具体讨论这些问题,则整个系统无的放矢,缺少具体研究对象。换言之,系统模型是残缺的。此时,又过度联想到freeRTOS或者linux等,思维头绪也越来越混乱。
协议转换的策略调度,没有任何进展,于是决定首先实现部分串口-can协议转换功能。在构思SDK软件程序逻辑过程中,意识到思维比较混乱。
以程序中的ScuGic配置为例,分三个层级说明程序注释:
明确注释逻辑的意义在于,引入过去写作的习惯,迁移去年投入写作的状态。这种相似感,可以增强写作编程的熟悉感。同时,编程反馈比较强,写出的内容能够比较快在电脑端进行测试;与之相对,个人观点写作只是表达自己想法,很少能够获得观点反馈。这种差异能够进一步激活编程表达的动力。此外,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口。逻辑是用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];//修改为结构体数组
存在如下现象:
原因猜想如下:
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) {
}
}
问题奇怪之外在于,原本程序可以正常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口的中断流程实现。
数组结构体使用存在盲点。面对没有实践过的内容,默认存在末知问题。
为什么考虑采用freeRTOS管理外设?
实现多路CAN口协议转换时,需要针对每个CAN口设计对应的中断函数逻辑。这些中断逻辑可能存在不同的优化级,不同的内存管理区域以及不同的复用特点。为了同时管理如此多路CAN口数据,同时提升CAN口的响应效率,需要增加DMA、内存管理和映射策略等办法。或者为了更好管理每路CAN口,有必要采用freertos管理。
为什么需要明确系统具体需求?
按照如下的测试逻辑,可以顺利8个CAN口的收发中断逻辑,无论上行还是下行。但是这只是搭建了一个基本的系统,系统没有任何需要处理的特征数据或者任务。这就需要明确自己系统的目标和具体任务,尤其是这个系统所处环境的数据特征等。如果无法明确每路CAN口的CAN帧数据周期控制特性,整个系统的建立就缺少实际意义。因此,仍然需要明确系统要解决的问题,这个有必要具体思考相关问题。
小的发现:
粗心:在测试每路CAN口时,确实存在粗心问题。在此心态下,经常性混淆不同对象之间的映射关系,比如代码中的不同外设对象命名和处理区别。
测试技巧:LED闪烁是测试程序功能的一个简便方法,可以在设计数据节点板卡时,添加相应的LED闪烁功能,从而判断程序进入的状态。毕竟板子上的LED闪烁是非常直观的一种现象。
结构化表达:碰到技术问题的特殊现象,分析原因,修正问题,额外内容补充。按照一定的分析流程,能够减少犹豫,更加高效定位问题。