[Cortex-M0技术交流] 一个最简的 USB Audio 示例

[复制链接]
 楼主| john_lee 发表于 2013-5-18 02:24 | 显示全部楼层 |阅读模式
IO, USB, ST, TE, TI
本帖最后由 john_lee 于 2013-5-18 14:34 编辑

经过了两三个月的痛苦,USB 协议栈的 Audio Device Class 框架已具雏形了,用了两三天时间,使用这个框架实战了一个基于新唐 M0 的最简单的 USB Audio 程序,可以作为 USB 声卡。
附件中是生成的 hex 文件,可以运行在菜农助学板上,烧写时需要配置 Config0 中的时钟选择为 external 12Mhz。
代码空间开销:
  1.    text    data     bss     dec     hex filename
  2.    1580     244     168    1992     7c8 .\default\adc.elf
整个程序占用 flash 大约 1.8K,除去中断向量表、系统启动、硬件初始化和 I2S功能的空间开销,框架在这个示例程序中所占空间大约 1.2K 左右,随着功能多少的变化,框架的开销也会有变化,但对比目前市面上的各种 USB 协议栈驱动,我想它应该是开销最小的框架了。
先贴出应用程序,恭请各位提提意见,我好改进框架,谢谢。
  1. #include <usb/hal/numicro>                                  // USB HAL(Hardware Abstract Layer),新唐 Numicro
  2. #include <usb/adc>                                          // USB Audio Device Class
  3. #include <numicro/nuc/sfr/i2s>                              // I2S Register

  4. void i2s_start();

  5. using namespace usb;                                        // 使用 usb 名空间
  6. using namespace adc;                                        // 使用 usb adc(audio device class)名空间

  7. template<typename PARENT, uint8_t ID>                       // 定式
  8. struct it_t : it_impl_t<                                    // 定义 Input Terminal 类模板
  9.             it_t, PARENT, ID,                               // 定式
  10.             0                                               // 指定 String ID,为 0 表示不定义
  11.         > {
  12.     using input_t = typename it_t::it_impl_t;               // 为本类指定一个“别名”,方便在定义 Entities 集合时标识
  13. };

  14. template<typename PARENT, uint8_t ID>
  15. struct ot_t : ot_impl_t<                                    // 定义 Output Terminal 类模板
  16.             ot_t, PARENT, ID,
  17.             0
  18.         > {
  19.     using output_t = typename ot_t::ot_impl_t;
  20. };

  21. template<typename PARENT>                                   // 定式
  22. struct tuple_t : entity_tuple_t<                            // 定义 Entities 集合类,其中可以加入以上定义的各个 Entity 类模板
  23.             PARENT,                                         // 定式
  24.             it_t,                                           // 已定义的类模板
  25.             ot_t                                            // 已定义的类模板
  26.         > {
  27.     using typename tuple_t::entity_tuple_t::input_t;        // 导入各个 Terminal 的别名
  28.     using typename tuple_t::entity_tuple_t::output_t;       // ...
  29.     struct __attribute__((packed)) entities_descriptor_t    // 定义 Entities 集合的描述符
  30.         :   td_t<                                           // 定义 Terminal 描述符
  31.                 input_t,                                    // 使用已导入的别名,用于确定本 Entity ID,
  32.                 USB_STREAMING,                              // 指定 Terminal 类型
  33.                 cluster_t<                                  // 定义 Channel(声道) Cluster,其中可以加入任意声道
  34.                     0,                                      // 指定 String ID
  35.                     cluster_channel_t::LEFT_FRONT,          // 指定 Left Front
  36.                     cluster_channel_t::RIGHT_FRONT          // 指定 Right Front
  37.                 >
  38.             >,
  39.             td_t<                                           // 定义 Terminal 描述符
  40.                 output_t,                                   // 使用已导入的别名,用于确定本 Entity ID,
  41.                 SPEAKER,                                    // 指定 Terminal 类型
  42.                 input_t                                     // 指定本输出 Terminal 的数据来源
  43.             > { };
  44. };

  45. template<typename PARENT, uint32_t PARAM>                   // 定式
  46. class ep_t : public streaming::data::out::ep_impl_t<        // 定义 Audio Data Endpoint(数据端点)类
  47.             ep_t, PARENT, PARAM,                            // 定式
  48.             core::transfer::iso::sync_t::SYNC,              // 指定端点的 sync 类型
  49.             false,                                          // 指定 bmAttributes 的 sampling frequency,参见 USB Audio 规范
  50.             false,                                          // 指定 bmAttributes 的 pitch,参见 USB Audio 规范
  51.             false,                                          // 指定 bmAttributes 的 MaxPacketsOnly, 参见 USB Audio 规范
  52.             streaming::UNDEFINED,                           // 指定 bLockDelayUnits, 参见 USB Audio 规范
  53.             0                                               // 指定 wLockDelay, 参见 USB Audio 规范
  54.         > { };

  55. #define SAMPLE_RATE     16000

  56. struct {                                                    // 定义 Audio Data 缓冲区
  57.     union {
  58.         struct {
  59.             uint8_t wrpos;                                  // 写缓冲指针
  60.             uint8_t rdpos;                                  // 读缓冲指针
  61.         };
  62.         uint16_t pos;
  63.     };
  64.     uint32_t data[64 * 2 / sizeof(uint32_t)];               // 缓冲数据区,包含两个 packet
  65. } buffer;

  66. template<typename PARENT, uint32_t PARAM>                   // 定式
  67. class ifc_t : public control::if_impl_t<                    // 定义 AudioControl Interface(控制接口)类
  68.             ifc_t, PARENT, PARAM,                           // 定式
  69.             0                                               // 指定 String ID
  70.         > { };

  71. template<typename PARENT, uint32_t PARAM>                   // 定式
  72. class ifs_t : public streaming::if_impl_t<                  // 定义 AudioStream Interface(流接口)类
  73.             ifs_t, PARENT, PARAM,                           // 定式
  74.             0,                                              // 指定 String ID
  75.             typename tuple_t<PARENT>::input_t,              // 指定连接到的 terminal
  76.             ep_t                                            // 指定本接口包含的数据端点(暂不支持同步端点)
  77.         > {
  78. public:
  79.     template<uint8_t N> using ep_t = typename core::elem_t<N, typename ifs_t::tuple_t>::type;

  80.     struct __attribute__((packed)) descriptor_t                                     // 定义流接口描述符
  81.             :   core::descriptor_t<core::descriptor::std_if_t<ifs_t, 0, 0, 0>>,     // Alternative 0
  82.                 core::descriptor_t<core::descriptor::std_if_t<ifs_t, 1, 0, 0>,      // Alternative 1
  83.                     streaming::pcm_descriptor_t<                                    // 定义 PCM 描述符
  84.                         ifs_t,              // 定式
  85.                         1,                  // bDelay
  86.                         2,                  // bNrChannels
  87.                         2,                  // bSubframeSize
  88.                         16,                 // bBitResolution
  89.                         true,               // 指定固定采样
  90.                         SAMPLE_RATE         // 采样率
  91.                     >,
  92.                     ep_t<0>                 // 指定 Alternative 1 所包含的 Endpoint
  93.                 > { };

  94.     __INLINE bool set_interface(uint_fast8_t alternative)   // Set Interface 处理
  95.     {
  96.         if (alternative) {
  97.             first = true;
  98.             this->read(buffer.data, 64);                    // 读 packet
  99.             buffer.pos = 0;
  100.         }
  101.         return true;
  102.     }
  103.     __INLINE void read_complete(uint_fast16_t length)       // 读 packet 完成
  104.     {
  105.         uint_fast8_t pos = buffer.wrpos;
  106.         pos ^= 64 / sizeof(uint32_t);
  107.         buffer.wrpos = pos;
  108.         this->read(&buffer.data[pos], 64);                  // 读 packet
  109.         if (first) {
  110.             first = 0;
  111.             i2s_start();                                    // 启动 I2S
  112.         }
  113.     }

  114. private:
  115.     uint8_t first;
  116. };

  117. template<typename PARENT, uint32_t PARAM>       // 定式
  118. class fn_t : public adc::fn_impl_t<             // 定义 Function
  119.             fn_t, PARENT, PARAM,                // 定式
  120.             0,                                  // 指定 String ID
  121.             0x100,                              // 指定 bcdADC,Release Number
  122.             ifc_t,                              // 已定义的 AudioControl Interface
  123.             tuple_t<fn_t<PARENT, PARAM>>,       // 已定义的 Entiies 集合
  124.             ifs_t                               // 已定义的 AudioStream Interface,可连续加入多个
  125.         > { };

  126. class usbd_t : public core::usbd_impl_t<        // 定义 USB 类
  127.         usbd_t,                                 // 定式
  128.         0x110,                  // bcdUSB
  129.         0,                      // bDeviceClass
  130.         0,                      // bDeviceSubClass
  131.         0,                      // bDeviceProtocol
  132.         0x0416,                 // idVendor
  133.         0x5011,                 // idProduct
  134.         0x100,                  // bcdDevice
  135.         1,                      // iManufacture
  136.         2,                      // iProduct
  137.         3,                      // iSerialNumber
  138.         true,                   // bmAttributes, Bus Powered
  139.         false,                  // bmAttributes, Self Powered
  140.         false,                  // bmAttributes, Remote Wakeup
  141.         50_mA,                  // bMaxPower
  142.         0,                      // iConfiguration
  143.         fn_t> {                 // 已定义的 Function,可连续加入多个 Function 和 Interface

  144. public:
  145.     __INLINE usbd_t() { }

  146. #if 1
  147.     __INLINE bool data_out(uint_fast8_t type, uint_fast8_t request, uint_fast16_t value, uint_fast16_t index, uint_fast16_t length)
  148.     {
  149.         out();
  150.         return true;
  151.     }
  152.     __INLINE bool data_in(uint_fast8_t type, uint_fast8_t request, uint_fast16_t value, uint_fast16_t index, uint_fast16_t length)
  153.     {
  154.         in();
  155.         return true;
  156.     }
  157. #endif

  158.     __INLINE const uint8_t* get_string_descriptor(uint_fast8_t index, uint_fast16_t lang_id)    // GetDescriptor(String) 处理
  159.     {
  160.         static const string_langid_t<langid_t::English_UnitedStates> desc0;
  161.         static const string_t<u'j', u'.', u'y', u'.', u'l', u'e', u'e', u'@', u'y', u'e', u'a', u'h', u'.', u'n', u'e', u't'> desc1;
  162.         static const string_t<u'U', u'S', u'B', u' ', u'A', u'u', u'd', u'i', u'o'> desc2;
  163.         static const string_t<u'0', u'0', u'0', u'0'> desc3;
  164.         static const uint8_t* const descriptor[] {
  165.             reinterpret_cast<const uint8_t*>(&desc0),
  166.             reinterpret_cast<const uint8_t*>(&desc1),
  167.             reinterpret_cast<const uint8_t*>(&desc2),
  168.             reinterpret_cast<const uint8_t*>(&desc3)
  169.         };
  170.         return index < sizeof(descriptor) / sizeof(descriptor[0]) ? descriptor[index] : nullptr;
  171.     }
  172. };

  173. usbd_t usbd;                // 定义 USB 类对象

  174. void usbd_isr()             // USBD_IRQn 中断向量 handler
  175. {
  176.     usbd.isr();             // 调用 USB 类的中断处理
  177. }

  178. void i2s_isr()              // I2S_IRQn 中断向量 handler
  179. {
  180.     using namespace sfr::i2s;
  181.     if (I2S.STATUS().TXUDF) {
  182.         I2S.STATUS(0).TXUDF(1);
  183.         I2S.CON(0);
  184.         I2S.IE(0);
  185.         return;
  186.     }
  187.     if (buffer.pos == 0x400 || buffer.pos == 0x1014) {
  188.         I2S.IE(0).TXUDFIE(1);
  189.         return;
  190.     }
  191.     // 从 Audio Data 缓冲填充数据到 I2S 发送 FIFO
  192.     uint_fast8_t rdpos{ buffer.rdpos };
  193.     do {
  194.         I2S.TXFIFO = buffer.data[rdpos++];
  195.         if (rdpos == sizeof(buffer.data) / 4)
  196.             rdpos = 0;
  197.     } while (I2S.STATUS().TXFULL == 0);
  198.     buffer.rdpos = rdpos;
  199.     // 调整 I2S 速率
  200.     uint_fast16_t pos{ buffer.pos };
  201.     if (pos == 0xc00 || pos == 0x1c10) {        // 加快
  202.         I2S.CLKDIV(0).BCLK_DIV(45);
  203.     } else if (pos == 0 || pos == 0x1010) {     // 减慢
  204.         I2S.CLKDIV(0).BCLK_DIV(47);
  205.     } else
  206.         I2S.CLKDIV(0).BCLK_DIV(46);             // 恢复正常
  207. }

  208. void i2s_start()                                // I2S 启动
  209. {
  210.     using namespace sfr::i2s;
  211.     do {
  212.         I2S.TXFIFO = 0;
  213.     } while (I2S.STATUS().TXFULL == 0);
  214.     I2S.CON(0)
  215.         .I2SEN(1)
  216.         .TXEN(1)
  217.         .WORDWIDTH(1)
  218.         .FORMAT(1)
  219.         .TXTH(4);
  220.     I2S.IE(0).TXTHIE(1);
  221. }

  222. int main()
  223. {
  224.     using namespace sfr::i2s;
  225.     I2S.CLKDIV(0).BCLK_DIV(46);                 // 设置 I2S 默认速率
  226.     *reinterpret_cast<volatile uint32_t*>(0xe000e100) = 1 << I2S_IRQn;  // NVIC:允许 I2S 中断
  227.     usbd.open(true);                            // 初始化 usb 对象
  228.     while (true);
  229. }
程序中所有的类和模板的层次结构:
  1. usbd_t                 USB device 的封装
  2. |
  3. +--fn_t<...>           Interface 的容器
  4.    |
  5.    +--ifc_t<...>       Audio Control Interface 的模板封装
  6.    |
  7.    +--tuple_t<...>     Terminal 和 Unit 的容器
  8.    |  |
  9.    |  +--it_t<...>     Input Terminal 的模板封装
  10.    |  |
  11.    |  +--ot_t<...>     Output Terminal 的模板封装
  12.    |
  13.    +--ifs_t<...>       Audio Stream Interface 的模板封装
  14.       |
  15.       +--ep_t<...>     Audio Data Endpoint 的模板封装

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

×

评分

参与人数 1威望 +8 收起 理由
xyz549040622 + 8

查看全部评分

缥缈九哥 发表于 2013-5-18 02:26 | 显示全部楼层
顶起。。。
 楼主| john_lee 发表于 2013-5-18 02:28 | 显示全部楼层
占沙发,:D
缥缈九哥 发表于 2013-5-18 02:36 | 显示全部楼层
john_lee <j.y.lee@yeah.net>  2:32:12
擦,动作也太快了吧!
居然把沙发给我抢走了
缥缈九哥(14131338)  2:34:48
哈哈
xukaiming 发表于 2013-5-18 09:26 | 显示全部楼层
地板
乡村男孩 发表于 2013-5-18 09:28 | 显示全部楼层
强人 看得我
lxyppc 发表于 2013-5-18 10:24 | 显示全部楼层
标记,这是个好东西
呆板书生 发表于 2013-5-18 10:40 | 显示全部楼层
标记一下
mcu5i51 发表于 2013-5-18 13:51 | 显示全部楼层
好东西要顶
nomefat 发表于 2013-5-18 14:58 | 显示全部楼层
路过
dirtwillfly 发表于 2013-5-18 21:35 | 显示全部楼层
顶李老师
缥缈九哥 发表于 2013-5-19 03:31 | 显示全部楼层
可是功力不够 。一看就有走火入魔的感觉 ,赶紧退出了。
dong_abc 发表于 2013-5-23 01:12 | 显示全部楼层
码功 太薄弱,看不懂。
 楼主| john_lee 发表于 2013-5-23 02:08 | 显示全部楼层
楼上在不在九哥的群 18748628 里?不在可以加进来聊聊。:D
 楼主| john_lee 发表于 2013-5-23 02:10 | 显示全部楼层
dong_abc 发表于 2013-5-23 01:12
码功 太薄弱,看不懂。

在不在九哥的群 18748628 里?不在可以加进来聊聊。
sunbingbing 发表于 2013-5-23 07:08 | 显示全部楼层
[em:2:
dong_abc 发表于 2013-5-23 12:27 | 显示全部楼层
john_lee 发表于 2013-5-23 02:10
在不在九哥的群 18748628 里?不在可以加进来聊聊。

我在菜农群的QQ号被腾讯收回去了, 下班回来再加. 近两年没折腾单片机了.
jstele 发表于 2013-5-24 07:58 | 显示全部楼层

顶!强烈支持
dong_abc 发表于 2013-5-24 22:01 | 显示全部楼层
缥缈九哥 发表于 2013-5-19 03:31
可是功力不够 。一看就有走火入魔的感觉 ,赶紧退出了。

9G,你这个群,我怎么加了3天都没加上啊,18748628 。
 楼主| john_lee 发表于 2013-5-24 22:21 | 显示全部楼层
dong_abc 发表于 2013-5-24 22:01
9G,你这个群,我怎么加了3天都没加上啊,18748628 。

群号没错啊,我问问9g。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

个人签名:坚持使用 GCC 一百年不动摇!

33

主题

1466

帖子

21

粉丝
快速回复 在线客服 返回列表 返回顶部