CAN->ethernet
- 测试CAN通信功能,
重新搭建工程
在试用以前工程时,失误删除某个文件,导致sdk软件闪退,于是选择搭建新的工程。考虑减少鼠标操作。
vivado更新同步问题依然存在,BD修改无法马上同步到wrapper文件。通过修改BD图的信号,重新output product有效。
因为GPIO IP默认宽度为32位,生成的信号接口也是32位接口。删除此接口,wrapper自然会同步更新。
实现Eth-Can协议转换
从recv_callback回调函数获得以太网包的数据结构指针pbuf,通过指针获得其中的payload和len。函数内容如下:
err_t recv_callback(void *arg, struct tcp_pcb *tpcb,
struct pbuf *p, err_t err)
{
/* do not read the packet if we are not in ESTABLISHED state */
if (!p) {
tcp_close(tpcb);
tcp_recv(tpcb, NULL);
print("error recv_callback\r\n");
return ERR_OK; //必要逻辑,避免错误
}
//
// /* indicate that the packet has been received */
// tcp_recved(tpcb, p->len);//这里接收内容
//
// /* echo back the payload */
// /* in this case, we assume that the payload is < TCP_SND_BUF */
// if (tcp_sndbuf(tpcb) > p->len) {
// err = tcp_write(tpcb, p->payload, p->len, 1);
// } else
// xil_printf("no space in tcp_sndbuf\n\r");
//获取pbuf内容,并直接打印
EthPacketPtr = (u8 *)p->payload;
EthPacketLen = (u16)p->len;
EthRecFlag = 1;//状态标志设置
xil_printf("uart 通信测试接收正常\r\n");
/* free the received pbuf */
pbuf_free(p);
xil_printf("t5\r\n");
return ERR_OK;
}
EthPacketPtr指针是全局变量,所以可以在主函数文件中调用。通过操作数据结构,将此以太网数据包数据转换到CAN帧结构中。流程如下:
/*
* CAN 初始化
*
*/
XCan_Initialize(&Can, 0);
//配置模式
XCan_EnterMode(&Can, XCAN_MODE_CONFIG);
XCan_SetBaudRatePrescaler(&Can, 3);//参数错误!
XCan_SetBitTiming(&Can, 2, 2, 7);
//
XCan_EnterMode(&Can, XCAN_MODE_NORMAL);
TxFrame[0] = XCan_CreateDlcValue(8);
TxFrame[1] = XCan_CreateIdValue(1024, 0, 0, 0, 0);
//can帧封装
TxFramePtr = (u8 *)(&TxFrame[2]);//数组取地址缺失
//以太网帧接收转发处理
if(EthRecFlag)
{
EthRecFlag = 0;
for(int i = 0; i < EthPacketLen; i++){
*TxFramePtr++ = *EthPacketPtr++; //eth包宽度未知,影响是什么?
}
XCan_Send(&Can, TxFrame);
下载程序程序正常运行、led闪烁,网络调试助手建立连接后,发送16进制数据。CAN调试助手接收到CAN帧,虽然数据位置顺序出现倒置。
问题是,程序发送一个以太网packet后,lwip连接断开,led不再闪烁,can调试助手无法接收CAN帧。猜想是,程序跑飞或者进入while循环中。
lwip单次发送断开连接
本实验参考例程是SDK2018-lwip echo server,采用echo模式,tcp发送。考虑到例程的特殊性,因此重新采用例程udp perf client。
下载程序文件,调试窗口信息为nothing found at port:COM3。预期是,调试窗口打印UDP client状态信息,网络调试助手发送数据,其也会有相应的反应。
检查设备管理器,发现端口号COM3消失。检查zynq开发板的USB线,出现松动,调整之后正常。重新添加SDK Terminal可以获取以下信息:
-----lwIP RAW Mode UDP Client Application-----
Start PHY autonegotiation
Waiting for PHY to complete autonegotiation.
autonegotiation complete
link speed for phy address 1: 10
ERROR: DHCP request timed out
Configuring default IP 192.168.1.10
Board IP: 192.168.1.10
Netmask : 255.255.255.0
Gateway : 192.168.1.1
UDP client connecting to 192.168.1.100 on port 5001
On Host: Run $iperf -s -i 5 -u
[ 1] local 192.168.1.10 port 49153 connected with 192.168.1.100 port 5001
[ ID] Interval Transfer Bandwidth
现象是,网络调试助手高频接收以太网包,软件直接崩溃。在主main中添加sleep(2)延时,网络调试窗口显示内容如下:
【图片】
内容是,周期性接收到板子的0123456789012循环数据。并且测试程序在sent 302 datagrams后,测试程序完成。
[ 1] 192.0-198.0 sec 11.2 KBytes 15.4 Kbits/sec
[ 1] 198.0-204.0 sec 11.2 KBytes 15.4 Kbits/sec
[ 1] 204.0-210.0 sec 11.2 KBytes 15.4 Kbits/sec
[ 1] 210.0-216.0 sec 11.2 KBytes 15.4 Kbits/sec
[ 1] 216.0-222.0 sec 11.2 KBytes 15.4 Kbits/sec
[ 1] 0.0-300.0 sec 425 KBytes 11.6 Kbits/sec
[ 1] sent 302 datagrams
UDP test passed Successfully
疑惑UDP的数据交互模式,因此再对比看看UDP perf server。按照打印信息提示,下载iperf3工具发送命令,无法连接。
On Host: Run $iperf -c 192.168.1.10 -i 5 -t 300 -u -b <bandwidth>
iperf3: error - unable to connect to server: Connection refused
UDP client connecting to 192.168.1.100 on port 5001
On Host: Run $iperf -s -i 5 -u
//测试出现如下错误,指令无效。
PS D:\lunwen> iperf3 -s -i 5 -u
iperf3: parameter error - some option you are trying to set is client only
iperf3工具测试效果不佳。由于lwip udp perf client例程下,网络调试助手接收信息,串口调试助手接收信息。基于此,判断以太网包是如何从板子发送到上位机,从而做到CAN帧发送到上位机。
梳理lwip udp client例程
例程中,udp_send(pcb[i], packet)发送以太网udp包,memcpy将send_buf数据复制到packet->payload。
static void udp_packet_send(u8_t finished)
{
int *payload;
static int packet_id;
u8_t i;
u8_t retries = MAX_SEND_RETRY;
struct pbuf *packet;
err_t err;
packet = pbuf_alloc(PBUF_TRANSPORT, UDP_SEND_BUFSIZE, PBUF_POOL);
if (!packet) {
xil_printf("error allocating pbuf to send\r\n");
return;
} else {
memcpy(packet->payload, send_buf, UDP_SEND_BUFSIZE);
}
/* always increment the id */
for (i = 0; i < NUM_OF_PARALLEL_CLIENTS; i++) {
payload = (int*) (packet->payload);
if (finished == FINISH)
packet_id = -1;
payload[0] = htonl(packet_id);
while (retries) {
err = udp_send(pcb[i], packet);
if (err != ERR_OK) {
xil_printf("Error on udp_send: %d\r\n", err);
retries--;
usleep(100);
void start_application(void)
/* initialize data buffer being sent with same as used in iperf */
for (i = 0; i < UDP_SEND_BUFSIZE; i++)
send_buf[i] = (i % 10) + '0';
}
目标是,修改程序逻辑,实现接收CAN帧触发udp_send。
can->udp协议转换
在lwip udp client例程中,程序运行现象是:串口调试助手打印通信速度信息,网络调试助手高频收到信息。
在transfer_data函数中,通过udp_conn_report函数打印速度信息,周期是:5s一次,300s udp发送测试结束。
main函数的while逻辑中,transfer_data()函数内的udp_packet_send(!FINISH)实现udp发送。因为while逻辑缺少sleep函数,所以网络调试助手UDP发送较快。
/** Transmit data on a udp session */
void transfer_data(void)
{
int i = 0;
for (i = 0; i < NUM_OF_PARALLEL_CLIENTS; i++) {
if (pcb[i] == NULL)
return;
}
// 测试1
// if (END_TIME || REPORT_INTERVAL_TIME) {
// u64_t now = get_time_ms();
// if (REPORT_INTERVAL_TIME) {
// if (client.i_report.start_time) {
// u64_t diff_ms = now - client.i_report.start_time;
// if (diff_ms >= REPORT_INTERVAL_TIME) {
// udp_conn_report(diff_ms, INTER_REPORT);
// client.i_report.start_time = 0;
// client.i_report.total_bytes = 0;
// }
// } else {
// client.i_report.start_time = now;
// }
// }
//测试1
// if (END_TIME) {
// /* this session is time-limited */
// u64_t diff_ms = now - client.start_time;
// if (diff_ms >= END_TIME) {
// /* time specified is over,
// * close the connection */
// udp_packet_send(FINISH);//finish强调直接跳出逻辑
// udp_conn_report(diff_ms, UDP_DONE_CLIENT);
// xil_printf("UDP test passed Successfully\n\r");
// return;
// }
// }
// }
udp_packet_send(!FINISH);
}
在注释对应的逻辑后,网络调试助手上显示填充的send_buf数组数据。但是存在两个端口同时发送UDP包。
为了实现直接的CAN帧转换,暂时忽略双端口UDP包问题。端口号为49153与49154,后来发现原因是创建立了两个client
再次遇见APU错误,重启vivado无效。间隔一天,vivado不再出现APU错误,说明此错误由SKD软件稳定性差异导致。
但是,下载程序后,led无法闪烁,串口也无法打印。猜想是,project explorer中包括四五个工程太多,导致sdk软件出现稳定性问题。删除其它工程后,工程正常运行!
无法发送can帧
led正常闪烁,串口正常打印,can调试助手无法接收can帧。现象是,只有一两次出现接收成功,而且状态窗口出现许多红色错误。
猜想是,软件编程逻辑、硬件问题或者Ecantool问题。在查看硬件连接时,突然发现螺钉端子出现松动。进一步验证硬件问题的猜想。更换端口、加强端子连接后,调试助手正常收发。
出现错误问题时,首先定位问题可能存在的层次,分析可能性最大的区域。软件编程时,在关键函数处添加if验证逻辑,方便定位软件层面问题。
网络调试助手无法接收udp包
can帧正常发送情况下,网络调试助手没有接收到任何udp包。创建新的工作,网络调试助手可以正常工作,说明CAN逻辑的添加影响了UDP逻辑。问题是,这两者直接上看不出什么依赖关系。
删除scugic的中断逻辑后,软件调试助手又能够正常接收UDP包,说明lwip的中断与scugic中断逻辑产生冲突。原因是,例程中已经设置了scugic,逻辑如下:
void init_platform()
{
platform_setup_timer();
platform_setup_interrupts();
return;
}
void platform_setup_interrupts(void)
{
Xil_ExceptionInit();
XScuGic_DeviceInitialize(INTC_DEVICE_ID);
/*
* Connect the interrupt controller interrupt handler to the hardware
* interrupt handling logic in the processor.
*/
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_IRQ_INT,
(Xil_ExceptionHandler)XScuGic_DeviceInterruptHandler,
(void *)INTC_DEVICE_ID);
/*
* 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.
*/
XScuGic_RegisterHandler(INTC_BASE_ADDR, TIMER_IRPT_INTR,
(Xil_ExceptionHandler)timer_callback,
(void *)&TimerInstance);
/*
* Enable the interrupt for scu timer.
*/
XScuGic_EnableIntr(INTC_DIST_BASE_ADDR, TIMER_IRPT_INTR);
return;
}
在此处添加中断逻辑,程序正常运行。
明确下阶段功能
在实现了CAN帧-udp包转换后,困惑于需要实现什么功能,达到什么目的。
预期是,can->udp协议转换主要用于数据采集,udp->can主要用于网络控制。为了解决初期提出的协议转换效率问题,先测试多can->UDP的协议转换效率更有意义。
因此接下来考虑,围绕X个CAN帧发送,采用buffer、直接转换或者timer等方式,协议转换效率差异。
测量延时的手段许多:1,内部延时记数;2,数字分析仪记录两个IO信号的变化。其中后者延时精度较高,只需要将特定条件下的状态转换为GPIO信号的变化。
协议转换效率
逻辑分析仪测量延时
对比逻辑分析仪测量的延时与get_time_ms获取的延时
get_time_ms函数值为零
应用get_time_ms函数获得运行时间时,初始值为零。
//can初始与结尾打印数据和记录时间
if((RxCnt == 100)|(RxCnt == 1)){
for(int i = 0; i < 8; i++)
{
//timeCan = get_time_ms();//分析这种测量方式精确度;
xil_printf("%d--",CanData[i]);
}
if(RxCnt == 1)
{
time0 = get_time_ms();//为什么打印数据为0?
xil_printf("start time is: %d\r\n", time0);
}
else if(RxCnt == 100)
{
time0 = get_time_ms()-time0;
xil_printf("diff time is: %d\r\n", time0);
}
else
{
xil_printf("time err\r\n");
}
}
打印的时间要么为0,要么为空白。
0--1--2--3--4--5--6--7--start time is:
99--1--2--3--4--5--6--7--diff time is:
有观点认为,sdk不支持**%f输出,但是例程其实存在。问题是,在打印get_time_ms函数里面的high和low**数值时,其并不为零。所以,输出格式化可能是这个现象的原因。
联想到xil_printf以及sdk存在过问题,猜想printf以及野火调试助手可以正常显示。结果是,数据正常输出。
测试数据是,接收100个can帧时间为441ms,这个时间与ECanTool的时间大致相符。由于发送每个CAN帧的通信时间大约为100us,协议转换时间可能相似,因此这个量级的数据和精确度无法满足性能分析的需要。换言之,需要采用数字分析仪的us量级精度。
enter while loop
0--1--2--3--4--5--6--7--start time is: 17009.000000 num
99--1--2--3--4--5--6--7--diff time is: 441.000000 num
本节说明,输出格式化、输出函数以及调试助手等三个方面如何影响输出。
逻辑分析仪测量延时
添加如下代码后,程度无法进入到while主循环逻辑。
/*
* gpio-ps初始化
*
* error: while loop influce the whole project?
*/
Gpio9Cfg = XGpioPs_LookupConfig(0);
Gpio10Cfg = XGpioPs_LookupConfig(1);
XGpioPs_CfgInitialize(&Gpio9, Gpio9Cfg, Gpio9Cfg->BaseAddr);
XGpioPs_CfgInitialize(&Gpio10, Gpio10Cfg, Gpio10Cfg->BaseAddr);
XGpioPs_SetDirectionPin(&Gpio9, 9, 1);
XGpioPs_SetOutputEnablePin(&Gpio9, 9, 1);
XGpioPs_SetDirectionPin(&Gpio10, 10, 1);
XGpioPs_SetOutputEnablePin(&Gpio10, 10, 1);
XGpioPs_WritePin(&Gpio9, 10, 0);
XGpioPs_WritePin(&Gpio10, 10, 0);
联想到assert逻辑中包含while循环,问题因此定位到GPIO的lookupconfig函数。在查看xparameter.h文件时,发现XGpioPs外设只有一个ID,意识到GPIO PS外设只有初始化一个对象就可。
修正此问题后,正常运行程序无法触发逻辑分析仪。原因是,通道0和通道1都设置为跳变触发,而软件逻辑中状态变化是[0-0]->[1-0]->[0-1]。此时,无法触发同时跳变。这说明,编程逻辑细节往往不易察觉,但是影响比较大。
图中可知,逻辑分析仪精度比较高,能够更加精确测量协议转换的延迟。
为了更加精确判断这个效果,测量板子从接收CAN帧到发出udp包的时间。结果是,跳变宽度为8.7us。按照CPU的运行频率来说,这个时间是合理的。
can帧缓冲性能优化
逻辑分析仪精度为us,是可以区分buffer的缓冲效果。即使buffer 100个can帧延时较低,也可以缓冲1000个,从而UDP包开销的影响就开始突显。
ecantool直接发送100个can帧时间为time0,buffer模式下缓冲时间为time1,减少的时间即为buffer抵消udp协议开销带来的收益。
//can帧提取8位can数据
//修改逻辑,重复下载更新流程
if(CanDataFlag ==1) //(RxCnt == 100||RxCnt == 1)
{
RxFramePtr = (u8 *)(&RxFrame[2]);
//step1:排序数据
for(int i = 0; i < 8; i++)
{
//反向排序
if(i < 4){
CanData[i] = *(RxFramePtr+3-i);//注意括号,避免直接操作指针
}else
{
CanData[i] = *(RxFramePtr+11-i);
}
}
//step2: 封装数据
for(int i = 0; i < 8; i++)
{
//数组条件清零
CanPack[(RxCnt-1)*8+i] = CanData[i];
}
//step3:tx udp can packet when rxcnt 100
if(RxCnt == 100){
RxCnt = 0;//重置边界值,避免边界冲突
transfer_data();
//条件数组清零
//memset(CanPack, 0, 100*sizeof(u8));
//猜想函数问题,果然如此,兴奋感
for(int i =0; i < 800; i++)
{
CanPack[i] = 0; //==与=不同
}
}
代码如上,ECANTOOL发送的can帧,可以正常在以太网调试助手显示。意料之外是,调整发送间隔1ms以下后,通常会出现无法接收udp包情况。如果调整发送间隔为1ms,接收的udp包出现问题,即首位数据不是从01开始。
通过图片可知,接收udp包与发送的CAN帧存在五个can帧的偏移。但是重新下载程序后,问题以消失。
udp包无法捕获问题
当ECANTOOL发送间隔为1.5s时,wireshark才能完整捕获全部udp包。考虑以太网的数据转换接收效率影响。
发送间隔为1.5s,发送can帧为100,延时为131.99701ms;buffer方式下延时为132.0275。问题是,节省的udp包开销太小,似乎直接被软件工具的偏差淹没掉。
由于比对数据相差较少,让人怀疑到底UDP协议开销是否值得运用1:N映射。明天考虑直接DMA设计,对比性能差异。
DMA功能和应用
按照小梅哥例程,在原系统上搭建DMA-BD工程时,遇到a lut5 cell is missing。按照此帖流程,发现信号定位到uartliteIP,于是直接删除此IP。
PL-DMA例程逻辑模糊。stm32实现DMA-外设之间数据传输时,初始化配置中包含外设地址。但是例程逻辑缺少此部分内容,simpleTransfer函数意义不明。资料表明,PL-DMA与PL外设无法实现这种功能。
PS-DMA能够实现这种功能,但是需要M_AXI_GP接口。问题是,PL外设只有AXI-LITE接口。
ug585数据手册
The peripheral request interfaces support the connection of DMA-capable peripherals to enable
memory-to-peripheral and peripheral-to-memory DMA transfers to occur, without intervention from
a microprocessor. These peripherals must be in the PL and attached to the M_AXI_GP interface. All peripheral request interface signals are synchronous to the respective clocks.
此贴指出,AXI SmartConnect接口可以扩展此接口,zynq模块再外扩一个M_AXI_GP1接口。但是建立BD模块后,出现如下错误,主要说明没有操作地址编辑器。在此界面点出重新分配地址即可。
“[BD 41-1356] Address block </can_3/CAN_S_AXI_LITE/Reg> is not mapped into </axi_dma_0/Data_MM2S>. Please use Address Editor to either map or exclude it.”
按照下图方式,修改地址配置,include segment后,BD框图成功通过。问题在于,实现数据搬运的过程比较模糊。
经过几轮测试,判断DMA-PS和PL无法实现CAN-PL与DDR之间直接的数据交换。