liuxiaofei126
认证:VIP会员
所在专题目录 查看专题
从IIC实测波形入手搞懂IIC通信
实例讲解(一)移位寄存器
单片机之USB 硬件和数据的四种传输方式
实例讲解(二)SPI通讯显示电子星球
实例讲解(三)之USB模拟串口
实例分析之可编程数控电源
作者动态 更多
stm32知识点总结
2021-11-24 23:12
工程师单片机LED项目
2021-11-11 07:46
电容型负载对跟随器的影响
2021-10-28 23:33
图腾柱和互补推挽两个“小冤家”
2021-10-22 23:15
学习单片机的捷径
2021-10-17 23:07

实例讲解(三)之USB模拟串口

这段时间学习STM32的USB通信,今天分享USB模拟串口,先上最小系统板和原理图

我们先来了解一下USB模拟串口方面的知识,记得D+要上拉1.5K电阻

1.stm32的USB固件库

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

1.USB_CORE下的文件介绍

2.USB_CONFIG下的文件介绍

3.中断

再看USB如何初始化

USB的配置通过上面三个函数完成:
       Set_USBClock();            //配置USB时钟,即从72M主频得到48M的USB时钟(1.5分频)
       USB_Interrupts_Config(); //设置USB唤醒中断和USB低优先级数据处理中断  
       USB_Init();                     //初始化USB,主要是调用Virtual_Com_Port_init函数,开启USB部分的电源等
 一、USB虚拟串口数据发送:
//发送一个字节数据到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结构体,定义如下:
//定义一个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串口发送的数据,都将先存到结构体的buffer数组(FIFO缓存区)里面,USB_USART_TXFIFO_SIZE定义了该数组的大小,通过writeptr和readptr来控制FIFO的写入和读出。该结构体buffer数据的写入,是通过USB_USART_SendData()函数实现的。buffer数据的读出(然后发送到USB)则是通过端点1的回调函数EP1_IN_Callback()函数实现的。
该函数在usb_endp.c中。代码如下:
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;//得到要发送的数据长度
	}else											//超过数组了 读指针>写指针
	{
		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中,代码如下:
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中实现,代码如下:
//处理从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;//接收数据错误,重新开始接收	
				}					
			}
		}   
	}  
} 

最后下载验证

发送和接受一致

声明:本内容为作者独立观点,不代表电子星球立场。未经允许不得转载。授权事宜与稿件投诉,请联系:editor@netbroad.com
觉得内容不错的朋友,别忘了一键三连哦!
赞 6
收藏 7
关注 206
成为作者 赚取收益
全部留言
0/200
  • dy-blNlwnWV 2021-02-25 21:44
    佩服楼主
    回复
  • keyhei66 2021-01-15 18:49
    可以
    回复
  • 星球居民-YBPLIKJ1 2021-01-14 19:52
    不错不错
    回复
  • lihui710884923 2021-01-07 10:44
    有资料吗?
    回复