基于4412
1、基本概念
• 什么是串口通信
串口通信是指一次只传送一个数据位。虽然在通信的时候串口有8 位或者9 位等,但是在物理层面传输的时候,它仍然是以单个bit 的方式传输的。
• 什么是串口
虽然以太网以及USB 等等也是以串行的方式发送数据,但是串口一般特指RS232 标准的接口。
• 什么是RS232
RS-232 是EIA(Electronic Industries Association)定义的串行通信的电器接口。RS-232事实上有三种(A,B 和C),它们分别采用不同的电压来表示on 和off。最被广泛使用的是RS-232C,它将mark(on)比特的电压定义为-3V 到-12V 之间,而将space(off)的电压定义到+3V 到+12V 之间。虽然RS-232C 标准说信号最远被传输8m,但事实上你可以使用它传输更长的距离,直到信号波特率已经小到不行了为止。RS-232 的连结线中除去用来传入传出数据的电线,还有一些用来提供时序,状态和握手的电线。
• RS232 的针脚定义
RS232 有两种标准定义,25 针和8 针的。不过即使是8 针,在大多数场合工程师也觉得太多了,绝大多数情况下都是只使用TX,RX 针脚发送和接收信号,再加上一个GND 地脚。其它定义大家可以通过网络了解一下。
• 什么是流控
两个串行接口之间的传输数据流通常需要协调一致才行。这可能是由于用以通信的某个串行接口或者某些存储介质的中间串行通信链路的限制造成的。对于异步数据这里有两个方法做到这一点。
第一种方法通常被叫做**“软件”流控制**。这种方法采用特殊字符来开始(XON,DC1,八进制数021)或者结束(XOFF,DC3 或者八进制数023)数据流。而这些字符都在ASCII 中定义好了。虽然这些编码对于传输文本信息非常有用,但是它们却不能被用于在特殊程序中的其他类型的信息。
第二种方法叫做**“硬件”流控制**。这种方法使用RS-232 标准的CTS 和RTS 信号来取代之前提到的特殊字符。当准备就绪时,接受一方会将CTS 信号设置成为space 电压,而尚未准备就绪时它会被设置成为mark 电压。相应得,发送方会在准备就绪的情况下将RTS 设置成space电压。正因为硬件流控制使用了于数据分隔的信号,所以与需要传输特殊字符的软件流控制相比它的速度很快。但是,并不是所有的硬件和操作系统都支持CTS/RTS 流控制。
特殊字符–软件流、信号–硬件流
串口编程流程
开始
打开串口
初始化串口
发送和接收数据
关闭
2、开机自启动
将可执行程序拷贝到 /bin
修改其权限
chmod 777 test
接着使用命令 vi /etc/init.d/rcS 打开文件系统的启动文件
接着进入文件的最后一行,添加 /bin/test &
3、打开串口(确定设备文件)
如何确认设备节点
– 串口选取“靠近耳机接口”的con2
– 通过原理图确定串口号ttySACx
uartopen.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
void main(){
int fd;
char *uart3 = "/dev/ttySAC3";
if((fd = open(uart3,O_RDWR|O_CREAT,0777))<0){
printf("open %s failed!\n",uart3);
}
else{
printf("open %s is success!\n",uart3);
}
close(fd);
}
4、串口初始化
• 串口初始化结构介绍
– 通过串口助手初步了解初始化参数
– 使用 source insight 查看内核参数定义的源码
– 内核目录“ \arch\arm\include\asm\termios.h”
• 结构体termio
– 常用参数
• 串口初始化步骤是
– 读取当前参数
– 修改参数
– 配置参数
• 函数 tcgetattr
– 命令 man 3 tcgetattr
• 读取当前参数函数
• int tcgetattr(int fd, struct termios *termios_p);
– 参数1:fd 是open 返回的文件句柄。
– 参数2:*termios_p 是前面介绍的结构体。
– 在初始化开始调用这个函数
• 获取当前波特率函数
• speed_t cfgetispeed(const struct termios *termios_p);
• speed_t cfgetospeed(const struct termios *termios_p);
– *termios_p:前面介绍的结构体
– 失败返回-1;成功返回波特率
• 波特率设置函数
• int cfsetispeed(struct termios *termios_p, speed_t speed);
• int cfsetospeed(struct termios termios_p, speed_t speed);
– 参数termios_p:前面介绍的结构体。
– 参数speed:speed 波特率,常用的B2400,B4800,B9600,B115200,
B460800 等等。
– 执行成功返回0,失败返回-1
• 清空串口BUFFER中的数据函数
• int tcflush(int fd, int queue_selector);
– 参数1:fd 是open 返回的文件句柄。
– 参数2:控制tcflush 的操作。有三个常用数值,
TCIFLUSH 清除正收到的数据,且不会读取出来;
TCOFLUSH 清除正写入的数据,且不会发送至终端;
TCIOFLUSH 清除所有正在发生的I/O 数据。
– 执行成功返回0,失败返回-1
• 设置串口参数函数
• int tcsetattr(int fd, int optional_actions,const struct termios termios_p);
– 参数fd:open 返回的文件句柄。
– 参数optional_actions:参数生效的时间。有三个常用的值:TCSANOW:
不等数据传输完毕就立即改变属性;TCSADRAIN:等待所有数据传输结束
才改变属性;TCSAFLUSH:清空输入输出缓冲区才改变属性。
– 参数termios_p :在旧的参数基础上修改的后的参数。
– 返回值:执行成功返回0,失败返回-1
– 一般在初始化最后会使用这个函数。
• 提供了一个文本“Linux下串口编程遇到的接收数据错误及原因.txt”
5、使用串口发送数据
uartwrite.c
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <errno.h>
int set_opt(int,int,int,char,int);
void main()
{
int fd,wr_static,i=10;
char *uart3 = "/dev/ttySAC3";
char *buffer = "hello world!\n";
printf("\r\nitop4412 uart3 writetest start\r\n");
if((fd = open(uart3, O_RDWR|O_NOCTTY|O_NDELAY))<0){
printf("open %s is failed",uart3);
}
else{
printf("open %s is success\n",uart3);
set_opt(fd, 115200, 8, 'N', 1);
while(i--)
{
wr_static = write(fd,buffer, strlen(buffer));
if(wr_static<0)
printf("write failed\n");
else{
printf("wr_static is %d\n",wr_static);
}
sleep(1);
}
}
close(fd);
}
int set_opt(int fd,int nSpeed, int nBits, char nEvent, int nStop)
{
struct termios newtio,oldtio;
if ( tcgetattr( fd,&oldtio) != 0) {
perror("SetupSerial 1");
return -1;
}
bzero( &newtio, sizeof( newtio ) );
newtio.c_cflag |= CLOCAL | CREAD;
newtio.c_cflag &= ~CSIZE;
switch( nBits )
{
case 7:
newtio.c_cflag |= CS7;
break;
case 8:
newtio.c_cflag |= CS8;
break;
}
switch( nEvent )
{
case 'O':
newtio.c_cflag |= PARENB;
newtio.c_cflag |= PARODD;
newtio.c_iflag |= (INPCK | ISTRIP);
break;
case 'E':
newtio.c_iflag |= (INPCK | ISTRIP);
newtio.c_cflag |= PARENB;
newtio.c_cflag &= ~PARODD;
break;
case 'N':
newtio.c_cflag &= ~PARENB;
break;
}
switch( nSpeed )
{
case 2400:
cfsetispeed(&newtio, B2400);
cfsetospeed(&newtio, B2400);
break;
case 4800:
cfsetispeed(&newtio, B4800);
cfsetospeed(&newtio, B4800);
break;
case 9600:
cfsetispeed(&newtio, B9600);
cfsetospeed(&newtio, B9600);
break;
case 115200:
cfsetispeed(&newtio, B115200);
cfsetospeed(&newtio, B115200);
break;
case 460800:
cfsetispeed(&newtio, B460800);
cfsetospeed(&newtio, B460800);
break;
default:
cfsetispeed(&newtio, B9600);
cfsetospeed(&newtio, B9600);
break;
}
if( nStop == 1 )
newtio.c_cflag &= ~CSTOPB;
else if ( nStop == 2 )
newtio.c_cflag |= CSTOPB;
newtio.c_cc[VTIME] = 0;
newtio.c_cc[VMIN] = 0;
tcflush(fd,TCIFLUSH);
if((tcsetattr(fd,TCSANOW,&newtio))!=0)
{
perror("com set error");
return -1;
}
// printf("set done!\n\r");
return 0;
}
6、使用串口接收数据
uartread.c
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <errno.h>
int set_opt(int,int,int,char,int);
//"/dev/ttySAC3"是con2,靠近耳机接口的串口
void main()
{
int fd,nByte;
char *uart3 = "/dev/ttySAC3";
char buffer[512];
char *uart_out = "please input\r\n";
memset(buffer, 0, sizeof(buffer));
if((fd = open(uart3, O_RDWR|O_NOCTTY))<0)
printf("open %s is failed",uart3);
else{
set_opt(fd, 115200, 8, 'N', 1);
write(fd,uart_out, strlen(uart_out));
while(1){
while((nByte = read(fd, buffer, 512))>0){
buffer[nByte+1] = '\0';
write(fd,buffer,strlen(buffer));
memset(buffer, 0, strlen(buffer));
nByte = 0;
}
}
}
}
int set_opt(int fd,int nSpeed, int nBits, char nEvent, int nStop)
{
struct termios newtio,oldtio;
if ( tcgetattr( fd,&oldtio) != 0) {
perror("SetupSerial 1");
return -1;
}
bzero( &newtio, sizeof( newtio ) );
newtio.c_cflag |= CLOCAL | CREAD;
newtio.c_cflag &= ~CSIZE;
switch( nBits )
{
case 7:
newtio.c_cflag |= CS7;
break;
case 8:
newtio.c_cflag |= CS8;
break;
}
switch( nEvent )
{
case 'O':
newtio.c_cflag |= PARENB;
newtio.c_cflag |= PARODD;
newtio.c_iflag |= (INPCK | ISTRIP);
break;
case 'E':
newtio.c_iflag |= (INPCK | ISTRIP);
newtio.c_cflag |= PARENB;
newtio.c_cflag &= ~PARODD;
break;
case 'N':
newtio.c_cflag &= ~PARENB;
break;
}
switch( nSpeed )
{
case 2400:
cfsetispeed(&newtio, B2400);
cfsetospeed(&newtio, B2400);
break;
case 4800:
cfsetispeed(&newtio, B4800);
cfsetospeed(&newtio, B4800);
break;
case 9600:
cfsetispeed(&newtio, B9600);
cfsetospeed(&newtio, B9600);
break;
case 115200:
cfsetispeed(&newtio, B115200);
cfsetospeed(&newtio, B115200);
break;
case 460800:
cfsetispeed(&newtio, B460800);
cfsetospeed(&newtio, B460800);
break;
default:
cfsetispeed(&newtio, B9600);
cfsetospeed(&newtio, B9600);
break;
}
if( nStop == 1 )
newtio.c_cflag &= ~CSTOPB;
else if ( nStop == 2 )
newtio.c_cflag |= CSTOPB;
newtio.c_cc[VTIME] = 0;
newtio.c_cc[VMIN] = 0;
tcflush(fd,TCIFLUSH);
if((tcsetattr(fd,TCSANOW,&newtio))!=0)
{
perror("com set error");
return -1;
}
// printf("set done!\n\r");
return 0;
}
7、Linux下串口编程遇到的接收数据错误及原因
笔记章节(4412)