stm32 usb应用笔记 -凯发k8国际

`
gashero
  • 浏览: 938466 次
  • 性别:
  • 来自: 北京
博主相关
  • 博客
  • 微博
  • 相册
  • 收藏
  • 社区版块
    • ( 0)
    • ( 3)
    • ( 0)
    存档分类
    最新评论

    stm32 usb应用笔记

    stm32 usb应用笔记

     

    usb

    作者:

    gashero

    日期:

    2013-02-06

    目录

    • 1   简介
    • 2   stm32的usb简介
    • 3   usb实现类
      • 3.1   usb-cdc
    • 4   代码分析
      • 4.1   stm32f4-discovery-usb-cdc-example分析
        • 4.1.1   main.c
        • 4.1.2   usbd_desc.h
        • 4.1.3   usbd_desc.c
        • 4.1.4   stm32f4xx_it.c
        • 4.1.5   usb_bsp.c
        • 4.1.6   usb_conf.h
        • 4.1.7   usbd_conf.h
        • 4.1.8   usbd_usr.c
        • 4.1.9   usbd_cdc.h
        • 4.1.10   usbd_cdc.c
        • 4.1.11   项目文件
      • 4.2   stsw_stm32081.zip中stm32_usb-fs-device_lib_v4.0.0的usb库
        • 4.2.1   usb_def.h
        • 4.2.2   usb_type.h
        • 4.2.3   usb_lib.h
        • 4.2.4   usb_core.h/.c
        • 4.2.5   usb_init.h/.c
        • 4.2.6   usb_int.h/.c
        • 4.2.7   usb_mem.h/.c
        • 4.2.8   usb_regs.h/.c
        • 4.2.9   usb_sil.h/.c
      • 4.3   stsw-stm32081.zip中stm32_usb-fs-device_lib_v4.0.0的virtualcomport_loopback
        • 4.3.1   main.c
        • 4.3.2   platform_config.h
        • 4.3.3   hw_config.h/.c
        • 4.3.4   stm32_it.h/.c
        • 4.3.5   usb_conf.h
        • 4.3.6   usb_desc.h/.c
        • 4.3.7   usb_istr.h/.c
        • 4.3.8   usb_prop.h/.c
        • 4.3.9   usb_pwr.h/.c
        • 4.3.10   usb_endp.c
    • 5   应用注记
      • 5.1   使用cdc类与上位机的通信
        • 5.1.1   尝试ctr中断的通信
      • 5.2   使用usb-fs-device的virtualcomport_loopback例子,作为与电脑的usb串口通信
      • 5.3   休眠处理

    1   简介

    一种还算凑合的通信方式。

    参考文献:

    1. [r] 基于stm32的usb程序开发笔记(序):
    2. [r] 基于stm32的usb程序开发笔记(一):
    3. [u] 基于stm32的usb程序开发笔记(二): 直接罗列代码
    4. [u] 基于stm32的usb程序开发笔记(三): 还是代码
    5. [u] 基于stm32的usb程序开发笔记(四): 设备枚举上
    6. [u] 基于stm32的usb程序开发笔记(五): 设备枚举下
    7. [u] 基于stm32的usb程序开发笔记(六): xp下usb驱动开发
    8. [u] 基于stm32的usb程序开发笔记(七): xp下usb驱动开发
    9. [i] 基于stm32的usb程序开发笔记.pdf: 2502410字节,就是如上一系列文章整理的
    10. [u] stm32的usb例程修改步骤:
    11. [u] 开始学习usb-从stm32的usb-demo开始:
    12. [u] stm32 usb学习:
    13. [r] 发一个stm32f4的usb虚拟串口程序: 一个1.13mb的源码下载,stm32f4 discovery
    14. [u] [coide]_usb_cdc_example.rar: 12.19mb没下载
    15. [u] 嵌入式系统的usb虚拟串口设计:
    16. [r] usb-serial on stm32f4: 很初级的介绍
    17. [u] stm32f4-discovery: help transfering data to pc: 一些源码例子的下载地址
    18. [u] ericherman/stm32f-discovery-example: 使用usb-cdc的例子
    19. [u] serialusb:
    20. [u] stm32f4 usb cdc connection:
    21. [u] stm32实现usb video class开始干活了:

    2   stm32的usb简介

    基本资料是stm32的参考手册、usb2.0规范、usb外设库。

    设备(device)只是被动触发的,主机(host)掌握主动权,包括发送什么数据,什么时候发送,读还是写。设备只是配合完成设备枚举、数据方向和大小,之类的。

    两个中断向量:

    /* 处理usb高优先级或can tx中断 */

    void usb_hp_can_tx_irqhandler(void) {

        usb_hpi();

    }

     

    /* 处理usb低优先级或can rx0中断 */

    void usb_lp_can_rx0_irqhandler(void) {

        usb_lpi();

    }

    usb_hpi()usb_lpi() 即转向 usb_core.h/.c 进行处理。中断传输、控制传输、批量传输(bulk)由 usb_lpi() 响应,批量传输也可以由 usb_hpi() 响应,同步传输只由 usb_hpi() 处理。

    这样只需要关注 usb_core.cusb_lpi()usb_hpi() 了。

    usb_lpi() 函数的定义: @page 7-10

    3   usb实现类

    3.1   usb-cdc

    cdc协议是通用的usb实现,在很多操作系统都不需要驱动就支持。所以有人实现了基于usb-cdc的串口,倒是个好思路。

    例子代码的下载:

    貌似我也应该使用bsp了,方便些。

    官方库的 stm32_usb-host-device_lib_v2.1.0/libraries/stm32_usb_device_library/class/cdc 提供了cdc的支持了。

    4   代码分析

    4.1   stm32f4-discovery-usb-cdc-example分析

    系统文件列表:

    1. startup_stm32f4xx.s:与我用的基本相同,除了注释
    2. stm32f4_discovery.c:有些不同,详见下面
    3. stm32f4_discovery.h:有些不同,详见下面
    4. stm32f4xx_conf.h:与我用的基本相同,除了注释
    5. stm32f4xx_it.c:对方加了好多东西,要看
    6. stm32f4xx_it.h:只有注释不同
    7. system_stm32f4xx.c:版本有所不同,但用我的更靠谱些

    对于stm32f4_discovery.c/.h文件,他们在该项目里,但不在 stm32f4_dsp_stdperiph_lib.zip 。存在于 stm32f4discovery_fw.zip 中。不过函数名有些差别。例如该项目的 stm32f4_discovery_ledinit() 对应 stm32f4discovery_fw.zip 中的 stm_eval_ledinit() 。以下的区别也是如此。

    usb支持文件:

    1. usb_bsp.c:是由usb支持库提供的,版本不同,差异很大
    2. usb_conf.h:与系统自带的差异很大,要看
    3. usbd_cdc.c:官方库没有找到对应内容,要看
    4. usbd_cdc.h:官方库没有找到对应内容,要看
    5. usbd_conf.h:与系统自带的差异很大,要看
    6. usbd_desc.c:配置文件,要看
    7. usbd_desc.h:配置文件,要看
    8. usbd_usr.c:与系统自带的差异很大,要看

    用户文件列表:

    1. main.c

    4.1.1   main.c

    需导入诸多头文件:

    1. stm32f4xx.h
    2. usbd_cdc_core.h
    3. usbd_cdc.h
    4. usbd_usr.h
    5. usbd_desc.h

    初始化一堆led,使用了bsp:

    stm32f4_discovery_ledinit(led3);

    stm32f4_discovery_ledinit(led4);

    stm32f4_discovery_ledinit(led5);

    stm32f4_discovery_ledinit(led6);

    stm32f4_discovery_pbinit(button_user,button_mode_exti);

    stm32f4_discovery_ledon(led3);

    delay(0xffff);

    usb的初始化,有可能的话,尽量使用otg_hs(480mbps):

    //外部声明

    __align_begin usb_otg_core_handle usb_otg_dev __align_end;

    //main()函数中

    usbd_init(&usb_otg_dev,

    #ifdef usb_usb_otg_hs

    usb_otg_hs_core_id,

    #else

    usb_otg_fs_core_id,

    #endif

    &usr_desc,

    &usbd_cdc_cb,

    &usr_cb);

    注释说,魔术发生在 usbd_cdc.c 文件,其他应该看的还有 usbd_desc.h

    最后就是每0x100000个周期让灯闪耀一次。

    这里调用的 usbd_init() 函数,定义于 libraries/stm32_usb_device_library/core/src/usbd_core.c

    4.1.2   usbd_desc.h

    实际上是几个常量定义,加一堆函数声明,实际配置内容并不在这里。常量:

    #define usb_device_descriptor_type              0x01

    #define usb_configuration_descriptor_type       0x02

    #define usb_string_descriptor_type              0x03

    #define usb_interface_descriptor_type           0x04

    #define usb_endpoint_descriptor_type            0x05

    #define usb_siz_device_desc                     18

    #define usb_siz_string_langid                   4

    4.1.3   usbd_desc.c

    317行。常量定义如下:

    #define usbd_vid                        0x304

    #define usbd_pid                        0xe457

    #define usbd_langid_string              0x40b

    #define usbd_manufacturer_string        "roope kokkoniemi"

    #define usbd_product_hs_string          "stm32f4-discovery-usb-cdc-example"

    #define usbd_serialnumber_hs_string     "00000000050b"

    #define usbd_product_fs_string          "stm32f4-discovery-usb-cdc-example"

    #define usbd_serialnumber_fs_string     "00000000050c"

    #define usbd_configuration_hs_string    "usb-cdc-example config"

    #define usbd_interface_hs_string        "usb-cdc-example interface"

    #define usbd_configuration_fs_string    "usb-cdc-example config"

    #define usbd_interface_fs_string        "usb-cdc-example interface"

    由此可见实际的vid、pid,以及定义的各种字符串。

    89行定义结构体变量 usr_desc

    usbd_device usr_desc= {

        usbd_usr_devicedescriptor,

        usbd_usr_langidstrdescriptor,

        usbd_usr_manufacturerstrdescriptor,

        usbd_usr_productstrdescriptor,

        usbd_usr_serialstrdescriptor,

        usbd_usr_configstrdescriptor,

        usbd_usr_interfacestrdescriptor,

    };

    具体函数定义都在下面呢。

    107行的结构体 usbd_devicedesc 定义了usb设备的详细信息。

    135行的结构体 usbd_devicequalifierdesc 似乎也是定义usb设备的,但是参数来源未知。10个成员。

    155行的结构体 usbd_langiddesc 是以字符串描述的设备信息。

    一系列不长,甚至仅仅用于返回字符串的函数:

    1. usbd_usr_devicedescriptor()
    2. usbd_usr_langidstrdescriptor()
    3. usbd_usr_productstrdescriptor()
    4. usbd_usr_manufacturerstrdescriptor()
    5. usbd_usr_serialstrdescriptor()
    6. usbd_usr_configstrdescriptor()
    7. usbd_usr_interfacestrdescriptor()

    4.1.4   stm32f4xx_it.c

    中断处理的,大部分还是空的,前头有些外部变量定义:

    extern usb_otg_core_handle           usb_otg_dev;

    extern uint32_t usbd_otg_isr_handler (usb_otg_core_handle *pdev);

    extern void discovery_exti_irqhandler(void);

     

    #ifdef usb_otg_hs_dedicated_ep1_enabled

    extern uint32_t usbd_otg_ep1in_isr_handler (usb_otg_core_handle *pdev);

    extern uint32_t usbd_otg_ep1out_isr_handler (usb_otg_core_handle *pdev);

    #endif

    otg_fs_wkup_irqhandler() 中断处理函数:

    #ifdef use_usb_otg_fs

    void otg_fs_wkup_irqhandler(void)

    {

      if(usb_otg_dev.cfg.low_power)

      {

        *(uint32_t *)(0xe000ed10) &= 0xfffffff9 ;

        systeminit();

        usb_otg_ungateclock(&usb_otg_dev);

      }

      exti_clearitpendingbit(exti_line18);

    }

    #endif

    otg_hs_wkup_irqhandler() 中断处理函数:

    #ifdef use_usb_otg_hs

    void otg_hs_wkup_irqhandler(void)

    {

      if(usb_otg_dev.cfg.low_power)

      {

        *(uint32_t *)(0xe000ed10) &= 0xfffffff9 ;

        systeminit();

        usb_otg_ungateclock(&usb_otg_dev);

      }

      exti_clearitpendingbit(exti_line20);

    }

    #endif

    然后是将一些中断处理函数映射出去:

    #ifdef use_usb_otg_hs

    void otg_hs_irqhandler(void)

    #else

    void otg_fs_irqhandler(void)

    #endif

    {

      usbd_otg_isr_handler (&usb_otg_dev);

    }

     

    #ifdef usb_otg_hs_dedicated_ep1_enabled

    void otg_hs_ep1_in_irqhandler(void)

    {

      usbd_otg_ep1in_isr_handler (&usb_otg_dev);

    }

    void otg_hs_ep1_out_irqhandler(void)

    {

      usbd_otg_ep1out_isr_handler (&usb_otg_dev);

    }

    #endif

    按钮的事件处理:

    void exti0_irqhandler(void) {

        discovery_exti_irqhandler();

        /* clear the exti line pending bit */

        exti_clearitpendingbit(user_button_exti_line);

    }

    可见基本上就是做一下初始化,然后把实际的中断处理都交给外面去做了。

    函数 usbd_otg_isr_handler()usbd_otg_ep1in_isr_handler()usbd_otg_ep1out_isr_handler() 定义于 usb_dcd_int.c 文件。

    4.1.5   usb_bsp.c

    382行。主要就两个函数:

    1. usb_otg_bsp_init() :从90行开始,根据几种开发板,和hs/fs设置相关引脚的功能
    2. usb_otg_bsp_enableinterrupt() :从309行开始,配置各种中断

    use_usb_otg_fs 用于定义stm32f4探索套件以otg_fs运行。下面只看对stm32f4 discovery的初始化。

    usb_otg_bsp_init() 的内容:

    1. 将pa8、pa9、pa11、pa12定义为100mhz,无上拉下拉,gpio_af_otg_fs
    2. 将pa10定义为100mhz、上拉,gpio_otype_od、gpio_af_otg_fs
    3. 启用外设时钟:
      rcc_apb2periphclockcmd(rcc_apb2periph_syscfg,enable);
    4. rcc_ahb2periphclockcmd(rcc_ahb2periph_otg_fs,enable);
    5. usb_otg_hs的初始化从132行开始到243行,用到的线更多,先不看了
    6. 250行,启用pwr时钟 rcc_apb1periphresetcmd(rcc_apb1periph_pwr,enable);
    7. 最后用了很多行分别配置fs和hs模式的user_button的功能,貌似用来唤醒usb的

    usb_otg_bsp_enableinterrupt() 的内容,就是配置各种nvic,优先级什么的,一共就29行。

    4.1.6   usb_conf.h

    271行。定义一些宏,以及各种usb ram fifo的大小。

    定义的宏,先假设我们定义了宏 use_usb_otg_fs

    1. usb_otg_fs_core
    2. rx_fifo_fs_size=128
    3. tx0_fifo_fs_size=32
    4. tx1_fifo_fs_size=128
    5. tx2_fifo_fs_size=32
    6. tx3_fifo_fs_size=0
    7. usb_device_mode
    8. __align_begin :对应的是空,但不为空时对应特定编译器的 __align(4) 用于对齐
    9. __align_end :对应的是空
    10. __packed __attribute__ ((__packed__)) :对gcc的宏

    4.1.7   usbd_conf.h

    98行。用于usb-cdc的一些设置。常量定义如下,也是假设定义了 use_usb_otg_fs

    1. usbd_cfg_max_num=1
    2. usbd_itf_max_num=1
    3. usb_max_str_desc_siz=50
    4. cdc_in_ep=0x81 :ep1用于data in
    5. cdc_out_ep=0x01 :ep1用于data out
    6. cdc_cmd_ep=0x82 :ep2用于cdc命令
    7. cdc_data_max_packet_size=64 :输入输出包最大大小
    8. cdc_cmd_packet_sze=8 :控制端点包大小
    9. cdc_in_frame_interval=5 :两个in传输间最大帧数量
    10. app_rx_data_size=2048 :in缓冲的总大小
    11. app_fops=cdc_fops

    4.1.8   usbd_usr.c

    189行。先导入几个头文件:

    1. usbd_usr.h
    2. usbd_ioreq.h
    3. stm32f4_discovery.h

    定义结构体变量:

    usbd_usr_cb_typedef usr_cb= {

        usbd_usr_init,

        usbd_usr_devicereset,

        usbd_usr_deviceconfigured,

        usbd_usr_devicesuspended,

        usbd_usr_deviceresumed,

    };

    这里引用的5个函数都在下面定义的,但是实际都是操作led5(红色)的:

    1. usbd_usr_init() :初始化led5
    2. usbd_usr_devicereset() :无内容,会传入 uint8_t speed 可选输出日志
    3. usbd_usr_deviceconfigured() :点亮led5
    4. usbd_usr_devicesuspended() :熄灭led5
    5. usbd_usr_deviceresume() :点亮led5

    4.1.9   usbd_cdc.h

    41行。就定义了两个宏:

    1. default_config=0
    2. other_config=1

    4.1.10   usbd_cdc.c

    218行。具体的usb-cdc实现。

    37-40行定义了收发缓冲区:

    extern uint8_t app_rx_buffer[];

    extern uint32_t app_rx_ptr_in;

    53-60行定义结构体变量:

    cdc_if_prop_typedef cdc_fops= {

        cdc_init,

        cdc_deinit,

        cdc_ctrl,

        cdc_datatx,

        cdc_datarx,

    };

    这里定义的5个函数中 cdc_init()cdc_deinit() 很简单,就是直接返回 usbd_ok 即可。

    cdc_ctrl() 是根据输入命令cmd来用switch做处理的,但是虽然列出了所有命令,但是没有做任何处理,最后直接返回了 usbd_ok

    cdc_datatx() 用于通过in端点发送数据,实际内部就是把参数的缓冲区内容复制到 app_rx_buffer 中。最后返回 usbd_ok

    cdc_datarx() 用于通过out端点接收数据,本例实际就是将接到的数据回发回去而已。该函数会阻塞其他out包接收,直到退出该函数。如果在cdc接口完成前退出,会收到更多数据,而之前的却不会发出。

    cdc_datarx() 将参数指定的缓冲区写入内容,当遇到a/a时点亮led6,当遇到s/s时熄灭led6,再通过 cdc_datatx() 函数将内容发出,最后返回 usbd_ok

    discovery_exti_irqhandler() 中断处理函数,用于指定缓冲区内容为 "terve" ,然后发送出去。

    4.1.11   项目文件

    文件名叫 stm32f4-discovery-usb-cdc-example.elf.launch

    其中多处提到 atollic.hardwaredebug ,不知道是什么ide的。而且也没有提到哪些文件应该一起编译进去,看来又要我自己想办法了。

    4.2   stsw_stm32081.zip中stm32_usb-fs-device_lib_v4.0.0的usb库

    4.2.1   usb_def.h

    一些枚举:

    typedef enum _recipient_type {

        device_recipient,

        interface_recipient,

        endpoint_recipient,

        other_recipient,

    } recipient_type;

     

    typedef enum _standard_requests {

        get_status=0,

        clear_feature,

        reserved1,

        set_feature,

        reserved2,

        set_address,

        get_descriptor,

        set_descriptor,

        get_confiruration,

        set_confiruration,

        get_interface,

        set_interface,

        total_srequest,

        synch_frame=12

    } stand_requests; //by gashero

     

    typedef enum _descriptor_type {

        device_descriptor=1,

        config_descriptor,

        string_descriptor,

        interface_descriptor,

        endpoint_descriptor,

    } descriptor_type;

     

    typedef enum _feature_selector {

        endpoint_stall,

        device_remote_wakeup,

    } feature_selector;

    一些常量定义:

    #define request_type    0x60

    #define standard_request    0x00

    #define class_request   0x20

    #define vendor_request  0x40

    #define recipient       0x1f

    4.2.2   usb_type.h

    内容特别短,如下:

    #include "usb_conf.h"

    #ifndef null

    #define null ((void*)0)

    #endif

     

    typedef enum {

        false=0, true=!false

    } bool;

    4.2.3   usb_lib.h

    只是导入了一堆其他头文件:

    #include "hw_config.h"

    #include "usb_type.h"

    #include "usb_regs.h"

    #include "usb_def.h"

    #include "usb_core.h"

    #include "usb_init.h"

    #include "usb_sil.h"

    #include "usb_mem.h"

    #include "usb_int.h"

    4.2.4   usb_core.h/.c

    usb_core.h

    定义了一些数据结构:

    1. _control_state 枚举
    2. onedescriptor 结构体
    3. _result 枚举
    4. _endpoint_info 结构体
    5. _device 结构体
    6. _device_info 结构体
    7. _device_prop 结构体
    8. _user_standard_request 结构体

    一堆导出函数就不写了,在 usb_core.c 里写。

    一些从外部导入的变量:

    1. device_prop device_property
    2. user_standard_requests user_standard_requests
    3. device device_table
    4. device_info device_info
    5. uint16_t saverstate
    6. uint16_t savetstate

    usb_core.c

    @wait

    4.2.5   usb_init.h/.c

    @wait

    4.2.6   usb_int.h/.c

    @wait

    4.2.7   usb_mem.h/.c

    @wait

    4.2.8   usb_regs.h/.c

    都很长。

    usb_regs.h

    定义的数据结构:

    1. _ep_dbuf_dir 枚举
    2. ep_buf_num 枚举

    一些地址:

    1. regbase:usb外设基址
    2. pmaaddr:packet memory area基址
    3. cntr:寄存器
    4. istr:寄存器
    5. fnr:寄存器
    6. daddr:寄存器
    7. btable:寄存器
    8. ep_out:端点寄存器
    9. ep_in:端点寄存器
    10. endp:端点枚举值

    一些成段的声明:

    1. istr中断事件:105-125行
    2. cntr寄存器位:130-144行
    3. fnr寄存器位:149-153行
    4. daddr寄存器位:157-158行
    5. 端点寄存器:163-205行

    @wait 看到206行的导出宏,太大了

    4.2.9   usb_sil.h/.c

    @wait

    4.3   stsw-stm32081.zip中stm32_usb-fs-device_lib_v4.0.0的virtualcomport_loopback

    分析的是模板,而不是我改出来的。(by gashero)

    要改进通信速度,应该从两个方面,一个是usb_endp.c中每秒发送次数,一个是使用ctr中断。

    4.3.1   main.c

    导入头文件:

    #include "hw_config.h"

    #include "usb_lib.h"

    #include "usb_desc.h"

    #include "usb_pwr.h"

    一些全局需要使用的变量,从外部导入:

    extern __io uint8_t receive_buffer[64];

    extern __io uint32_t receive_length;

    extern __io uint32_t length;

    uint8_t send_buffer[64];

    uint32_t packet_sent=1;

    uint32_t packet_receive=1;

    主函数,初始化usb相关的东西,以及按照收到的数据来转发:

    int main() {

        set_system();

        set_usbclock();

        usb_interrupts_config();

        usb_init();

     

        while(1) {

            if (bdevicestate==configured) {

                cdc_receive_data();

                if(receive_length!=0) {

                    if(packet_sent==1) {

                        cdc_send_data((unsigned char*)receive_buffer,receive_length);

                    }

                    receive_length=0;

                }

            }

        }

    }

    标准的断言处理:

    #ifdef use_full_assert

    void assert_failed(uint8_t *file, uint32_t line) {

        while(1);

    }

    #endif

    4.3.2   platform_config.h

    看来是平台相关配置。

    note

    移植过程大量修改这里。

    32行到72行,必须声明个开发板什么的,其实无所谓,都删除掉就是了。然后导入 "stm32f10x.h" 。

    76行到94行,是声明3个id,不知干啥用的,反正没改也过去了。

    97行到148行,是声明d 上拉电阻控制引脚的。该引脚低电平有效,开启d 的上拉电阻。我是将其全部删掉,然后自己重新定义的:

    #define usb_disconnect      gpiob

    #define usb_disconnect_pin  gpio_pin_1

    #define rcc_apb2periph_gpio_disconnect rcc_apb2periph_gpiob

    4.3.3   hw_config.h/.c

    hw_config.h

    导入头文件:

    #include "platform_config.h"

    #include "usb_type.h"

    几个导出常量,不太清楚:

    #define mass_memory_start       0x04002000

    #define bulk_max_packet_size    0x00000040

    #define led_on                  0xf0

    #define led_off                 0xff

    然后就是声明10个函数,不写了。

    hw_config.c

    导入头文件:

    #include "stm32_it.h"

    #include "usb_lib.h"

    #include "usb_prop.h"

    #include "usb_desc.h"

    #include "hw_config.h"

    #include "usb_pwr.h"

    一堆似有变量声明:

    errorstatus hsestartupstatus;

    exti_inittypedef exti_initstructure;

    extern __io uint32_t packet_sent;

    extern __io uint8_t send_buffer[virtual_com_port_data_size];

    extern __io uint32_t packet_receive;

    extern __io uint8_t receive_length;

    uint8_t receive_buffer[64];

    uint32_t send_length;

    static void inttounicode(uint32_t value, uint8_t *pbuf, uint8_t len);

    extern line_coding linecoding;

    各个函数的定义:

    1. set_system() :63-141行,配置usb使用的gpio,以及中断线
    2. set_usbclock() :149-162行,开启usb时钟
    3. enter_lowpowermode() :170-174行,进入低功耗模式
    4. leave_lowpowermode() :182-198行,离开低功耗模式,需要重新初始化系统
    5. usb_interrupts_config() :206-254行,中断配置,包括低优先级,高优先级、唤醒
    6. usb_cable_config(newstate) :262-284行,控制d 上拉是否开启
    7. get_serialnum() :293-308行,创建一个序列号字符串描述符
    8. inttounicode(value,*pbuf,len) :317-336行,看来是数字到unicode的转换,有必要么
    9. cdc_send_data(*ptrbuffer,send_length) :345-362行,发送数据,直接调用usb库的3个函数,成功返回1,失败返回0
    10. cdc_receive_data() :371-377行,获取数据,返回1

    4.3.4   stm32_it.h/.c

    stm32_it.h 就是声明了一堆的中断处理函数,共11个。

    stm32_it.c 定义了11个中断处理函数。

    导入头文件:

    #include "hw_config.h"

    #include "stm32_it.h"

    #include "usb_lib.h"

    #include "usb_istr.h"

    中断处理函数中,9个标准的:

    1. nmi_handler() :空的
    2. hardfault_handler() :死循环
    3. memmanage_handler() :死循环
    4. busfault_handler() :死循环
    5. usagefault_handler() :死循环
    6. svc_handler() :空的
    7. debugmon_handler() :空的
    8. pendsv_handler() :空的
    9. systick_handler() :空的

    然后就是两个usb相关的,以hd设备为例:

    usb_lp_irqhandler() :内部直接调用 usb_istr()

    usb_fs_wkup_irqhandler() :内部直接调用 exti_clearitpendingbit(exti_line18)

    4.3.5   usb_conf.h

    一些声明。

    1. ep_num (4) :使用的端点数量
    2. btable_address (0x00) :缓冲表基址
    3. endp_rxaddr (0x40) :端点n的接收缓冲基址
    4. endp_txaddr (0x80) :端点n的发送缓冲基址
    5. imr_msk :事件屏蔽位
    6. ep_in_callback :输入端点回调,全是空的
    7. ep_out_callback :输出端点回调,全是空的

    实际的缓冲表部分:

    #define btable_address  (0x00)

    #define endp0_rxaddr    (0x40)

    #define endp0_txaddr    (0x80)

    #define endp1_txaddr    (0xc0)

    #define endp2_txaddr    (0x100)

    #define endp3_rxaddr    (0x110)

    imr_msk的声明:

    #define imr_msk (cntr_ctrm | cntr_wkupm | cntr_suspm | cntr_errm | \

        cntr_sofm | cntr_esofm | cntr_resetm)

    4.3.6   usb_desc.h/.c

    决定了设备显示的名字,和其他字符串描述。收发缓冲区大小也是在这里。

    usb_desc.h 一些常量定义和函数声明:

    #define usb_device_descriptor_type          0x01

    #define usb_configuration_descriptor_type   0x02

    #define usb_string_descriptor_type          0x03

    #define usb_interface_descriptor_type       0x04

    #define usb_endpoint_descriptor_type        0x05

     

    #define virtual_com_port_data_size          64

    #define virtual_com_port_int_size           8

     

    #define virtual_com_port_siz_device_desc    18

    #define virtual_com_port_siz_config_desc    67

    #define virtual_com_port_siz_string_langid  4

    #define virtual_com_port_siz_string_vendor  38

    #define virtual_com_port_siz_string_product 50

    #define virtual_com_port_siz_string_serial  26

     

    #define standard_endpoint_desc_size         0x09

    usb_desc.c 一些结构体的定义。

    导入头文件:

    #include "usb_lib.h"

    #include "usb_desc.h"

    virtual_com_port_devicedescriptor 数组,元素是uint8_t类型,18个元素。

    virtual_com_port_configdescriptor 数组,元素是uint8_t类型,65个元素。

    virtual_com_port_stringlangid 数组,元素是uint8_t类型,4个元素。

    virtual_com_port_stringvendor 数组,元素是uint8_t类型,具体长度看virtual_com_port_siz_string_vendor。

    virtual_com_port_stringproduct 数组,元素是uint8_t类型,具体长度看virtual_com_port_siz_string_product。

    virtual_com_port_stringserial 数组,元素是uint8_t类型,具体长度看virtual_com_port_siz_string_serial。

    4.3.7   usb_istr.h/.c

    分发回调函数的声明和定义。

    usb_istr.h 一些声明。

    导入头文件:

    #include "usb_conf.h"

    导出函数 usb_istr() 。直接声明的回调函数 ep_in_callbackep_in_callback 其中n取1~7。

    一些需要宏定义才声明的函数,对应宏的名字就是函数名的全大写:

    1. ctr_callback()
    2. dovr_callback()
    3. err_callback()
    4. wkup_callback()
    5. susp_callback()
    6. reset_callback()
    7. sof_callback()
    8. esof_callback()

    usb_istr.c 各类回调函数的定义。

    导入头文件:

    #include "usb_lib.h"

    #include "usb_prop.h"

    #include "usb_pwr.h"

    #include "usb_istr.h"

    似有变量声明:

    __io uint16_t wistr;

    __io uint8_t bintpacksof=0; //by gashero

    __io uint32_t esof_counter=0;

    __io uint32_t wcntr=0;

    非控制端点的函数指针 pepint_inpepint_out

    usb_istr() 从77-229行。包括按照各种标识调用各个其他回调函数,相当于一个分派器。从现在看还是针对usb的,而不是虚拟串口的。

    4.3.8   usb_prop.h/.c

    usb_prop.h 常量定义和14个函数声明。

    一个结构体的定义:

    typedef struct {

        uint32_t bitrate;

        uint8_t format;

        uint8_t paritytype;

        uint8_t datatype;

    } line_coding;

    常量定义:

    #define virtual_com_port_getconfiguration          nop_process

    //#define virtual_com_port_setconfiguration          nop_process

    #define virtual_com_port_getinterface              nop_process

    #define virtual_com_port_setinterface              nop_process

    #define virtual_com_port_getstatus                 nop_process

    #define virtual_com_port_clearfeature              nop_process

    #define virtual_com_port_setendpointfeature        nop_process

    #define virtual_com_port_setdevicefeature          nop_process

    //#define virtual_com_port_setdeviceaddress          nop_process

     

    #define send_encapsulated_command   0x00

    #define get_encapsulated_response   0x01

    #define set_comm_feature            0x02

    #define get_comm_feature            0x03

    #define clear_comm_feature          0x04

    #define set_line_coding             0x20

    #define get_line_coding             0x21

    #define set_control_line_state      0x22

    #define send_break                  0x23

    usb_prop.c

    导入头文件:

    #include "usb_lib.h"

    #include "usb_conf.h"

    #include "usb_prop.h"

    #include "usb_desc.h"

    #include "usb_pwr.h"

    #include "hw_config.h"

    定义变量:

    uint8_t request=0;

    一些结构体的实例化:

    1. line_coding linecoding :波特率115200,无停止位,无校验位,8位数据
    2. device device_table :就2个字段,ep_num和1
    3. device_prop device_property :12个字段的结构体定义,传入串口的一堆操作函数
    4. user_standard_requests user_standard_requests :9个字段,也是一堆函数引用
    5. one_descriptor device_descriptor :2个字段,描述符
    6. one_descriptor config_descriptor :2个字段,描述符
    7. one_descriptor string_descriptor[4] :4个元素的数组,每个2个字段,字符串描述符

    虚拟串口相关的操作函数:

    1. virtual_com_port_init() :121-137行,串口初始化
    2. virtual_com_port_reset() :146-191行,串口复位
    3. virtual_com_port_setconfiguration() :200-209行,设置配置
    4. virtual_com_port_setdeviceaddress() :218-221行,设置设备地址
    5. virtual_com_port_status_in() :230-236行,in状态
    6. virtual_com_port_status_out() :245-256行,out状态,无内容
    7. virtual_com_port_data_setup() :255-286行,数据设置
    8. virtual_com_port_nodata_setup() :295-311行,无数据设置
    9. virtual_com_port_getdevicedescriptor() :320-323行,获取设备描述符
    10. virtual_com_port_getconfigdescriptor() :332-335行,获取配置描述符
    11. virtual_com_port_getstringdescriptor() :344-355行,获取字符串描述符
    12. virtual_com_port_get_interface_setting() :366-377行,获取接口设置
    13. virtual_com_port_getlinecoding() :386-394行,获取行编码
    14. virtual_com_port_setlinecoding() :403-411行,设置行编码

    4.3.9   usb_pwr.h/.c

    usb_pwr.h

    两个枚举定义,恢复状态和设备状态:

    typedef enum _resume_state {

        resume_external,

        resume_internal,

        resume_later,

        resume_wait,

        resume_start,

        resume_on,

        resume_off,

        resume_esof,

    } resume_state;

     

    typedef enum _device_state {

        unconnected,

        attached,

        powered,

        suspended,

        addressed,

        configured,

    } device_state;

    然后是5个函数的声明。

    两个变量的声明:

    extern __io uint32_t bdevicestate;

    extern __io bool fsuspendenabled;

    usb_pwr.c

    导入头文件:

    #include "usb_lib.h"

    #include "usb_conf.h"

    #include "usb_pwr.h"

    #include "hw_config.h"

    变量和结构体定义:

    __io uint32_t bdevicestate = unconnected;

    __io bool fsuspendenabled=true;

    __io uint32_t ep[8];

     

    struct {

        __io resume_state estate;

        __io uint8_t besofcnt;

    } resumes;

     

    __io uint32_t remotewakeupon=0;

    函数的定义:

    1. poweron() :64-85行,一些基本配置,返回usb_success
    2. poweroff() :94-108行,基本配置,返回usb_success
    3. suspend() :117-213行,挂起支持
    4. resume_init() :222-245,恢复的初始化
    5. resume() :259-316行,恢复

    4.3.10   usb_endp.c

    导入头文件:

    #include "usb_lib.h"

    #include "usb_desc.h"

    #include "usb_mem.h"

    #include "hw_config.h"

    #include "usb_istr.h"

    #include "usb_pwr.h"

    一个常量定义,发送in数据包的间隔1帧=1ms:

    #define vcomport_in_frame_interval 5

    一些变量定义:

    extern __io uint32_t packet_sent;

    extern __io uint32_t packet_receive;

    extern __io uint8_t receive_buffer[64];

    uint32_t receive_length;

    然后是一堆看起来是回调函数的。不过刚才在 usb_conf.h 中说了都是空的。具体不清楚,先把定义都写了吧:

    void ep1_in_callback() {

        packet_sent=1;

    }

     

    void ep3_out_callback() {

        packet_receive=1;

        receive_length=geteprxcount(endp3);

        pmatouserbuffercopy((unsigned char*)receive_buffer, endp3_rxaddr, receive_length);

    }

    5   应用注记

    5.1   使用cdc类与上位机的通信

    已经使用usb库实现了,但是现在通信经常丢包,所以研究接下来的办法。

    5.1.1   尝试ctr中断的通信

    分析发现ctr关键字在 stm32_usb-fs-device_lib_v4.0.0 库中,出现在如下文件中:

    1. inc/usb_int.h:3处
    2. inc/usb_regs.h:20处
    3. src/usb_int.c:27处
    4. src/usb_regs.c:8处

    且没有看到具体的中断处理有关语句。

    usb_regs.h 中声明的两个函数 clearep_ctr_rx(bepnum)clearep_ctr_tx(bepnum) 看来是用来清除收发两个方向的ctr标识的。

    src/usb_int.c 中有关ctr的调用全部在两个函数中,即 ctr_hp()ctr_lp() 。貌似是中断处理函数。

    在应用代码中,ctr在 usb_conf.h 中出现两处,用于定义要启用ctr_callback。应该从这里启用ctr回调。另外在 usb_istr.h 中出现两处,没有意义。在 usb_istr.c 中出现六处,启用回调。

    所以要启用ctr回调,分为几个步骤:

    1. usb_conf.h 的75行,启用宏 ctr_callback
    2. 不要修改 usb_istr.* 文件
    3. 在应用中定义 ctr_callback() 函数,内容自己玩

    确定了每个端点都有自己的ctr_tx和ctr_rx位,我需要的是向上位机发送数据,所以要寻找特定端点的ctr_tx位。

    usb_regs.h 中定义了端点寄存器的一些值 ep_ctr_rxep_ctr_tx 。也许就是 geteptxstatus() 函数。实际的实现是在 usb_regs.h:337 的一个宏。当该函数返回0x30时,就是可以发送数据了。

    usb外设的基址 0x4000,5c00 。

    分析下usb cdc应用中的4个端点:

    1. ep0:usb_ep0r=0x5210,control端点
    2. ep1:usb_ep1r=0x0031,bulk端点
    3. ep2:usb_ep2r=0x0622,interrupt端点
    4. ep3:usb_ep3r=0x3003,bulk端点

    这里几乎看不出东西。那就分析 cdc_send_data() 函数。直接在 hw_config.c 中发现了,是通过ep1发送的。而接收则是ep3。没找到ep2干啥的。

    标准做法是收到主机ack后,通过usb_istr寄存器的ep_id和dir位识别是哪里产生的事件,然后清除ctr_tx位,然后准备好发送缓冲区。dir=0时是只有ctr_tx被置位,dir=1时则ctr_rx被置位,而ctr_tx可能被置位。所以对于只关心ctr_tx的我,可以不看dir。实际上usb_istr中并没有看到任何值,都是0。

    5.2   使用usb-fs-device的virtualcomport_loopback例子,作为与电脑的usb串口通信

    先从 stm32_usb-fs-device_lib_v4.0.0/projects/virtualcomport_loopback 目录打开,其内重要的内容包括inc目录里的头文件和src目录里的c文件。把如下文件拷贝到应用的目录里:

    1. hw_config.h/.c
    2. stm32_it.h/.c
    3. usb_conf.h
    4. usb_desc.h/.c
    5. usb_endp.c
    6. usb_istr.h/.c
    7. usb_prop.h/.c
    8. usb_pwr.h/.c

    然后都要编译到程序里。

    原装的 platform_config.h 太麻烦了,自己写一个简单的:

    #ifndef __platform_config_h

    #define __platform_config_h

     

    #ifdef blueridge13

     

    #include

     

    #define usb_disconnect          gpiob

    #define usb_disconnect_pin      gpio_pin_1

    #define rcc_apb2periph_gpio_disconnect rcc_apb2periph_gpiob

    #define         id1          (0x1ffff7e8)

    #define         id2          (0x1ffff7ec)

    #define         id3          (0x1ffff7f0)

     

    #endif

     

    #endif

    所以这里的关键内容就是定义芯片的头文件,usb断开的引脚(pb1),所用外设时钟,以及3各id,不知干啥的。

    另外在自己程序的主文件里需要声明几个全局变量以及头文件,方便后续使用:

    #include "hw_config.h"

    #include "usb_lib.h"

    #include "usb_desc.h"

    #include "usb_pwr.h"

    #include "usb_regs.h"

     

    extern __io uint8_t receive_buffer[64];

    extern __io uint32_t receive_length;

    extern __io uint32_t length;

     

    __io uint32_t packet_sent=1;

    __io uint32_t packet_receive=1;

    拥有如上信息就能编译成功了。

    stm32_it.c 中有一些没必要的中断声明,反倒耽误我做事了,可以直接过去注释掉,比如 systick_handler()

    main() 函数里需要加几行初始化内容,然后才能实际的发送内容:

    set_system();       //必须有

    set_usbclock();

    usb_interrupts_config();

    usb_init();

    发送数据到pc的例子,基于systick,初始化为 systick_config(9000000);    //8hz 。然后实际的代码:

    void systick_handler() {

        stm_eval_ledtoggle(led2);

        if ((geteptxstatus(1) & ep_tx_nak)=ep_tx_nak) {

            cdc_send_data((uint8_t*)"hello\r\n",7);

        }

    }

    然后用minicom就可以看到发来的数据了。minicom在数据的发送上是每次一个字符的,务必小心。单片机接收到的也是每次一个字符。而不是在回车时一个完整的。而python的serial库等,就能一次发送个完整的字符串。

    要在单片机上接收上位机发来的信息,使用:

    extern __io uint8_t receive_buffer[64];

    extern __io uint32_t receive_length;                //没错,后面的length是全小写的

     

    while(1) {

        cdc_receive_data();     //接收信息并更新全局变量

        if (receive_length!=0) {    //收到了信息

            cdc_send_data((uint8_t*)receive_buffer,receive_length);

            receive_length=0;   //必须写

        }

    }

    5.3   休眠处理

    stm32提供的usb库会在特定情况下让芯片进入挂起状态来省电。但一旦进入挂起模式,hse会停止,导致jtag/swd调试也停止了,就没法继续调试了。而这个功能,对于大多数时候并没有什么意义。

    挂起模式的实现在 usb_pwr.csuspend() ,被 usb_istr.c 所调用。逻辑是当变量 fsuspendenabled=true 时就调用。

    最简单方便的解决方法是在主程序的启动文件里声明一下该变量:

    extern __io bool fsuspendenabled;

    然后在主程序里将其设置为不进入挂起:

    fsuspendenabled=false;

    然后就不会进入该死的挂起了。

     

    这种挂起状态往往是因为usb设置出了问题,usb初始化失败,从而进入了挂起。而正常启动usb设备时不会出现该问题。

    0
    2
    分享到:
    评论

    相关推荐

      动开发以及应用程序做了一些介绍,为更好理解,请仔细学习 stm32 usb 的参考手册以及 usb 协议,如果对 vc6 下开发还不是太熟悉 或者说不曾学过,那么如果想理解有些问题,就必须学习 vc6 了。本套笔记是基于我编写...

      详细介绍stm32开发usb的应用的全过程,内容详细明了

      基于stm32的usb程序开发笔记 cd00158241 stm32 usb mass storage学习资料 stm32的usb详解 stsw-stm32121 五、stm32勘误资料 stm32f10xx8b_errata_ch_v6 stm32f10xx46_errata_ch_v2_20190725_171256 stm32f10xxcde_...

      以前一直就有打玩usb的想法,最近时间充足于是决心打玩stm32的usb,购买的是万利的stm3210b-lk1板,琢磨usb已有半个多月,在固 件、上位机驱动以及应用程序的访问这三方面终于有所突破,这期间通过网络上授寻了许多...

      基于stm32的传统usb 2.0接口到type-c的转换方案,en.... st官方有个应用笔记【an4775】介绍了如何用usb type-c替代传统usb 2.0连接器的方案建议。同时,笔记里也简单介绍了有关usb type-c及usb电源传输的一些基础知识。

      围绕usb type-c接口的话题已经很了,很多公司也推出... st有个应用笔记【an4775】介绍了如何用usb type-c替代传统usb 2.0连接器的方案建议。同时,笔记里也简单介绍了有关usb type-c及usb电源传输的一些基础知识。  

      本文章是stm32f103驱动marvell8801/marvell88w8801的应用手册,包含以下篇幅 1)整体介绍篇,主要对开发板做整体介绍 2)硬件篇,主要对板子外设驱动做说明 --》主要对ssd1306 oled介绍,spi flash介绍,fatfs介绍...

      stm32f1应用笔记,程序设计参考资料,中文译稿。介绍了stm32f10xx系列的外设应用实例,包括有dma,adc,usart,gpio,iic,tim1,fsmc,usb,等外设使用说明

      这篇应用笔记的目的就是为创建一个iap应用程序提供一些通用的指导原则。本文中使用stm3210b-eval/stm3210e-eval板验证iap驱动。 stm32f10xxx微控制器可以运行用户指定的固件,在微控制器嵌入的闪存上实现iap。这个...

      本应用笔记的目的是提供在at32微控制器上创建iap应用程序的一般准则。 at32微控制器可以运行用户特定的固件来对微控制器中嵌入的闪存执行iap。此功能可以使用产品可 用和支持的任何通信接口。使用自定义协议协议的...

      stm32的概述与开发环境的搭建一....stm32f1系列是来自arm公司具有突破性的以arm cortex-m3为内核的32为微处理器,内核为arm公司为要求高性能,低功耗,低成本,性价比高的嵌入式应用专门设计的cortex

      这篇应用笔记的目的就是为创建一个iap应用程序提供一些通用的指导原则。本文中使用stm3210b-eval/stm3210e-eval板验证iap驱动。 stm32f10xxx微控制器可以运行用户指定的固件,在微控制器嵌入的闪存上实现iap。这个...

      iap( in application programming)即在线应用编程,也就是用户可以使用自己的程序对单片机的user flash的某一区域(一般为存放自己程序的区域)进行烧写。在真正的工作中产品发布后,可以很方便的使用预留的通信...

      的stm32cube包装 这是一个简单的示例项目存根,用作将库集成到stm32cubemx项目中的起点。 由于所有stm32设备都具有相同的usb或usb_otg外设,因此该凯发k8国际娱乐官网入口的解决方案在整个stm32范围内都是兼容的(但事实...应用笔记外部dp上拉

      围绕usb type-c接口的话题已经很火爆了,很多公司也... st官方有个应用笔记【an4775】介绍了如何用usb type-c替代传统usb 2.0连接器的方案建议。同时,笔记里也简单介绍了有关usb type-c及usb电源传输的一些基础知识。

      自举程序存储在 stm32 器件的内部自举 rom 存储器 (系统存储器)中。在芯片生产期间由 st 编程。其主要任务是通过一种可用的串行外(usart、can、usb

      该存储库使用硬件触发器和dma实现用于adc的stm32驱动程序。 当您调用adc_read_async时,adc将开始在循环缓冲区模式下使用dma将样本读取到缓冲区。 每次adc转换将在发生硬件触发事件后开始。 在当前实现中,我们还...

      stm32与esp8266通讯采用串口通讯,应用层协议使用at指令集,stm32做at客户端(at client),esp8266做at 服务器(at server),注意区别上面的网络层的客户端与服务器。使用esp8266,连接wifi,获取自身本地ip,进入...

      kvmkit,一个uart-> hid键盘/鼠标适配器 项目: 便携式笔记本电脑usb-kvm凯发k8国际娱乐官网入口的解决方案的一半,与廉价的hdmi-usb采集卡搭配使用。... stm32f7核板开发环境usb-uart适配器位于uart4上,尚不支持hid鼠标 hdmi-usb: o

      硬件框架主要使用了art-pi开发板上的stm32h750处理器的spi,timer, uart, 板载资源使用了led,其它硬件模块包括st的六轴传感器以及自备的usb转串口板。 软件框架说明软件模块说明演示效果视频演示: 比赛感悟通过参与...

    global site tag (gtag.js) - google analytics
    网站地图