前言

STM32F103系列芯片都自带USB接口,不过STM32F103的USB都只能用来做设备,而不能用作主机。

目录:

53.1 USB简介

USBF103自带的USB符合USB2.0规范。
    在USB主机上,D-和d +都接了15K下拉电阻到地,所以没有设备接入时,d +和D-都是低电平。
    在USB设备中,如果是高速设备,d +上接一个1.5K上拉电阻到VCC;如果是低速设备,D-上接入一个1.5K
。上拉电阻到VCC这样主机就能判断是否有设备接入,接入的设备是高速还是低速设备。

1)STM32的USB从控制器

①PC主机和MCU之间的数据传输是通过共享一专用的数据缓冲区来完成的,该数据缓冲区被USB外设直接

访问。这块专用数据缓冲区的大小由所使用的端点数目和每个端点最大的数据分组大小所决定,每个端点最大

可使用512字节缓冲区(专用512字节,和CAN公用),最多可用于16个单向或8个双向端点。

②USB模块和PC主机通信,根据USB规范实现令牌分组的检测,数据发送/接收处理,和握手分组的处理。整

个传输的过程由硬件完成,包括CRC的生成和校验。

③每个端点都有一个缓冲区描述块,描述该端点使用的缓冲区地址,大小和需要传输的字节数。当USB模块识

别出一个有效的功能/端点的令牌包分组时,(如果需要传输数据并且端点已配置)随之发生相关的数据传输。

USB模块通过一个内部的16位寄存器实现端口与专用缓冲区之间的数据交换。在所有的数据传输完成后,如

果需要,根据传输的方向,发送或接收适当的握手分组。在数据传输结束时,USB模块将触发与端点相关的中断,

通过读状态寄存器和/或者利用不同的中断来处理。

④USB的中断映射单元:将可能产生中断的USB时间映射到三个不同的NVIC请求线上:

一个,USB低优先级中断(通道20):可由所有的USB事件触发(正确传输,USB复位等)固件在处理中断前应先确定中断源。

B,USB高优先级中断(通道19):仅能由同步和双缓冲批量传输的正确传输事件触发,目的是保证最大的传输速率。

C,USB唤醒中断(通道42):由USB挂起模式的唤醒事件唤醒。

2)STM32的USB设备框图

53.2硬件设计

    本章实验​​功能简介:

利用STM32自带的USB功能,连接电脑USB虚拟出一个USB串口,实现电脑和开发板的数据通信。在找到虚拟串口

后,即可打开串口调试助手,通过USB虚拟串口和上位机对话,STM32在收到上位机发过来的字符串(以回车换行结

束)后,原样返回给上位机。下载后,DS0闪烁,提示程序在运行,每个一定时间,通过USB虚拟串口发送一段信息给

电脑。

    所要用到的硬件资源如下:

1)指示灯DS0,DS1;

2)串口;

3)TFTLCD模块;

4)USB SLAVE接口。

的MiniUSB接口与STM32连接电路图:

因为PA11和PA12既是USB接口又是CAN接口,所以用连接座P9转接,需要用USB时,用跳线帽将PA11,PA12与

D-,d +连接即可。

53.3软件设计

1)stm32的USB固件库

移植时,重点要修改USB_CONFIG文件夹下的代码,USB_CORE下的代码一般不用修改。

①USB_CORE下代码介绍:

序号 文件名 作用 备注
1 usb_regs.c 操作USB控制寄存器; 里面有对各种USB寄存器的底层操作函数;
2 usb_init.c 初始化USB控制器; 只有一个函数USB_Init,调用其它函数,以使代码规范;
3 usb_int.c 中断处理函数; 只有两个函数:
CTR_LP:负责USB低优先级中断的处理;
CTR_HP:负责USB高优先级中断的处理;
4 usb_mem.c 处理PMA数据; PMA全称为数据包存储区,是stm32内部用于USB / CAN的专用数据缓冲区。
只有两个函数:
PMAToUserBufferCopy:将USB数据传送到
主机; UserToPMABufferCopy:将主机数据传送到USB;
usb_core.c 处理USB2.0协议  
6 usb_sil.c 为USB端点提供简化的读写访问函数。  

以上的函数具有很强的独立性,直接调用内部的函数即可。

②USB_CONFIG下的代码:

序号 文件名 作用
1 hw_config.c 配置硬件,比如初始化USB时钟,USB中断,低功耗模式处理等;
2 usb_desc.c 处理Virtual Com描述符;
3 usb_endp.c 处理正确传输中断回调函数,用于非控制传输;
4 usb_pwr.c 管理USB控制器的电源;
usb_istr.c 处理USB中断;
6 usb_prop.c 处理所有Virtual Com的相关事件,包括Virtual Com的初始化,复位等操作。

③其他

序号 文件名 作用 备注
1 stm32_it.c 处理USB相关中断。 包括两个中断服务函数:
USB_LP_CAN1_RX0_IRQHandler:调用USB_Istr函数,处理USB发生的各种中断;
USBWakeUp_IRQHandler:清除中断标志。
注:为了方便,一般把USB中断相关代码放到hw_config.c里面。

2)USB实现代码

int main(void)
{
u16 t;
u16 len;
u16 times=0;
u8 usbstatus=0;
delay_init(); //延时函数初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
uart_init(115200); //串口初始化为115200USB_Port_Set(0);     //USB先断开
delay_ms(700);
USB_Port_Set(1);         //USB再次连接Set_USBClock(); //配置USB时钟,即从72M主频得到48M的USB时钟(1.5分频)
USB_Interrupts_Config(); //设置USB唤醒中断和USB低优先级数据处理中断
USB_Init();   //初始化USB,主要是调用Virtual_Com_Port_init函数,开启USB部分的电源等while(1)
{
if(usbstatus!=bDeviceState)//USB连接状态发生了改变.
{
usbstatus=bDeviceState;//记录新的状态
if(usbstatus==CONFIGURED)
{
LED1=0;//DS1亮 提示USB连接成功
}else
{
LED1=1;//DS1灭 提示USB断开
}
}if(USB_USART_RX_STA&0x8000)
{
len=USB_USART_RX_STA&0x3FFF; //得到此次接收到的数据长度usb_printf("\r\n您发送的消息为:%d\r\n\r\n",len);
for(t=0;t<len;t++)
{
USB_USART_SendData(USB_USART_RX_BUF[t]); //以字节方式,发送给USB
}
usb_printf("\r\n\r\n"); //插入换行USB_USART_RX_STA=0;
}
else
{
times++;
if(times%5000==0)
{
usb_printf("\r\n战舰STM32开发板USB虚拟串口实验\r\n");
usb_printf("正点原子@ALIENTEK\r\n\r\n");
}if(times%200==0)
usb_printf( “请输入数据,以回车键结束\ r \ n”);
if(times%30==0)
LED0 = LED0; //闪烁的LED,提示系统正在运行。
delay_ms(10);
}
}
}

    主要代码作用:

 USB的配置通过三个函数完成:

Set_USBClock(); //配置USB时钟,即从72M主频得到48M的USB时钟(1.5分频)

USB_Interrupts_Config(); //设置USB唤醒中断和USB低优先级数据处理中断

USB_Init(); //初始化USB,主要是调用Virtual_Com_Port_init函数,开启USB部分的电源等

为什么不设置PA11和PA12管脚?

因为,一旦开启USB电源(USB_CNTR的PDWN位清零),PA11和PA12将不再做其他功能使用,仅供USB使用,所以在

开启了USB电源之后,无论怎么配置这两个管脚,都是无效的。要再次获取这两个管脚的配置权,需要关闭USB电源,即置

位USB_CNTR的PDWN位,我们通过USB_Port_Set函数来禁止/使能USB连接,在复位时,先禁止,再使能,这样每次我们

按复位键,电脑都可以识别到USB鼠标,而不需要我们每次都拔USB线.USB_Port_Set函数在hw_config.c中实现,

代码如下:

//USB使能连接/断线
//enable:0,断开; 1,允许连接
void USB_Port_Set(u8 enable)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); //使能PORTA时钟
if(enable)
{
_SetCNTR(_GetCNTR()&(~(1<<1)));//退出断电模式
}
else
{
_SetCNTR(_GetCNTR()|(1<<1)); // 断电模式
GPIOA->CRH&=0XFFF00FFF;
GPIOA->CRH|=0X00033000;
PAout(12)=0;
}
}

    USB虚拟串口数据发送:

该函数在hw_config.c中


//发送一个字节数据到USB虚拟串口
void USB_USART_SendData(u8 data)
{
uu_txfifo.buffer[uu_txfifo.writeptr]=data;
uu_txfifo.writeptr++;
if(uu_txfifo.writeptr==USB_USART_TXFIFO_SIZE)//超过buf大小了,归零.
{
uu_txfifo.writeptr=0;
}
}

该函数实现发送1字节数据到虚拟串口中。

这里用到了一个uu_txfifo结构体,该结构体是一个USB虚拟串口发送数据FIFO结构体,定义如下:

#define USB_USART_TXFIFO_SIZE 1024 //USB虚拟串口发送FIFO大小//定义一个USB USART FIFO结构体
typedef struct
{
u8 buffer[USB_USART_TXFIFO_SIZE]; //buffer
vu16 writeptr; //写指针
vu16 readptr; //读指针
}_usb_usart_fifo;
extern _usb_usart_fifo uu_txfifo; //USB串口发送FIFO

该结构体用于处理USB串口要发送的数据,所以通过USB串口发送的数据,都将先存到结构体的缓冲数组(FIFO缓存区)里

面,USB_USART_TXFIFO_SIZE定义了该数组的大小,通过writeptr和readptr来控制FIFO的写入和读出。该结构体缓冲数

据的写入,是通过USB_USART_SendData()函数实现的。

缓冲数据的读出(然后发送到USB)则是通过端点1的回调函数EP1_IN_Callback()函数实现的。

该函数在usb_endp.c中代码如下:

//Function Name : EP1_IN_Callback
void EP1_IN_Callback (void)
{
u16 USB_Tx_ptr;
u16 USB_Tx_length;
if(uu_txfifo.readptr==uu_txfifo.writeptr)//无任何数据要发送,直接退出
{
return;
}if(uu_txfifo.readptr<uu_txfifo.writeptr) //没有超过数组, 读指针 < 写指针
{
USB_Tx_length=uu_txfifo.writeptr-uu_txfifo.readptr;    //得到要发送的数据长度
}
否//超过数组了读指针>写指针
{
USB_Tx_length=USB_USART_TXFIFO_SIZE-uu_txfifo.readptr; //得到要发送的数据长度
}if(USB_Tx_length> VIRTUAL_COM_PORT_DATA_SIZE)//超过64字节?
{
USB_Tx_length=VIRTUAL_COM_PORT_DATA_SIZE;            //此次发送数据量
}USB_Tx_ptr = uu_txfifo.readptr; //发送起始地址
uu_txfifo.readptr += USB_Tx_length;         //读指针偏移if(uu_txfifo.readptr>=USB_USART_TXFIFO_SIZE) //读指针归零
{
uu_txfifo.readptr=0;
}UserToPMABufferCopy(&uu_txfifo.buffer[USB_Tx_ptr], ENDP1_TXADDR, USB_Tx_length);
SetEPTxCount(ENDP1, USB_Tx_length);
SetEPTxValid(ENDP1);
}

函数这个由USB中断处理相关函数调用,将通过USB发送给电脑的数据拷贝到端点1的发送区,然后通过USB发送给电脑,

从而实现串口数据的发送。

因为每次传输数据长度不超过VIRTUAL_COM_PORT_DATA_SIZE,所以USB的发送数据长度:USB_Tx_length的最大值

只能是VIRTUAL_COM_PORT_DATA_SIZE。

以上就是USB虚拟串口数据发送过程。

USB虚拟串口数据接收:

USB虚拟串口的接收,通过端点3来实现,端点3的回调函数为EP3_OUT_Callback(),该函数也在usb_endp.c中,代码如下:

//Function Name : EP3_OUT_Callback
void EP3_OUT_Callback(void)
{
u16 USB_Rx_Cnt;
USB_Rx_Cnt = USB_SIL_Read(EP3_OUT, USB_Rx_Buffer); //得到USB接收到的数据及其长度
USB_To_USART_Send_Data(USB_Rx_Buffer, USB_Rx_Cnt); //处理数据(其实就是保存数据)
SetEPRxValid(ENDP3);     //使能端点3的数据接收
}

该函数也是由USB中断处理相关函数调用,该函数通过调用USB_To_USART_Send_Data函数,实现USB接收数据的保存,

该函数在hw_config.c中实现,代码如下:

u8 USB_USART_RX_BUF[USB_USART_REC_LEN];    //接收缓冲,最大USART_REC_LEN个字节。
u16 USB_USART_RX_STA=0;                    //接收状态,bit15, 接收完成标志,bit14, 接收到 0x0d,bit13~0,接收到的有效字节数目
//处理从USB虚拟串口接收到的数据,DataBuffer中:数据缓存区,Nb_bytes:接收到的字节数。
void USB_To_USART_Send_Data(u8* data_buffer, u8 Nb_bytes)
{
u8 i;
u8 res;
for(i=0;i<Nb_bytes;i++)
{
res=data_buffer[i];
if((USB_USART_RX_STA&0x8000) == 0) //接收未完成
{
if(USB_USART_RX_STA & 0x4000) //接收到了0x0d
{
if(res != 0x0a)
USB_USART_RX_STA = 0; //接收错误,重新开始
else
USB_USART_RX_STA |= 0x8000; //接收完成了
}
else //还没收到0X0d
{
if(res == 0x0d)
USB_USART_RX_STA |= 0x4000;
else
{
USB_USART_RX_BUF[USB_USART_RX_STA&0X3FFF] = res;
USB_USART_RX_STA++;if(USB_USART_RX_STA > (USB_USART_REC_LEN-1))
USB_USART_RX_STA = 0; //接收数据错误,重新开始接收
}
}
}
}
}

用类似串口1接收数据的方法,来处理USB虚拟串口接收到的数据。

53.4下载验证

本例程需要在电脑上先安装ST提供的USB虚拟串口驱动软件,安装好后,重新插入USB数据线,可以看到如下信息。

打开串口调试助手,选择串口号,打开串口,就可以进行测试了。

可以看到,串口调试助手,收到了开发板的数据,同时,按发送按钮(串口调试助手必须勾选“发送新行”),也可以收到电脑发送

给开发板的数据(原样返回),说明实验成功,实验现象同串口帐相同。

USB虚拟串口实验_STM32F1开发指南——USB学习笔记相关推荐

  1. SD卡实验_STM32F1开发指南_第四十三章

                                                           第四十三章 SD卡实验 序言: 战舰STM32F103自带了标准的SD卡接口,使用STM3 ...

  2. 《Oracle PL/SQL开发指南》学习笔记28——源码调试——PL/SQL基础知识(第六部分)

    控制结构 1. 条件结构 1)if, elsif和else语句 重要概念: 三值逻辑(Three-Valued Logic) Three-valued logic means basically th ...

  3. 【正点原子STM32连载】第五十八章 USB虚拟串口(Slave)实验 摘自【正点原子】MiniPro STM32H750 开发指南_V1.1

    1)实验平台:正点原子MiniPro H750开发板 2)平台购买地址:https://detail.tmall.com/item.htm?id=677017430560 3)全套实验源码+手册+视频 ...

  4. linux系统串口透传,基于CC2540的USB虚拟串口透传方案

    1.USB虚拟串口代替物理串口的可行性 首先,越来越多带USB接口的器件涌现出来,如带USB接口的单片机,或独立的USB接口器件,而且这些器件的成本已经很接近于使用RS232电平转换芯片所带来的成本. ...

  5. STM32CUBEMX F103 HAL库开发之 USB虚拟串口

    今天心血来潮想写点东西 做比赛这么久了,应该写点东西,一是为了给自己学的知识一个总结,二是可以锻炼自己的写作能力与表达能力,帮助他人避免自己走过的坑 stm32 USB VPC虚拟串口简介 很多stm ...

  6. linux内核配置usb虚拟串口,Linux USB虚拟串口设备

    Linux内核中usb设备侧驱动程序分成3个层次:UDC驱动程序.Gadget API和Gadget驱动程序.UDC驱动程序(USB控制器)直接访问硬件,控制USB设备和主机间的底层通信,向上层提供与 ...

  7. GD32F4上使用HAL库实现USB虚拟串口通讯

    一直使用STM32F4搭配HAL库做项目,最近需要将软件移植至国产的GD32F4上,但兆易仅提供了标准库,并未像ST那样提供HAL库,但二者USB库又互不兼容.最后,为了原软件不做大的修改,故决定使用 ...

  8. STM32 USB虚拟串口收发任意长度字节例程

    STM32 USB虚拟串口收发任意长度字节例程 创建STM32 CubeMX工程 代码修改 收发任意长度测试 创建STM32 CubeMX工程 基于STM32 CubeMX进行开发,使用USB的高速模 ...

  9. RT_Thread Studio使用——USB虚拟串口(VCP)

    硬件:正点原子阿波罗F429开发板,主控STM32F429IGT6 软件:RT-Thread Studio 2.2.5 RT-Thread 版本:4.1.0 在RT-Thread Studio中开启外 ...

最新文章

  1. antd Drawer 如何实现自动刷新
  2. 得到课程《组织行为学》学习笔记07
  3. PrimeFaces Extensions中的全新JSF组件
  4. [转载] --- 数据库基本知识
  5. python编程软件排行榜_Python编程开发工具:这10个对Web开发者最有用的Python包
  6. vue+mintUI搭建移动端新闻类网站
  7. NOIP201501金币
  8. SharePoint2007文档的点击率统计
  9. Andro - Multipurpose OpenCart 2.X 自适应主题模板 ABC-0651
  10. 语音助手(基于v3s开源啦)
  11. 告别传统IT 信天通信助推教育行业云化变革
  12. 精确控制Origin to Word图片格式、大小及主题使用技巧
  13. 正则表达式,固话和手机号码验证,支持验证分机号
  14. [Linux]生产者消费者模型(基于BlockQueue的生产者消费者模型 | 基于环形队列的生产者消费者模型 | 信号量 )
  15. 关于导数意义的新认知
  16. 玩转全球最大同性交友网站--- 开源社区GitHub
  17. B2C购物车功能应该怎么设计,以及我的设计构思分析
  18. 债转股问题研究(lunwen+开题报告+外文翻译)
  19. 计算机中的微信无法启动,无法打开微信计算机版本文件如果无法打开微信计算机版本怎么办...
  20. 小学计算机教案模板范文,小学信息技术教案模板锦集5篇范本

热门文章

  1. W7启动报错0xc0000f
  2. Putty建立隧道访问页面步骤
  3. FPGA产生2FSK信号(1)
  4. 【Python文本处理】基于运动路线记录GPX的文件解析,及对经纬度坐标的数学模型运动速度求解
  5. 哪些人需要CISA证书?
  6. SharedPreference存储精炼详解
  7. 【UML】UML几种图的绘制
  8. 运输道路变“大型烟花现场”?资产监测设备避免烟花运输发生意外!
  9. 用服务器监控手机位置,云服务器监控手机
  10. linux入门书籍 英文版,BeginningLinuxProgramming3