Funpack5-2 - 基于Microchip EV41C56A 实现USB设备控制LED和监控IO输入
该项目使用了Microchip EV41C56A,实现了USB从站设备的设计,它的主要功能为:使用USB上位机通过USB通讯控制板载的LED和监控按键IO状态。
标签
嵌入式系统
Funpack活动
EPTmachine
更新2026-06-22
江南大学
37

本次活动的PIC32CM LS00 Curiosity Nano+ Touch Evaluation Kit是一款板载资源丰富的开发板,内核、RAM和存储的参数如下。

Parameter

Value

Part Family

PIC32CM LS00

CPU Type

Cortex-M23

Max Speed (MHz)

48

Program Memory Size (KB)

512

SRAM (KB)

64

本项目利用芯片的USB控制器,实现USB从站设备,通过USB通讯,实现上位机板载IO信号的监控功能。

1、功能介绍

USB(Universial Serial Bus)通用串行总线,是嵌入式中常见的通讯接口。USB协议相关的资料可以在USB-IF标准网页找到相关的资料。

https://www.usb.org/

项目实现的功能需要USB用于数据通讯,LED和按键作为IO设备。实现USB协议中的Vendor设备用于实现上述自定义功能。程序的软件栈可以用下图表示,其中虚线框中的部分为厂商提供的USB硬件驱动软件,用户在此基础上实现Vendor Class和用户程序部分即可。

USB_Device_lib.png

2、功能实现

Microchip提供MPLAB X集成开发环境、MCC 代码配置工具,用于PIC32CM LS00 Curiosity Nano+ Touch Evaluation Kit开发。提供硬件配置、软件栈管理、代码管理等功能。

2.1 硬件配置

MPLAB X中的MCC Harmony代码配置工具用于管理32bit MCU开发所需安装的软件组件和MCU硬件配置。关于MCC Harmoy软件的具体功能说明,Microchip官方提供具体的使用说明。

https://onlinedocs.microchip.com/oxy/GUID-1F7007B8-9A46-4D03-AEED-650357BA760D-en-US-6/GUID-AFAB9227-B10C-4FAE-9785-98474664B50A.html

通过MCC中Content Manager安装需要使用的USB软件栈,相关的依赖项会由软件自动安装。

Install_USB_Lib.png

MCC的'Device Resource'选项卡中,添加'Vendor Function'组件。

Add_USB_Vendor_Function.png

'Project Resource'选项卡中,查看添加USB软件栈后的组件。

Project_graph_view.png

  • Core (Harmony Core Service):用于支持程序调度以及应用程序管理;
  • USB Full Speed Driver:USB通讯控制器外设的驱动程序;
  • USB Device Layer:定义USB设备的描述符;
  • Vendor Function:定义Vendor设备的End point数量;

Core (Harmony Core Service)中勾选需要的组件,并设置应用层应用程序的数量。

Core_Settings.png

USB Full Speed Driver页面中选择USB的工作模式为Device,由于PIC32CM LS00 Curiosity Nano+ Touch Evaluation Kit硬件不支持VBus检测,取消VBus检测模块。

USB_Full_Speed_Driver_Settings.png

USB Device Layer页面中Production ID Selection选项卡选选择Vendor Demo,其余设置会同步更新。

USB_Device_layer_Settings.png

Vendor Function采用默认参数即可。

Vendor_Function_Settings.png

PIC32CM LS00 Curiosity Nano+ Touch Evaluation Kit创建的项目是默认开启TrustZone的,项目由No-Secure工程和Sercure工程构成,MCC生成的USB应用相关的代码会自动分配到No-Secure工程中。本文出于方便,不考虑程序的TrustZone相关的设置,应用程序主要运行在No-Secure环境中。

Arm_TrustZone_Settings.png

打开Pin Configuration插件,设置LED、按键和USB相关的引脚,并将其访问权限设置为No-Secure。

Pins_Settings.png

USB中断的安全访问权限同样设置为No-Secure

NVIC_Configuration_Settings.png

完成配置,使用MCC生成相应的代码。

2.2 应用程序

在工程中添加应用处理程序,对USB通讯的事件进行处理,并根据USB通讯得到的数据做出相应的处理,控制板载LED的亮灭,并发送按键IO的状态。

app.h中定义用于管理应用程序运行状态的状态机数据类型。

#define SWITCH_STATE_UNPRESSED 0  
#define SWITCH_STATE_PRESSED 1
typedef enum
{
/* Application's state machine's initial state. */
APP_STATE_INIT=0,

/* Application waits for device configuration */
APP_STATE_WAIT_FOR_CONFIGURATION,

/* Application runs the main task */
APP_STATE_MAIN_TASK,

/* Application error occurred */
APP_STATE_ERROR

} APP_STATES;

定义用于管理USB通讯应用程序的结构体,结构体中包含USB设备句柄、应用程序状态机、USB设备回调函数等相关信息。

typedef struct
{
/* Device layer handle returned by device layer open function */
USB_DEVICE_HANDLE usbDevHandle;

/* Application state*/
APP_STATES state;

/* Track device configuration */
bool deviceIsConfigured;

/* Configuration value */
uint8_t configValue;

/* speed */
USB_SPEED speed;

/* ep data sent */
bool epDataWritePending;

/* ep data received */
bool epDataReadPending;

/* Transfer handle */
USB_DEVICE_TRANSFER_HANDLE writeTranferHandle;

/* Transfer handle */
USB_DEVICE_TRANSFER_HANDLE readTranferHandle;

/* The transmit endpoint address */
USB_ENDPOINT_ADDRESS endpointTx;

/* The receive endpoint address */
USB_ENDPOINT_ADDRESS endpointRx;

/* Tracks the alternate setting */
uint8_t altSetting;

/* True is switch was pressed */
bool isSwitchPressed;

/* True if the switch press needs to be ignored*/
bool ignoreSwitchPress;

/* Flag determines SOF event occurrence */
bool sofEventHasOccurred;

/* Switch debounce timer */
unsigned int switchDebounceTimer;

/* Switch debounce timer count */
unsigned int debounceCount;

/* The endpoint size is 64 for FS and 512 for HS */
uint16_t endpointMaxPktSize;

} APP_DATA;

应用初始化函数对应用程序结构进行初始化

void APP_Initialize ( void )
{
/* Place the App state machine in its initial state. */
appData.state = APP_STATE_INIT;
appData.usbDevHandle = USB_DEVICE_HANDLE_INVALID;
appData.deviceIsConfigured = false;
appData.endpointRx = (APP_EP_BULK_OUT | USB_EP_DIRECTION_OUT);
appData.endpointTx = (APP_EP_BULK_IN | USB_EP_DIRECTION_IN);
appData.epDataReadPending = false;
appData.epDataWritePending = false;
appData.altSetting = 0;


/* TODO: Initialize your application's state machine and other
* parameters.
*/
}

定义USB设备事件处理函数,对USB相关的事件进行处理。

void APP_USBDeviceEventHandler(USB_DEVICE_EVENT event, void * eventData, uintptr_t context)
{
uint8_t * configurationValue;
USB_SETUP_PACKET * setupPacket;
switch(event)
{
case USB_DEVICE_EVENT_RESET:
case USB_DEVICE_EVENT_DECONFIGURED:

/* Device is reset or deconfigured. Provide LED indication.*/
LED_Clear();

appData.deviceIsConfigured = false;

break;

case USB_DEVICE_EVENT_CONFIGURED:

/* Check the configuration */
configurationValue = (uint8_t *)eventData;
if(*configurationValue == 1 )
{
/* The device is in configured state. Update LED indication */
LED_Set();

/* Reset endpoint data send & receive flag */
appData.deviceIsConfigured = true;
}
break;

case USB_DEVICE_EVENT_SUSPENDED:

LED_Clear();
/* Device is suspended. */
break;


case USB_DEVICE_EVENT_POWER_DETECTED:

/* VBUS is detected. Attach the device */
USB_DEVICE_Attach(appData.usbDevHandle);
break;

case USB_DEVICE_EVENT_POWER_REMOVED:

/* VBUS is removed. Detach the device */
USB_DEVICE_Detach (appData.usbDevHandle);
LED_Clear();
break;

case USB_DEVICE_EVENT_CONTROL_TRANSFER_SETUP_REQUEST:
/* This means we have received a setup packet */
setupPacket = (USB_SETUP_PACKET *)eventData;
if(setupPacket->bRequest == USB_REQUEST_SET_INTERFACE)
{
/* If we have got the SET_INTERFACE request, we just acknowledge
for now. This demo has only one alternate setting which is already
active. */
USB_DEVICE_ControlStatus(appData.usbDevHandle,USB_DEVICE_CONTROL_STATUS_OK);
}
else if(setupPacket->bRequest == USB_REQUEST_GET_INTERFACE)
{
/* We have only one alternate setting and this setting 0. So
* we send this information to the host. */

USB_DEVICE_ControlSend(appData.usbDevHandle, &appData.altSetting, 1);
}
else
{
/* We have received a request that we cannot handle. Stall it*/
USB_DEVICE_ControlStatus(appData.usbDevHandle, USB_DEVICE_CONTROL_STATUS_ERROR);
}
break;

case USB_DEVICE_EVENT_ENDPOINT_READ_COMPLETE:
/* Endpoint read is complete */
appData.epDataReadPending = false;
break;

case USB_DEVICE_EVENT_ENDPOINT_WRITE_COMPLETE:
/* Endpoint write is complete */
appData.epDataWritePending = false;
break;

/* These events are not used in this demo. */
case USB_DEVICE_EVENT_RESUMED:
if(appData.deviceIsConfigured == true)
{
LED_Set();
}
break;
case USB_DEVICE_EVENT_ERROR:
default:
break;
}
}

应用任务中通过管理状态机,实现应用程序的状态机管理,完成USB设备开启、配置、数据收发以及外设监控功能。

void APP_Tasks (void )
{
switch(appData.state)
{
case APP_STATE_INIT:
/* Open the device layer */
appData.usbDevHandle = USB_DEVICE_Open( USB_DEVICE_INDEX_0,
DRV_IO_INTENT_READWRITE );

if(appData.usbDevHandle != USB_DEVICE_HANDLE_INVALID)
{
/* Register a callback with device layer to get event notification (for end point 0) */
USB_DEVICE_EventHandlerSet(appData.usbDevHandle, APP_USBDeviceEventHandler, 0);

appData.state = APP_STATE_WAIT_FOR_CONFIGURATION;
}
else
{
/* The Device Layer is not ready to be opened. We should try
* again later. */
}

break;

case APP_STATE_WAIT_FOR_CONFIGURATION:

/* Check if the device is configured */
if(appData.deviceIsConfigured == true)
{
if (USB_DEVICE_ActiveSpeedGet(appData.usbDevHandle) == USB_SPEED_FULL)
{
appData.endpointMaxPktSize = 64;
}
else if (USB_DEVICE_ActiveSpeedGet(appData.usbDevHandle) == USB_SPEED_HIGH)
{
appData.endpointMaxPktSize = 512;
}
if (USB_DEVICE_EndpointIsEnabled(appData.usbDevHandle, appData.endpointRx) == false )
{
/* Enable Read Endpoint */
USB_DEVICE_EndpointEnable(appData.usbDevHandle, 0, appData.endpointRx,
USB_TRANSFER_TYPE_BULK, appData.endpointMaxPktSize);
}
if (USB_DEVICE_EndpointIsEnabled(appData.usbDevHandle, appData.endpointTx) == false )
{
/* Enable Write Endpoint */
USB_DEVICE_EndpointEnable(appData.usbDevHandle, 0, appData.endpointTx,
USB_TRANSFER_TYPE_BULK, appData.endpointMaxPktSize);
}
/* Indicate that we are waiting for read */
appData.epDataReadPending = true;

/* Place a new read request. */
USB_DEVICE_EndpointRead(appData.usbDevHandle, &appData.readTranferHandle,
appData.endpointRx, &receivedDataBuffer[0], sizeof(receivedDataBuffer) );

/* Device is ready to run the main task */
appData.state = APP_STATE_MAIN_TASK;
}
if(SWITCH_Get() == SWITCH_STATE_PRESSED)
{
USB_DEVICE_Attach(appData.usbDevHandle);
}
break;

case APP_STATE_MAIN_TASK:

if(!appData.deviceIsConfigured)
{
/* This means the device got deconfigured. Change the
* application state back to waiting for configuration. */
appData.state = APP_STATE_WAIT_FOR_CONFIGURATION;

/* Disable the endpoint*/
USB_DEVICE_EndpointDisable(appData.usbDevHandle, appData.endpointRx);
USB_DEVICE_EndpointDisable(appData.usbDevHandle, appData.endpointTx);
appData.epDataReadPending = false;
appData.epDataWritePending = false;
}
else if (appData.epDataReadPending == false)
{
/* Look at the data the host sent, to see what kind of
* application specific command it sent. */

switch(receivedDataBuffer[0])
{
case 0x80:

/* This is the toggle LED command */
LED_Toggle();
break;

case 0x81:

/* This is a switch check command. Check if the TX is free
* for us to send a status. */

if(appData.epDataWritePending == false)
{
/* Echo back to the host PC the command we are fulfilling
* in the first byte. In this case, the Get Pushbutton
* State command. */

transmitDataBuffer[0] = 0x81;

if(SWITCH_Get() == SWITCH_STATE_PRESSED)
{
transmitDataBuffer[1] = 0x00;
}
else
{
transmitDataBuffer[1] = 0x01;
}

/* Send the data to the host */

appData.epDataWritePending = true;

USB_DEVICE_EndpointWrite ( appData.usbDevHandle, &appData.writeTranferHandle,
appData.endpointTx, &transmitDataBuffer[0],
sizeof(transmitDataBuffer),
USB_DEVICE_TRANSFER_FLAGS_MORE_DATA_PENDING);
}
break;
default:
break;
}

appData.epDataReadPending = true ;

/* Place a new read request. */
USB_DEVICE_EndpointRead ( appData.usbDevHandle, &appData.readTranferHandle,
appData.endpointRx, &receivedDataBuffer[0], sizeof(receivedDataBuffer) );
}
break;

case APP_STATE_ERROR:
break;

default:
break;
}
}

应用程序状态机的转换如下所示:

APP_STATE_MACHINE_TRANS.png

APP_STATE_INIT状态机中,打开USB设备并切换状态机到APP_STATE_WAIT_FOR_CONFIGURATION,等待USB设备配置完成。USB设备在接入到HOST主机,检测USB设备配置完成,使能USB EndPoint的读写,并切换到APP_STATE_MAIN_TASK;调用USB的读写函数,根据接收到的数据控制LED的状态翻转,并读取按键IO的状态,发送按键的状态信息。

3、上位机程序和功能演示

以上示例基于Microchip提供的usb_apps_device仓库中的代码参考实现,代码仓库apps\vendor\bin中提供用于通讯的USB上位机。

https://github.com/Microchip-MPLAB-Harmony/usb_apps_device.git

开发板的USB Device接口连接到PC,运行WinUSB_PnP_Demo上位机,使用上位机控制板载LED的翻转以及按键状态的监控。

上位机的源码可以在Github上找到

https://github.com/mentatpsi/Microchip.git

连接开发板到上位机,上位机程序的运行效果如图所示。

LED翻转:

按键IO监测:

4、总结

利用Microchip提供的开发示例和工具源码可以快速上手开发嵌入式USB应用,是十分不错的学习资料。

附件下载
usb_button.7z
工程文件
团队介绍
嵌入式爱好者
评论
0 / 100
查看更多
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号