西电通院微控速通指南FPGA部分
在微控讨论贡献度时,做FPGA和MCU的同学贡献度通常会高的离谱,但是事实上这两方面工作难度是有,但是如果认真学习这方面知识,微控的MCU与FPGA难度并不高。我认为微控作为一个小组作业,有分工合作,每个人都要完成自己分内的东西,秘书,公关,产品经理所作文书工作虽然简单,但是要做好也不容易,也并不应该因为选择了做这个,最终微控得到了很低的得分。
目录
前言
在微控讨论贡献度时,做FPGA和MCU的同学贡献度通常会高的离谱,但是事实上这两方面工作难度是有,但是如果认真学习这方面知识,微控的MCU与FPGA难度并不高。
我认为微控作为一个小组作业,有分工合作,每个人都要完成自己分内的东西,秘书,公关,产品经理所作文书工作虽然简单,但是要做好也不容易,也并不应该因为选择了做这个,最终微控得到了很低的得分。FPGA,MCU部分的有一定门槛,但是在我看来绝大多数做该部分的同学,并没有真正去学习这方面的知识,而是使用着从各处找的工程,或者在别人的博文上抄的代码,不知其原理,每天坐在大厅里改来改去,改到凌晨三四点,看似辛苦,做的大部分都是无用功罢了。
所以写本文章的目的把我这次微控所写的工程进行讲解,搭建一个可以直接使用的微控系统设计的代码框架,帮助学弟学妹们在面对微控可以轻松完成MCU与FPGA任务,减少难度的同时不希望出现某些做这两部分工作,就觉得自己比秘书,产品经理,公关做的努力多得多,要求高出20%贡献度的情况。(可以高但是不要高的离谱,例如MCU+FPGA的人拿20%以上,剩下人只有15%甚至更低,这种情况在本人看来十分离谱!!!除非你的队友什么都不干!)
话不多说,先放上我的工程链接zlzj12/Week1_FPGA,直接下载即可。
希望同学们可以认真阅读,轻松应对微控。
FPGA部分微控对其具体要求为
①实现FPGA控制舵机转动。
②实现FPGA与MCU的通信。
③通过MCU发送数据控制FPGA,FPGA进而控制舵机转动。
我使用的Vivado版本为2023.2,下载时使用了大约300GB存储空间。这里推荐使用2018版本,软件内存占用小。由于Vivadio版本兼容性的问题,可能我的工程不能直接使用。所以接下来讲解具体实现与代码编写。包括使用时该如何修改我给出的代码。
如果有同学对于Vivado软件的环境不熟悉,不知道怎么进行代码仿真,综合,引脚分配,可以去B站看一下 小梅哥FPGA视频,目的是熟练Vivado软件的环境,如何新建工程,编写代码,烧录代码等基础操作。知道这些操作后,FPGA部分可以直接复制下面我提及的5个V语言文件,然后新建工程,自己跑一边综合仿真生成比特率文件烧录至FPGA上。【零基础轻松学习FPGA】小梅哥Xilinx FPGA基础入门到项目应用培训教程(2024全新课程已上线)_哔哩哔哩_bilibili
FPGA代码的模块化非常清晰,我的编写习惯为在顶层模块中调用小模块实现功能。可以理解为C语言在main.c文件中调用其他文件中函数的关系。小模块即为要调用的函数。
FPGA任务1:控制舵机转动
控制舵机的原理为通过FPGA产生周期20ms的PWM波通过改变占空比,即高电平存在时间,控制舵机转动角度。使用的舵机转动角度映射关系为0°-180°(0.5ms-2.5ms)。
下面给出的代码为最终版本代码,实现了外部发送degree角度来控制舵机转动。
如果语法看不懂也没关系,直接使用就可以,下面会详细讲解代码的内容。
Pwm_sevo.v代码
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
//
// Create Date: 2025/03/24 19:14:18
// Design Name:
// Module Name: Pwm_sevo
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////
module Pwm_sevo
(
input clk ,
input rst_n ,
input wire[7:0] degree ,
output reg pwm
);
//参数
parameter TIM_20ms = 20'd 100_0000; //50M时钟分频50
parameter TIM_0d5ms = 20'd 2_5000; //0.5ms 0度
parameter TIM_1ms = 20'd 5_0000; //1ms 45度
parameter TIM_1d5ms = 20'd 7_5000; //1.5ms 90度
parameter TIM_2ms = 20'd 10_0000; //2ms 135度
parameter TIM_2d5ms = 20'd 12_5000; //2.5ms 180度
reg [31:0] cnt_pwm ;
reg [31:0] cnt ;
//内部信号I
//wire add_cnt;
//wire end_cnt;
//功能
//20ms计数
always @(posedge clk or negedge rst_n) begin
if(!rst_n)begin
cnt <= 0;
end
else if(cnt >= TIM_20ms - 1)begin
cnt <= 0;
end
else begin
cnt <= cnt + 1;
end
end
//assign add_cnt = 1;
//ssign end_cnt = add_cnt && cnt == TM_20ms - 1;
//修改pwm波形
always @(posedge clk or negedge rst_n) begin
if(!rst_n)begin
end
else if(degree<=8'd180)begin
cnt_pwm <= 32'd2_5000 + degree*32'd10_0000/32'd180;
end
else begin
cnt_pwm <= 32'd2_5000 ;//超过180度复位
end
end
//输出pwm
always @(posedge clk or negedge rst_n) begin
if(!rst_n)begin
pwm <= 0;
end
else if (cnt < cnt_pwm) begin
pwm <= 1;
end
else begin
pwm <= 0;
end
end
endmodule
代码讲解
接下来讲解具体代码(目的是实现占空比)
这段代码为Verilog中的模块,input为输入,output为输出。
只实现控制舵机,即产生固定占空比的PWM波,那我们只需确定占空比即可,通过degree控制占空比。
clk是输入的时钟信号,rst_n为低电平复位信号,该信号拉低将会一直在复位状态。
module Pwm_sevo
(
input clk ,
input rst_n ,
input wire[7:0] degree ,
output reg pwm
);
endmodule
这部分是定义的一些参数与变量。parameter定义常量 可以类比为C语言中的define。reg [31:0]定义寄存器变量,定义了一个32位寄存器。cnt_pwm这个寄存器变量控制了pwm波占空比。
//参数
parameter TIM_20ms = 20'd 100_0000; //50M时钟分频50
parameter TIM_0d5ms = 20'd 2_5000; //0.5ms 0度
parameter TIM_1ms = 20'd 5_0000; //1ms 45度
parameter TIM_1d5ms = 20'd 7_5000; //1.5ms 90度
parameter TIM_2ms = 20'd 10_0000; //2ms 135度
parameter TIM_2d5ms = 20'd 12_5000; //2.5ms 180度
reg [31:0] cnt_pwm ;
reg [31:0] cnt ;
这部分代码实现20ms计数,即产生周期20ms的pwm波。这段代码不需要修改。
//20ms计数
always @(posedge clk or negedge rst_n) begin
if(!rst_n)begin
cnt <= 0;
end
else if(cnt >= TIM_20ms - 1)begin
cnt <= 0;
end
else begin
cnt <= cnt + 1;
end
end
这部分代码通过接收到的degree,给cnt_pwm赋值,控制PWM波的占空比,控制角度。
//修改pwm波形
always @(posedge clk or negedge rst_n) begin
if(!rst_n)begin
end
else if(degree<=8'd180)begin
cnt_pwm <= 32'd2_5000 + degree*32'd10_0000/32'd180;
end
else begin
cnt_pwm <= 32'd2_5000 ;//超过180度复位
end
end
这段代码实现了PWM的输出。
//输出pwm
always @(posedge clk or negedge rst_n) begin
if(!rst_n)begin
pwm <= 0;
end
else if (cnt < cnt_pwm) begin
pwm <= 1;
end
else begin
pwm <= 0;
end
end
FPGA任务2:实现通信
FPGA与MCU通信,此处我选择使用UART的方式。
数据传输链只有MCU——>FPGA,传输的数据为舵机转动角度,所以规定MCU只发送角度信息即degree给FPGA。degree限制在0~180°,这样子MCU仅发送八位数据(一字节)就可以控制舵机。
这部分代码直接复制使用,因为我也是抄这篇博文,略做修改的。【FPGA协议篇】UART通信及其verilog实现(代码采用传参实现模块通用性,适用于快速开发)_fpga串口波特率自适应设计-CSDN博客
UART部分代码的结构为 UART_Top.v 顶层文件调用 UART_TX.v UART_RX.v 两个模块,所以有三段代码,这部分代码不做讲解,大家会复制到自己工程中即可。
要修改串口通信的波特率,不用在此处修改,因为这里我们做的都是模块设计,UART模块作为整个工程中一个模块,那么我们可以通过顶层来直接设置传输的通信波特率。这部分代码直接使用。
UART_Top.v代码
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
//
// Create Date: 2025/04/06 21:10:44
// Design Name:
// Module Name: UART_Top
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////
module UART_Top(
input i_clk_sys,
input i_rst_n,
input i_uart_rx,
output o_uart_tx,
output o_ld_parity
);
localparam DATA_WIDTH = 8;
localparam BAUD_RATE = 9600;
localparam PARITY_ON = 0;
localparam PARITY_TYPE = 0;
wire w_rx_done;
wire[DATA_WIDTH-1 : 0] w_data;
UART_RX
#(
.CLK_FRE(50), //时钟频率,默认时钟频率为50MHz
.DATA_WIDTH(DATA_WIDTH), //有效数据位,缺省为8位
.PARITY_ON(PARITY_ON), //校验位,1为有校验位,0为无校验位,缺省为0
.PARITY_TYPE(PARITY_TYPE), //校验类型,1为奇校验,0为偶校验,缺省为偶校验
.BAUD_RATE(BAUD_RATE) //波特率,缺省为9600
) u_uart_rx
(
.i_clk_sys(i_clk_sys), //系统时钟
.i_rst_n(i_rst_n), //全局异步复位,低电平有效
.i_uart_rx(i_uart_rx), //UART输入
.o_uart_data(w_data), //UART接收数据
.o_ld_parity(o_ld_parity), //校验位检验LED,高电平位为校验正确
.o_rx_done(w_rx_done) //UART数据接收完成标志
);
UART_TX
#(
.CLK_FRE(50), //时钟频率,默认时钟频率为50MHz
.DATA_WIDTH(DATA_WIDTH), //有效数据位,缺省为8位
.PARITY_ON(PARITY_ON), //校验位,1为有校验位,0为无校验位,缺省为0
.PARITY_TYPE(PARITY_TYPE), //校验类型,1为奇校验,0为偶校验,缺省为偶校验
.BAUD_RATE(BAUD_RATE) //波特率,缺省为9600
) u_uart_tx
( .i_clk_sys(i_clk_sys), //系统时钟
.i_rst_n(i_rst_n), //全局异步复位
.i_data_tx(w_data), //传输数据输入
.i_data_valid(w_rx_done), //传输数据有效
.o_uart_tx(o_uart_tx) //UART输出
);
endmodule
UART_Tx.v代码
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
//
// Create Date: 2025/04/06 21:09:50
// Design Name:
// Module Name: UART_TX
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////
module UART_TX
#(
parameter CLK_FRE = 50, //时钟频率,默认时钟频率为50MHz
parameter DATA_WIDTH = 8, //有效数据位,缺省为8位
parameter PARITY_ON = 0, //校验位,1为有校验位,0为无校验位,缺省为0
parameter PARITY_TYPE = 0, //校验类型,1为奇校验,0为偶校验,缺省为偶校验
parameter BAUD_RATE = 9600 //波特率,缺省为9600
)
( input i_clk_sys, //系统时钟
input i_rst_n, //全局异步复位
input [DATA_WIDTH-1 : 0] i_data_tx, //传输数据输入
input i_data_valid, //传输数据有效
output reg o_uart_tx //UART输出
);
//状态机定义
reg [2:0] r_current_state; //当前状态
reg [2:0] r_next_state; //次态
localparam STATE_IDLE = 3'b000; //空闲状态
localparam STATE_START = 3'b001; //开始状态
localparam STATE_DATA = 3'b011; //数据发送状态
localparam STATE_PARITY = 3'b100; //数据校验计算和发送
localparam STATE_END = 3'b101; //结束状态
localparam CYCLE = CLK_FRE * 1000000 / BAUD_RATE; //波特计数周期
reg baud_valid; //波特计数有效位
reg [15:0] baud_cnt; //波特率计数器
reg baud_pulse; //波特率采样脉冲
reg [3:0] r_tx_cnt; //接收数据位计数
//波特率计数器
always@(posedge i_clk_sys or negedge i_rst_n)
begin
if(!i_rst_n)
baud_cnt <= 16'h0000;
else if(!baud_valid)
baud_cnt <= 16'h0000;
else if(baud_cnt == CYCLE - 1)
baud_cnt <= 16'h0000;
else
baud_cnt <= baud_cnt + 1'b1;
end
//波特采样脉冲
always@(posedge i_clk_sys or negedge i_rst_n)
begin
if(!i_rst_n)
baud_pulse <= 1'b0;
else if(baud_cnt == CYCLE/2-1)
baud_pulse <= 1'b1;
else
baud_pulse <= 1'b0;
end
//状态机状态变化定义
always@(posedge i_clk_sys or negedge i_rst_n)
begin
if(!i_rst_n)
r_current_state <= STATE_IDLE;
else if(!baud_valid)
r_current_state <= STATE_IDLE;
else if(baud_valid && baud_cnt == 16'h0000)
r_current_state <= r_next_state;
end
//状态机次态定义
always@(*)
begin
case(r_current_state)
STATE_IDLE: r_next_state <= STATE_START;
STATE_START: r_next_state <= STATE_DATA;
STATE_DATA:
if(r_tx_cnt == DATA_WIDTH)
begin
if(PARITY_ON == 0)
r_next_state <= STATE_END;
else
r_next_state <= STATE_PARITY; //校验位开启时进入校验状态
end
else
begin
r_next_state <= STATE_DATA;
end
STATE_PARITY: r_next_state <= STATE_END;
STATE_END: r_next_state <= STATE_IDLE;
default:;
endcase
end
reg [DATA_WIDTH-1 : 0] r_data_tx;
reg r_parity_check;
//状态机输出逻辑
always@(posedge i_clk_sys or negedge i_rst_n)
begin
if(!i_rst_n)
begin
baud_valid <= 1'b0;
r_data_tx <= 'd0;
o_uart_tx <= 1'b1;
r_tx_cnt <= 4'd0;
r_parity_check <= 1'b0;
end
else
case(r_current_state)
STATE_IDLE:begin
o_uart_tx <= 1'b1;
r_tx_cnt <= 4'd0;
r_parity_check <= 4'd0;
if(i_data_valid)
begin
baud_valid <= 1'b1;
r_data_tx <= i_data_tx;
end
end
STATE_START:begin
if(baud_pulse)
o_uart_tx <= 1'b0;
end
STATE_DATA:begin
if(baud_pulse)
begin
r_tx_cnt <= r_tx_cnt + 1'b1;
o_uart_tx <= r_data_tx[0];
r_parity_check <= r_parity_check + r_data_tx[0];
r_data_tx <= {1'b0 ,r_data_tx[DATA_WIDTH-1:1]};
end
end
STATE_PARITY:begin
if(baud_pulse)
begin
if(PARITY_TYPE == 1)
o_uart_tx <= r_parity_check;
else
o_uart_tx <= r_parity_check + 1'b1;
end
end
STATE_END:begin
if(baud_pulse)
begin
o_uart_tx <= 1'b1;
baud_valid <= 1'b0;
end
end
default:;
endcase
end
endmodule
UART_RX.v代码
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
//
// Create Date: 2025/04/06 21:09:34
// Design Name:
// Module Name: UART_RX
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////
module UART_RX
#(
parameter CLK_FRE = 50, //时钟频率,默认时钟频率为50MHz
parameter DATA_WIDTH = 8, //有效数据位,缺省为8位
parameter PARITY_ON = 0, //校验位,1为有校验位,0为无校验位,缺省为0
parameter PARITY_TYPE = 0, //校验类型,1为奇校验,0为偶校验,缺省为偶校验
parameter BAUD_RATE = 9600 //波特率,缺省为9600
)
(
input i_clk_sys, //系统时钟
input i_rst_n, //全局异步复位,低电平有效
input i_uart_rx, //UART输入
output reg[DATA_WIDTH-1 :0] o_uart_data, //UART接收数据
output reg o_ld_parity, //校验位检验LED,高电平位为校验正确
output reg o_rx_done //UART数据接收完成标志
);
/*
UART输入是异步输入,最好是同步到FPGA内部的时钟域
可以省略,但在异步电路中,保持时钟域同步是一个良好习惯
*/
reg sync_uart_rx;
always@(posedge i_clk_sys or negedge i_rst_n)
begin
if(!i_rst_n)
sync_uart_rx <= 1'b1;
else
sync_uart_rx <= i_uart_rx;
end
/*
连续采样五个接收路电平来判断rx是否有信号传来
用五个采样信号来作为判断标准可以有效排除毛刺噪声带来的误判
*/
reg [4:0] r_flag_rcv_start;
wire w_rcv_start;
always@(posedge i_clk_sys or negedge i_rst_n)
begin
if(!i_rst_n)
r_flag_rcv_start <= 5'b11111;
else
r_flag_rcv_start <= {r_flag_rcv_start[3:0], sync_uart_rx};
end
//状态机定义
reg [2:0] r_current_state; //当前状态
reg [2:0] r_next_state; //次态
localparam STATE_IDLE = 3'b000; //空闲状态
localparam STATE_START = 3'b001; //开始状态
localparam STATE_DATA = 3'b011; //数据接收状态
localparam STATE_PARITY = 3'b100; //数据校验状态
localparam STATE_END = 3'b101; //结束状态
localparam CYCLE = CLK_FRE * 1000000 / BAUD_RATE; //波特计数周期
reg baud_valid; //波特计数有效位
reg [15:0] baud_cnt; //波特率计数器
reg baud_pulse; //波特率采样脉冲
reg [3:0] r_rcv_cnt; //接收数据位计数
//波特率计数器
always@(posedge i_clk_sys or negedge i_rst_n)
begin
if(!i_rst_n)
baud_cnt <= 16'h0000;
else if(!baud_valid)
baud_cnt <= 16'h0000;
else if(baud_cnt == CYCLE - 1)
baud_cnt <= 16'h0000;
else
baud_cnt <= baud_cnt + 1'b1;
end
//波特采样脉冲
always@(posedge i_clk_sys or negedge i_rst_n)
begin
if(!i_rst_n)
baud_pulse <= 1'b0;
else if(baud_cnt == CYCLE/2-1)
baud_pulse <= 1'b1;
else
baud_pulse <= 1'b0;
end
//状态机状态变化定义
always@(posedge i_clk_sys or negedge i_rst_n)
begin
if(!i_rst_n)
r_current_state <= STATE_IDLE;
else if(!baud_valid)
r_current_state <= STATE_IDLE;
else if(baud_valid && baud_cnt == 16'h0000)
r_current_state <= r_next_state;
end
//状态机次态定义
always@(*)
begin
case(r_current_state)
STATE_IDLE: r_next_state <= STATE_START;
STATE_START: r_next_state <= STATE_DATA;
STATE_DATA:
if(r_rcv_cnt == DATA_WIDTH)
begin
if(PARITY_ON == 0)
r_next_state <= STATE_END;
else
r_next_state <= STATE_PARITY; //校验位开启时进入校验状态
end
else
begin
r_next_state <= STATE_DATA;
end
STATE_PARITY: r_next_state <= STATE_END;
STATE_END: r_next_state <= STATE_IDLE;
default:;
endcase
end
reg[DATA_WIDTH - 1 :0] r_data_rcv;
reg r_parity_check;
//状态机输出逻辑
always@(posedge i_clk_sys or negedge i_rst_n)
begin
if(!i_rst_n)
begin
baud_valid <= 1'b0;
r_data_rcv <= 'd0;
r_rcv_cnt <= 4'd0;
r_parity_check <= 1'b0;
o_uart_data <= 'd0;
o_ld_parity <= 1'b0;
o_rx_done <= 1'b0;
end
else
case(r_current_state)
STATE_IDLE:begin
//闲置状态下对寄存器进行复位
r_rcv_cnt <= 4'd0;
r_data_rcv <= 'd0;
r_parity_check <= 1'b0;
o_rx_done <= 1'b0;
//连续检测到低电平时认为UART传来数据,拉高baud_valid
if(r_flag_rcv_start == 5'b00000)
baud_valid <= 1'b1;
end
STATE_START:begin
if(baud_pulse && sync_uart_rx) //波特率采样脉冲到来时再次检测是否为低电平,如果不为低电平,认为前期误检测,重新进入IDLE状态
baud_valid <= 1'b0;
end
STATE_DATA:begin
if(baud_pulse)
begin
r_data_rcv <= {sync_uart_rx, r_data_rcv[DATA_WIDTH-1 : 1]}; //数据移位存储
r_rcv_cnt <= r_rcv_cnt + 1'b1; //数据位计数
r_parity_check <= r_parity_check + sync_uart_rx; //校验位做加法验证高电平的奇偶
end
end
STATE_PARITY:begin
if(baud_pulse)
begin
//校验检测,正确则o_ld_parity拉高,可输出给led检测,如果闪烁则表示有错误数据发生
if(r_parity_check + sync_uart_rx == PARITY_TYPE)
o_ld_parity <= 1'b1;
else
o_ld_parity <= 1'b0;
end
else
o_ld_parity <= o_ld_parity;
end
STATE_END:begin
if(baud_pulse)
begin
//没有校验位或者校验位正确时才输出数据,否则直接丢弃数据
if(PARITY_ON == 0 || o_ld_parity)
begin
o_uart_data <= r_data_rcv;
o_rx_done <= 1'b1;
end
end
else
begin
o_rx_done <= 1'b0;
end
if(baud_cnt == 16'h0000)
baud_valid <= 1'b0;
end
default:;
endcase
end
endmodule
FPGA任务3:MCU发送数据控制FPGA,控制舵机转动
上面我们已经完成了UART,舵机控制模块的编写,接下来编写顶层模块,由于题目给了三个舵机,此处代码实现了三个舵机联合控制。具体的逻辑为 MCU连续发送三个角度数据给FPGA,FPGA将三个角度数据分别控制三个舵机。只需要修改模块例化下的BAUD_RATE参数就可以修改波特率。
这部分代码也可以直接使用,但是有一个bug是每次上电后,MCU连续发送三个数据控制的舵机顺序不同,需要自己修改发送角度的顺序吗,此处修改MCU代码,FPGA代码不用动。
这个顶层代码也不做讲解,看注释即可。
TOP_design.v代码
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
//
// Create Date: 2025/04/06 21:05:22
// Design Name:
// Module Name: TOP_design
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////
module TOP_design(
input i_clk_sys,//时钟
input i_rst_n,//复位
input i_uart_rx,
output o_uart_tx,
output o_ld_parity,
output wire w_rx_done,
output [2:0] pwm // 三路PWM输出
);
//模块例化
localparam DATA_WIDTH = 8;
localparam BAUD_RATE = 9600;
localparam PARITY_ON = 0;
localparam PARITY_TYPE = 0;
// 内部信号声明
wire [7:0] w_data;
reg [7:0] reg1, reg2, reg3; // 新增三个数据寄存器
reg [1:0] cnt; // 接收计数器
UART_RX
#(
.CLK_FRE(50), //时钟频率,默认时钟频率为50MHz
.DATA_WIDTH(DATA_WIDTH), //有效数据位,缺省为8位
.PARITY_ON(PARITY_ON), //校验位,1为有校验位,0为无校验位,缺省为0
.PARITY_TYPE(PARITY_TYPE), //校验类型,1为奇校验,0为偶校验,缺省为偶校验
.BAUD_RATE(BAUD_RATE) //波特率,缺省为9600
) u_uart_rx
(
.i_clk_sys(i_clk_sys), //系统时钟
.i_rst_n(i_rst_n), //全局异步复位,低电平有效
.i_uart_rx(i_uart_rx), //UART输入
.o_uart_data(w_data), //UART接收数据
.o_ld_parity(o_ld_parity), //校验位检验LED,高电平位为校验正确
.o_rx_done(w_rx_done) //UART数据接收完成标志
);
// 数据分配控制逻辑
always @(posedge i_clk_sys or negedge i_rst_n) begin
if (!i_rst_n) begin
cnt <= 2'b00;
reg1 <= 8'h00;
reg2 <= 8'h00;
reg3 <= 8'h00;
end
else begin
if (w_rx_done) begin
case (cnt)
2'b00: reg1 <= w_data; // 第一个数据存reg1
2'b01: reg2 <= w_data; // 第二个数据存reg2
2'b10: reg3 <= w_data; // 第三个数据存reg3
endcase
cnt <= (cnt == 2'b10) ? 2'b00 : cnt + 1; // 循环计数
end
end
end
// 三路PWM模块例化
Pwm_sevo P_Pwm_sevo1 (
.clk(i_clk_sys),
.rst_n(i_rst_n),
.degree(reg1), // 使用第一个寄存器
.pwm(pwm[0])
);
Pwm_sevo P_Pwm_sevo2 (
.clk(i_clk_sys),
.rst_n(i_rst_n),
.degree(reg2), // 使用第二个寄存器
.pwm(pwm[1])
);
Pwm_sevo P_Pwm_sevo3 (
.clk(i_clk_sys),
.rst_n(i_rst_n),
.degree(reg3), // 使用第三个寄存器
.pwm(pwm[2])
);
endmodule
关于接口板的使用与FPGA引脚分配
最后讲一下FPGA引脚分配,发设备时会发一个绿色的大接口板,阅读接口板手册,按照手册连接接口板,FPGA引出了如下接口至接口板,恰好三个5V供电供给舵机。

这是我的引脚配置,PWM即为PWM波输出口,用于控制舵机,i_uart_rx为UART的RX,连接MCU的TX, 用于接收信息,i_rst_n为低电平复位信息线,这里我接到了板子的拨码开关,常置高电平。D4是50M时钟输出口,一定接ckl信号。其他信号线无所谓。

总结
微控FPGA部分较为公式化,代码可以直接进行移植,完全不用改动我提交的工程,做好引脚分配即可。
最后要注意一点,我们必须将Top_design.v文件置于顶层,这样子才能是一样的结果。
如下图,虽然我有很多的文件,但是只有TOP_desigin是顶层文件,所以仿真,综合时用的该文件。顶层文件一定一定不能设置错误。右边有三个方格。

更多推荐



所有评论(0)