FPGA:串口通信发送模块

news2024/11/15 16:02:13

FPGA:串口通信发送模块

  • 1、串口通信的概念及分类
    • (1)串口通信概念
    • (2)串口通信分类
  • 2、UART协议
    • (1)FPGA实现UART协议发送模块思路
    • (2)Verilog设计文件
      • a.波特率选择模块
      • b.单个比特发送模块
      • c.位状态计数器
      • d.延时计数器
      • e.数据保存寄存器
      • f.位发送逻辑
      • g.led翻转逻辑
      • h.使能信号en_send逻辑
    • (3)verilog测试文件(tb)
  • 3、仿真测试
  • 4、误差分析

1、串口通信的概念及分类

(1)串口通信概念

外设和计算机间,通过数据信号线 、地线、控制线等,按位进行传输数据的一种通讯方式。

(2)串口通信分类

首先,按照时钟进行划分,可以分为异步串口通信和同步串口通信。异步串口通信是一种无需发送时钟信号的通信方式。数据传输通过起始位、数据位、奇偶校验位和停止位来同步。同步串口通信使用时钟信号来同步发送方和接收方的数据传输。
其次,按照工作方式进行划分,可以分为全双工通信和半双工通信。全双工通信允许同时发送和接收数据。而半双工通信指数据传输只能在一个方向上进行,即一个时间点只能发送或接收数据。
常见异步串口通信方式:RS-232、RS-422、RS-485以及UART
常见同步串口通信方式:SPI、I²C
常见全双工通信:RS-232、UART、SPI
常见半双工通信:RS485等
目前比较流行的还有一些无线技术进行串口通信,常见的标准包括:蓝牙技术或者Zigbee、LoRa等无线协议。
本文基于Xilinx开发板实现UART串口的第一部分,即串口发送。

2、UART协议

UART(Universal Asynchronous Receiver/Transmitter,通用异步收发器)是一种异步串口通信方式。它属于异步串口通信的范畴,因为它不使用时钟信号来同步发送方和接收方的数据传输。UART使用起始位、数据位、可选奇偶校验位和停止位进行数据传输。下面来一一阐述一下各个位的作用以及常用的参数。
(1)数据位:单个UART数据传输在开始到停止期间可发送的数据的位数,可选值5、6、7、8。默认为8。
(2)波特率:每秒钟可通信的比特的个数,常见有9600、19200、38400、115200等。
(3)奇偶校验位:进行奇偶校验,有奇校验和偶校验两种,奇校验让原有数据序列中(包括要加上的校验位)1的个数为奇数。如1101000(0),使得整体1的个数为奇数个。偶校验同理。
(4)起始位:起始位是一个逻辑低电平(0)。当 UART 空闲时,数据线处于逻辑高电平(1)。因此,当一个数据帧开始时,数据线会从高电平变为低电平,这个电平变化标志着数据传输的开始。
(5)停止位:标志传输完成。停止位是一个逻辑高电平(1)。UART 通常使用 1 个或 1.5 个或 2 个停止位,这取决于通信双方的设置。
在这里插入图片描述

(1)FPGA实现UART协议发送模块思路

本例中,发送部分由1位起始位(0),8位数据位,1位停止位(1)组成。波特率有四种选择9600、19200、38400、115200。每一秒发送一次(10位)数据,以波特率9600而言,发送1位数据需要 1 ÷ 9600 = 1 9600 s 1÷9600=\frac{1}{9600}s 1÷9600=96001s,对应时钟周期数为 1 0 9 n s ÷ 20 n s ÷ 1 9600 ≈ 5208 T 10^9ns÷20ns÷\frac{1}{9600}≈5208T 109ns÷20ns÷960015208T,即需要5208个时钟周期。需要13位二进制对单个数据位传输时进行计数。我们可以借助下图来帮助我们理解(起始位和结束位忽略):
在这里插入图片描述
刚才的13位波特率计数器就是对里面的每一个比特传输是否完成进行计数。
接下来,我们需要设计一个位状态计数器,用来判断当前发送到哪一位了,如果发送到第10位,此时需要置0。一共有10位数据,所以需要4位二进制设计位状态计数器
然后还需要设计一个一秒的计数器,一秒钟在之前计算过,为50_000_000个时钟周期,需要26位二进制进行计数。此外我们再设计一个led灯,每次发送完成后led灯翻转一次。

(2)Verilog设计文件

基本信号定义如下:

module uart_baud(
	Data,Reset_n,Baud,clk,uart_tx,led
    );
input[7:0] Data;
input[2:0] Baud;   // 提供四种不同波特的计数器,分别为9600,19200,38400,115200
input clk;
input Reset_n;
output reg uart_tx;
output reg led;

a.波特率选择模块

提供了四种不同波特率,分别是9600、19200、38400、115200,需要提供一个两位状态寄存器作为波特选择模块,这里使用三位,方便读者后续进行波特率扩充。单个比特传输最慢的波特率为9600,其需要 1 9600 s \frac{1}{9600}\mathrm{s} 96001s,即5208个时钟周期,之前计算,需要13位二进制进行计数。

input[2:0] Baud;   // 提供四种不同波特的计数器,分别为9600,19200,38400,115200
reg [12:0] baud_param;   //9600的时间只需要13位
always@(posedge clk or negedge Reset_n)
if (!Reset_n)
	baud_param<=1'b0;
else
	begin
		case(Baud)
			0:baud_param = 1000000000/9600/20-1;
			1:baud_param = 1000000000/19200/20-1;	
			2:baud_param = 1000000000/38400/20-1;	
			3:baud_param = 1000000000/115200/20-1;	
		endcase
	end

b.单个比特发送模块

单个比特发送模块可分以下几个判断:如果复位信号来临,就让比特计数器为0。根据上一部分图片可以知道传输结束后有一部分“空闲等待”的时间,这个时间不发送比特,所以需要一个使能信号en_send,有效时才发送数据,当en_send有效,我们需要判断当前计数器是否已经到了波特计数器的临界点,例如9600波特率为5208-1,如果到临界点则置零,否则自加。

reg en_send; //使能信号定义
reg [12:0] baud_div_cnt;
always@(posedge clk or negedge Reset_n)
if (!Reset_n)
	baud_div_cnt<=1'b0;
else if(en_send)   // 使能信号有效
	begin 
		if(baud_div_cnt==baud_param)  //如果到了临界点
			baud_div_cnt<=1'b0;
		else 
			baud_div_cnt<= baud_div_cnt+1'b1;
	end
else
	baud_div_cnt<=1'b0;

c.位状态计数器

一共有10个位,即1个起始位(低电平),8个数据位,1个结束位(高电平)。所以需要4位二进制进行状态计数。如果复位信号来临,状态置零。如果在波特计数器计时满一次(代表一位传输完成),这时需要判断是否已到结束位,如果在,则置零,否则置1。

reg [3:0] bit_cnt;
always@(posedge clk or negedge Reset_n)
if (!Reset_n)
	bit_cnt<=1'b0;
else if(baud_div_cnt==baud_param)
	begin
		if(bit_cnt==9)    // 当前已发送到结束位且已完成
			bit_cnt<=1'b0;
		else
			bit_cnt<=bit_cnt+1'b1;
	end

d.延时计数器

计时一秒, 1 s = 1 0 9 n s 1\mathrm{s}=10^9\mathrm{ns} 1s=109ns,而一个时钟周期 20 n s 20\mathrm{ns} 20ns,所以一秒钟有 1 0 9 ÷ 20 = 50000000 T 10^9\div20=50000000\mathrm{T} 109÷20=50000000T,需要26位二进制。

// 延时计数器,计时1秒钟
parameter MCNT_DLY=50_000_000-1;
reg [25:0] Delay_cnt;
always@(posedge clk or negedge Reset_n)
if(!Reset_n)
	Delay_cnt<=1'b0;
else if(Delay_cnt== MCNT_DLY)
	Delay_cnt<=1'b0;
else 
	Delay_cnt <= Delay_cnt+1'b1;

e.数据保存寄存器

在每次传输的时候,需要保证数据在传输过程内不发生变化,所以需要保存下每一次开始数据,可以设置一个相同位的寄存器,即8位寄存器保存对应数据,在每次传输完成后保存下当前数据。

reg [7:0] r_data;
// 保存Data数据
always@(posedge clk or negedge Reset_n)
if(!Reset_n)
	r_data<=0;
else if(Delay_cnt == MCNT_DLY)
	r_data <= Data;

f.位发送逻辑

主要负责uart端口的输出,如果复位信号来临,令它为1,因为起始位为0,这样容易进行区分,还需判断此时是否在“空闲等待”阶段,如果空闲阶段也要令uart为1,其余的时候值根据前面定义的bit_cnt,即位状态进行判断。

always@(posedge clk or negedge Reset_n)
if(!Reset_n)
	uart_tx<=0;
else if(en_send==0)
	uart_tx<=1;
else
	case(bit_cnt)
		0:uart_tx<=1'b0;
		1:uart_tx<=r_data[0];
		2:uart_tx<=r_data[1];
		3:uart_tx<=r_data[2];
		4:uart_tx<=r_data[3];
		5:uart_tx<=r_data[4];
		6:uart_tx<=r_data[5];
		7:uart_tx<=r_data[6];
		8:uart_tx<=r_data[7];
		9:uart_tx<=1'b1;
		default uart_tx<=uart_tx;
	endcase

g.led翻转逻辑

当复位信号有效时,led为0,即不亮,否则当每次数据(10位)传输完成时,led等翻转一次。即需要满足条件bit_cnt==9,表示计数到结束位,还要保证结束位传输结束才翻转,即当前的波特率计数器到达对应的值,即baud_div_cnt == baud_param。

always@(posedge clk or negedge Reset_n)
if(!Reset_n)
	led<=1'b0;
else if((bit_cnt==9) && (baud_div_cnt==baud_param))
	led<=!led;

h.使能信号en_send逻辑

使能信号,经过之前分析,可知当复位有效,不使能;当在空闲态的时候,不使能;当每一秒结束,即延时计数器满了的时候,使能,表示可再次发送数据。

always@(posedge clk or negedge Reset_n)
if(!Reset_n)
	en_send<=1'b0;
else if((bit_cnt==9) && (baud_div_cnt==baud_param))
	en_send<=1'b0;
else if(Delay_cnt==MCNT_DLY)
	en_send<=1'b1;
endmodule

以上就是所有模块的实现原理以及具体代码实现,为了方便大家运行测试,已经将整体代码整合在下方:

module uart_baud(
	Data,Reset_n,Baud,clk,uart_tx,led
    );
input[7:0] Data;
input[2:0] Baud;   // 提供四种不同波特的计数器,分别为9600,19200,38400,115200
input clk;
input Reset_n;
output reg uart_tx;
output reg led;
reg en_send; // 波特率使能信号
reg [7:0] r_data;
parameter MCNT_DLY=50_000_000-1;
// 波特率选择功能
reg [12:0] baud_param;   //9600的时间只需要13位
always@(posedge clk or negedge Reset_n)
if (!Reset_n)
	baud_param<=1'b0;
else
	begin
		case(Baud)
			0:baud_param = 1000000000/9600/20-1;
			1:baud_param = 1000000000/19200/20-1;	
			2:baud_param = 1000000000/38400/20-1;	
			3:baud_param = 1000000000/115200/20-1;	
		endcase
	end
// 波特率计数器
reg [12:0] baud_div_cnt;
always@(posedge clk or negedge Reset_n)
if (!Reset_n)
	baud_div_cnt<=1'b0;
else if(en_send)
	begin 
		if(baud_div_cnt==baud_param)
			baud_div_cnt<=1'b0;
		else 
			baud_div_cnt<= baud_div_cnt+1'b1;
	end
else
	baud_div_cnt<=1'b0;
// 位状态计数器
reg [3:0] bit_cnt;
always@(posedge clk or negedge Reset_n)
if (!Reset_n)
	bit_cnt<=1'b0;
else if(baud_div_cnt==baud_param)
	begin
		if(bit_cnt==9)
			bit_cnt<=1'b0;
		else
			bit_cnt<=bit_cnt+1'b1;
	end
// 延时计数器,计时1秒钟
reg [25:0] Delay_cnt;
always@(posedge clk or negedge Reset_n)
if(!Reset_n)
	Delay_cnt<=1'b0;
else if(Delay_cnt== MCNT_DLY)
	Delay_cnt<=1'b0;
else 
	Delay_cnt <= Delay_cnt+1'b1;
// 保存Data数据
always@(posedge clk or negedge Reset_n)
if(!Reset_n)
	r_data<=0;
else if(Delay_cnt == MCNT_DLY)
	r_data <= Data;
// 位发送逻辑
always@(posedge clk or negedge Reset_n)
if(!Reset_n)
	uart_tx<=0;
else if(en_send==0)
	uart_tx<=1;
else
	case(bit_cnt)
		0:uart_tx<=1'b0;
		1:uart_tx<=r_data[0];
		2:uart_tx<=r_data[1];
		3:uart_tx<=r_data[2];
		4:uart_tx<=r_data[3];
		5:uart_tx<=r_data[4];
		6:uart_tx<=r_data[5];
		7:uart_tx<=r_data[6];
		8:uart_tx<=r_data[7];
		9:uart_tx<=1'b1;
		default uart_tx<=uart_tx;
	endcase
// led翻转逻辑
always@(posedge clk or negedge Reset_n)
if(!Reset_n)
	led<=1'b0;
else if((bit_cnt==9) && (baud_div_cnt==baud_param))
	led<=!led;
// 使能信号定义
always@(posedge clk or negedge Reset_n)
if(!Reset_n)
	en_send<=1'b0;
else if((bit_cnt==9) && (baud_div_cnt==baud_param))
	en_send<=1'b0;
else if(Delay_cnt==MCNT_DLY)
	en_send<=1'b1;
endmodule

(3)verilog测试文件(tb)

测试文件主要就是进行例化和信号的初始化,在这里主要进行三个测试,首先复位后让波特率为9600,延时 30 m s 30\mathrm{ms} 30ms传输两个不同的数据,分别为10101010和01010101,然后修改波特率为19200,数据仍然是01010101,波特率增大一倍,所以传输时间应该缩小到原来的一半,待会可以从仿真波形查看。具体代码如下:

`timescale 1ns / 1ps
module uart_baud_tb();
reg [7:0] Data;
reg [2:0] Baud;
reg clk;
reg Reset_n;
wire uart_tx;
wire led;
uart_baud uart_baud_inst(   //例化模块
	.Data(Data),
	.Reset_n(Reset_n),
	.Baud(Baud),
	.clk(clk),
	.uart_tx(uart_tx),
	.led(led)
);
//减少仿真的时间,使MCNT_DLY在测试文件缩小到原来的1/100,即10ms发送一次
defparam uart_baud_inst.MCNT_DLY=500000-1;   
initial clk<=1;
always #10 clk<=!clk;
initial begin
Reset_n<=0;
Data<=0;
#201;  //复位结束
Baud<=0;  // 波特率为9600
Reset_n<=1;
Data<=8'b10101010;  // 初始化Data值
#30000000;  // 延时30ms
Data<=8'b01010101;    // 修改Data值
#30000000;// 延时30ms
Baud<=1;   // 修改波特率为19200
Data<=8'b01010101;  // Data值保持不变
#30000000;
$stop;
end
endmodule

3、仿真测试

首先查看整体的状态,如下所示:
在这里插入图片描述
基本实现了数据发送和led灯在每次数据发送完成后翻转,接下来放大波形,具体查看每次发送的数据以及时间计算波特率。
在这里插入图片描述
再看一下30ms后发送的数据
在这里插入图片描述
再查看波特率修改为原来两倍后的结果
在这里插入图片描述

520.7800 × 2 = 1041.56 ( μ s ) 520.7800×2=1041.56(\mathrm{μs}) 520.7800×2=1041.56(μs),而上面显示的是 1041.58 μ s 1041.58\mathrm{μs} 1041.58μs,那少的部分哪里去了呢?

4、误差分析

先说结论,我们在计算的时候,小数部分我们舍弃了导致的误差,这是因为Verilog里面没有浮点型的运算,所以要用整数替换。
计算9600波特率传输一个比特的时间为
1 0 9 ÷ 9600 ÷ 20 = 5208 1 3 T 10^9\div9600\div20=5208\frac{1}{3}T 109÷9600÷20=520831T而我们计算的时候是按照 5208 T 5208T 5208T计算的,同理 1 0 9 ÷ 19200 ÷ 20 = 2604 1 6 T 10^9\div19200\div20=2604\frac{1}{6}T 109÷19200÷20=260461T按照 2604 T 2604T 2604T计算,所以单个数据之间相差 1 3 − 1 6 = 1 6 T \frac{1}{3}-\frac{1}{6}=\frac{1}{6}T 3161=61T
一次发送10位数据,相差
1 6 × 10 = 5 3 T \frac{1}{6}×10=\frac{5}{3}T 61×10=35T
再加上四舍五入和计算精度的误差,所以会造成误差。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

鸿蒙系统开发【ASN.1密文转换】安全

ASN.1密文转换 介绍 本示例对使用kit.CryptoArchitectureKit加密后的密文格式进行转换。kit.CryptoArchitectureKit加密后的密文格式默认为以base64显示的ASN.1格式问题&#xff0c;通过对密文进行base64变换后得到字符数组&#xff0c;以16进制数字显示&#xff0c;再此基础…

看完这几本大模型书籍,你就是LLM大师,非常详细收藏我这一篇就够了

以下是几本关于大模型和人工智能领域的经典书籍&#xff0c;它们各自具有独特的特点和适用人群&#xff1a; 《深度学习》&#xff08;Deep Learning&#xff09; 作者&#xff1a;伊恩古德费洛&#xff08;Ian Goodfellow&#xff09;、约书亚本吉奥&#xff08;Yoshua Bengi…

刀具磨损预测工器具磨损预测-RIME-CNN-SVM霜冰算法优化-完整代码数据

直接看项目演示: 刀具磨损预测工器具磨损预测-RIME-CNN-SVM霜冰算法优化_哔哩哔哩_bilibili 效果演示: 代码: import numpy as np import torch import torch.nn as nn import torch.nn.functional as F import torch.optim as optim from torch.utils.data import DataLo…

算法第十七天:leetcode242.有效的字母异位词

一、有效的字母异位词的题目描述与链接 242.有效的字母异位词如下表所示&#xff0c;您可以直接复制下面网址进入力扣学习&#xff0c;在观看下面的内容之前您一定要先做一遍哦&#xff0c;以便让你印象更加深刻&#xff01; https://leetcode.cn/problems/valid-anagram/desc…

curl wget介绍

0 Preface/Foreword 1 curl介绍 curl&#xff1a;transfer a URL synopsis&#xff1a; curl [options] [URL...] 描述&#xff1a; curl is a tool to transfer data from or to a server, using one of the supported protocols (DICT, FILE, HTTP, HTTPS, IMAP, FTP, FT…

【JavaEE初阶】懒汉模式与饿汉模式及指令重排序问题

目录 &#x1f4d5; 单例模式 &#x1f333; 饿汉模式 &#x1f6a9; 线程安全 &#x1f38d; 懒汉模式 &#x1f6a9; 懒汉模式-单线程版 &#x1f6a9; 懒汉模式-多线程版 &#x1f384; 指令重排序 &#x1f4d5; 单例模式 单例模式是一种经典的设计模式&#xff0c;…

Python | Leetcode Python题解之第312题戳气球

题目&#xff1a; 题解&#xff1a; class Solution:def maxCoins(self, nums: List[int]) -> int:n len(nums)rec [[0] * (n 2) for _ in range(n 2)]val [1] nums [1]for i in range(n - 1, -1, -1):for j in range(i 2, n 2):for k in range(i 1, j):total v…

文档编辑协作神器,全方位解决方案,灵活部署赋能个人与企业-onlyoffice

文章目录 1. 引言2. 免费畅享&#xff0c;个人的编辑利器2.1 PDF直接修改功能2.2 插件支持&#xff0c;应对多种工作场景2.3 AI助手智慧办公 3. 私有部署&#xff0c;开发者的自定义编辑器3.1 开发者版本介绍3.2 私有部署&#xff0c;解决数据安全问题3.3 实用功能丰富&#xf…

基于SpringBoot+Vue的流浪猫狗救助救援网站(带1w+文档)

基于SpringBootVue的流浪猫狗救助救援网站(带1w文档) 基于SpringBootVue的流浪猫狗救助救援网站(带1w文档) 该流浪猫狗救助救援网站在Windows平台下完成开发&#xff0c;采用java编程语言开发&#xff0c;将应用程序部署于Tomcat上&#xff0c;加之MySQL接口来实现交互式响应服…

LKEB-4030电阻器LKEB4030模块可议价

LKEB-4030电阻器LKEB4030模块可议价 LKEB-4030电阻器LKEB4030模块可议价 LKEB-4030电阻器LKEB4030模块可议价 LKEB-4030电阻器LKEB4030模块说明书 LKEB-4030电阻器LKEB4030模块接线图 LKEB-4030电阻器LKEB4030模块线路图 LKEB-4030电阻器&#xff08;Resistor&#xff09…

新手如何参加护网行动?一篇带你零基础入门到精通

前言 1、什么是护网行动 “护网行动”是指国家组织的网络安全防护演练行动。 护网行动通常由国家相关部门组织&#xff0c;旨在模拟真实的网络攻击情况&#xff0c;检验和提升关键信息基础设施、重要信息系统和大数据的安全防护能力。 在护网行动中&#xff0c;会有专业的攻…

基于微信小程序的搬家服务系统(源码+lw+部署文档+讲解等)

博主介绍&#xff1a;✌全网粉丝10W,csdn特邀作者、博客专家、CSDN新星计划导师、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和学生毕业项目实战,高校老师/讲师/同行前辈交流✌ 涉及技术栈&#xff1a;SpringBoot、Vue、SSM、H…

SEO域名篇—如何选择一个好域名

SEO 域名选择误区 很多做站的大佬都告诉新入行的SEO优化员&#xff0c;选择域名一定要选择老域名&#xff0c;这里也是一个误区&#xff0c;很多新手在听了大佬的话后去就选择一些5年-15年的老域名直接做站&#xff0c;不去查查这个域名是否被360、QQ、微信等等一切搜索引擎拦…

Unity | Shader基础知识(第二十集:应用-简易流光、LOD)

目录 一、前言 二、LOD 1.什么是LOD 2.代码如何调节LOD 三、流光 1.资源准备 2.uv移动 3.获取图片中的uv 4.改变uv去取流光的颜色&#xff08;时间的应用&#xff09; 5.图片叠加 6.透明图片的叠加 四、纯净代码 五、作者的碎碎念 一、前言 有小伙伴问&#xf…

使用腾讯云域名解析实现网站重定向

前言 最近&#xff0c;在CSDN平台上我写了一系列博客&#xff0c;希望能与同学分享一些技术心得。然而&#xff0c;每当需要向他人推荐我的博客时&#xff0c;那串复杂且缺乏规律的CSDN博客首页域名总让我感到不便。这让我开始思考&#xff0c;如果能将这一域名替换为一个既个…

技术速递|VS Code Java 7月更新 - Gradle 支持增强!用户体验改进与 Spring 新功能

作者&#xff1a;Nick Zhu 排版&#xff1a;Alan Wang 大家好&#xff0c;欢迎来到 Visual Studio Code 7月份的 Java 更新&#xff01;本博客将覆盖最新的 Gradle 功能增强、用户体验改进以及新的 Spring 功能&#xff0c;让我们开始吧&#xff01; Gradle 功能增强 支持 Gr…

算法强训day19

一、小易的升级之路 链接&#xff1a;小易的升级之路_牛客题霸_牛客网 简单题 #include<iostream> using namespace std; long long gcd(long long m, long long x) {long long n ;while(x>0){n m % x;m x;x n;}return m; } int main() {int n;long long m;cin &…

文件上传漏洞--之upload-labs靶场(第6-10关)专栏更新ing......

注意&#xff1a; 为避免执行之前关卡的上传了的php文件代码&#xff0c;可以将upload文件夹下的文件清空 第六关&#xff1a; 第一步&#xff1a;查看源码 对比前面的几个关卡&#xff0c;里面没有文件去空格语句&#xff0c;可以使用后缀名加空格绕过 第二步&#xff1a;…

Python初学者必须掌握的基础知识点

Python初学者必须掌握的基础知识点包括数据类型与变量、控制结构&#xff08;条件语句和循环语句&#xff09;、基本数据结构&#xff08;列表、元组、字典、集合&#xff09;、函数与模块、以及字符串处理等。以下是对这些基础知识点及其对应代码的详细介绍&#xff1a; 1. …

Git常用命

转自&#xff1a;https://blog.csdn.net/ahjxhy2010/article/details/80047553 1.查看某个文件或目录的修改历史 git log filename #查看fileName相关的commit记录 git log -p filenam # 显示每次提交的diff#只看某次提交中的某个文件变化&#xff0c;commit-id  文件名…