嵌入式系统编程实现485串口收发数据

从现在开始,对485串口进行研究,第一步先研究下485数据的收发,想完成的目的是分2个程序,一个收,一个发,这样设计的原因是因为485属于半双工,如果收发同时操作,那么在一个开发板内部测试会出现问题,如产生乱码的现象,具体全双工模式,咱们以后在实现.
不说废话了,上代码:
1.485send.c

/*
 * @Author: Yu Fu Chang
 * @Date: 2022-06-27 10:51:59
 * @LastEditTime: 2022-07-05 16:57:49
 * @LastEditors: Yu Fu Chang
 * @Description: 用于测试485半双工的通讯,和485read进程配合使用
 * @FilePath: \armc\ttysend\main.c
 * 个性签名
 */

#include           /*标准输入输出定义*/
#include          /*标准函数库定义*/
#include          /*Unix标准函数定义*/
#include       /**/
#include        /**/
#include           /*文件控制定义*/
#include         /*PPSIX终端控制定义*/
#include           /*错误号定义*/
#include	 
#include     
#include     
#include     

#define TRUE 1
#define FALSE -1

/**
 * @description: 打印帮助使用说明
 * @param {char} *pname
 * @return {*}
 */
static void print_usage(const char *pname)
{
    printf("Usage: %s -d devicesname -b baudrate -n times"
           "\n\t Examples : %s -d /dev/ttymxc3"
           "\n\t %s -d /dev/ttymxc4 -b 115200 "
           "\n\t %s -d /dev/ttymxc2 -b 9600 -n 10 (default,0:forever)\n",           
           pname,pname,pname,pname);
}

/**
 * @description: 波特率转换
 * @param {int} baudrate
 * @return {*}
 */
static speed_t getBaudrate(int baudrate)
{
    switch(baudrate) {
        case 0: return B0;
        case 50: return B50;
        case 75: return B75;
        case 110: return B110;
        case 134: return B134;
        case 150: return B150;
        case 200: return B200;
        case 300: return B300;
        case 600: return B600;
        case 1200: return B1200;
        case 1800: return B1800;
        case 2400: return B2400;
        case 4800: return B4800;
        case 9600: return B9600;
        case 19200: return B19200;
        case 38400: return B38400;
        case 57600: return B57600;
        case 115200: return B115200;
        case 230400: return B230400;
        case 460800: return B460800;
        case 500000: return B500000;
        case 576000: return B576000;
        case 921600: return B921600;
        case 1000000: return B1000000;
        case 1152000: return B1152000;
        case 1500000: return B1500000;
        case 2000000: return B2000000;
        case 2500000: return B2500000;
        case 3000000: return B3000000;
        case 3500000: return B3500000;
        case 4000000: return B4000000;
        default: return -1;
    }
}

/**
 * @description: 主函数 
 * @param {int} argc
 * @param {char} *
 * @return {*}
 */
int main(int argc, char **argv)
{
    int fd;
    int nread;
    long count=10;                                      //默认发送10次,
    speed_t speed = B115200;                            //默认波特率为115200    
    char test[100]="forlinx_uart_test.1234567890...";   //定义发送的默认字符串
    int sendforflag=0;                                  //死循环发送标志,=1 ,无限循环标记
    char buffer[512];
    int n=0,i=0;
    char* dev  = NULL;
    struct termios oldtio,newtio;

    long index=1;

    int next_option,devflag = 0,countflag=0,speedflag=0,flow = 0;
    const char *const short_opt = "hd:n:b:";
    const struct option long_opt[] = {
        {"devices",1,NULL,'d'},
        {"counts",1,NULL,'n'},
        {"speed",1,NULL,'b'},
        {NULL,0,NULL,0},
    };
    //分析参数
    do{
        next_option = getopt_long(argc,argv,short_opt,long_opt,NULL);
        //printf("params is %s\n",optarg);
        switch (next_option) {
            case 'd':
                dev = optarg;
                devflag = 1;
                break;           
            case 'n':
                count=atol(optarg);
                if(count==0)    sendforflag=1;                
                countflag=1;
                break;
            case 'b':
                speed=getBaudrate(atoi(optarg));
                speedflag=1;
                break;
            case '?':
                print_usage(argv[0]);
                break;    
            case -1:
                if(devflag)
                    break;
            default:
                print_usage(argv[0]);
                exit(1);

        }
    }while(next_option != -1);

    if(dev == NULL)
    {
        printf("Please input seria device name ,for exmaple /dev/ttySAC0 -n 3.\n");
	
            exit(1);
    }	

    /*  打开串口 */
    fd = open(dev, O_RDWR | O_NONBLOCK| O_NOCTTY | O_NDELAY); 
    // O_RDWR表示打开 的文件可读/写,O_NDELAY表示以非阻塞方式打开,
    // O_NOCTTY表示若打开的文件为终端设备,则不会将终端作为进程控制终端(即使串口上发来ctrl+c数据也不会影响本终端)。
    if (fd < 0)	{
        printf("Can't Open Serial Port!\n");
        exit(0);	
    }
    else
    {
        printf("Welcome to uart test\n");
    }	
	
     //save to oldtio
    tcgetattr(fd,&oldtio);                      //读取串口配置
    //将newtio结构清零
    bzero(&newtio,sizeof(newtio));
    //设置控制模式      CLOCAL 忽略DCD信号,若不使用MODEM,或没有串口没有CD脚就设置此标志
    //                 CREAD  启用接收装置,可以接收字符
    //                  CSTOPB   使用两个停止位,若用一位应设置~CSTOPB
    //                  PARENB   启用奇偶校验
    newtio.c_cflag = speed|CS8|CLOCAL|CREAD;
    newtio.c_cflag &= ~CSTOPB;
    newtio.c_cflag &= ~PARENB;
    //设置输入模式
    //IGNPAR  忽略校验错误
    newtio.c_iflag = IGNPAR;  
    newtio.c_oflag = 0;
    //为保证读/写操作不被串口缓冲区中原有的数据干拢,可以在读/写数据前用tcflush()函数清空串口发送/接收缓冲区
    //TCIFLUSH      清空输入队列        TCOFLUSH   清空输出队列        TCIOFLUSH   同时清空输入和输出队列
    tcflush(fd,TCIFLUSH);  
    //设置终端属性      TCSANOW         改变立即生效
    //   TCSADRAIN      在所有的输出都被传输后改变生效,适用于更改影响输出参数的情况。
    //   TCSAFLUSH    在所有输出都被传输后改变生效,丢弃所有末读入的输入(清空输入缓存)。
    tcsetattr(fd,TCSANOW,&newtio);  
    //重新读取串口配置
    tcgetattr(fd,&oldtio);
	//将内存buff初始化为0
    memset(buffer,0,sizeof(buffer));

    while(1)
    {      
        printf("Send test %d data:[ %s ]\n",index++,test);
        write(fd, test, strlen(test) + 1);
        if( index > count && sendforflag==0 )
        {
            exit(EXIT_SUCCESS);
        }
        sleep(1);
    }
    close(fd); 
}

2.485read.c

/*
 * @Author: Yu Fu Chang
 * @Date: 2022-06-27 10:51:59
 * @LastEditTime: 2022-07-05 17:05:52
 * @LastEditors: Yu Fu Chang
 * @Description: 用于测试485半双工通讯,配合485send使用
 * @FilePath: \armc\ttyread\main.c
 * 个性签名
 */
#include           /*标准输入输出定义*/
#include          /*标准函数库定义*/
#include          /*Unix标准函数定义*/
#include       /**/
#include        /**/
#include           /*文件控制定义*/
#include         /*PPSIX终端控制定义*/
#include           /*错误号定义*/
#include	 
#include     
#include     
#include     

#define TRUE 1
#define FALSE -1

/**
 * @description:打印帮助说明 
 * @param {char} *pname
 * @return {*}
 */
static void print_usage(const char *pname)
{
    printf("Usage: %s -d devicesname"
           "\nExamples: %s -d /dev/ttyXRUSB0 -b 115200"
           "\nreceive data is correct ,then save to file '485'\n",
           pname,pname);
}

/**
 * @description: 删除文件
 * @param {char} *filename
 * @return {*}
 */
static void removefile(char *filename)
{
    FILE *fp = NULL;//recvive data save to file
    if((access(filename,F_OK))!=-1)
    {
        remove(filename);
    }   
}
/**
 * @description: 转换波特率 
 * @param {int} baudrate
 * @return {*}
 */
static speed_t getBaudrate(int baudrate)
{
    switch(baudrate) {
        case 0: return B0;
        case 50: return B50;
        case 75: return B75;
        case 110: return B110;
        case 134: return B134;
        case 150: return B150;
        case 200: return B200;
        case 300: return B300;
        case 600: return B600;
        case 1200: return B1200;
        case 1800: return B1800;
        case 2400: return B2400;
        case 4800: return B4800;
        case 9600: return B9600;
        case 19200: return B19200;
        case 38400: return B38400;
        case 57600: return B57600;
        case 115200: return B115200;
        case 230400: return B230400;
        case 460800: return B460800;
        case 500000: return B500000;
        case 576000: return B576000;
        case 921600: return B921600;
        case 1000000: return B1000000;
        case 1152000: return B1152000;
        case 1500000: return B1500000;
        case 2000000: return B2000000;
        case 2500000: return B2500000;
        case 3000000: return B3000000;
        case 3500000: return B3500000;
        case 4000000: return B4000000;
        default: return -1;
    }
}
/**
 * @description: 主函数
 * @param {int} argc
 * @param {char} *
 * @return {*}
 */
int main(int argc, char **argv)
{
    int fd;
    FILE *fp = NULL;//recvive data save to file
    char *filename="485"; //接收数据保存的文件名称
    int nread;
    char buffer[512];
    int n=0,i=0;
    char* dev  = NULL;
    struct termios oldtio,newtio;
	speed_t speed = B115200;
    int next_option,havearg = 0,flow = 0;
    const char *const short_opt = "fd:b:";
    const struct option long_opt[] = {
        {"devices",1,NULL,'d'},
        {"speed",1,NULL,'b'},        
        {NULL,0,NULL,0},
    };
    do{
        next_option = getopt_long(argc,argv,short_opt,long_opt,NULL);
        switch (next_option) {
            case 'd':
                dev = optarg;
                havearg = 1;
                break;
            case 'b':
                speed=getBaudrate(atoi(optarg));
                break;
            case '?':
                print_usage(argv[0]);
                break;
            case -1:
                if(havearg)
                    break;
            default:
                print_usage(argv[0]);
                exit(1);

        }
    }while(next_option != -1);

    removefile(filename);//remove  file 485

    if(dev == NULL)
    {
        printf("Please input seria device name ,for exmaple /dev/ttySAC0.\nNote:This is loop test application. Make sure that your serial is loop\n");           
        exit(1);
    }	

    /*  打开串口 */
    fd = open(dev, O_RDWR | O_NONBLOCK| O_NOCTTY | O_NDELAY); 
    if (fd < 0)	{
        printf("Can't Open Serial Port!\n");
        exit(0);	
    }
	
	printf("Welcome to uart test\n");
	
    //save to oldtio
    tcgetattr(fd,&oldtio);
    bzero(&newtio,sizeof(newtio));
    newtio.c_cflag = speed|CS8|CLOCAL|CREAD;
    newtio.c_cflag &= ~CSTOPB;
    newtio.c_cflag &= ~PARENB;
    newtio.c_iflag = IGNPAR;  
    newtio.c_oflag = 0;
    tcflush(fd,TCIFLUSH);  
    tcsetattr(fd,TCSANOW,&newtio);  
    tcgetattr(fd,&oldtio);
	
    memset(buffer,0,sizeof(buffer));
    char test[100]="forlinx_uart_test.1234567890...";
	fd_set rd;
    
    while(1)
    {        
	    nread = read(fd, &buffer[n], strlen(test) + 1);
        //if (strlen(test) == strlen(buffer))
        //这里不判断长度,计时有丢报文或者乱码的情况也是可以显示的,易于判断错误
        if(nread>0)
        {
            printf("Read Test Data finished,Read:[ %s ]\n",buffer);
            //将接收到的数据保存到文件中,可以进行文件比较
            fp = fopen (filename, "a");
            fprintf(fp,"%s:\t%s\n",dev,buffer);
            //printf("dev=%s,buffer=%s\n",dev,buffer);    
            fclose(fp);

            memset(buffer,0,sizeof(buffer));
            tcflush(fd, TCIOFLUSH);
        }
	    usleep(300000);
    }
    printf("receive data is saveed to file: 485 \n");
    sleep(2);    
    close(fd);
}

编译完成后,生成2个文件 485send,485read.
3.测试方法和步骤
1)将程序拷贝到开发板,如果开发板有2个485接口,那么将H,L接到一起.脚本执行如下:
./ttyread -d devicename &
./ttysend -d devicename -n 10
就可以看到发送和接收的报文了.
另外我们从代码可以看到,接收方接收到数据后将数据保存到了文件485中,我们可以利用这个点对文件进行比较.
先手动执行接收发送10次,将485的文件拷贝成文件485a,作为一个标准文件,当然要先确认下这个文件的正确性,如我的是这个样子的.
/dev/ttymxc2: forlinx_uart_test.1234567890…
/dev/ttymxc2: forlinx_uart_test.1234567890…
/dev/ttymxc2: forlinx_uart_test.1234567890…
/dev/ttymxc2: forlinx_uart_test.1234567890…
/dev/ttymxc2: forlinx_uart_test.1234567890…
/dev/ttymxc2: forlinx_uart_test.1234567890…
/dev/ttymxc2: forlinx_uart_test.1234567890…
/dev/ttymxc2: forlinx_uart_test.1234567890…
/dev/ttymxc2: forlinx_uart_test.1234567890…
/dev/ttymxc2: forlinx_uart_test.1234567890…
然后编辑脚本 485_compare.sh如下

#!/bin/bash
index=1
while true
do
	echo "test ${index}  1->1  starting "
	./485read -d /dev/ttySC2 -b 9600 & 
	sleep 1s
	./485send -d /dev/ttySC2 -b 9600 -n 10
	sleep 2s
	killall 485read
	sleep 2s
	diff 485 485a
	if [ $? -eq 0 ]
	then
		echo -e "\e[0;32;1m|          1->2 OK           |\e[0m"
	else
		echo -e "\e[0;32;1m|          1->2 FAIL           |\e[0m"
	fi
	rm 485
 
	index=$((${index}+1))
done

执行这个脚本就可以得到最后的结果是OK,还是FAIL,这样测试更加的精确.
2)2块开发板对测
在开发板上分别执行发送和接收的程序
在接收端就可以看到发送的数据了,可以调整设备名称,波特率,发送次数等参数.举例如下:

root@okg2l:~# ./485send -d /dev/ttySC2 -b 9600 -n 100
Welcome to uart test
Send test 1 data:[ forlinx_uart_test.1234567890... ]
Send test 2 data:[ forlinx_uart_test.1234567890... ]
Send test 3 data:[ forlinx_uart_test.1234567890... ]
Send test 4 data:[ forlinx_uart_test.1234567890... ]
Send test 5 data:[ forlinx_uart_test.1234567890... ]

oot@fl-imx6ull:~/1# ./485read -d /dev/ttymxc1 -b9600
Welcome to uart test
Read Test Data finished,Read:[ forlinx_uart_test.1234567890... ]
Read Test Data finished,Read:[ forlinx_uart_test.1234567890... ]
Read Test Data finished,Read:[ forlinx_uart_test.1234567890... ]
Read Test Data finished,Read:[ forlinx_uart_test.1234567890... ]
Read Test Data finished,Read:[ forlinx_uart_test.1234567890... ]
Read Test Data finished,Read:[ forlinx_uart_test.1234567890... ]

你可能感兴趣的:(嵌入式,C,编程,linux,c++)