【项目简介】
本项目使用了HC - HR04超声波模块,实现的障碍目标距离检测,且通过zynq实现uart串口实时显示测试结果,后续还可以添加距离报警模块与串口设置报警距离等其他功能。
【系统框图】
图1项目整体框架
如图1,mea_dis模块测距,结果显示在led灯上,serial_test模块将测距信息连接data,并依uart通信协议传递给zynq_uart_wrapper 的UART1_rxd接收端,最后将结果实时显示在上位机。
【过程结果】
图2 zynq_core ip核配置
如图2,Zynq核配置:FCLK1输出50mhz频率的脉冲信号,作为串口发送模块serial_test_tb的输入时钟,FCLK_RESETON作为串口模块serial_test_tb的复位信号。
图3 PL端 端口引脚配置
如图3,PL端的引脚配置:dis接八个灯,展示距离,系统时钟sysclk是H16引脚提供125mzh频率的脉冲信号,复位按键为ST0,超声波驱动信号trig,与超声波返回计算值echo接Y12,Y11两个I/O口。
图4 超声波测距模块测试
如图4,通过设置echo值的变化,模拟超声波发送与受到障碍物的情况,测得在一定情况下的距离值,并与理论计算值比较,发现一致。
图5 UART发送模块测试
如图5,该部分测试向量,依靠顶层文件serial_test,每个信号触发沿改变data值,对比 uart发送数据,观察发现一致。
图6整体效果展示
如图6,可以看到测量结果是正确的,并且串口软件也有结果显示,且一致。
TOP
module TOP(dis,sysclk,rst_n,echo,trig);
output wire [7:0 ] dis;
input sysclk;
input rst_n;
input echo;
output trig;
wire clk50, rstn; //zynq核输出50M时钟,低电平有效复位
mea_dis( .sysclk(sysclk),.rst_n(rst_n),.echo(echo),.trig (trig),.dis (dis));
serial_test(.clk(clk50),.reset_n(rstn),.uart_tx(rxd),.data(dis));
zynq_uart_wrapper zynqCore(.FCLK0(clk100),.FCLK1(clk50),.FCLK_RESETON(rstn),.UART1_rxd(rxd), .UART1_txd(txd) );
endmodule
mea_dis超声波测距模块top
module mea_dis(
input sysclk ,
input rst_n ,
input echo ,
output trig ,
output [7:0] dis
);
Trig trg(
.sysclk (sysclk ) ,
.rst_n (rst_n ) ,
.trig (trig )
);
ECHO echo_u(
.sysclk (sysclk ) ,
.rst_n (rst_n ) ,
.echo (echo ) ,
.dis (dis )
);
Endmodule
HC—HR04驱动模块
module Trig(
input sysclk ,
input rst_n ,
output trig
);
parameter DELAY = ( 70 * 125000 ) + 15 * 125;//发送时钟周期(70ms+15us)
reg [20:0] cnt;
//---------------count----------------//
always@(posedge sysclk)
if(!rst_n)
cnt <= 0;
else if(cnt == DELAY - 1)
cnt <= 0;
else
cnt <= cnt + 1;
//---------------initial trig--------------//
assign trig = (( cnt > 0) && (cnt < 15 * 125 )) ? 1 : 0;
endmodule
计算距离模块
module ECHO(
input sysclk ,
input rst_n ,
input echo ,
output [7:0] dis //distance
);
//————————state——————//
parameter IDLE = 3'b001,
s1 = 3'b010,
s2 = 3'b100;
reg [2:0] cur_state;
reg [2:0] next_state;
reg [31:0] cnt;
reg [31:0] cnt_reg;
//————————first————————//
always@(posedge sysclk)
if(!rst_n)
cur_state <= IDLE;
else
cur_state <= next_state;
//————————second————————//
always@(*)
case(cur_state)
IDLE :begin//空闲状态
if(echo == 1)
next_state = s1;
else
next_state = IDLE;
end
s1 :begin//
if(echo == 0)
next_state = s2;
else
next_state = s1;
end
s2 :begin
next_state = IDLE;
end
default:next_state = IDLE;
endcase
//——————————third——————————//
always@(posedge sysclk)
if(!rst_n)
begin
cnt <= 0;
cnt_reg <= 0;
end
else
case(cur_state)
IDLE :begin
cnt <= 0;
cnt_reg <= cnt_reg;
end
s1 :begin
cnt <= cnt + 1;
cnt_reg <= cnt_reg;
end
s2 :begin
cnt_reg <= cnt;
cnt <= 0;
end
default:begin
cnt <= 0;
cnt_reg <= 0;
end
endcase
//————————distance——————————//
assign dis = (cnt_reg * 8)/58/1000;
endmodule
串口接收uart模块
module serial_test(clk,reset_n,uart_tx,data);
input clk;
input [7:0] data;
input reset_n;
output uart_tx;
reg send_go;
wire [7:0]data;
wire tx_done;
serial s1(
.clk(clk),
.reset_n(reset_n),
.data(data),
.send_go(send_go),
.uart_tx(uart_tx),
.tx_done(tx_done),
.baud_set(4)
);
//设置计数器每满10ms send_en拉高
reg[18:0]cnt;
always@(posedge clk,negedge reset_n)begin
if(!reset_n)
cnt<=0;
else if(cnt==499999)
cnt<=0;
else
cnt=cnt+1;
end
always@(posedge clk,negedge reset_n)begin
if(!reset_n)
send_go<=0;
else if(cnt==1)
send_go<=1;
else
send_go<=0;
end
endmodule
协议模块
module serial(
clk,
reset_n,
data, send_go,
//串行信号带
uart_tx,
//发送结束标志
tx_done,
//可设置多种波特率
baud_set
);
input clk,reset_n;
input[7:0] data;
reg send_en;
//典型波特率有300,1200,2400,9600,19200,115200
//设置5种模式 0:9600,1:19200,2:38400,3:57600,4:115200
input[2:0] baud_set;
input send_go;
output reg uart_tx;
output reg tx_done;
wire bps_clk;
assign bps_clk=(div_cnt==bps_dr-1);
//计数器 之五种模式设置的值
reg[17:0] bps_dr;
always@(*)begin
case(baud_set)
0:bps_dr=1000000000/9600/20;
1:bps_dr=1000000000/19200/20;
2:bps_dr=1000000000/38400/20;
3:bps_dr=1000000000/57600/20;
4:bps_dr=1000000000/115200/20;
default:bps_dr=1000000000/9600/20;
endcase
end
always@(posedge clk,negedge reset_n)begin
if(!reset_n)
send_en<=0;
else if(send_go)
send_en<=1;
else if(tx_done)
send_en<=0;
end
reg [7:0] datacache;
always@(posedge clk)begin
if(send_go)
datacache<=data;
else
datacache<=datacache;
end
//计数器
reg[17:0] div_cnt;
always@(posedge clk,negedge reset_n)begin
if(!reset_n)
div_cnt<=0;
else if(send_en)begin
if(bps_clk)
div_cnt<=0;
else
div_cnt<=div_cnt+1;
end
else
div_cnt<=0;
end
reg[3:0] bps_cnt;
always@(posedge clk,negedge reset_n)begin
if(!reset_n)
bps_cnt<=0;
else if(send_en)begin
if(div_cnt==1)begin
if(bps_cnt==12)
bps_cnt<=0;
else
bps_cnt<=bps_cnt+1'b1;
end
end
else
bps_cnt<=0;
end
//发送数据
always@(posedge clk,negedge reset_n)begin
if(!reset_n)
begin
uart_tx<=1;
tx_done<=0;
end
else begin
case(bps_cnt)
1:uart_tx<=0;
2:uart_tx<=datacache[0];
3:uart_tx<=datacache[1];
4:uart_tx<=datacache[2];
5:uart_tx<=datacache[3];
6:uart_tx<=datacache[4];
7:uart_tx<=datacache[5];
8:uart_tx<=datacache[6];
9:uart_tx<=datacache[7];
10:uart_tx<=1;
11:begin
uart_tx<=1;
end
default:uart_tx<=1;
endcase
end
end
always@(posedge clk,negedge reset_n)begin
if(!reset_n)
tx_done<=0;
else if((bps_clk)&&(bps_cnt==10))
tx_done<=1;
else
tx_done<=0;
end
endmodule
PS端代码-vitis端
功能
主要功能是实现两个UART串口之间的双向中继传输
(原理:它使用Xilinx提供的驱动库函数对UART进行初始化和配置,并根据需要设置波特率和FIFO阈值。通过不断检查串口的接收缓冲区是否有数据,并将数据发送到另一个串口,从而实现了数据在两个串口之间的传递。)
#include "xparameters.h"
#include "xplatform_info.h"
#include "xuartps.h"
#include "xil_exception.h"
#include "xil_printf.h"
#include "xscugic.h"
char hh[] = "UART TEST\n";
#define UART_ID XPAR_PS7_UART_1_DEVICE_ID
#define UART_INT_ID XPAR_XUARTPS_1_INTR
#define SCU_ID XPAR_SCUGIC_0_DEVICE_ID
XUartPs_Config *uart_config;
XUartPs uart_inst;
XScuGic_Config *xscu_config;
XScuGic scu_inst;
void uart_Handler(void *callback){
XUartPs *psuart = (XUartPs *)callback;
u8 dec_rec;
u32 status;
//XUartPs_SendByte(XPAR_PS7_UART_1_BASEADDR,25);
status = XUartPs_ReadReg(psuart->Config.BaseAddress,XUARTPS_IMR_OFFSET);
status&= XUartPs_ReadReg(psuart->Config.BaseAddress,XUARTPS_ISR_OFFSET);
if(status&XUARTPS_IXR_RXOVR){
dec_rec = XUartPs_RecvByte(XPAR_PS7_UART_1_BASEADDR);
XUartPs_SendByte(XPAR_PS7_UART_1_BASEADDR,dec_rec);
//清除中断标志位
XUartPs_WriteReg(psuart->Config.BaseAddress, XUARTPS_ISR_OFFSET, XUARTPS_IXR_RXOVR);
xil_printf("%x",XUartPs_ReadReg(psuart->Config.BaseAddress,XUARTPS_ISR_OFFSET));
}
}
int main()
{
int status;
//print("UART TEST\n");
//串口初始化
uart_config = XUartPs_LookupConfig(UART_ID);
status = XUartPs_CfgInitialize(&uart_inst,
uart_config, uart_config->BaseAddress);
if(status!=XST_SUCCESS){
print("failed\n");
return XST_FAILURE;
}
XUartPs_EnableUart(&uart_inst);
//设置串口模式
XUartPs_SetOperMode(&uart_inst, XUARTPS_OPER_MODE_NORMAL);
//速率设置
status = XUartPs_SetBaudRate(&uart_inst, 115200);
//设置FIFO溢出个数
XUartPs_SetFifoThreshold(&uart_inst, 1);
if(status!=XST_SUCCESS){
print("failed\n");
return XST_FAILURE;
}
//查找中断控制器
xscu_config = XScuGic_LookupConfig(SCU_ID);
status = XScuGic_CfgInitialize(&scu_inst,xscu_config,
xscu_config->CpuBaseAddress);
if(status!=XST_SUCCESS){
print("failed\n");
return XST_FAILURE;
}
//老规矩,打开中断控制器后打开异常中断的处理函数
Xil_ExceptionInit();
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
(Xil_ExceptionHandler)XScuGic_InterruptHandler,
(void *)&scu_inst);
Xil_ExceptionEnable();
//设置回调函数
XScuGic_Connect(&scu_inst, UART_INT_ID,
(Xil_InterruptHandler)uart_Handler, (void *)&uart_inst);
//设置中断的触发方式
XUartPs_SetInterruptMask(&uart_inst, XUARTPS_IXR_RXOVR);
//打开中断控制器的使能
XScuGic_Enable(&scu_inst, UART_INT_ID);
//XUartPs_Send(&uart_inst, (u8*)hh, (u32)strlen(hh));
print("UART TEST\n");
while(1){
}
return 0;
}