参考:https://blog.csdn.net/wangzhen209/article/details/52620906
执笔时间:2019.1.18
一、安装串口驱动
在 /dev/目录中 tty* ttys* 的文件为串口设备的驱动文件,现在的笔记本和台式机很多已经不在自带串口,我用了USB-串口的数据线,此时驱动为ttyUSB*,我用的Ubuntu 18.04 内核中已经自带USB-串口的驱动文件,不过没有编译。如果直接使用串口线,而没有用到USB转串口设备,就不需要安装驱动。
1、如果使用了USB转串口,一般情况下也不需要安装驱动了,目前linux系统已经包含了该驱动,可以自动识别,亦可通过以下命令查看以便确认是否支持。
lsmod | grep usbserial
如果输出内容中包含 usbserial 说
明能正确识别该设备,否则安装该设备的驱动。
参考:https://blog.csdn.net/hhhlizhao/article/details/53790822
输入 dmesg | tail -f
若出现类似
[ 8697.367818] usb 1-2: Detected FT232RL
[ 8697.368307] usb 1-2: FTDI USB Serial Device converter now attached to ttyUSB0
的信息,说明USB转串口的驱动已经加载,可以直接使用。
注意:默认情况下ubuntu已经安装了USB转串口驱动(pl2303)。
2、插上USB转串口,在终端输入命令#dmesg | grep ttyUSB0,如果出现连接成功信息,则说明ubuntu系统已经识别该设备了。
注意:虚拟机环境下的ubuntu默认情况下是不能自动识别的,需要在虚拟机窗口右下角点击"Prolific USB-Serial Controller",然后选择"Connect (Disconnect from Host)",这样才能被ubuntu系统识别。
3、在上面minicom的配置中设置Serial Device: /dev/ttyUSB0,重启开发板,这样基本上就可以正常使用minicom来打印串口信息了。
4、问题
如果经过上面的步骤minicom还是不能正常工作,出现如下错误提示:
# sudo minicom
minicom: cannot open /dev/ttyUSB0: 没有该文件或目录
这时可以尝试换一个USB口,然后再次操作以上流程。如果还是提示这个错误,那么可以使用下面的方法来解决。
这 种方法是在硬件里添加串口设备,将window下的设备添加到虚拟机里。也就是说,要在window获得焦点的时候加入usb转串口,然后再到虚拟机下将 这个设备添加进去,这时就可以在ubuntu下查看添加的这个设备的设备文件,一般是/dev/tty0或者 /dev/ttyS0。
这种方法其实是将window的usb转串口作为虚拟机的串口,所以就是tty0或者ttyS0了,而不是真正在ubuntu下加载的。
具体步骤如下:
(1)打开虚拟机环境,然后选择"VM-->Settings(Ctrl+D)"。
(2)点"Add",进入添加硬件向导,选择"Serial Port",点"Next"。
(3)选择第一项"Use physical port on the host",点"Next"。
(4)选择"Physical serial port"方式为"Auto detect",勾选"Connect at power on",点"Finish"完成。
二、串口测试
现在已经有很多linux串口调试工具,https://blog.csdn.net/wilylcyu/article/details/51766283
其中cutecom简单实用,界面友好,适合类似我这种Linux入门的同志使用。
安装 cutecom :sudo apt-get install cutecom
打开程序:sudo cutecom
点开右上角settings,设置合适自己的参数,重点是设备选取一定要正确
我的是 /dev/ttyUSB0
点击Open打开串口,在input栏输入内容按Enter即可令串口发送数据
将串口的第二、第三脚短接,再次发送数据,即可在接受栏中看到发送的数据
证明串口设备正常发、收数据。
三、串口编程
参考:https://blog.csdn.net/u012010054/article/details/81092579
#include <termios.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <linux/kernel.h>
#include <stdio.h>
#include <pthread.h>
#include <string.h>
#include <sys/select.h>
#include <sys/time.h>
char rev_buf[256];
char databuf[] = {1,2,0x33,0x34,0x35,6,7,11,12,89};
int uart_fd;
#define DEV_NAME "/dev/ttyUSB0"
int Set_Com_Config(int fd, int baud_rate, int data_bits, char parity, int stop_bits)
{
struct termios new_cfg, old_cfg;
int speed;
/*保存并测试现有串口参数设置,在这里如果串口号等出错,会有相关出错信息*/
if(tcgetattr(fd, &old_cfg) != 0) /*该函数得到fd指向的终端配置参数,并将它们保存到old_cfg变量中,成功返回0,否则-1*/
{
perror("tcgetttr");
return -1;
}
/*设置字符大小*/
new_cfg = old_cfg;
cfmakeraw(&new_cfg); /*配置为原始模式*/
new_cfg.c_cflag &= ~CSIZE; /*用位掩码清空数据位的设置*/
/*设置波特率*/
switch(baud_rate)
{
case 2400:
speed = B2400;
break;
case 4800:
speed = B4800;
break;
case 9600:
speed = B9600;
break;
case 19200:
speed = B19200;
break;
case 38400:
speed = B38400;
break;
default:
case 115200:
speed = B115200;
break;
}
cfsetispeed(&new_cfg, speed); //设置输入波特率
cfsetospeed(&new_cfg, speed); //设置输出波特率
/*设置数据长度*/
switch(data_bits)
{
case 5:
new_cfg.c_cflag &= ~CSIZE;//屏蔽其它标志位
new_cfg.c_cflag |= CS5;
break;
case 6:
new_cfg.c_cflag &= ~CSIZE;//屏蔽其它标志位
new_cfg.c_cflag |= CS6;
break;
case 7:
new_cfg.c_cflag &= ~CSIZE;//屏蔽其它标志位
new_cfg.c_cflag |= CS7;
break;
default:
case 8:
new_cfg.c_cflag &= ~CSIZE;//屏蔽其它标志位
new_cfg.c_cflag |= CS8;
break;
}
/*设置奇偶校验位*/
switch(parity)
{
default:
case 'n':
case 'N': //无校验
{
new_cfg.c_cflag &= ~PARENB;
new_cfg.c_iflag &= ~INPCK;
}
break;
case 'o': //奇校验
case 'O':
{
new_cfg.c_cflag |= (PARODD | PARENB);
new_cfg.c_iflag |= INPCK;
}
break;
case 'e': //偶校验
case 'E':
{
new_cfg.c_cflag |= PARENB;
new_cfg.c_cflag &= ~PARODD;
new_cfg.c_iflag |= INPCK;
}
break;
}
/*设置停止位*/
switch(stop_bits)
{
default:
case 1:
new_cfg.c_cflag &= ~CSTOPB;
break;
case 2:
new_cfg.c_cflag |= CSTOPB;
break;
}
/*设置等待时间和最小接收字符*/
new_cfg.c_cc[VTIME] = 1; /* 读取一个字符等待1*(1/10)s */
new_cfg.c_cc[VMIN] = 1; /* 读取字符的最少个数为1 */
/*处理未接收字符*/
tcflush(fd, TCIFLUSH); //溢出数据可以接收,但不读
/* 激活配置 (将修改后的termios数据设置到串口中)
* TCSANOW:所有改变立即生效
*/
if((tcsetattr(fd, TCSANOW, &new_cfg))!= 0)
{
perror("tcsetattr");
return -1;
}
return 0;
}
int Uart_Send(int fd, char *data, int data_len)
{
int len = 0;
len = write(fd, data, data_len);
if(len == data_len) {
return len;
} else {
tcflush(fd, TCOFLUSH); //TCOFLUSH刷新写入的数据但不传送
return -1;
}
}
int Uart_Recv(int fd, char *rev_buf, int data_len)
{
int len, fs_sel;
fd_set fs_read;
struct timeval tv_timeout;
FD_ZERO(&fs_read); //清空集合
FD_SET(fd,&fs_read); // 将一个给定的文件描述符加入集合之中
tv_timeout.tv_sec = 5;
tv_timeout.tv_usec = 0;
//使用select实现串口的多路通信
fs_sel = select(fd + 1, &fs_read, NULL, NULL, &tv_timeout); //如果select返回值大于0,说明文件描述符发生了变化
//printf("fs_sel = %d\n",fs_sel); //如果返回0,代表在描述符状态改变前已超过timeout时间,错误返回-1
if(fs_sel)
{
len = read(fd, rev_buf, data_len);
printf("len = %d, fs_sel = %d\n", len, fs_sel);
return len;
}
else
{
return 0;
}
}
int Uart_Init(int fd, int speed, int databits, int parity, int stopbits)
{
//设置串口数据帧格式
if (Set_Com_Config(fd, speed, databits, parity, stopbits) == 0)
{
return 0;
}
else
{
return -1;
}
}
int Uart_Receive_thread(void)
{
int r_count;
while(1)
{
r_count = Uart_Recv(uart_fd, rev_buf, sizeof(rev_buf));
if(r_count)
{
for(int i = 0; i < r_count; i++)
{
printf("rev_buf[%d] = 0x%x\n", i, rev_buf[i]);
}
}
}
}
void Uart_Send_thread(void)
{
int s_count;
while(1)
{
sleep(1);
printf("This is Uart_Send_thread.\n");
s_count = Uart_Send(uart_fd, databuf, strlen(databuf));
if(s_count == -1)
printf("Uart_Send Error!\n");
else
printf("Send %d data successfully!\n", s_count);
}
}
int main(int argc, char *argv[])
{
pthread_t id_Uart_Receive, id_Uart_Send;
int ret1, ret2;
uart_fd = open(DEV_NAME, O_RDWR | O_NOCTTY);
if(uart_fd == -1)
{
printf("Uart Open Failed!\n");
exit(EXIT_FAILURE);
}
if(Uart_Init(uart_fd, 115200, 8, 'E', 1) == -1)
{
printf("Uart_Init Failed!\n");
exit(EXIT_FAILURE);
}
ret1 = pthread_create(&id_Uart_Receive, NULL, (void *) Uart_Receive_thread, NULL);
ret2 = pthread_create(&id_Uart_Send, NULL, (void *) Uart_Send_thread, NULL);
if(ret1!=0){
printf ("Create Uart_Receive_thread error!\n");
close(uart_fd);
exit (1);
}
if(ret2!=0){
printf ("Create Uart_Send_thread error!\n");
close(uart_fd);
exit (1);
}
while(1)
{
printf("This is the main process.\n");
usleep(1000000);
}
pthread_join(id_Uart_Receive, NULL);
pthread_join(id_Uart_Send, NULL);
return 0;
}
存为文件Comtest.c,用gcc编译:
gcc -o ComTest ComTest.c
报错:
ComTest.c:(.text+0x6a8):对‘pthread_create’未定义的引用
ComTest.c:(.text+0x6c8):对‘pthread_create’未定义的引用
参考:https://www.cnblogs.com/leechanx/p/3322611.html
这是因为linux标准库没有pthread.h,需要在运行参数中指定需要动态加载的库
在编译命令末尾加上 -lpthread
gcc -o ComTest ComTest.c -lpthread
编译成功。
运行ComTest程序,此处需要使用超级管理员权限:
sudo ./ComTest
串口不断发送数据:
This is the main process.
This is Uart_Send_thread.
Send 10 data successfully!
This is the main process.
This is Uart_Send_thread.
Send 10 data successfully!
短接串口2、3引脚后:
This is the main process.
This is Uart_Send_thread.
Send 10 data successfully!
len = 10, fs_sel = 1
rev_buf[0] = 0x1
rev_buf[1] = 0x2
rev_buf[2] = 0x33
rev_buf[3] = 0x34
rev_buf[4] = 0x35
rev_buf[5] = 0x6
rev_buf[6] = 0x7
rev_buf[7] = 0xb
rev_buf[8] = 0xc
rev_buf[9] = 0x59
成功实现Linux下串口设备C语言编程调试。