linux 串口接收不到0x11, 0x0d, 0x13

linux 环境下读取串口, 接收不到0x11, 0x0d, 0x13等数据的解决办法


    // add by dern
    options.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);


即可解决特殊字符丢失问题。


网上许多流行的linux串口编程的版本中都没对c_iflag(termios成员变量)这个变量进行有效的设置,这样传送ASCII码时没什么问题,但传送二进制数据时遇到0x0d,0x11和0x13却会被丢掉。不用说也知道,这几个肯定是特殊字符,被用作特殊控制了。关掉ICRNL和IXON选项即可解决。

c_iflag &= ~(ICRNL | IXON);

0x0d 回车符CR

0x11 ^Q VSTART字符

0x13 ^S VSTOP字符

ICRNL 将输入的CR转换为NL  

IXON 使起动/停止输出控制流起作用

在《UNIX环境高级编程第二版》第18章第11小节看到把终端I/O设置为原始模式(串口通讯就是终端I/O的原始模式)时输入属性设置为

term.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);

屏蔽了许多属性,怪不得有人说如果是使用串口通讯c_iflag和c_oflag都设置为0就行了!


以下是我的设置的一些重要的串口属性

term.c_cflag |= CLOCAL | CREAD;

term.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);

term.c_oflag &= ~OPOST;

term.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);


初始化串口:
int setup_com(int fd){
    struct termios options;
    tcgetattr(fd, &options);
    /* Set the baud rates to 38400...*/
    cfsetispeed(&options, B38400);
    cfsetospeed(&options, B38400);
    /* Enable the receiver and set local mode...*/
    options.c_cflag |= (CLOCAL | CREAD);
    /* Set c_cflag options.*/
    options.c_cflag |= PARENB;
    options.c_cflag &= ~PARODD;
    options.c_cflag &= ~CSTOPB;
    options.c_cflag &= ~CSIZE;
    options.c_cflag |= CS8;   
    /* Set c_iflag input options */
    options.c_iflag &=~(IXON | IXOFF | IXANY);
    options.c_iflag &=~(INLCR | IGNCR | ICRNL);
    options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
    /* Set c_oflag output options */
    options.c_oflag &= ~OPOST;   
    /* Set the timeout options */
    options.c_cc[VMIN]  = 0;
    options.c_cc[VTIME] = 10;
    tcsetattr(fd, TCSANOW, &options);
    return 1;
}

其中:
c_cflag  : 控制选项
c_lflag  : 线选项
c_iflag  : 输入选项
c_oflag  :输出选项
c_cc    :控制字符
c_ispeed :输入数据波特率
c_ospeed :输出数据波特率
c_cflag: CLOCAL 本地模式,不改变端口的所有者
         CREAD  表示使能数据接收器
         PARENB  表示偶校验
         PARODD 表示奇校验
CSTOPB  使用两个停止位
CSIZE    对数据的bit使用掩码
CS8      数据宽度是8bit
c_lflag:  ICANON 使能规范输入,否则使用原始数据(本文使用)
ECHO    回送(echo)输入数据
ECHOE   回送擦除字符
ISIG      使能SIGINTR,SIGSUSP, SIGDSUSP和 SIGQUIT 信号
c_iflag:  IXON     使能输出软件控制
         IXOFF    使能输入软件控制
         IXANY    允许任何字符再次开启数据流
         INLCR    把字符NL(0A)映射到CR(0D)
         IGNCR    忽略字符CR(0D)
       ICRNL    把CR(0D)映射成字符NR(0A)
    c_oflag: OPOST  输出后处理,如果不设置表示原始数据(本文使用原始数据raw)
c_cc[VMIN]:  最少可读数据
c_cc[VTIME]: 等待数据时间(10秒的倍数)

初始化完后就可以直接对串口像普通文件一样使用文件操作函数read和write进行读写操作了,read和write函数返回的是成功收到(送出)的字节数,如果成功那么返回值大于零,很容易判断;
不过要注意的是:linux下对串口的read是半阻塞式的,不设置好VMIN和VTIME的话,会导致执行read时发生明显的阻塞现象,上面的VMIN0和VTIME10设置成只要串口收到数据read就立即返回数据,如果一直没收到数据,则10秒后read超时才返回0值;如果VMIN和VTIME不设置的话,缺省值好像是60秒后read才能超时返回,在60秒内如果串口没收到数据将一直阻塞,看上去程序像死掉一样;
如果需要read无论有没有收到数据都立即返回,可以初始化成不延迟方式:
fcntl(fd, F_SETFL, FNDELAY);
这样执行到read时如果收到数据则返回成功收到的字节数,如果没收到数据则立即返回0,不会产生阻塞现象。


http://blog.csdn.net/yangshishu/article/details/44957303

http://taiwanxia.blog.163.com/blog/static/483307302010212111949866/




  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
在Qt中向串口发送数据并处理接收到的数据可以通过QSerialPort类的信号和槽机制来实现。以下是一个示例代码,演示如何向串口发送数据并处理接收到的数据: ```c++ #include <QCoreApplication> #include <QSerialPort> #include <QSerialPortInfo> class SerialPort : public QObject { Q_OBJECT public: SerialPort(QObject *parent = nullptr) : QObject(parent) { serialPort.setPortName("COM1"); // 设置串口号 serialPort.setBaudRate(QSerialPort::Baud9600); // 设置波特率 serialPort.setDataBits(QSerialPort::Data8); // 设置数据位 serialPort.setParity(QSerialPort::NoParity); // 设置校验位 serialPort.setStopBits(QSerialPort::OneStop); // 设置停止位 connect(&serialPort, &QSerialPort::readyRead, this, &SerialPort::onReadyRead); } bool open() { if(serialPort.open(QIODevice::ReadWrite)) { return true; } return false; } void close() { serialPort.close(); } void write(const QByteArray &data) { serialPort.write(data); } signals: void receivedData(const QByteArray &data); private slots: void onReadyRead() { QByteArray data = serialPort.readAll(); for(int i = 0; i < data.size(); i++) { if(data.at(i) == (char)0x02 && i + 2 < data.size()) { if(data.at(i + 1) == (char)0x71 && data.at(i + 2) == (char)0x03) { QByteArray receivedData; receivedData.append(data.at(i)); receivedData.append(data.at(i + 1)); receivedData.append(data.at(i + 2)); emit receivedData(receivedData); } } } } private: QSerialPort serialPort; }; int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); // 打开串口 SerialPort serialPort; if(!serialPort.open()) { qDebug() << "Failed to open serial port!"; return -1; } // 发送数据 QByteArray sendData; sendData.append((char)0x02); sendData.append((char)0x31); sendData.append((char)0x30); serialPort.write(sendData); // 处理接收到的数据 QObject::connect(&serialPort, &SerialPort::receivedData, [](const QByteArray &data) { qDebug() << "Received data:" << data.toHex(); }); return a.exec(); } ``` 在上面的代码中,首先创建了一个SerialPort类,其中包含了打开串口、关闭串口、发送数据和处理接收到的数据等函数。在SerialPort类的构造函数中,连接了QSerialPort的readyRead信号和SerialPort类的onReadyRead槽函数,当有数据可读时,onReadyRead函数将被调用。 在发送数据时,创建一个QByteArray对象sendData,并使用append()函数向其中添加0x02、0x31和0x30三个字节的数据。然后,调用SerialPort类的write()函数将sendData中的数据发送到串口上。 在处理接收到的数据时,当读取到0x02字节时,判断其后面是否有两个字节分别为0x71和0x03,如果是,将接收到的数据发送出去,通过SerialPort类的receivedData信号发送。在main函数中,使用QObject::connect()函数连接SerialPort类的receivedData信号,并在lambda表达式中打印接收到的数据。 需要注意的是,串口的参数设置必须与接收数据的设备的参数设置相匹配,否则可能会发送失败或接收数据错误。另外,在实际应用中,可能需要对接收到的数据进行更加复杂的处理。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值