我正在尝试在我的程序中设置半双工通信.我的RS485收发器使用RTS标志(TIOCM_RTS)在发送和接收之间来回切换.要发送/接收数据,我需要手动更改RTS标志:

 

>将RTS设置为高.
>发送数据.
>将RTS设置为低.

 

int setRTS(int level) {
    int status;
    ioctl(ser_port, TIOCMGET, &status);
    if(level) {
        status |= TIOCM_RTS;
    } else {
        status &= ~TIOCM_RTS;
    }
    ioctl(ser_port, TIOCMSET, &status);
    return 1;
}

我的问题是:Linux内核不应该自动切换RTS吗?
以及如何确保在调用setRTS(0)之前发送数据?

最佳答案

shouldn’t the linux kernel be able to switch RTS automatically?

是的,从Linux 3.0开始就有这个内核框架.

 

RS485通信的时候,再应用程序中,可以进行对485设置为自动方向切换模式。防止由于我们手动切换引脚时,存在的时间问题,因为我们手动切换的时候,每次写串口的时候,都要将引脚拉高或拉低来切换成写模式,调用完写串口的函数后,就要去进行延时一段时间,确保数据在底层硬件层传输完毕了,然后将引脚拉高或拉低在切换到读状态,这样由于你计算的时间的误差性于你拉高拉低引脚的时间误差性都会导致485的通信不稳定。所以我们如果采用485的自动方向切换模式,将会将这个通信误差缩小,当每次写串口时,他自己就会切换为写状态,写完后,又自动切换为读状态。

在arm linux上的485自动切换的应用程序操作如下解释:

 

打开串口的方法,与设置串口的属性等,更正常我们在应用程序中使用串口一样,一样的方式进行打开串口就可以。

打开串口之后,我们要进行下一面一部,设置一个关于RS485自动方向模式切换要用的结构体和几个宏,这个结构体将来在填充好后,用ioctl函数写入到所打开的串口文件描述符中即可。

 

#define TIOCGRS485      0x542E
#define TIOCSRS485      0x542F
struct my_serial_rs485    //关于485自动方向切换模式要设置的内容的结构体。
{
 /* RS485 feature flags */
#define SER_RS485_ENABLED /* If enabled */
#define SER_RS485_RTS_ON_SEND /* Logical level for
                               RTS pin when
                               sending */
#define SER_RS485_RTS_AFTER_SEND /* Logical level for
                               RTS pin after sent*/
#define SER_RS485_RX_DURING_TX (1 << 4)
 /* Delay before send (milliseconds) */
 /* Delay after send (milliseconds) */
 /* Memory is cheap, new structs
                       are a royal PITA .. */
};
 
int main(void)
{
    struct my_serial_rs485 rs485conf;
    bool flag = false;
    flag = g_com1.open_com(Serial_COM_10, O_RDWR | O_NOCTTY);    //打开串口,当然这个串口类是要你自己去封装的
    if ( !flag )
    {
        printf("open com 10 is failed.\n");
        _exit(-1);
    }
 
    /**************458config 的填充***********************************************/
    /* Enable RS485 mode: */
    rs485conf.flags |= SER_RS485_ENABLED;    //设置使能485
 
    /* Set logical level for RTS pin equal to 1 when sending: */
    rs485conf.flags |= SER_RS485_RTS_ON_SEND;    //设置rts,当逻辑电平为高的时候,为发送
    //rs485conf.flags |= SER_RS485_RTS_AFTER_SEND;
 
    /* set logical level for RTS pin equal to 0 after sending: */
    rs485conf.flags &= ~(SER_RS485_RTS_AFTER_SEND);    //设置rts,当发送完后逻辑电平为低
    //rs485conf.flags &= ~(SER_RS485_RTS_ON_SEND);
    /****************************************************************************/
 
//485的使用
    pthread_mutex_lock(&mutex_net1);
    len = cclinet1.Recv_from_Net(recv_buffer, MAX_BUF_LEN);    //从网络中读取数据,len表示读取到的长度
    pthread_mutex_unlock(&mutex_net1);
 
   rs_485_wait_time = 10 * len / baudrate_list[g_info.baud]  * 1000;   //ms    //根据len的长度和波特率算出输出这么个len长度的字节的时间需要多少ms
    /**********************************自动切换485******************/
    pthread_mutex_lock(&mutex_com1);
    rs485conf.delay_rts_after_send = rs_485_wait_time;    //将计算出的发送len长度字节的时间,填充到结构体的成员中,表示发送完串口数据后延时一段时间
    if (ioctl (g_com1.m_fd, TIOCSRS485, &rs485conf) < 0)    //将填充的结构体写入到文件描述符中
    {
        /* Error handling.*/
        printf("ioctl TIOCSRS485 error.\n");
    }
    pthread_mutex_unlock(&mutex_com1);
 
/********************之后就可以像操作232的串口一样去操作485的串口了。因为在每次要写数据时,会自动切换为写模式,并延时比较准确的时间后去自动切换为读模式。
 
    return 0;
 
}

 

由于程序是在工程开发中的一部分拿出来的,来做485记录的,并不完整,但关于485的自动切换模式的使用,却大致就是如此了。