关于与E103-W01 WIFI模块(基于ESP8266)进行串口通信的代码测试

前情提要:由于嵌入式模块需要使用WIFI进行数据交互通信,因此尝试接入亿佰特的E103-W01 WIFI模块编写代码进行串口通信测试,中间遇到了一些问题,于此进行记录。

测试环境:E103-W01模块或E103-W01-IPX模块(可以理解成是一样的模块,区别只是有没有陶瓷天线)、E103-W01测试底板、带有WIFI功能电脑、路由器或带有热点功能的手机。
开发环境:Windows平台、QT+MSVC2019。

将串口通讯的底层逻辑封装成了SerialInterface类(参考文章https://blog.csdn.net/LX520143/article/details/106448817,并在原作基础上做了部分改进),代码提供在文章末尾。

问题一:
    串口回复数据乱码

在这里插入图片描述
解决方案:
    排查串口的打开方式是否正确,使用串口工具打开串口并进行数据交互,查看是否存在乱码,进而比较自己的串口打开方式与串口工具是否不同。
    使用XCOM工具打开串口发现能正常交互,因此与自己的代码中串口的打开方式进行比较,发现是代码中奇偶校验方式错误,应当由ODDPARITY修改为NOPARITY
在这里插入图片描述
在这里插入图片描述
修正后乱码消失,能够正常进行交互。
在这里插入图片描述

问题二:
    第一次串口通讯收到的回复总是失败,此后的每次消息收到的回复总是上一次交互的回复。

在这里插入图片描述
解决方案:
    遇到该状况应该首先考虑通信的延时问题,是否是因为接收函数运行过快,总是未能等到WIFI模块返回处理结果就提前返回,导致了第一次交互无法接收到数据,此后的交互总是从数据交互的缓存中取到上次通讯返回的结果。
    因此考虑为串口通讯设置延迟,参考如下代码:
其中com是SerialInterface类,是自定义的串口通讯类(下文代码中会提供);COMMTIMEOUTS是windows延迟设定用数据结构。同时需要注意,超时的设定需要在串口建立连接之后,否则会无法生效。

 COMMTIMEOUTS TimeOuts;
  // 设定读超时
  TimeOuts.ReadIntervalTimeout = 100;
  TimeOuts.ReadTotalTimeoutMultiplier = 100;
  TimeOuts.ReadTotalTimeoutConstant = 100;

  // 设定写超时
  TimeOuts.WriteTotalTimeoutMultiplier = 1000;
  TimeOuts.WriteTotalTimeoutConstant = 50000;
  com.setTimeouts(TimeOuts);

值得注意的是: 需要设置足够长的读超时,如果超时时间设置过短,可能会出现如下图的,在一次交互中消息返回不全,会在下次通讯中混合无序返回的情况。
在这里插入图片描述

serialinterface.cpp文件如下:

#include "SerialInterface.h"
#include <iostream>

/*******************************************************************
* 名称: openSyn
* 功能: 同步方式打开串口,并配置默认信息
* 参数:
    serial_name:串口名称
    baud_rate  :波特率,取值如下
        ......
        CBR_9600    9600bps
        CBR_14400   14400bps
        ......
    parity     :校验方式
        EVENPARITY  偶校验
        MARKPARITY  标号校验
        NOPARITY    无校验
        ODDPARITY   奇校验
        SPACEPARITY 空格校验
    byte_size  :数据位大小
        4,5,6,7,8
    stop_bits  :停止位
        ONESTOPBIT      1个停止位
        ONE5STOPBITS    1.5个停止位
        TWOSTOPBITS     2个停止位
* 返回: 正确返回为ture,错误返回为false
*******************************************************************/
bool SerialInterface::openSyn(string serial_name, int baud_rate,
                              unsigned char parity, unsigned char byte_size,
                              unsigned char stop_bits) {
  if (!openSyn(serial_name))
    return false;
  DCB dcb;

  if (false == GetCommState(hCom, &dcb)) // 获得当前串口的配置信息
  {
    setSerialLastError("SerialInterface::open() : GetCommState Error");
    return false;
  }

  dcb.DCBlength = sizeof(DCB);
  dcb.BaudRate = baud_rate;
  dcb.Parity = parity;
  dcb.ByteSize = byte_size;
  dcb.StopBits = stop_bits;

  if (false == SetCommState(hCom, &dcb)) // 用DCB结构重新配置串行端口信息
  {
    setSerialLastError("SerialInterface::open() : SetCommState Error");
    return false;
  }

  // 超时处理
  COMMTIMEOUTS timeouts;
  timeouts.ReadIntervalTimeout = MAXDWORD; // 读间隔超时
  // 把间隔超时设为最大,把总超时设为0将导致ReadFile立即返回并完成操作

  timeouts.ReadTotalTimeoutMultiplier = 0;   // 读时间系数
  timeouts.ReadTotalTimeoutConstant = 0;     // 读时间常量
  timeouts.WriteTotalTimeoutMultiplier = 50; // 写时间系数
  timeouts.WriteTotalTimeoutConstant = 2000; // 写时间常量
  // 总的读/写超时时间 = Read(Write)TotalTimeoutMultiplier x 要读/写的字节数 +
  // Read(Write)TotalTimeoutConstant.
  if (false == SetCommTimeouts(hCom, &timeouts)) {
    setSerialLastError("SerialInterface::open() : SetCommTimeouts Error");
    return false;
  }

  // 清空缓冲区,为读写串口做准备
  if (false == PurgeComm(hCom, PURGE_TXCLEAR | PURGE_RXCLEAR | PURGE_TXABORT |
                                   PURGE_RXABORT)) {
    setSerialLastError("SerialInterface::open() : PurgeComm Error");
    return false;
  }
  return true;
}
/*******************************************************************
* 名称: openSyn
* 功能: 同步方式打开串口(需自己配置串口参数)
* 参数:
    serial_name:串口名称
* 返回: 正确返回为ture,错误返回为false
*******************************************************************/
bool SerialInterface::openSyn(string serial_name) {
  hCom = CreateFileA(
      serial_name.data(), // 普通文件名或者设备文件名,这里是串口名
      GENERIC_READ | GENERIC_WRITE, // 访问模式,读和写
      0,                            // 共享模式,独占模式
      NULL,          // 指向安全属性的指针,不使用,传NULL
      OPEN_EXISTING, // 如何创建,在串口中必须设置为OPEN_EXISTING。表示不能创建新端口只能打开已有的端口。
      FILE_ATTRIBUTE_NORMAL, // 文件属性,使用默认属性FILE_ATTRIBUTE_NORMAL。
      NULL // 用于复制文件句柄,通常这个参数设置为NULL,为空表示不使用模板
  );

  if (INVALID_HANDLE_VALUE == hCom) // 出错判断
  {
    hCom = NULL;
    setSerialLastError("open():CreateFileA Error!");
    return false;
  }
  return true;
}
/*******************************************************************
 * 名称: closeComm
 * 功能: 关闭串口
 * 参数: 无
 * 返回: 正确返回为ture,错误返回为false
 *******************************************************************/
void SerialInterface::closeComm() {
  if (NULL == hCom)
    return;
  CloseHandle(hCom);
  hCom = NULL;
}
/*******************************************************************
 * 名称: closeComm
 * 功能: 判断串口是否打开
 * 参数: 无
 * 返回: 正确返回为ture,错误返回为false
 *******************************************************************/
bool SerialInterface::isOpened() { return NULL == hCom ? false : true; }

DWORD SerialInterface::readData(char *buffer, int length) {
  DWORD writeSize = 0;

  bool ret = false;

  ret = ReadFile(hCom, buffer, length, &writeSize, NULL);

  if (false == ret)
    return 0;

  return writeSize;
}

DWORD SerialInterface::readStr(string *str) {
  DWORD readSize = 1024;
  unsigned char buffer[1024];

  bool ret = false;

  ret = ReadFile(hCom, buffer, readSize, &readSize, NULL);

  std::cout << "readSize= " << readSize << std::endl;

  if (false == ret || 0 == readSize)
    return 0;

  for (int i = 0; i < readSize; i++) {
    *str += buffer[i];
  }

  // std::cout << "str= " << *str << std::endl;

  return readSize;
}

DWORD SerialInterface::writeData(char *buffer, int length) {
  DWORD writeSize = 0;

  bool ret = false;

  ret = WriteFile(hCom, buffer, length, &writeSize, NULL);

  if (false == ret)
    return 0;

  return writeSize;
}

DWORD SerialInterface::writeStr(string str) {
  bool ret = false;

  DWORD writeSize = 0;

  ret = WriteFile(hCom, str.data(), str.length(), &writeSize, NULL);

  if (0 == ret) {
    last_error = "Error By writeStr(string str)";
    return 0;
  }

  last_error = "";
  return writeSize;
}
/*******************************************************************
* 名称: setTimeouts
* 功能: 设置超时
* 参数:
    timeouts:超时配置的COMMTIMEOUTS结构体
* 返回: 正确返回为ture,错误返回为false
*******************************************************************/
bool SerialInterface::setTimeouts(COMMTIMEOUTS &timeouts) {

  if (false == SetCommTimeouts(hCom, &timeouts)) {
    setSerialLastError(
        "SerialInterface::setTimeouts() : SetCommTimeouts Error");
    return false;
  }
  return true;
}
/*******************************************************************
* 名称: setDCB
* 功能: 设置串口信息
* 参数:
    dcb:串口信息配置的DCB结构体
* 返回: 正确返回为ture,错误返回为false
*******************************************************************/
bool SerialInterface::setDCB(DCB &dcb) {
  if (false == SetCommState(hCom, &dcb)) {
    setSerialLastError("SerialInterface::setDCB() : SetCommState Error");
    return false;
  }
  return true;
}
/*******************************************************************
* 名称: purgeBuff
* 功能: 刷新缓冲区
* 参数:
    flags:需要完成的操作,取值如下
        PURGE_RXABORT
终止所有未完成的重叠读取操作并立即返回,即使读取操作尚未完成。 PURGE_RXCLEAR
清除输入缓冲区(如果设备驱动程序有一个)。 PURGE_TXABORT
终止所有未完成的重叠写操作并立即返回,即使写操作尚未完成。 PURGE_TXCLEAR
清除输出缓冲区(如果设备驱动程序有一个)。
* 返回: 正确返回为ture,错误返回为false
* 提示:PurgeComm()函数可以在读写操作的同时,清空缓冲区。当应用程序在读写操作时
调用PurgeComm()函数,不能保证缓冲区内的所有字符都被发送。
*******************************************************************/
bool SerialInterface::purgeBuff(DWORD flags) {

  if (false == PurgeComm(hCom, flags)) {
    setSerialLastError("SerialInterface::purgeBuff() : PurgeComm Error");
    return false;
  }
  return true;
}
/*******************************************************************
 * 名称:flushBuff
 * 功能:刷新缓冲区
 * 参数:无
 * 返回:正确返回为ture,错误返回为false
 * 提示:该函数只受流量控制的支配,不受超时控制的支配,它在所有的写操作完成后才返回。
 *******************************************************************/
bool SerialInterface::flushBuff() {
  if (FlushFileBuffers(hCom)) {
    setSerialLastError("SerialInterface::flushBuff() : FlushFileBuffers Error");
    return false;
  }
  return true;
}
/*******************************************************************
* 名称: setBufferSize
* 功能: 设置推荐的缓冲大小
* 参数:
    inputBuff:输入缓冲大小
    outBuffer:输出缓冲大小
* 返回: 正确返回为ture,错误返回为false
*******************************************************************/
bool SerialInterface::setBufferSize(DWORD inputBuff, DWORD outBuffer) {
  if (inputBuff <= 0 || outBuffer <= 0)
    return false;

  return SetupComm(hCom, inputBuff, outBuffer);
}
/*******************************************************************
 * 名称: getSerialLastError
 * 功能: 得到最后一次失败的错误信息
 * 参数: 无
 * 返回: 数据类型:string,错误信息
 *******************************************************************/
string SerialInterface::getSerialLastError() { return last_error; }

void SerialInterface::setSerialLastError(string error_msg) {
  last_error = error_msg;
}
void SerialInterface::clearSerialLastError() { last_error = ""; }
SerialInterface::SerialInterface() { hCom = NULL; }
SerialInterface::~SerialInterface() {}

serialinterface.h文件如下:

#ifndef __SerialInterface_H_
#define __SerialInterface_H_
#include <Windows.h>
#include <string>
using namespace std;

class SerialInterface
{
private:
    /* data */
    HANDLE hCom;
    string last_error;
public:
    SerialInterface();
    ~SerialInterface();
public:
    //同步方式打开串口,并配置默认信息
    bool openSyn(string serial_name,int baud_rate,unsigned char parity, unsigned char byte_size, unsigned char stop_bits);
    //同步方式打开串口(需自己配置串口参数)
    bool openSyn(string serial_name);

    //设置推荐的缓冲大小
    bool setBufferSize(DWORD inputBuff,DWORD outBuffer);
    //设置超时
    bool setTimeouts(COMMTIMEOUTS &timeouts);
    //设置串口信息
    bool setDCB(DCB& dcb);

    //刷新缓冲区
    bool purgeBuff(DWORD flags);
    //刷新缓冲区
    bool flushBuff();
    //写数据
    DWORD writeData(char *buffer,int length);
    //读数据
    DWORD readData(char *buffer,int length);
    //读字符串
    DWORD readStr(string *str);
    //写字符串
    DWORD writeStr(string str);
    //关闭串口
    void closeComm();
    //判断串口是否打开
    bool isOpened();
    //得到最后一次失败的错误信息
    string getSerialLastError();
private:
    //设置最后一次的错误信息
    void setSerialLastError(string error_msg);
    //清chu最后一次的错误信息
    void clearSerialLastError();
};

#endif /*__SerialInterface_H_*/

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值