「C++」Linux串口通信 | Ubuntu与单片机通信 | 测量HC-05蓝牙模块的传输延迟

收集数据集的时候需要给数据打时间戳,数据是从单片机上用蓝牙传输到电脑上的,所以需要测试一下传输的延迟,在此做一下记录。

用到的设备

单片机:STM32F103

蓝牙模块:HC-05,两个

USB转TTL:一个

PC:Ubuntu16.04系统

主要思想

从PC端通过蓝牙发送一个数据给单片机,单片机再发送回来,计算时间差除以2,记录多次取平均。

蓝牙和电脑通过USB转TTL连接,与串口等效;和单片机串口直连,也和单片机串口等效。

文章目录

  • 准备
  • 单片机部分
  • Ubuntu部分
    • 获取当前时间
    • 串口通信
  • 结果
  • 关于Linux串口的大小端问题

准备

两个蓝牙模块需要配置为主从机,教程参考:
https://blog.csdn.net/zx3517288/article/details/52291027

单片机部分

单片机的程序主要是串口接收,然后再发送就好了。

  • 初始化串口(这里使用串口1)以及新建工程等等网上资料很多,不做赘述
  • 串口中断函数

PS:蓝牙模块连接串口1,程序和串口通信相同

//中断处理
void USART1_IRQHandler(void)
{
  
	if(USART_GetITStatus(USART1, USART_IT_TXE) != RESET)
	{   
	    //把寄存器的数据原封不动发回去
		USART_SendData(USART1, USART1->DR); 
		USART_ITConfig(USART1, USART_IT_TXE, DISABLE); 

	}
	else if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
	{
	    //清除接收标志位
		USART_ClearITPendingBit(USART1, USART_IT_RXNE);
		//使能发送中断
		USART_ITConfig(USART1, USART_IT_TXE, ENABLE);
	}
	USART_ClearITPendingBit(USART1,USART_IT_ORE);

}

主函数比较简单,系统初始化之后就while(1){}就好。

Ubuntu部分

主要实现的功能是:

  • 向串口发送一个数据后等待单片机回传,同时记录当前时间;
  • 收到回传后检查是否与发送的数据相同,相同则再次记录时间,计算延迟。

获取当前时间

#include 
#include 
long timestamp()
{
    struct timeval tv;
    std::string time_string;
    gettimeofday(&tv, NULL);
    long time_long = tv.tv_sec*1000 + tv.tv_usec/1000;
    return time_long;
}

串口通信

配置参考了网上的代码,整理如下:

usart.cpp

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "main.h"

//初始化
void usart_init()
{
    struct termios signal_usart;
    struct sigaction saio;           /* definition of signal action */

    /* open the device to be non-blocking (read will return immediatly) */
    usart_0 = open(MODEMDEVICE, O_RDWR | O_NOCTTY | O_NONBLOCK);
    if (usart_0 <0)
    {
        perror(MODEMDEVICE);
        std::cout << "Can not open " << MODEMDEVICE << std::endl;
        exit(-1);
    }
    
    //设置中断处理函数,就是接收到数据后会执行的函数,定义在后面
    saio.sa_handler = bluetooth_receive;
    
    sigemptyset(&saio.sa_mask);
    saio.sa_flags = 0;
    saio.sa_restorer = NULL;
    sigaction(SIGIO,&saio,NULL);

    fcntl(usart_0, F_SETOWN, getpid());//allow the process to receive SIGIO
    fcntl(usart_0, F_SETFL, FASYNC);//恢复串口的状态为阻塞状态,用于等待串口数据的读入
    set_opt(signal_usart,usart_0,115200,8,'N',1);
}
int set_opt(struct termios newtio,int fd,int nSpeed, int nBits, char nEvent, int nStop)
{
    /*步骤一,设置字符大小*/
    newtio.c_cflag |= CLOCAL | CREAD;
    newtio.c_cflag &= ~CSIZE;
    /*设置数据位*/
    switch( nBits )
    {
        case 7:
            newtio.c_cflag |= CS7;
            break;
        case 8:
            newtio.c_cflag |= CS8;
            break;
    }
    /*设置奇偶校验位*/
    switch( nEvent )
    {
        case 'O': //奇数
            newtio.c_cflag |= PARENB;
            newtio.c_cflag |= PARODD;
            newtio.c_iflag |= (INPCK | ISTRIP);
            break;
        case 'E': //偶数
            newtio.c_iflag |= (INPCK | ISTRIP);
            newtio.c_cflag |= PARENB;
            newtio.c_cflag &= ~PARODD;
            break;
        case 'N': //无奇偶校验位
            newtio.c_cflag &= ~PARENB;
            break;
    }
    /*设置波特率*/
    switch( nSpeed )
    {
        case 2400:
            cfsetispeed(&newtio, B2400);
            cfsetospeed(&newtio, B2400);
            break;
        case 4800:
            cfsetispeed(&newtio, B4800);
            cfsetospeed(&newtio, B4800);
            break;
        case 9600:
            cfsetispeed(&newtio, B9600);
            cfsetospeed(&newtio, B9600);
            break;
        case 115200:
            cfsetispeed(&newtio, B115200);
            cfsetospeed(&newtio, B115200);
            break;
        case 460800:
            cfsetispeed(&newtio, B460800);
            cfsetospeed(&newtio, B460800);
            break;
        default:
            cfsetispeed(&newtio, B9600);
            cfsetospeed(&newtio, B9600);
            break;
    }
    /*设置停止位*/
    if( nStop == 1 )
        newtio.c_cflag &= ~CSTOPB;
    else if ( nStop == 2 )
        newtio.c_cflag |= CSTOPB;
    /*设置等待时间和最小接收字符*/
    newtio.c_cc[VTIME] = 0;
    newtio.c_cc[VMIN] = 0;

    //使用原始模式,如果以字符串形式接收下面两行可以去掉
    newtio.c_lflag &= ~(ICANON | ECHO | ECHOE |ISIG);
    newtio.c_oflag &= ~OPOST;

    /*处理未接收字符*/
    tcflush(fd,TCIFLUSH);
    /*激活新配置*/
    if((tcsetattr(fd,TCSANOW,&newtio))!=0)
    {
        perror("com set error");
        return -1;
    }
    printf("Serial port set done!\n");
    return 0;
}

usart.h

#ifndef USART_HPP
#define USART_HPP

#include 
#include 
#include 
#include 

void usart_init();
int set_opt(struct termios newtio,int fd,int nSpeed, int nBits, char nEvent, int nStop);

extern int usart_0;
#endif // USART_HPP

主函数:

main.cpp

//
// Created by chenyr on 19-10-12.
//

#include "main.h"

int8_t test_data = 0x55; //用于测试的数据
long time_send, time_rece;
std::vector delays;

int main()
{
    delays.reserve(1000);
    usart_init();
    bluetooth_send(test_data); //发送

    float average = 0;
    while(delays.size() < 1000) //发送1000+次
    {

    }
    //计算平均时间
    for (int i = 0; i < delays.size(); i++)
        average += delays[i];
    average /= delays.size();
    std::cout << "Average delay time: " << average << std::endl;

}

void bluetooth_send(int8_t temp)
{
    time_send = timestamp();
    write(usart_0, &temp, sizeof(int8_t));
}

//串口初始化时绑定的处理函数
void bluetooth_receive(int status)
{
    int8_t buf;
    std::string receive_str;
    read(usart_0,&buf, sizeof(int8_t));

    if (buf == test_data)
    {
        long time_rece = timestamp();
        std::cout << "delay time: " << (time_rece - time_send)/2.0 << std::endl;
        delays.push_back((float)(time_rece - time_send)/2.0f);
    }
    else
    {
        std::cout << "receive wrong data: " << buf << std::endl;
    }
    bluetooth_send(test_data);
}

main.h

//
// Created by chenyr on 19-10-12.
//

#ifndef MAIN_H
#define MAIN_H

#include 
#include "usart.h"
#include "timestamp.h"
#include 
#include 

void bluetooth_send(int8_t temp);
void bluetooth_receive(int status);

#endif 

结果

在蓝牙收发模块的直线距离不超过2m的情况下做了3组测试,距离逐渐增加,得到的结果如下:

  • 蓝牙延迟在19-20ms左右,波动有点大,估计在正负5ms以上(没有具体计算)
  • 距离增加时延迟会增加,但在2m的范围内都是微秒级的增加

关于Linux串口的大小端问题

在测试的时候发现Ubuntu和STM32的大小端并不一样,所以如果发送16位以上的数据,接收到的数据高低位是反的,所以我测试的时候用的是8位的数据(int8_t)。

你可能感兴趣的:(C++,Linux串口通信,蓝牙延迟)