目录
概述
1 Linux环境下UART设备
2 轮询方式操作UART功能实现
2.1 打开串口函数:usr_serial_open
2.2 关闭串口函数: usr_serial_close
2.3 发送数据函数: usr_serial_sendbytes
2.4 接收数据函数: usr_serial_readinterrupt
3 完整代码
3.1 usr_serial.c 文件内容
3.1 usr_serial.h 文件内容
4 编写测试代码
4.1 编写测试代码
4.2 编写测试代码的Makefile
5 测试中断模式下串口数据的发送和接收功能
源代码下载地址:Linux环境下使用interrupt方式操作UART资源-CSDN文库
概述
本文介绍Linux环境下使用interrupt方式操作UART的方法,实现了串口打开,关闭,发送数据,接收数据功能,还编写测试代码,验证该功能。
1 Linux环境下UART设备
在linux环境下,UART作为一个终端设备存在,可使用命令, 系统会罗列出该目录下所有的device,其中以tty开头的设备为终端设备。串口也是这些设备之一。
ls /dev/ -l
执行该命令后,可以看见许多以tty开头的设备:
user根据板卡的信息,找到对应的端口,然后才能使用这些串口,笔者使用是基于iMX6ull芯片的板卡,板卡上COM1被用于调试终端,COM3可作为用户终端。
2 轮询方式操作UART功能实现
2.1 打开串口函数:usr_serial_open
函数参数
参数 | 描述 |
---|---|
port | 终端设备: /dev/tty0 |
baudrate | 波特率: 1200/2400/4800 ... /115200 |
databit | 数据bit位: /5/6/7/8 |
stopbit | 停止位:"1" / "1.5" / "2" |
parity | 奇偶位使能: 'N' / 'E' / 'O' |
函数实现方法:
代码 43行: 打开端口
代码 49行: 保存termios数据结构中,旧的参数
代码 51行:设置当前用户参数
源代码:
int usr_serial_open( char *port, unsigned int baudrate,
unsigned int databit, const char *stopbit, char parity)
{
int err;
fd = open (port, O_RDWR | O_NOCTTY | O_NDELAY);
if (-1 == fd) {
fprintf(stderr, "cannot open port %s\n", port);
return (-1);
}
tcgetattr (fd, &termios_old); /* save the form termios value */
err = set_portattr (baudrate, databit, stopbit, parity);
if ( err ) {
fprintf ( stderr, "\nport %s cannot set baudrate at %d\n",
port, baudrate);
}
usr_baudrate = baudrate;
return fd;
}
2.2 关闭串口函数: usr_serial_close
函数实现方法:
代码 64行: 恢复termios default参数
代码 65行:关闭fd端口
void usr_serial_close( void )
{
/* flush output data before close and restore old attribute */
tcsetattr(fd, TCSADRAIN, &termios_old);
close(fd);
}
2.3 发送数据函数: usr_serial_sendbytes
函数参数
参数 | 描述 |
---|---|
*data | 存贮数据的数组 |
datalength | 发送的数据长度 |
函数实现方法:
代码 72行: 使用write函数发送数据
2.4 接收数据函数: usr_serial_readinterrupt
函数参数
参数 | 描述 |
---|---|
*data | 存贮数据的数组 |
datalength | 接收的数据长度 |
函数实现方法:
代码 100~102行: 配置接收中断
代码 104行: 使用read函数写数据
unsigned int usr_serial_readinterrupt (void *data, unsigned int datalength)
{
int total_len = 0;
/**
* caculate the time of 5 characters and get the maxim
* with 3ms and 5 ch's time
*/
tv_timeout.tv_sec = 0;
tv_timeout.tv_usec = ( (CH_TO_WAIT * CH_BITS) * (1000000/usr_baudrate));
while(1){
FD_ZERO (&fs_read);
FD_SET (fd, &fs_read);
select (fd + 1, &fs_read, NULL, NULL, &tv_timeout);
total_len = read(fd, data, datalength);
if (total_len > 0) {
printf("Receive %d bytes: %.*s\n", total_len, (char*)data);
return total_len;
}
}
return total_len;
}
3 完整代码
代码文件命名为usr_serial, 包含两个文件
usr_serial.c usr_serial.h
3.1 usr_serial.c 文件内容
/***************************************************************
Copyright 2024-2029. All rights reserved.
文件名 : 01_usr_serial.c
作者 : tangmingfei2013@126.com
版本 : V1.0
描述 : linux 串口应用程序接口
其他 : 无
日志 : 初版V1.0 2024/03/01
***************************************************************/
#include "usr_serial.h"
/* Private define ------------------------------------------------------------*/
#define TIMEOUT_SEC(buflen,baud) (buflen*20/baud+2)
#define TIMEOUT_USEC 0
#define CH_TO_WAIT 5
#define CH_BITS 11
/* Private variables ---------------------------------------------------------*/
static unsigned int fd;
static struct timeval tv_timeout;
static struct termios termios_old;
static struct termios termios_new;
static fd_set fs_read;
static unsigned int usr_baudrate;
/* Private function prototypes -----------------------------------------------*/
static speed_t baudrate_to_Bxx (unsigned int baudrate);
static void set_data_bit (unsigned int databit);
static unsigned int set_portattr ( unsigned int baudrate,unsigned int databit, const char *stopbit,char parity);
int usr_serial_open( char *port, unsigned int baudrate,
unsigned int databit, const char *stopbit, char parity)
{
int err;
fd = open (port, O_RDWR | O_NOCTTY | O_NDELAY);
if (-1 == fd) {
fprintf(stderr, "cannot open port %s\n", port);
return (-1);
}
tcgetattr (fd, &termios_old); /* save the form termios value */
err = set_portattr (baudrate, databit, stopbit, parity);
if ( err ) {
fprintf ( stderr, "\nport %s cannot set baudrate at %d\n",
port, baudrate);
}
usr_baudrate = baudrate;
return fd;
}
void usr_serial_close( void )
{
/* flush output data before close and restore old attribute */
tcsetattr(fd, TCSADRAIN, &termios_old);
close(fd);
}
unsigned int usr_serial_sendbytes (void * data, unsigned int datalength)
{
unsigned int total_len = 0;
total_len = write(fd, data, datalength);
return (total_len);
}
int usr_serial_readbytes (void *data, unsigned int datalength)
{
unsigned int total_len = 0;
total_len = read(fd, data, datalength);
if (total_len > 0) {
printf("Receive %d bytes: %.*s\n", total_len, (char*)data);
}
return (total_len);
}
unsigned int usr_serial_readinterrupt (void *data, unsigned int datalength)
{
int total_len = 0;
/**
* caculate the time of 5 characters and get the maxim
* with 3ms and 5 ch's time
*/
tv_timeout.tv_sec = 0;
tv_timeout.tv_usec = ( (CH_TO_WAIT * CH_BITS) * (1000000/usr_baudrate));
while(1){
FD_ZERO (&fs_read);
FD_SET (fd, &fs_read);
select (fd + 1, &fs_read, NULL, NULL, &tv_timeout);
total_len = read(fd, data, datalength);
if (total_len > 0) {
printf("Receive %d bytes: %.*s\n", total_len, (char*)data);
return total_len;
}
}
return total_len;
}
static void set_data_bit (unsigned int databit)
{
termios_new.c_cflag &= ~CSIZE;
switch (databit) {
default:
case 8:
termios_new.c_cflag |= CS8;
break;
case 7:
termios_new.c_cflag |= CS7;
break;
case 6:
termios_new.c_cflag |= CS6;
break;
case 5:
termios_new.c_cflag |= CS5;
break;
}
}
static void set_stopbit (const char *stopbit)
{
if (0 == strcmp (stopbit, "1")) {
termios_new.c_cflag &= ~CSTOPB; /* 1 stop bit */
}
else if (0 == strcmp (stopbit, "1.5")) {
termios_new.c_cflag &= ~CSTOPB; /* 1.5 stop bits */
}
else if (0 == strcmp (stopbit, "2")) {
termios_new.c_cflag |= CSTOPB; /* 2 stop bits */
}
else {
termios_new.c_cflag &= ~CSTOPB; /* 1 stop bit */
}
}
static void set_parity (char parity)
{
switch (parity) {
case 'N': /* no parity check */
termios_new.c_cflag &= ~PARENB;
break;
case 'E': /* even */
termios_new.c_cflag |= PARENB;
termios_new.c_cflag &= ~PARODD;
break;
case 'O': /* odd */
termios_new.c_cflag |= PARENB;
termios_new.c_cflag |= ~PARODD;
break;
default: /* no parity check */
termios_new.c_cflag &= ~PARENB;
break;
}
}
static speed_t baudrate_to_Bxx (unsigned 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 2400:
return (B2400);
case 9600:
return (B9600);
case 19200:
return (B19200);
case 38400:
return (B38400);
case 57600:
return (B57600);
case 115200:
return (B115200);
default:
return (B9600);
}
}
static void set_baudrate (unsigned int baudrate)
{
speed_t speed;
speed = baudrate_to_Bxx (baudrate); /* set baudrate */
cfsetispeed(&termios_new, speed); // set input speed
cfsetospeed(&termios_new, speed); // set output speed
}
static unsigned int set_portattr ( unsigned int baudrate, // 1200 2400 4800 9600 .. 115200
unsigned int databit, // 5, 6, 7, 8
const char *stopbit, // "1", "1.5", "2"
char parity) // N(o), O(dd), E(ven)
{
bzero(&termios_new, sizeof (termios_new));
cfmakeraw (&termios_new);
set_baudrate (baudrate);
termios_new.c_cflag |= CLOCAL | CREAD; /* | CRTSCTS */
set_data_bit (databit);
set_parity (parity);
set_stopbit (stopbit);
termios_new.c_cc[VTIME] = 1; /* unit: 1/10 second. */
termios_new.c_cc[VMIN] = 255; /* minimal characters for reading */
return (tcsetattr (fd, TCSANOW, &termios_new));
}
/* End of this file */
3.1 usr_serial.h 文件内容
#ifndef __USR_SERIAL_H
#define __USR_SERIAL_H
#include <termios.h> /* tcgetattr, tcsetattr */
#include <stdio.h> /* perror, printf, puts, fprintf, fputs */
#include <unistd.h> /* read, write, close */
#include <fcntl.h> /* open */
#include <sys/signal.h>
#include <sys/types.h>
#include <string.h> /* bzero, memcpy */
#include <limits.h> /* CHAR_MAX */
#ifdef __cplusplus
extern "C" {
#endif
int usr_serial_open( char *port, unsigned int baudrate, unsigned int databit, const char *stopbit, char parity);
void usr_serial_close( void );
unsigned int usr_serial_sendbytes (void * data, unsigned int datalength);
int usr_serial_readbytes (void *data, unsigned int datalength);
unsigned int usr_serial_readinterrupt (void *data, unsigned int datalength);
#ifdef __cplusplus
}
#endif
#endif /* __USR_SERIAL_H */
4 编写测试代码
4.1 编写测试代码
代码实现功能介绍:
代码 39行:初始化串口设备,设置baud,数据位,停止位等参数
代码 48行:从串口读取数据
代码 55行:向串口写数据
4.2 编写测试代码的Makefile
代码实现功能介绍:
代码 2行:编译器地址
代码 3行:linux内核地址
代码 3行:链接的.o文件名
代码 6行:生成可执行型文件
5 测试中断模式下串口数据的发送和接收功能
使用Make命令编译代码,然后将生成的可执行性文件copy到NFS的共享目录下,然后在板卡中执行。
在代码中,定义要发送的数据如下:
strcpy(buf, "I am from iMX.6ULL board, hello world! \r\n");
PC端,使用串口调试助手接收数据,详细信息如下: