FPGA_学习_14_第一个自写模块的感悟和ila在线调试教程与技巧(寻找APD的击穿偏压)
前一篇博客我们提到了,如果要使用算法找到Vbr,通过寻找APD采集信号的噪声方差的剧变点去寻找Vbr是一个不错的方式。此功能的第一步是在FPGA中实现方差的计算,这个我们已经在上一篇博客中实现了。
继上一篇博客之后,感觉过了很久了,原因是最近陷入的FPGA在线调试的无线循环。 万事开头难,自决定自学FPGA以来已3月有余。 刚开始我以为的万事开头难是如何从零开始在板子上跑个程序。 而真正的万事开头难是根据项目的需求,自己写出的第一个具有特定功能的模块。 而就在刚才,我经历了千辛万苦,终于算是把我第一个模块调通了。要不是我拥有我这个年纪本不该拥有的稳重,差点就热泪盈眶啦,因为调试过程确实比较曲折。根据以往的经验,一旦有所感悟一定要立马记下来,好记性不如烂笔头。 但本文绝不是我在这里发表感慨,而是我认为确实有一些值得记录的点。本文主要分为三个部分:1、第一个自写模块的感悟;2、ila在线调试教程;3、ila在线调试的技巧和注意事项。
由于目前处于自学初级阶段,也只会一些简单的调试技巧,后续如果有了新的技巧,要不断的添加更新。
1 第一个自写模块的感悟
1.1 明确模块要实现的目标,输入输出是什么?
先来回顾一下我自己是怎么写出这个模块的,一开始肯定是一脸懵逼的,不知道从何下手。所以我首先思考的是这个模块的目标是什么,更确切一点就是它需要什么输入,然后它能够输出什么。 我们的目标是找到某一个通道的APD击穿电压。
需要的输入是: 时钟、复位信号、主板温度信息、本模块的使能信号(电平使能)、该通道下ADC的采样数据。
需要的输出是: 找到击穿偏压的标志(脉冲信号,就是只有一个时钟周期的高电平)、ADC采集数据的方差(用于调试观测)、通道1 APD击穿时对应的DAC码值。由于要修改APD的偏压,需要控制DAC,而控制DAC的信号在模块外,因此需要在此模块中引出。
module find_vbr(input wire clk , // 50M input wire rst_n ,input wire [15:0] temper_front , // PS端传入的主板温度(16位为1表示数据有效,数据为8位温度数据+90)input wire find_vbr_ena , // 寻找击穿偏压的使能信号input wire [31:0] adc_data_ch1 , // CH1 ADC采样数据output wire vbr_found_flag , // CH1 已找到击穿偏压标志output reg [15:0] adc_var_ch1 , // CH1 ADC采样数据的方差output reg [11:0] vbr_hv_code_ch1 , // CH1 击穿偏压对应的码值output reg [1:0] hv_dac_addr ,output reg [11:0] hv_dac_data ,output reg hv_dac_start
);
当然啦,模块的输入输出,是会在模块的实现过程中增删的,这是很正常的事情。所以最开始的时候,也不必想的很全面,我们对这个模块只需要有一个初步的输入输出定义就好啦。
1.2 拆解目标
有了明确的目标,也有了输入输出之后,接下来,就是思考要实现这个目标,我要是实现哪些步骤了。 因此我没先急着写代码,而是先写了点注释。
// 0、什么时候开始
// 1、根据温度获取对应Vbr的码值
// 2、Vbr偏压码值 - 0x50
// 3、步长0x08变化偏压码值,设置后延迟相应的时间让设置的偏压稳定
// 4、待当前偏压稳定后,计算信号的方差
// 5、判断当前信号底噪方差与上一个码值对应的信号底噪方差(默认为0)的差值是否超过阈值(30)
// 6、如果差值超过阈值则Flag拉高,如果差值未超过阈值,则继续加偏压,直到超过阈值为止
// 7、什么时候结束
要实现我的目标,那就按照上述步骤一步一步实现就可以了。由于以前C语言编写的比较多,潜意识里都是串行思路,因此在思考和拆解大目标的时候,习惯用的是串行思维。 在后续的开发过程中要注意习惯并行思维的应用。当然了,即使是到写博客的现在呢,我仍然是认为这个模块就应该用串行的思维来思考和拆解。 只是警醒一下自己,不要忘记有并行的思维。
1.3 实现目标-硬着头皮写
即使明确了目标,也拆解了目标,对于一个FPGA初学者来讲,要动手去从0到1的实现,也是需要很大的魄力的。 开发板的例程,你有得抄,更注重理解。而现在你真要上了, 没有代码给你抄(上一篇博客我们其实是参考了C站的C知道给出的答案,有点走捷径的感觉),你得自己尝试着写了。 这里就只能硬着头皮写了,没有捷径,没有任何技巧。当然了,硬着头皮写的前提是基于开发板的基础学习还是要扎实的,不然你头发掉光了也是写不出来的,多少有点自欺欺人了。
硬着头皮写呢,有时候也会陷入一种瞻前顾后,犹豫不决,害怕失败的感觉,迟迟不敢往下写,这是正常的。 我可以肯定的告诉你,你第一把写出来的程序,百分之百有问题。 你根本不用担心失败不失败的问题,因为肯定有问题。
你先写出来,我们主要追求的是一个完整性。
别看功能也不复杂,硬着头皮完整写完,这个步骤我基本上花了1周的时间。 调试我花了两个周,哈哈。 在后面的调试过程中,我又做了很多修改。 我觉得其中值得注意的一点就是, 你要在草稿纸上简单画一画时序图, 你希望你的这些信号的时序图长什么样子。 这是你在实现的时候思考和关注的问题。后续有在调试的时候,也要看实测抓出的波形是不是如你设计的那样。
我把最终成功运行的代码贴出来吧,供参考和备忘。
`timescale 1ns / 1psmodule find_vbr(input wire clk , // 50M input wire rst_n ,input wire [15:0] temper_front , // PS端传入的主板温度(16位为1表示数据有效,数据为8位温度数据+90)input wire find_vbr_ena , // 寻找击穿偏压的使能信号input wire [31:0] adc_data_ch1 , // CH1 ADC采样数据output wire vbr_found_flag , // CH1 已找到击穿偏压标志output reg [15:0] adc_var_ch1 , // CH1 ADC采样数据的方差output reg [11:0] vbr_hv_code_ch1 , // CH1 击穿偏压对应的码值output reg [1:0] hv_dac_addr ,output reg [11:0] hv_dac_data ,output reg hv_dac_start
);//==================================================================
// Parameter define
//==================================================================
parameter THRESHOLD = 50;
parameter MAX_WAIT_COUNT = 100_000_000 - 1; // 20ns x 100_000_000 = 2 s
parameter HVCODE_STEP = 8; // 偏压码值变化8,偏压实际变化约等于0.25V
parameter DEFAULT_HVCODE = 12'h4E0; // 默认APD偏压码值(修改后可设置默认偏压)
parameter APD_SET_DELAY = 32'd500_000; // 设置单通道APD后等待时间
parameter APD_SET_WIDE = 32'd500; // 设置使能脉宽// parameter THRESHOLD = 30;
// parameter MAX_WAIT_COUNT = 20 - 1; // 20ns x 100_000_000 = 2 s
// parameter HVCODE_STEP = 8; // 偏压码值变化8,偏压实际变化约等于0.25V
// parameter DEFAULT_HVCODE = 12'h4E0; // 默认APD偏压码值(修改后可设置默认偏压)
// parameter APD_SET_DELAY = 32'd20; // 设置单通道APD后等待时间
// parameter APD_SET_WIDE = 32'd10; // 设置使能脉宽//==================================================================
// Internal Signals
//==================================================================
(* MARK_DEBUG="true" *) wire [11:0] rom_hv_code; // rom查找的APD偏压码值 官方给出的击穿电压再减去2V所对应的码值
(* MARK_DEBUG="true" *) reg [27:0] wait_cnt; // 延迟计数变量
(* MARK_DEBUG="true" *) wire [11:0] pre_hv_code;
(* MARK_DEBUG="true" *) reg [11:0] cur_hv_code;
(* MARK_DEBUG="true" *) reg is_init;
(* MARK_DEBUG="true" *) reg is_finish;
(* MARK_DEBUG="true" *) reg [31:0] apd_set_wait_cnt; //自动设置状态停留计数(* MARK_DEBUG="true" *) reg is_hv_can_be_set; // 偏压是否进入可设置状态
(* MARK_DEBUG="true" *) reg is_hv_can_be_wait; // 偏压是否进入等待响应状态
(* MARK_DEBUG="true" *) reg is_hv_set_completed; // 偏压设置是否已完成
(* MARK_DEBUG="true" *) wire [15:0] cur_var; // 当前信号方差
(* MARK_DEBUG="true" *) wire var_available; // 当前信号方差可用
(* MARK_DEBUG="true" *) reg [15:0] his_var; // 历史信号方差
(* MARK_DEBUG="true" *) reg [15:0] delta_var; // 方差变化量// (* MARK_DEBUG="true" *) reg rst_n;
// reg [7:0] rst_counter; // 默认为0
// parameter RESET_COUNT_MAX = 100;// always @(posedge clk) begin
// if (rst_counter < RESET_COUNT_MAX) begin
// rst_counter <= rst_counter + 1;
// end
// else if(rst_counter == RESET_COUNT_MAX)begin
// rst_counter <= rst_counter;
// end
// else begin
// rst_counter <= 'd0;
// end
// end// always @(posedge clk) begin
// if(rst_counter==RESET_COUNT_MAX) begin
// rst_n <= 1'b1;
// end else begin
// rst_n <= 1'b0;
// end
// end//----------------------------- pre_hv_code -----------------------------
// assign pre_hv_code = (temper_front[15] == 1'b1) ? rom_hv_code:DEFAULT_HVCODE; // 验证温度数据是否有效(当disable拉高时,上电设置默认偏压值)
assign pre_hv_code = DEFAULT_HVCODE; APD_rom R_APD_rom ( // 通过Rom读取当前温度对应的官方击穿偏压 - 2V所对应的码值.a(temper_front[7:0]), // input wire [7:0] a.spo(rom_hv_code) // output wire [11:0] spo
);//----------------------------- is_init -----------------------------
always @(posedge clk or negedge rst_n) beginif (rst_n == 1'b0) beginis_init <= 1'b0; endelse if(find_vbr_ena == 1'b1 && is_init == 1'b0) begin // 当前条件下,初始化要设置的偏压码值current_HVCODEis_init <= 1'b1;endelse beginis_init <= is_init;end
end//----------------------------- is_init -----------------------------
always @(posedge clk or negedge rst_n) beginif (rst_n == 1'b0) beginis_finish <= 1'b0; endelse if(var_available==1'b1 && delta_var >= THRESHOLD) begin // 当前条件下,初始化要设置的偏压码值current_HVCODEis_finish <= 1'b1;endelse beginis_finish <= is_finish;end
end//----------------------------- is_finish ----------------------------- // 若方差已计算,且方差变化量大于等于阈值,则结束。
// assign is_finish = (var_available==1'b1 && delta_var >= THRESHOLD) ? 1'b1:1'b0;//----------------------------- cur_hv_code -----------------------------
always @(posedge clk or negedge rst_n) beginif (rst_n == 1'b0) begincur_hv_code <= DEFAULT_HVCODE; endelse if(find_vbr_ena == 1'b1 && is_init == 1'b0) begincur_hv_code <= pre_hv_code - 12'h040; endelse if( (is_hv_set_completed == 1'b1) && (var_available == 1'b1) && (is_finish == 1'b0) && cur_hv_code < 12'h578) begin // 若已初始化,方差已计算,且未结束,则偏压码值按固定步长增长进入下一轮。 cur_hv_code <= cur_hv_code + HVCODE_STEP;endelse begincur_hv_code <= cur_hv_code;end
end//----------------------------- is_hv_can_be_set -----------------------------
always @(posedge clk or negedge rst_n) beginif (rst_n == 1'b0) beginis_hv_can_be_set <= 1'b0; endelse if(find_vbr_ena == 1'b1 && is_init == 1'b0) begin // 初始化后,isHvCanBeSet被拉高is_hv_can_be_set <= 1'b1;endelse if( (is_hv_set_completed == 1'b1) && (var_available == 1'b1) && (is_finish == 1'b0)) begin // 若已初始化,方差已计算,且未结束,isHvCanBeSet被拉高 is_hv_can_be_set <= 1'b1;end else if ( (is_hv_can_be_set ==1'b1) && (is_hv_can_be_wait==1'b1)) beginis_hv_can_be_set <= 1'b0;end else beginis_hv_can_be_set <= is_hv_can_be_set;end
end//----------------------------- hv_dac_start+hv_dac_data ----------------------------- // 设置偏压操作
always @(posedge clk or negedge rst_n) beginif (rst_n == 1'b0) beginhv_dac_addr <= 2'd0;hv_dac_start <= 1'b0;apd_set_wait_cnt<= 'd0; endelse if(is_hv_can_be_set == 1'b1)beginif (apd_set_wait_cnt > APD_SET_DELAY / 2) beginhv_dac_start <= 1;endif (apd_set_wait_cnt > (APD_SET_DELAY / 2) + (APD_SET_WIDE / 2)) begin hv_dac_data <= cur_hv_code;endif (apd_set_wait_cnt > APD_SET_DELAY / 2 + APD_SET_WIDE) beginhv_dac_start <= 'd0;endif (apd_set_wait_cnt > APD_SET_DELAY) beginapd_set_wait_cnt <= 'd0;endelse beginapd_set_wait_cnt <= apd_set_wait_cnt + 1'b1;end end
end//----------------------------- is_hv_can_be_wait -----------------------------
always @(posedge clk or negedge rst_n) beginif (rst_n == 1'b0) beginis_hv_can_be_wait <= 1'b0; endelse if (apd_set_wait_cnt > APD_SET_DELAY) beginis_hv_can_be_wait <= 1'b1;endelse if (is_hv_can_be_wait == 1'b1 && wait_cnt == MAX_WAIT_COUNT) beginis_hv_can_be_wait <= 1'b0;endelse beginis_hv_can_be_wait <= is_hv_can_be_wait;end
end//----------------------------- wait_cnt ----------------------------- // 计数两秒
always @(posedge clk or negedge rst_n) beginif (rst_n == 1'b0) beginwait_cnt <= 'd0; endelse if(is_hv_can_be_wait == 1'b1) beginif (wait_cnt == MAX_WAIT_COUNT) beginwait_cnt <= 'd0;endelse beginwait_cnt <= wait_cnt + 1'b1;endendelse beginwait_cnt <= 'd0;end
end//----------------------------- is_hv_set_completed -----------------------------
always @(posedge clk or negedge rst_n) beginif (rst_n == 1'b0) beginis_hv_set_completed <= 1'b0; endelse if (is_hv_can_be_wait == 1'b1 && wait_cnt == MAX_WAIT_COUNT) begin // 偏压设置完成后,计数两秒等待结束,isHvSetCompleted被拉高is_hv_set_completed <= 1'b1;endelse if (is_hv_set_completed == 1'b1 && var_available == 1'b1) begin // 方差计算完成后,isHvSetCompleted被拉低is_hv_set_completed <= 1'b0;endelse beginis_hv_set_completed <= is_hv_set_completed;end
end//----------------------------- var_compute -----------------------------
var_compute var_calculator (.clk ( clk ) ,.rst_n ( rst_n ) ,.data_in ( adc_data_ch1[7:0] ) ,.valid_in ( is_hv_set_completed ) , .variance ( cur_var ) ,.valid_out ( var_available )
);//----------------------------- delta_var ----------------------------- // 方差变化量
//assign delta_var = (var_available == 1'b1 && cur_var > his_var) ? (cur_var - his_var) : 'd0;//----------------------------- vbr_found_flag ----------------------------- // 是否找到击穿偏压
assign vbr_found_flag = (delta_var >= THRESHOLD) ? 1'b1 : 1'b0;//----------------------------- his_var -----------------------------
always @(posedge clk or negedge rst_n) beginif (rst_n == 1'b0) beginhis_var <= 'd0; endelse if(var_available == 1'b1) begin // 如果当前偏压并不是击穿偏压,则记录历史方差,以便于后续计算方差变化量his_var <= cur_var;endelse beginhis_var <= his_var;end
end//----------------------------- delta_var -----------------------------
always @(posedge clk or negedge rst_n) beginif (rst_n == 1'b0) begindelta_var <= 'd0; endelse if(var_available == 1'b1) begin // 如果当前偏压并不是击穿偏压,则记录历史方差,以便于后续计算方差变化量if (cur_var >= his_var) begindelta_var <= cur_var - his_var;endelse begindelta_var <= 'd0;endendelse begindelta_var <= delta_var;end
endendmodule
1.4 前仿真和后仿真
硬着头皮写完之后呢,就是进行仿真了,有经验的老师傅说,一般来讲前仿真(功能仿真)通过了,在线就不会有太大的问题。 而后仿真呢,是最接近与上机跑的真实情况的,但是后仿真编译的时间也比较长,所以很少有人搞后仿真的。 Modelsim搞仿真还是有一套的,再次安利一波。 在讲仿真的这里,我特别想强调的一个点,就是一定要尽量模拟真实的情况。 否则,你所认为的仿真“通过”了,就很片面,局限。 仿真这里一定要考虑全面。 我就是吃了这方面的亏, 在上一篇博客,我实现了一个方差计算小模块,我用的输入数据是0~255,这个数据仿真是没问题的(V2.0)。 但是我在用真机调试的时候,就出现了问题。 一方面在计算方差的时候,位宽的问题,我也没有考虑周到,一方面方差的计算精度,我也没有考虑到,最后优化后都是V4.0了。由于仿真的粗心,在调更大模块的时候,我的先验知识就让我不要去考虑是不是方差计算模块出了问题,而是去考虑其他地方的问题,但是恰巧就是方差模块出了问题,这样就导致了无法准确定位的问题真正的位置。 所以在仿真的时候, 要考虑全面,细致。 比如我们的真实数据是在7f和80之间来回变化,那么我们如何在Testbench代码中把真实的数据模仿出来,这个是要好好考虑的问题(就在写博客的时候我已经想到如何实现了,比如根据求计数器余数的办法给出是7f是80,所以别畏难,肯定有办法)。 在调试的时候,由于一直无法定位问题,后仿真我也测试过,也是“通过”的。但是上机还是通不过,我还怀疑是板子硬件有问题,还去换了板子测试,结果是一样的。
所以仿真的全面和细致真的很重要,另外,对于所谓的仿真“通过”,是要保持一颗怀疑的心的。
1.5 对自己吹一口彩虹屁
之前在调试的时候,总是找不到问题,也请教了前辈,但是仍然没有解决问题。 他们就说我的这种实现方式(没有用状态机)有点不稳定,很容易出问题。 我当时也认同没有用状态机可能程序没那么稳定的观点。 我也想过要不就用状态机重新实现一遍。但如果让我稀里糊涂的重构用状态机实现,我心里是不甘心,不服气的。即使我的实现方式有问题,那我也一定要找到我目前这种实现方法的问题在那里,不然不明不白的重写我是无法接受的。 我的确也没有重构,通过两星期持续的坚持调试,我最终定位到了问题,并且也解决了。 戏剧的点是,其实根本就不是我实现方式的问题,而是方差子模块的问题。 因此我要感谢自己,感谢自己的不甘心,感谢自己的不服气。学习FPGA编程的态度,当如是也!
2 ila在线调试教程
ila是一种FPGA常用的在线调试方式,和DSP、STM32的断点调试不同,ila是通过抓取信号来判断你的程序是否正常运行的。学习ila我是看了B站的一个up主的视频的:Vivado在线调试工具ILA使用教程【小梅哥FPGA】_哔哩哔哩_bilibili,全程1个半小时,很受用。如视频所说的,用ila实现在线调试的方式有好几种,在这里呢,我把我最近用的这种方式记录下来,供大家和未来的自己参考。
第一步:
在所有在线调试需要抓取的变量前 添加(* MARK_DEBUG="true" *)
(* MARK_DEBUG="true" *) wire [11:0] rom_hv_code; // rom查找的APD偏压码值 官方给出的击穿电压再减去2V所对应的码值
(* MARK_DEBUG="true" *) reg [27:0] wait_cnt; // 延迟计数变量
(* MARK_DEBUG="true" *) wire [11:0] pre_hv_code;
(* MARK_DEBUG="true" *) reg [11:0] cur_hv_code;
(* MARK_DEBUG="true" *) reg is_init;
(* MARK_DEBUG="true" *) reg is_finish;
(* MARK_DEBUG="true" *) reg [31:0] apd_set_wait_cnt; //自动设置状态停留计数
第二步:
综合电路
第三步:
打开综合设计
正常的话,会等待一段时间
第四步:
第五步:
Next三下
第六步:
添加带观测变量并设置时钟域
第七步:
选择采样的数据深度、勾选捕获和触发。
第八步:
Finish
第九步:
后面会出现一堆提示,一路OK下去。
第十步:
重新综合、布线、生成bit文件,以便后续烧写程序,在线调试
3 ila在线调试的技巧和注意事项
在工程比较大的时候,在vivado版本比较低的时候,在你不熟悉vivado的时候,你去用ila在线调试,你会遇到各种奇葩的问题,解决办法也很奇葩。 只要你动了项目里微小的东西,比如加个IP核,减个IP核,甚至哪怕你源代码中多了一个空格。同样的工程,以前编译能够通过的,现在很有可能编译通不过了。 编译通不过的原因一般出现时布局布线上面,这里面有很多随机性。 同样地,你在ila调试的时候,你增加一个观测变量,你删除一个观测变量。 都有可能造成编译无法通过,最终无法生成bit文件。
调试技巧1
当你编译通不过了,你尝试删除几个ila的观测变量,如果再通不过,那就再删除几个。 你这次删除后编译通过了,下次编译你再慢慢加上去,也可以的。
调试技巧2
如果编译通过了,硬件上电正常,仿真器连接都正常,但是,你始终没办法打开你的硬件。 解决办法可以是:重新打开另一个vivado
调试技巧3
正常的调试流程,也稍微说一下
先把程序烧写到板子上
选择.bit文件
下载之后,Refresh device一下,这个操作一定别忽略。
在触发设置的窗口,添加触发信号 ,可以用 这个加号添加,也可以用拖动的方式。
然后设置触发条件,并且运行。
运行之后,如果系统捕获到了你的触发条件,那么波形窗口就会显示出来。
这个按钮是连续触发的意思,你先选中这个按钮,然后再点运行,它就会根据你的触发条件,连续不断的触发。刷新你的波形数据。 在调试一些需要观察数据变化的时候可以用使用。
另外,当你想要用多个信号来进行触发的时候,需要点击这个按钮。
调试技巧4
调试的过程,一般是由顶层逐步向下再展开去看信号是否正常, 比如顶层的top,top里面实例化了一个 find_vbr子模块叫inst_find_vbr, find_vbr子模块里面实例化了一个var_compute子模块 叫inst_var_compute。 你调试的过程应该是先看 top层的信号正不正常, 再看find_vbr层的信号正不正常,再看var_compute层的信号正不正常。 逐步的深入。 这三层模块的内部信号,都是可以使用(* MARK_DEBUG="true" *)标记,然后在线调试观测的。
调试技巧5
如果你想判断程序是否执行了某个条件,那么你可以添加一个test_flag变量, 复位的时候拉低,然后在你想检测条件下面把这个flag拉高。 这样我们就可以判断出,这个条件是否被执行过。
注意事项
代码只要烧写进去了,它就会自己跑起来,不会等你点 这个按钮,它才开始跑。
比如你自己写了一个内部的复位信号, 你在调试的时候,你是抓不到rst_n的上升沿的。 复位时间是小于1ms的,因为等你去抓的时候,人家早就已经拉高了。
(* MARK_DEBUG="true" *) reg rst_n;
reg [7:0] rst_counter; // 默认为0
parameter RESET_COUNT_MAX = 100;always @(posedge clk) beginif (rst_counter < RESET_COUNT_MAX) beginrst_counter <= rst_counter + 1;end else if(rst_counter == RESET_COUNT_MAX)beginrst_counter <= rst_counter;endelse beginrst_counter <= 'd0;end
endalways @(posedge clk) begin if(rst_counter==RESET_COUNT_MAX) begin rst_n <= 1'b1;end else begin rst_n <= 1'b0;end
end
越学习,越觉得自己无知,后面应该会有一篇讲VIO的博客。欢迎大家留私信,或者评论区讨论。 分享大家的调试问题和技巧。
未完待续...
相关文章:
FPGA_学习_14_第一个自写模块的感悟和ila在线调试教程与技巧(寻找APD的击穿偏压)
前一篇博客我们提到了,如果要使用算法找到Vbr,通过寻找APD采集信号的噪声方差的剧变点去寻找Vbr是一个不错的方式。此功能的第一步是在FPGA中实现方差的计算,这个我们已经在上一篇博客中实现了。 继上一篇博客之后,感觉过了很久了…...
【2023新教程】树莓派定时自动拍照并上传腾讯云对象存储COS
1 换源 仅适用于Release date: May 3rd 2023、Debian version: 11 (bullseye)这个树莓派OS版本,其他版本不保证有效。 首先使用如下命令,查看自己树莓派的架构。 uname -a结果如下: 如果红圈处显示为aarch64,使用命令sudo na…...
校企合作谋发展 合作共赢谱新篇|云畅科技与湖南民族职业学院签订校企合作协议
产业是经济发展的重要引擎,人才是产业发展的重要资源。为积极探索软件人才培育新路径,共商政产学研协同新机制,8月8日,云畅科技与湖南省民族职业学院教育技术学院软件技术专业签订校企合作协议。 会上,学院副校长王志平…...
vue技术学习
vue快速入门 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>vue快速入门</title> </head> <body> <!--老师解读 1. div元素不是必须的,也可以是其它元素࿰…...
基于空间的图卷积神经网络:GNN
目录 欧氏空间中神经网络发挥巨大最作用,DNA,知识图谱三维或者多维空间不行 邻接矩阵实现图结构的矩阵化表示:造梦师 局和操作实现层内消息传递:带线的连接机传递消息 GCN通过邻域聚合实现特征提取 SVM支持向量机 编辑 硬分…...
.net core发布到IIS上出现 HTTP 错误 500.19
1.检查.net core 环境运行环境是否安装完成,类似如下环境 2.IIS是否安装全 本次原因就是IIS未安装全导致的 按照网上说的手动重启iis(iisreset)也不行...
01_Redis单线程与多线程
01——Redis单线程与多线程 一、Redis是单线程还是多线程 在谈Redis的单线程或多线程时,需要根据版本来区分。 在redis 3.x之前,redis是单线程的从redis 4.x开始,redis引入多线程。处理客户端请求时,使用单线程;在异…...
机器学习——随机森林【手动代码】
随机森林这个内容,是目前来说。。。最最最简单,最好理解,应该也是最好实现的了!!! 先挖坑,慢慢填 随机森林,这个名字取得,果然深得该算法的核心精髓,既随机&a…...
Vue 2 处理边界情况
访问元素和组件 通过Vue 2 组件基础一文的学习,我们知道组件之间可以通过传递props或事件来进行通信。 但在一些情况下,我们使用下面的方法将更有用。 1.访问根实例 根实例可通过this.$root获取。 我们在所有子组件中都可以像上面那样访问根实例&…...
写一个mysql 正则表达式,每三个img标签图片后面添加<hr>
你可以使用MySQL的REGEXP_REPLACE函数来实现这个需求。下面是一个示例的正则表达式和SQL语句: sql UPDATE your_table SET your_column REGEXP_REPLACE(your_column, (<img[^>]*>){3}, $0<hr>) WHERE your_column REGEXP (<img[^>]*>){3}…...
Spring MVC异常处理
Spring MVC异常处理 Spring MVC异常处理机制HandlerExceptionResolver的实现类DefaultHandlerExceptionResolver实现类DefaultHandlerExceptionResolver 在Controller的请求处理方法中手动使用try…catch块捕捉异常,当捕捉到指定的异常时,系统返回对应的…...
Centos7安装docker后默认开启docker0的网卡|卸载默认网卡
docker实战(一):centos7 yum安装docker docker实战(二):基础命令篇 docker实战(三):docker网络模式(超详细) docker实战(四):docker架构原理 docker实战(五):docker镜像及仓库配置 docker实战(六):docker 网络及数据卷设置 docker实战(七):docker 性质及版本选择 认知升…...
04_Redis与mysql数据双写一致性案例
04——redis与mysql数据双写一致性 一、canal 是什么 canal[ka’nel,中文翻译为水道/管道/沟渠/运河,主要用途是用于MySQL数据库增量日志数据的订阅、消费和解析,是阿里巴巴开发并开源的,采用Java语言开发; 历史背景是早期阿里巴巴因为杭州和…...
vue的开发者工具下载『保姆级别』
1.先进官网 极简插件_Chrome扩展插件商店_优质crx应用下载 (zzzmh.cn) 2.搜索vue devtools,点击进去 3.下载插件 4.下载到文件下你自己的文件下:我的是下载到E盘下。 5.压缩到当前目录下 6.电脑进入拓展程序(不同的浏览器操作不同ÿ…...
vue的scrollTop手机环境设置值失效,本地正常可以赋值
获取div盒子ref或者document获取都行 监听方法 一定要加this.$nexttick,在本地测试只用nexttick是没有问题的,但是到手机测试就不行了,原因是因为手机渲染比本地更快,所以结合setTimeout使用 如果有更好的处理方法,恳请大佬指点一…...
[前端系列第7弹]Vue:一个渐进式的 JavaScript 框架
Vue 是一个用于构建用户界面的 JavaScript 框架,它具有以下特点: 渐进式:Vue 可以根据不同的使用场景,灵活地选择使用库或者框架的方式,从而实现渐进式的开发。响应式:Vue 通过数据绑定和虚拟 DOM 技术&am…...
C#键盘按键对应Keys类大全
...
SpringBoot 学习(03): 弱语言的注解和SpringBoot注解的异同
弱语言代表:Hyperf,一个基于 PHP Swoole 扩展的常驻内存框架 注解概念的举例说明; 说白了就是,你当领导,破烂事让秘书帮你去安排,你只需要批注一下,例如下周要举办一场活动,秘书将方…...
CloudQuery:更好地管理你的 OceanBase 数据库
前言:作为 OceanBase 的生态合作伙伴,CloudQuery(简称“CQ”) 最新发布的社区版 2.2.0 新增了 OceanBase 数据库,为企业使用 OceanBase 数据库提供全面的支持。包括连接与认证、查询与分析、数据安全与权限管理&#x…...
php的password_verify 和 password_hash密码验证
password_hash() 使用足够强度的单向散列算法创建密码的散列(hash)。 当前支持的算法: PASSWORD_DEFAULT - 使用 bcrypt 算法 (PHP 5.5.0 默认)。 注意,该常量会随着 PHP 加入更新更高强度的算法而改变。 所以,使用此常量生成结果的长度将在未…...
JAVA免杀学习与实验
1 认识Webshell 创建一个JSP文件: <% page import"java.io.InputStream" %> <% page import"java.io.BufferedReader" %> <% page import"java.io.InputStreamReader" %> <% page language"java" p…...
Apche Kafka + Spring的消息监听容器
目录 一、消息的接收1.1、消息监听器 二、消息监听容器2.1、 实现方法2.1.1、KafkaMessageListenerContainer2.1.1.1、 基本概念2.1.1.2、如何使用 KafkaMessageListenerContainer 2.1.2、ConcurrentMessageListenerContainer 三、偏移 四、监听器容器自动启动 一、消息的接收 …...
[JavaWeb]【五】web后端开发-Tomcat SpringBoot解析
目录 一 介绍Tomcat 二 基本使用 2.1 解压绿色版 2.2 启动TOMCAT 2.3 关闭TOMCAT 2.4 常见问题 2.5 修改端口号 2.6 部署应用程序 三 SpringBootWeb入门程序解析 前言:tomcat与SpringBoot解析 一 介绍Tomcat 二 基本使用 2.1 解压绿色版 2.2 启动TOMCAT 2…...
css 用过渡实现,鼠标离开li时,背景色缓慢消息的样式
要实现鼠标悬停时背景颜色变为黄色,鼠标离开时背景颜色慢慢消失并变回白色的效果, 可以使用CSS的过渡(transition)属性 li {background: #fff;color: #000;transition: background 0.5s ease-out; }li:hover {background: #fbb31…...
pytorch 线性层Linear详解
线性层就是全连接层,以一个输入特征数为2,输出特征数为3的线性层为例,其网络结构如下图所示: 输入输出数据的关系如下: 写成矩阵的形式就是: 下面通过代码进行验证: import torch.nn as nn …...
LeetCode 833. 字符串中的查找与替换
2235. 两整数相加 添加链接描述 给你两个整数 num1 和 num2,返回这两个整数的和。 示例 1: 输入:num1 12, num2 5 输出:17 解释:num1 是 12,num2 是 5 ,它们的和是 12 5 17 ,…...
Oracle故障案例之-19C时区补丁DSTV38更新
📢📢📢📣📣📣 哈喽!大家好,我是【IT邦德】,江湖人称jeames007,10余年DBA工作经验 一位上进心十足的【大数据领域博主】!😜ὡ…...
设计模式之组合模式(Composite)的C++实现
1、组合模式的提出 在软件开发过程中,使用者Client过多依赖所操作对象内部的实现结构,如果对象内部的实现结构频繁发生变化,则使用者的代码结构将要频繁地修改,不利于代码地维护和扩展性;组合模式可以解决此类问题。组…...
mongo的include方法踩坑
前言 又是不认识自己代码的一天 问题 Query query new Query(); if(StringUtils.isNotNull(reqVO.getFieldLimitList()) && reqVO.getFieldLimitList().size() > 0){for(String filedName : reqVO.getFieldLimitList()){query.fields().include(filedName);} }看到…...
阿里云无影云电脑/云桌面收费价格表_使用申请方法
阿里云无影云电脑配置具体收费价格表,4核8G企业办公型云电脑可以免费使用3个月,无影云电脑地域不同价格不同,无影云电脑费用是由云桌面配置、云盘、互联网访问带宽、AD Connector 、桌面组共用桌面session 等费用组成,阿里云百科分…...
绍兴网站建设哪家好/平台广告推广
返璞归真这几天项目有一个linux下部署数据库的操作,数据库使用python进行初始化安装。然后问题来了,由于linux服务器涉及安全要求,除了代码以来的Python3.6版本外不允许安装其他插件与工具,不巧的是python的代码报错了…如果放在平…...
网站突然消失了/域名怎么注册
构造思路: 1.socket 连接获取 Banner --> 2.与存在漏洞的 Banner 集合进行对比 中间细节: 1.需要判断用户所给参数是否存在且是否有读权限 2.需要判断 Banner 是否存在,处理异常 学习 os sys socket 各个模块的基本使用 直接上代码&…...
利用已有网站 制作/安卓优化大师hd
话说C语言是很多语言学习的基础,理解并掌握好了C语言的一些语法规则,对于以后学习或者说自学其他语言,可以节省不少的时间。下面我们就来了解C语言程序当中的结构体知识点。结构体的定义形式如下:struct 结构体名{结构体成员}&…...
青岛网站建设找/网站优化外包推荐
首先确保机器上安装了openssl和openssl-devel #yum install openssl #yum install openssl-devel 然后就是自己颁发证书给自己 #cd /usr/local/nginx/conf #openssl genrsa -des3 -out server.key 1024 #生成私钥,需要输入密码,记住就…...
淘宝网站c 设计怎么做的/百度站长工具网站提交
用C做后台开发有哪些需要注意的问题 说起后台开发,严格地说和用什么语言开发没有必然的关系。比如说网络游戏的后台,用C开发的有很多,但用Java开发的也不少,而且在某些情况下,用Java做服务器效果还较好。其实…...
做啥网站赚钱?/google搜索引擎优化
当给一个SAP CRM Quotation文档的行项目维护一个产品时,遇到如下错误信息:Pricing procedure could not be determined 通过调试得知错误消息在function module CRM_PRIDOC_COM_PRCPROC_DET_SEL第55行抛出: 在subroutine DETERMINE_PROCEDURE…...