很多硬件传感器需要用到如下的通信协议
串口(上层应用的串口代码编写,不是底层的串口代码编写)
SPI(外设串口通信,没有直接跟CPU通信,效果好)
IIC
CAN
USB
Uart
I2C
串口蓝牙,串口wifi
开发板跟电脑
常见的硬件校验算法有哪些?算法原理是怎么样?
CRC校验 --》循环冗余校验
哈希校验
QT中的串口编程(既可以在window上运行,也能在ARM平台,linux平台都能运行)
linux中的串口编程(只能在ARM平台,linux平台运行)
波特率: 串口传输数据的速度 115200表示每一秒钟传输115200个比特位
数据位:串口传输一帧数据的位数
奇偶校验:校验--》检查数据收发是否发生了错误
奇校验
偶校验
停止位:标记数据的结尾
流控: 当数据的接收端无法收到数据的时候,会通知发送端停止发送
类和方法
添加的库: serialport
QSerialPortInfo //获取当前系统中所有的串口信息
QSerialPort //表示串口
第一步:获取当前系统所有的串口信息
[static] QList<QSerialPortInfo> QSerialPortInfo::availablePorts()
返回值: QList存放的就是所有的串口信息
QString QSerialPortInfo::description() const
返回:串口的描述信息
QString QSerialPortInfo::portName() const
返回:串口的名字
第二步:创建QSerialPort 串口对象,配置串口的参数
QSerialPort::QSerialPort(const QString &name)
参数:name --》串口的名字
bool setBaudRate(qint32 baudRate)
参数:baudRate --》你要设置的波特率 QSerialPort::Baud115200
bool setDataBits(QSerialPort::DataBits dataBits)
参数:dataBits --》QSerialPort::Data8 8位数据位
bool setParity(QSerialPort::Parity parity)
参数:parity --》QSerialPort::NoParity 无奇偶校验
QSerialPort::EvenParity 偶校验
QSerialPort::OddParity 奇校验
bool setStopBits(QSerialPort::StopBits stopBits)
参数:stopBits --》QSerialPort::OneStop 1位停止位
QSerialPort::OneAndHalfStop 1.5位停止位
bool setFlowControl(QSerialPort::FlowControl flowControl)
参数:flowControl --》QSerialPort::NoFlowControl 无流控
QSerialPort::HardwareControl 硬件流控
QSerialPort::SoftwareControl 软件流控
第三步:打开串口
bool QSerialPort::open(QIODevice::OpenMode mode)
第四步:读写串口数据
读串口--》接收对方从串口发送过来的数据
[signal] void QIODevice::readyRead() //关联这个信号,在槽函数中读取数据
read/readAll
写串口--》你把数据从串口发送给对方
write
第五步:关闭串口
void QSerialPort::close()
答案:认真阅读传感器厂家提供的使用说明书–》清楚地告诉你串口该如何配置
struct termios
{
//里面都是标志位,用来设置串口参数
c_cflag //控制模式标志位
}
第一步:打开串口的驱动
连接电脑 --》/dev/ttySAC0
右上角第一排 --》/dev/ttySAC1
右上角第二排 --》/dev/ttySAC2
右上角第三排 --》/dev/ttySAC3
open()
第二步:配置串口
#include
#include
int tcgetattr(int fd, struct termios *termios_p); //获取当前串口的配置信息
参数: fd --》串口的文件描述符
termios_p --》结构体指针,用来保存串口配置信息
struct termios myios;
bzero(&myios,sizeof(myios));
myios.c_cflag |= CLOCAL | CREAD;
void cfmakeraw(struct termios *termios_p);
波特率:
int cfsetispeed(struct termios *termios_p, speed_t speed);
int cfsetospeed(struct termios *termios_p, speed_t speed);
参数:speed --》你要设置的波特率B115200 B数字(就是波特率)
数据位:
myios.c_cflag &= ~CSIZE; /* 用数据位掩码清空数据位设置 */
myios.c_cflag |= CS8; //CS5 CS6 CS7 CS8
奇偶校验:
奇校验
myios.c_cflag |= (PARODD | PARENB);
myios.c_iflag |= INPCK;
偶校验
myios.c_cflag |= PARENB;
myios.c_cflag &= ~PARODD; /* 清除奇校验标志,则配置为偶校验*/
myios.c_iflag |= INPCK;
无校验
myios.c_cflag &= ~PARENB; //使能 --》开启 失能 --》关闭
停止位
myios.c_cflag &= ~CSTOPB; 1位停止位
myios.c_cflag |= CSTOPB; 2位停止位
设置最少读取的字符数量和最长等待时间
myiosc_cc[VTIME] = 0;
myios.c_cc[VMIN] = 4;
让刚才的设置生效(激活配置)
int tcflush(int fd, int queue_selector);]
参数: fd --》串口的文件描述符
queue_selector --》TCIOFLUSH 刷新串口的输入输出缓冲区
tcsetattr(int fd, int optional_actions, const struct termios*termios_p);
参数: fd --》串口的文件描述符
optional_actions --》TCSANOW 设置立即生效
第三步:收发数据
发送 write()
接收 read()
第四步:关闭串口
close()
通信协议:硬件传感器依照什么样的数据格式去发送/接收数据
类似于tcp通信发送字符串/发送表情包
A命令(请求): 激活RFID读卡模块,使之能够对外发射电磁场,当有卡靠近模块的时候,模块能够感应到卡的存在
char abuf[7];
abuf[0]=0x07; //帧长
abuf[1]=0x02; //ISO14443A
abuf[2]='A'; // A命令
abuf[3]=0x01; //信息长度
abuf[4]=0x52; //ALL模式
abuf[5]=校验和;
abuf[6]=0x03; //帧结束标记
//发送A命令给模块
write(fd,abuf,7); //串口发送A命令给RFID读卡模块 主机--》从机 命令
//接收模块回复的应答信息
char rbuf[8];
read(fd,rbuf,8); 从机--》主机 状态
//判断接收的应答信息是否正确
if(rbuf[2]==0x00) //应答成功
printf("A命令发送成功了!\n");
else
printf("A命令没有发送成功了!\n");
B命令(防碰撞): 防止多张卡同时进入磁场范围,出现读写冲突的问题(只会挑选其中一张来读写)
char bbuf[8];
bbuf[0]=0x08; //帧长
bbuf[1]=0x02; //ISO14443A
bbuf[2]='B'; // B命令
bbuf[3]=0x02; //信息长度
bbuf[4]=0x93; //一级防碰撞
bbuf[5]=0x00;
bbuf[6]=校验和;
bbuf[7]=0x03; //帧结束标记
//发送B命令给模块
write(fd,bbuf,8); //串口发送B命令给RFID读卡模块 主机--》从机 命令
//接收模块回复的应答信息
char rbuf[10];
read(fd,rbuf,10); 从机--》主机 状态
//判断接收的应答信息是否正确
if(rbuf[2]==0x00) //应答成功
printf("B命令发送成功了,同时得到了序列号!\n"); //卡的序列号 rbuf[4]rbuf[5]rbuf[6]rbuf[7]
else
printf("B命令没有发送成功了!\n");
参考代码:
第一个:以非阻塞的方式打开串口(无论串口是否有数据可读,read去读取串口数据的时候都不会阻塞)
fd = open("/dev/ttySAC1", O_RDWR | O_NOCTTY | O_NONBLOCK);
第二个:使用了多路复用监测串口是否有数据可读(可读就调用read去读取模块的响应信息)
第三个:延时–》while(1)执行速度太快了,串口的读写速度跟不上,所以适当延时一下
#include "mainwindow.h"
#include
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include
#include
#include
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
void on_openbt_clicked();
void on_portbox_activated(const QString &arg1);
void on_bardbox_activated(const QString &arg1);
void on_databox_activated(const QString &arg1);
void on_jobox_activated(const QString &arg1);
void on_stopbox_activated(const QString &arg1);
private:
Ui::MainWindow *ui;
QString portstr; //端口名称
QString bardstr; //波特率
QString datastr; //数据位
QString jostr; //奇偶校验
QString stopstr; //停止位
QSerialPort *myport;
};
#endif // MAINWINDOW_H
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
//获取当前系统所有可以使用的串口信息
QList<QSerialPortInfo> mylist=QSerialPortInfo::availablePorts();
for(auto &x:mylist)
ui->portbox->addItem(x.portName());
//初始化波特率
ui->bardbox->addItem("9600");
ui->bardbox->addItem("115200");
//初始化数据位
ui->databox->addItem("5");
ui->databox->addItem("6");
ui->databox->addItem("7");
ui->databox->addItem("8");
//初始化停止位
ui->stopbox->addItem("1");
ui->stopbox->addItem("1.5");
ui->stopbox->addItem("2");
//初始化奇偶校验
ui->jobox->addItem("none");
ui->jobox->addItem("odd");
ui->jobox->addItem("even");
}
MainWindow::~MainWindow()
{
delete ui;
}
//打开串口
void MainWindow::on_openbt_clicked()
{
//初始化串口对象
myport=new QSerialPort(portstr);
//获取串口配置信息
if(bardstr=="115200")
myport->setBaudRate(QSerialPort::Baud115200);
if(datastr=="8")
myport->setDataBits(QSerialPort::Data8);
if(jostr=="none")
myport->setParity(QSerialPort::NoParity);
if(stopstr=="1")
myport->setStopBits(QSerialPort::OneStop);
myport->setFlowControl(QSerialPort::NoFlowControl);
//打开串口
myport->open(QIODevice::ReadWrite);
}
void MainWindow::on_portbox_activated(const QString &arg1)
{
portstr=arg1;
}
void MainWindow::on_bardbox_activated(const QString &arg1)
{
bardstr=arg1;
}
void MainWindow::on_databox_activated(const QString &arg1)
{
datastr=arg1;
}
void MainWindow::on_jobox_activated(const QString &arg1)
{
jostr=arg1;
}
void MainWindow::on_stopbox_activated(const QString &arg1)
{
stopstr=arg1;
}
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>600</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralwidget">
<widget class="QComboBox" name="portbox">
<property name="geometry">
<rect>
<x>100</x>
<y>30</y>
<width>161</width>
<height>31</height>
</rect>
</property>
</widget>
<widget class="QLabel" name="label">
<property name="geometry">
<rect>
<x>20</x>
<y>40</y>
<width>31</width>
<height>16</height>
</rect>
</property>
<property name="text">
<string>端口</string>
</property>
</widget>
<widget class="QLabel" name="label_2">
<property name="geometry">
<rect>
<x>20</x>
<y>76</y>
<width>51</width>
<height>20</height>
</rect>
</property>
<property name="text">
<string>波特率</string>
</property>
</widget>
<widget class="QComboBox" name="bardbox">
<property name="geometry">
<rect>
<x>100</x>
<y>70</y>
<width>161</width>
<height>31</height>
</rect>
</property>
</widget>
<widget class="QComboBox" name="databox">
<property name="geometry">
<rect>
<x>100</x>
<y>120</y>
<width>161</width>
<height>31</height>
</rect>
</property>
</widget>
<widget class="QComboBox" name="jobox">
<property name="geometry">
<rect>
<x>100</x>
<y>170</y>
<width>161</width>
<height>31</height>
</rect>
</property>
</widget>
<widget class="QLabel" name="label_3">
<property name="geometry">
<rect>
<x>20</x>
<y>180</y>
<width>81</width>
<height>16</height>
</rect>
</property>
<property name="text">
<string>奇偶校验</string>
</property>
</widget>
<widget class="QLabel" name="label_4">
<property name="geometry">
<rect>
<x>20</x>
<y>230</y>
<width>61</width>
<height>16</height>
</rect>
</property>
<property name="text">
<string>停止位</string>
</property>
</widget>
<widget class="QComboBox" name="stopbox">
<property name="geometry">
<rect>
<x>100</x>
<y>220</y>
<width>161</width>
<height>31</height>
</rect>
</property>
</widget>
<widget class="QLabel" name="label_5">
<property name="geometry">
<rect>
<x>20</x>
<y>130</y>
<width>51</width>
<height>16</height>
</rect>
</property>
<property name="text">
<string>数据位</string>
</property>
</widget>
<widget class="QCheckBox" name="checkBox">
<property name="geometry">
<rect>
<x>370</x>
<y>50</y>
<width>71</width>
<height>16</height>
</rect>
</property>
<property name="text">
<string>DTR/DSR</string>
</property>
</widget>
<widget class="QLabel" name="label_6">
<property name="geometry">
<rect>
<x>370</x>
<y>20</y>
<width>31</width>
<height>16</height>
</rect>
</property>
<property name="text">
<string>流控</string>
</property>
</widget>
<widget class="QCheckBox" name="checkBox_2">
<property name="geometry">
<rect>
<x>370</x>
<y>80</y>
<width>71</width>
<height>16</height>
</rect>
</property>
<property name="text">
<string>RTS/CTS</string>
</property>
</widget>
<widget class="QCheckBox" name="checkBox_3">
<property name="geometry">
<rect>
<x>370</x>
<y>110</y>
<width>71</width>
<height>16</height>
</rect>
</property>
<property name="text">
<string>XON/XOFF</string>
</property>
</widget>
<widget class="QPushButton" name="openbt">
<property name="geometry">
<rect>
<x>20</x>
<y>280</y>
<width>241</width>
<height>41</height>
</rect>
</property>
<property name="text">
<string>打开串口</string>
</property>
</widget>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>22</height>
</rect>
</property>
</widget>
<widget class="QStatusBar" name="statusbar"/>
</widget>
<resources/>
<connections/>
</ui>
串口概述
常见的数据通信的基本方式可分为并行通信与串行通信两种。
并行通信是指:利用多条数据传输线将一个字数据的各比特位同时传送。
它的特点是传输速度快,适用于传输距离短且传输速度较高的通信。
串行通信是指:利用一条传输线将数据以比特位为单位顺序传送。
特点是通信 线路简单,利用简单的线缆就可实现通信,降低成本,适用于传输距离长且传输速度较慢的通信。
串口设置详解
串口参数的配置读者在配置SecurCRT也已经接触过,一般包括波特率、起始位比特数、数据位比特数、停止位比特数和流控模式。在此,可以将其配置 为波特率 115200、起始位 1b、数据位 8b、停止位 1b 和无流控模式。
串口的设置主要是设置 struct termios 结构体的各成员值
#include<termios.h>
struct termios
{
unsigned short c_iflag; /* 输入模式标志 */
unsigned short c_oflag; /* 输出模式标志 */
unsigned short c_cflag; /* 控制模式标志*/
unsigned short c_lflag; /* 本地模式标志 */
unsigned char c_line; /* 线路规程 */
unsigned char c_cc[NCC]; /* 控制特性 */
speed_t c_ispeed; /* 输入速度 */
speed_t c_ospeed; /* 输出速度 */
};
termios 是在 POSIX 规范中定义的标准接口,表示终端设备(包括虚拟终端、串 口等)。口是一种终端设备,一般通过终端编程接口对其进行配置和控制。在具体讲 解串口相关编程之前,先了解一下终端相关知识。
终端 有 3 种 工 作 模 式 ,分 别为 规 范模 式 ( canonical mode )、 非规 范模 式
(non-canonical mode)和原始模式(raw mode)。
在 非规 范模 式 下, 对 参 数 MIN ( c_cc[VMIN] )和 TIME
(c_cc[VTIME])的设置决定 read()函数的调用方式。设置可以有 4 种不同的情况。
MIN = 0 和 TIME = 0:read()函数立即返回。若有可读数据,则读取数据并 返回被读取的字节数,否则读取失败并返回 0。
MIN > 0 和 TIME = 0:read()函数会被阻塞直到 MIN 个字节数据可被读取。
MIN = 0 和 TIME > 0:只要有数据可读或者经过 TIME 个十分之一秒的时间, read()函数则立即返回,返回值为被读取的字节数。如果超时并且未读到数 据,则 read()函数返回 0。
MIN > 0 和 TIME > 0:当有 MIN 个字节可读或者两个输入字符之间的时间 间隔超过 TIME 个十分之一秒时,read()函数才返回。因为在输入第一个字符 之后系统才会启动定时器,所以在这种情况下,read()函数至少读取一个字 节之后才返回。
按照严格意义来讲,原始模式是一种特殊的非规范模式。在原始模式下,所有的 输入数据以字节为单位被处理。在这个模式下,终端是不可回显的,而且所有特定的 终端输入/输出控制处理不可用。通过调用 cfmakeraw()函数可以将终端设置为原始模 式
第一期 QT上位机安装与新建项目教程
第二期 QT平台使用规则和代码逻辑学习
第三期 QT中信号与槽和字符串QString的使用
第四期 QT组件布局管理器和多界面传参跳转
第五期 QT消息盒子-对话框-定时器-日期和时间
第六期 QTmplayer视频播放器+列表框+交叉编译QT程序+QT控制硬件+多进程
第七期 QTwindows打包QT工程+多线程QThread+菜单栏+打包QT程序
第八期 QT网络编程TCP/IP/UDP+Http和JSON解析+qt事件软键盘
第九期 QT音视频Linux中的V4L2摄像头编程
第十期 QT容器及摄像头配合多线程、定时器显示jpeg图片
第十一期 QT安装和使用alsa库和jpeg库实现音视频录制
第十二期 QT实现计划及函数指针+指针函数+函数数组指针+笔试题分析+软键盘的隐藏和显示
第十三期 QT嵌入式数据库sqlite3介绍移植教程和使用SQL语句【插入,查询,删除,修改】
第十四期 QT QSqlite3数据库操作【增删改查精髓】