从现在开始,对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... ]