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 简介
一种还算凑合的通信方式。
参考文献:
- [r] 基于stm32的usb程序开发笔记(序):
- [r] 基于stm32的usb程序开发笔记(一):
- [u] 基于stm32的usb程序开发笔记(二): 直接罗列代码
- [u] 基于stm32的usb程序开发笔记(三): 还是代码
- [u] 基于stm32的usb程序开发笔记(四): 设备枚举上
- [u] 基于stm32的usb程序开发笔记(五): 设备枚举下
- [u] 基于stm32的usb程序开发笔记(六): xp下usb驱动开发
- [u] 基于stm32的usb程序开发笔记(七): xp下usb驱动开发
- [i] 基于stm32的usb程序开发笔记.pdf: 2502410字节,就是如上一系列文章整理的
- [u] stm32的usb例程修改步骤:
- [u] 开始学习usb-从stm32的usb-demo开始:
- [u] stm32 usb学习:
- [r] 发一个stm32f4的usb虚拟串口程序: 一个1.13mb的源码下载,stm32f4 discovery
- [u] [coide]_usb_cdc_example.rar: 12.19mb没下载
- [u] 嵌入式系统的usb虚拟串口设计:
- [r] usb-serial on stm32f4: 很初级的介绍
- [u] stm32f4-discovery: help transfering data to pc: 一些源码例子的下载地址
- [u] ericherman/stm32f-discovery-example: 使用usb-cdc的例子
- [u] serialusb:
- [u] stm32f4 usb cdc connection:
- [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.c 的 usb_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分析
系统文件列表:
- startup_stm32f4xx.s:与我用的基本相同,除了注释
- stm32f4_discovery.c:有些不同,详见下面
- stm32f4_discovery.h:有些不同,详见下面
- stm32f4xx_conf.h:与我用的基本相同,除了注释
- stm32f4xx_it.c:对方加了好多东西,要看
- stm32f4xx_it.h:只有注释不同
- 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支持文件:
- usb_bsp.c:是由usb支持库提供的,版本不同,差异很大
- usb_conf.h:与系统自带的差异很大,要看
- usbd_cdc.c:官方库没有找到对应内容,要看
- usbd_cdc.h:官方库没有找到对应内容,要看
- usbd_conf.h:与系统自带的差异很大,要看
- usbd_desc.c:配置文件,要看
- usbd_desc.h:配置文件,要看
- usbd_usr.c:与系统自带的差异很大,要看
用户文件列表:
- main.c
4.1.1 main.c
需导入诸多头文件:
- stm32f4xx.h
- usbd_cdc_core.h
- usbd_cdc.h
- usbd_usr.h
- 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 是以字符串描述的设备信息。
一系列不长,甚至仅仅用于返回字符串的函数:
- usbd_usr_devicedescriptor()
- usbd_usr_langidstrdescriptor()
- usbd_usr_productstrdescriptor()
- usbd_usr_manufacturerstrdescriptor()
- usbd_usr_serialstrdescriptor()
- usbd_usr_configstrdescriptor()
- 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行。主要就两个函数:
- usb_otg_bsp_init() :从90行开始,根据几种开发板,和hs/fs设置相关引脚的功能
- usb_otg_bsp_enableinterrupt() :从309行开始,配置各种中断
宏 use_usb_otg_fs 用于定义stm32f4探索套件以otg_fs运行。下面只看对stm32f4 discovery的初始化。
usb_otg_bsp_init() 的内容:
- 将pa8、pa9、pa11、pa12定义为100mhz,无上拉下拉,gpio_af_otg_fs
- 将pa10定义为100mhz、上拉,gpio_otype_od、gpio_af_otg_fs
-
启用外设时钟:
rcc_apb2periphclockcmd(rcc_apb2periph_syscfg,enable); - rcc_ahb2periphclockcmd(rcc_ahb2periph_otg_fs,enable);
- usb_otg_hs的初始化从132行开始到243行,用到的线更多,先不看了
- 250行,启用pwr时钟 rcc_apb1periphresetcmd(rcc_apb1periph_pwr,enable);
- 最后用了很多行分别配置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 :
- usb_otg_fs_core
- rx_fifo_fs_size=128
- tx0_fifo_fs_size=32
- tx1_fifo_fs_size=128
- tx2_fifo_fs_size=32
- tx3_fifo_fs_size=0
- usb_device_mode
- __align_begin :对应的是空,但不为空时对应特定编译器的 __align(4) 用于对齐
- __align_end :对应的是空
- __packed __attribute__ ((__packed__)) :对gcc的宏
4.1.7 usbd_conf.h
98行。用于usb-cdc的一些设置。常量定义如下,也是假设定义了 use_usb_otg_fs :
- usbd_cfg_max_num=1
- usbd_itf_max_num=1
- usb_max_str_desc_siz=50
- cdc_in_ep=0x81 :ep1用于data in
- cdc_out_ep=0x01 :ep1用于data out
- cdc_cmd_ep=0x82 :ep2用于cdc命令
- cdc_data_max_packet_size=64 :输入输出包最大大小
- cdc_cmd_packet_sze=8 :控制端点包大小
- cdc_in_frame_interval=5 :两个in传输间最大帧数量
- app_rx_data_size=2048 :in缓冲的总大小
- app_fops=cdc_fops
4.1.8 usbd_usr.c
189行。先导入几个头文件:
- usbd_usr.h
- usbd_ioreq.h
- 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(红色)的:
- usbd_usr_init() :初始化led5
- usbd_usr_devicereset() :无内容,会传入 uint8_t speed 可选输出日志
- usbd_usr_deviceconfigured() :点亮led5
- usbd_usr_devicesuspended() :熄灭led5
- usbd_usr_deviceresume() :点亮led5
4.1.9 usbd_cdc.h
41行。就定义了两个宏:
- default_config=0
- 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
定义了一些数据结构:
- _control_state 枚举
- onedescriptor 结构体
- _result 枚举
- _endpoint_info 结构体
- _device 结构体
- _device_info 结构体
- _device_prop 结构体
- _user_standard_request 结构体
一堆导出函数就不写了,在 usb_core.c 里写。
一些从外部导入的变量:
- device_prop device_property
- user_standard_requests user_standard_requests
- device device_table
- device_info device_info
- uint16_t saverstate
- 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
定义的数据结构:
- _ep_dbuf_dir 枚举
- ep_buf_num 枚举
一些地址:
- regbase:usb外设基址
- pmaaddr:packet memory area基址
- cntr:寄存器
- istr:寄存器
- fnr:寄存器
- daddr:寄存器
- btable:寄存器
- ep
_out:端点寄存器 - ep
_in:端点寄存器 - endp
:端点枚举值
一些成段的声明:
- istr中断事件:105-125行
- cntr寄存器位:130-144行
- fnr寄存器位:149-153行
- daddr寄存器位:157-158行
- 端点寄存器: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;
各个函数的定义:
- set_system() :63-141行,配置usb使用的gpio,以及中断线
- set_usbclock() :149-162行,开启usb时钟
- enter_lowpowermode() :170-174行,进入低功耗模式
- leave_lowpowermode() :182-198行,离开低功耗模式,需要重新初始化系统
- usb_interrupts_config() :206-254行,中断配置,包括低优先级,高优先级、唤醒
- usb_cable_config(newstate) :262-284行,控制d 上拉是否开启
- get_serialnum() :293-308行,创建一个序列号字符串描述符
- inttounicode(value,*pbuf,len) :317-336行,看来是数字到unicode的转换,有必要么
- cdc_send_data(*ptrbuffer,send_length) :345-362行,发送数据,直接调用usb库的3个函数,成功返回1,失败返回0
- 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个标准的:
- nmi_handler() :空的
- hardfault_handler() :死循环
- memmanage_handler() :死循环
- busfault_handler() :死循环
- usagefault_handler() :死循环
- svc_handler() :空的
- debugmon_handler() :空的
- pendsv_handler() :空的
- systick_handler() :空的
然后就是两个usb相关的,以hd设备为例:
usb_lp_irqhandler() :内部直接调用 usb_istr() 。
usb_fs_wkup_irqhandler() :内部直接调用 exti_clearitpendingbit(exti_line18) 。
4.3.5 usb_conf.h
一些声明。
- ep_num (4) :使用的端点数量
- btable_address (0x00) :缓冲表基址
- endp
_rxaddr (0x40) :端点n的接收缓冲基址 - endp
_txaddr (0x80) :端点n的发送缓冲基址 - imr_msk :事件屏蔽位
- ep
_in_callback :输入端点回调,全是空的 - 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
一些需要宏定义才声明的函数,对应宏的名字就是函数名的全大写:
- ctr_callback()
- dovr_callback()
- err_callback()
- wkup_callback()
- susp_callback()
- reset_callback()
- sof_callback()
- 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_in 和 pepint_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;
一些结构体的实例化:
- line_coding linecoding :波特率115200,无停止位,无校验位,8位数据
- device device_table :就2个字段,ep_num和1
- device_prop device_property :12个字段的结构体定义,传入串口的一堆操作函数
- user_standard_requests user_standard_requests :9个字段,也是一堆函数引用
- one_descriptor device_descriptor :2个字段,描述符
- one_descriptor config_descriptor :2个字段,描述符
- one_descriptor string_descriptor[4] :4个元素的数组,每个2个字段,字符串描述符
虚拟串口相关的操作函数:
- virtual_com_port_init() :121-137行,串口初始化
- virtual_com_port_reset() :146-191行,串口复位
- virtual_com_port_setconfiguration() :200-209行,设置配置
- virtual_com_port_setdeviceaddress() :218-221行,设置设备地址
- virtual_com_port_status_in() :230-236行,in状态
- virtual_com_port_status_out() :245-256行,out状态,无内容
- virtual_com_port_data_setup() :255-286行,数据设置
- virtual_com_port_nodata_setup() :295-311行,无数据设置
- virtual_com_port_getdevicedescriptor() :320-323行,获取设备描述符
- virtual_com_port_getconfigdescriptor() :332-335行,获取配置描述符
- virtual_com_port_getstringdescriptor() :344-355行,获取字符串描述符
- virtual_com_port_get_interface_setting() :366-377行,获取接口设置
- virtual_com_port_getlinecoding() :386-394行,获取行编码
- 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;
函数的定义:
- poweron() :64-85行,一些基本配置,返回usb_success
- poweroff() :94-108行,基本配置,返回usb_success
- suspend() :117-213行,挂起支持
- resume_init() :222-245,恢复的初始化
- 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 库中,出现在如下文件中:
- inc/usb_int.h:3处
- inc/usb_regs.h:20处
- src/usb_int.c:27处
- 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回调,分为几个步骤:
- 在 usb_conf.h 的75行,启用宏 ctr_callback
- 不要修改 usb_istr.* 文件
- 在应用中定义 ctr_callback() 函数,内容自己玩
确定了每个端点都有自己的ctr_tx和ctr_rx位,我需要的是向上位机发送数据,所以要寻找特定端点的ctr_tx位。
usb_regs.h 中定义了端点寄存器的一些值 ep_ctr_rx 和 ep_ctr_tx 。也许就是 geteptxstatus() 函数。实际的实现是在 usb_regs.h:337 的一个宏。当该函数返回0x30时,就是可以发送数据了。
usb外设的基址 0x4000,5c00 。
分析下usb cdc应用中的4个端点:
- ep0:usb_ep0r=0x5210,control端点
- ep1:usb_ep1r=0x0031,bulk端点
- ep2:usb_ep2r=0x0622,interrupt端点
- 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文件。把如下文件拷贝到应用的目录里:
- hw_config.h/.c
- stm32_it.h/.c
- usb_conf.h
- usb_desc.h/.c
- usb_endp.c
- usb_istr.h/.c
- usb_prop.h/.c
- 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.c 的 suspend() ,被 usb_istr.c 所调用。逻辑是当变量 fsuspendenabled=true 时就调用。
最简单方便的解决方法是在主程序的启动文件里声明一下该变量:
extern __io bool fsuspendenabled;
然后在主程序里将其设置为不进入挂起:
fsuspendenabled=false;
然后就不会进入该死的挂起了。
这种挂起状态往往是因为usb设置出了问题,usb初始化失败,从而进入了挂起。而正常启动usb设备时不会出现该问题。
相关推荐
动开发以及应用程序做了一些介绍,为更好理解,请仔细学习 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转串口板。 软件框架说明软件模块说明演示效果视频演示: 比赛感悟通过参与...