如果没有用minicom打开一次,则无法从串口读取
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如果没有用minicom打开一次,则无法从串口读取相关的知识,希望对你有一定的参考价值。
我已经从C中的串口实现了阻塞读取。我的目标是在新数据到达之前进行阻塞读取。
这是我如何实现串行伪对象(我删除了多线程保护,使代码更清晰)。
typedef struct
{
int fd;
se_serial_speed_t speed;
se_serial_parity_t parity;
bool flow_control;
}se_serial_t;
int se_serial_constructor(se_serial_t** self, char* serial_port)
{
int fd;
if(NULL != *self)
{
return ERR_NNULL_PTR;
}
else if(0 != access(serial_port, F_OK))
{
ERRNO("Serial port is not available");
return ERR_ILLEGAL_PARAM;
}
else
{
if(-1 == (fd = open(serial_port, O_RDWR | O_NOCTTY)))
{
ERRNO("Error opening %s in rw mode", serial_port);
return ERR_OFILE_FAIL;
}
else if(NULL == (*self = malloc(sizeof(se_serial_t))))
{
ERROR("Error allocating memory for Serial");
return ERR_MALLOC_FAIL;
}
(*self)->fd = fd;
}
return ERR_OK;
}
int se_serial_configure_interface(se_serial_t* self, se_serial_speed_t speed, se_serial_parity_t parity, bool flow_control)
{
struct termios options;
if(NULL == self)
{
return ERR_NULL_PTR;
}
else
{
if(0 != tcgetattr(self->fd, &options))
{
ERRNO("Unable to get serial port current configuration");
}
if(0 != cfsetospeed(&options, speed))
{
ERRNO("Unable to set serial port output speed");
}
if(0 != cfsetispeed(&options, speed))
{
ERRNO("Unable to set serial port input speed");
}
switch(parity)
{
case SE_SERIAL_PARITY_8N1:
options.c_cflag &= ~PARENB;
options.c_cflag &= ~CSTOPB;
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS8;
break;
case SE_SERIAL_PARITY_7E1:
options.c_cflag |= PARENB;
options.c_cflag &= ~PARODD;
options.c_cflag &= ~CSTOPB;
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS7;
break;
case SE_SERIAL_PARITY_7O1:
options.c_cflag |= PARENB;
options.c_cflag |= PARODD;
options.c_cflag &= ~CSTOPB;
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS7;
break;
case SE_SERIAL_PARITY_7S1:
options.c_cflag &= ~PARENB;
options.c_cflag &= ~CSTOPB;
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS8;
break;
default:
WARNING("Unable to set serial port parity");
break;
}
if(flow_control)
options.c_cflag |= CRTSCTS;
else
options.c_cflag &= ~CRTSCTS;
options.c_cc[VMIN] = 1;
options.c_cc[VTIME] = 0;
if(0 != tcsetattr(self->fd, TCSANOW, &options))
{
ERRNO("Error configuring serial port");
return ERR_SERIAL_CONF_FAIL;
}
self->speed = speed;
self->parity = parity;
self->flow_control = flow_control;
}
return ERR_OK;
}
int se_serial_read(se_serial_t* self, uint8_t* buffer, int size)
{
int bytes_read = 0;
int ret;
if(NULL == self)
{
return ERR_NULL_PTR;
}
else
{
while(bytes_read < size)
{
if(0 > (ret = read(self->fd, &(buffer[bytes_read]), size - bytes_read)))
{
ERROR("Error reading from %s : %d
", self->serial_port, ret);
return ERR_RFILE_FAIL;
}
bytes_read += ret;
}
size = bytes_read;
}
return size;
}
我正在通信的设备在启动后每秒发送一个11字节的帧。
所以我在初始化串口后在无限循环中接收帧,然后我打印它们。
se_serial_t* serial = NULL;
uint8_t buffer[1024] = {0};
int ret = 0;
int i;
if(0 > (ret = se_serial_constructor(&serial, "/dev/ttyUSB0")))
{
ERROR("Error creating serial : %d", ret);
return ERR_SERIAL_CREATION_FAIL;
}
else if(0 > (ret = se_serial_configure_interface(serial, SE_SERIAL_SPEED_B115200, SE_SERIAL_PARITY_8N1, false)))
{
ERROR("Error configuring serial interface : %d", ret);
return ERR_SERIAL_CONFIG_FAIL;
}
while(1)
{
if(0 > (ret = se_serial_read(serial, buffer, 11)))
{
ERROR("Error reading from serial : %d", ret);
return ret;
}
else
{
for(i=0;i<ret;i++)
{
printf("%02x ", buffer[i]);
}
printf("
");
}
}
我得到的结果有些奇怪,即使我知道设备正在发送帧,读取也会永久阻塞。
但是,如果我用另一个程序如minicom打开端口,我可以在其中接收帧。一旦使用minicom打开端口并且我已退出端口,我的程序将运行良好,直到下次重新启动计算机。
如果我重新启动设备,代码会阻塞,直到它开始发送帧并接收它们为止。
我也试过一个Raspberry Pi 3,以确保它不是我的笔记本电脑上的配置问题,但我得到相同的结果。
有没有人知道我为什么会这样做?
我得到的结果有些奇怪,即使我知道设备正在发送帧,读取也会永久阻塞。 ... 一旦使用minicom打开端口并且我已退出端口,我的程序将运行良好,直到下次重新启动计算机。
一点都不奇怪。 这清楚地表明您的程序初始化串行终端是不完整的,并且取决于预先存在的初始化是否合适。 (BTW“奇怪”是一种基于意见的描述,不传达技术信息以帮助调试。)
串行终端的默认模式通常是系统引导后的规范模式(用于传输文本)。
因此,串行终端的(无意的)规范读取()将被阻塞,直到遇到线路终止字符(例如,新行字符或0x0A)。
如果源从不发送任何行终止字符,您的程序将永远阻止。
您可以使用stty -F /dev/ttyUSB0 -a
命令确认这一点,并发现icanon属性前面没有连字符。
Minicom将串行终端配置为非规范模式,这是您的程序显然也期望串行终端运行的模式。 但是,您的程序仅配置用于波特率,成帧和流量控制的termios参数。 它缺少串行终端可靠运行的几个显着术语。
如果您的程序需要非规范模式,那么它必须显式配置该模式,而不是依赖于预先存在的配置。 由于还应为非规范模式设置或清除许多其他相关属性,因此宏cfmakeraw()是对代码的最简单编辑。 插入
cfmakeraw(&options);
介于您的波特率和“奇偶校验”配置之间。 请注意,如果数据不仅仅是ASCII文本,则使用7位数据帧可能会导致损坏,因此在程序中支持这三种模式是不协调的。
另一个显着的遗漏是启用接收器并设置本地模式:
options.c_cflag |= (CLOCAL | CREAD);
我正在与之通信的设备发送一个11字节的帧
顺便说一句,在串行终端的上下文中使用“frame”是不合适的。在异步串行通信中,每个字符或字节都是成帧的。您对“框架”的引用更适合称为消息或数据包或数据报。
以上是关于如果没有用minicom打开一次,则无法从串口读取的主要内容,如果未能解决你的问题,请参考以下文章
访问 - VBA - 无法导入新的 excel 文件 - 但打开一次后可以