【【UART 传输数据实验】】

UART 传输数据实验

通信方式在日常的应用中一般分为串行通信(serial communication)和并行通信(parallel communication)。
我们再来了解下串行通信的特点。串行通信是指数据在一条数据线上,一比特接一比特地按顺序传送的方式,这一点与并行通信是不同的。这里我们以传输一个字节(8 位)数据为例,在并行通信中,一个字节的数据是在 8 条并行传输线上同时由源地传送到目的地;而在串行通信中,因为数据是在一条传输线上一位接一位地顺序传送的,所以一个字节的数据要分 8 次进行传送。
如果我们以 T 为一个时间单位的话,那么并行通信发送一个字节的数据只需要 1T 的时间,而串行通信需要 8T 的时间,由此可以总结出串行通信的的特点:一是节省传输线,大大降低了使用成本,二是数据传送速度慢,这一点在大位宽的数据传输上尤为明显。综上可知,串行通信主要应用于长距离、低速率的通信场合。本次实验我们主要讲解下串行通信。
串行通信一般有 2 种通信方式:同步串行通信(synchronized serial communication)和异步串行通信(asynchronous serial communication)。同步串行通信需要通信双方在同一时钟的控制下同步传输数据;异步串行通信是指具有不规则数据段传送特性的串行数据传输。在常见的通信总线协议中,I2C,SPI 属于同步通信而 UART 属于异步通信。同步通信的通信双方必须先建立同步,即双方的时钟要调整到同一个频率,收发双方不停地发送和接收连续的同步比特流。异步通信在发送字符时,发送端可以在任意时刻开始发送字符,所以,在 UART 通信中,数据起始位和停止位是必不可少的。

UART 是一种采用异步串行通信方式的通用异步收发传输器(universal asynchronous receiver-transmitter),它在发送数据时将并行数据转换成串行数据来传输,在接收数据时将接收到的串行数据转换成并行数据。
UART 串口通信需要两根信号线来实现,一根用于串口发送,另外一根负责串口接收,如下图所示。对于 PC 来说它的 TX 要和对于 FPGA 来说的 RX 连接,同样 PC 的 RX 要和 FPGA 的 TX 连接,如果是两个TX 或者两个 RX 连接那数据就不能正常被发送出去或者接收到,所以这里大家不要弄混。
在这里插入图片描述

UART 在发送或接收过程中的一帧数据由 4 部分组成,起始位、数据位、奇偶校验位和停止位
在这里插入图片描述

起始位: 当不传输数据时,UART 数据传输线通常保持高电压电平。若要开始数据传输,发送 UART 会将传输线从高电平拉到低电平并保持 1 个波特率周期。当接收 UART 检测到高到低电压跃迁时,便开始以波特率对应的频率读取数据帧中的位。
数据帧: 数据帧包含所传输的实际数据。如果使用奇偶校验位,数据帧长度可以是 5 位到 8 位。如果不使用奇偶校验位,数据帧长度可以是 9 位。在大多数情况下,数据以最低有效位优先方式发送。
奇偶校验: 奇偶性描述数字是偶数还是奇数。通过奇偶校验位,接收 UART 判断传输期间是否有数据发生改变。电磁辐射、不一致的波特率或长距离数据传输都可能改变数据位。接收 UART 读取数据帧后,将计数值为 1 的位,检查总数是偶数还是奇数。如果奇偶校验位为 0(偶数奇偶校验),则数据帧中的 1 或逻辑高位总计应为偶数。如果奇偶校验位为 1(奇数奇偶校验),则数据帧中的 1 或逻辑高位总计应为奇数。当奇偶校验位与数据匹配时,UART 认为传输未出错。但是,如果奇偶校验位为 0,而总和为奇数,或者奇偶校验位为 1,而总和为偶数,则 UART 认为数据帧中的位已改变。
停止位: 为了表示数据包结束,发送 UART 将数据传输线从低电压驱动到高电压并保持 1 到 2 位时间。

UART 通信过程中的数据格式及传输速率是可设置的,为了正确的通信,收发双方应约定并遵循同样的设置。数据位可选择为 5、6、7、8 位,其中 8 位数据位是最常用的,在实际应用中一般都选择 8 位数据位;校验位可选择奇校验、偶校验或者无校验位;停止位可选择 1 位(默认),1.5 或 2 位。串口通信的速率用波特率表示,它表示每秒传输二进制数据的位数,单位是 bps(位/秒),常用的波特率有 9600、19200、38400、57600 以及 115200 等。

那么什么是波特率呢?波特率:即每秒传输的位数(bit)。一般选波特率都会有 9600,19200,115200等选项。其实意思就是每秒传输这么多个比特位数(bit)。在信息传输通道中,携带数据信息的信号单元叫作码元(因为串口是 1bit 进行传输的,所以其码元就代表一个二进制数),每秒通过信号传输的码元数称为码元的传输速率,简称“波特率”,常用符号“Baud”表示,其单位为“波特每秒”(Bps)。串口常见的波特率有 4800、9600、115200 等,此处我们选用 115200 的波特率进行讲解。通信信道每秒传输的信息量称为位传输速率,简称“比特率”,其单位为“每秒比特数”(bps)。比特率可由波特率计算得出,公式为比特率=波特率×单个调制状态对应的二进制位数。如果使用的是 115200 的波特率,其串口的比特率为 115200Bps×1bit = 115200bps,由计算得串口发送或者接收 1bit 数据的时间为一个波特,即 1/115200s。
在设置好数据格式及传输速率之后,UART 负责完成数据的串并转换,而信号的传输则由外部驱动电路实现。电信号的传输过程有着不同的电平标准和接口规范,针对异步串行通信的接口标准有 RS232、RS422、RS485 等,它们定义了接口不同的电气特性,如 RS-232 是单端输入输出,而 RS-422/485 为差分输入输出等。RS-232 标准的串口最常见的接口类型为 DB9。

现在来说 对于这样的 大的接口 DB9 来说 现在并不实用 我们用的更多的是 USB的形式 通过 USB 在电脑上 装入USB转串口协议 完成实现功能
在这里插入图片描述

实验任务:
本节实验任务是上位机通过串口调试助手发送数据给领航者开发板,领航者开发板 PL 端通过USB_UART 串口接收数据并将接收到的数据发送给上位机,完成串口数据环回。
在这里插入图片描述

下面是 发送端的 波形图
在这里插入图片描述

top.v

module uart_loopback(input            sys_clk  ,   //外部50MHz时钟input            sys_rst_n,   //系外部复位信号,低有效//UART端口    input            uart_rxd ,   //UART接收端口output           uart_txd     //UART发送端口);//parameter define
parameter CLK_FREQ = 50000000;    //定义系统时钟频率
parameter UART_BPS = 115200  ;    //定义串口波特率//wire define
wire         uart_rx_done;    //UART接收完成信号
wire  [7:0]  uart_rx_data;    //UART接收数据//*****************************************************
//**                    main code
//*****************************************************//串口接收模块
uart_rx #(.CLK_FREQ  (CLK_FREQ),.UART_BPS  (UART_BPS))    u_uart_rx(.clk           (sys_clk     ),.rst_n         (sys_rst_n   ),.uart_rxd      (uart_rxd    ),.uart_rx_done  (uart_rx_done),.uart_rx_data  (uart_rx_data));//串口发送模块
uart_tx #(.CLK_FREQ  (CLK_FREQ),.UART_BPS  (UART_BPS))    u_uart_tx(.clk          (sys_clk     ),.rst_n        (sys_rst_n   ),.uart_tx_en   (uart_rx_done),.uart_tx_data (uart_rx_data),.uart_txd     (uart_txd    ),.uart_tx_busy (            ));endmodule

uart_rx.v

module uart_rx(input               clk         ,  //系统时钟input               rst_n       ,  //系统复位,低有效input               uart_rxd    ,  //UART接收端口output  reg         uart_rx_done,  //UART接收完成信号output  reg  [7:0]  uart_rx_data   //UART接收到的数据);//parameter define
parameter CLK_FREQ = 50000000;               //系统时钟频率
parameter UART_BPS = 115200  ;               //串口波特率
localparam BAUD_CNT_MAX = CLK_FREQ/UART_BPS; //为得到指定波特率,对系统时钟计数BPS_CNT次//reg define
reg          uart_rxd_d0;
reg          uart_rxd_d1;
reg          uart_rxd_d2;
reg          rx_flag    ;  //接收过程标志信号
reg  [3:0 ]  rx_cnt     ;  //接收数据计数器
reg  [15:0]  baud_cnt   ;  //波特率计数器
reg  [7:0 ]  rx_data_t  ;  //接收数据寄存器//wire define
wire        start_en;//*****************************************************
//**                    main code
//*****************************************************
//捕获接收端口下降沿(起始位),得到一个时钟周期的脉冲信号
assign start_en = uart_rxd_d2 & (~uart_rxd_d1) & (~rx_flag);//针对异步信号的同步处理
always @(posedge clk or negedge rst_n) beginif(!rst_n) beginuart_rxd_d0 <= 1'b0;uart_rxd_d1 <= 1'b0;uart_rxd_d2 <= 1'b0;endelse beginuart_rxd_d0 <= uart_rxd;uart_rxd_d1 <= uart_rxd_d0;uart_rxd_d2 <= uart_rxd_d1;end
end//给接收标志赋值
always @(posedge clk or negedge rst_n) beginif(!rst_n) rx_flag <= 1'b0;else if(start_en)    //检测到起始位rx_flag <= 1'b1; //接收过程中,标志信号rx_flag拉高//在停止位一半的时候,即接收过程结束,标志信号rx_flag拉低else if((rx_cnt == 4'd9) && (baud_cnt == BAUD_CNT_MAX/2 - 1'b1))rx_flag <= 1'b0;elserx_flag <= rx_flag;
end        //波特率的计数器赋值
always @(posedge clk or negedge rst_n) beginif(!rst_n) baud_cnt <= 16'd0;else if(rx_flag) begin     //处于接收过程时,波特率计数器(baud_cnt)进行循环计数if(baud_cnt < BAUD_CNT_MAX - 1'b1)baud_cnt <= baud_cnt + 16'b1;else baud_cnt <= 16'd0; //计数达到一个波特率周期后清零end    elsebaud_cnt <= 16'd0;     //接收过程结束时计数器清零
end//对接收数据计数器(rx_cnt)进行赋值
always @(posedge clk or negedge rst_n) beginif(!rst_n) rx_cnt <= 4'd0;else if(rx_flag) begin                  //处于接收过程时rx_cnt才进行计数if(baud_cnt == BAUD_CNT_MAX - 1'b1) //当波特率计数器计数到一个波特率周期时rx_cnt <= rx_cnt + 1'b1;        //接收数据计数器加1elserx_cnt <= rx_cnt;endelserx_cnt <= 4'd0;                     //接收过程结束时计数器清零
end        //根据rx_cnt来寄存rxd端口的数据
always @(posedge clk or negedge rst_n) beginif(!rst_n) rx_data_t <= 8'b0;else if(rx_flag) begin                           //系统处于接收过程时if(baud_cnt == BAUD_CNT_MAX/2 - 1'b1) begin  //判断baud_cnt是否计数到数据位的中间case(rx_cnt)4'd1 : rx_data_t[0] <= uart_rxd_d2;   //寄存数据的最低位4'd2 : rx_data_t[1] <= uart_rxd_d2;4'd3 : rx_data_t[2] <= uart_rxd_d2;4'd4 : rx_data_t[3] <= uart_rxd_d2;4'd5 : rx_data_t[4] <= uart_rxd_d2;4'd6 : rx_data_t[5] <= uart_rxd_d2;4'd7 : rx_data_t[6] <= uart_rxd_d2;4'd8 : rx_data_t[7] <= uart_rxd_d2;   //寄存数据的高低位default : ;endcase  endelserx_data_t <= rx_data_t;endelserx_data_t <= 8'b0;
end        //给接收完成信号和接收到的数据赋值
always @(posedge clk or negedge rst_n) beginif(!rst_n) beginuart_rx_done <= 1'b0;uart_rx_data <= 8'b0;end//当接收数据计数器计数到停止位,且baud_cnt计数到停止位的中间时else if(rx_cnt == 4'd9 && baud_cnt == BAUD_CNT_MAX/2 - 1'b1) beginuart_rx_done <= 1'b1     ;  //拉高接收完成信号uart_rx_data <= rx_data_t;  //并对UART接收到的数据进行赋值end    else beginuart_rx_done <= 1'b0;uart_rx_data <= uart_rx_data;end
endendmodule

uart_tx.v

module uart_tx(input               clk         , //系统时钟input               rst_n       , //系统复位,低有效input               uart_tx_en  , //UART的发送使能input     [7:0]     uart_tx_data, //UART要发送的数据output  reg         uart_txd    , //UART发送端口output  reg         uart_tx_busy  //发送忙状态信号);//parameter define
parameter CLK_FREQ = 50000000;               //系统时钟频率
parameter UART_BPS = 115200  ;               //串口波特率
localparam BAUD_CNT_MAX = CLK_FREQ/UART_BPS; //为得到指定波特率,对系统时钟计数BPS_CNT次//reg define
reg  [7:0]  tx_data_t;  //发送数据寄存器
reg  [3:0]  tx_cnt   ;  //发送数据计数器
reg  [15:0] baud_cnt ;  //波特率计数器//*****************************************************
//**                    main code
//*****************************************************//当uart_tx_en为高时,寄存输入的并行数据,并拉高BUSY信号
always @(posedge clk or negedge rst_n) beginif(!rst_n) begintx_data_t <= 8'b0;uart_tx_busy <= 1'b0;end//发送使能时,寄存要发送的数据,并拉高BUSY信号else if(uart_tx_en) begintx_data_t <= uart_tx_data;uart_tx_busy <= 1'b1;end//当计数到停止位结束时,停止发送过程else if(tx_cnt == 4'd9 && baud_cnt == BAUD_CNT_MAX - BAUD_CNT_MAX/4) begintx_data_t <= 8'b0;     //清空发送数据寄存器uart_tx_busy <= 1'b0;  //并拉低BUSY信号endelse begintx_data_t <= tx_data_t;uart_tx_busy <= uart_tx_busy;end
end//波特率的计数器赋值
always @(posedge clk or negedge rst_n) beginif(!rst_n) baud_cnt <= 16'd0;//当处于发送过程时,波特率计数器(baud_cnt)进行循环计数else if(uart_tx_busy) beginif(baud_cnt < BAUD_CNT_MAX - 1'b1)baud_cnt <= baud_cnt + 16'b1;else baud_cnt <= 16'd0; //计数达到一个波特率周期后清零end    elsebaud_cnt <= 16'd0;     //发送过程结束时计数器清零
end//tx_cnt进行赋值
always @(posedge clk or negedge rst_n) beginif(!rst_n) tx_cnt <= 4'd0;else if(uart_tx_busy) begin             //处于发送过程时tx_cnt才进行计数if(baud_cnt == BAUD_CNT_MAX - 1'b1) //当波特率计数器计数到一个波特率周期时tx_cnt <= tx_cnt + 1'b1;        //发送数据计数器加1elsetx_cnt <= tx_cnt;endelsetx_cnt <= 4'd0;                     //发送过程结束时计数器清零
end//根据tx_cnt来给uart发送端口赋值
always @(posedge clk or negedge rst_n) beginif(!rst_n) uart_txd <= 1'b1;else if(uart_tx_busy) begincase(tx_cnt) 4'd0 : uart_txd <= 1'b0        ; //起始位4'd1 : uart_txd <= tx_data_t[0]; //数据位最低位4'd2 : uart_txd <= tx_data_t[1];4'd3 : uart_txd <= tx_data_t[2];4'd4 : uart_txd <= tx_data_t[3];4'd5 : uart_txd <= tx_data_t[4];4'd6 : uart_txd <= tx_data_t[5];4'd7 : uart_txd <= tx_data_t[6];4'd8 : uart_txd <= tx_data_t[7]; //数据位最高位4'd9 : uart_txd <= 1'b1        ; //停止位default : uart_txd <= 1'b1;endcaseendelseuart_txd <= 1'b1;                    //空闲时发送端口为高电平
endendmodule

README.md

在 tx端的 36行 我们把显示出来 时序提前 四分之一 
正点原子做了提前了 16分之一 我在实现的时候 传输的数据一直对不上 
else if(tx_cnt == 4'd9 && baud_cnt == BAUD_CNT_MAX - BAUD_CNT_MAX/4) begin

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【SpringCloudAlibaba】Sentinel熔断限流工具的使用

一、前言 随着微服务的流行&#xff0c;服务和服务之间的稳定性变得越来越重要。Sentinel 是面向分布式、多语言异构化服务架构的流量治理组件&#xff0c;主要以流量为切入点&#xff0c;从流量路由、流量控制、流量整形、熔断降级、系统自适应过载保护、热点流量防护等多个维…

用python怎么输出个人信息,python输入输出学生信息

本篇文章给大家谈谈python输入自己的姓名,输出hello,某某某同学&#xff0c;以及python输入姓名打印学生信息&#xff0c;希望对各位有所帮助&#xff0c;不要忘了收藏本站喔。 校验身份证号码并输出个人信息 类型&#xff1a;流程控制 描述 中国目前采用的是18位身份证号&…

记录今日将C语言的Windows程序更改为python语言Windows程序,实现子窗口控制,类似微信程序框架最简单的原型

基本思路 为什么要选择python制作Windows应用程序&#xff0c;主要就是源代码直接展示&#xff0c;发现问题随时修改&#xff0c;同时可以不断增加新的功能方便。 由于C语言的Windows程序中结构类型在python中不能使用&#xff0c; 因此我们按照ctypes模块指导意见继承structu…

luttuce(RedisTempate)实现hash(动态数据) expire lua脚本

话不多说先放脚本&#xff1a; local argv ARGV local length #argv if length > 0 then local unpackArgs {} for i 1, length - 1 dotable.insert(unpackArgs, argv[i]) end if redis.call(exists, KEYS[1]) 1 thenredis.call(del, KEYS[1])redis.call(hset, KEYS[…

9.鸿蒙app用户界面的跳转abilityslice的跳转

9.用户界面的跳转abilityslice的跳转&#xff0c;值传递&#xff0c;数值累加 首页页面显示1&#xff0c;第2页显示2&#xff0c;再次点击返回首页3。。。 MainAbilitySlice.java 关键代码&#xff1a; 点击事件 text.setClickedListener(new Component.ClickedListener() …

SQL语句整理二--Mysql

文章目录 知识点梳理&#xff1a;1. mysql 中 in 和 exists 区别2. varchar 与 char 的区别 查看表结构&#xff1a;获取当前时间&#xff1a;查看建表语句&#xff1a;修改用户密码&#xff1a;查看所有用户&#xff1a;grant命令&#xff1a;判断当前数据库有多少连接数&…

day34算法训练|贪心算法

1005.K次取反后最大化的数组和 两次贪心算法思路 1. 数组中有负数时&#xff0c;把绝对值最大的负数取反 2. 数组全为非负数时&#xff0c;一直取反最小的那个数 步骤&#xff1a; 第一步&#xff1a;将数组按照绝对值大小从大到小排序&#xff0c;注意要按照绝对值的大小…

未来医疗的新希望:人工智能与智能器官的奇妙融合

导言 人工智能技术的不断演进在医疗领域掀起了一场革命。随着智能器官与人工智能的深度融合&#xff0c;虽然医学领域迎来了前所未有的机遇&#xff0c;但同时也伴随着一系列潜在的问题与挑战。本文将深入探讨人工智能如何与智能器官相互融合&#xff0c;为医学带来新的治疗可能…

GeoTrust SSL证书详细介绍

GeoTrust是著名的证书颁发CA机构DigiCert的品牌。GeoTrustSSL产品在Internet上提供从基本域名验证到扩展验证SSL标准支持的最高级验证的安全性。 GeoTrust OV&#xff08;组织验证&#xff09;证书验证域所有权和组织的存在。在颁发证书之前&#xff0c;会检查该组织在公共数据…

【网络安全】-Linux操作系统基础

文章目录 Linux操作系统目录结构Linux命令格式Linux文件和目录操作命令Linux用户和用户组操作命令Linux查看和操作文件内容命令Linux文件压缩和解压缩命令Linux网络管理命令Linux磁盘管理和系统状态命令Linux安全加固总结 Linux是一个强大的操作系统&#xff0c;广泛用于服务器…

2023年第四届 “赣网杯” 网络安全大赛 gwb-web3 Write UP【PHP 临时函数名特性 + 绕过trim函数】

一、题目如下&#xff1a; 二、代码解读&#xff1a; 这段代码是一个简单的PHP脚本&#xff0c;它接受通过GET请求传递的两个参数&#xff1a;‘pass’和’func’&#xff1a; ① $password trim($_GET[pass] ?? );&#xff1a;从GET请求中获取名为’pass’的参数&#xff0…

一天吃透Redis面试八股文

目录&#xff1a; Redis是什么&#xff1f;Redis优缺点&#xff1f;Redis为什么这么快&#xff1f;讲讲Redis的线程模型&#xff1f;Redis应用场景有哪些&#xff1f;Memcached和Redis的区别&#xff1f;为什么要用 Redis 而不用 map/guava 做缓存?Redis 数据类型有哪些&…

TSX-3225 (MHz范围晶体单元微型低轮廓贴片)

TSX-322系列晶体谐振器是爱普生主推的一款无源晶振型号&#xff0c;频率范围16mhz ~ 48mhz&#xff0c;3.2*2.5mm较小的外部尺寸&#xff0c;可以广泛使用在手机&#xff0c;蓝牙&#xff0c;无线-局域网、ISM 频段电台广播&#xff0c;MPU时钟等产品中。 规范 运动阻力(ESR) 外…

Python 全栈体系【四阶】(七)

第四章 机器学习 六、多项式回归 1. 什么是多项式回归 线性回归适用于数据呈线性分布的回归问题。如果数据样本呈明显非线性分布&#xff0c;线性回归模型就不再适用&#xff08;下图左&#xff09;&#xff0c;而采用多项式回归可能更好&#xff08;下图右&#xff09;。例…

回归预测 | MATLAB实现GA-LSSVM基于遗传算法优化最小二乘向量机的多输入单输出数据回归预测模型 (多指标,多图)

回归预测 | MATLAB实现GA-LSSVM基于遗传算法优化最小二乘向量机的多输入单输出数据回归预测模型 &#xff08;多指标&#xff0c;多图&#xff09; 目录 回归预测 | MATLAB实现GA-LSSVM基于遗传算法优化最小二乘向量机的多输入单输出数据回归预测模型 &#xff08;多指标&#…

Relocations for this machine are not implemented,IDA版本过低导致生成汇编代码失败

目录 1、问题描述 2、安卓app发生崩溃&#xff0c;需要查看汇编代码上下文去辅助分析 3、使用IDA打开.so动态库文件&#xff0c;提示Relocations for this machine are not implemented 4、IDA版本较老&#xff0c;不支持ARM64的指令集&#xff0c;使用7.0版本就可以了 5、…

vue中echarts柱状图点击x轴数据复制

参考自&#xff1a;Vue 3 使用 vue-echarts 的柱状图 barItem 和 x, y 轴点击事件实现_echarts x轴点击事件-CSDN博客 例如柱状图如下&#xff1a; 步骤&#xff1a; 一、数据处理的时候需要在 xAxis 对象中添加&#xff1a;triggerEvent: true 这个键值对&#xff0c;以增加…

vscode如何开发微信小程序?(保姆级教学)

1.安装“微信小程序开发工具”扩展 2.安装“vscode weapp api”扩展 3.安装“vscode wxml”扩展 4.安装“vscode-wechat”扩展 5.在终端执行命令&#xff1a; vue create -p dcloudio/uni-preset-vue uniapp-test uniapp-test就是我这里的项目名称了 6.如果遇到了这个错误&a…

构建平战结合的融合通信指挥调度系统平台

华脉智联PTTLINK融合通信指挥调度系统将语音、视频、GIS进行高度融合&#xff0c;构建“平战结合”的指挥调度模式&#xff0c;既满足平时的日常办公、会议会商、应急培训、应急演练等需求&#xff0c;也能够应对战时的应急指挥、应急救援、应急决策等需求&#xff0c;达到统一…

前端性能监控和错误监控

聚沙成塔每天进步一点点 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 欢迎来到前端入门之旅&#xff01;感兴趣的可以订阅本专栏哦&#xff01;这个专栏是为那些对Web开发感兴趣、刚刚踏入前端领域的朋友们量身打造的。无论你是完全的新手还是有一些基础的开发…