精华内容
下载资源
问答
  • 用到串口读取数据,要求数据

        用到串口读取数据,要求数据要完整,即是一个数据帧,在用read读的时候会出现帧解析错误,以为是数据传输错误,尝试修改串口参数,没有解决问题,后来发现read(fd,buf,len)函数(非阻塞情况)并不会在读满len长度后返回,一般是读取比len小的长度就返回,才导致数据解析出错,所以解决办法是自己写一个读取指定数据长度的“read函数”。

    代码如下:

    int read_n_bytes(int fd,char* buf,int n)
    {

    /*read_len用来记录每次读到的长度,len用来记录当前已经读取的长度*/
        int read_len = 0,len = 0;

    /*read_buf用来存储读到的数据*/
        char* read_buf = buf;
        while(len < n)
        {
            if((read_len = read(fd,read_buf,n)) > 0)
            {

                len += read_len;
                read_buf += read_len;
                read_len = 0;
            }

    //休眠时间视情况而定,可以参考波特率来确定数量级,也可以将usleep放到else语句里面
            usleep(5);

        }
        return n;
    }


    展开全文
  • QT--串口读取数据并展示实时波形图

    多人点赞 热门讨论 2021-06-04 13:48:39
    串口读取数据

    1.串口读取数据

    与下位机,单片机通信少不了使用串口进行通信,Qt 也提供了串口通信的类。

    使用的时候在 pro 添加这句导入模块

    QT += serialport

    1.连接串口 第一步就是 要先获取到 可连接的所有的串口的名字

    获取到串口名字列表以后,我们需要选择一个需要连接的 (自行根据选择)

    foreach(const QSerialPortInfo &info, QSerialPortInfo::availablePorts())
        {
            QSerialPort serial;
            serial.setPort(info);
            if (serial.open(QIODevice::ReadWrite))
            {
                portList.push_back(serial.portName());
                serial.close();
            }
        }

    2.根据串口名字 打开串口

    进行初始化串口

    void frmserialport::inialSerialPort()         //初始化串口(串口号、波特率、数据位、停止位、流控制)
    {
        m_flag = 0;
        QList<QString> portList;
        QStringList baudList;
        QStringList parityList;
        QStringList dataBitList;
        QStringList stopBitList;
        QStringList flowList;
    
        baudList<<"9600"<<"115200"<<"460800";
        dataBitList<<"8";
        parityList<<"无";
        stopBitList<<"1";
        flowList<<"无";
    }
    void frmserialport::on_btn_Open_clicked()   //打开串口
    {
        m_pSerialPort = new QSerialPort(this);
        m_pSerialPort->setPortName(ui->cbox_PortName->currentText());           //串口名
        m_pSerialPort->setBaudRate(ui->cbox_BaudRate->currentText().toInt());   //波特率
        switch (ui->cbox_DataBit->currentIndex()) {
        case 0:
            m_pSerialPort->setDataBits(QSerialPort::Data8);      //数据位为8位
            break;
        default:
            break;
        }
    
        switch (ui->cbox_Parity->currentIndex()) {
        case 0:
            m_pSerialPort->setParity(QSerialPort::NoParity);  //无校验位
            break;
        default:
            break;
        }
    
        switch (ui->cbox_StopBit->currentIndex()) {
        case 0:
            m_pSerialPort->setStopBits(QSerialPort::OneStop);    //一位停止位
            break;
        default:
            break;
        }
    
        switch (ui->cbox_Flow->currentIndex()) {
        case 0:
            m_pSerialPort->setFlowControl(QSerialPort::NoFlowControl); //无流控制
            break;
        default:
            break;
        }
    
        if (!m_pSerialPort->open(QIODevice::ReadWrite)){
            QMessageBox::information(this,"提示","串口连接失败");
        }
        else{
            QMessageBox::information(this,"提示",ui->cbox_PortName->currentText() + tr("连接成功"));
            ui->btn_Open->setEnabled(false);
            ui->btn_Close->setEnabled(true);
            ui->btn_Send->setEnabled(true);
        }
         connect(m_pSerialPort,&QSerialPort::readyRead,this,&frmserialport::serialReadData); 
    
    

    3.发送数据和清除数据

    void frmserialport::on_btn_Send_clicked()    //发送数据
    {
        QString strMessage = ui->txt_Send->toPlainText();
        sendCMD(strMessage);
    }
    
    void frmserialport::on_btn_Clear_clicked()  //清除
    {
        if (!ui->txt_Receive->toPlainText().isEmpty()){
            ui->txt_Receive->clear();
            ui->btn_Clear->setEnabled(false);
        }
    }

    4.关闭串口

    void frmserialport::on_btn_Close_clicked()   //关闭串口
    {
        m_pSerialPort->clear();
        m_pSerialPort->close();
        delete m_pSerialPort;
        m_pSerialPort = nullptr;
        m_flag = 0;
    
        ui->btn_Open->setEnabled(true);
        ui->btn_Close->setEnabled(false);
        ui->btn_Send->setEnabled(false);
    }

    2.展示实时波形图(使用QCharts)

    1.QCharts的使用可以参考《Qt开发技术:QCharts》

    链接:Qt开发技术:QCharts

    2.想要绘制曲线图需要定义画布 , 线 , 轴 ,我的x轴是用实时时间所以要加多一个定时器

        //绘图变量和坐标
        QDateTimeAxis *axisX_aX; //用时间表示x轴
        QDateTimeAxis *axisX_aY;
        QDateTimeAxis *axisX_aZ;
        QValueAxis *axisY_aX;    //aX数据,表示y轴
        QValueAxis *axisY_aY;    //aY数据
        QValueAxis *axisY_aZ;    //aZ数据
        QTimer *timer;     //图表的计时器
        QChart *chart;
        QSplineSeries *series_aX; //数据点
        QSplineSeries *series_aY;
        QSplineSeries *series_aZ;

    3.设置数据,定义绘图

        axisX_aX->setFormat("hh:mm:ss");
        axisY_aX->setRange(-3,3);
        chart->addSeries(series_aX);
        axisX_aX->setTickCount(10);
        axisY_aX->setTickCount(10);
        axisX_aX->setTitleText("实时时间");
        axisY_aX->setTitleText("加速度A(G):");
        series_aX->setPen(QColor(Qt::red));
        axisY_aX->setLinePen(peny);
        axisX_aX->setLinePen(peny);
        chart->addAxis(axisX_aX , Qt::AlignBottom);
        chart->addAxis(axisY_aX , Qt::AlignLeft);
        series_aX->attachAxis(axisX_aX);
        series_aX->attachAxis(axisY_aX);
        QDateTime currentTime = QDateTime::currentDateTime();           //实时时间
    
        chart->axisX()->setMin(QDateTime::currentDateTime().addSecs(-60*1));
        chart->axisX()->setMax(QDateTime::currentDateTime().addMSecs(0));
    
        qsrand(QTime(0,0,0).secsTo(QTime::currentTime()));
        series_aX->append(currentTime.toMSecsSinceEpoch() , aX);   //提供x,y轴数据

     

    最后就实现啦!

     

    我是qt小白,刚开始学习qt不久,如果有问题请大家多多指教,感谢大家阅读,觉得不错的话点个赞收藏下呗!!最后列出几个我写这个项目时遇到的问题吧!

     

    1.读取到的串口数据会分段

    解决方案:串口延时接收数据

    处理思路:创建两个槽函数(SLOT),当串口有数据应答时系统会给一个 readyRead()信号,用槽函数接收一个信号,并处理。在进入到这个槽函数后设置一个定时,当定时走完会有一个timeout()信号,用另一个槽函数处理定时完成后的事情。

    这里感谢Humboldt大佬的思路。

     //connect(timer,SIGNAL(timeout()),this,SLOT(RealtimeDataSlot())); 没有延时接受数据前的槽函数
        
    connect(timer,SIGNAL(timeout()),this,SLOT(DrawLine()));
    
    
    //connect(m_pSerialPort,&QSerialPort::readyRead,this,&frmserialport::serialReadData); //没有延时接受数据前的槽函数
    
        //延时接收数据
        connect(m_pSerialPort,SIGNAL(readyRead()),this,SLOT(on_serialReadData()));     //当有数据来是显示
        connect(&timer1,SIGNAL(timeout()),this,SLOT(serialReadData()));                //当定时结束时进入

    2.分离串口数据

    我采用的是字符串分隔的方法

    因为串口读取到的数据转成QString后长度是不一定的,所以用到了索引的方法解决。

        QByteArray receiveData;
        receiveData = m_pSerialPort->readAll();
        qDebug()<<"Re:"<<receiveData;
    
        QString str=receiveData;
        int x1=str.indexOf(":");
        int x=str.indexOf("G");
        int x2=x-x1-1;

     

    成功截取到后就转成Double类型提供Y轴数据给曲线。

     

    如果侵犯版权等问题,私聊马上删除。

    展开全文
  • java串口读取数据(转载)

    千次阅读 2014-09-25 16:43:06
    如何用Java语言向串口读写数据 串口, RS-232-C(又称EIA RS-232-C,以下简称RS232)是在1970年由美国电子工业协会(EIA)联合贝尔系统、调制解调器厂家及计算机终端生产厂家共同制定的用于串行通讯的标准。RS232是一个...
    

    如何用Java语言向串口读写数据

    串口, RS-232-C(又称EIA RS-232-C,以下简称RS232)是在1970年由美国电子工业协会(EIA)联合贝尔系统、调制解调器厂家及计算机终端生产厂家共同制定的用于串行通讯的标准。RS232是一个全双工的通讯协议,它可以同时进行数据接收和发送的工作。串口是计算机上一种非常通用设备通信的协议。以前,大多数计算机包含两个基于RS232的串口。串口同时也是仪器仪表设备通用的通信协议;很多GPIB兼容的设备也带有RS-232口。同时,串口通信协议也可以用于获取远程采集的数据。

      串口通信的概念非常简单,串口按位(bit)发送和接收字节。尽管比按字节(byte)的并行通信慢,但是串口可以在使用一根线发送数据的同时用另一根线接收数据。它很简单并且能够实现远距离通信。比如IEEE488定义并行通行状态时,规定设备线总常不得超过20米,并且任意两个设备间的长度不得超过2米;而对于串口而言,长度可达1200米。

      说到串口,也许很多人都要怀疑,这都什么年代了,USB设备已经如此普及了,谁还能想起这个老掉牙的通信端口呀?其实,在现实生活中,串口正因为它老,才会在实际使用中经常用到它。举个简单的例子:工业制造及其设备与计算机之间的通讯、还有在各种电子工厂测试设备的过程中,都无一例外使用的串口。因为它可以不用像USB设备一样必须有软件驱动的支持才可以进行工作。

      Java对串口通信的支持

      常见的Java串口包

      SUN的CommAPI分别提供了对常用的RS232串行端口和IEEE1284并行端口通讯的支持。目前,常见的Java串口包有SUN在1998年发布的串口通信API:comm2.0.jar(Windows下)、comm3.0.jar(Linux/Solaris);IBM的串口通信API以及一个开源的实现。鉴于在Windows下SUN的API比较常用以及IBM的实现和SUN的在API层面都是一样的,那个开源的实现又不像两家大厂的产品那样让人放心,这里就只介绍SUN的串口通信API在Windows平台下的使用。

      串口包的安装(Windows下)

      到SUN的网站下载javacomm20-win32.zip,包含的东西如下所示:

      按照其使用说明(Readme.html)的说法,要想使用串口包进行串口通信,除了设置好环境变量之外,还要将win32com.dll复制到\bin目录下;将comm.jar复制到\lib;把javax.comm.properties也同样拷贝到\lib目录下。然而在真正运行使用串口包的时候,仅作这些是不够的。因为通常当运行“java MyApp”的时候,是由JRE下的虚拟机启动MyApp的。而我们只复制上述文件到JDK相应目录下,所以应用程序将会提示找不到串口。解决这个问题的方法很简单,我们只须将上面提到的文件放到JRE相应的目录下就可以了。

      串口API介绍

      javax.comm.CommPort

      这是用于描述一个被底层系统支持的端口的抽象类。它包含一些高层的IO控制方法,这些方法对于所有不同的通讯端口来说是通用的。SerialPort 和ParallelPort都是它的子类,前者用于控制串行端口而后者用于控这并口,二者对于各自底层的物理端口都有不同的控制方法。这里我们只关心SerialPort。

      javax.comm.CommPortIdentifier

      这个类主要用于对串口进行管理和设置,是对串口进行访问控制的核心类。主要包括以下方法

      确定是否有可用的通信端口

      为IO操作打开通信端口

      决定端口的所有权

      处理端口所有权的争用

      管理端口所有权变化引发的事件(Event)

      javax.comm.SerialPort

      这个类用于描述一个RS-232串行通信端口的底层接口,它定义了串口通信所需的最小功能集。通过它,用户可以直接对串口进行读、写及设置工作。

      串口API实例

      压缩包中除了api,还包括了几个小例子,下面我们就一起看一下串口包自带的例子---SerialDemo中的一小段代码来加深对串口API核心类的使用方法的认识。

      列举出本机所有可用串口

      void listPortChoices() {

      CommPortIdentifier portId;

      Enumeration en = CommPortIdentifier.getPortIdentifiers();

      // iterate through the ports.

      while (en.hasMoreElements()) {

      portId = (CommPortIdentifier) en.nextElement();

      if (portId.getPortType() == CommPortIdentifier.PORT_SERIAL) {

      System.out.println(portId.getName());

      }

      }

      portChoice.select(parameters.getPortName());

      }

      以上代码可以列举出当前系统所有可用的串口名称,我的机器上输出的结果是COM1和COM3。

      串口参数的配置

      串口一般有如下参数可以在该串口打开以前配置进行配置:


      包括波特率,输入/输出流控制,数据位数,停止位和奇偶校验。

      SerialPort sPort;

      try {

      sPort.setSerialPortParams(BaudRate,Databits,Stopbits,Parity);

      //设置输入/输出控制流

      sPort.setFlowControlMode(FlowControlIn | FlowControlOut);

      } catch (UnsupportedCommOperationException e) {}

      串口的读写

      对串口读写之前需要先打开一个串口:

      CommPortIdentifier portId = CommPortIdentifier.getPortIdentifier(PortName);

      try {

      SerialPort sPort = (SerialPort) portId.open("串口所有者名称", 超时等待时间);

      } catch (PortInUseException e) {//如果端口被占用就抛出这个异常

      throw new SerialConnectionException(e.getMessage());

      }

      //用于对串口写数据

      OutputStream os = new BufferedOutputStream(sPort.getOutputStream());

      os.write(int data);

      //用于从串口读数据

      InputStream is = new BufferedInputStream(sPort.getInputStream());

      int receivedData = is.read();

      读出来的是int型,你可以把它转换成需要的其他类型。

      这里要注意的是,由于Java语言没有无符号类型,即所有的类型都是带符号的,在由byte到int的时候应该尤其注意。因为如果byte的最高位是1,则转成int类型时将用1来占位。这样,原本是10000000的byte类型的数变成int型就成了1111111110000000,这是很严重的问题,应该注意避免。

      串口通信的通用模式及其问题

      下面开始我们本次的重点--串口应用的研究。由于向串口写数据很简单,所以这里我们只关注于从串口读数据的情况。通常,串口通信应用程序有两种模式,一种是实现SerialPortEventListener接口,监听各种串口事件并作相应处理;另一种就是建立一个独立的接收线程专门负责数据的接收。由于这两种方法在某些情况下存在很严重的问题,所以我的实现是采用第三种方法来解决这个问题。

      事件监听模型

      现在我们来看看事件监听模型是如何运作的:

      首先需要在你的端口控制类(例如SManager)加上“implements SerialPortEventListener”

      在初始化时加入如下代码:

      try {

      SerialPort sPort.addEventListener(SManager);

      } catch (TooManyListenersException e) {

      sPort.close();

      throw new SerialConnectionException("too many listeners added");

      }

      sPort.notifyOnDataAvailable(true);

      覆写public void serialEvent(SerialPortEvent e)方法,在其中对如下事件进行判断:

      BI -通讯中断.

      CD -载波检测.

      CTS -清除发送.

      DATA_AVAILABLE -有数据到达.

      DSR -数据设备准备好.

      FE -帧错误.

      OE -溢位错误.

      OUTPUT_BUFFER_EMPTY -输出缓冲区已清空.

      PE -奇偶校验错.

      RI - 振铃指示.

      一般最常用的就是DATA_AVAILABLE--串口有数据到达事件。也就是说当串口有数据到达时,你可以在serialEvent中接收并处理所收到的数据。然而在我的实践中,遇到了一个十分严重的问题。

      首先描述一下我的实验:我的应用程序需要接收传感器节点从串口发回的查询数据,并将结果以图标的形式显示出来。串口设定的波特率是115200,串口每隔128毫秒返回一组数据(大约是30字节左右),周期(即持续时间)为31秒。实测的时候在一个周期内应该返回4900多个字节,而用事件监听模型我最多只能收到不到1500字节,不知道这些字节都跑哪里去了,也不清楚到底丢失的是那部分数据。值得注意的是,这是我将serialEvent()中所有处理代码都注掉,只剩下打印代码所得的结果。数据丢失的如此严重是我所不能忍受的,于是我决定采用其他方法。

      串口读数据的线程模型

      这个模型顾名思义,就是将接收数据的操作写成一个线程的形式:

      public void startReadingDataThread() {

      Thread readDataProcess = new Thread(new Runnable() {

      public void run() {

      while (newData != -1) {

      try {

      newData = is.read();

      System.out.println(newData);

      //其他的处理过程

      ……….

      } catch (IOException ex) {

      System.err.println(ex);

      return;

      }

      }

      readDataProcess.start();

      }

      在我的应用程序中,我将收到的数据打包放到一个缓存中,然后启动另一个线程从缓存中获取并处理数据。两个线程以生产者—消费者模式协同工作,数据的流向如下图所示:

          这样,我就圆满解决了丢数据问题。然而,没高兴多久我就又发现了一个同样严重的问题:虽然这回不再丢数据了,可是原本一个周期(31秒)之后,传感器节电已经停止传送数据了,但我的串口线程依然在努力的执行读串口操作,在控制台也可以看见收到的数据仍在不断的打印。原来,由于传感器节点发送的数据过快,而我的接收线程处理不过来,所以InputStream就先把已到达却还没处理的字节缓存起来,于是就导致了明明传感器节点已经不再发数据了,而控制台却还能看见数据不断打印这一奇怪的现象。唯一值得庆幸的是最后收到数据确实是4900左右字节,没出现丢失现象。然而当处理完最后一个数据的时候已经快1分半钟了,这个时间远远大于节点运行周期。这一延迟对于一个实时的显示系统来说简直是灾难!

      后来我想,是不是由于两个线程之间的同步和通信导致了数据接收缓慢呢?于是我在接收线程的代码中去掉了所有处理代码,仅保留打印收到数据的语句,结果依然如故。看来并不是线程间的通信阻碍了数据的接收速度,而是用线程模型导致了对于发送端数据发送速率过快的情况下的数据接收延迟。这里申明一点,就是对于数据发送速率不是如此快的情况下前面者两种模型应该还是好用的,只是特殊情况还是应该特殊处理。

      第三种方法

      TinyOS中有一部分是和我的应用程序类似的串口通信部分,于是我下载了它的1.x版的Java代码部分,参考了它的处理方法。解决问题的方法说穿了其实很简单,就是从根源入手。根源不就是接收线程导致的吗,那好,我就干脆取消接收线程和作为中介的共享缓存,而直接在处理线程中调用串口读数据的方法来解决问题,于是程序变成了这样:

      public byte[] getPack(){

      while (true) {

      // PacketLength为数据包长度

      byte[] msgPack = new byte[PacketLength];

      for(int i = 0; i < PacketLength; i++){

      if( (newData = is.read()) != -1){

      msgPack = (byte) newData;

      System.out.println(msgPack);

      }

      }

      return msgPack;

      }

      }

      在处理线程中调用这个方法返回所需要的数据序列并处理之,这样不但没有丢失数据的现象行出现,也没有数据接收延迟了。这里唯一需要注意的就是当串口停止发送数据或没有数据的时候is.read()一直都返回-1,如果一旦在开始接收数据的时候发现-1就不要理它,继续接收,直到收到真正的数据为止。

    原文出自【比特网】,转载请保留原文链接:http://soft.chinabyte.com/database/232/12715232.shtml

    展开全文
  • STM32串口发送数据和接收数据方式总结

    万次阅读 多人点赞 2018-05-13 20:34:22
    之前写了篇关于ESP8266使用AT指令进行互相通讯的实验,在写STM32串口接发数据的程序中,觉得有必要将之前学的有关于串口方面的使用经历加以总结。 串口发送数据: 1. 串口发送数据最直接的方式就是标准调用...

     

           之前写了篇关于ESP8266使用AT指令进行互相通讯的实验,在写STM32串口接发数据的程序中,觉得有必要将之前学的有关于串口方面的使用经历加以总结。

     

    串口发送数据:

           1. 串口发送数据最直接的方式就是标准调用库函数  void USART_SendData(USART_TypeDef* USARTx, uint16_t Data);
    第一个参数是发送的串口号,第二个参数是要发送的数据了。但是用过的朋友应该觉得不好用,一次只能发送单个字符,所以我们有必要根据这个函数加以扩展。

    void Send_data(u8 *s)
    {
    	while(*s!='\0')
    	{ 
    		while(USART_GetFlagStatus(USART1,USART_FLAG_TC )==RESET);	
    		USART_SendData(USART1,*s);
    		s++;
    	}
    }

            以上程序的形参就是我们调用该函数时要发送的字符串,这里通过循环调用USART_SendData来一 一发送我们的字符串。

    while(USART_GetFlagStatus(USART1,USART_FLAG_TC )==RESET);

             这句话有必要加,他是用于检查串口是否发送完成的标志,如果不加这句话会发生数据丢失的情况。这个函数只能用于串口1发送。有些时候根据需要,要用到多个串口发送那么就还需要改进这个程序。如下: 

    void Send_data(USART_TypeDef * USARTx,u8 *s)
    {
    	while(*s!='\0')
    	{ 
    		while(USART_GetFlagStatus(USARTx,USART_FLAG_TC )==RESET);	
    		USART_SendData(USARTx,*s);
    		s++;
    	}
    }

            这样就可实现任意的串口发送。但有一点,我在使用实时操作系统的时候(如UCOS,Freertos等),需考虑函数重入的问题。当然也可以简单的实现把该函数复制一下,然后修改串口号也可以避免该问题。然而这个函数不能像printf那样传递多个参数,所以还可以在改进,最终程序如下

    void USART_printf ( USART_TypeDef * USARTx, char * Data, ... )
    {
    	const char *s;
    	int d;   
    	char buf[16];
    	
    	va_list ap;
    	va_start(ap, Data);
    
    	while ( * Data != 0 )     // 判断是否到达字符串结束符
    	{				                          
    		if ( * Data == 0x5c )  //'\'
    		{									  
    			switch ( *++Data )
    			{
    				case 'r':							          //回车符
    				USART_SendData(USARTx, 0x0d);
    				Data ++;
    				break;
    
    				case 'n':							          //换行符
    				USART_SendData(USARTx, 0x0a);	
    				Data ++;
    				break;
    
    				default:
    				Data ++;
    				break;
    			}			 
    		}
    		
    		else if ( * Data == '%')
    		{									  //
    			switch ( *++Data )
    			{				
    				case 's':										  //字符串
    				s = va_arg(ap, const char *);
    				
    				for ( ; *s; s++) 
    				{
    					USART_SendData(USARTx,*s);
    					while( USART_GetFlagStatus(USARTx, USART_FLAG_TXE) == RESET );
    				}
    				
    				Data++;
    				
    				break;
    
    				case 'd':			
    					//十进制
    				d = va_arg(ap, int);
    				
    				itoa(d, buf, 10);
    				
    				for (s = buf; *s; s++) 
    				{
    					USART_SendData(USARTx,*s);
    					while( USART_GetFlagStatus(USARTx, USART_FLAG_TXE) == RESET );
    				}
    				
    				Data++;
    				
    				break;
    				
    				default:
    				Data++;
    				
    				break;
    				
    			}		 
    		}
    		
    		else USART_SendData(USARTx, *Data++);
    		
    		while ( USART_GetFlagStatus ( USARTx, USART_FLAG_TXE ) == RESET );
    		
    	}
    }

            该函数就可以像printf使用可变参数,方便很多。通过观察函数但这个函数只支持了%d,%s的参数,想要支持更多,可以仿照printf的函数写法加以补充。
            2. 直接使用printf函数。        很多朋友都知道想要STM32要直接使用printf不行的。需要加上以下的重映射函数

           如果不想添加以上代码,也可以勾选以下的Use MicroLI选项来支持printf函数使用。

     

    串口接收数据:       

            串口接收最后应有一定的协议,如发送一帧数据应该有头标志或尾标志,也可两个标志都有。这样在处理数据时既能能保证数据的正确接收,也有利于接收完后我们处理数据。串口的配置在这里就不在赘述,这里我以串口2接收中断服务程序函数且接收的数据包含头尾标识为例。

    #define Max_BUFF_Len 18
    unsigned char Uart2_Buffer[Max_BUFF_Len];
    unsigned int Uart2_Rx=0;
    void USART2_IRQHandler() 
    {
    	if(USART_GetITStatus(USART2,USART_IT_RXNE) != RESET) //中断产生 
    	{
    		USART_ClearITPendingBit(USART2,USART_IT_RXNE); //清除中断标志
    			 
    		Uart2_Buffer[Uart2_Rx] = USART_ReceiveData(USART2);     //接收串口1数据到buff缓冲区
    		Uart2_Rx++; 
         		 
    		if(Uart2_Buffer[Uart2_Rx-1] == 0x0a || Uart2_Rx == Max_BUFF_Len)    //如果接收到尾标识是换行符(或者等于最大接受数就清空重新接收)
    		{
    			if(Uart2_Buffer[0] == '+')                      //检测到头标识是我们需要的 
    			{
    				printf("%s\r\n",Uart2_Buffer);        //这里我做打印数据处理
    				Uart2_Rx=0;                                   
    			} 
    			else
    			{
    				Uart2_Rx=0;                                   //不是我们需要的数据或者达到最大接收数则开始重新接收
    			}
    		}
    	}
    }

     


            数据的头标识为“\n”既换行符,尾标识为“+”。该函数将串口接收的数据存放在USART_Buffer数组中,然后先判断当前字符是不是尾标识,如果是说明接收完毕,然后再来判断头标识是不是“+”号,如果还是那么就是我们想要的数据,接下来就可以进行相应数据的处理了。但如果不是那么就让Usart2_Rx=0重新接收数据。这样做的有以下好处:

            1.可以接受不定长度的数据,最大接收长度可以通过Max_BUFF_Len来更改

            2.可以接受指定的数据

            3.防止接收的数据使数组越界
            这里我的把接受正确数据直接打印出来,也可以通过设置标识位,然后在主函数里面轮询再操作。

            

            以上的接收形式,是中断一次就接收一个字符,这在UCOS等实时内核系统中频繁的中断,非常消耗CPU资源,在有些时候我们需要接收大量数据时且波特率很高的情况下,长时间中断会带来一些额外的问题。所以以DMA形式配合串口的IDLE(空闲中断)来接受数据将会大大的提高CPU的利用率,减少系统资源的消耗。首先还是先看代码。

    #define DMA_USART1_RECEIVE_LEN 18
    void USART1_IRQHandler(void)                                 
    {     
        u32 temp = 0;  
        uint16_t i = 0;  
          
        if(USART_GetITStatus(USART1, USART_IT_IDLE) != RESET)  
        {  
            USART1->SR;  
            USART1->DR; //这里我们通过先读SR(状态寄存器)和DR(数据寄存器)来清USART_IT_IDLE标志 			
            DMA_Cmd(DMA1_Channel5,DISABLE);  
            temp = DMA_USART1_RECEIVE_LEN - DMA_GetCurrDataCounter(DMA1_Channel5); //接收的字符串长度=设置的接收长度-剩余DMA缓存大小 
            for (i = 0;i < temp;i++)  
            {  
                Uart2_Buffer[i] = USART1_RECEIVE_DMABuffer[i];  
                    
            }  
            //设置传输数据长度  
            DMA_SetCurrDataCounter(DMA1_Channel5,DMA_USART1_RECEIVE_LEN);  
            //打开DMA  
            DMA_Cmd(DMA1_Channel5,ENABLE);  
        }        
    } 

            之前的串口中断是一个一个字符的接收,现在改为串口空闲中断,就是一帧数据过来才中断进入一次。而且接收的数据时候是DMA来搬运到我们指定的缓冲区(也就是程序中的USART1_RECEIVE_DMABuffer数组),是不占用CPU时间资源的。具体什么是IDLE中断和DMA需要朋友们先行了解。

        参考链接:

        https://blog.csdn.net/jdh99/article/details/8444474

        https://blog.csdn.net/phker/article/details/51925668   

       最后在讲下DMA的发送

    #define DMA_USART1_SEND_LEN 64
    void DMA_SEND_EN(void)
    {
    	DMA_Cmd(DMA1_Channel4, DISABLE);      
    	DMA_SetCurrDataCounter(DMA1_Channel4,DMA_USART1_SEND_LEN);   
    	DMA_Cmd(DMA1_Channel4, ENABLE);
    }

            这里需要注意下DMA_Cmd(DMA1_Channel4,DISABLE)函数需要在设置传输大小之前调用一下,否则不会重新启动DMA发送。

        有了以上的接收方式,对一般的串口数据处理是没有问题的了。下面再讲一下,在ucosiii中我使用信号量+消息队列+储存管理的形式来处理我们的串口数据。先来说一下这种方式对比其他方式的一些优缺点。一般对串口的处理形式是"生产者"和"消费者"的模式,即本次接收的数据要马上处理,否则当数据大量涌进的时候,就来不及"消费"掉生产者(串口接收中断)的数据,那么就会丢失本次的数据处理。所以使用队列就能够很方便的解决这个问题。

        在下面的程序中,对数据的处理是先接受,在处理,如果在处理的过程中,有串口中断接受数据,那么就把它依次放在队列中,队列的特征是先进先出,在串口中就是先处理先接受的数据,所以根据生产和消费的速度,定义不同大小的消息队列缓冲区就可以了。缺点就是太占用系统资源,一般51单片机是没可能了。下面是从我做的项目中截取过来的程序

    OS_MSG_SIZE  Usart1_Rx_cnt;          //字节大小计数值
    unsigned char Usart1_data;           //每次中断接收的数据
    unsigned char* Usart1_Rx_Ptr;        //储存管理分配内存的首地址的指针
    unsigned char* Usart1_Rx_Ptr1;       //储存首地址的指针
    void USART1_IRQHandler() 
    {
    	OS_ERR err;
    	OSIntEnter();
    	
      if(USART_GetFlagStatus(USART1,USART_FLAG_RXNE) != RESET) //中断产生 
      { 	 
        USART_ClearFlag(USART1, USART_FLAG_RXNE);     //清除中断标志
    		
        Usart1_data = USART_ReceiveData(USART1);     //接收串口1数据到buff缓冲区
    		
    		if(Usart1_data =='+')                     //接收到数据头标识
    		{
    //			OSSemPend((OS_SEM*		)&SEM_IAR_UART,  //这里请求信号量是为了保证分配的存储区,但一般来说不允许
    //			(OS_TICK		)0,                   //在终端服务函数中调用信号量请求但因为
    //			(OS_OPT			)OS_OPT_PEND_NON_BLOCKING,//我OPT参数设置为非阻塞,所以可以这么写
    //			(CPU_TS*		)0,
    //			(OS_ERR*		)&err); 
    //			if(err==OS_ERR_PEND_WOULD_BLOCK)	    //检测到当前信号量不可用
    //			{
    //				 printf("error");
    //			}				
    			Usart1_Rx_Ptr=(unsigned char*) OSMemGet((OS_MEM*)&UART1_MemPool,&err);//分配存储区
    			Usart1_Rx_Ptr1=Usart1_Rx_Ptr;		        //储存存储区的首地址
    		}
    		if(Usart1_data == 0x0a )   				//接收到尾标志
    		{                    
    			*Usart1_Rx_Ptr++=Usart1_data;
    			Usart1_Rx_cnt++;                        	//字节大小增加
    			OSTaskQPost((OS_TCB    *  )&Task1_TaskTCB,
                                       (void      *  )Usart1_Rx_Ptr1,    //发送存储区首地址到消息队列
                                       (OS_MSG_SIZE  )Usart1_Rx_cnt,
                                       (OS_OPT       )OS_OPT_POST_FIFO,  //先进先出,也可设置为后进先出,再有地方很有用
                                       (OS_ERR    *  )&err);
    									
    			Usart1_Rx_Ptr=NULL;          //将指针指向为空,防止修改
    			Usart1_Rx_cnt=0;	     //字节大小计数清零
    		}
    		else
    		{
    			*Usart1_Rx_Ptr=Usart1_data; //储存接收到的数据
    			Usart1_Rx_Ptr++;
    			Usart1_Rx_cnt++;
    		}	
    	}		 	
    	OSIntExit();
    }

           上面被注释掉的代码为我是为了防止当分区中没有空闲的存储块时加入信号量,打印出报警信息。当然我们也可以将存储块直接设置大一点,但是还是无法避免当没有可有存储块时会程序会崩溃现象。希望懂的朋友能告知下~。

            下面是串口数据处理任务,这里删去了其他代码,只把他打印出来了而已。

    void task1_task(void *p_arg)
    {
    	OS_ERR err;
    	OS_MSG_SIZE Usart1_Data_size;
    	u8 *p;
    	
    	while(1)
    	{
    		p=(u8*)OSTaskQPend((OS_TICK		)0, //请求消息队列,获得储存区首地址
    			(OS_OPT				)OS_OPT_PEND_BLOCKING,
    			(OS_MSG_SIZE*	)&Usart1_Data_size,
    			(CPU_TS*			)0,
    			(OS_ERR*			)&err);
    
    		printf("%s\r\n",p);        //打印数据
    
    		delay_ms(100);
    		OSMemPut((OS_MEM*	)&UART1_MemPool,    //释放储存区
    		(void*			)p,
    		(OS_ERR*		)&err);
    						 
    		OSSemPost((OS_SEM*	)&SEM_IAR_UART,    //释放信号量
    		(OS_OPT 	)OS_OPT_POST_NO_SCHED,
    		(OS_ERR*	)&err);
    						 
    		OSTimeDlyHMSM(0,0,1,500,OS_OPT_TIME_PERIODIC,&err);				 
    	}
    }

     

     

     

    展开全文
  • 串口本身,标准和硬件 什么是串行通信 什么是RS-232 信号定义 异步通讯 什么是全双工和半双工 什么是流控制 什么是BREAK 同步通讯 用户看到的串口和用户空间的串口编程 串口的设备...
  • 那么我们跟着教程来学习一下如何读取手机通过无线串口发送给电脑的数据。 这里我通过一个usb-ttl工具将蓝牙连接到电脑上,然后通过手机连接蓝牙给蓝牙发送数据,然后电脑从usb-ttl工具读取数据并显示。 1.首先,...
  • Arduino Uno 连接JY-61陀螺仪模块 串口读取数据

    万次阅读 热门讨论 2016-12-07 15:52:48
    最近偶然得到一块JY-61的陀螺仪模块, 其功能是,直接输出MPU6050经...本示例演示的是串口方式读取数据,还有IIC连接,以后有机会再分享 实验效果 BOM Arduino Uno *1 JY-61 *1(含邮票底板) 跳线若...
  • C语言——读取串口数据,并处理

    万次阅读 2019-09-17 00:15:14
    2.接收串口数据程序要用到的通用函数模块(可直接引用,无需更改); 3.接收串口数据程序的示例。 1.接收串口数据程序的编程逻辑示意图: 2.与串口有关的函数模块及数组(可直接引用到自己的程序中): main.c #...
  • select实现串口阻塞读取数据 2020年11月4日 14:56 static int fd; static char *device="/dev/ttySTM3"; fd = open_port(device); fd_set rset; int rv = -1 ,i=0; int nread=0; struct timeval timeout; timeout.tv...
  • 串口通信课程中需要理解的过程 用到了MFC里的MSComm控件 实现串口之间的通信
  • 最近在imx6上开发程序使用go作为后台服务程序,需要用到读取串口数据,想通过cgo用c来读取串口数据 package main /* #cgo LDFLAGS: lib/testrs232.a #include "lib/testrs232.h" int opendev(cha...
  • 公司为了做地磅管理系统,所以用到了取COM口上的数据,而厂家软件要3000-4000元,所以化了二三天时间进行了开发,这是读取COM的代码,以分享给大家,系统大家有用。
  • Android串口通信:串口读写

    万次阅读 2016-01-13 18:04:22
    Android串口通信:串口读写公司有个项目要用到串口通信,同事有写好一个DEMO,用的时候发现会有问题,从jni读串口数据时,经常会被截断,修改select延时还是无济于事,于是想到用JAVA直接去读/写串口文件,经过搜索在...
  • 串口读取称或是地磅数据

    千次阅读 2011-04-08 14:53:00
    我接触的称或是地磅都是串口输出数据,貌似都是串口输出(可能有别的我没接触到的)。 1、利用串口调试助手接收数据 首先我们来了解称是怎么把数据传输出来的,一般情况称传出模式有2中,  模式一是连续发送,...
  • 串口数据记录器

    2015-03-10 17:17:22
    C#串口数据发送、接收并保存为文件。源代码编译通过。经常用到串口接收数据,比如gps,惯性器件、工控设备,就编了串口接收并存储为文件的小工具。 内有一个完整的多线程串口类;一个INI格式配置文件操作类。
  • CH340串口读取

    万次阅读 2018-12-26 17:38:04
    最近在开发android工控机同硬件设备通信的时候,用到了ch340U转串,所以把关于这个串口的一些知识分享给大家。 简介: CH34x 系列芯片是 USB 总线的转接芯片,主要包含 CH340、CH341、CH345,通过 USB 总线提供异 ...
  • C#SerialPort串口通讯数据异常

    千次阅读 2019-12-01 20:37:16
    C#串口通讯数据异常 本篇文章只适合刚开始学习C#.Net,并使用串口通讯的同学借鉴。笔者也是在写C#与Arduino串口进行通讯的时候收到的数据异常,翻阅了其他CSDN博主对串口通讯的建议,最好就是使用线程对接收的数据...
  • 记得之前在很多项目开发中,都需要通过上位机来控制ECU或者通过上位机来读取ECU中的数据。其中上位机和ECU的通讯接口有串口,LIN或者CAN总线。串口作为一种低成本而又简单的通讯方式,仍然有很多客户在使用。毕竟,...
  • 开发串口程序首先要求你的设备需要支持串口通信,可以在设备上装一个App端的串口工具来检测一下 链接:https://pan.baidu.com/s/11L4aZI9orBhbnztka6H1Og 提取码:bvot 或者在电脑端下载一个友善串口助手检测一下...
  • 串口数据处理

    2019-03-24 15:37:56
    最近接了一个项目,用到串口处理,在日常的java业务开发中,进行串口操作的场景还真不多,一般让C人员处理。由于最近缺少C人员,所以只好用java的rxtx来实现,具体的rxtx怎么用这个我就不多说了。这里只简单看一下...
  • 想做个课设,可以把单片机获取的数据传到微信小程序端显示,但是不知道需要用到哪些知识?
  • 出现这种状况的原因可能是串口读取结束但发送端还没有发完。 用到的demo链接:https://github.com/kongqw/AndroidSerialPort 解决方法: AndroidSerialPort-master\SerialPortLibrary\src\main\java\...
  • 关于串口数据的发送和接收(调试必备)

    万次阅读 多人点赞 2019-03-21 20:44:53
    对于串口数据发送和接收,大多是都是利用串口中断来进行的,但是这样对于编程方面有一定要求,并且程序也不太好写,比如说,如果让你随意接收一段数据,然后利用串口将它发送出来,第一个需要考虑的问题就是接收...
  • 串口发送接收浮点型数据

    万次阅读 2018-09-13 15:25:49
    转自:... ... 在做下位机通信往往会用到串口,包括下位机将数据传输给上位机,或者是下位机与下位机之间进行数据传输,这时候就会遇到发送数据的问题,单片机通过串口发送数据时...
  • ROS串口通信(2)以十六进制指令读取IMU数据引言1、下载安装ROS的serial软件包2、通用serial通信代码3、使用ROS serial包实现IMU十六进制指令发送及数据读取编解码保存。 引言 前期准备参考博客 1、下载安装ROS的...
  • C# 串口通讯 读写

    2010-01-29 10:01:16
    C# 串口通讯 VS 2005 开发 串口通讯用到了 加载dll 简单的对串口 进行操作
  • C#中的串口读写

    2020-09-14 09:15:39
    问题描述:做项目时用到串口读写,将扫码枪的数据显示到界面上。虽然很简单,但对于编程瓜皮的我,还是记录下来,方便以后学习。。。 public class { public static SerialPort serialPort = null; public ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 16,052
精华内容 6,420
关键字:

串口读取数据时需要用到