一句话概述:
现象:电脑通过usb转串口与stm32串口通信,每当电脑打开串口,stm32就跑飞,给人的感觉就是死机了。
原因:单片机的串口使用的是CH340的TLL转USB,其DTR管脚连接者mcu的复位。一旦上位机开启 了DTR,就会造成MCU复位,给人一种mcu死机的假象。
解决办法:上位机打开串口时,关闭DTR和RTS
1 问题现象
电脑通过usb转串口与stm32串口通信,每当电脑打开串口,stm32就跑飞,给人的感觉就是死机了。
2 操作情况
2.1 上位机open串口代码
使用VC2010开发环境,纯C语言写的上位机串口工具。
HANDLE open_port(int num)
{
int err;
DCB dcb;
HANDLE hPort;
TCHAR name[30];
// 打开串口
if (num == -1)
num = get_port();
_sntprintf_s(name, _countof(name), _countof(name), TEXT("\\\\.\\COM%d"), num);
hPort = CreateFile(name, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
if (hPort == INVALID_HANDLE_VALUE)
{
return INVALID_HANDLE_VALUE;
}
// 设置波特率
dcb.DCBlength = sizeof(DCB);
GetCommState(hPort, &dcb);
dcb.BaudRate = CBR_115200;
dcb.ByteSize = 8;
dcb.Parity = NOPARITY;
dcb.StopBits = ONESTOPBIT;
dcb.fRtsControl = RTS_CONTROL_ENABLE;
dcb.fDtrControl = DTR_CONTROL_ENBALE;
SetCommState(hPort, &dcb);
lastopen = GetTickCount();
return hPort;
}
2.2 stm32 usb转串口图
2.2 stm32串口代码
void USART1_Init(u32 bound)
{
//GPIO端口设置
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE); //打开时钟
/* 配置GPIO的模式和IO口 */
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_9;//TX //串口输出PA9
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP; //复用推挽输出
GPIO_Init(GPIOA,&GPIO_InitStructure); /* 初始化串口输入IO */
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_10;//RX //串口输入PA10
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING; //模拟输入
GPIO_Init(GPIOA,&GPIO_InitStructure); /* 初始化GPIO */
//USART1 初始化设置
USART_InitStructure.USART_BaudRate = bound;//波特率设置
USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位
USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收发模式
USART_Init(USART1, &USART_InitStructure); //初始化串口1
USART_Cmd(USART1, ENABLE); //使能串口1
USART_ClearFlag(USART1, USART_FLAG_TC);
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启相关中断
//Usart1 NVIC 配置
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;//串口1中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3;//抢占优先级3
NVIC_InitStructure.NVIC_IRQChannelSubPriority =3; //子优先级3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器
}
// USART1中断函数
void USART1_IRQHandler(void) //串口1中断服务程序
{
u8 r;
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收中断
{
r = USART_ReceiveData(USART1);//(USART1->DR); //读取接收到的数据
QueueIn(&g_UpdataRxBuff, r);
}
}
3 定位过程
说起来串口是很简单的协议,最容易想到的就是TX,RX,GND连线。这些就算错误也不至于导致mcu死机。而且上位机还未开始向下位机发送数据,所以也不可能是单片机串口中断出的问题。仅仅是上位机open串口就已经死机了。
按照此思路想到了下位机的USB转串口这部分,仔细看了这部分电路发现了CH340的DTR管脚连到了mcu的reset管脚,这个嫌疑就大了。
立马去查看上位机打开串口的代码,果然配置串口参数的时候使能了DTR。到这里基本就定位了
4 解决问题
上位机在open串口时,不要使能串口的DTR,这样就不会导致mcu reset管脚被置位。
解决问题后的上位机代码
HANDLE open_port(int num)
{
int err;
DCB dcb;
HANDLE hPort;
TCHAR name[30];
// 打开串口
if (num == -1)
num = get_port();
_sntprintf_s(name, _countof(name), _countof(name), TEXT("\\\\.\\COM%d"), num);
hPort = CreateFile(name, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
if (hPort == INVALID_HANDLE_VALUE)
{
return INVALID_HANDLE_VALUE;
}
// 设置波特率
dcb.DCBlength = sizeof(DCB);
GetCommState(hPort, &dcb);
dcb.BaudRate = CBR_115200;
dcb.ByteSize = 8;
dcb.Parity = NOPARITY;
dcb.StopBits = ONESTOPBIT;
dcb.fRtsControl = RTS_CONTROL_DISABLE; // 关闭RTS
dcb.fDtrControl = DTR_CONTROL_DISABLE; // 关闭DTR
SetCommState(hPort, &dcb);
lastopen = GetTickCount();
return hPort;
}
遇到问题不要慌,理清思路很重要,机器永远是机器,按照正确的理论查下去,问题一定能解决!!!
需要上位机与下位机串口通信代码的,可以私信我,免费发给你。