之前使用了libmodbus这个库来与传感器通讯,使用ModBus485协议。但是有几个传感器在最后的CRC校验时出错,这情况在使用Pythonminimalmodbus库时也出现过,所以当时使用了pyserial库,现在C语言的libmodbus库也出现这个问题,只能说明这几个传感器有问题。

好了,前期情况就说到这里。通过在网上的一大圈搜索才找到了libserialport这个仅用C来实现的库,其他的都是用C++的,但是我对C++不熟。

开始使用libserialport

源码地址 https://sigrok.org/wiki/libserialport,在Github上有同步的源码 https://github.com/sigrokproject/libserialport

编译安装

# 需要提前安装 git make automake 等工具
git clone git://sigrok.org/libserialport
cd libserialport
 ./autogen.sh && ./configure
make && make install

使用

直接在API页面查看它的使用方式 https://sigrok.org/api/libserialport/unstable/index.html

这里直接以send_receive.c示例来说明

/* The data we will send. */
char *data = "Hello!";
int size = strlen(data);

源码中是直接将"Hello!"字符串发送到串口的,但是我的传感器需要以十六进制发送01 03 00 00 00 01 84 0A这样的数据。在stackoverflow找到了Write hexadecimal to file in C这个链接,知道了使用strtol将十六进制的数据写入到文件中,因为在Linux的哲学中,任何东西都是文件,串口也是文件的一种。进而知道了strtoul函数。

通过C语言strtoul()函数:将字符串转换成unsigned long(无符号长整型数)01 03 00 00 00 01 84 0A字符串转换成无符号长整型数

/* The data we will send. */
/*
将 string 转换成 unsigned long 数据
*/
unsigned long value = strtoul("0A84010000000301",NULL,16);
unsigned long *data = &value;
int size = sizeof(value);

这里使用0A84010000000301是因为数是从低往高数,字节码从前往后数,所以顺序必须要倒过来,之后会进一步处理以达到至少我们输入是正序,程序处理是倒序。

同时建立了一个*data指针指向value

发送数据到串口

/* Send data. */
printf("Sending '%lu' (%d bytes) on port %s.\n",
                value, size, sp_get_port_name(tx_port));
result = check(sp_blocking_write(tx_port, data, size, timeout));

从串口接收数据

/* Allocate a buffer to receive data. */                
unsigned long *buf = malloc(size + 1);                

/* Try to receive the data on the other port. */
printf("Receiving %d bytes on port %s.\n",
                size, sp_get_port_name(rx_port));
result = check(sp_blocking_read(rx_port, buf, size, timeout)) + 1;

/* Check whether we received the number of bytes we wanted. */
if (result == size)
        printf("Received %d bytes successfully.\n", size);
else
        printf("Timed out, %d/%d bytes received.\n", result, size);

/* Check if we received the same data we sent. */
char *buffer;
ultostr(*buf,buffer,16);
/* 打印数据 */
printf("Received '%s'.\n", buffer);

/* Free receive buffer. */
free(buf);

这里自定义了一个ultostr函数用来把从传感器返回的数据换成字符串形式,作用正好与strtoul相反,关于ultostr,也是从stackoverflow看来的,stackoverflow永远滴神!!!

当然最后的结果也倒序的FF796501020301,之后有时间再解决这个问题了。

全部源码

#include <libserialport.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
/* Example of how to send and receive data.
 *
 * This example file is released to the public domain. */
 
/* Helper function for error handling. */
int check(enum sp_return result);

/* unsigned long to string 函数原型 */
char *ultostr(unsigned long value, char *ptr, int base);
 
int main(int argc, char **argv)
{
        /* This example can be used with one or two ports. With one port, it
         * will send data and try to receive it on the same port. This can be
         * done by connecting a single wire between the TX and RX pins of the
         * port.
         *
         * Alternatively it can be used with two serial ports connected to each
         * other, so that data can be sent on one and received on the other.
         * This can be done with two ports with TX/RX cross-connected, e.g. by
         * a "null modem" cable, or with a pair of interconnected virtual ports,
         * such as those created by com0com on Windows or tty0tty on Linux. */
 
        /* Get the port names from the command line. */
        if (argc < 2 || argc > 3) {
                printf("Usage: %s <port 1> [<port 2>]\n", argv[0]);
                return -1;
        }
        
        // /dev/ttyUSB0
        // /dev/ttyS4
                
        int num_ports = argc - 1;
        char **port_names = argv + 1;
 
        /* The ports we will use. */
        struct sp_port *ports[2];
 
        /* Open and configure each port. */
        for (int i = 0; i < num_ports; i++) {
                printf("Looking for port %s.\n", port_names[i]);
                check(sp_get_port_by_name(port_names[i], &ports[i]));
 
                printf("Opening port.\n");
                check(sp_open(ports[i], SP_MODE_READ_WRITE));
 
                printf("Setting port to 9600 8N1, no flow control.\n");
                check(sp_set_baudrate(ports[i], 9600));
                check(sp_set_bits(ports[i], 8));
                check(sp_set_parity(ports[i], SP_PARITY_NONE));
                check(sp_set_stopbits(ports[i], 1));
                check(sp_set_flowcontrol(ports[i], SP_FLOWCONTROL_NONE));
        }
 
        /* Now send some data on each port and receive it back. */
        for (int tx = 0; tx < num_ports; tx++) {
                /* Get the ports to send and receive on. */
                int rx = num_ports == 1 ? 0 : ((tx == 0) ? 1 : 0);
                struct sp_port *tx_port = ports[tx];
                struct sp_port *rx_port = ports[rx];
 
                /* The data we will send. */
                /*
                https://stackoverflow.com/questions/26304765/write-hexadecimal-to-file-in-c-not-ascii
                http://c.biancheng.net/cpp/html/130.html
                将 string 转换成 unsigned long 数据
                */
                unsigned long value = strtoul("0A84010000000301",NULL,16);
                unsigned long *data = &value;
                int size = sizeof(value);               
 
                /* We'll allow a 1 second timeout for send and receive. */
                unsigned int timeout = 100;
 
                /* On success, sp_blocking_write() and sp_blocking_read()
                 * return the number of bytes sent/received before the
                 * timeout expired. We'll store that result here. */
                int result;
 
                /* Send data. */
                printf("Sending '%lu' (%d bytes) on port %s.\n",
                                value, size, sp_get_port_name(tx_port));
                result = check(sp_blocking_write(tx_port, data, size, timeout));                
 
                /* Check whether we sent all of the data. */
                if (result == size)
                        printf("Sent %d bytes successfully.\n", size);
                else
                        printf("Timed out, %d/%d bytes sent.\n", result, size);
 
                /* Allocate a buffer to receive data. */                
                unsigned long *buf = malloc(size + 1);                
 
                /* Try to receive the data on the other port. */
                printf("Receiving %d bytes on port %s.\n",
                                size, sp_get_port_name(rx_port));
                result = check(sp_blocking_read(rx_port, buf, size, timeout)) + 1;
 
                /* Check whether we received the number of bytes we wanted. */
                if (result == size)
                        printf("Received %d bytes successfully.\n", size);
                else
                        printf("Timed out, %d/%d bytes received.\n", result, size);
 
                /* Check if we received the same data we sent. */
                char *buffer;
                ultostr(*buf,buffer,16);
                /* 打印数据 */
                printf("Received '%s'.\n", buffer);
 
                /* Free receive buffer. */
                free(buf);
        }
 
        /* Close ports and free resources. */
        for (int i = 0; i < num_ports; i++) {
                check(sp_close(ports[i]));
                sp_free_port(ports[i]);
        }
 
        return 0;
}
 
/* Helper function for error handling. */
int check(enum sp_return result)
{
        /* For this example we'll just exit on any error by calling abort(). */
        char *error_message;
 
        switch (result) {
        case SP_ERR_ARG:
                printf("Error: Invalid argument.\n");
                abort();
        case SP_ERR_FAIL:
                error_message = sp_last_error_message();
                printf("Error: Failed: %s\n", error_message);
                sp_free_error_message(error_message);
                abort();
        case SP_ERR_SUPP:
                printf("Error: Not supported.\n");
                abort();
        case SP_ERR_MEM:
                printf("Error: Couldn't allocate memory.\n");
                abort();
        case SP_OK:
        default:
                return result;
        }
}

/*
https://stackoverflow.com/questions/2709713/how-to-convert-unsigned-long-to-string
将 unsigned long 数据转换成 string 数据
*/

/* unsigned long to string */
char *ultostr(unsigned long value, char *ptr, int base)
{
  unsigned long t = 0, res = 0;
  unsigned long tmp = value;
  int count = 0;

  if (NULL == ptr)
  {
    return NULL;
  }

  if (tmp == 0)
  {
    count++;
  }

  while(tmp > 0)
  {
    tmp = tmp/base;
    count++;
  }

  ptr += count;

  *ptr = '\0';

  do
  {
    res = value - base * (t = value / base);
    if (res < 10)
    {
      * -- ptr = '0' + res;
    }
    else if ((res >= 10) && (res < 16))
    {
        * --ptr = 'A' - 10 + res;
    }
  } while ((value = t) != 0);

  return(ptr);
}

在编译的时加上参数gcc send_receive.c -o send_receive -lserialport


参考链接:
http://c.biancheng.net/cpp/html/130.html
https://stackoverflow.com/questions/26304765/write-hexadecimal-to-file-in-c-not-ascii
https://stackoverflow.com/questions/2709713/how-to-convert-unsigned-long-to-string
https://www.v2ex.com/t/757638

标签: none

评论已关闭