使用 libserialport 来实现串口通信
之前使用了libmodbus这个库来与传感器通讯,使用ModBus485协议。但是有几个传感器在最后的CRC校验时出错,这情况在使用Python的minimalmodbus库时也出现过,所以当时使用了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
评论已关闭