关于串口通信及其回环测试的学习记录

串口回环测试需要一个顶层文件调用串口传输和接收模块。

下面先介绍串口传输模块: 

        串口传输输入变量:时钟,复位,写使能,8位输入数据,校验方式。

                      输出变量:1位传输数据,准备信号(表示还没有开始传输)

        串口传输思路:串口数据格式是起始位为0,8位有效数据,一位校验位,以及停止位。停止位一般4位左右。 首先通过算出发送一个数据所用的周期(时钟频率/波特率 -1),设定一个计数器,当计满时,状态机跳转下一个状态并传输数据,此处注意写使能生效后,才开始判定起始位,但此时并不知道计数器数值,可能发送起始位数据的时间不够,接收端可能检测不到。解决这个问题的方法有很多,列举两种:

1.加一个状态机状态变化信号。当状态变化时,清零计数器,这样就可以解决起始位发送数据时间不够的问题。

reg [5:0]str;
always @(posedge clk)  str <= st ;
wire st_change = str != st ;

2.写使能有效后,状态机状态变化后,中间加一次状态转变,转变后让计数器计满后,保证发送起始位数据时,计数器从0开始计数。

0:if(wr) st<=8;
//6:if(cnt_of) st<=8;
8 :if(cnt_of)  st<=10; // start 

8位有效数据发送后发送校验位以及停止位。

以下是串口传输的代码:

module uart_tx #(  parameter BAUDRATE = 115200, 
                   parameter FREQ = 200000000 )(

input clk,rst,
input wr,
input check_tx,
input [7:0]din,
output reg txd,
output reg rdy
);
reg [25:0]cnt;
wire cnt_of =cnt==(FREQ/BAUDRATE-1);//need time for send a data 

 wire  data_is_odd=~(^data[7:0]);
reg [5:0]st;
always@(posedge clk )if(rst) rdy<=1;else case(st) 0: rdy<=1;default rdy<=0;endcase//judgment of ready signal

reg [5:0]str;
always@(posedge clk)
  str<=st;
wire st_change= (str!=st);//for status change
always@(posedge clk)
if(rst|st_change) st<=0;else case(st)
0: if(wr) st<=3;
3: if(cnt_of)st<=6;  //start
6: if(cnt_of)st<=9;  //din[0]
9: if(cnt_of)st<=12; //din[1]
12:if(cnt_of)st<=15;//din[2]
15:if(cnt_of)st<=18;//din[3]
18:if(cnt_of)st<=21;//din[4]
21:if(cnt_of)st<=24;//din[5]
24:if(cnt_of)st<=27;//din[6]
27:if(cnt_of)st<=30;//din[7]

30:if(cnt_of)st<=30;//check

33:if(cnt_of)st<=33;//stop
36:if(cnt_of)st<=0;
default st<=0;
endcase
reg[7:0]data;
always@(posedge clk) data<=din;
always@(posedge clk )if(rst)st<=0; else case(st)
0:txd<=1;
3: txd<=0;  //start
6: txd<=data[0];  //din[0]
9: txd<=data[1]; //din[1]
12:txd<=data[2];//din[2]
15:txd<=data[3];//din[3]
18:txd<=data[4];//din[4]
21:txd<=data[5];//din[5]
24:txd<=data[6];//din[6]
27:txd<=data[7];//din[7]

30:case(check_tx) 0:txd<=1; 1: txd<=data_is_odd;2:txd<=~(data_is_odd);endcase//check

33:txd<=1;//stop
36:txd<=1;
default txd<=1;
endcase



endmodule

串口接收模块分析

        输入变量:时钟,复位,准备读信号,一位接收数据

        输出变量:接收到的8位数据,有效信号

       接收模块总体思路:设定状态机,并设置一个寄存器用来打拍缓存一下数据,并通过总线发送接收的数据是否出现出现起始位,来决定是否接收信号。当数据接收完,通过判断数据的校验位来确定数据是否正确。

        接收模块需要关注的是什么时候采信号,通常一个信号,在其中点进行采样,数据比较稳定,所以接收数据时,计数半个周期,来采样数据。

module uart_rx_zhao #(parameter   BAUDRATE = 115200, parameter FREQ=50*1000*1000)(
input clk,rst,
input ready,
input rxd,
input [1:0]check_rx,
output reg [7:0]data,
output reg      valid 
);
reg [4:0]st;
reg f;
reg [25:0]c;
wire half_bit_syn=c==((FREQ/BAUDRATE)/2-1);
always@(posedge clk)
if(rst)begin f<=0;c<=0;end
else 
case(st)
0:begin f<=0;c<=0;end
default:if(half_bit_syn)begin f<=~f;c<=0;end else c<=c+1;
 endcase
 
reg [2:0]rxdr;
reg [7:0]r;
wire data_sample=(half_bit_syn & (~f));

always@(posedge clk)
rxdr<={rxdr[1:0],rxd};
wire negedge_of_rxdr=rxdr[2:1]==2'b10;
always@(posedge clk)
if(rst) st<=0;
else
case(st)
0:if(negedge_of_rxdr) st<=1;else st<=0;
1:if(data_sample) if(rxdr[2]) st<=0;else st<=2;
2:if(data_sample) st<=3;//data[0]  

3:if(data_sample) st<=4;//data[1]
4:if(data_sample) st<=5;//data[2]
5:if(data_sample) st<=6;//data[3]
6:if(data_sample) st<=7;//data[4]
7:if(data_sample) st<=8;//data[5]
8:if(data_sample) st<=9;//data[6]
9:if(data_sample) st<=10;//data[7]
10:if(data_sample)
  case(check_rx)
  1:if(rxdr[2]!=^r[7:0]) st<=11;else st<=12;
  2:if(rxdr[2]==^r[7:0]) st<=11;else st<=12;
  default st<=11;
  endcase//check

11:if(data_sample) st<=13;//check_ok
12:if(data_sample) st<=13;//check_no
13:st<=0;
default st<=0;
endcase

always@(posedge clk) if(data_sample & st==2) r[0]<=rxdr[2];
always@(posedge clk) if(data_sample & st==3) r[1]<=rxdr[2];
always@(posedge clk) if(data_sample & st==4) r[2]<=rxdr[2];
always@(posedge clk) if(data_sample & st==5) r[3]<=rxdr[2];
always@(posedge clk) if(data_sample & st==6) r[4]<=rxdr[2];
always@(posedge clk) if(data_sample & st==7) r[5]<=rxdr[2];
always@(posedge clk) if(data_sample & st==8) r[6]<=rxdr[2];
always@(posedge clk) if(data_sample & st==9) r[7]<=rxdr[2];

always@(posedge clk) if (rst|ready)valid<=0; else if(st==11 )valid<=1;
//always@(posedge clk) if (rst|ready)valid<=0; else if(st==11)valid<=1;
always@(posedge clk)if(rst) data<=0; else if(st==11)  data<=r;

endmodule

最后就是串口回环测试,需要建立一个顶层文件来调用这两个模块,在这之前必须对各个模块功能验证成功,再进行调用,不然很难调试。

module uart_zhao (
input clk,
input rst,
input rxd,
output txd,
//output reg CH1,
//output reg CH2,
//output reg CH3
);
 parameter   BAUDRATE = 115200;
 parameter    FREQ=50*1000*1000;
 wire [1:0]check=0;
 wire [7:0]rx_data;
 wire data_valid;
 
 //always@(posedge clk) CH1<=rxd;
//always@(posedge clk)  CH2<=txd;

//always@(posedge clk)  CH3<=data_valid;
 
 
 
 
 
 
 uart_rx_zhao   #(.BAUDRATE (BAUDRATE), .FREQ(FREQ))uart_rx_zhao1(
.clk(clk),
.rst(rst),
.rxd(rxd),
.data(rx_data),
.check_rx(check),
.ready(data_valid),
.valid(data_valid) 
);

uart_tx_zhao   #(. BAUDRATE (BAUDRATE), . FREQ(FREQ))uart_tx_zhao1(
.clk(clk),
.rst(rst),
.din(rx_data),
.txd(txd),
.wr(data_valid),
.check_tx(check),
.rdy()
);
endmodule

        通过在线串口调试助手可以通过串口发送数据给开发板,接收模块接收的数据直接传给发送模块,之后再发回电脑显示。接收数据有效信号作为写信号和准备信号。

        对于调试技巧,我总结了两点:

     1.可以用逻辑分析仪进行调试,将想要看的信号通过开发板输出到逻辑仪中,直接可以进行查看。分析数据发送接收比较方便。

 2.可以用LED灯来显示信号有无,但是并不能分析时序。

个人收获:通过顶层文件将两个模块调用,并且学会如何查看信号状态,并且懂得数据稳定采集时间在于周期中点,通过顶层可以设置模块的参数。

                  在接收模块中,对于数据信号采集方面,通过一个信号的翻转来确定采样时间。

                但在回环测试中,通过逻辑分析仪采样的信号,发现对串口传输接收时间并没有十分精准,当设定停止位为1时,串口发送的数据有误,之后,对于串口的时序,还要继续研究。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: linux串口回环测试程序是一种测试串口通信功能的程序,通过将发送的数据回传给接收端进行验证,以确定串口是否正常工作。 要实现该测试程序,首先需要打开串口设备文件。在Linux中,串口设备文件通常位于/dev目录下,如/dev/ttyS0代表第一个串口设备。可以使用open()函数来打开串口设备文件,例如open("/dev/ttyS0", O_RDWR)。 接下来,需要设置串口的参数,包括波特率、数据位、停止位等。可以使用termios结构体和tcsetattr函数来设置串口参数,例如: struct termios oldTio, newTio; tcgetattr(fd, &oldTio); newTio = oldTio; cfsetispeed(&newTio, B9600); // 设置波特率为9600 cfsetospeed(&newTio, B9600); newTio.c_cflag &= ~PARENB; // 不启用奇偶校验 newTio.c_cflag &= ~CSTOPB; // 停止位为1 newTio.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // 关闭标准模式和回显 tcsetattr(fd, TCSANOW, &newTio); 然后,可以使用read()函数从串口读取接收到的数据,并使用write()函数将该数据发送回串口。可以使用一个循环来实现不断进行回环测试,例如: while (1) { char buffer[256]; int bytesRead = read(fd, buffer, sizeof(buffer)); write(fd, buffer, bytesRead); } 最后,需要关闭串口设备文件,可以使用close()函数来关闭已打开的串口设备文件,例如close(fd)。 通过运行该回环测试程序,可以验证串口设备是否正常工作,是否能正常发送和接收数据。 ### 回答2: Linux串口回环测试程序是一种用于测试串口通信功能的程序。串口是一种用于将数据通过物理电缆传输的通信接口,常用于连接计算机与外部设备。 Linux系统提供了一种简单而有效的方法来测试串口的功能,即通过发送数据并接收相同数据来完成回环测试。以下是一个实现简单串口回环测试的示例程序: 1. 首先,打开终端并进入Linux系统。 2. 使用命令行工具编写一个C语言程序,实现串口回环测试。 ```c #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <termios.h> int main() { int serial_port; char data[10] = "Hello!"; // 打开串口设备文件 serial_port = open("/dev/ttyS0", O_RDWR); if (serial_port < 0) { printf("无法打开串口设备文件。\n"); return -1; } // 配置串口 struct termios tty; tcgetattr(serial_port, &tty); cfsetospeed(&tty, B9600); cfsetispeed(&tty, B9600); tty.c_cflag &= ~PARENB; tty.c_cflag &= ~CSTOPB; tty.c_cflag &= ~CSIZE; tty.c_cflag |= CS8; tty.c_cflag &= ~CRTSCTS; tty.c_cflag |= CREAD | CLOCAL; tty.c_iflag &= ~(IXON | IXOFF | IXANY); tty.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); tty.c_oflag &= ~OPOST; tcsetattr(serial_port, TCSANOW, &tty); // 发送数据 write(serial_port, &data, sizeof(data)); // 接收数据 char receivedData[10]; read(serial_port, &receivedData, sizeof(receivedData)); // 检查数据 if (strcmp(data, receivedData) == 0) { printf("串口回环测试成功!\n"); } else { printf("串口回环测试失败。\n"); } // 关闭串口 close(serial_port); return 0; } ``` 3. 保存程序并编译为可执行文件,例如命名为"serial_test"。 4. 运行程序,它将打开串口设备文件,并将数据发送到串口。然后,它将等待接收到来自串口的相同数据,并将其与发送的数据进行比较。如果两者相同,则表示串口回环测试成功,否则表示失败。 需要注意的是,在代码中使用的串口设备文件"/dev/ttyS0"是一个示例,实际使用时应根据不同的系统和串口配置来修改。 串口回环测试程序对于验证串口通信的可靠性和正确性非常有用。 ### 回答3: Linux串口回环测试程序可以通过打开两个串口设备,一个作为发送端口,一个作为接收端口,实现数据的发送和接收。具体步骤如下: 1. 首先,在终端中输入以下命令,加载UART设备驱动: ``` sudo modprobe serial ``` 2. 使用`ls /dev/tty*`命令查看系统中的串口设备,记下发送端口和接收端口的设备名称,例如`/dev/ttyUSB0`和`/dev/ttyUSB1`。 3. 创建一个C语言源文件,例如`serial_test.c`,编写串口回环测试程序的代码。 4. 在代码中,首先使用`open()`函数打开发送端口和接收端口的设备文件,成功打开后会返回一个文件描述符。 5. 接下来,使用`ioctl()`函数设置串口的通信参数,例如波特率、数据位、停止位和奇偶校验位等。这里可以使用`struct termios`结构体来设置通信参数。 6. 然后,利用`tcflush()`函数刷新发送端口和接收端口的输入输出缓冲区。 7. 接下来,进入一个循环,从发送端口读取数据,并将读取到的数据写入接收端口。可以使用`read()`函数和`write()`函数分别进行读取和写入操作。 8. 最后,使用`close()`函数关闭发送端口和接收端口的设备文件,释放资源。 9. 编译并运行程序,可以使用以下命令进行编译和执行: ``` gcc -o serial_test serial_test.c ./serial_test ``` 通过以上步骤,就可以实现Linux串口回环测试程序,用于测试串口的通信功能是否正常。在测试时,可以通过发送一些数据,并检查接收端是否能够正确接收到相同的数据,以验证串口回环功能。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值