Update rst
This commit is contained in:
43
docs/source/demo/audio_mic_speaker.rst
Normal file
43
docs/source/demo/audio_mic_speaker.rst
Normal file
@@ -0,0 +1,43 @@
|
||||
USB 双通道麦克风和扬声器
|
||||
============================
|
||||
|
||||
软件实现
|
||||
------------
|
||||
|
||||
详细代码参考 `demo/audio_v1_mic_speaker_multichan_template.c`
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
usbd_desc_register(audio_descriptor);
|
||||
usbd_add_interface(usbd_audio_alloc_intf());
|
||||
usbd_add_interface(usbd_audio_alloc_intf());
|
||||
usbd_add_interface(usbd_audio_alloc_intf());
|
||||
usbd_add_endpoint(&audio_in_ep);
|
||||
usbd_add_endpoint(&audio_out_ep);
|
||||
|
||||
usbd_audio_add_entity(0x02, AUDIO_CONTROL_FEATURE_UNIT);
|
||||
usbd_audio_add_entity(0x05, AUDIO_CONTROL_FEATURE_UNIT);
|
||||
|
||||
usbd_initialize();
|
||||
|
||||
- 调用 `audio_init` 配置 audio 描述符并初始化 usb 硬件
|
||||
- 因为 麦克风+扬声器+控制需要 3 个接口,所以我们需要调用 `usbd_add_interface` 3 次
|
||||
- 默认描述符中开启了 mute 和 volume 的控制,所以需要注册对应的 entity,使用 `usbd_audio_add_entity`
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
void usbd_audio_open(uint8_t intf)
|
||||
{
|
||||
}
|
||||
void usbd_audio_close(uint8_t intf)
|
||||
{
|
||||
}
|
||||
|
||||
- 当我们打开 PC 的音量图标,或者音乐播放器、麦克风界面时,会调用到这两个接口,用于启动或者停止数据传输
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
usbd_ep_start_write(AUDIO_IN_EP, write_buffer, 2048);
|
||||
|
||||
- 由于 audio 协议中没有应用层相关的协议,传输的只有音频的原始数据,所以直接调用 `usbd_ep_start_write` 即可,发送完成会进入完成中断
|
||||
- 由于扬声器需要使用 out 端点,所以需要在 `usbd_configure_done_callback` 中启动第一次接收,当然如果没有能力接收,可以不启动,在想启动的时候启动
|
||||
78
docs/source/demo/cdc_acm.rst
Normal file
78
docs/source/demo/cdc_acm.rst
Normal file
@@ -0,0 +1,78 @@
|
||||
USB 虚拟串口(无 UART 功能)
|
||||
============================
|
||||
|
||||
USB 虚拟串口主要是借助 USB CDC ACM 类实现,将其模拟成一个 VCP 设备,当插在电脑上的时候,可以显示成一个串口设备。跟市面上的 USB2TTL模块的区别在于,虚拟串口仅仅只使用到了 USB ,没有与串口(UART外设)进行连动。
|
||||
|
||||
软件实现
|
||||
------------
|
||||
|
||||
详细代码参考 `demo/cdc_acm_template.c`
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
usbd_desc_register(cdc_descriptor);
|
||||
usbd_add_interface(usbd_cdc_acm_alloc_intf());
|
||||
usbd_add_interface(usbd_cdc_acm_alloc_intf());
|
||||
usbd_add_endpoint(&cdc_out_ep);
|
||||
usbd_add_endpoint(&cdc_in_ep);
|
||||
usbd_initialize();
|
||||
|
||||
- 调用 `cdc_acm_init` 配置 cdc acm 描述符并初始化 usb 硬件
|
||||
- 因为 cdc 有 2 个接口,所以我们需要调用 `usbd_add_interface` 2 次
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
void usbd_configure_done_callback(void)
|
||||
{
|
||||
/* setup first out ep read transfer */
|
||||
usbd_ep_start_read(CDC_OUT_EP, read_buffer, 2048);
|
||||
}
|
||||
|
||||
void usbd_cdc_acm_bulk_out(uint8_t ep, uint32_t nbytes)
|
||||
{
|
||||
USB_LOG_RAW("actual out len:%d\r\n", nbytes);
|
||||
// for (int i = 0; i < 100; i++) {
|
||||
// printf("%02x ", read_buffer[i]);
|
||||
// }
|
||||
// printf("\r\n");
|
||||
/* setup next out ep read transfer */
|
||||
usbd_ep_start_read(CDC_OUT_EP, read_buffer, 2048);
|
||||
}
|
||||
|
||||
void usbd_cdc_acm_bulk_in(uint8_t ep, uint32_t nbytes)
|
||||
{
|
||||
USB_LOG_RAW("actual in len:%d\r\n", nbytes);
|
||||
|
||||
if ((nbytes % CDC_MAX_MPS) == 0 && nbytes) {
|
||||
/* send zlp */
|
||||
usbd_ep_start_write(CDC_IN_EP, NULL, 0);
|
||||
} else {
|
||||
ep_tx_busy_flag = false;
|
||||
}
|
||||
}
|
||||
|
||||
void usbd_cdc_acm_set_dtr(uint8_t intf, bool dtr)
|
||||
{
|
||||
if (dtr) {
|
||||
dtr_enable = 1;
|
||||
} else {
|
||||
dtr_enable = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void cdc_acm_data_send_with_dtr_test(void)
|
||||
{
|
||||
if (dtr_enable) {
|
||||
memset(&write_buffer[10], 'a', 2038);
|
||||
ep_tx_busy_flag = true;
|
||||
usbd_ep_start_write(CDC_IN_EP, write_buffer, 2048);
|
||||
while (ep_tx_busy_flag) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- `usbd_cdc_acm_set_dtr` 函数是主机发送流控命令时的回调函数,这里我们使用 dtr ,当开启 dtr 时,启动发送
|
||||
- `usbd_configure_done_callback` 是枚举完成的回调函数,因为 cdc acm 有 out 端点,所以我们需要在这里启动第一次数据的接收,当然,如果你现在没有能力接收数据,可以不启动。 **数据长度需要是最大包长的整数倍**。
|
||||
- `usbd_cdc_acm_bulk_out` 是接收完成中断回调,我们在这里面启动下一次接收
|
||||
- `usbd_cdc_acm_bulk_in` 是发送完成中断回调,我们在这里检查发送长度是否是最大包长的整数,如果是,需要发送 zlp 包表示结束
|
||||
- 调用 `usbd_ep_start_write` 进行发送,需要注意,如果返回值小于0,不能执行下面的 while
|
||||
@@ -1,2 +0,0 @@
|
||||
DAPLINK V2.0调试器
|
||||
=========================
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 13 KiB |
@@ -1,2 +0,0 @@
|
||||
键盘与鼠标
|
||||
=========================
|
||||
@@ -1,2 +0,0 @@
|
||||
U盘制作 BootLoader
|
||||
=========================
|
||||
42
docs/source/demo/msc_ram.rst
Normal file
42
docs/source/demo/msc_ram.rst
Normal file
@@ -0,0 +1,42 @@
|
||||
USB 模拟 U 盘
|
||||
============================
|
||||
|
||||
软件实现
|
||||
------------
|
||||
|
||||
详细代码参考 `demo/msc_ram_template.c`
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
usbd_desc_register(msc_ram_descriptor);
|
||||
usbd_add_interface(usbd_msc_alloc_intf(MSC_OUT_EP, MSC_IN_EP));
|
||||
|
||||
usbd_initialize();
|
||||
|
||||
- 调用 `msc_ram_init` 配置 msc 描述符并初始化 usb 硬件
|
||||
- 因为 msc 有1个接口,所以我们需要调用 `usbd_add_interface` 1次
|
||||
- msc 中的端点的数据流是协议栈这边管理,所以不需要用户注册端点的回调函数。同理 `usbd_configure_done_callback` 也不需要,为空即可
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
void usbd_msc_get_cap(uint8_t lun, uint32_t *block_num, uint16_t *block_size)
|
||||
{
|
||||
*block_num = 1000; //Pretend having so many buffer,not has actually.
|
||||
*block_size = BLOCK_SIZE;
|
||||
}
|
||||
int usbd_msc_sector_read(uint32_t sector, uint8_t *buffer, uint32_t length)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int usbd_msc_sector_write(uint32_t sector, uint8_t *buffer, uint32_t length)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
- 实现三个接口即可使用 msc,读写操作如果没有 os 则是在中断中
|
||||
- `CONFIG_USBDEV_MSC_BLOCK_SIZE` 可以为 512 的整数倍,更改此项,可以增加 msc 的读写速度,当然,也会消耗更多的 ram
|
||||
|
||||
|
||||
.. note:: MSC 一般配合 rtos 使用,因为读写操作是阻塞的,放中断是不合适的, `CONFIG_USBDEV_MSC_THREAD` 则是使能 os 管理
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
USB 双通道扬声器和麦克风
|
||||
===========================
|
||||
@@ -1,198 +0,0 @@
|
||||
USB 转串口
|
||||
=========================
|
||||
|
||||
USB转串口即实现计算机USB接口到通用串口之间的转换。为没有串口的计算机提供快速的通道,而且,使用USB转串口设备等于将传统的串口设备变成了即插即用的USB设备。市面上的比如 CH340、FT232、PL2302 等等。
|
||||
所以如果需要实现该功能,那么我们使用的芯片需要具备以下两个条件:
|
||||
|
||||
- 有 UART 外设
|
||||
- 有 USB DEVICE 外设
|
||||
|
||||
接下来,我们就可以愉快的进行代码的编写了。USB 转串口我们采用的是 CDC ACM 类。第一步我们先让 USB 能正常收发。
|
||||
|
||||
- 首先是实现 `usbd_cdc_acm_set_line_coding` 函数,这个函数的作用就是当我们点击电脑上的串口软件设置串口相关信息时(如下图所示),主机会通过 USB 发送设置串口的命令给设备,对设备上的串口进行配置,所以这边我们需要根据形参来配置串口。
|
||||
|
||||
.. figure:: img/usb2uart1.png
|
||||
|
||||
- 其次实现 `usbd_cdc_acm_set_rts`,主要用来设置串口硬件流控。这里需要注意, windows 上任何软件是无法设置 RTS 的,可以说是 windows 驱动问题
|
||||
- 调用 `usbd_desc_register` 注册 cdc acm 描述符,包含设备描述符、配置描述符、cdc acm 特定描述符、接口描述符、端点描述符、字符串描述符等等
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
static const uint8_t cdc_descriptor[] = {
|
||||
USB_DEVICE_DESCRIPTOR_INIT(USB_2_0, 0xEF, 0x02, 0x01, USBD_VID, USBD_PID, 0x0100, 0x01),
|
||||
USB_CONFIG_DESCRIPTOR_INIT(USB_CONFIG_SIZE, 0x02, 0x01, USB_CONFIG_BUS_POWERED, USBD_MAX_POWER),
|
||||
CDC_ACM_DESCRIPTOR_INIT(0x00, CDC_INT_EP, CDC_OUT_EP, CDC_IN_EP, 0x02),
|
||||
///////////////////////////////////////
|
||||
/// string0 descriptor
|
||||
///////////////////////////////////////
|
||||
USB_LANGID_INIT(USBD_LANGID_STRING),
|
||||
///////////////////////////////////////
|
||||
/// string1 descriptor
|
||||
///////////////////////////////////////
|
||||
0x14, /* bLength */
|
||||
USB_DESCRIPTOR_TYPE_STRING, /* bDescriptorType */
|
||||
'C', 0x00, /* wcChar0 */
|
||||
'h', 0x00, /* wcChar1 */
|
||||
'e', 0x00, /* wcChar2 */
|
||||
'r', 0x00, /* wcChar3 */
|
||||
'r', 0x00, /* wcChar4 */
|
||||
'y', 0x00, /* wcChar5 */
|
||||
'U', 0x00, /* wcChar6 */
|
||||
'S', 0x00, /* wcChar7 */
|
||||
'B', 0x00, /* wcChar8 */
|
||||
///////////////////////////////////////
|
||||
/// string2 descriptor
|
||||
///////////////////////////////////////
|
||||
0x26, /* bLength */
|
||||
USB_DESCRIPTOR_TYPE_STRING, /* bDescriptorType */
|
||||
'C', 0x00, /* wcChar0 */
|
||||
'h', 0x00, /* wcChar1 */
|
||||
'e', 0x00, /* wcChar2 */
|
||||
'r', 0x00, /* wcChar3 */
|
||||
'r', 0x00, /* wcChar4 */
|
||||
'y', 0x00, /* wcChar5 */
|
||||
'U', 0x00, /* wcChar6 */
|
||||
'S', 0x00, /* wcChar7 */
|
||||
'B', 0x00, /* wcChar8 */
|
||||
' ', 0x00, /* wcChar9 */
|
||||
'C', 0x00, /* wcChar10 */
|
||||
'D', 0x00, /* wcChar11 */
|
||||
'C', 0x00, /* wcChar12 */
|
||||
' ', 0x00, /* wcChar13 */
|
||||
'D', 0x00, /* wcChar14 */
|
||||
'E', 0x00, /* wcChar15 */
|
||||
'M', 0x00, /* wcChar16 */
|
||||
'O', 0x00, /* wcChar17 */
|
||||
///////////////////////////////////////
|
||||
/// string3 descriptor
|
||||
///////////////////////////////////////
|
||||
0x16, /* bLength */
|
||||
USB_DESCRIPTOR_TYPE_STRING, /* bDescriptorType */
|
||||
'2', 0x00, /* wcChar0 */
|
||||
'0', 0x00, /* wcChar1 */
|
||||
'2', 0x00, /* wcChar2 */
|
||||
'2', 0x00, /* wcChar3 */
|
||||
'1', 0x00, /* wcChar4 */
|
||||
'2', 0x00, /* wcChar5 */
|
||||
'3', 0x00, /* wcChar6 */
|
||||
'4', 0x00, /* wcChar7 */
|
||||
'5', 0x00, /* wcChar8 */
|
||||
'6', 0x00, /* wcChar9 */
|
||||
#ifdef CONFIG_USB_HS
|
||||
///////////////////////////////////////
|
||||
/// device qualifier descriptor
|
||||
///////////////////////////////////////
|
||||
0x0a,
|
||||
USB_DESCRIPTOR_TYPE_DEVICE_QUALIFIER,
|
||||
0x00,
|
||||
0x02,
|
||||
0x02,
|
||||
0x02,
|
||||
0x01,
|
||||
0x40,
|
||||
0x01,
|
||||
0x00,
|
||||
#endif
|
||||
0x00
|
||||
};
|
||||
|
||||
.. caution:: 注意数组最后的结束符,不要遗漏
|
||||
|
||||
- cdc acm 一共需要两个接口,一个控制接口,一个数据接口,由于我们只有一个 cdc acm,所以只需要注册一个 class + 两个接口。其中控制接口需要一个中断端点,数据接口需要两个 bulk 端点,由于接口驱动我们已经支持,所以调用 cdc acm 相关的添加接口的 API 即可。代码如下:
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
/*!< class */
|
||||
usbd_class_t cdc_class;
|
||||
/*!< interface one */
|
||||
usbd_interface_t cdc_cmd_intf;
|
||||
/*!< interface two */
|
||||
usbd_interface_t cdc_data_intf;
|
||||
|
||||
/*!< endpoint call back */
|
||||
usbd_endpoint_t cdc_out_ep = {
|
||||
.ep_addr = CDC_OUT_EP,
|
||||
.ep_cb = usbd_cdc_acm_out
|
||||
};
|
||||
|
||||
usbd_endpoint_t cdc_in_ep = {
|
||||
.ep_addr = CDC_IN_EP,
|
||||
.ep_cb = usbd_cdc_acm_in
|
||||
};
|
||||
|
||||
usbd_desc_register(cdc_descriptor);
|
||||
/*!< add interface */
|
||||
usbd_cdc_add_acm_interface(&cdc_class, &cdc_cmd_intf);
|
||||
usbd_cdc_add_acm_interface(&cdc_class, &cdc_data_intf);
|
||||
/*!< interface add endpoint */
|
||||
usbd_interface_add_endpoint(&cdc_data_intf, &cdc_out_ep);
|
||||
usbd_interface_add_endpoint(&cdc_data_intf, &cdc_in_ep);
|
||||
|
||||
usbd_initialize();
|
||||
|
||||
- 最后调用 `usbd_initialize` 初始化 usb。
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
/* function ------------------------------------------------------------------*/
|
||||
void usbd_cdc_acm_out(uint8_t ep)
|
||||
{
|
||||
uint8_t data[64];
|
||||
uint32_t read_byte;
|
||||
|
||||
usbd_ep_read(ep, data, 64, &read_byte);
|
||||
for (uint8_t i = 0; i < read_byte; i++) {
|
||||
printf("%02x ", data[i]);
|
||||
}
|
||||
printf("\r\n");
|
||||
printf("read len:%d\r\n", read_byte);
|
||||
usbd_ep_read(ep, NULL, 0, NULL);
|
||||
}
|
||||
|
||||
void usbd_cdc_acm_in(uint8_t ep)
|
||||
{
|
||||
printf("in\r\n");
|
||||
}
|
||||
|
||||
volatile uint8_t dtr_enable = 0;
|
||||
|
||||
void usbd_cdc_acm_set_dtr(bool dtr)
|
||||
{
|
||||
if (dtr) {
|
||||
dtr_enable = 1;
|
||||
} else {
|
||||
dtr_enable = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void cdc_acm_data_send_with_dtr_test(void)
|
||||
{
|
||||
if (dtr_enable) {
|
||||
uint8_t data_buffer[10] = { 0x31, 0x32, 0x33, 0x34, 0x35, 0x31, 0x32, 0x33, 0x34, 0x35 };
|
||||
usbd_ep_write(CDC_IN_EP, data_buffer, 10, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
- out 中断中我们将接收的数据进行打印,in 中断则是当 `usbd_ep_write` 发送完成后,进行打印
|
||||
- 实现 `usbd_cdc_acm_set_dtr` 进行流控
|
||||
|
||||
.. caution:: 注意端点接收中断中,接收 buf 的大小根据全速和高速来定,并且尽量不要在中断中开辟这么大的 buf
|
||||
|
||||
|
||||
- 此时,插上电脑可以枚举出一个 USB 设备,名称为 **USB 串行设备(COMx)**,然后我们可以打开枚举的串口,发一些数据,然后看调试口是否有数据,有的话,证明 USB 方面是通的,那上面测试的一些代码就可以删删了。
|
||||
|
||||
|
||||
上面完成后,第二步,则是跟 UART 配合使用了,那怎么完成最终的 USB 转串口呢?首先我们需要梳理一下,整个的数据传输。
|
||||
|
||||
- 首先是 USB 发数据给设备,设备接收到数据以后,通过 UART TX 发送出去
|
||||
- 其次是 UART RX 接收的数据,通过 USB 发送给主机
|
||||
|
||||
所以关于 UART RX 我们可以使用中断接收,来接收数据,但是呢,UART RX 的数据如果太多,会丢,所以我们需要准备一个 Ringbuffer 组件,将 UART RX 接收的数据存到 Ringbuffer,同理, USB OUT 接收的数据也放到 Ringbuffer,所以我们需要准备两块 Ringbuffer。关于 Ringbuffer 原理和代码,大家自行百度。
|
||||
然后接下来的代码就非常简单啦,根据上面梳理的结果,代码呈现的结果就如下:
|
||||
|
||||
- 初始化 Ringbuffer A 和 Ringbuffer B
|
||||
- USB out 中断中读取数据并存入 Ringbuffer A 中
|
||||
- while(1) 中从 Ringbuffer A 中读取数据,并使用 UART 发送函数发出去
|
||||
- UART RX 中断中接收数据,并存入 Ringbuffer B 中,while(1)中从 Ringbuffer B 中读取数据,并使用 USB 发送函数发出去
|
||||
|
||||
代码参考 :https://github.com/bouffalolab/bl_mcu_sdk/tree/master/examples/usb/usb2uart
|
||||
2
docs/source/demo/usb_video.rst
Normal file
2
docs/source/demo/usb_video.rst
Normal file
@@ -0,0 +1,2 @@
|
||||
USB 摄像头
|
||||
============================
|
||||
@@ -1,2 +0,0 @@
|
||||
USB 摄像头
|
||||
===========================
|
||||
Reference in New Issue
Block a user