关于ok335xSII支持串口Mark/Space校验位的变通实现方法

关于ok335xSII支持串口Mark/Space校验位的变通实现方法

一)思路

1.对于ok335xSII开发板使用POSIX不支持Mark/Space串口校验问题,我的解决方法是变通:
  首先,我们了解一下串口的校验方式:N:无校验位,O:奇校验位,E:偶效验位,M:1校验位,S:0校验位。N无校验位输出,O保证带校验位,数据有奇数个1,E保证带校验位,数据有偶数个1,M校验位始终为1,S校验位始终为0。当有校验位时,应根据需要给定模式,但实际上linux系统中,支持POSIX越好,对于M和S的支持越不好。
  其次,解决M,S支持的变通方法处理。既然M是1校验,S是0校验,只要保证数据校验位正确即可,我们使用O,E完成这个过程的处理,首先确保您的O,E校验位可以有效使用,不确定可以使用示波器查看停止位前的第9位有无。串口数据格式:低开始位(1位),数据7/8位(先发低位,后发高位),校验位(1位),停止位(1/2位)。方法如下:校验位要S模式时,统计该字节数据字节的1出现的次数,为偶数次时给定E,为奇数次时给定O;校验位要M模式时,统计该字节数据字节的1出现的次数,为偶数次时给定O,为奇数时给定E。
  再次,一般使用多机模式时,会用到M/S校验位切换,地址使用M,数据使用S。为了处理这样的过程,我们需要编写校验位切换函数,以达到多机通讯的目的,在接收数据时,使用N校验接收,不关心校验位。

二)支持代码

serial.h

#ifndef __SERIAL_H
#define __SERIAL_H

#ifdef __cplusplus
extern "C" {
#endif

/*头文件*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <termios.h>
#include <string.h>
/*调试宏*/
//#define DEBUG

#ifdef DEBUG
#define pr_debug(fmt, ...) printf(fmt, ##__VA_ARGS__)
#else
#define pr_debug(fmt, ...) 
#endif
//MARK/SPACE校验用宏
#define CMSPAR 010000000000
/*设备文件名*/
#define SERIAL_DEVFILE_NAME0 "/dev/ttyO1"  //整机和1-12功放串口文件名,RS485
#define SERIAL_DEVFILE_NAME1    "/dev/ttyO2"  //激励器1串口文件名(备用口),TTL
#define SERIAL_DEVFILE_NAME2 "/dev/ttyO3" //激励器2或13-24功放串口文件名,TTL
 
/*SERIAL数据结构*/
struct serial_cmd {
    unsigned int bps; //波特率
    unsigned char databits; //数据位
    unsigned char stopbits; //停止位
    unsigned char parity; //奇偶校验位
    unsigned char reserved;
};

/*
 	函数功能:打开SERIAL设备
	参数:SERIAL_DEVFILE_NAME0或SERIAL_DEVFILE_NAME1
	返回值:打开设备成功,返回设备文件描述符fd;失败返回-1 
*/
extern int serial_open(const char*filename);

/*
	函数功能:关闭SERIAL设备
	参数:
		@fd:设备文件描述符
	返回值:无
*/
extern void serial_close(int fd);

/*
	函数功能:配置SERIAL的工作参数
	参数:
		@fd:设备文件描述符;
	        @serialctrl:控制SERIAL命令
        返回值:操作成功,返回0,否则返回-1
*/
extern int serial_config(int fd, struct serial_cmd *serialctrl);
/*
	函数功能:特殊的配置SERIAL的工作参数
	参数:
		@fd:设备文件描述符;
	        @serialctrl:控制SERIAL命令
        返回值:操作成功,返回0,否则返回-1
        说明:只更改MARK/SPACE,不刷新缓冲区
*/
extern int read_serial_config(int fd, struct serial_cmd *serialctrl);
/*
        函数功能:读取串口
	参数:
		@fd:设备文件描述符;
        返回值:操作成功,返回0,否则返回-1
 */
extern int serial_read(int fd, char *buf, int size);

/*
        函数功能:写串口
	参数:
		@fd:设备文件描述符;
        返回值:操作成功,返回0,否则返回-1
 */
extern int serial_write(int fd, char *buf, int size);

#ifdef __cplusplus
}
#endif

#endif

serial.c

#include "serial.h"

int serial_open(const char*filename)
{
	int fd = open(filename, O_RDWR|O_NOCTTY|O_SYNC);//不成为控制终端的控制程序(O_NOCTTY),并在完全写入硬件后返回(O_SYNC)
	if (fd < 0) {
		pr_debug("open serial device failed.\n");
		return -1;	
	}
	pr_debug("open serial device successfully!\n");
	return fd;
}

void serial_close(int fd) 
{
	pr_debug("close serial device...\n");
	close(fd);
}

static int speed_arr[] = {B115200,B38400,B19200,B9600,B4800,B2400,B1200,B300};
static int bps_arr[]  = {115200,38400,19200,9600,4800,2400,1200,300};

int serial_config(int fd, struct serial_cmd *serialctrl) 
{
        int i;
        int ret;
        int status;
        struct termios opt;  //串口设置结构体
        //检错
        if (fd < 0) {
                pr_debug("fd is invalid.\n");
                return -1;
        }

        if (serialctrl == NULL) {
                pr_debug("serialctrl is invalid.\n");
                return -1;
        }

        /*配置波特率*/
        if (tcgetattr(fd,&opt) != 0) //获取串口默认属性 
        {
                pr_debug("Get Serial Options Error");
                return(-1);
        }
        for(i=0;i<sizeof(speed_arr)/sizeof(int);i++) {
                if(serialctrl->bps==bps_arr[i])        //获取原有信息
                {       
                        tcflush(fd,TCIOFLUSH);          //刷新IO缓冲区
                        cfsetispeed(&opt,speed_arr[i]); //配置输入波特率
                        cfsetospeed(&opt,speed_arr[i]); //配置输出波特率
                        status = tcsetattr(fd,TCSANOW,&opt); //设置到硬件寄存器上,TCSANOW立即执行不等接受完,TCSADRAIN等待所有数据传递完成之后执行,TCSAFLUSH刷新输入输出缓冲,并且做出改变
                        if(status != 0)
                        {
                                pr_debug("Set Serial Bps Error");
                                return -1;
                        }
                }
                tcflush(fd,TCIOFLUSH); //刷新IO缓冲区
        }
        pr_debug("config serial %d bps successfully!\n", serialctrl->bps);
        /*配置数据位,停止位,奇偶校验位*/
        if(tcgetattr(fd,&opt) != 0)
        {
                pr_debug("Get Serial Options Error");
                return(-1);
        }
        opt.c_cflag &= ~CSIZE;//控制模式
        opt.c_iflag &= ~(IXON|IXOFF|IXANY); //用于解决0x11,0x13不能接受的问题,软件流控制屏蔽
        opt.c_iflag &= ~(INLCR|ICRNL|IGNCR);//输入屏蔽回车,换行等字符控制,0x0a,0x0d
        opt.c_oflag &= ~(ONLCR|OCRNL);      //输出屏蔽回车,换行等字符控制 
        //set data bit length
        switch (serialctrl->databits) /*设置数据位数*/
        {
                case 7:
                        opt.c_cflag |= CS7;
                        break;
                case 8:
                        opt.c_cflag |= CS8;
                        break;
                default:
                        pr_debug("Unsupported data size!\n");
                        return (-1);
        }
        pr_debug("config serial %d databits successfully!\n", serialctrl->databits);
        //set parity bit mode
        switch (serialctrl->parity)    //设置校验方式
        {
                case 'n':
                case 'N':
                        opt.c_cflag &= ~PARENB;		/* Clear parity enable */
                        opt.c_iflag &= ~INPCK;			/* Disable parity checking */
                        break;
                case 'o':
                case 'O':
                        opt.c_cflag |= (PARODD|PARENB);	/* 设置为奇效验*/ 
                        opt.c_iflag |= INPCK;			/* Enable parity checking */
                        break;
                case 'e':
                case 'E':
                        opt.c_cflag |= PARENB;     		/* Enable parity */
                        opt.c_cflag &= ~PARODD;   		/* 转换为偶效验*/  
                        opt.c_iflag |= INPCK;      		/* Enable parity checking */ 
                        break; 
                case 's': 
                case 'S':  				/*as no parity*/
                        opt.c_cflag |= PARENB|CMSPAR;
                        opt.c_cflag &=~PARODD;
                        opt.c_iflag |= INPCK;                    /*Enable parity checking*/
                        break;
                case 'm':                               /*mark parity*/
                case 'M':
                        opt.c_cflag |= PARENB|CMSPAR|PARODD;
                        opt.c_iflag |= INPCK;                    /*Enable parity checking*/
                        break;
                default:
                        pr_debug("Unsupported parity\n");
                        return (-1);
        }
        pr_debug("config serial %c databits successfully!\n", serialctrl->parity);
        //set stop bit length
        switch (serialctrl->stopbits) //设置停止位
        {
                case 1:
                        opt.c_cflag &= ~CSTOPB;
                        break;
                case 2:
                        opt.c_cflag |= CSTOPB;
                        break;
                default:
                        pr_debug("Unsupported stop bits\n");
                        return (-1);
        }
        pr_debug("config serial %d stopbits successfully!\n", serialctrl->stopbits);

        opt.c_lflag &= ~(ICANON|ECHO|ECHOE|ISIG);//本地控制标志,行方式输入,不经处理直接发送
        opt.c_oflag &= ~OPOST;
        opt.c_cflag |= (CLOCAL | CREAD);   
        //读取超时200ms,最小字节0个,全为0时,不阻塞
        opt.c_cc[VTIME] = 2;
        opt.c_cc[VMIN] = 0;
         
        tcflush(fd,TCIFLUSH);//刷新输入缓冲区			//Update the options and do it NOW
        if (tcsetattr(fd,TCSANOW,&opt) != 0)   //设置属性
        {
                pr_debug("Set Serial Options Error");
                return (-1);
        }

        pr_debug("config serial device successfully!\n");
        return 0;   //正常返回
}

int read_serial_config(int fd, struct serial_cmd *serialctrl) 
{
        struct termios opt;  //串口设置结构体
        //检错
        if (fd < 0) {
                pr_debug("fd is invalid.\n");
                return -1;
        }

        if (serialctrl == NULL) {
                pr_debug("serialctrl is invalid.\n");
                return -1;
        }
        /*配置校验位*/
        if(tcgetattr(fd,&opt) != 0)    //获取属性
        {
                pr_debug("Get Serial Options Error");
                return(-1);
        }
        //set parity bit mode,设置校验方式
        switch (serialctrl->parity) 
        {
                case 'n':                               //无校验
                case 'N':
                        opt.c_cflag &= ~PARENB;		/* Clear parity enable */
                        opt.c_iflag &= ~INPCK;			/* Disable parity checking */
                        break;
                case 'o':                               //奇校验
                case 'O':
                        opt.c_cflag |= (PARODD|PARENB);	/* 设置为奇效验*/ 
                        opt.c_iflag |= INPCK;			/* Enable parity checking */
                        break;
                case 'e':                               //偶校验
                case 'E':
                        opt.c_cflag |= PARENB;     		/* Enable parity */
                        opt.c_cflag &= ~PARODD;   		/* 转换为偶效验*/  
                        opt.c_iflag |= INPCK;      		/* Enable parity checking */ 
                        break; 
                case 's':                               //零校验
                case 'S':  				/*as no parity*/
                        opt.c_cflag |= PARENB|CMSPAR;
                        opt.c_cflag &=~PARODD;
                        opt.c_iflag |= INPCK;                    /*Enable parity checking*/
                        break;
                case 'm':                               /*mark parity,一校验*/
                case 'M':
                        opt.c_cflag |= PARENB|CMSPAR|PARODD;
                        opt.c_iflag |= INPCK;                    /*Enable parity checking*/
                        break;
                default:
                        pr_debug("Unsupported parity\n");
                        return (-1);
        }
        if (tcsetattr(fd,TCSANOW,&opt) != 0)   //设置属性
        {
                pr_debug("Set Serial Options Error");
                return (-1);
        }
        pr_debug("read_serial_config:config serial device successfully!\n");
        return 0;   //正常返回
}

int serial_read(int fd, char *buf, int size)
{
        int ret;
        unsigned long data;

	if (fd < 0) {
		pr_debug("fd is invalid.\n");
		return -1;
	}

        if (buf == NULL) {
            pr_debug("buf is invalid.\n");
            return -1;
        }
        
        if (size < 0) {
            pr_debug("size is invalid.\n");
            return -1;
        }

        ret = read(fd, buf, size);
        if (ret == -1) {
                pr_debug("read serial device failed.\n");
                return -1;
        }

        return ret;
}

int serial_write(int fd, char *buf, int size)
{
        int ret;
        unsigned long data;

	if (fd < 0) {
		pr_debug("fd is invalid.\n");
		return -1;
	}

        if (buf == NULL) {
            pr_debug("buf is invalid.\n");
            return -1;
        }
        
        if (size < 0) {
            pr_debug("size is invalid.\n");
            return -1;
        }

        ret = write(fd, buf, size);
        if (ret == -1) {
                pr_debug("write serial device failed.\n");
                return -1;
        }

        return ret;
}

serial_app.h

#ifndef SERIAL_APP_H
#define SERIAL_APP_H

#ifdef __cplusplus
extern "C"{
#endif
// 头文件
#include"serial.h"//串口文件

//调试宏
//#define DEBUG
#ifdef DEBUG
#define pr_debug(fmt, ...) printf(fmt,##__VA_ARGS__)
#else
#define pr_debug(fmt, ...)
#endif

/*
  功能:MARK,SPACE转常规校验字
  参数:数据字节,需求校验字
  返回值:校验字
*/
extern char getMarkSpaceParity(char data,char parity);
/*
  功能:发送markspace数据
  参数:文件描述符,数组指针,数据大小,串口参数指针,校验字(m,s)
  返回值:校验字
*/
extern int serial_write_MarkSpace(int fd, char *buf, int size,struct serial_cmd *serialctrl,char parity);

#ifdef __cplusplus
}
#endif

#endif

serial_app.c

#include"serial_app.h"

/*
  功能:MARK,SPACE转常规校验字
  参数:数据字节,需求校验字
  返回值:校验字
*/
char getMarkSpaceParity(char data,char parity){
    unsigned char num=0,i=0;
    char ret='n';//返回默认None
    for(i=0;i<8;i++){//遍历数据位,统计1个数
      char d=data>>i;
      if(d&0x01){
        num+=1;
      }
    }
    //根据数据
    if((parity=='s'||parity=='S')&&(num%2!=0)){//要s(0),数据为奇数
        ret='o';//奇校验
    }else if((parity=='s'||parity=='S')&&(num%2==0)){//要s(0),数据为偶数
        ret='e';//偶校验
    }if((parity=='m'||parity=='M')&&(num%2!=0)){//要m(1),数据为奇数
        ret='e';//偶校验
    }else if((parity=='m'||parity=='M')&&(num%2==0)){//要m(1),数据为偶数
        ret='o';//奇校验
    }else if(parity=='n'||parity=='N'){//无校验
        ret='n';
    }else if(parity=='o'||parity=='O'){//奇校验
        ret='o';
    }else if(parity=='e'||parity=='E'){//偶校验
        ret='e';
    }
    return ret;//返回校验字
}
/*
  功能:发送markspace数据
  参数:文件描述符,数组指针,数据大小,串口参数指针,校验字(m,s)
  返回值:0正常,1错误
*/
int serial_write_MarkSpace(int fd, char *buf, int size,struct serial_cmd *serialctrl,char parity){
   int ret=0,i=0;
   unsigned long data;
   struct serial_cmd newserialctrl;
   if (fd < 0) {
      pr_debug("fd is invalid.\n");
      return -1;
   }
   if (buf == NULL) {
      pr_debug("buf is invalid.\n");
      return -1;
   }  
   if (size <=0) {
      pr_debug("size is invalid.\n");
      return -1;
   }
   if (parity!='s'&&parity!='S'&&parity!='m'&&parity!='M'&&parity!='n'&&parity!='N'&&parity!='o'&&parity!='O'&&parity!='e'&&parity!='E'){
      pr_debug("parity is invalid.\n,your input parity:%c",parity);
      return -1;
   }
   memcpy((void*)&newserialctrl,(void*)serialctrl,sizeof(newserialctrl));//拷贝参数
   for(i=0;i<size;i++){//循环发送
     newserialctrl.parity=getMarkSpaceParity(buf[i],parity);//获取常规校验
     if(read_serial_config(fd, &newserialctrl) == -1){//配置串口参数
       printf("serial_config_test:fault\n");
       return -1;
     }
     usleep(50);//延时50us
     ret = write(fd, buf+i, 1);//每次发发送一个
     if (ret == -1) {
       pr_debug("write serial device failed.\n");
       return -1;
     }
     usleep(420);//延时420us,根据实际调整,delay_T(us)=1秒/(波特率/8)*1000000
   }
   return ret;
}

调用部分

#include "serial_app.h"

int main(int argc, char *argv[])
{
	int fd;
    struct serial_cmd serialctrl;
    char wbuf[100] = {0x40,0x46,0x06,0x01,0x8D,0x96,0x00};
	char rbuf[100] ={0};
    int i=0;
	fd = serial_open(SERIAL_DEVFILE_NAME0);
	if (fd < 0)return -1;
    serialctrl.bps = 19200;
    serialctrl.stopbits = 1;
    serialctrl.databits = 8;
    serialctrl.parity = 'n';
    if (serial_config(fd, &serialctrl) == -1){
        printf("serial_config:fault\n");   
        return -1;
    }
    while (1) {
      usleep(80000);//延时80ms
      if(serial_write_MarkSpace(fd, wbuf, 1,&serialctrl,'m') == -1){//发送地址字节,M校验位
          printf("serial_write_MarkSpace:fault\n");
          return -1;
      }
      if(serial_write_MarkSpace(fd, wbuf+1, 5,&serialctrl,'s') == -1){//发送数据数据,S校验位
          printf("serial_write_MarkSpace:fault\n");
          return -1;
      }
      //切换校验为N
      serialctrl.parity = 'n';
      if (read_serial_config(fd, &serialctrl) == -1){
          printf("read_serial_config:fault\n");
           return -1;
      }
      //循环接收数据,每次接收一个字节
      for(i=0;i<100;i++){
          if (serial_read(fd, rbuf+i,1) <=0){
              printf("serial_read:fault\n");
              break;
          }
      }
      printf("recv data is:");
      for(i=0;i<100;i++){
          printf("0X%X ",rbuf[i]);
      }
      printf("\n");
    } 
    serial_close(fd);//关闭串口
	return 0; 
}

三)总结

多机模式的通讯应用在以前的控制系统中比较常见,随着硬件的更新,多机模式的通讯应用不再主流,POSIX的可移植接口标准,对部分需求不再支持,需要我们变通解决问题。
  我是Simon,再这里期待您的关注。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值