Linux驱动应用编程(三)UART串口

本文目录

  • 前述
  • 一、手册查看
  • 二、命令行调试串口
    • 1. 查看设备节点
    • 2. 使用stty命令设置串口
    • 3. 查看串口配置信息
    • 4. 调试串口
  • 三、代码编写
    • 1. 常用API
    • 2. 例程
      • ●线程优化
      • ●poll优化
      • ●select优化(功能和poll一样)

  

前述

   在开始实验前,请一定要检查测试好所需硬件是否使用正常,不然调试过程中出现的问题,会让你很烦恼。因为我在测试的时候发现一直接收不到消息,后面才知道ttl转usb的tx引脚坏了。所以确保硬件良好是我们后续测试的基本保障。

一、手册查看

   我们前面讲到在Linux中一切皆是文件!无论是上一章节的GPIO,还是串口这边都同样如此。首先,我们就先来使用命令行基本调试一下我们的串口,来确保串口的可用性。无论是什么开发板,我们都需要先查看手册来确定开发板的串口io。这里以香橙派AIPro为例,手册中提醒我们串口0已经被系统使用,不能当作普通串口给用户使用。 所以用户可用串口只有两个。分别是UART7和UART2。

在这里插入图片描述

查看设备节点,如下所示。
在这里插入图片描述

二、命令行调试串口

1. 查看设备节点

设备节点都在 /dev目录下。
在这里插入图片描述

2. 使用stty命令设置串口

常用配置:stty -F /dev/ttyAMA2 115200 cs8 -parenb -cstopb iutf8

在这里插入图片描述

3. 查看串口配置信息

命令:stty -F /dev/ttyAMA2 -a
在这里插入图片描述

4. 调试串口

我们使用TTL转USB接口连接香橙派与电脑,使用串口调试助手测试。注意:在串口助手中要设置为UTF8显示。
坑:在使用TTL连接时,一定要接地线!以保证电气基准电位!不要只接TX和RX!!

(1)香橙派发送数据
在这里插入图片描述

(2)香橙派接收数据
在这里插入图片描述

三、代码编写

   终端设备属性结构体,我们在操作一些设备文件时,常常会用到下面这个结构体。在Unix系统中常用于控制终端的输入输出参数,比如波特率、字符大小、控制字符等。通过操作这个结构体,可以对终端的各种属性进行设置和获取。
头文件:#include<termios.h>

struct termios {
	unsigned short c_iflag;//控制终端的输入方式,如是否启用回车、换行等
	unsigned short c_oflag; //控制终端的输出方式,如是否启用回车、换行等。
	unsigned short c_cflag; //控制终端的控制模式,如波特率、数据位数等。
	unsigned short c_lflag;//控制终端的本地模式,如是否启用回显、是否启用信号等。
	unsigned char c_line;//行(线)规程,指定终端的行规程,比如终端是终端设备还是伪终端设备。
	unsigned char c_cc[NCC]; //控制字符数组,用于定义特殊控制字符的行为,比如终端中的删除、结束、停止等功能键的行为。
};

   这些成员里都有很多配置参数的宏定义,我们只需要将成员与要配置参数的宏定义进行|=(置1)或&=~(置0)操作即可配置相应功能。我这里只先列举出来几个常用的功能。这里我将不再展示参数的宏定义,因为太多了,很多也用不到,有需要的自己去查看其他博客来学习。

●配置举例如下:

struct termios termios_p; //初始化结构体
termios_p.c_cflag |=CS8; //设置八位数据位。
termios_p.c_cflag &=~CSTOPB; //设置一位停止位。
termios_p.c_cflag &=~PARENB; //无奇偶校验位。
termios_p.c_lflag &=~ECHO ; //不回显

1. 常用API

(1)设置波特率。
   波特率,常用 B2400,B4800,B9600,B115200,B460800。

int cfsetispeed(struct termios *termios_p,speed_t speed)  //设置接收波特率
int cfsetospeed(struct termios *termios_p,speed_t speed)  //设置发送波特率
//

(2)清空缓冲区数据。
   主要用于清除输入和输出缓冲区中的数据。这个命令在处理串口通信时非常有用,特别是在初始化或重置通信通道时,以确保没有残留的数据干扰通信。

int tcflush(int fd,int queue_selector)
//int fd :文件描述符
//int queue_selector: 
/*	        TCIFLUSH:清空正读的数据,且不会读出
			TCOFLUSH:清空正写入的数据,且不会发送到终端
			TCIOFLUSH:清空所有正在发生的 I/O 数据.
	*/

(3)获取终端设备参数。
   函数的作用是获取指定文件描述符(fd)所关联的终端设备的当前属性,并将这些属性存储到指定的 termios 结构体(termios_p)中。

int tcgetattr(int fd,struct termios *termios_p)
//int fd :文件描述符。
//struct termios *termios_p: 设备终端结构体。

(4)设置终端设备参数,激活配置。

int tcsetattr(int fd,int optional_actions,cons struct termios *termios_p)
//int fd :文件描述符。
//int optional_actions :
/*			TCSANOW:不等数据传输完毕,立即改变属性
			TCSADRAIN:等所有数据传输完毕,再改变属性
			TCSAFLUSH:清空输入输出缓冲区才改变属性
*/		
//cons struct termios *termios_p :终端设备属性的结构体。

2. 例程

uart.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <unistd.h>
#include <string.h>
#include "uart.h"
#include <termios.h>

void UART_Close(int fd)
{
   close(fd);
}

int UART_Send(int fd, char *data)
{
    int num;
    num=write(fd, data, strlen(data));
    return num;
}


int UART_Receive(int fd, char *receive)
{
    int num;
    num=read(fd, receive, sizeof(receive)-1);
    return num;
}

int UART_Init(char *device, int baud)
{
	int fd;
	int ret;
	struct termios termios_p; 
//1.打开串口设备。不当作控制台。
    fd = open(device, O_NOCTTY | O_RDWR);
    if (fd < 0) {
        perror("open error");
        return -1;
    }
//2. 填充设备结构体
	memset(&termios_p, 0 ,sizeof(termios_p));
	termios_p.c_cflag |= CREAD;     //使能接收器的接收功能!必须配置的。
	termios_p.c_cflag |= CLOCAL;    //忽略调制解调器线路状态。
	termios_p.c_cflag |=CS8;        //设置八位数据位。
	
    //这里为了方便观看,所以写出来下面的配置,这里其实可以不写,因为我们已经先前清空结构体了。
	termios_p.c_cflag &=~CSTOPB; //设置一位停止位。
	termios_p.c_cflag &=~PARENB; //无奇偶校验位。
	termios_p.c_lflag &=~ECHO ; //不回显
	
	// 设置超时和最小读取字符数。必须配置!!
    termios_p.c_cc[VTIME] = 1;
    termios_p.c_cc[VMIN] = 128;
	
    switch (baud){  //设置波特率
			case 9600:
						cfsetispeed(&termios_p,B9600);  //设置接收波特率
						cfsetospeed(&termios_p,B9600);  //设置发送波特率
						break;
			case 115200:
						cfsetispeed(&termios_p,B115200);  //设置接收波特率
						cfsetospeed(&termios_p,B115200);  //设置发送波特率
						break;
			default:
						printf("不支持此波特率\n");
						break;
   }
   
//3. 清空接收/发送缓冲,准备发送和接收数据 
	tcflush(fd, TCIOFLUSH);

//4. 将配置好的设备结构体设置上(绑定),激活设置的配置。
	ret =tcsetattr( fd,TCSAFLUSH,&termios_p);
	if (ret < 0) {
        perror("tcsetattr error");
        return -3;
    }
	
	return fd; //返回文件描述符
}

uart.h

#ifndef __UART_H
#define __UART_H

void UART_Close(int fd);
int UART_Init(char *device, int baud);
int UART_Receive(int fd, char *receive);
int UART_Send(int fd, char *data);
#endif

main.c

#include <stdio.h>
#include <stdlib.h>
#include "uart.h" 
#include <unistd.h>
#include <termios.h>
#include <string.h>
int main()
{
	char buff[100];
	int fd;
	fd=UART_Init("/dev/ttyAMA2",115200);
	if(fd<0){
		perror("UART_Init error");
		return -1;
	}
	
	while(1){
		fgets(buff,sizeof(buff),stdin);
		UART_Send(fd, buff);
	}
	
	UART_Close(fd);
	return 0;
}

在这里插入图片描述

●线程优化

功能:添加线程实现可收可发!
main.c

#include <stdio.h>
#include <stdlib.h>
#include "uart.h" 
#include <unistd.h>
#include <termios.h>
#include <string.h>
#include <pthread.h>
#include <signal.h>

pthread_attr_t attr; //线程属性
int fd;  //文件描述符

void signal_task(int arg)
{
    printf("销毁相关属性\n");
	pthread_attr_destroy(&attr); //销毁线程属性
    UART_Close(fd);
    exit(0);
}

// 线程任务函数
void *task(void *arg) {
    int fd = *(int *)arg;
    int ret;
    char buff[64];

    while (1) {
		        memset(buff, 0, sizeof(buff));
		        ret = UART_Receive(fd, buff); // 读取数据到buff中
		        if (ret > 0) {
		            buff[ret] = '\0'; // 确保字符串以NULL结尾
		            printf("Receive: %s\n", buff);
		        } 
		        else break;
             }
    pthread_exit(NULL);
}

int main()
{
	char buff[100];
	int ret;
	pthread_t thread;

	fd=UART_Init("/dev/ttyAMA2",115200);
	if(fd<0){
		perror("UART_Init error");
		return -1;
	}
	
	signal(2, signal_task);
	
	pthread_attr_init(&attr);  //初始化线程属性
	pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); //设置子线程分离属性,子线程推出后自动销毁。
	ret=pthread_create(&thread,NULL,task,(void *)&fd); //只有一个子线程,可以取地址操作。
	if(ret<0){
         perror("pthread_create error");
         return -1;
    }
    
	while(1){
	    memset(buff,0,sizeof(buff));
		fgets(buff,sizeof(buff),stdin);
		UART_Send(fd, buff);
	}
	
	return 0;
}

在这里插入图片描述

●poll优化

当没有事件要读写时,释放CPU。当有事件发生时,执行相应动作。

uart.c

#include <poll.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <unistd.h>
#include <string.h>
#include <termios.h>
#include "uart.h"

extern struct pollfd fds; 

void UART_Close(int fd)
{
   close(fd);
}

int UART_Send(int fd, char *data)
{ 
    int num;
    int ret;
    ret = poll(&fds, 1, 0);
	if (ret == -1) {
			perror("poll");
			return -1;
	 } 
    else if (ret > 0) {
   		 if(fds.revents &POLLOUT){
		    num=write(fd, data, strlen(data));
		    return num;
	    }
    }
}

int UART_Receive(int fd, char *receive)
{
    int num;
	int ret;
	ret = poll(&fds, 1, 0);
	if (ret == -1) {
		perror("poll");
		return -1;
	} 
	else if (ret > 0) {
		if(fds.revents &POLLIN){
		    num=read(fd, receive, sizeof(receive)-1);
		    return num;
		}
	}
}

int UART_Init(char *device, int baud)
{
	int fd;
	int ret;
	struct termios termios_p; 
//1.打开串口设备。不当作控制台。
    fd = open(device, O_NOCTTY | O_RDWR);
    if (fd < 0) {
        perror("open error");
        return -1;
    }
//2. 填充设备结构体
	memset(&termios_p, 0 ,sizeof(termios_p));
	termios_p.c_cflag |= CREAD;     //使能接收器的接收功能!必须配置的。
	termios_p.c_cflag |= CLOCAL;    //忽略调制解调器线路状态。
	termios_p.c_cflag |=CS8;        //设置八位数据位。
	
    //这里为了方便观看,所以写出来下面的配置,这里其实可以不写,因为我们已经先前清空结构体了。
	termios_p.c_cflag &=~CSTOPB; //设置一位停止位。
	termios_p.c_cflag &=~PARENB; //无奇偶校验位。
	termios_p.c_lflag &=~ECHO ; //不回显
	
	// 设置超时和最小读取字符数。必须配置!!
    termios_p.c_cc[VTIME] = 1;
    termios_p.c_cc[VMIN] = 128;
	
    switch (baud){  //设置波特率
			case 9600:
						cfsetispeed(&termios_p,B9600);  //设置接收波特率
						cfsetospeed(&termios_p,B9600);  //设置发送波特率
						break;
			case 115200:
						cfsetispeed(&termios_p,B115200);  //设置接收波特率
						cfsetospeed(&termios_p,B115200);  //设置发送波特率
						break;
			default:
						printf("不支持此波特率\n");
						break;
   }
   
//3. 清空接收/发送缓冲,准备发送和接收数据 
	tcflush(fd, TCIOFLUSH);

//4. 将配置好的设备结构体设置上(绑定),激活设置的配置。
	ret =tcsetattr( fd,TCSAFLUSH,&termios_p);
	if (ret < 0) {
        perror("tcsetattr error");
        return -3;
    }
	
	return fd; //返回文件描述符
}


main.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <termios.h>
#include <string.h>
#include <pthread.h>
#include <signal.h>
#include <poll.h>
#include "uart.h" 

pthread_attr_t attr; //线程属性
int fd;  //文件描述符
struct pollfd fds;

void signal_task(int arg)
{
    printf("销毁相关属性\n");
    UART_Close(fd);
    pthread_attr_destroy(&attr); //销毁线程属性
    exit(0);
}

// 线程任务函数
void *task(void *arg) {
    int ret;
    char buff[64];
    while (1) {
			        memset(buff, 0, sizeof(buff));
			        ret = UART_Receive(fds.fd, buff); // 读取数据到buff中
			        if (ret > 0) {
			            buff[ret] = '\0'; // 确保字符串以NULL结尾
			            printf("Receive: %s\n", buff);
			        } 
			        else if(ret <0) break;
             }
    pthread_exit(NULL);
}

int main()
{
	char buff[100];
	int ret;
	pthread_t thread;
	char poll_state[64];

	fd=UART_Init("/dev/ttyAMA2",115200);
	if(fd<0){
		perror("UART_Init error");
		return -1;
	}
	
	fds.fd=fd;
	fds.events = POLLOUT|POLLIN; // 有普通事件写/有普通事件读

	signal(2, signal_task);
	
	pthread_attr_init(&attr);  //初始化线程属性
	pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); //设置子线程分离属性,子线程推出后自动销毁。
	ret=pthread_create(&thread,NULL, task, NULL); //只有一个子线程,可以取地址操作。
	if(ret<0){
         perror("pthread_create error");
         return -1;
    }
    
	while(1){
	    memset(buff,0,sizeof(buff));
		fgets(buff,sizeof(buff),stdin);
		UART_Send(fds.fd, buff);
	  }
	return 0;
}

●select优化(功能和poll一样)

   select 函数可以监视多个文件描述符,等待其中的一个或多个变为“就绪”状态。其功能和poll一样。
   使用步骤:先定义一个结构体(检测读/ 检测写),将检测的文件描述符添加到这个结构体中。然后使用select进行绑定检测,如果检测到有事件发生,则函数返回值大于0,然后再进入大于0的函数体判断文件描述符在哪个结构体中(读结构体还是写结构体),如果文件描述符在写的结构体中,就进行写操作。反之相同。
   返回值:成功时,返回已就绪的文件描述符数量。超时且没有文件描述符就绪时,返回0。出错时,返回-1,并设置 errno。

<1> 函数原型

#include <sys/select.h>
#include <sys/time.h>

int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
/*
	nfds:指定要监视的文件描述符的数量。其值应为所有文件描述符中最大值加1。
	readfds:指向 fd_set 结构的指针,表示需要监视的可读文件描述符集合。
	writefds:指向 fd_set 结构的指针,表示需要监视的可写文件描述符集合。
	exceptfds:指向 fd_set 结构的指针,表示需要监视的异常文件描述符集合。
	timeout:指向 timeval 结构的指针,指定等待的超时时间。如果为 NULL,select 将无限等待。
*/

<2> 相关宏函数

FD_ZERO(fd_set *set);    // fd_set 结构清零。
FD_SET(int fd, fd_set *set);  //在 fd_set 结构中添加一个文件描述符。
FD_CLR(int fd, fd_set *set);  //从 fd_set 结构中删除一个文件描述符。
FD_ISSET(int fd, fd_set *set);  //检查文件描述符是否在 fd_set 结构中。

<3> 优化示例
uart.c

#include <sys/select.h>
#include <sys/time.h>
int UART_Send(int fd, char *data)
{ 
    int num;
    int ret;
    fd_set writefds;
    struct timeval timeout;
    
    // 设置超时时间100ms
    timeout.tv_sec = 0;
    timeout.tv_usec = 100000;
    FD_ZERO(&writefds); //清空结构体,丢弃原来旧的数据。
    FD_SET(fd, &writefds);  //在 fd_set 结构中添加一个文件描述符。

    ret=select(fd+1,  NULL, &writefds , NULL, &timeout);
	if (ret == -1) {
			perror("select");
			return -1;
	 } 
    else if (ret > 0) {
   		 if(FD_ISSET(fd,&writefds)){
		    num=write(fd, data, strlen(data));
		    return num;
	    }
    }
}

int UART_Receive(int fd, char *receive)
{
    int num;
	int ret;
	fd_set readfds;
	struct timeval timeout;
    
    // 设置超时时间100ms
    timeout.tv_sec = 0;
    timeout.tv_usec = 100000;
    FD_ZERO(&readfds); //清空结构体,丢弃原来旧的数据。
    FD_SET(fd, &readfds);  //在 fd_set 结构中添加一个文件描述符。

    ret=select(fd+1,  &readfds, NULL, NULL, &timeout);
	if (ret == -1) {
		perror("poll");
		return -1;
	} 
	else if (ret > 0) {
		if(FD_ISSET(fd, &readfds)){
		    num=read(fd, receive, sizeof(receive)-1);
		    return num;
		}
	}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/695703.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【RabbitMQ】RabbitMQ配置与交换机学习

【RabbitMQ】RabbitMQ配置与交换机学习 文章目录 【RabbitMQ】RabbitMQ配置与交换机学习简介安装和部署1. 安装RabbitMQ2.创建virtual-host3. 添加依赖4.修改配置文件 WorkQueues模型1.编写消息发送测试类2.编写消息接收&#xff08;监听&#xff09;类3. 实现能者多劳 交换机F…

【深度学习】—— 神经网络介绍

神经网络介绍 本系列主要是吴恩达深度学习系列视频的笔记&#xff0c;传送门&#xff1a;https://www.coursera.org/deeplearning-ai 目录 神经网络介绍神经网络的应用深度学习兴起的原因 神经网络&#xff0c;全称人工神经网络&#xff08;Artificial Neural Network&#xf…

25.逢七必过

上海市计算机学会竞赛平台 | YACSYACS 是由上海市计算机学会于2019年发起的活动,旨在激发青少年对学习人工智能与算法设计的热情与兴趣,提升青少年科学素养,引导青少年投身创新发现和科研实践活动。https://www.iai.sh.cn/problem/363 题目描述 逢七必过的游戏规则如下:对一…

Linux安装Docker | 使用国内镜像

环境 CentOS7 先确认能够上网 curl www.baidu.com返回该输出说明网络OK 步骤一&#xff1a;安装gcc 和 gcc-c yum -y install gccyum -y install gcc-c步骤二&#xff1a;安装Docker仓库 yum install -y yum-utils接下来配置yum的国内镜像 yum-config-manager --add-re…

激活乡村振兴新动能:推动农村产业融合发展,打造具有地方特色的美丽乡村,实现乡村全面振兴

目录 一、推动农村产业融合发展 1、农业产业链条的延伸 2、农业与旅游业的结合 二、挖掘地方特色&#xff0c;打造美丽乡村 1、保护和传承乡村文化 2、发展特色农业 三、加强基础设施建设&#xff0c;提升乡村品质 1、改善农村交通条件 2、提升农村水利设施 四、促进…

大数据湖一体化运营管理建设方案(49页PPT)

方案介绍&#xff1a; 本大数据湖一体化运营管理建设方案通过构建统一存储、高效处理、智能分析和安全管控的大数据湖平台&#xff0c;实现了企业数据的集中管理、快速处理和智能分析。该方案具有可扩展性、高性能、智能化、安全性和易用性等特点&#xff0c;能够为企业数字化…

水滴型锤片粉碎机:多功能粉碎利器

在现代工业生产中&#xff0c;粉碎机作为一种重要的机械设备&#xff0c;广泛应用于饲料、化工、木材等多个领域。其中&#xff0c;水滴型锤片粉碎机凭借其设计和粉碎能力&#xff0c;成为市场上的热门产品。 水滴型锤片粉碎机其设计灵感来源于水滴的形态。这种设计使得机器在…

vmware-17虚拟机安装教程,安装linux centos系统

下载VMware 1.进入VMware官网&#xff1a;https://www.vmware.com/sg/products/workstation-pro.html 2.向下翻找到&#xff0c;如下界面并点击“现在安装” 因官网更新页面出现误差&#xff0c;现提供vmware17安装包网盘链接如下&#xff1a; 链接&#xff1a;https://pan.b…

【SpringBoot + Vue 尚庭公寓实战】基本属性接口实现(七)

【SpringBoot Vue 尚庭公寓实战】基本属性接口实现&#xff08;七&#xff09; 文章目录 【SpringBoot Vue 尚庭公寓实战】基本属性接口实现&#xff08;七&#xff09;1、保存或更新属性名称2、保存或更新属性值3、查询全部属性名称和属性值列表4、根据ID删除属性名称5、根据…

freertos内核拓展DAY2(消息队列)

这节内容是信号量的基础&#xff0c;因为创建以及发送/等待信号量所调用的底层函数&#xff0c;就是创建/发送/接受消息队列时所用到的通用创建函数&#xff0c;这里先补充一下数据结构中关于队列的知识。 目录 1. 队列原理 1.1 顺序队列操作 1.2 循环队列操作 2.消息队列原…

N32G45XVL-STB之移植LVGL(lvgl-8.2.0)

目录 概述 1 软硬件介绍 1.1 软件版本信息 1.2 ST7796-LCD 1.3 MCU IO与LCD PIN对应关系 2 认识LVGL 2.1 LVGL官网 2.2 LVGL库文件下载 3 移植LVGL 3.1 准备移植文件 3.2 添加lvgl库文件到项目 3.2.1 src下的文件 3.2.2 examples下的文件 3.2.3 配置文件路径 3.2…

python-数字黑洞

[题目描述] 给定一个三位数&#xff0c;要求各位不能相同。例如&#xff0c;352是符合要求的&#xff0c;112是不符合要求的。将这个三位数的三个数字重新排列&#xff0c;得到的最大的数&#xff0c;减去得到的最小的数&#xff0c;形成一个新的三位数。对这个新的三位数可以重…

【Web世界探险家】3. CSS美学(二)文本样式

&#x1f4da;博客主页&#xff1a;爱敲代码的小杨. ✨专栏&#xff1a;《Java SE语法》 | 《数据结构与算法》 | 《C生万物》 |《MySQL探索之旅》 |《Web世界探险家》 ❤️感谢大家点赞&#x1f44d;&#x1f3fb;收藏⭐评论✍&#x1f3fb;&#xff0c;您的三连就是我持续更…

Web学习_SQL注入_布尔盲注

盲注就是在SQL注入过程中&#xff0c;SQL语句执行后&#xff0c;查询到的数据不能 回显到前端页面。此时&#xff0c;我们需要利用一些方法进行判断或者尝 试&#xff0c;这个过程称之为盲注。而布尔盲注就是SQL语句执行后&#xff0c;页面 不返回具体数据&#xff0c;数据库只…

32、matlab:基于模板匹配的车牌识别

1、准备工作 1&#xff09;准备材料 车牌字符模板和测试的实验车牌 2&#xff09;车牌字符模板 数字、字母和省份缩写 3&#xff09;测试车牌 四张测试车牌 2、车牌识别实现(已将其嵌入matlab) 1&#xff09;打开APP 找到APP 找到我的APP双击点开 2)界面介绍 包括&am…

神经网络 torch.nn---Non-Linear Activations (ReLU)

ReLU — PyTorch 2.3 documentation torch.nn - PyTorch中文文档 (pytorch-cn.readthedocs.io) 非线性变换的目的 非线性变换的目的是为神经网络引入一些非线性特征&#xff0c;使其训练出一些符合各种曲线或各种特征的模型。 换句话来说&#xff0c;如果模型都是直线特征的…

【数据结构】排序(上)

个人主页~ 堆排序看这篇~ 还有这篇~ 排序 一、排序的概念及应用1、概念2、常见的排序算法 二、常见排序的实现1、直接插入排序&#xff08;1&#xff09;基本思想&#xff08;2&#xff09;代码实现&#xff08;3&#xff09;时间复杂度&#xff08;4&#xff09;空间复杂度 2…

eNSP学习——RIP的路由引入

目录 主要命令 原理概述 实验目的 实验内容 实验拓扑 实验编址 实验步骤 1、基本配置 2、搭建公司B的RIP网络 3、优化公司B的 RIP网络 4、连接公司A与公司B的网络 需要eNSP各种配置命令的点击链接自取&#xff1a;华为&#xff45;NSP各种设备配置命令大全PDF版_ensp…

Windows开始ssh服务+密钥登录+默认启用powershell

文章内所有的命令都在power shell内执行&#xff0c;使用右键单击Windows徽标&#xff0c;选择终端管理员即可打开 Windows下OpenSSH的安装 打开Windows power shell&#xff0c;检查SSH服务的安装状态。会返回SSH客户端和服务器的安装状态&#xff0c;一下是两个都安装成功的…

操作系统教材第6版——个人笔记5

3.2 单连续分区存储管理 3.2.1 单连续分区存储管理 单连续分区存储管理 每个进程占用一个物理上完全连续的存储空间(区域) 单用户连续分区存储管理固定分区存储管理可变分区存储管理 单用户连续分区存储管理 主存区域划分为系统区与用户区设置一个栅栏寄存器界分两个区域…