# 本人所使用的是 ARM tiny6410开发板。
# 以下所属个人实际操作的来,若有不妥或是不对之处,请多多之处不足之处。
# 部分解释来源百度百科Termios
# 首先要明确:实现串口编程的要点在何处,只有明白才会简单明了。
#要验证编程是否成功,记得准备平台哦!不然怎么知道自己的对错呢!
一.准备ARM和一台宿主计算机,要能通信及编译哈~不会?请看此处!
二.理解解释的代码,试着对比管道,回想下。
三.尝试自己加点 信号、线程、进程编译调试,看看结果和不和自己的意思,但不需要强求。
——————————————————————————————————————————————————————————
程序头文件提供
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "serial.h" //自己编写的头文件
——————————————————————————————————————————————————————————
基层的代码 serial.c中的串口数据初始化函数 set_opt。(后面会给出相关代码)
函数定义 int set_opt(int fd, int nspeed, int nbits, char nevent, int nstop)
fd:文件描述符。
nspeed:波特率设置。
nbits:数据位传输格式。
nevent:传输协议方式。
nstop:设置停止位。
函数大致内容:
1.定义termios 变量,保存初始化数据。
2.变量值清空,大致是置全零的意思。
3.根据自己的ARM设置波特率。
4.设置传输格式。
5.设置传输协议。
6.设置停止位。
7.设置传输模式
实现内容:c_cflag—控制模式 c_cc—控制字符
定义两个 struct termios类的变量 new_state old_state
termios 是记录有大量数据的参数的结构体,里面包括了许许多多的状态标志。
old_state 是保存现有状态值,如果串口操作乱套了,要回复不是很麻烦?所以养成好习惯吧!就算用不到!
new_state 则就是用来使用的啦~
清零 new_state
bzero(&new_state,sizeof(new_state));//将new_state这一个内存空间清零
new_state.c_cflag |= CLOCAL | CREAD; //此句是可以不要的,因为这句是为了以前串口发展还不成熟用的
new_state.c_cflag &= ~CSIZE; //将字符长度掩码
设置波特率
cfsetispeed(&new_state,B115200);//设置 输入 波特率是115200
cfsetospeed(&new_state,B115200);//设置 输出 波特率是115200
设置传输格式
new_state.c_cflag |= CS8; //设置传输以8位bit位为传输方式
设置传输协议
new_state.c_cflag |= PARENB; //允许输出产生奇偶信息以及输入的奇偶校验。我的理解就是“开启”
new_state.c_cflag |= PARODD; //输入输出是奇校验
new_state.c_cflag |= (INPCK | ISTRIP);//启用输入奇偶检测 | 去掉第8位 //想知道为什么去掉第八位请百度奇偶检验吧~
设置停止位
new_state.c_cflag |= CSTOPB;//设置为2位停止位
设置传输模式
new_state.c_cc[VTIME] = 0;//非 canonical 模式非 canonical 模式读的最小字符数。
new_state.c_cc[VMIN] = 0; //非 canonical 模式读的最小字符数。
清空串口里面数据
tcflush(fd,TCIFLUSH);
以下是set_opt参考代码
int set_opt(int fd, int nspeed, int nbits, char nevent, int nstop){
struct termios old_state, new_state;
if (tcgetattr(fd, &old_state)!=0){
perror("SetupSerial old");
return -1;
}
bzero(&new_state,sizeof(new_state));
// new_state.c_cflag |= CLOCAL | CREAD;
new_state.c_cflag &= ~CSIZE;
switch(nspeed){
case 2400:
cfsetispeed(&new_state,B2400);
cfsetospeed(&new_state,B2400);
break;
case 4800:
cfsetispeed(&new_state,B4800);
cfsetospeed(&new_state,B4800);
break;
case 9600:
cfsetispeed(&new_state,B9600);
cfsetospeed(&new_state,B9600);
break;
case 115200:
cfsetispeed(&new_state,B115200);
cfsetospeed(&new_state,B115200);
break;
case 460800:
cfsetispeed(&new_state,B460800);
cfsetospeed(&new_state,B460800);
break;
default:
cfsetispeed(&new_state, B9600);
cfsetospeed(&new_state, B9600);
break;
}
switch(nbits){
case 7:
new_state.c_cflag |= CS7;
break;
case 8:
new_state.c_cflag |= CS8;
break;
}
switch(nevent){
case 'O':
new_state.c_cflag |= PARENB;
new_state.c_cflag |= PARODD;
new_state.c_cflag |= (INPCK | ISTRIP);
break;
case 'E':
new_state.c_cflag |= PARENB;
new_state.c_cflag |= (INPCK | ISTRIP);
new_state.c_cflag &= ~PARODD;
break;
case 'N':
new_state.c_cflag &= ~PARENB;
break;
}
if(nstop==1)
new_state.c_cflag &= ~CSTOPB;
else
if(nstop==2)
new_state.c_cflag |= CSTOPB;
new_state.c_cc[VTIME] = 0;
new_state.c_cc[VMIN] = 0;
tcflush(fd,TCIFLUSH);
if((tcsetattr(fd,TCSANOW,&new_state))!=0){
perror("con set error");
return -1;
}
printf("set done!\n");
return 0;
}
——————————————————————————————————————————————————————————
基层的代码 serial.c中连接端口设置函数 open_port。(后面会给出相关代码)
函数定义int open_port (int fd, int comport)
fd:文件描述符
comport:端口选择
函数大致内容
1.选择端口,建立连接
2.提供描述符控制参数 //具体我还不清楚是做什么的
实现内容:
open("端口",O_RDWR|O_NOCTTY|O_NDELAY)
//读写模式
//如果路径指向终端设备,不要把设备用作控制终端
//如果路径名FIFO/块文件/字符文件,则文件的打开和后继I/O
fcntl和isatty 不会哈……以后修改
以下是open_port参考代码
int open_port (int fd, int comport){
char *dev[]={"/dev/ttySAC3","/dev/ttyUSB1"};
long vdisable;
if (comport==1){
fd=open("/dev/ttySAC3", O_RDWR|O_NOCTTY|O_NDELAY);
if (-1 ==fd ){
perror("Can't Open Serial Port");
return (-1);
}
}
else if(comport==2){
fd=open("/dev/ttyUSB1", O_RDWR|O_NOCTTY|O_NDELAY);
if (-1 == fd){
perror("Can't Open Serial Port");
return(-1);
}
}
if(fcntl(fd, F_SETFL, 0)<0)
printf("fcntl failed!\n");
else
printf("fcntl=%d\n",fcntl(fd, F_SETFL,0));
if(isatty(STDIN_FILENO)==0)
printf("standard input is not a terminal device\n");
else
printf("isatty success!\n");
printf("fd-open=%d\n",fd);
return fd;
}
——————————————————————————————————————————————————————————
基于上述serial.c文件,我们生成一个简单.h文件吧
int set_opt(int,int,int,char,int);
int open_oprt(int,int);
——————————————————————————————————————————————————————————
读的代码 read.c(后面会给出相关代码)
select 函数相关用法
FD_ZERO(&rd) //设立分组rd为一个组
FD_SET(fd,&rd)//绑定描述符
FD_ISSET(fd,&rd)//判断描述符fd是否在给定的描述符集fdset中
实现代码 以下我是想加入signal的程序 不过都已注释
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "serial.h"
#include
/*
int k=1;
void sig(int sig){
k=0;
printf("this system will be exit!\n");
}
*/
int main(){
int fd;
int nread,i;
char buff[8];
fd_set rd;
if ((fd=open_port(fd,1))<0){
perror("open_port");
return;
}
if ((i=set_opt(fd,115200,8,'N',1))<0){
perror("set_pot error");
return;
}
FD_ZERO(&rd);
FD_SET(fd,&rd);//归并集合
// signal(SIGINT,sig);
// while(k){
while(FD_ISSET(fd,&rd)){
if(select(fd+1,&rd,NULL,NULL,NULL)<0)
perror("select");
else {
while ((nread=read(fd, buff, 8))>0){
printf("nread=%d,%s\n",nread,buff);
}
}
}
// }
close(fd);
return;
}
——————————————————————————————————————————————————————————
写的代码write.c(相关代码)
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "serial.h"
#include
#include
/*
int k=1;
void sig(int sig){
k=0;
printf("this system will be exit!\n");
}
*/
int main(){
int fd;
int nwrite,i;
printf("please give me Ctrl+c that exit system!\n");
// getchar();
// signal(SIGINT,sig);
// char *p=NULL;
char p[]="Hello\n";
if((fd=open_port(fd,2))<0){
perror("open_port error");
return;
}
if((i=set_opt(fd,115200,8,'N',1))<0){
perror("set_opt error");
return;
}
/* p=(char*)malloc(sizeof(char)*8);
if(p==NULL){
printf("create malloc that is fail!\n");
exit(0);
}
*/
printf("fd=%d\n\n",fd);
// while(k){
puts("please give message\n");
// gets(p);
nwrite=write(fd,p,8);
printf("nwrite=%d\n",nwrite);
// }
// free(p);
close(fd);
return;
}
下面说下我的见解 串口编程 与 进程见通信、管道等基本上都是相同的
建立fd 描述符,打开“通道”,对通道的读与写的操作都是一致的。
不同用的是所利用的“通道”建立方式不一样。具体……