ZYNQ:lwip UDP与CAN协议转换

CAN->ethernet

  1. 测试CAN通信功能,

重新搭建工程

在试用以前工程时,失误删除某个文件,导致sdk软件闪退,于是选择搭建新的工程。考虑减少鼠标操作。

vivado更新同步问题依然存在,BD修改无法马上同步到wrapper文件。通过修改BD图的信号,重新output product有效。

因为GPIO IP默认宽度为32位,生成的信号接口也是32位接口。删除此接口,wrapper自然会同步更新。

实现Eth-Can协议转换

recv_callback回调函数获得以太网包的数据结构指针pbuf,通过指针获得其中的payloadlen。函数内容如下:

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包,memcpysend_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-DMAPL外设无法实现这种功能。

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之间直接的数据交换。

  • 41
    点赞
  • 53
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值