Funpack3-4——基于MCXN947实现USB转UART、CAN的HUB
该项目使用了C语言、Python语言,实现了FRDM-MCXN947的设计,它的主要功能为:实现了一个USB多协议HUB,支持使用CAN口和UART口向MCU发送信息,通过USB显示出数据同样也支持从USB端可以指定向CAN和UART发送数据,并从对应端口发送数据。
标签
嵌入式系统
Funpack活动
开发板
yearnext
更新2024-09-09
25

Funpack第三季第四期-项目文档

一、功能介绍

该USB多协议HUB项目旨在提供一个多功能的通信中枢,通过统一的USB接口与外部设备进行数据交互,同时支持多种通信协议,如CAN(控制器局域网)和UART(通用异步收发传输器)。这种设计使得用户可以方便地通过USB接口与MCU进行双向通信,满足多种应用场景的需求,系统框图如图1所示:


框图.png

图1



操作说明:

  1. 将USB多协议HUB通过USB接口连接到PC,确保电源和数据传输正常
  2. 启动PC端应用程序,该程序用于配置和监控HUB设备
  3. 在PC端软件中操作设备面板(目前支持操作CAN通讯设备以及UART通讯设备),PC根据操作的设备类型将数据包通过USB发送到指定的HUB端口
  4. HUB设备端口接收到数据包后解析数据包类型并将发送至设备的数据包通过相应的端口发送至相应的设备上
  5. 设备接受到数据包后进行解析并执行相应的命令,并将应答数据包通过通讯端口发送至HUB设备
  6. HUB设备从CAN或UART端口接收数据并通过USB接口将数据传输回PC
  7. PC端软件接收到数据并进行解析,在界面上实时显示接收到的数据,便于用户监控和分析


通过以上操作逻辑,用户可以方便地使用USB多协议HUB实现与MCU的双向通信,满足多种应用场景的需求。



二、开发环境介绍

本开发环境基于FRDM-MCXN947开发板进行设计开发,通过控制外设实现各种功能,控制板外观如图2所示:

image.png

图2


FRDM-MCXN947 板由⼀个 MCXN947 设备和⼀个 64 Mbit 外部串⾏闪存(由 Winbond 提供)组成, 板载P3T1755DP I3C温度传感器、 TJA1057GTK/3Z CAN PHY、以太⽹ PHY、SDHC 电路(DNP卡槽)、RGB LED、触摸板、⾼速 USB 电路、按钮和 MCU-Link 调试探针电 路。该板与 Arduino 屏蔽模块、Pmod和 mikroBUS接口兼容。开发板采用MCXN947作为主处理器,MCXN947集成了双核 Arm Cortex-M33 微控制器和神经处理单元 (NPU) ,框图如图3所示:

image.png

图3



三、代码介绍

未命名绘图-工作流程图.drawio.png

图4


程序运行流程如图4所示,控制板上电后执行初始化函数,包含UART、CAN通讯初始化等,程序初始化完成后,开始运行主程序。

3.1 初始化代码

#if defined(__CC_ARM) || (defined(__ARMCC_VERSION)) || defined(__GNUC__)
int main(void)
#else
void main(void)
#endif
{
    /* attach FRO 12M to FLEXCOMM4 (debug console) */
    CLOCK_SetClkDiv(kCLOCK_DivFlexcom4Clk, 1u);
    CLOCK_AttachClk(BOARD_DEBUG_UART_CLK_ATTACH);

    /* attach PLL1Clk0 to FLEXCAN0 */
    CLOCK_SetClkDiv(kCLOCK_DivPLL1Clk0, 2U);

    //! 初始化外设
    BOARD_InitBootPins();
    BOARD_PowerMode_OD();
    BOARD_InitBootClocks();
    BOARD_InitDebugConsole();
    DrvUartInit();
    DrvCanInit();
    CLOCK_SetupExtClocking(BOARD_XTAL0_CLK_HZ);

    LED_RED_INIT(LOGIC_LED_OFF);
    LED_GREEN_INIT(LOGIC_LED_OFF);
    LED_BLUE_INIT(LOGIC_LED_OFF);

    //! 初始化任务环境
    memset(&periphIpc, 0, sizeof(periphIpc));
    if (USB_DeviceCdcVcomTaskInit(&periphIpc) == -1)
    {
        usb_echo("(1)USB composite Task Env Init Failed, Exit Task!\r\n");
        while (1);
    }

    if (UartTaskEnvInit(&periphIpc) == -1)
    {
        usb_echo("(2)Uart Task Env Init Failed, Exit Task!\r\n");
        while (1);
    }

    if (CanTaskEnvInit(&periphIpc) == -1)
    {
        usb_echo("(3)CAN Task Env Init Failed, Exit Task!\r\n");
        while (1);
    }
    if (BridgeTaskEnvInit(&periphIpc) == -1)
    {
        usb_echo("(4)Bridge Task Env Init Failed, Exit Task!\r\n");
        while (1);
    }

    g_composite.ipcHandle = (void *)&periphIpc;
    if (xTaskCreate(APPTask, /* pointer to the task */
        s_appName, /* task name for kernel awareness debugging */
        APP_TASK_STACK_SIZE / sizeof(portSTACK_TYPE), /* task stack size */
        &g_composite, /* optional task startup argument */
        4, /* initial priority */
        &g_composite.applicationTaskHandle /* optional task handle to create */
    ) != pdPASS)
    {
        usb_echo("app task create failed!\r\n");
#if (defined(__CC_ARM) || (defined(__ARMCC_VERSION)) || defined(__GNUC__))
        return 1;
#else
        return;
#endif
    }

#if 1
    if (xTaskCreate(UartTask, /* pointer to the task */
        "Uart Task", /* task name for kernel awareness debugging */
        APP_TASK_STACK_SIZE / sizeof(portSTACK_TYPE), /* task stack size */
        &periphIpc, /* optional task startup argument */
        4, /* initial priority */
        NULL /* optional task handle to create */
    ) != pdPASS)
    {
        usb_echo("uart task create failed!\r\n");
#if (defined(__CC_ARM) || (defined(__ARMCC_VERSION)) || defined(__GNUC__))
        return 1;
#else
        return;
#endif
    }
#endif

#if 1
    if (xTaskCreate(CanTask, /* pointer to the task */
        "CAN Task", /* task name for kernel awareness debugging */
        APP_TASK_STACK_SIZE / sizeof(portSTACK_TYPE), /* task stack size */
        &periphIpc, /* optional task startup argument */
        4, /* initial priority */
        NULL /* optional task handle to create */
        ) != pdPASS)
    {
        usb_echo("can task create failed!\r\n");
#if (defined(__CC_ARM) || (defined(__ARMCC_VERSION)) || defined(__GNUC__))
        return 1;
#else
        return;
#endif
    }
#endif

    if (xTaskCreate(BridgeTask, /* pointer to the task */
        "Bridge Task", /* task name for kernel awareness debugging */
        APP_TASK_STACK_SIZE / sizeof(portSTACK_TYPE), /* task stack size */
        &periphIpc, /* optional task startup argument */
        3, /* initial priority */
        NULL /* optional task handle to create */
    ) != pdPASS)
    {
        usb_echo("uart task create failed!\r\n");
#if (defined(__CC_ARM) || (defined(__ARMCC_VERSION)) || defined(__GNUC__))
        return 1;
#else
        return;
#endif
    }

    vTaskStartScheduler();

#if (defined(__CC_ARM) || (defined(__ARMCC_VERSION)) || defined(__GNUC__))
    return 1;
#endif
}


3.2 USB处理函数

void USB_DeviceCdcVcomTask(void *param)
{
    uint8_t tempBuf[8];
    periph_handle_t *ipc = ((usb_device_composite_struct_t *)param)->ipcHandle;
    usb_status_t error = kStatus_USB_Error;
    volatile usb_cdc_vcom_struct_t *vcomInstance;

    xQueueReceive(vcomMsgQueueHandle, &usbRxMsgBuf, portMAX_DELAY);
    switch (usbRxMsgBuf.msg)
    {
        case PERIPH_MSG_RX_DONE:
        {
            vcomInstance = (volatile usb_cdc_vcom_struct_t *)usbRxMsgBuf.handle;
            //! parser format 拦截端口属性数据包
            if (!(usbRxMsgBuf.buf[1] & 0x0F))
            {
                vcomInstance->sendSize = 0;
                if (usbRxMsgBuf.buf_len <= DATA_BUFF_SIZE)
                {
                    memset(tempBuf, 0, sizeof(tempBuf));
                    tempBuf[0] = 0xA5;
                    tempBuf[1] = (usbRxMsgBuf.buf[1] & 0xF0) | 0x01;
                    tempBuf[2] = (usbRxMsgBuf.buf[2] & 0x1F) | (0x01 << 5);
                    tempBuf[3] = usbRxMsgBuf.id;
                    tempBuf[7] = usrProtocolCalcCheckSum(tempBuf, 8);
                    memcpy(vcomInstance->currSendBuf, tempBuf, sizeof(tempBuf));
                    USB_DeviceCdcAcmSend(vcomInstance->cdcAcmHandle, vcomInstance->bulkInEndpoint,
                    vcomInstance->currSendBuf, sizeof(tempBuf));
                }
            }
            else
            {
                if (usbRxMsgBuf.id == 1)
                {
                    BridgeUsb2Uart(ipc, usbRxMsgBuf.buf, usbRxMsgBuf.buf_len);
                }
                else if (usbRxMsgBuf.id == 2)
                {
                    BridgeUsb2Can(ipc, usbRxMsgBuf.buf, usbRxMsgBuf.buf_len);
                }
            }
            break;
        }
        case PERIPH_MSG_DEV2USB:
        {
            vcomInstance = &g_deviceComposite->cdcVcom[(usbRxMsgBuf.id == 0) ? 0 : 1];
            vcomInstance->sendSize = 0;
            memcpy(vcomInstance->currSendBuf, usbRxMsgBuf.buf, usbRxMsgBuf.buf_len);
            usb_status_t status = USB_DeviceCdcAcmSend(vcomInstance->cdcAcmHandle, vcomInstance->bulkInEndpoint,
            vcomInstance->currSendBuf, usbRxMsgBuf.buf_len);
            usb_echo("id:%d, status:%d, sendSize:%d\r\n", usbRxMsgBuf.id, status, usbRxMsgBuf.buf_len);
            break;
        }
        default:
            break;
    }
}



3.3 CAN处理函数

void CanTask(void *handle)
{
    while (1)
    {
        xQueueReceive(canMsgQueueHandle, &canRxMsgBuf, portMAX_DELAY);
        switch (canRxMsgBuf.msg)
        {
            //! CAN接收事件
            case PERIPH_MSG_RX_DONE:
            {
                rxXfer.frame = &frame;
                rxXfer.mbIdx = 0;
                (void)FLEXCAN_TransferReceiveNonBlocking(EXAMPLE_CAN, &flexcanHandle, &rxXfer);
                BridgeCan2Usb(handle, canRxMsgBuf.buf, canRxMsgBuf.buf_len);

               dev_pkt_t *crtPkt = (dev_pkt_t *)&canRxMsgBuf.buf[0];
               if (crtPkt->opt == 0x03)
               {
                   if (crtPkt->addr == 0x01)
                   {
                       if (crtPkt->databuf[0])
                       {
                            LED_BLUE_ON();
                       }
                       else
                       {
                           LED_BLUE_OFF();
                       }
                   }
               }
               else if (crtPkt->opt == 0x06)
               {
                   if (crtPkt->addr == 0x01 || crtPkt->addr == 0x00)
                   {
                       if (crtPkt->databuf[0])
                       {
                            LED_BLUE_ON();
                       }
                       else
                       {
                            LED_BLUE_OFF();
                       }
                   }
               }
               break;
           }
           //! 串口转发事件
           case PERIPH_MSG_USB2DEV:
           {
               frame.id = FLEXCAN_ID_STD(0x181);
               frame.format = (uint8_t)kFLEXCAN_FrameFormatStandard;
               frame.type = (uint8_t)kFLEXCAN_FrameTypeData;
               frame.length = (uint8_t)canRxMsgBuf.buf_len;
               txXfer.mbIdx = (uint8_t)1;
               txXfer.frame = &frame;
               frame.dataByte0 = canRxMsgBuf.buf[0];
               frame.dataByte1 = canRxMsgBuf.buf[1];
               frame.dataByte2 = canRxMsgBuf.buf[2];
               frame.dataByte3 = canRxMsgBuf.buf[3];
               frame.dataByte4 = canRxMsgBuf.buf[4];
               frame.dataByte5 = canRxMsgBuf.buf[5];
               frame.dataByte6 = canRxMsgBuf.buf[6];
               frame.dataByte7 = canRxMsgBuf.buf[7];
               FLEXCAN_TransferSendBlocking(EXAMPLE_CAN, TX_MESSAGE_BUFFER_NUM, &frame);
           }
           default:
                break;
        }
    }
}



3.4 UART处理函数

void UartTask(void *handle)
{
    while (1)
    {
        xQueueReceive(uartMsgQueueHandle, &uartRxMsgBuf, portMAX_DELAY);
        switch (uartRxMsgBuf.msg)
        {
        //! 串口接收事件
        case PERIPH_MSG_RX_DONE:
        {
            drv_uart_rx_callback(handle, uartRxMsgBuf.buf, uartRxMsgBuf.buf_len);
            break;
        }
        //! 串口转发事件
        case PERIPH_MSG_USB2DEV:
        {
            if (uartRxMsgBuf.buf_len <= ECHO_BUFFER_SIZE)
            {
                memcpy(sendXfer.data, uartRxMsgBuf.buf, uartRxMsgBuf.buf_len);
                LPUART_WriteBlocking(LP_FLEXCOMM5_PERIPHERAL, uartRxMsgBuf.buf, uartRxMsgBuf.buf_len);
            }
        }
        default:
            break;
        }

    }
}





四、功能演示

4.1 连接设备,自动获取目标端口类型

  • 打开CAN设备端口

image.pngimage.png

  • 打开UART设备端口

image.pngimage.png


4.2 控制CAN设备

image.pngimage.png


4.3 读取CAN信息

image.pngimage.png


4.4 控制UART设备

image.png126f1b694bfb6ce9698aa38cea9343f.jpg


4.5 读取UART设备信息

image.pngimage.png



五、心得体会

本人有幸使用NXP的FRDM-MCXN947开发板进行了一次项目开发,成功实现了一个多协议的USB集线器(HUB),在此过程中,我积累了一些心得体会,并希望分享对这次开发活动的看法,包括一些意见和建议。

FRDM-MCXN947配备高性能的MCU,具有强大的处理能力和丰富的外设接口,适用于复杂的嵌入式系统开发,开发板支持各种外设扩展接口,如GPIO、I2C、SPI等,方便连接和扩展各种传感器和外部设备,支持多种通信协议,包括CAN、UART和USB,适合多协议通信项目的开发。

通过这次开发,我不仅加深了对NXP FRDM-MCXN947开发板和多协议通信的理解,也积累了宝贵的实践经验。希望这些心得体会和建议能为其他开发者提供一些参考和帮助,同时也希望NXP能够不断改进,为开发者提供更好的支持和资源。

十分期待硬禾学堂下期的活动!

附件下载
pc.zip
frdmmcxn947.zip
gd-board.zip
团队介绍
本团队基于FRDM-MCXN947开发板实现了一个USB多协议HUB
团队成员
yearnext
评论
0 / 100
查看更多
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号