当前位置: 首页 > news >正文

【【FPGA 之 MicroBlaze 自定义IP核 之 呼吸灯实验】】

FPGA 之 MicroBlaze 自定义IP核 之 呼吸灯实验

通过创建和封装 IP 向导的方式来自定义 IP 核,支持将当前工程、工程中的模块或者指定文件目录封装成 IP 核,当然也可以创建一个带有 AXI4 接口的 IP 核,用于 MicroBlaze 软核处理器和可编程逻辑的数据通信。本次实验选择常用的方式,即创建一个带有 AXI 接口的 IP 核,该 IP 核通过 AXI协议实现 MicroBlaze 软核处理器和可编程逻辑的数据通信。AXI 协议是一种高性能、高带宽、低延迟的片内总线
下面展示 本次实验的系统框图
在这里插入图片描述

Breath LED IP 核为自定义的 IP 核,McroBlaze 处理器通过 AXI 接口为LED IP 模块发送配置数据,从而来控制 LED 灯

实验任务 :
本章的实验任务是通过自定义一个 LED IP 核,来控制 LED 呈现呼吸灯的效果,并且可以通过 AXI 接口来控制呼吸灯的开关和呼吸的频率。

我们去创建自定义IP核
创建之后 会帮助我们创建一个AXI的外壳
之前我也做过类似的设计 AXI 设计 但是并没有设计的很好 这次我们细致的分析一下 该有的做法
在这里插入图片描述

我们来看
在这里插入图片描述

一共分为了2组
首先上面那组是 我们自定义的参数
下面一组是 系统定义的AXI接口的参数 系统也提示我们无法修改

接下来我们看下一部分用户自定义的
在这里插入图片描述

我们 添加了上面两个 东西 一个参数 一个输出
因为这里是 主函数 main 所以 在下面例化的添加 对参数和端口的例化

接下来我们来看 主函数下的另一个函数
这个函数的出现是为了 补充 不让main看上去很臃肿
在这里插入图片描述

这个函数的部分内容是可以修改的
我们往其中写入部分函数 使得其能够 加载入LED 呼吸灯的功能
函数同样提醒了 可以添加参数进行修改
在这里插入图片描述

为什么要修改这里的东西 主要是 我们想通过这里 AXI 的 寄存器 的 结果 来调配呼吸灯的值
现在告诉你 slv_reg0 至 slv_reg3 是寄存器地址0 至寄存器地址 3 对应的数据,通过例化呼吸灯模块,将寄存器地址对应的数据和呼吸灯模块的控制端口相连接,即可实现对呼吸灯的控制。

在这里插入图片描述

观察 我们可以知道
在这里插入图片描述

我们通过寄存器地址 0 对应的数据来控制呼吸灯的使能(sw_ctrl)
寄存器地址 1 对应数据的最高位控制呼吸灯频率的设置有效信号(set_en)
寄存器地址 1 对应数据的低 10 位控制呼吸灯频率的步长(set_freq_step)

我们会很容易的发现这个很奇怪是不是 因为我们连最重要的 breath 文件都没添加
下面补上呼吸灯的verilog 代码

module breath_led( input sys_clk , //系统时钟 50MHz input sys_rst_n , //系统复位,低电平有效input sw_ctrl , //呼吸灯开关控制信号 1:亮 0:灭input set_en , //设置呼吸灯频率设置使能信号input [9:0] set_freq_step , //设置呼吸灯频率变化步长output led //LED 灯); //parameter define parameter START_FREQ_STEP = 10'd1 ; //设置频率步长初始值parameter CNT_2US_MAX = 7'd100 ;parameter CNT_2MS_MAX = 10'd1000 ; parameter CNT_2S_MAX = 10'd1000 ; //reg define reg [6:0] cnt_2us ; reg [9:0] cnt_2ms ;reg [9:0] cnt_2s ;reg inc_dec_flag ; //亮度递增/递减 0:递增 1:递减reg [9:0] freq_step ; //呼吸灯频率间隔步长reg led_t ; //***************************************************** //** main code //***************************************************** assign led = led_t & sw_ctrl; //设置频率间隔,频率步长值在 1-10 之间always @(posedge sys_clk) begin if(!sys_rst_n) freq_step <= START_FREQ_STEP; else if(set_en) begin if(set_freq_step == 0) freq_step <= 10'd1; else if(set_freq_step >= 10'd10) freq_step <= 10'd10; elsefreq_step <= set_freq_step; endend //cnt_2us:计数 2us always@(posedge sys_clk or negedge sys_rst_n) begin if(!sys_rst_n) cnt_2us <= 7'b0; else if(cnt_2us == (CNT_2US_MAX - 7'b1 )) cnt_2us <= 7'b0; else cnt_2us <= cnt_2us + 7'b1; end //cnt_2ms:计数 2ms always@(posedge sys_clk or negedge sys_rst_n) begin if(!sys_rst_n) cnt_2ms <= 10'b0; else if(cnt_2ms == (CNT_2MS_MAX - 10'b1) && cnt_2us == (CNT_2US_MAX - 7'b1)) cnt_2ms <= 10'b0; else if(cnt_2us == CNT_2US_MAX - 7'b1) cnt_2ms <= cnt_2ms + 10'b1; else cnt_2ms <= cnt_2ms; end //cnt_2s:计数 2s always@(posedge sys_clk or negedge sys_rst_n) begin if(!sys_rst_n) cnt_2s <= 10'b0; else if(cnt_2s >= (CNT_2S_MAX - 10'b1) && cnt_2ms == (CNT_2MS_MAX - 10'b1)&& cnt_2us == (CNT_2US_MAX - 7'b1)) cnt_2s <= 10'b0; else if(cnt_2ms == (CNT_2MS_MAX - 10'b1) && cnt_2us == (CNT_2US_MAX - 7'b1)) cnt_2s <= cnt_2s + freq_step; else cnt_2s <= cnt_2s;end //inc_dec_flag 为低电平,led 灯由暗变亮,inc_dec_flag 为高电平,led 灯由亮变暗always@(posedge sys_clk or negedge sys_rst_n) begin if(!sys_rst_n) inc_dec_flag <= 1'b0; else if(cnt_2s >= (CNT_2S_MAX - 10'b1) && cnt_2ms ==( CNT_2MS_MAX - 10'b1) && cnt_2us == (CNT_2US_MAX - 7'b1)) inc_dec_flag <= ~inc_dec_flag; else inc_dec_flag <= inc_dec_flag; end //led:输出信号连接到外部的 led 灯always@(posedge sys_clk or negedge sys_rst_n) begin if(!sys_rst_n) led_t <= 1'b0; else if((inc_dec_flag == 1'b1 && cnt_2ms >= cnt_2s) || (inc_dec_flag == 1'b0 && cnt_2ms <= cnt_2s)) led_t <= 1'b1; else led_t<= 1'b0; end endmodule

同样的 在我们自定义完整个IP之后 会自动连接成c语言 方便在 Vitis 软件中对 IP 核进行操作
在这里插入图片描述

Makefile 让人想起了 Linux 的编译 这涉及到了另一个领域

接下来我们装载进入 vitis
在这里插入图片描述

在 BSP 中包含了 我们的自己创建的 breath_led
下面介绍整个 main.c函数

#include "stdio.h"
#include "xparameters.h"
#include "xil_printf.h"
#include "breath_led_ip.h"
#include "xil_io.h"
#include "sleep.h"
#define LED_IP_BASEADDR XPAR_BREATH_LED_IP_0_S0_AXI_BASEADDR //LED IP 基地址
#define LED_IP_REG0 BREATH_LED_IP_S0_AXI_SLV_REG0_OFFSET //LED IP 寄存器地址 0
#define LED_IP_REG1 BREATH_LED_IP_S0_AXI_SLV_REG1_OFFSET //LED IP 寄存器地址 1
//main 函数
int main()
{
int freq_flag; //定义频率状态,用于循环改变呼吸灯的呼吸频率
int led_state; //定义 LED 灯的状态
xil_printf("LED User IP Test!\n");
while(1){
//根据 freq_flag 的标志位,切换呼吸灯的频率
if(freq_flag == 0){
BREATH_LED_IP_mWriteReg(LED_IP_BASEADDR,LED_IP_REG1,0x800000ef);
freq_flag = 1;
}
else{
BREATH_LED_IP_mWriteReg(LED_IP_BASEADDR,LED_IP_REG1,0x8000002f);
freq_flag = 0;
}
//获取 LED 当前开关状态 1:打开 0:关闭
led_state = BREATH_LED_IP_mReadReg(LED_IP_BASEADDR,LED_IP_REG0);
//如果开关关闭,打开呼吸灯
if(led_state == 0){
BREATH_LED_IP_mWriteReg (LED_IP_BASEADDR, LED_IP_REG0, 1);
xil_printf("Breath LED ON\n");
}
sleep(5);
//获取 LED 当前开关状态 1:打开 0:关闭
led_state = BREATH_LED_IP_mReadReg(LED_IP_BASEADDR,LED_IP_REG0);
//如果开关打开,关闭呼吸灯
if(led_state == 1){
BREATH_LED_IP_mWriteReg (LED_IP_BASEADDR, LED_IP_REG0, 0);
xil_printf("Breath LED OFF\n");
}
sleep(1);
}
}

我们开始分析一下
其实掌握自己的东西很开心
我们打开platform 可以看到
在这里插入图片描述

有我们的 自定义的 IP 生成的 .c .h 内文件
我们来看主函数
我们先介绍一个非常重要的基本概念
IP的接口就是函数的输入参数
生成的.c文件 就相当于 把硬件抽象 成 一个库函数
通过库函数 来设置 输入参数
所以生成的逻辑 就是 根据你的硬件IP 的接口来的

ok 我们在做设计的时候 当合成一个IP之后 进入 C语言的层次之后 其实我们并不需要 知道 这个模块究竟是如何连接的 我们只需要知道这个模块的引脚是什么 功能是什么即可
对于这里的设计 我们需要在意的是 IP的 工作 输入输出 是什么 输入是 S_AXI 而 输出是 LED (LED 我们知道是寄存器达标的值)
那么 功能是 把 数写进 寄存器
LED的改变只是 因为寄存器变化而带来的附加效果
其实相当于把 数据存进到 寄存器
所以可想而知 .c 文件大概率也是这些功能
因为是系统来完成的 我们所能做的就是 观察 得出结论
在这里插入图片描述

这句话 就相当于 把数据放进基地址+偏移量的位置
在这里插入图片描述

这句话 就相当于 输出的意思

ok 解析完 系统生成的.c .h 我们接下来看主函数 对我们自定义的.c的调用
很简单的 使用我们的.c .h文件

#include "stdio.h"
#include "xparameters.h"
#include "xil_printf.h"
#include "breath_led_ip.h"
#include "xil_io.h"
#include "sleep.h"
#define LED_IP_BASEADDR   XPAR_BREATH_LED_IP_0_S0_AXI_BASEADDR //LED IP 基地址
#define LED_IP_REG0     BREATH_LED_IP_S0_AXI_SLV_REG0_OFFSET //LED IP 寄存器地址 0
#define LED_IP_REG1     BREATH_LED_IP_S0_AXI_SLV_REG1_OFFSET //LED IP 寄存器地址 1
//main 函数
int main()
{
int freq_flag; //定义频率状态,用于循环改变呼吸灯的呼吸频率
int led_state; //定义 LED 灯的状态
xil_printf("LED User IP Test!\n");
while(1){
//根据 freq_flag 的标志位,切换呼吸灯的频率
if(freq_flag == 0){
BREATH_LED_IP_mWriteReg(LED_IP_BASEADDR,LED_IP_REG1,0x800000ef);
freq_flag = 1;
}
else{
BREATH_LED_IP_mWriteReg(LED_IP_BASEADDR,LED_IP_REG1,0x8000002f);
freq_flag = 0;
}
//获取 LED 当前开关状态 1:打开 0:关闭
led_state = BREATH_LED_IP_mReadReg(LED_IP_BASEADDR,LED_IP_REG0);
//如果开关关闭,打开呼吸灯
if(led_state == 0){
BREATH_LED_IP_mWriteReg (LED_IP_BASEADDR, LED_IP_REG0, 1);
xil_printf("Breath LED ON\n");
}
sleep(5);
//获取 LED 当前开关状态 1:打开 0:关闭
led_state = BREATH_LED_IP_mReadReg(LED_IP_BASEADDR,LED_IP_REG0);
//如果开关打开,关闭呼吸灯
if(led_state == 1){
BREATH_LED_IP_mWriteReg (LED_IP_BASEADDR, LED_IP_REG0, 0);
xil_printf("Breath LED OFF\n");
}
sleep(1);
}
}

相关文章:

  • 深入理解同源限制:网络安全的守护者(下)
  • css 3D背景反转实现
  • NIO--07--Java lO模型详解
  • 如何打印社保参保凭证
  • python之pyqt专栏5-信号与槽1
  • 钉钉聊天审计软件有哪些
  • web前端之vue3
  • cyclictest 交叉编译与使用
  • 【ArcGIS Pro微课1000例】0040:ArcGIS Pro创建北极点、南极点
  • 公平锁和非公平锁以及他们的实现原理是什么
  • YOLOv5算法进阶改进(7)— 将主干网络SPPF更换为SimSPPF / SPP-CSPC / SPPF-CSPC
  • node.js-连接SQLserver数据库
  • 谨慎Apache-Zookeeper-3.5.5以后在CentOS7.X安装的坑
  • 程序员学习方法
  • Hdoop学习笔记(HDP)-Part.19 安装Kafka
  • happypack两次报错的问题
  • JavaScript 奇技淫巧
  • JavaScript标准库系列——Math对象和Date对象(二)
  • scrapy学习之路4(itemloder的使用)
  • vagrant 添加本地 box 安装 laravel homestead
  • 从tcpdump抓包看TCP/IP协议
  • 多线程事务回滚
  • 简单易用的leetcode开发测试工具(npm)
  • 解析带emoji和链接的聊天系统消息
  • 如何利用MongoDB打造TOP榜小程序
  • 使用Gradle第一次构建Java程序
  • 想晋级高级工程师只知道表面是不够的!Git内部原理介绍
  • shell使用lftp连接ftp和sftp,并可以指定私钥
  • ​【C语言】长篇详解,字符系列篇3-----strstr,strtok,strerror字符串函数的使用【图文详解​】
  • ​Linux·i2c驱动架构​
  • ​低代码平台的核心价值与优势
  • ​马来语翻译中文去哪比较好?
  • #1014 : Trie树
  • #QT(串口助手-界面)
  • (Matalb时序预测)PSO-BP粒子群算法优化BP神经网络的多维时序回归预测
  • (MonoGame从入门到放弃-1) MonoGame环境搭建
  • (分布式缓存)Redis分片集群
  • (附源码)spring boot车辆管理系统 毕业设计 031034
  • (附源码)spring boot基于小程序酒店疫情系统 毕业设计 091931
  • (附源码)springboot炼糖厂地磅全自动控制系统 毕业设计 341357
  • (附源码)ssm基于web技术的医务志愿者管理系统 毕业设计 100910
  • (附源码)计算机毕业设计ssm基于B_S的汽车售后服务管理系统
  • (六)vue-router+UI组件库
  • (四)Tiki-taka算法(TTA)求解无人机三维路径规划研究(MATLAB)
  • (原創) 如何動態建立二維陣列(多維陣列)? (.NET) (C#)
  • (转载)利用webkit抓取动态网页和链接
  • *1 计算机基础和操作系统基础及几大协议
  • ./和../以及/和~之间的区别
  • .NET 6 在已知拓扑路径的情况下使用 Dijkstra,A*算法搜索最短路径
  • .net 流——流的类型体系简单介绍
  • .NET/C# 在 64 位进程中读取 32 位进程重定向后的注册表
  • .NET开源快速、强大、免费的电子表格组件
  • /bin/bash^M: bad interpreter: No such file or directory
  • []error LNK2001: unresolved external symbol _m
  • [ArcPy百科]第三节: Geometry信息中的空间参考解析