Funpack第三季第四期-项目文档
一、功能介绍
该USB多协议HUB项目旨在提供一个多功能的通信中枢,通过统一的USB接口与外部设备进行数据交互,同时支持多种通信协议,如CAN(控制器局域网)和UART(通用异步收发传输器)。这种设计使得用户可以方便地通过USB接口与MCU进行双向通信,满足多种应用场景的需求,系统框图如图1所示:
图1
操作说明:
- 将USB多协议HUB通过USB接口连接到PC,确保电源和数据传输正常
- 启动PC端应用程序,该程序用于配置和监控HUB设备
- 在PC端软件中操作设备面板(目前支持操作CAN通讯设备以及UART通讯设备),PC根据操作的设备类型将数据包通过USB发送到指定的HUB端口
- HUB设备端口接收到数据包后解析数据包类型并将发送至设备的数据包通过相应的端口发送至相应的设备上
- 设备接受到数据包后进行解析并执行相应的命令,并将应答数据包通过通讯端口发送至HUB设备
- HUB设备从CAN或UART端口接收数据并通过USB接口将数据传输回PC
- PC端软件接收到数据并进行解析,在界面上实时显示接收到的数据,便于用户监控和分析
通过以上操作逻辑,用户可以方便地使用USB多协议HUB实现与MCU的双向通信,满足多种应用场景的需求。
二、开发环境介绍
本开发环境基于FRDM-MCXN947开发板进行设计开发,通过控制外设实现各种功能,控制板外观如图2所示:
图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所示:
图3
三、代码介绍
图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设备端口
- 打开UART设备端口
4.2 控制CAN设备
4.3 读取CAN信息
4.4 控制UART设备
4.5 读取UART设备信息
五、心得体会
本人有幸使用NXP的FRDM-MCXN947开发板进行了一次项目开发,成功实现了一个多协议的USB集线器(HUB),在此过程中,我积累了一些心得体会,并希望分享对这次开发活动的看法,包括一些意见和建议。
FRDM-MCXN947配备高性能的MCU,具有强大的处理能力和丰富的外设接口,适用于复杂的嵌入式系统开发,开发板支持各种外设扩展接口,如GPIO、I2C、SPI等,方便连接和扩展各种传感器和外部设备,支持多种通信协议,包括CAN、UART和USB,适合多协议通信项目的开发。
通过这次开发,我不仅加深了对NXP FRDM-MCXN947开发板和多协议通信的理解,也积累了宝贵的实践经验。希望这些心得体会和建议能为其他开发者提供一些参考和帮助,同时也希望NXP能够不断改进,为开发者提供更好的支持和资源。
十分期待硬禾学堂下期的活动!