在传统 UART(串口)通信中,每一帧数据通常包含1位起始位,8位数据位,可以选配的1位奇偶校验位,以及1位或者2位停止位,可参考下图的数据帧结构:
在一些特定的应用中,比如多机通信、地址/数据区分等场景中,需要用到串口的9bit通讯模式,该模式下每帧数据会扩展到9位,数据的最后一位为额外的标识位。这个额外的第 9 位可以用于标识数据类型(比如地址/数据区分)或者多机通信中的设备寻址等。可参考下图的数据帧结构:
串口的9bit模式比较常见的使用方式是当总线上有多个设备时,第一帧数据使用第 9 位标记为“地址帧”,其余设备判断是否接收。后续帧第 9 位标记为“数据帧”,所有设备按需处理。一般第 9 位为0 表示普通数据,为1 表示特殊命令或地址。
在这种应用模式下,就要求程序先设置为地址模式(即第9位为1),发送第一个地址帧后再切换为数据模式(第9位为0),再发送后续的数据帧。如果用户每次发送都进行切换的话,一是代码相对繁琐,另一点是在应用层切换存在调度机制,可能会让地址帧与数据帧出现一定的间隔。如果实际的场景对通讯的响应速度有一定的要求的话,在应用层来回切换可能就无法达到要求。
考虑到这一点,英创公司在Linux工控主板的驱动中进行了优化,给要使用9bit通讯模式的用户增加了一个专用的设置接口。通过这个设置接口,用户只需在发送数据前进行一次设置,就可以将数据一次性填入,主板会自动以地址帧的模式发送第一个字节,然后切换为数据帧发送后续字节,整个切换流程都是在驱动中自动完成,能够保证数据之间几乎没有间隔。下面就介绍一下具体的使用方法。
专用的设置接口借鉴了原来设置串口参数的方式,在struct termios中的c_cflag指定了一个未使用特殊的值,当驱动检测到这个特殊值后,就会自动切换地址帧与数据帧来发送填入的数据。这样就可保持UART串口设置的习惯方式不变。具体设置c_cflag的值方法为:使能CMSPAR位,异或PARODD位,对应的代码如下(在英创提供的串口例程基础上修改):
int CSerial::set_port_bit9() { struct termios new_opt; int status; tcgetattr(m_fd,&new_opt); new_opt.c_cflag &= ~(PARENB | CMSPAR); new_opt.c_cflag |= CMSPAR; new_opt.c_cflag ^= PARODD; new_opt.c_iflag |= INPCK; status = tcsetattr(m_fd,TCSANOW,&new_opt); if(status != 0) { perror("Cannot set the serial bit9"); return -1; } return status; } int main( int argc,char* argv[] ) { int i1; int portno, baudRate; char Buf[20], cmd; //打开串口,并设置参数 i1 = m_Serial.OpenPort( portno, baudRate, '8', '1', 'N'); if( i1<0 ) { printf( "serial open fail\n"); return -1; } Buf[0] = 0xaa; //第一个字节为地址 Buf[1] = 0xaa; //后面的字节为数据 //设置9bit通讯模式 m_Serial.set_port_bit9(); //发送填入的数据 i1 = m_Serial.WritePort(Buf, 2); return 0; }
用户使用时,如果需要按照9bit通讯模式发送数据,在调用发送前先调用一次封装好的set_port_bit9()函数设置串口后,再进行发送即可。为了避免做其他设置的时候影响到相关的标志位,建议用户在调用发送函数前,再调用set_port_bit9()函数设置,然后就立即进行发送。
下图是测试的波形,使用9bit通讯模式发送了两个字节(都是0xaa),第一个字节位的第9位(标识位)为1,代表地址或者特殊命令,第二个字节的第9位(标识位)为0,代表正常数据,图中红色箭头指向的就是两个字节的第9位:
另外可以看到由于是一次性填入的数据进行发送,所以两个字节之间(第一个字节的停止位和第二个字节的起始位)几乎没有什么间隔,能够保证数据发送的实时性。
我们在主流的产品ESM335x、ESM6800、ESM7000、ESM6200、ESM6400、ESM8000、ESM7400、ESM8400、ESM3568中均已实现该功能,如果对该方案感兴趣的用户,可以联系英创的工程师获取详细的测试例程。
成都英创信息技术有限公司 028-8618 0660