2.17. USB
Overview
提供 usb 应用示例、配置介绍和常见问题。USB各类协议文件均存放在 apps/common/usb
文件夹下。
2.17.1. 应用实例
示例演示:
USB做masstorge主从机方法
USB做UAC主从机方法
USB做UVC主从机方法
USB做HID主从机方法
USB与PC通信
2.17.2. 公共配置说明
- 1.在
apps/demo/demo_DevKitBoard/app_main.c
中增加USB任务名字如下 /*任务列表 */ const struct task_info task_info_table[] = { {"app_core", 15, 2048, 1024 }, {"sys_event", 29, 512, 0 }, {"systimer", 14, 256, 0 }, {"sys_timer", 9, 512, 128 }, #if CPU_CORE_NUM > 1 {"#C0usb_msd", 1, 512, 128 }, #else {"usb_msd", 1, 512, 128 }, #endif {0, 0, 0, 0, 0}, };
- 1.在
2.在
apps/demo/demo_DevKitBoard/board/wl82/DevKitBoard.c
中增加USB配置如下#if TCFG_USB_SLAVE_ENABLE || TCFG_USB_HOST_ENABLE #include "otg.h" #include "usb_host.h" #include "usb_common_def.h" #include "usb_storage.h" #include "asm/uvc_device.h" #endif #if TCFG_USB_SLAVE_ENABLE || TCFG_USB_HOST_ENABLE static const struct otg_dev_data otg_data = { .usb_dev_en = 0x01, //USB口使能:0x1-->USB0;0x2-->USB1;0x3--->USB0和USB1 #if TCFG_USB_SLAVE_ENABLE .slave_online_cnt = 10,//USB从机的上线检测次数,单位为detect_time_interval值 .slave_offline_cnt = 10,//USB从机的下线检测次数,单位为detect_time_interval值 #endif #if TCFG_USB_HOST_ENABLE .host_online_cnt = 10,//US主机的上线检测次数,单位为detect_time_jjjinterval值 .host_offline_cnt = 10,//US主机的下线检测次数,单位为detect_time_interval值 #endif .detect_mode = OTG_HOST_MODE | OTG_SLAVE_MODE | OTG_CHARGE_MODE,//检测模式:主机检测、从机检测、拔插充电检测。 .detect_time_interval = 50,//检测上下线的时间间隔,单位ms,默认50ms(不建议修改变小,否则USB事件可能会丢,最低为10ms)。 }; #endif REGISTER_DEVICES(device_table) = { #if TCFG_USB_SLAVE_ENABLE || TCFG_USB_HOST_ENABLE { "otg", &usb_dev_ops, (void *)&otg_data}, #endif };
USB的拔插事件通知在
apps/common/system/device_mount.c
的device_event_handler
函数,当注册app状态机,则状态机事件处理也会有事件通知(详情 app_state_machine )。
2.17.3. 操作说明
1.USB做masstorge从机方法 打开四个宏即可
1.1 配置SD卡相关参考 SD例子
- 1.2
app_config.h
修改配置,注意:读卡器功能,同时需要打开SD卡功能并插SD卡,电脑端才能显示读卡器的U盘盘符。
-
#define TCFG_SD0_ENABLE 1 //使能sd卡 #define TCFG_PC_ENABLE 1 //使用从机模式 #define USB_PC_NO_APP_MODE 2 //不使用app_core #define USB_DEVICE_CLASS_CONFIG (MASSSTORAGE_CLASS) //msd
- 1.2
2.USB做UAC从机方法
2.1 配置AUDIO工程相关参考 demo_audio工程
- 2.2
app_config.h
修改配置 -
#define TCFG_PC_ENABLE 1 //使用从机模式 #define USB_DEVICE_CLASS_CONFIG (AUDIO_CLASS) //audio #define CONFIG_PCM_ENC_ENABLE #define CONFIG_PCM_DEC_ENABLE #define CONFIG_AUDIO_ENC_SAMPLE_SOURCE AUDIO_ENC_SAMPLE_SOURCE_MIC
- 2.2
- 2.3 增加uac相关库
audio_config.c | 用到了相关的api函数(get_ps_cal_api)
usb_audio_api.c | usb和audio对接api函数
audio_server.a | 音频编解码库
lib_usb_syn.a | 用于变采样 专用于usb 不能用于其他地方
- 2.4 在
board.c
初始化dac相关硬件 -
void board_early_init() { extern void dac_early_init(u8 trim_en, u8 pwr_sel, u32 dly_msecs); dac_early_init(1, 1, 1000); devices_init(); } \
- 2.4 在
- 2.5 在
app_main.c
增加相关代码 {"uac_sync", 20, 512, 0 }, {"uac_play", 7, 768, 0 }, {"uac_record", 7, 768, 32 },
- 2.5 在
3.USB做UVC从机方法 (支持DVP摄像头数据到电脑)
- 3.1 配置VIDEO工程相关参考 demo_video工程 ,先要移植摄像头并调试可以出图,电脑端才能有图像。
//*********************************************************************************// // USB配置 USB查看软件 请在sdk_tools文件夹中找到AMCapSetup.exe // //*********************************************************************************// #define TCFG_PC_ENABLE 1 //打开usb从机功能(打开连接PC电脑模式) #if TCFG_PC_ENABLE #define USB_PC_NO_APP_MODE 2 //不用APP状态机接收消息 #define USB_MALLOC_ENABLE 1 //buff采用申请方式 #define USB_DEVICE_CLASS_CONFIG (UVC_CLASS) //从机UVC电脑查看功能选择 //#define USB_DEVICE_CLASS_CONFIG (MASSSTORAGE_CLASS) //从机电脑盘符功能选择 #if ((USB_DEVICE_CLASS_CONFIG & UVC_CLASS) == UVC_CLASS) #define CONFIG_USR_VIDEO_ENABLE #endif #endif
- 3.2 增加uvc相关库
stream_protocol.c | 实时流中间层
user_video_rec.c | 打开实时流api
video_rt_usr.c | 封装给stream_protocol
gc0308.c | 摄像头驱动,根据实际摄像头选择驱动
- 3.3 uvc从机流程
(1)、电脑开启UVC摄像头:设备USB连接电脑,电脑开启摄像头时,系统会进
uvc.c
的uvc_vs_itf_handle
函数,执行打开摄像头usb_video_open_camera()
static u32 uvc_vs_itf_handle(struct usb_device_t *usb_device, struct usb_ctrlrequest *req) { ... case USB_TYPE_STANDARD: ... usb_video_open_camera(usb_id); ... } ///执行打开摄像头,指向函数uvc_video_open() static void usb_video_open_camera(usb_dev usb_id) { ... uvc_handle[usb_id]->video_open(usb_id, uvc_slave_probe_commit_control[usb_id][2] - 1, //Format Index uvc_slave_probe_commit_control[usb_id][3] - 1, //Frame Index 10000000 / fps); } static int uvc_video_open(int idx, int fmt, int frame_id, int fps) { ... #if UVC_FORMAT_MJPG set_video_rt_cb(uvc_send_buf, &uvc_video_info);//注册JPEG回调函数 //uvc_video_open函数调用user_video_rec.c里的API,实现打开摄像头出JPG图 user_video_rec0_open(0); }(2)、UVC发送JPEG到电脑:电脑请求设备UVC的数据包,则进入
uvc_g_bulk_transfer()
函数,进行JPEG图片获取,最终跑到uvc_video_req_bug()
函数。static void uvc_g_bulk_transfer(struct usb_device_t *usb_device, u32 ep) { ... //该函数为uvc_video_req_buf() len = uvc_handle[usb_id]->video_reqbuf(usb_id, tx_addr, UVC_FIFO_TXMAXP, &IsEOFFrame); ... //发送JPEG到USB usb_g_bulk_write(usb_id, UVC_STREAM_EP_IN, tx_addr, len); } static int uvc_video_req_buf(int idx, void *buf, u32 len, u32 *frame_end) { ... //复制JPEG到USB缓存,下一步发送到USB memcpy(buf, uvc_video_info.reqbuf, uvc_video_info.send_len); ... }
USB做HID从机方法(演示做键盘的方法)
4.1 在
apps/demo/demo_DevKitBoard/include/demo_config.h
,开启宏USE_USB_HID_SLAVE_TEST_DEMO
。4.2 增加
hid_keyboard.c
4.3 在
app_config.h
修改宏和选择增加枚举键盘还是pos机,pos机流程一样,具体可以参考apps/scan_box/app_main.c
。#define USB_DEVICE_CLASS_CONFIG (HID_CLASS) #if TCFG_USB_SLAVE_HID_ENABLE #define USB_HID_KEYBOARD_ENABLE 1
USB做主机读U盘
5.1 在
board.c
增加配置{ "udisk", &mass_storage_ops, NULL },
- 5.2 在
app_config.h
打开宏 -
#define TCFG_UDISK_ENABLE 1 //U盘功能 // #define USB_DEVICE_CLASS_CONFIG (AUDIO_CLASS)
- 5.2 在
5.3 在
apps/demo/demo_DevKitBoard/include/demo_config.h
,开启宏USE_USB_UDISK_HOST_TEST_DEMO
。
USB做UVC主机(设备USB口接上UVC摄像头)
- 6.1 在
app_config.h
打开宏 -
#define TCFG_HOST_UVC_ENABLE 1 //打开USB 后拉摄像头功能,需要使能住机模式
- 6.1 在
- 6.2 查看USB摄像头方法:
A:设备连接后,打开电脑自带的相机出图。下面三种方法是跑
wifi_camera
工程时的方法。B:使用DVrunning2的APP可以直接连接wifi热点出图。
C:使用
wifi_camera
下demo_ui
,则开启UI,打开UI宏:codeblock的工程编译选项(build options)的#define选项加上CONFIG_UI_ENABLE,可以显示到屏幕(或者linux环境下的makefile加上UI宏CONFIG_UI_ENABLE)。D: 在
apps\wifi_camera\app_main.c
开启插卡录像功能,如下代码块。插USB UVC摄像头后,再插SD卡就可以开启录像功能,在录像一个文件结束(默认3分钟)就可以拔出SD卡来插卡查看UVC录像视频。
- 6.3 UVC主机流程:
(1)、打开UVC。以user_video_rec2.c为例子,user_video_rec2.c中的user_video_rec0_open()调用server_open()。
(2)、中断接收数据。
//*************usb_video.c****************// int uvc_host_open_camera(void *fd) { ... usb_uvc_info(device);//发送配置到从机UVC ret = usb_control_msg(host_dev, USB_REQ_SET_INTERFACE, 0x01, uvc->if_alt_num, hdl->itf_base + 1, NULL, 0); log_debug("USB_REQ_SET_INTERFACE %d to %d", hdl->itf_base + 1, uvc->if_alt_num); check_ret(ret); iso_ep_rx_init(device);//中断接收初始化 uvc->camera_open = 1; ... } static void vs_iso_handler(struct usb_host_device *host_dev, u32 ep) { //中断函数,数据包读取,数据包回调 ... //请求下一个数据包 usb_h_ep_read_async(usb_id, uvc->host_ep, uvc->ep, NULL, 0, USB_ENDPOINT_XFER_ISOC, 1); //当前数据包 start_addr = hdl->buffer; ... //一帧JPEG结束,进入帧结束回调。uvc->stream_out实际指向uvc_host.c的uvc_mjpg_stream_out(); if (uvc->stream_out) { uvc->stream_out(uvc->priv, -1, NULL, STREAM_ERR); stream_state = STREAM_ERR; } } //*************uvc_host.c****************// int uvc_mjpg_stream_out(void *fd, int cnt, void *stream_list, int state) { ... uvc_buf_stream_finish(fh, fh->buf, ch);//推流往应用层 ... }
6.4 常见问题:
(1)、UVC应用层接收帧率低问题处理。
A:确定中断是否接受到足够帧,在中断函数一帧 JPEG 图片完成回调函数加打印计数等方式检测。
B:检测是否在数据包回调函数丢帧,
uvc_mjpg_stream_out()
函数。打开丢帧处理打印putchar('d');
。C:在
app_config.h
注释CONFIG_VIDEO_REC_PPBUF_MODE
,则使用 lbuf 模式。D:加大应用层视频对应的 buf 缓冲区大小,如
user_video_rec2.c
的请求视频流的req.rec.buf
和req.rec.buf_len
参数的值。E:APP 图传的部分详情
WIFI_CAMERA工程说明
。
USB做HID主机方法(演示做键盘的方法)
7.1 在
app_config.h
修改宏 ,开启宏#define TCFG_HID_HOST_ENABLE
。#ifdef CONFIG_USB_ENABLE #define TCFG_PC_ENABLE 0 //使用从机模式 #define USB_PC_NO_APP_MODE 2 //不使用app_core #define USB_MALLOC_ENABLE 1 #define USB_DEVICE_CLASS_CONFIG (HID_CLASS) #define TCFG_HOST_AUDIO_ENABLE 0 #define TCFG_HOST_UVC_ENABLE 0 //打开USB 后拉摄像头功能,需要使能住机模式 #define TCFG_HID_HOST_ENABLE 1 //HID主机功能 #define TCFG_UDISK_ENABLE 0 //U盘功能
USB做UAC主机方法,具体代码参考
uac_host_demo.c
,跑wifi_story_machine
工程
8.1 在
board_config.h
修改宏#ifdef CONFIG_USB_ENABLE #define TCFG_PC_ENABLE 0 //使用从机模式 #define TCFG_HOST_AUDIO_ENABLE 1 //uac主机功能,用户需要自己补充uac_host_demo.c里面的两个函数
2.17.4. USB摄像头使用说明(UVC)
例如:使用工程
wifi_camera
进行编译测试 。
配置芯片使用SROM 即 去掉 CONFIG_NO_SDRAM_ENABLE宏 一般在全局宏配置。
在
app_config.h
中将CONFIG_UVC_VIDEO2_ENABLE宏开启。
在
video_buf_config.h
中改大NET_VREC0_FBUF_SIZE宏大小。
将CONFIG_VIDEO_REC_PPBUF_MODE宏进行屏蔽 屏蔽默认使用LBUF 即帧buf。
有关帧数使用规格640*480 25fps(USB摄像头) wifi图传20fps。
当想要使用UI显示UVC摄像头时候需要提前确认好屏幕驱动已经调好能正常显示,然后将CONFIG_UI_ENABLE宏开启 并且在
app_config.h
中将TCFG_DEMO_UI_RUN, NO_UI_LCD_TEST 均配置为 1。(其他工程暂不支持)
支持屏幕和UVC同时显示摄像头内容。
2.17.5. USB与PC通信
列举了两种USB与PC端通信协议的使用方式,SCSI协议与CDC协议,可参考应用于PC端的上位机开发。
1.USB SCSI协议的使用
1.USB配置。SCSI协议一般用于U盘、读卡器等大容量存储设备。在SDK中,将USB配置成从机模式,并配置设备类型为存储设备;在
app_config.h
文件中进行配置,如下:#define TCFG_PC_ENABLE 1 //打开usb从机功能(打开连接PC电脑模式) #define USB_DEVICE_CLASS_CONFIG (MASSSTORAGE_CLASS) //从机电脑盘符功能选择
2.SCSI协议数据处理。在
include_lib/driver/device/usb/scsi.h
文件中,添加自定义的SCSI协议的操作码,注意不能和现有的操作码重复,如下:#define MY_TEST_OPCODE 0x7d
3.在
apps/common/usb/device/msd.c
文件中,添加SCSI协议数据的处理函数,如下:enum { TEST_CMD_0, TEST_CMD_1, }; void test_cmd_deal(const struct usb_device_t *usb_device) { u8 len; u8 buf[32]; switch (msd_handle[usb_id]->cbw.lun) { case TEST_CMD_0: printf("^^^TEST_CMD_0\n"); //返回数据至USB主机 memset(buf, 0xaa, sizeof(buf)); msd_mcu2usb(usb_device, buf, sizeof(buf)); break; case TEST_CMD_1: printf("^^^TEST_CMD_1\n"); //接收USB主机发送的数据 len = msd_handle[usb_id]->cbw.LengthL; msd_usb2mcu(usb_device, buf, len); put_buf(buf, sizeof(buf)); break; default: printf("^^^no support cmd\n"); break; } } void USB_MassStorage(const struct usb_device_t *usb_device) { //...... switch (msd_handle[usb_id]->cbw.operationCode) { //...... case MY_TEST_OPCODE: //自定义的操作码处理 test_cmd_deal(usb_device); break; //...... } }
4.数据收发测试。可使用USB调试助手,向USB从机发送SCSI命令进行测试,如下:
2.USB CDC协议的使用
1.USB配置。CDC协议将USB设备枚举为虚拟串口,PC端通过串口协议与USB从机进行通信。在SDK中,将USB配置成从机模式,并配置设备类型为CDC类设备;在
app_config.h
文件中进行配置,如下://3.从机连接PC模式下:电脑盘符功能、UVC功能 #define TCFG_PC_ENABLE 1 //打开usb从机功能(打开连接PC电脑模式) #if TCFG_PC_ENABLE ... #define USB_DEVICE_CLASS_CONFIG (CDC_CLASS) //从机电脑盘符功能选择 #endif
2.CDC传输中断使能。在
apps/common/usb/device/cdc.c
中,开启cdc协议接收中断:static void cdc_endpoint_init(struct usb_device_t *usb_device, u32 itf) { ...... usb_g_set_intr_hander(usb_id, CDC_DATA_EP_OUT | USB_DIR_OUT, cdc_intrrx); //打开使能cdc中断 /* usb_g_set_intr_hander(usb_id, CDC_DATA_EP_OUT | USB_DIR_OUT, cdc_wakeup_handler);//使能cdc中断同步注释*/ ...... } void cdc_register(const usb_dev usb_id) { ...... cdc_set_output_handle(0,NULL, usb_cdc_output_handler);//注册数据处理函数 ...... }
3.CDC协议数据处理。在
apps/common/usb/device/cdc.c
中,添加自定义的CDC协议数据处理代码。(该代码默认关闭,如需开启,把late_initcall(cdc_test_main)
的注释去除即可)如下:static OS_SEM psem; static u8 rx_buf[64]; static u8 rx_len; s32 usb_cdc_output_handler(void *priv, u8 *buf, u32 len) { //将收到的数据打印 printf("len = %d, buf = %s\n", len, buf); rx_len = len; memcpy(rx_buf, buf, len); //post信号量 os_sem_post(&psem); return 0; } void cdc_rx_test() { os_sem_create(&psem, 0); while(1){ //pend信号量 os_sem_pend(&psem, 0); printf("len = %d, buf = %s\n", rx_len, rx_buf); //将收到的数据发送至终端 cdc_write_data(0, &rx_buf, rx_len); printf("cdc_test\n"); } } void cdc_test_main() { if (thread_fork("cdc_rx_test", 10, 512, 0, NULL, cdc_rx_test, NULL) != OS_NO_ERR) { printf("thread fork fail\n"); } return; } late_initcall(cdc_test_main);
4.数据收发测试。可通过串口调试助手进行收发测试,如下:
5.若想使用USB虚拟串口打印log:
在
apps/common/debug/debug_user.c
打开宏#define CONFIG_USB_DEBUG_ENABLE //开关USB虚拟串口打印信息
2.17.6. 常见问题
1. 宏TCFG_PC_ENABLE,是USB从机功能总包起来的宏,具体的定义可以进入 apps/common/usb/usb_std_class_def.h
和 apps/common/usb/usb_common_def.h
中查看。
如需移植usb从机功能时,需包含 usb_std_class_def.h
和 usb_common_def.h
这两个文件。
2. 需要移植usb功能时,需要添加 apps/common/system/device_mount.c
文件。