使用的是飞凌嵌入式开发板OKMX6UL-C为底板的嵌入式开发板,测试效果串口助手向开发板发送内容打印在终端,需要注意的是,交叉编译使用与开发板相对于的交叉编译工具,采用arm-XXX-linux-gcc.核心板为FETMX6UL-C,如图:
头文件及其相关定义:
#include <termios.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <signal.h>
#define msleep(n) usleep(n*1000) //延时1ms
/*定义全局变量*/
char first_test[200]="The first test loop\r\n";
volatile int fd; //volatile防止被优化
char* dev = NULL; //创建线程指针,串口名称串口三为:/dev/ttymxc2
pthread_mutex_t mut; //线程锁保证所有线程对数据的修改是一致的
fd_set rd; //fd_set的数据结构,实际上是一long类型的数组
int nread,retval; //读操作和返回值
unsigned char msg[14]; //消息数组
char msg1[200];
int receiving_bit = 0; //消息接收标志位
struct timeval timeout={0,100}; //超时时间结构体
volatile pthread_t thread[2]; //声明线程ID
volatile const int READ_THREAD_ID = 0; //读线程ID
volatile const int SEND_THREAD_ID = 1; //发送线程ID
volatile int COM_READ_STATU = 1;
volatile int COM_SEND_STATU = 1;
volatile int sendnum=0,sendnum_times=0,recenum=0,recenum_times=0,cornum=0,cornum_times=0;
主函数调用相关函数:
/*定义主函数调用的函数*/
void read_port(void); //监视端口情况读写或异常
void SignHandler(int iSignNo); //终端信号监督
int OpenDev(char *Dev); //打开配置相关串口驱动
static speed_t getBaudrate(int baudrate); //波特率相关配置列表
int start_thread_func(void*(*func)(void*), pthread_t* pthread, void* par, int* COM_STATU);//创建线程
void* com_read(void* pstatu); //读取端口线程锁控制
void* com_send(void* p); //向端口发送loop内容
//void convertUnCharToStr(char* str, unsigned char* UnChar, int ucLen); //unsigned char数组转化为char数组
首先看主函数:
/*主函数*/
int main(int argc, char **argv)
{
//char *dev ="/dev/ttymxc1";
signal(SIGINT,SignHandler); //信号是进程在运行过程中,由自身产生或由进程外部发过来的事件
dev = argv[1]; //指针的指针,指向运行时的参数一
if(dev==NULL)
{
printf("Please input serial device name ,for exmaple /dev/ttymxc2.\n");
exit(1);
}
fd = OpenDev(dev); //打开驱动相关配置
if (fd>0)
{}
else
{
printf("Can't Open Serial Port %s \n",dev);
exit(0);
}
printf("\nWelcome to TTYtest! Press Ctrl + 'c' to stop.\n\n");
pthread_mutex_init(&mut,NULL); //用默认属性初始化互斥锁
if(start_thread_func(com_read, (pthread_t*)&thread[READ_THREAD_ID], (int *)&COM_READ_STATU, (int *)&COM_READ_STATU) != 0)
{
printf("error to leave\n");
return -1;
}
if(start_thread_func(com_send, (pthread_t*)&thread[SEND_THREAD_ID], (int *)&COM_SEND_STATU, (int *)&COM_SEND_STATU) != 0)
{
printf("error to leave\n");
return -1;
}
while(1)
{} //根据具体情况决定在主函数中需要处理的事件
}
监视端口情况读写或异常:
void read_port(void) //监视端口情况读写或异常
{
FD_ZERO(&rd); //将rd清零使集合中不含任何fd
FD_SET(fd,&rd); //将fd加入rd集合
retval = select (fd+1,&rd,NULL,NULL,&timeout); //监视我们需要监视的内容描述符的变化情况
switch (retval)
{
case 0:
// printf("no data input within 1s.\n");
break;
case -1:
perror("select");
break;
default:
if((nread=read(fd,msg,1))>0)
{
receiving_bit = 1;
printf("%s",msg);
if(recenum<65535)
{
recenum++;
}
else
{
recenum_times++;
recenum=1;
}
}
break;
}
}
终端信号处理:
void SignHandler(int iSignNo) //终端信号监督
{
int tmp_t=0;
COM_SEND_STATU = 0;
msleep(1000);
COM_READ_STATU = 0;
msleep(1000);
//printf("%s,stop send,sendnum=%d,receivenum=%d\n",dev,sendnum*32,recenum);
while(tmp_t<50000)
{
read_port();
tmp_t++;
}
printf("\n%s,Send: %d ,Receive: %d \n",dev,(sendnum_times*65535+sendnum)*32,(recenum_times*65535+recenum));
exit(1);
}
波特率处理,下面列出两中方式,本文采用第二种
方式一:
int speed_arr[] = {B115200, B38400, B19200, B9600, B4800, B2400, B1200, B300, };
int name_arr[] = {115200, 38400, 19200, 9600, 4800, 2400, 1200, 300, };
void set_speed (int fd, int speed)
{
int i;
int status;
struct termios Opt;
tcgetattr (fd, &Opt);
for (i = 0; i < sizeof (speed_arr) / sizeof (int); i++)
{
if (speed == name_arr[i])
{
tcflush (fd, TCIOFLUSH);
cfsetispeed (&Opt, speed_arr[i]);
cfsetospeed (&Opt, speed_arr[i]);
status = tcsetattr (fd, TCSANOW, &Opt);
if (status != 0)
{
perror ("tcsetattr fd1");
return;
}
tcflush (fd, TCIOFLUSH);
}
}
}
主函数中调用:set_speed (fd, 115200);
方式二:
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;
}
}
配置相关串口参数驱动是本文重点
int OpenDev(char *Dev) //打开配置相关串口参数驱动
{
speed_t speed;
int i=0;
int fdt,c=0,num;
struct termios oldtio,newtio;
speed = getBaudrate(9600); //波特率相关配置speed = B9600;
fdt=open(Dev,O_RDWR | O_NONBLOCK| O_NOCTTY | O_NDELAY);
//O_RDWR:一可读可写方式打开
//O_NONBLOCK:使I/O变成非阻塞模式,它在读取不到数据时会回传-1,并且设置errno为EAGAIN
//O_NOCTTY:如果文件为终端,那么终端不可以调用open系统调用的那个进程的控制终端
//O_NDELAY:使I/O变成非阻塞模式,它在读取不到数据时会回传0
if(fdt<0)
{
perror(Dev);
exit(1);
}
//save to oldtio
tcgetattr(fdt,&oldtio); //使用函数tcgetattr保存原串口属性
//clear newtio
bzero(&newtio,sizeof(newtio)); //清空newtio中相应位
//newtio.c_cflag = speed|CS8|CLOCAL|CREAD|CRTSCTS;
newtio.c_cflag = speed|CS8|CLOCAL|CREAD;// 控制模式标志:波特率|8个数据位|本地连接(不改变端口所有者)|接收使能
newtio.c_iflag = IGNPAR; //输入模式标志:忽略奇偶校验错误
newtio.c_oflag = 0; //输出模式标志:Raw 模式输出
//printf("newtio.c_cflag=%x\n",newtio.c_cflag);
tcflush(fdt,TCIFLUSH); //丢弃要写入引用的对象,TCIFLUSH刷新收到的数据但是不读
tcsetattr(fdt,TCSANOW,&newtio); //设置与终端相关的参数,TCSANOW改变立即发生
tcgetattr(fdt,&oldtio); //得到与 fd 指向的对象相关的参数保存于结构中,函数可以从后台进程中调用;但是,终端属性可能被后来的前台进程所改变
//printf("oldtio.c_cflag=%x\n",oldtio.c_cflag);
return fdt;
}
线程处理相关内容:
void* com_read(void* pstatu) //读取端口线程锁控制
{
int o;
while(COM_READ_STATU)
{
pthread_mutex_lock(&mut);
read_port();
pthread_mutex_unlock(&mut);
}
pthread_exit(NULL);
}
void* com_send(void* p) //向端口发送loop内容
{
int a;
int ii;
while(COM_SEND_STATU)
{
if(receiving_bit != 0)
{
receiving_bit = 0;
write(fd, first_test, strlen(first_test));
if(sendnum<65535)
{
sendnum++;
}
else
{
sendnum_times++;
sendnum=1;
}
msleep(500);
}
/* write(fd, first_test, strlen(first_test));
if(sendnum<65535)
{
sendnum++;
}
else
{
sendnum_times++;
sendnum=1;
}
msleep(500);
*/
}
pthread_exit(NULL);
}
int start_thread_func(void*(*func)(void*), pthread_t* pthread, void* par, int* COM_STATU) //创建线程
{
*COM_STATU = 1;
memset(pthread, 0, sizeof(pthread_t));
int temp;
/*creat thread*/
if((temp = pthread_create(pthread, NULL, func, par)) != 0)
{
printf("creat thread failer!\n");
}
else
{
int id = pthread_self();
printf("%s,creat thread %lu sucess\n",dev, *pthread);
}
return temp;
}
交叉编译此.c文件,放置开发板目录下方,运行方式:
./编译时输入的名称 /dev/ttymxc2
比如(串口三):
* arm-linux-gcc uart.c -otty_test -lpthread
* ./tty_test /dev/ttymxc2