资源描述
实验2串行端口程序设计一、实验目的1了解在 linux 环境下串行程序设计的基本方法。2掌握终端的主要属性及设置方法,熟悉终端I /O 函数的使用。3学习使用多线程来完成串口的收发处理。二、实验内容读懂程序源代码, 学习终端I/O 函数的使用方法,学习将多线程编程应用到串口的接收和发送程序设计中。三、实验设备及工具硬件:UP-TECH S2410/P270 DVP 嵌入式实验平台、PC 机Pentium 500 以上, 硬盘10G以上。软件:PC 机操作系统REDHAT LINUX 9.0超级终端(或X-shell)ARM-LINUX 开发环境。四、实验原理异步串行 I/O 方式是将传输数据的每个字符一位接一位(例如先低位、后高位)地传送。数据的各不同位可以分时使用同一传输通道,因此串行I/O 可以减少信号连线,最少用一对线即可进行。接收方对于同一根线上一连串的数字信号,首先要分割成位,再按位组成字符。为了恢复发送的信息,双方必须协调工作。在微型计算机中大量使用异步串行I/O方式,双方使用各自的时钟信号,而且允许时钟频率有一定误差,因此实现较容易。但是由于每个字符都要独立确定起始和结束(即每个字符都要重新同步),字符和字符间还可能有长度不定的空闲时间,因此效率较低。上图给出异步串行通信中一个字符的传送格式。开始前,线路处于空闲状态,送出连续“1”。传送开始时首先发一个“”作为起始位,然后出现在通信线上的是字符的二进制编码数据。每个字符的数据位长可以约定为5 位、6 位、7 位或8 位,一般采用ASCII编码。后面是奇偶校验位,根据约定,用奇偶校验位将所传字符中为“1”的位数凑成奇数个或偶数个。也可以约定不要奇偶校验,这样就取消奇偶校验位。最后是表示停止位的“1”信号,这个停止位可以约定持续1 位、1.5 位或2 位的时间宽度。至此一个字符传送完毕,线路又进入空闲,持续为“1”。经过一段随机的时间后,下一个字符开始传送才又发出起始位。每一个数据位的宽度等于传送波特率的倒数。微机异步串行通信中,常用的波特率为50,95,110,150,300,600,1200,2400,4800,9600 等。五、程序分析Linux 操作系统从一开始就对串行口提供了很好的支持,为进行串行通讯提供了大量的函数,我们的实验主要是为掌握在Linux 中进行串行通讯编程的基本方法。本实验的程序流程图如下所示。实验代码如下:#include #include #include #include #include #include #define BAUDRATE B115200#define COM1 /dev/ttyS0#define COM2 /dev/ttyS1#define ENDMINITERM 27 /* ESC to quit miniterm */#define FALSE 0#define TRUE 1volatile int STOP=FALSE;volatile int fd;void child_handler(int s)printf(stop!n);STOP=TRUE;/*-*/void* keyboard(void * data)int c;for (;)c=getchar();if( c= ENDMINITERM)STOP=TRUE;break ;return NULL;/*-*/* modem input handler */void* receive(void * data)int c;printf(read modemn);while (STOP=FALSE)read(fd,&c,1); /* com port */write(1,&c,1); /* stdout */printf(exit from reading modemn);return NULL;/*-*/void* send(void * data)int c=0;printf(send datan);while (STOP=FALSE) /* modem input handler */c+;c %= 255;write(fd,&c,1); /* stdout */usleep(100000);return NULL; /*-*/int main(int argc,char* argv)struct termios oldtio,newtio,oldstdtio,newstdtio;struct sigaction sa;int ok;pthread_t th_a, th_b, th_c;void * retval;if( argc 1)fd = open(COM2, O_RDWR );elsefd = open(COM1, O_RDWR ); /| O_NOCTTY |O_NONBLOCK);if (fd 0) error(COM1);exit(-1);tcgetattr(0,&oldstdtio);tcgetattr(fd,&oldtio); /* save current modem settings */tcgetattr(fd,&newstdtio); /* get working stdtio */newtio.c_cflag = BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD; /*ctrol flag*/newtio.c_iflag = IGNPAR; /*input flag*/newtio.c_oflag = 0; /*output flag*/newtio.c_lflag = 0;newtio.c_ccVMIN=1;newtio.c_ccVTIME=0;/* now clean the modem line and activate the settings for modem */tcflush(fd, TCIFLUSH);tcsetattr(fd,TCSANOW,&newtio); /*set attrib*/sa.sa_handler = child_handler;sa.sa_flags = 0;sigaction(SIGCHLD,&sa,NULL); /* handle dying child */pthread_create(&th_a, NULL, keyboard, 0);pthread_create(&th_b, NULL, receive, 0);pthread_create(&th_c, NULL, send, 0);pthread_join(th_a, &retval);pthread_join(th_b, &retval);pthread_join(th_c, &retval);tcsetattr(fd,TCSANOW,&oldtio); /* restore old modem setings */tcsetattr(0,TCSANOW,&oldstdtio); /* restore old tty setings */close(fd);exit(0);下面我们对这个程序的主要部分做一下简单的分析头文件:#include /*标准输入输出定义*/#include /*标准函数库定义*/#include /*linux 标准函数定义*/#include #include #include /*文件控制定义*/#include /*PPSIX 终端控制定义*/#include /*错误号定义*/#include /*线程库定义*/打开串口:在 Linux 下串口文件位于/dev 下,一般在老版本的内核中串口一/dev/ttyS0 ,串口二为 /dev/ttyS1, 在我们的开发板中串口设备位于/dev/tts/下,因为开发板中没有ttyS0这个设备,所以我们要建立一个连接,方法如下:/mnt/yaffs cd /dev/dev ln sf /dev/tts/0 ttyS0打开串口是通过标准的文件打开函数来实现的int fd;fd = open( /dev/ttyS0, O_RDWR); /*以读写方式打开串口*/if (-1 = fd) /* 不能打开串口一*/perror( 提示错误!);串口设置最基本的设置串口包括波特率设置,效验位和停止位设置。串口的设置主要是设置struct termios 结构体的各成员值, 关于该结构体的定义可以查看/arm2410cl/kernel/linux-2410cl/include/asm/termios.h 文件。struct termiounsigned short c_iflag; /* 输入模式标志 */unsigned short c_oflag; /* 输出模式标志 */unsigned short c_cflag; /* 控制模式标志 */unsigned short c_lflag; /* local mode flags */unsigned char c_line; /* line discipline */unsigned char c_ccNCC; /* control characters */;波特率设置:下面是修改波特率的代码:struct termios Opt;tcgetattr(fd, &Opt);cfsetispeed(&Opt,B19200); /*设置为19200Bps*/cfsetospeed(&Opt,B19200);tcsetattr(fd,TCANOW,&Opt);校验位和停止位的设置:无效验 8 位Option.c_cflag &= PARENB;Option.c_cflag &= CSTOPB;Option.c_cflag &= CSIZE;Option.c_cflag |= CS8;奇效验(Odd) 7 位Option.c_cflag |= PARENB;Option.c_cflag &= PARODD;Option.c_cflag &= CSTOPB;Option.c_cflag &= CSIZE;Option.c_cflag |= CS7;偶效验(Even) 7 位Option.c_cflag &= PARENB;Option.c_cflag |= PARODD;Option.c_cflag &= CSTOPB;Option.c_cflag &= CSIZE;Option.c_cflag |= CS7;Space 效验 7 位Option.c_cflag &= PARENB;Option.c_cflag &= CSTOPB;Option.c_cflag &= &CSIZE;Option.c_cflag |= CS8;设置停止位:1 位:options.c_cflag &= CSTOPB;2 位:options.c_cflag |= CSTOPB;注意:如果不是开发终端之类的,只是串口传输数据,而不需要串口来处理,那么使用原始模式(RawMode)方式来通讯,设置方式如下:options.c_lflag &= (ICANON | ECHO | ECHOE | ISIG); /*Input*/options.c_oflag &= OPOST; /*Output*/读写串口设置好串口之后,读写串口就很容易了,把串口当作文件读写就可以了。发送数据:char buffer1024;int Length1024;int nByte;nByte = write(fd, buffer ,Length)读取串口数据:使用文件操作read 函数读取,如果设置为原始模式(Raw Mode)传输数据,那么read函数返回的字符数是实际串口收到的字符数。可以使用操作文件的函数来实现异步读取,如fcntl,或者select 等来操作。char buff1024;int Len1024;int readByte = read(fd, buff, Len); 关闭串口关闭串口就是关闭文件。close(fd);七、实验步骤1、阅读理解源码进入 expbasic03_tty 目录,使用vi 编辑器或其他编辑器阅读理解源代码。2、编译应用程序运行 make 产生term 可执行文件rootzxt root# cd /arm2410cl/exp/basic/03_tty/rootzxt 03_tty# makearmv4l-unknown-linux-gcc -c -o term.o term.carmv4l-unknown-linux-gcc -o ./bin/term term.o -lpthreadarmv4l-unknown-linux-gcc -o term term.o -lpthreadrootzxt 03_tty# lsMakefile Makefile.bak term term.c term.o tty.c3、下载调试到超级终端窗口,使用 NFS mount 开发主机的/arm2410cl 到/host 目录。进入expbasic03_tty 目录,运行term,观察运行结果的正确性。/mnt/yaffs mount -t nfs -o nolock 192.168.0.56:/arm2410cl /host/mnt/yaffscd /host/exp/basic/03_tty/host/exp/basic/03_tty./termread modemsend data123456789:;?ABCDEFGHIJKLMNOPQRSTUVWX注意:如果在执行./term 时出现下面的错误,可以通过我们前文提到的方法建立一个连接来解决。/dev/ttyS0: No such file or directory解决方法:/mnt/yaffs cd /dev/dev ln sf /dev/tts/0 ttyS0 (注意首字母是l, 不是数字1)由于内核已经将串口 1 作为终端控制台,所以可以看到term 发出的数据,却无法看到开发主机发来的数据,可以使用另外一台主机连接串口2 进行收发测试; Ctrl+c 或者ESC 可使程序强行退出。
展开阅读全文