基于立创逻辑派与高云FPGA的100MHz双通道数字示波器DIY全解析

news2026/3/17 20:40:56
基于立创逻辑派与高云FPGA的100MHz双通道数字示波器DIY全解析最近有不少朋友问我想深入学习FPGA和嵌入式系统有没有什么能动手又有挑战性的项目今天我就来分享一个自己刚做完的“大玩具”——一个基于立创逻辑派开发板和高云FPGA的100MHz双通道数字示波器。这个项目麻雀虽小五脏俱全从模拟电路设计、FPGA数字逻辑到单片机控制几乎涵盖了电子DIY的方方面面。如果你对示波器原理好奇或者想找一个综合性的FPGAMCU实战项目那这篇文章就是为你准备的。咱们这个示波器核心思路是用FPGA来处理高速数据采集和显示用MCU来处理菜单、按键这些控制逻辑。这样分工既发挥了FPGA并行处理速度快、适合做高速数据流的优势又避免了在FPGA里写复杂状态机去处理用户界面的麻烦。最终实现了一个采样率100MHz、存储深度16KB的双通道示波器还附带一个简易的DDS信号发生器功能。下面我就手把手带你把这个项目拆解一遍从硬件选型、电路设计到FPGA和MCU的程序编写最后再到调试校准。咱们一步步来。1. 系统架构与硬件选型做项目第一步得先搞清楚我们要用什么东西以及这些东西怎么配合工作。先来看一张系统框图心里有个谱。整个系统可以分成三大块模拟前端负责处理外部输入的信号把它调理成ADC能“吃”得下的样子。数字核心由FPGA和MCU组成是示波器的大脑。FPGA负责高速ADC数据采集、波形存储、触发、显示驱动和DDS信号生成。MCU负责通过SPI与FPGA通信读取按键和编码器处理菜单逻辑。人机交互包括4寸LCD屏幕、两个EC11编码器和8个按键。1.1 核心芯片选型选型就像搭积木核心部件选好了项目就成功了一半。主控FPGA高云 GW2A-LV18这是立创逻辑派开发板自带的核心。为什么选FPGA因为示波器需要实时、高速地处理ADC过来的数据流每秒1亿个点这种活交给FPGA这种并行处理的硬件来做最合适。高云这款FPGA资源足够性价比高开发环境高云云源用起来也还算顺手。辅助MCUGD32F303同样是逻辑派开发板自带的。它的任务相对“轻松”但繁琐管理用户界面。通过SPI读写FPGA内部的寄存器来设置触发模式、电压档位等参数同时扫描按键和编码器更新菜单显示。用MCU来做这些程序结构清晰开发调试也简单。高速ADCAD9288这是实现100MHz采样率的关键。AD9288是一颗8位精度、双通道、最高100MSPS采样率的ADC芯片。它输出的是数字信号直接送给FPGA读取。比起用单片机内置的ADC它的速度是天壤之别。波形输出DAC3PD5651如果你想让你做的示波器还能当个简易信号源用那就需要它。这是一颗10位精度的电流输出型DACFPGA产生的数字波形数据通过它转换成模拟电压输出。模拟前端运放们信号进来不是直接进ADC的需要一套“调理电路”。这个项目参考了成熟的开源设计主要用到了AD603压控增益放大器VCA。用它来实现精确的电压档位比如1V/格0.5V/格控制比用模拟开关切换电阻更精准阻抗影响小。ADA4932差分放大器。AD9288是差分输入的ADC所以需要它把单端信号转换成差分信号同时还能起到驱动作用。AD8065高速电压反馈型运放用作跟随器起缓冲和阻抗匹配作用。1.2 成本估算与物料清单DIY总得关心一下花了多少钱。原作者列了个清单我整理了一下你可以参考部件型号/说明预估单价(元)数量小计(元)核心板立创逻辑派开发板 (含FPGAMCU)138.001138.00ADC芯片AD9288-10012.00112.00DAC芯片3PD56516.0016.00压控增益放大器AD6034.5029.00差分放大器ADA493212.00224.00电源芯片TPS82130 (3.3V DCDC)6.5016.50运放AD80656.00212.00继电器信号继电器/固态继电器~10.00若干10.00程控DACMCP4728 (四通道)8.6018.60BNC接头3.50310.50显示屏4.0寸 IPS LCD (ST7796)37.00137.00其他阻容、接插件、PCB等~50.001批50.00总计~323.60注意这是基于当时市场价的粗略估算实际采购时价格会有浮动。你也可以根据自己的需求“减配”比如先做一个通道或者用更便宜的运放做实验成本可以降下来。2. 电路设计详解硬件设计是项目的基石。这部分我们分电源、模拟输入、模拟输出和其他电路来看。2.1 电源设计稳定是第一位示波器对电源噪声很敏感尤其是模拟部分。这个项目用了多路电源数字3.3V (VDD_3.3V)给FPGA、MCU、LCD等数字部分供电。电流需求大用了TPS82130这款DCDC芯片来提供效率高。负压-4.5V (VEE_4.5V)给运放提供负电源轨。最初用的TPS5430但发现USB供电时电压可能不够它启动。踩坑记录后来换成了SCT2433它的最低输入电压更低4.3V更可靠。换了芯片后反馈电阻也要从3.74kΩ改为2.2kΩ。模拟4.5V/3.3V (VCC_4.5V/VCC_3.3V)给ADC、运放等模拟部分供电。对噪声要求高所以用了低噪声的LDO来产生确保电源干净。2.2 模拟输入电路信号的“化妆师”这是示波器的“眼睛”决定了能看到多小、多快的信号。电路借鉴了OSCFUN开源示波器的设计非常经典。信号进来后的处理流程是这样的耦合选择通过固态继电器选择AC隔直耦合或DC直通耦合。衰减网络通过信号继电器切换x1或x10两路衰减实现不同的电压量程比如1V档和10V档。这里保证了1MΩ的输入阻抗。缓冲跟随用AD8065构成电压跟随器高输入阻抗、低输出阻抗隔离前后级。程控放大核心是AD603压控增益放大器。MCU通过MCP4728 DAC产生一个控制电压给AD603从而精确控制放大倍数微调每个档位的增益。差分驱动最后用ADA4932将单端信号转换成差分信号驱动ADCAD9288的差分输入端。提示板子上有四个可调电容是用来做阻抗匹配补偿的。如果你发现测直流和交流时幅度不一样或者输入的方波有失真可以微调这几个电容来校准。2.3 模拟输出电路简易信号源如果你想玩点花样让这个示波器还能输出信号这部分电路就用上了。FPGA内部用DDS直接数字频率合成技术生成数字波形送给3PD5651这颗电流输出型DAC。DAC输出是差分的所以后面用了一级运放把差分信号转成单端信号再经过一个电压跟随器缓冲和RC低通滤波最终输出比较干净的波形。2.4 其他电路与结构控制面板屏幕和8个按键做在另一块小PCB上作为“顶板”。通过一根34pin的同向FPC排线连接到主板。屏幕用的是4寸IPS屏驱动芯片是ST7796。程控电压产生用了一片MCP4728四通道DAC来产生各种控制电压比如给AD603的增益控制电压、通道的直流偏置电压等。这些电压的精度要求不高因为后期可以通过软件校准来补偿。3. FPGA程序设计高速数据流的管家FPGA是这里最忙的“打工人”它要同时干好几件事读取ADC数据、滤波、存储、触发、显示、产生DDS波形、和MCU通信。我们用Verilog来给它编程。3.1 ADC数据读取与滤波AD9288输出的数据格式是补码而我们在内部处理时常用偏移二进制码。所以第一步是转换。// AD9288输出是补码转换为偏移码0-255对应负满量程到正满量程 wire [7:0] DIN_A $unsigned(AD9288_DIN_A 8d127); wire [7:0] DIN_B $unsigned(AD9288_DIN_B 8d127); always (posedge clk) begin if (!rst_n) begin AD9288_DOUT_A 0; AD9288_DOUT_B 0; end else begin AD9288_DOUT_A DIN_A; AD9288_DOUT_B DIN_B; end end转换后数据可能还有毛刺加一个简单的滑动均值滤波平滑一下。这里用了8个数据的平均值。// 滑动均值滤波深度为8 reg [7:0] data_reg [0:7]; always (posedge clk) begin if(deci_valid) begin // deci_valid是降频后的数据有效信号 data_reg[0] ad_data_in; data_reg[1] data_reg[0]; // ... 依次移位 data_reg[7] data_reg[6]; end end // 求和并求平均右移3位相当于除以8 wire [11:0] sum data_reg[0] data_reg[1] data_reg[2] data_reg[3] data_reg[4] data_reg[5] data_reg[6] data_reg[7]; assign filtered_data sum 3; // AVE_DATA_BIT33.2 波形存储与触发逻辑示波器不能一直刷屏得抓住你想看的那一段波形这就是触发的作用。触发检测比较当前采样值和上一个采样值结合设定的触发电平和边沿上升沿或下降沿产生一个触发脉冲。// trig_edge: 0为上升沿1为下降沿 // trig_level: 触发电平值 wire trig_pulse trig_edge ? ((ad_data_current trig_level) (ad_data_previous trig_level)) : // 下降沿触发 ((ad_data_current trig_level) (ad_data_previous trig_level)); // 上升沿触发存储状态机这是一个经典的设计。我们用一个深度为16KB的RAM来存波形。状态0等待清空RAM。状态1预触发存储。不停地往RAM里存数据存到一半深度比如8K点时进入状态2。这样能保证触发点前后都有数据。状态2等待触发。继续存数据但覆盖最早的旧数据环形缓冲。一旦检测到触发信号记录下触发位置进入状态3。状态3触发后存储。继续存满剩下的深度比如再存8K点然后停止。// 状态机片段示例 always (posedge clk) begin case(state) 2b01: begin // 状态1预存储 if(deci_valid) begin write_enable 1; write_addr (write_addr 1) 14h3FFF; // 环形地址深度16K write_data ad_data; ad_cnt ad_cnt 1; // 存满一半深度后进入等待触发状态 if(ad_cnt 8191) state 2b10; end end 2b10: begin // 状态2等待触发 if(auto_trig || trig_pulse) begin // 自动触发或边沿触发 trig_pos write_addr; // 记录触发点位置 state 2b11; // 进入触发后存储 end // 继续环形存储... end 2b11: begin // 状态3触发后存储存满后停止 // ... 存储逻辑 if(ad_cnt 16383) begin // 存满总深度 write_enable 0; state 2b00; // 回到空闲等待读取显示 end end endcase end3.3 与MCU通信SPI寄存器接口FPGA和MCU之间通过SPI通信。我在FPGA内部开辟了一组寄存器MCU可以通过SPI读写这些寄存器从而控制FPGA的所有功能触发模式、电平、档位等也可以读取FPGA的状态如按键值。SPI从机模块负责解析MCU发来的指令。指令格式通常包含一个地址字节告诉FPGA要操作哪个寄存器一个读写标志然后是数据。// SPI状态机片段接收地址 RECEIVE_ADDR: begin if (bit_count 8) begin if(sck_pos) begin // 在SCK上升沿采样数据 address[7-bit_count] MOSI; // 接收地址位 bit_count bit_count 1; end end else begin // 8位地址接收完毕判断是读还是写 if (WR) begin // WR引脚为高是写操作 current_state WRITE_DATA; bit_count 0; end else begin // WR引脚为低是读操作 current_state SEND_DATA; // 根据地址准备要发送的数据 if(address 8d16) data_to_read regs[address[4:0]]; bit_count 0; end end end寄存器译码寄存器的值被翻译成各种控制信号。例如寄存器0的第0位控制耦合方式AC/DC。// 寄存器0的第0位0DC耦合1AC耦合 assign acdc_coupling regs[0][0]; // 寄存器1的第1-0位触发模式 assign trig_mode regs[1][1:0]; // 00自动01正常10单次 // 寄存器68位触发电平值 assign trig_level regs[6][7:0];3.4 LCD显示驱动用状态机“画”出界面用FPGA驱动LCD屏本质上就是按照屏的时序要求把颜色数据一个一个点地送出去。为了显示波形和UI需要设计一个复杂的状态机。显示逻辑主状态机控制整个刷屏周期。里面嵌套了多个子状态机分别负责屏幕初始化、绘制背景网格、从RAM读取波形数据并画线、显示字符和菜单等。SCAN: begin case(cnt_scan) 5d0: begin ram_addr trig_pos - step; // 从触发点前某个位置开始读 cnt_scan cnt_scan 1; end 5d1: begin ram_en 1; // 使能RAM读 cnt_scan cnt_scan 1; end 5d2: begin ram_addr ram_addr 1; // 地址递增 cnt_scan cnt_scan 1; end 5d3: begin // 读取两个通道的数据并做偏移因为屏幕坐标原点在左上角 ram_data_ch1 255 - ram_data_a_out; ram_data_ch2 255 - ram_data_b_out; cnt_scan cnt_scan 1; end 5d4: begin // 遍历屏幕的每一行(y坐标)如果该行的y值等于波形数据值就在对应的(x,y)画点 if(y_cnt 256) begin // 行扫描完 y_cnt 0; if(x_cnt 400) begin // 列也扫描完一帧结束 x_cnt 0; cnt_scan cnt_scan 1; end else begin x_cnt x_cnt 1; cnt_scan 5d2; // 回到地址更新继续画下一个点 end end else begin // 判断并设置当前要画的点的颜色黄色给通道1蓝色给通道2等 if(ram_data_ch1 y_cnt) color YELLOW; else if(ram_data_ch2 y_cnt) color BLUE; else if(grid_line) color GREY; // 网格线 else color BLACK; // 背景 // 设置画点坐标并跳转到画点子状态机 x x_cnt; y y_cnt; state DRAW_POINT; return_state SCAN; // 画完后返回SCAN状态 y_cnt y_cnt 1; end end 5d5: begin cnt_scan 0; state IDLE; // 一帧画完回到空闲等待下一帧 ram_en 0; end endcase end最终实现的UI界面如下图所示包含了波形显示区、参数显示区和菜单控件。3.5 DDS信号发生器FPGA内部调用高云提供的DDS IP核和Xilinx的DDS IP核兼容可以产生频率可控的正弦波。通过一些数字处理我们还能得到方波、三角波等。// 假设dds_sine_wave是DDS IP核输出的有符号正弦波数据 reg signed [9:0] square_wave; // 方波输出 reg signed [9:0] triangle_wave; // 三角波输出 // 1. 方波生成比较相位值 always (posedge clk) begin if (dds_phase[31:16] 16h8000) // 相位值高16位小于0x8000输出高电平 square_wave 10d511; // 高电平中间值满幅一半 else square_wave -10d512; // 低电平中间值-满幅一半 end // 2. 三角波生成利用相位值线性变化的特点 wire signed [9:0] phase_slice dds_phase 22; // 取相位值的一部分作为锯齿波 always (posedge clk) begin if (phase_slice 0) begin triangle_wave (phase_slice - 10d256) 1; // 上升沿 end else begin triangle_wave (10d768 - phase_slice) 1; // 下降沿 end end幅度控制通过移位和加法来实现简单的幅度衰减模拟示波器上的幅度档位。// 根据衰减选择(attenuation_sel)对原始波形(wave_reg)进行幅度缩放 always (posedge clk) begin case (attenuation_sel) 8d0: DAC_out wave_reg; // 满幅 8d1: DAC_out (wave_reg 1) (wave_reg 2) (wave_reg 3); // 近似0.875倍 8d2: DAC_out (wave_reg 1) (wave_reg 2); // 0.75倍 8d3: DAC_out (wave_reg 1) (wave_reg 3); // 0.625倍 8d4: DAC_out wave_reg 1; // 0.5倍 // ... 更多档位 default: DAC_out wave_reg; endcase end4. MCU程序设计用户交互的指挥官MCUGD32F303的程序用C语言编写主要任务就是当好“管家”管理按键、更新菜单、通过SPI告诉FPGA用户想要什么。4.1 与FPGA的SPI通信这是MCU和FPGA对话的桥梁。我们编写了两个核心函数写寄存器和读寄存器。// 写寄存器函数发送【地址 数据高8位 数据低8位】 void SPI_WriteRegister(uint8_t address, uint16_t data) { uint8_t tx_buf[3] {address, (data 8) 0xFF, data 0xFF}; GPIO_BC(SPI_CS_PORT) SPI_CS_PIN; // 拉低CS片选 GPIO_BOP(SPI_WR_PORT) SPI_WR_PIN; // 拉高WR表示写操作 for (int i 0; i 3; i) { while (RESET spi_i2s_flag_get(SPI0, SPI_FLAG_TBE)); // 等待发送缓冲区空 spi_i2s_data_transmit(SPI0, tx_buf[i]); // 发送数据 while (RESET spi_i2s_flag_get(SPI0, SPI_FLAG_RBNE)); // 等待接收完成 spi_i2s_data_receive(SPI0); // 读取丢弃返回的数据 } GPIO_BOP(SPI_CS_PORT) SPI_CS_PIN; // 拉高CS GPIO_BC(SPI_WR_PORT) SPI_WR_PIN; // 拉低WR } // 读寄存器函数发送【地址 两个填充字节】接收返回的数据 uint16_t SPI_ReadRegister(uint8_t address) { uint8_t tx_buf[3] {address, 0xFF, 0xFF}; uint8_t rx_buf[3]; GPIO_BC(SPI_CS_PORT) SPI_CS_PIN; // 拉低CS GPIO_BC(SPI_WR_PORT) SPI_WR_PIN; // 拉低WR表示读操作 for (int i 0; i 3; i) { while (RESET spi_i2s_flag_get(SPI0, SPI_FLAG_TBE)); spi_i2s_data_transmit(SPI0, tx_buf[i]); while (RESET spi_i2s_flag_get(SPI0, SPI_FLAG_RBNE)); rx_buf[i] spi_i2s_data_receive(SPI0); // 保存接收的数据 } GPIO_BOP(SPI_CS_PORT) SPI_CS_PIN; // 拉高CS // 返回的数据在第二、三个字节 return ((uint16_t)rx_buf[1] 8) | rx_buf[2]; }4.2 按键与编码器扫描由于逻辑派开发板引脚限制按键和编码器直接接到了FPGA的IO上。MCU通过SPI读取FPGA中的一个特定寄存器比如地址32来获取它们的电平状态。// 在定时器中断里定期扫描比如每1ms一次 void TIMER2_IRQHandler(void) { static bool last_A1 1, last_B1 1; if (timer_interrupt_flag_get(TIMER2, TIMER_INT_FLAG_UP)) { timer_interrupt_flag_clear(TIMER2, TIMER_INT_FLAG_UP); uint16_t key_state SPI_ReadRegister(32); // 从FPGA读取按键/编码器状态 bool current_A1 (key_state 0x0400) ! 0; bool current_B1 (key_state 0x0800) ! 0; // 编码器1正反转判断A相B相90度相位差 if(!current_A1 last_A1 current_B1) encoder1_flag 2; // 反转 last_A1 current_A1; if(!current_B1 last_B1 current_A1) encoder1_flag 1; // 正转 last_B1 current_B1; // 处理8个独立按键消抖在另一个状态机中 for(uint8_t i 0; i 8; i) { key[i].current_state (key_state i) 0x01; key_debounce_state_machine(key[i]); // 调用消抖状态机 } } }4.3 菜单与控件任务调度为了程序结构清晰我采用了“任务函数”的思想。每个可操作的控件比如触发电平旋钮、通道开关按钮都对应一个任务函数。主循环依次调用这些任务函数检查是否有对应的事件发生比如编码器转动、按键按下并执行相应的操作更新寄存器、改变菜单焦点等。// 主循环非常简单 int main(void) { // 初始化各种外设 hardware_init(); // 初始化控件参数 ctrl_init(); while(1) { // 依次执行所有控件的任务函数 trigger_channel_task(); vertical_scale_task(); horizontal_scale_task(); trigger_level_task(); // ... 其他控件任务 menu_display_task(); // 最后刷新显示 } } // 示例触发通道选择任务函数 void trigger_channel_task() { if(encoder1_flag 1) { // 编码器1正转 encoder1_flag 0; current_channel CH1; // 切换到通道1 SPI_WriteRegister(REG_TRIG_CH, current_channel); // 通知FPGA update_display_channel(); // 更新屏幕显示 } else if(encoder1_flag 2) { // 编码器1反转 encoder1_flag 0; current_channel CH2; // 切换到通道2 SPI_WriteRegister(REG_TRIG_CH, current_channel); update_display_channel(); } // 还可以响应按键事件... }5. 制作、调试与校准心得硬件焊好程序烧进去并不代表就能用了。调试和校准才是真正考验功夫的时候。5.1 硬件制作注意事项仔细看原理图有些元件旁边标注了“NC”不连接或“DNP”不焊接比如原作者提到的NE5532输出端的电容如果焊了可能导致运放振荡要根据实际情况决定。结构件上下两块PCB需要用25mm高的铜柱和贴片螺母固定。编码器要买侧插的别买成直插的不然装不进去。排线连接上下板的FPC排线是34Pin、0.5mm间距、同向的。如果买的线太长需要适当弯折不然会顶到外壳。5.2 软件校准让测量变准确这是最关键的一步。模拟电路不可能完全理想每个元件的误差都会导致测量不准。我们需要通过软件来补偿。在MCU程序中为每个电压档位定义了一个校准结构体typedef struct { uint16_t dac_gain_ctrl; // 控制AD603增益的DAC输出值 uint16_t vertical_offset; // 通道垂直偏移基线位置 bool attenuation_sel; // 继电器衰减选择 (0x1, 1x10) } Calibration; // 示例通道1的10个档位5mV/div 到 5V/div的校准参数 Calibration ch1_cali[10] { {600, 2330, 1}, // 5mV/div档 {900, 2435, 1}, // 10mV/div档 {1300, 2485, 1}, // 20mV/div档 {1850, 2520, 1}, // 50mV/div档 {2270, 2530, 1}, // 100mV/div档 {2675, 2530, 1}, // 200mV/div档 {1425, 2530, 0}, // 500mV/div档 (切换到x1衰减) {1840, 2535, 0}, // 1V/div档 {2270, 2540, 0}, // 2V/div档 {2740, 2535, 0} // 5V/div档 };校准步骤偏移校准将探头接地输入0V。调节vertical_offset这个值通过MCP4728改变输入运放的直流偏置让屏幕上的波形水平线对准屏幕中央的零刻度线。增益校准输入一个已知幅度的标准信号比如1V/div档下输入一个3Vpp的正弦波。调节dac_gain_ctrl这个值改变AD603的增益让波形的峰峰值正好占据屏幕上3格的高度。提示dac_gain_ctrl的值对应MCP4728的DAC输出范围是0-4095。attenuation_sel控制输入衰减继电器一般小电压档位用x10衰减大电压档位用x1衰减。5.3 资源与时序报告项目最后别忘了在FPGA开发工具里看一下综合报告确保资源够用时序能跑稳。资源用量这个设计在高云GW2A-LV18上占用资源不多还有很大余量可以添加更多功能比如FFT、数学运算等。时序收敛确保你的设计能满足100MHzADC采样时钟甚至更高的系统时钟要求。如果时序不满足需要优化代码或添加约束。6. 项目总结与资源这个基于立创逻辑派和FPGA的示波器项目到这里就解析完了。它不仅仅是一个能用的示波器更是一个绝佳的FPGAMCU异构系统学习平台。你可以在上面练习高速数据采集与实时处理的FPGA设计思路。状态机在复杂逻辑控制中的应用。SPI通信协议的FPGA从机与MCU主机实现。模拟电路与数字系统的协同设计与调试。嵌入式GUI菜单的设计与实现。正如原作者所说它比起商业示波器还很简陋但所有核心环节都已打通并且留下了巨大的优化和扩展空间。你可以尝试增加自动测量功能、改进触发算法、添加数字滤波甚至移植一个轻量级的嵌入式操作系统来管理任务。开源资料参考项目原工程立创开源硬件平台https://oshwhub.com/Alpha-go/a5IITjkVGF1cA9kyTV2V9sIHra7GcIg2另一个参考项目https://oshwhub.com/824366a/fpga-oscilloscope-824366a模拟前端设计参考OSCFUN开源示波器 http://oscfun.com/希望这个详细的解析能帮你理清思路。嵌入式开发就是这样从一个个具体的项目中去啃硬骨头知识自然就积累起来了。如果动手做了欢迎分享你的成果和遇到的坑我们一起交流进步。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2420586.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…

网络编程(Modbus进阶)

思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板&#xff0c;载入页面后&#xff0c;会显示引导弹窗&#xff0c;适用于引导用户使用页面&#xff0c;点击完成后&#xff0c;会显示下一个引导弹窗&#xff0c;直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…

接口测试中缓存处理策略

在接口测试中&#xff0c;缓存处理策略是一个关键环节&#xff0c;直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性&#xff0c;避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明&#xff1a; 一、缓存处理的核…

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…