串口UART学习笔记(一)

        买了一个开发板学习FPGA,找到的各种东西就记录在这个博客里了,同时也方便把自己不会的问题找到的结果记录一下,都是自己手打,所以可能说的话不那么严谨,不那么精准,看到的人要带着自己的思考去看,记住尽信书不如无书,哈哈哈。。。。。。

         一、UART是什么?

         UART是一种通用串行数据总线,也就是用于数据传输。是用于主机与辅助设备进行通信。这里的主机理解为计算机,计算机内部采用并行数据,辅助设备采用串行数据。中间需要设备进行数据转换,这也决定了UART工作原理是将传输数据的每个字符一位接一位地传输。UART采用异步传输模式,异步传输将比特分成小组进行传送,小组可以是8位的1个字符或更长。发送方可以在任何时刻发送这些比特组,而接收方从不知道它们会在什么时候到达。例如计算机键盘与主机的通信。UART用于远距离传输较为适合。可以数据同时发送和接收。

                                                1,异步传输是面向字符的传输,而同步传输是面向比特的传输。

                                                2,异步传输的单位是字符同步传输的单位是帧。
                                                3,异步传输通过字符起始和停止码抓住再同步的机会,而同步传输则是在数据中抽取同步信息。
                                                4,异步传输对时序的要求较低,同步传输往往通过特定的时钟线路协调时序。
                                                5,异步传输相对于同步传输效率较低。
我的理解是对时序要求是采用UART的原因,数据到达需要给出到达信号也解释了UART采用8位1字符的串行数据输出模式,也解释了协议中起始位与停止位的重要性。
uart指通用异步收发传输器,本质上是硬件,用来异步传输数据。RS232是一种物理层协议,规定了特定的接口标准。https://www.zhihu.com/question/22632011 
 
进行数据传输时,需要考虑时钟同步问题,传输速率有一个很重要的概念。波特率是衡量资料传送速率的指标。表示每秒钟传送的符号数(symbol)。一个符号代表的信息量(比特数)与符号的阶数有关。例如资料传送速率为120字符/秒,传输使用256阶符号,每个符号代表8bit,则波特率就是120baud,比特率是120*8=960bit/s。位 bit (比特)(Binary Digits):存放一位二进制数,即 0 或 1,最小的存储单位。字节Byte:8个二进制位为一个字节(B),最常用的单位。
常用波特率9600,19200,38400,115200等。
其中各位的意义如下: 
起始位:先发出一个逻辑”0”信号,表示传输字符的开始。 
数据位:可以是5~8位逻辑”0”或”1”。如ASCII码(7位),扩展BCD码(8位)。 
校验位:数据位加上这一位后,使得“1”的位数应为偶数(偶校验)或奇数(奇校验)。 
停止位:它是一个字符数据的结束标志。可以是1位、1.5位、2位的高电平。 
空闲位:处于逻辑“1”状态,表示当前线路上没有资料传送。

      二、UART串口通信一般包括部分
    任何程序都包括一个主控制程序,因为是双向通信,还需要串口发送程序,串口接收程序,时钟控制程序。举例来分别解释四个程序的代码。
      这里采用50MHz的系统时钟,产生UART时钟信号产生和发送的波特率为9600bps。为了保证采样的时候不会发生误码或者滑码,我们对于一位数据不只采用一个时钟周期进行数据采集,而采用16个时钟周期。那么就需要对时钟进行分频处理。50,000,000/(16*9600),分频系数取整为326 。偶数分频比较简单,此处采用了326,至于奇数分频以及如何做出占空比为50%以后整理。
 
 
 1 `timescale 1ns / 1ps
 2 //
 3 // Module Name:    clkdiv
 4 // 产生一个波特率9600的16倍频的时钟,9600*16= 153600
 5 // 相当于50MHz的326分频,50000000/153600=326
 6 //
 7 module clkdiv(clk50, clkout);
 8 input clk50;              //系统时钟
 9 output clkout;          //采样时钟输出
10 reg clkout;
11 reg [15:0] cnt;
12 
13 //分频进程,对50Mhz的时钟326分频
14 always @(posedge clk50)   
15 begin
16   if(cnt == 16'd162)
17   begin
18     clkout <= 1'b1;
19     cnt <= cnt + 16'd1;
20   end
21   else if(cnt == 16'd325)
22   begin
23     clkout <= 1'b0;
24     cnt <= 16'd0;
25   end
26   else
27   begin
28     cnt <= cnt + 16'd1;
29   end
30 end
31 endmodule

分频比较简单,我写的另一个,

 1  `timescale 1ns / 1ps
 2 
 3  module clkdiv(clk50, clkout);
 4  input clk50;              //系统时钟
 5  output clkout;          //采样时钟输出
 6  reg clkout;
 7  reg [15:0] cnt;
 8 
 9 always @(posedge clk50)
10     begin
11       if (cnt < 16'd162)
12       cnt <= cnt + 16'b1;
13      else  if  (cnt == 16'd162)
14       begin
15           clkout <= ~clkout;
16           cnt <= 16'b0;
17       end
18      end

三、串口发送程序

     UART采用异步传输,就涉及起始位与停止位,下面是代码例子,我的总结用红笔标出。

  1 `timescale 1ns / 1ps
  2 //
  3 // Module Name:    uarttx 
  4 // 说明:16个clock发送一个bit,
  5 //
  6 module uarttx(clk, datain, wrsig, idle, tx);
  7 input clk;                //UART时钟
  8 input [7:0] datain;       //需要发送的数据
  9 input wrsig;              //发送命令,上升沿有效
 10 output idle;              //线路状态指示,高为线路忙,低为线路空闲
 11 output tx;                //发送数据信号
 12 reg idle, tx;
 13 reg send;
 14 reg wrsigbuf, wrsigrise;
 15 reg presult;
 16 reg[7:0] cnt;             //计数器
 17 parameter paritymode = 1'b0;
 18 
 19 //检测发送命令是否有效,判断wrsig的上升沿            //先检测发送命令是否有效,然后判断线路状态
 20 always @(posedge clk)
 21 begin
 22    wrsigbuf <= wrsig;
 23    wrsigrise <= (~wrsigbuf) & wrsig;         //边沿检测,检测发送命令
 24 end
 25 
 26 always @(posedge clk)
 27 begin
 28   if (wrsigrise &&  (~idle))  //当发送命令有效且线路为空闲时,启动新的数据发送进程
 29   begin
 30      send <= 1'b1;
 31   end
 32   else if(cnt == 8'd168)      //一帧资料发送结束
 33   begin
 34      send <= 1'b0;
 35   end
 36 end
 37 
 38 /
 39 //使用168个时钟发送一个数据(起始位、8位数据、奇偶校验位、停止位),每位占用16个时钟//    //停止位为8个时钟周期
 40 
 41 always @(posedge clk)
 42 begin
 43   if(send == 1'b1)  begin
 44     case(cnt)                 //tx变低电平产生起始位,0~15个时钟为发送起始位
 45     8'd0: begin
 46          tx <= 1'b0;
 47          idle <= 1'b1;
 48          cnt <= cnt + 8'd1;
 49     end
 50     8'd16: begin
 51          tx <= datain[0];    //发送数据位的低位bit0,占用第16~31个时钟
 52          presult <= datain[0]^paritymode;     //奇偶校验位的获取
 53          idle <= 1'b1;
 54          cnt <= cnt + 8'd1;
 55     end
 56     8'd32: begin
 57          tx <= datain[1];    //发送数据位的第2位bit1,占用第47~32个时钟
 58          presult <= datain[1]^presult;
 59          idle <= 1'b1;
 60          cnt <= cnt + 8'd1;
 61     end
 62     8'd48: begin
 63          tx <= datain[2];    //发送数据位的第3位bit2,占用第63~48个时钟
 64          presult <= datain[2]^presult;
 65          idle <= 1'b1;
 66          cnt <= cnt + 8'd1;
 67     end
 68     8'd64: begin
 69          tx <= datain[3];    //发送数据位的第4位bit3,占用第79~64个时钟
 70          presult <= datain[3]^presult;
 71          idle <= 1'b1;
 72          cnt <= cnt + 8'd1;
 73     end
 74     8'd80: begin 
 75          tx <= datain[4];   //发送数据位的第5位bit4,占用第95~80个时钟
 76          presult <= datain[4]^presult;
 77          idle <= 1'b1;
 78          cnt <= cnt + 8'd1;
 79     end
 80     8'd96: begin
 81          tx <= datain[5];    //发送数据位的第6位bit5,占用第111~96个时钟
 82          presult <= datain[5]^presult;
 83          idle <= 1'b1;
 84          cnt <= cnt + 8'd1;
 85     end
 86     8'd112: begin
 87          tx <= datain[6];    //发送数据位的第7位bit6,占用第127~112个时钟
 88          presult <= datain[6]^presult;
 89          idle <= 1'b1;
 90          cnt <= cnt + 8'd1;
 91     end
 92     8'd128: begin 
 93          tx <= datain[7];    //发送数据位的第8位bit7,占用第143~128个时钟
 94          presult <= datain[7]^presult;
 95          idle <= 1'b1;
 96          cnt <= cnt + 8'd1;
 97     end
 98     8'd144: begin
 99          tx <= presult;      //发送奇偶校验位,占用第159~144个时钟      //将计算结果作为奇偶校验码输出
100          presult <= datain[0]^paritymode;     //无意思,此行计算结果无用,多余
101          idle <= 1'b1;
102          cnt <= cnt + 8'd1;
103     end
104     8'd160: begin
105          tx <= 1'b1;         //发送停止位,占用第160~167个时钟            
106          idle <= 1'b1;
107          cnt <= cnt + 8'd1;
108     end
109     8'd168: begin
110          tx <= 1'b1;             
111          idle <= 1'b0;       //一帧资料发送结束
112          cnt <= cnt + 8'd1;
113     end
114     default: begin
115           cnt <= cnt + 8'd1;
116     end
117    endcase
118   end
119   else  begin
120     tx <= 1'b1;
121     cnt <= 8'd0;
122     idle <= 1'b0;
123   end
124 end
125 endmodule

 


 

四、串口接收程序

      成为UART接收信号,将一位一位的串口数据转化为并行数据。

  1 `timescale 1ns / 1ps
  2 //
  3 // Module name    uartrx.v
  4 // 说明:          16个clock接收一个bit,16个时钟采样,取中间的采样值
  5 //
  6 module uartrx(clk, rx, dataout, rdsig, dataerror, frameerror);
  7 input clk;             //采样时钟
  8 input rx;              //UART数据输入
  9 output dataout;        //接收数据输出
 10 output rdsig;          //接收数据有效,高说明接收到一个字节 ,来区分数据处于接收状态或者接收控制信号
 11 output dataerror;      //数据出错指示
 12 output frameerror;     //帧出错指示
 13 
 14 reg[7:0] dataout;
 15 reg rdsig, dataerror;
 16 reg frameerror;
 17 reg [7:0] cnt;
 18 reg rxbuf, rxfall, receive;
 19 parameter paritymode = 1'b0;
 20 reg presult, idle;
 21 
 22 always @(posedge clk)   //检测线路rx的下降沿, 线路空闲的时候rx为高电平
 23 begin
 24   rxbuf <= rx;
 25   rxfall <= rxbuf & (~rx);      //下降沿检测,检测是否接收到接收信号
 26 end
 27 
 28 always @(posedge clk)
 29 begin
 30   if (rxfall && (~idle))  //检测到线路的下降沿并且原先线路为空闲,启动接收数据进程
 31   begin
 32     receive <= 1'b1;      //开始接收数据
 33   end
 34   else if(cnt == 8'd168)  //接收数据完成
 35   begin
 36    receive <= 1'b0;
 37   end
 38 end
 39 
 40 /
 41 //使用176个时钟接收一个数据(起始位、8位数据、奇偶校验位、停止位),每位占用16个时钟//
 42 
 43 always @(posedge clk)
 44 begin
 45   if(receive == 1'b1)
 46   begin
 47    case (cnt)
 48    8'd0:                   //0~15个时钟为接收第一个比特,起始位
 49      begin
 50       idle <= 1'b1;
 51       cnt <= cnt + 8'd1;
 52       rdsig <= 1'b0;
 53      end
 54    8'd24:                  //16~31个时钟为第1个bit数据,取中间第24个时钟的采样值          //在发送程序中,第0位数据在16个时钟周期后开始传输,接收过程中,
 55     begin                           //从第24个时钟周期开始接收第0位数据,保证信号被采集。
 56       idle <= 1'b1;
 57       dataout[0] <= rx;
 58       presult <= paritymode^rx;
 59       cnt <= cnt + 8'd1;
 60       rdsig <= 1'b0;
 61      end
 62    8'd40:                 //47~32个时钟为第2个bit数据,取中间第40个时钟的采样值 
 63     begin
 64       idle <= 1'b1;
 65       dataout[1] <= rx;
 66       presult <= presult^rx;
 67       cnt <= cnt + 8'd1;
 68       rdsig <= 1'b0;
 69      end
 70    8'd56:                 //63~48个时钟为第3个bit数据,取中间第56个时钟的采样值   
 71     begin
 72       idle <= 1'b1;
 73       dataout[2] <= rx;
 74       presult <= presult^rx;
 75       cnt <= cnt + 8'd1;
 76       rdsig <= 1'b0;
 77      end
 78    8'd72:                //79~64个时钟为第4个bit数据,取中间第72个时钟的采样值   
 79     begin
 80       idle <= 1'b1;
 81       dataout[3] <= rx;
 82       presult <= presult^rx;
 83       cnt <= cnt + 8'd1;
 84       rdsig <= 1'b0;
 85      end
 86    8'd88:               //95~80个时钟为第5个bit数据,取中间第88个时钟的采样值    
 87     begin
 88       idle <= 1'b1;
 89       dataout[4] <= rx;
 90       presult <= presult^rx;
 91       cnt <= cnt + 8'd1;
 92       rdsig <= 1'b0;
 93      end
 94    8'd104:             //111~96个时钟为第6个bit数据,取中间第104个时钟的采样值    
 95     begin
 96       idle <= 1'b1;
 97       dataout[5] <= rx;
 98       presult <= presult^rx;
 99       cnt <= cnt + 8'd1;
100       rdsig <= 1'b0;
101      end
102    8'd120:             //127~112个时钟为第7个bit数据,取中间第120个时钟的采样值     
103     begin
104       idle <= 1'b1;
105       dataout[6] <= rx;
106       presult <= presult^rx;
107       cnt <= cnt + 8'd1;
108       rdsig <= 1'b0;
109      end
110    8'd136:            //143~128个时钟为第8个bit数据,取中间第136个时钟的采样值   
111     begin
112       idle <= 1'b1;
113       dataout[7] <= rx;
114       presult <= presult^rx;
115       cnt <= cnt + 8'd1;
116       rdsig <= 1'b1;      //接收数据有效
117      end
118    8'd152:            //159~144个时钟为接收奇偶校验位,取中间第152个时钟的采样值     
119     begin
120       idle <= 1'b1;
121       if(presult == rx)
122         dataerror <= 1'b0;
123       else
124         dataerror <= 1'b1;       //如果奇偶校验位不对,表示数据出错
125       cnt <= cnt + 8'd1;
126       rdsig <= 1'b1;             
127       end
128    8'd168:            //160~175个时钟为接收停止位,取中间第168个时钟的采样值  
129      begin
130      idle <= 1'b1;
131      if(1'b1 == rx)
132        frameerror <= 1'b0;
133      else
134        frameerror <= 1'b1;      //如果没有接收到停止位,表示帧出错
135      cnt <= cnt + 8'd1;
136      rdsig <= 1'b1;
137      end
138    default:
139      begin
140       cnt <= cnt + 8'd1;
141      end
142    endcase
143   end
144   else
145   begin
146     cnt <= 8'd0;
147     idle <= 1'b0;
148     rdsig <= 1'b0;
149   end
150  end
151 endmodule

五、控制程序

     这里主要学习程序的调用,如何用总的控制程序完成UART数据传输。

 1 `timescale 1ns / 1ps
 2 //
 3 // Module Name:    uart_test 
 4 // 
 5 //
 6 module uart_test(clk50, rx, tx, reset);
 7 input clk50;
 8 input reset;
 9 input rx;
10 output tx;
11 
12 wire clk;       //clock for 9600 uart port
13 wire [7:0] txdata,rxdata;     //串口发送数据和串口接收数据
14 
15 
16 
17 //产生时钟的频率为16*9600
18 clkdiv u0 (
19         .clk50                   (clk50),               //50Mhz的晶振输入                     
20         .clkout                  (clk)                  //16倍波特率的时钟                        
21  );
22 
23 //串口接收程序
24 uartrx u1 (
25         .clk                     (clk),                 //16倍波特率的时钟 
26       .rx                       (rx),                     //串口接收
27         .dataout                 (rxdata),              //uart 接收到的数据,一个字节                     
28       .rdsig                   (rdsig),               //uart 接收到数据有效 
29         .dataerror               (),
30         .frameerror              ()
31 );
32 
33 //串口发送程序
34 uarttx u2 (
35         .clk                     (clk),                  //16倍波特率的时钟  
36        .tx                      (tx),                      //串口发送
37         .datain                  (txdata),               //uart 发送的数据   
38       .wrsig                   (wrsig),                //uart 发送的数据有效  
39       .idle                    ()     
40     
41  );

 
54 endmodule

就像c语言里调用子函数一样,每个 模块是并行运行的, 各个模块连接完成整个系统需要一个顶层文件(top-module) 。 顶层文件 通过调用、连接低层模块的实例来实现复杂的功能。

 

 

学习UART通信,最主要还是理解异步和串口这两个东西,方便远距离传输,串口一位一位传输,牺牲了时间降低时序要求。

 
 
 
 
 
 
 
 
 
 
  • 4
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
你好!感谢提问。下面是有关 K210 学习笔记五中串口通信的一些内容: 在 K210 开发板上,串口是一种常见的通信接口,用于将数据从一个设备传输到另一个设备。串口通信通常使用 UART (Universal Asynchronous Receiver/Transmitter) 协议。 要在 K210 上进行串口通信,首先需要使用两个引脚:一个用于接收数据(RX),一个用于发送数据(TX)。这些引脚可以通过 GPIO 端口与外部设备连接。 在 K210 的开发环境中,可以使用 MaixPy 或者 Kendryte Standalone SDK 进行串口编程。 在 MaixPy 中,可以使用 `uart` 模块来进行串口通信。例如,要初始化一个串口对象并设置波特率为 115200,可以使用以下代码: ```python from machine import UART uart = UART(UART.UART1, 115200) ``` 然后,可以使用 `uart.write()` 函数发送数据,使用 `uart.read()` 函数接收数据。例如: ```python uart.write("Hello, world!\n") data = uart.read(10) ``` 在 Kendryte Standalone SDK 中,可以使用 `uart.h` 头文件中的函数来进行串口通信。例如,要初始化一个串口对象并设置波特率为 115200,可以使用以下代码: ```c #include "uart.h" uart_init(UART_DEVICE_1, 115200); ``` 然后,可以使用 `uart_send_data()` 函数发送数据,使用 `uart_receive_data()` 函数接收数据。例如: ```c uart_send_data(UART_DEVICE_1, "Hello, world!\n", 14); char buffer[10]; uart_receive_data(UART_DEVICE_1, buffer, 10); ``` 以上是关于 K210 学习笔记五中串口通信的简要介绍。如果你有更具体的问题,请随时提问!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Paul安

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值