历史上的今天
今天是:2024年11月05日(星期二)
2020年11月05日 | STM32 USB Virtual COM USB转串口的功能实现
2020-11-05 来源:eefocus
这次讲的是如何实现USB转串口功能的实现。首先看看工程的布局吧:
我们主要要介绍的文件的在USB_User这个组文件。从上面的截图可以看到USB_User这个文件由hw_config.c、usb_desc.c、usb_endp.c、usb_istr.c、usb_prop.c、usb_pwr.c几个文件组成。其中usb_istr.c和usb_pwr.c整两个文件不用修改,其他的文件都需要修改。下面接慢慢将来。
首先讲讲hw_config.c这个文件。由于我们用到串口,所以这个文件需要添加串口相关代码。在这个文件的开始就需要定义一下串口的相关变量:
uint8_t USART_Rx_Buffer [USART_RX_DATA_SIZE]; //串口接收缓冲
uint32_t USART_Rx_ptr_in = 0; //这里采用的是一个环形缓冲,串口数据输入起始位置
uint32_t USART_Rx_ptr_out = 0; //环形缓冲的数据结束位置
uint32_t USART_Rx_length = 0; //接收数据的长度
uint8_t USB_Tx_State = 0; //USB发送标志,当串口缓冲有数据没有发送,该位置1
这里开了一个2K的环形缓冲如下图所示:
STM32 USB Virtual COM USB转串口的功能实现 - ziye334 - ziye334的博客其中 USART_Rx_ptr_in指向的就是图中read position处, USART_Rx_ptr_out指向write position处, USART_Rx_length就是数据的长度,就是图中橙色的圆弧。当没有数据的时候, USART_Rx_ptr_in= USART_Rx_ptr_out,有数据收到的时候 USART_Rx_ptr_in就向后偏移,当数据被读出去的时候 USART_Rx_ptr_out也会向后偏移。
这里需要定义一个串口默认配置:波特率为9600,数据长度为8位,停止位为1位,奇校验,没有数据流控制,代码如下:
/*******************************************************************************
* Function Name : USART_Config_Default.
* Description : 串口的默认配置值
* Input : None.
* Return : None.
*******************************************************************************/
void USART_Config_Default(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
/* 使能 UART2 时钟 */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
/* 配置 USART2 的Tx 引脚类型为推挽式的 */
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* 配置 USART2 的Rx 为输入悬空 */
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* 串口默认配置*/
/* 串口配置值如下:
- 波特率 = 9600 baud
- 数据长度 = 8 Bits
- 1位停止位
- 奇校验
- 不使能硬件流控制
- 接收传输使能
*/
USART_InitStructure.USART_BaudRate = 9600; //波特率9600
USART_InitStructure.USART_WordLength = USART_WordLength_8b; //8位数据位
USART_InitStructure.USART_StopBits = USART_StopBits_1; //1位停止位
USART_InitStructure.USART_Parity = USART_Parity_Odd; //奇校验
USART_InitStructure.USART_HardwareFlowControl=USART_HardwareFlowControl_None;//没有数据流控制
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //接收、发送使能
USART_Init(USART2, &USART_InitStructure); //配置串口
USART_ITConfig(USART2, USART_IT_RXNE, ENABLE); //使能串口接收中断
USART_Cmd(USART2,ENABLE); //串口使能
}
这里还要定义一个串口配置函数,这个函数会根据linecoding这个结构体来配置串口设置:
/*******************************************************************************
* Function Name : USART_Config.
* Description : 根据line coding 结构体配置串口.
* Input : None.
* Return : 配置状态
TRUE : 配置成功
FALSE: 配置中断
*******************************************************************************/
bool USART_Config(void)
{
/*设置停止位*/
switch (linecoding.format)
{
case 0:
USART_InitStructure.USART_StopBits = USART_StopBits_1; //1位停止位
break;
case 1:
USART_InitStructure.USART_StopBits = USART_StopBits_1_5; //1.5为停止位
break;
case 2:
USART_InitStructure.USART_StopBits = USART_StopBits_2; //2位停止位
break;
default :
{
USART_Config_Default(); //默认配置
return (FALSE);
}
}
/* 设置校验位*/
switch (linecoding.paritytype)
{
case 0:
USART_InitStructure.USART_Parity = USART_Parity_No; //没有校验
break;;
case 1:
USART_InitStructure.USART_Parity = USART_Parity_Even; //偶校验
break;
case 2:
USART_InitStructure.USART_Parity = USART_Parity_Odd; //奇校验
break;
default :
{
USART_Config_Default(); //默认配置
return (FALSE);
}
}
/*设置数据位: 8位或9位*/
switch (linecoding.datatype)
{
case 0x07:
USART_InitStructure.USART_WordLength = USART_WordLength_8b;//8为数据位,这个选项就校验位必须设置(奇校验/偶校验)
break;
case 0x08:
if (USART_InitStructure.USART_Parity == USART_Parity_No)//没有设置校验位时
{
USART_InitStructure.USART_WordLength = USART_WordLength_8b;//8位数据位
}
else
{
USART_InitStructure.USART_WordLength = USART_WordLength_9b;//9位数据位
}
break;
default :
{
USART_Config_Default(); //默认配置
return (FALSE);
}
}
USART_InitStructure.USART_BaudRate = linecoding.bitrate; //设置波特率
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//设置没有硬件数据流控制
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //使能接收、发送
USART_Init(USART2, &USART_InitStructure); //初始化串口
return (TRUE);
}
还要定义一个USB_To_USART_Send_Data()函数,将USB接收到的数据从串口发送数据:
/*******************************************************************************
* Function Name : USB_To_USART_Send_Data.
* Description : 将USB接收到的数据到URAT.
* Input : data_buffer: data address.
Nb_bytes: number of bytes to send.
* Return : none.
*******************************************************************************/
void USB_To_USART_Send_Data(uint8_t* data_buffer, uint8_t Nb_bytes)
{
uint32_t i;
for (i = 0; i < Nb_bytes; i++) //串口发送数据
{
USART_SendData(USART2, *(data_buffer + i));
while(USART_GetFlagStatus(USART2, USART_FLAG_TXE) == RESET);
}
}
接下去要定义Handle_USBAsynchXfer();函数,这个函数将串口收到的数据通过USB发送给PC机:
/*******************************************************************************
* Function Name : Handle_USBAsynchXfer.
* Description : 给USB发送数据
* Input : None.
* Return : none.
*******************************************************************************/
void Handle_USBAsynchXfer (void)
{
uint16_t USB_Tx_ptr; //USB发送数据位置
uint16_t USB_Tx_length;
if(USB_Tx_State != 1) //USB没有在发送
{
if (USART_Rx_ptr_out == USART_RX_DATA_SIZE)//环形接收缓冲输出位置到了缓冲区的最后
{
USART_Rx_ptr_out = 0; //设置接收缓冲位置为从0开始
}
if(USART_Rx_ptr_out == USART_Rx_ptr_in) //环形接收缓冲输出位置等于输入位置,说明USB数据发送完毕了
{
USB_Tx_State = 0; //设置USB发送标志位为0
return;
}
if(USART_Rx_ptr_out > USART_Rx_ptr_in) //环形接收缓冲输出位置大于输入位置,说明已经串口数如数据已经越过界了
{
USART_Rx_length = USART_RX_DATA_SIZE - USART_Rx_ptr_out;//计算缓冲数据长度
}
else
{
USART_Rx_length= USART_Rx_ptr_in - USART_Rx_ptr_out; //计算串口缓冲的数据长度
}
if (USART_Rx_length > VIRTUAL_COM_PORT_DATA_SIZE)//如果数据的长度大于端点的最大长度
{
USB_Tx_ptr = USART_Rx_ptr_out; //USB发送数据起始位置为串口缓冲输出位置
USB_Tx_length=VIRTUAL_COM_PORT_DATA_SIZE;//USB的发送长度为 端点的最大传输长度
USART_Rx_ptr_out += VIRTUAL_COM_PORT_DATA_SIZE;//移动串口缓冲输出位置
USART_Rx_length -= VIRTUAL_COM_PORT_DATA_SIZE;//计算剩余数据长度
}
else //如果数据的长度小于端点的最大长度
{
USB_Tx_ptr = USART_Rx_ptr_out; //USB发送数据起始位置为串口缓冲输出位置
USB_Tx_length = USART_Rx_length;//USB发送数据长度为 串口缓冲数据的长度
USART_Rx_ptr_out += USART_Rx_length;//移动串口缓冲输出位置
USART_Rx_length = 0; //没有剩余的数据要发送了
}
USB_Tx_State = 1; //设置USB发送状态为正在发送
UserToPMABufferCopy(&USART_Rx_Buffer[USB_Tx_ptr], ENDP1_TXADDR, USB_Tx_length);//从缓冲数据区拷贝数据到端点发送数据
SetEPTxCount(ENDP1, USB_Tx_length); //设置端点1发送计数
SetEPTxValid(ENDP1); //使能端点1
}
}
这里还要定义一个函数用来调整串口接收到的串口环形缓冲的写入读出数据的位子 USART_To_USB_Send_Data() :
/*******************************************************************************
* Function Name : UART_To_USB_Send_Data.
* Description : 发送串口接收到的数据大USB
* Input : None.
* Return : none.
*******************************************************************************/
void USART_To_USB_Send_Data(void)
{
if (linecoding.datatype == 7) //数据长度为7
{
USART_Rx_Buffer[USART_Rx_ptr_in] = USART_ReceiveData(USART2)&0x7F; //保存接收到的数据
}
else if (linecoding.datatype == 8) //数据长度为8
{
USART_Rx_Buffer[USART_Rx_ptr_in] = USART_ReceiveData(USART2);//保存接收到的数据
}
USART_Rx_ptr_in++; //串口缓冲输入位置递增
/* To avoid buffer overflow */
if(USART_Rx_ptr_in == USART_RX_DATA_SIZE) //如果串口缓冲数据位置到达了缓冲的最后
{
USART_Rx_ptr_in = 0; //重新开始
}
}
接下去要将的是usb_desc.c真个文件。先讲讲设备描述符,设备描述符跟之前讲述的没有多少出入,只是厂商ID要独享为0483,否则上位机无法安装相应的驱动。
/* USB标准设备描述符*/
const uint8_t Virtual_Com_Port_DeviceDescriptor[VIRTUAL_COM_PORT_SIZ_DEVICE_DESC] =
{
0x12, /*bLength:长度,设备描述符的长度为18字节*/
USB_DEVICE_DESCRIPTOR_TYPE, /*bDescriptorType:类型,设备描述符的编号是0x01*/
0x00, /*bcdUSB:所使用的USB版本为2.0*/
0x02,
0x02, /*bDeviceClass:设备所使用的类代码*/
0x00, /*bDeviceSubClass:设备所使用的子类代码*/
0x00, /*bDeviceProtocol:设备所使用的协议*/
0x40, /*bMaxPacketSize:最大包长度为64字节*/
0x83, /*idVendor:厂商ID为0x0483*/
0x04,
0x40, /*idProduct:产品ID为0x5740*/
0x57,
0x00, /*bcdDevice:设备的版本号为2.00*/
0x02,
1, /*iManufacturer:厂商字符串的索引*/
2, /*iProduct:产品字符串的索引*/
史海拾趣
|
Cadence MMsim v7.01 Linux 3CD模拟/数模混合电路加速仿真技术 Cadence MMsim v7.01 Linux 3CD模拟/数模混合电路加速仿真技术 MMsim v7.01软件下载 license 授权 模拟/数模混合电路加速仿真技术 需要的话,请联系QQ 3385251 tel:13017525669 对今天的混合信号SoC设计,往往包括模拟、射频、数字 ...… 查看全部问答> |
|
C++学习的门厅---控制台应用程序 //WIN程序开发的绚丽舞台,引无数手握空拳者纷至沓来,兴奋之余,竟然很容易忘记WIN开发的基础是C++:( //看中的VC++2005的也好,看中的BCB6.0也罢,均有可以编写、编译、测试的控制台应用程 ...… 查看全部问答> |
|
本帖最后由 dontium 于 2015-1-23 13:07 编辑 USB单片DAC PCM2702及其应用文/吴玥 编者按:PCM2702是由德州仪器推出的一款带有USB接口的16位立体声数模转换芯片,它可以为实现电脑音源的一系列外围设计提供帮助。《无线电》杂志特准备50片PCM270 ...… 查看全部问答> |
|
瞬联软件是国内比较知名的软件外包公司(http://www.cienet.com.cn),总部在北京,在上海,杭州,成都均有分布,客户几乎包含了所有知名的通信厂商,比如诺基亚西门子网络(NSN),摩托罗拉,爱立信等。在程序员中有很好的口碑,提供的待遇和福利也 ...… 查看全部问答> |
|
请问各位大侠: 我用万用表的通断档测量电路板的正负极之间是否短路时,(电路板未加电时测量) 万用表通断档有时显示的是.548,用电阻档测量时阻值为1.5K;有时通断档显示为1.254,电阻档测得阻值为5.2K. &nb ...… 查看全部问答> |




