您的位置:首页 > 汽车 > 新车 > 著名logo设计案例_企业的网站建设_免费网站制作软件平台_百度客服电话24小时

著名logo设计案例_企业的网站建设_免费网站制作软件平台_百度客服电话24小时

2025/7/13 16:24:32 来源:https://blog.csdn.net/m0_74375685/article/details/146543745  浏览:    关键词:著名logo设计案例_企业的网站建设_免费网站制作软件平台_百度客服电话24小时
著名logo设计案例_企业的网站建设_免费网站制作软件平台_百度客服电话24小时

目录

1.终端(tty)

/dev/tty*:物理/虚拟终端

/dev/pts/*:伪终端

/dev/tty:当前进程的控制终端

/dev/tty0:当前活动的虚拟控制台

2.行规程模式(line discipline)

比较行规程和原始模式:

1. 行规程模式

2.原始模式

 3.串口API

termios 结构体

基本的API

4.串口实验(看看代码怎么写)

4.1 串口回环实验(传出去马上传回来)

自定义 set_opt 设置串口参数函000数:

自定义 open_port 打开串口设备函数 

main函数示例

4.2 GPS 模块实验 (不看也行,差不多的,就是加了点应用性)


1.终端(tty)

/dev/tty*:物理/虚拟终端

/dev/ttyS*:物理串口(如 RS-232)

/dev/ttyUSB*:USB 转串口设备

/dev/tty1 ~ tty63:本地虚拟控制台(通过 Ctrl+Alt+F1~F12 切换)


/dev/pts/*:伪终端

特点
        动态创建:由终端模拟器或 SSH 会话按需生成,退出后自动消失。

        无硬件关联:完全由软件模拟,用于多用户会话管理。


典型用途

        SSH 远程连接

        图形界面中的终端模拟器(就是Ubuntu图形化界面的终端,虽然它是tty2,但它是运行在 X Server/Wayland 上的,而不是原生的文本控制台,是通过 伪终端(/dev/pts/*) 实现的,与 /dev/tty0 无直接关联)


/dev/tty:当前进程的控制终端

        指向当前会话实际使用的终端设备

切换到 tty3,执行su root之后,执行下面命令观察现象:

while [ 1 ]; do echo msg_from_tty3 > /dev/tty; sleep 3; done

现象:只有执行命令的那个终端会收到并打印信息 

/dev/tty0:当前活动的虚拟控制台

代表当前前台虚拟终端,不适用于伪终端(如 SSH 或图形终端

切换到 tty3,执行su root之后,执行下面命令观察现象:

while [ 1 ]; do echo msg_from_tty3 > /dev/tty0; sleep 3; done

现象:把哪个终端切换到前台,那个终端就会收到并打印信息

2.行规程模式(line discipline)

在终端和串口通信的过程中,设备和程序之间有一个行规程模式处理方式

韦的图,大致意思就是说 pc 在调试开发板的串口终端上输入一个字符 “a” ,通过串口传到开发板后,先不传入程序(app)中,而是保存在行规程中,然后行规程会把当前字符回传给 pc,所以串口终端界面上会出现我们输入的字符,这个过程叫做 “回显”,需要退格删掉一个字符也是把退格传到行规程,行规程删除字符后再把现存字符回显到 pc 上。直到行规程收到 “回车键” ,才会把保存的字符都发送给程序(app)处理

后面需要把设备的行规程模式设置为原始模式,是因为需要把信息的处理全权交给程序

比较行规程原始模式:

1. 行规程模式

特点:

        行缓冲:数据按行处理(遇到\n或EOF才提交给程序)

        字符回显:输入字符会显示在终端上

        特殊字符处理:支持Ctrl+C(中断)、Ctrl+Z(暂停)等控制功能


典型场景:

        用户交互式终端(如SSH会话、本地Shell)

        需要逐行输入的命令行工具

示例:在行规程模式下,输入 hello 后按回车,程序才会收到完整字符串

2.原始模式

特点:

        无缓冲:数据立即传递给程序,无需等待行结束符

        无回显:输入字符不自动显示

        禁用控制字符:Ctrl+C等被视为普通数据

        完全控制:可精确设置数据位、超时等参数

典型场景:

        串口通信(如与单片机、传感器通信)

        需要实时响应的应用(如游戏、网络协议栈)

        二进制数据传输

示例:在原始模式下,每次接收到1个字节就会立即触发读取操作。

可以看看下面原因:

 3.串口API

在 Linux 系统中,操作设备的统一接口就是:open/ioctl/read/write。

对于 UART,又在 ioctl 之上封装了很多函数,主要是用来设置行规程等参数

所以UART应用编程的套路就是:

  1. open;
  2. 设置行规程,比如波特率、数据位、停止位、检验位、RAW 模式(ioctl)
  3. read/write;

termios 结构体

struct termios 是 Linux 系统中用于终端 I/O 控制的关键数据结构,定义在 <termios.h> 头文件中。它包含了终端设备的全部控制参数,用于配置串口、控制台等设备的通信行为。

基本的API

tc:terminal contorl
cf:control flag

tcgetattr:获取终端的属性
tcsetattr:修改终端参数
tcflush:清空终端未完成的输入/输出请求及数据
cfsetispeed: 设置输入波特率
cfsetospeed: 设置输出波特率
cfsetspeed: 同时设置输入、输出波特率

这些API其实就是修改上面的 termios 结构体,这些函数更底层其实就是调用 ioctl 修改 termios 结构体

4.串口实验(看看代码怎么写)

4.1 串口回环实验(传出去马上传回来)

自定义 set_opt 设置串口参数函000数:

/*** 设置串口参数* @param fd      串口文件描述符* @param nSpeed  波特率(2400/4800/9600/115200)* @param nBits   数据位(7或8)* @param nEvent  校验方式(N:无校验,O:奇校验,E:偶校验)* @param nStop   停止位(1或2)* @return        成功返回0,失败返回-1*/
int set_opt(int fd, int nSpeed, int nBits, char nEvent, int nStop) 
{struct termios newtio, oldtio;/* 1. 获取当前串口配置 */if (tcgetattr(fd, &oldtio) != 0) {perror("tcgetattr failed");return -1;}/* 2. 初始化新配置结构体 */bzero(&newtio, sizeof(newtio));/* 3. 设置控制模式标志 */newtio.c_cflag |= CLOCAL | CREAD;  // 保持本地连接和启用接收newtio.c_cflag &= ~CSIZE;          // 清除数据位掩码/* 4. 设置输入/输出模式 */newtio.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);  // 原始输入模式(非规范模式)newtio.c_oflag &= ~OPOST;                           // 原始输出模式(无处理)/* 5. 设置数据位 */switch (nBits) {case 7:newtio.c_cflag |= CS7;break;case 8:newtio.c_cflag |= CS8;break;default:newtio.c_cflag |= CS8;  // 默认8位数据位break;}/* 6. 设置校验位 */switch (nEvent) {case 'O':  // 奇校验newtio.c_cflag |= PARENB | PARODD;newtio.c_iflag |= (INPCK | ISTRIP);break;case 'E':  // 偶校验newtio.c_cflag |= PARENB;newtio.c_cflag &= ~PARODD;newtio.c_iflag |= (INPCK | ISTRIP);break;case 'N':  // 无校验newtio.c_cflag &= ~PARENB;break;}/* 7. 设置波特率 */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;default:  // 默认9600cfsetispeed(&newtio, B9600);cfsetospeed(&newtio, B9600);break;}/* 8. 设置停止位 */if (nStop == 1) {newtio.c_cflag &= ~CSTOPB;  // 1位停止位} else if (nStop == 2) {newtio.c_cflag |= CSTOPB;   // 2位停止位}/* 9. 设置非规范模式下的读取参数 */newtio.c_cc[VMIN]  = 1;   // 最小读取字节数:至少读取1字节才返回newtio.c_cc[VTIME] = 0;   // 超时时间(单位:0.1秒),0表示无限等待//等待第1个数据的时间//比如VMIN设为10表示至少读到10个数据才返回,但是没有数据总不能一直等吧? 可以设置VTIME,如果超时时间内至少读到了1个字节,那就继续等待,完全读到VMIN个数据再返回/* 10. 清空输入缓冲区 */tcflush(fd, TCIFLUSH);/* 11. 应用新配置(立即生效) */if (tcsetattr(fd, TCSANOW, &newtio) != 0) {perror("tcsetattr failed");return -1;}return 0;
}

自定义 open_port 打开串口设备函数 

/*** 打开并初始化串口设备* @param com 串口设备路径(如 "/dev/ttyS0")* @return 成功返回文件描述符,失败返回-1*/
int open_port(char *com) 
{int fd;/* 以读写模式打开串口设备,并确保不被用作控制终端 */fd = open(com, O_RDWR | O_NOCTTY);if (fd == -1) {perror("open serial port failed");return -1;}/* 显式设置文件状态标志为阻塞模式,也就是当程序无法读/写数据,程序会休眠*/if (fcntl(fd, F_SETFL, 0) < 0) {perror("fcntl F_SETFL failed");close(fd);  // 失败时关闭文件描述符return -1;}return fd;  // 返回有效的文件描述符
}

main函数示例

/*** 串口通信测试程序* 功能:打开串口,配置参数(115200,8N1),实现简单的回显测试*/
int main(int argc, char **argv) 
{int fd;      // 串口文件描述符int iRet;    // 操作返回值char c;      // 读写数据的缓冲区/* 参数检查 */if (argc != 2) {printf("Usage: %s </dev/ttySAC1 or other>\n", argv[0]);return -1;}/* 1. 打开串口 */fd = open_port(argv[1]);if (fd < 0) {printf("open %s err!\n", argv[1]);return -1;}/* 2. 配置串口参数(115200波特率,8数据位,无校验,1停止位)*/iRet = set_opt(fd, 115200, 8, 'N', 1);if (iRet) {printf("set port err!\n");close(fd);  // 配置失败时关闭串口return -1;}/* 3. 串口读写测试 */printf("Enter a char: ");while (1) {/* 从标准输入获取字符 */scanf("%c", &c);/* 写入串口 */iRet = write(fd, &c, 1);if (iRet != 1) {printf("write failed\n");continue;}/* 从串口读取回显数据(非阻塞模式立即返回)*/iRet = read(fd, &c, 1);if (iRet == 1) {printf("get: %02x %c\n", c, c);  // 打印十六进制和ASCII格式} else {printf("can not get data\n");}}close(fd);  // 理论上不会执行到这里return 0;
}

因为有可能会出现读取串口数据时,由于串口回环设备传输太慢,导致发送后回环读取时会出现读取失败的情况,是因为数据还没传到。

set_opt 中 :

newtio.c_cc[VMIN]  = 1;   // 最小读取字节数:至少读取1字节才返回
newtio.c_cc[VTIME] = 0;   // 超时时间(单位:0.1秒),0表示无限等待//等待第1个数据的时间//比如VMIN设为10表示至少读到10个数据才返回,但是没有数据总不能一直等吧? 可以设置VTIME,如果超时时间内至少读到了1个字节,那就继续等待,完全读到VMIN个数据再返回

这两段代码就设置了等待数据时间(这里设置为0,即无限等待)

这就解决了问题,把数据等到程序才继续运行,否则就一直阻塞

4.2 GPS 模块实验 (不看也行,差不多的,就是加了点应用性)

使用串口接收数据,收到的数据包含:$GPGGA(GPS 定位数据)、$GPGLL (地理定位信息)、$GPGSA(当前卫星信息)、$GPGSV(可见卫星状态信息)、 $GPRMC(推荐最小定位信息)、$GPVTG(地面速度信息)

只分析$GPGGA (Global Positioning System Fix Data)即可, 它包含了 GPS 定位经纬度、质量因子、HDOP、高程、参考站号等字段。

数据标准格式:
$GPGGA,<1>,<2>,<3>,<4>,<5>,<6>,<7>,<8>,<9>,M,<10>,M,<11>,<12>*hh

其他关于gps的介绍看手册吧,这里只放代码

沿用上面的 set_opt 和 open_port 函数

/*** GPS数据读取与解析程序* 功能:从串口读取GPS模块的NMEA数据并解析位置信息*//*** 从串口读取一行GPS原始数据* @param fd 串口文件描述符* @param buf 存储读取数据的缓冲区* @return 成功返回0,失败返回-1*/
int read_gps_raw_data(int fd, char *buf)
{int i = 0;int iRet;char c;int start = 0;  // 标记是否开始接收有效数据while (1) {iRet = read(fd, &c, 1);  // 每次读取1个字符if (iRet == 1) {if (c == '$') {  // NMEA语句起始符start = 1;i = 0;       // 重置缓冲区索引}if (start) {buf[i++] = c;  // 存储有效数据}// 遇到换行符表示一行数据结束if (c == '\n' || c == '\r') {buf[i] = '\0';  // 添加字符串结束符return 0;}} else {return -1;  // 读取失败}}
}/*** 解析GPS原始数据(GPGGA格式)* @param buf 原始数据缓冲区* @param time 存储时间信息* @param lat 存储纬度* @param ns 存储南北半球* @param lng 存储经度* @param ew 存储东西半球* @return 成功返回0,失败返回-1*/
int parse_gps_raw_data(char *buf, char *time, char *lat, char *ns, char *lng, char *ew)
{char tmp[10];// 检查数据有效性if (buf[0] != '$') {  // 必须以$开头return -1;} else if (strncmp(buf+3, "GGA", 3) != 0) {  // 必须是GPGGA语句return -1;} else if (strstr(buf, ",,,,,")) {  // 无效定位数据printf("Place the GPS to open area\n");return -1;} else {// 解析关键字段sscanf(buf, "%[^,],%[^,],%[^,],%[^,],%[^,],%[^,]",tmp, time, lat, ns, lng, ew);return 0;}
}/** 主函数* 用法:./gps_reader </dev/ttySAC1 or other>*/
int main(int argc, char **argv)
{int fd;              // 串口文件描述符int iRet;            // 函数返回值char buf[1000];      // 原始数据缓冲区char time[100];      // 时间字段char Lat[100];       // 纬度字段char ns[100];        // 南北半球标识char Lng[100];       // 经度字段char ew[100];        // 东西半球标识float fLat, fLng;    // 转换后的经纬度/* 1. 参数检查 */if (argc != 2) {printf("Usage: %s </dev/ttySAC1 or other>\n", argv[0]);return -1;}/* 2. 打开串口 */fd = open_port(argv[1]);if (fd < 0) {printf("open %s err!\n", argv[1]);return -1;}/* 3. 配置串口(9600波特率,8N1)*/iRet = set_opt(fd, 9600, 8, 'N', 1);if (iRet) {printf("set port err!\n");close(fd);return -1;}/* 4. 主循环:读取并解析GPS数据 */while (1) {/* 读取一行NMEA数据 */iRet = read_gps_raw_data(fd, buf);/* 解析GPGGA数据 */if (iRet == 0) {iRet = parse_gps_raw_data(buf, time, Lat, ns, Lng, ew);}/* 打印解析结果 */if (iRet == 0) {printf("\n------ GPS Data ------\n");printf("Time : %s\n", time);printf("Lat  : %s %s\n", Lat, ns);printf("Lng  : %s %s\n", Lng, ew);/* 转换纬度格式:ddmm.mmmm → 十进制 */sscanf(Lat+2, "%f", &fLat);fLat = fLat / 60;fLat += (Lat[0] - '0')*10 + (Lat[1] - '0');/* 转换经度格式:dddmm.mmmm → 十进制 */sscanf(Lng+3, "%f", &fLng);fLng = fLng / 60;fLng += (Lng[0] - '0')*100 + (Lng[1] - '0')*10 + (Lng[2] - '0');printf("Decimal Coordinates:\n");printf("Lng,Lat: %.06f,%.06f\n", fLng, fLat);}}close(fd);  // 理论上不会执行到这里return 0;
}

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com