• 嵌入式系统编程实现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); 
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202

    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);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196

    编译完成后,生成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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    执行这个脚本就可以得到最后的结果是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... ]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    就写到这里吧,下一篇准备研究232全双工的验证方法,可能要用到多线程,我现在去研究了.嘿嘿

    写作不易,关注,点赞再走呗!,共同进步!!!

  • 相关阅读:
    借AI之势,打破创意与想象的边界
    目标检测 YOLOv5 - 模型的输出
    ASP.NET-框架分类与详解
    FFmpeg开发笔记(十二)Linux环境给FFmpeg集成libopus和libvpx
    DSPE-PEG-Biotin,CAS:385437-57-0,磷脂-聚乙二醇-生物素可延长循环半衰期
    Runner GoUI自动化测试发布
    回调地狱、syn函数和await函数
    【Android笔记19】Android中的事件处理(监听和回调事件)
    【JAVA项目实战】【图书管理系统】用户添加功能【Servlet】+【Jsp】+【Mysql】
    Oracle数据库体系结构(二)_物理结构
  • 原文地址:https://blog.csdn.net/mainmaster/article/details/125640786