20260120 - Linux驱动学习笔记:SPI子系统核心层到具体硬件驱动

详细追踪从spi.c中的函数接口spi_write()spi-imx.c中具体硬件操作的完整调用链

完整的函数调用链

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 第1步:应用层/设备驱动调用 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━// oled_drv.c - 具体的字符设备驱动程序staticvoidspi_write_datas(constunsignedchar*buf,intlen){spi_write(oled,buf,len);// ↑// oled 是 struct spi_device*}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 第2步:SPI 核心层-spi_write()-spi.c 作为核心层 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━// drivers/spi/spi.cintspi_write(structspi_device*spi,constvoid*buf,size_tlen){structspi_transfert={.tx_buf=buf,.len=len,};returnspi_sync_transfer(spi,&t,1);// ↓}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 第3步:SPI 核心层-spi_sync_transfer()━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━// drivers/spi/spi.cintspi_sync_transfer(structspi_device*spi,structspi_transfer*xfers,unsignedintnum_xfers){structspi_messagemsg;// 初始化消息spi_message_init(&msg);// 将 transfer 加入消息spi_message_add_tail(&xfers[0],&msg);// ↓// msg.transfers 链表中有一个 transferreturnspi_sync(spi,&msg);// ↓}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 第4步:SPI 核心层-spi_sync()━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━// drivers/spi/spi.cintspi_sync(structspi_device*spi,structspi_message*message){intret;mutex_lock(&spi->master->bus_lock_mutex);ret=__spi_sync(spi,message);// ↓mutex_unlock(&spi->master->bus_lock_mutex);returnret;}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 第5步:SPI 核心层-__spi_sync()━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━// drivers/spi/spi.cstaticint__spi_sync(structspi_device*spi,structspi_message*message){DECLARE_COMPLETION_ONSTACK(done);intstatus;structspi_master*master=spi->master;// ↑// 获取对应的 spi_master// 这个 master 是 spi-imx.c 创建的// 验证消息status=__spi_validate(spi,message);if(status!=0)returnstatus;// 设置完成回调message->complete=spi_complete;message->context=&done;message->spi=spi;// 关键判断:新方法还是老方法if(master->transfer==spi_queued_transfer){// ↑// master->transfer 在 spi_register_master() 时设置的// 如果 spi-imx.c 没有实现 master->transfer// 内核会自动设置为 spi_queued_transfer// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━// 新方法(队列化传输)- 大多数驱动走这里// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━spin_lock_irqsave(&master->bus_lock_spinlock,flags);status=__spi_queued_transfer(spi,message,false);// ↓spin_unlock_irqrestore(&master->bus_lock_spinlock,flags);if(status==0){// 在当前上下文中处理(优化)__spi_pump_messages(master,false);// ↓// 等待完成wait_for_completion(&done);status=message->status;}}else{// 老方法(直接调用驱动的 transfer 函数)status=spi_async_locked(spi,message);}returnstatus;}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 第6步:SPI 核心层-__spi_queued_transfer()━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━// drivers/spi/spi.cstaticint__spi_queued_transfer(structspi_device*spi,structspi_message*msg,bool need_pump){structspi_master*master=spi->master;unsignedlongflags;spin_lock_irqsave(&master->queue_lock,flags);// 检查 master 是否运行if(!master->running){spin_unlock_irqrestore(&master->queue_lock,flags);return-ESHUTDOWN;}// 初始化消息状态msg->actual_length=0;msg->status=-EINPROGRESS;// 将消息加入队列list_add_tail(&msg->queue,&master->queue);// ↑// 消息现在在队列中等待处理// 如果需要,唤醒工作线程if(!master->busy&&need_pump)kthread_queue_work(&master->kworker,&master->pump_messages);spin_unlock_irqrestore(&master->queue_lock,flags);return0;}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 第7步:SPI 核心层-__spi_pump_messages()━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━// drivers/spi/spi.cstaticvoid__spi_pump_messages(structspi_master*master,bool in_kthread){unsignedlongflags;bool was_busy=false;intret;// 获取队列中的第一条消息spin_lock_irqsave(&master->queue_lock,flags);if(list_empty(&master->queue)||!master->running){master->busy=false;spin_unlock_irqrestore(&master->queue_lock,flags);return;}// 取出消息master->cur_msg=list_first_entry(&master->queue,structspi_message,queue);list_del_init(&master->cur_msg->queue);master->busy=true;spin_unlock_irqrestore(&master->queue_lock,flags);// 准备硬件(如果定义了)if(!was_busy&&master->prepare_transfer_hardware){ret=master->prepare_transfer_hardware(master);// ↓// 这可能调用到 spi-imx.c 的函数}// 准备消息(如果定义了)if(master->prepare_message){ret=master->prepare_message(master,master->cur_msg);// ↓// 这可能调用到 spi-imx.c 的函数}// 关键:传输消息if(master->transfer_one_message){// 方式A:一次传输整个 messageret=master->transfer_one_message(master,master->cur_msg);// ↓// 这会调用到 spi-imx.c 的函数!}else{// 方式B:逐个传输 transfer(大多数驱动用这个)ret=spi_transfer_one_message(master,master->cur_msg);// ↓}}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 第8步:SPI 核心层-spi_transfer_one_message()━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━// drivers/spi/spi.cstaticintspi_transfer_one_message(structspi_master*master,structspi_message*msg){structspi_transfer*xfer;intret=0;// 设置片选spi_set_cs(msg->spi,true);// 遍历消息中的所有 transferlist_for_each_entry(xfer,&msg->transfers,transfer_list){// ↑// msg->transfers 链表中的每个 transfer// 传输单个 transferret=master->transfer_one(master,msg->spi,xfer);// ↑// 关键!这里调用 spi-imx.c 的 transfer_one 函数// ↓// ↓// 到达 spi-imx.c!if(ret<0){// 传输失败spi_transfer_one_message_failed(master,msg,ret);returnret;}// 如果需要延迟if(xfer->delay_usecs)udelay(xfer->delay_usecs);// 如果需要改变片选if(xfer->cs_change){spi_set_cs(msg->spi,false);udelay(10);spi_set_cs(msg->spi,true);}}// 释放片选spi_set_cs(msg->spi,false);// 完成消息msg->status=0;spi_finalize_current_message(master);return0;}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 第9步:SPI Master 驱动-spi_imx_transfer_one()━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━// drivers/spi/spi-imx.cstaticintspi_imx_transfer_one(structspi_master*master,structspi_device*spi,structspi_transfer*transfer){structspi_imx_data*spi_imx=spi_master_get_devdata(master);unsignedlonghz_per_byte,byte_limit;// 配置传输参数spi_imx->speed_hz=transfer->speed_hz;spi_imx->bits_per_word=transfer->bits_per_word;spi_imx->count=transfer->len;spi_imx->tx_buf=transfer->tx_buf;spi_imx->rx_buf=transfer->rx_buf;spi_imx->txfifo=0;// 重新初始化完成量reinit_completion(&spi_imx->xfer_done);// 配置 SPI 控制器寄存器spi_imx_config(spi_imx);// ↓// 写硬件寄存器// 根据传输大小和是否支持 DMA 选择传输方式if(spi_imx->usedma){// 使用 DMA 传输ret=spi_imx_dma_transfer(spi_imx,transfer);// ↓wait_for_completion(&spi_imx->dma_tx_completion);wait_for_completion(&spi_imx->dma_rx_completion);}else{// 使用 PIO(中断)传输// 启动传输(写 FIFO,触发中断)spi_imx_push(spi_imx);// ↓// 写数据到 TX FIFO// 启用中断// 等待传输完成wait_for_completion(&spi_imx->xfer_done);// ↑// 中断处理函数会调用 complete(&spi_imx->xfer_done)}return0;}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 第10步:硬件操作-spi_imx_config()━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━// drivers/spi/spi-imx.cstaticintspi_imx_config(structspi_imx_data*spi_imx){unsignedintctrl=MX51_ECSPI_CTRL_ENABLE;u32 clkdiv;// 计算时钟分频clkdiv=spi_imx_clkdiv_2(spi_imx->spi_clk,spi_imx->speed_hz);// 配置控制寄存器ctrl|=(spi_imx->bits_per_word-1)<<MX51_ECSPI_CTRL_BL_OFFSET;if(spi_imx->spi->mode&SPI_CPHA)ctrl|=MX51_ECSPI_CTRL_PHA;if(spi_imx->spi->mode&SPI_CPOL)ctrl|=MX51_ECSPI_CTRL_POL;// 写寄存器writel(ctrl,spi_imx->base+MX51_ECSPI_CTRL);// ↑// 直接写硬件寄存器!writel(clkdiv,spi_imx->base+MX51_ECSPI_CONFIG);return0;}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 第11步:硬件操作-spi_imx_push()━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━// drivers/spi/spi-imx.cstaticvoidspi_imx_push(structspi_imx_data*spi_imx){unsignedintburst_length;// 计算可以发送多少字节burst_length=spi_imx_get_fifosize(spi_imx)-spi_imx->txfifo;// 填充 TX FIFOwhile(spi_imx->txfifo<burst_length&&spi_imx->count){if(spi_imx->tx_buf){// 发送数据spi_imx->tx(spi_imx);// ↓// 根据字长调用不同的函数// 写数据到 TX FIFO 寄存器}else{// 发送 dummy 数据writel(0,spi_imx->base+MXC_CSPITXDATA);}spi_imx->txfifo++;}// 启用中断if(!spi_imx->usedma)spi_imx_intctrl(spi_imx,MXC_INT_TE|MXC_INT_RR);// ↑// 启用发送空和接收就绪中断}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 第12步:中断处理-spi_imx_isr()━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━// drivers/spi/spi-imx.cstaticirqreturn_tspi_imx_isr(intirq,void*dev_id){structspi_imx_data*spi_imx=dev_id;// 读取接收到的数据while(spi_imx->txfifo>0){// 从 RX FIFO 读取spi_imx->rx(spi_imx);// ↓// readl(spi_imx->base + MXC_CSPIRXDATA)spi_imx->txfifo--;}// 如果还有数据要发送if(spi_imx->count){spi_imx_push(spi_imx);// 继续发送returnIRQ_HANDLED;}// 传输完成if(spi_imx->txfifo==0){// 禁用中断spi_imx_intctrl(spi_imx,0);// 通知传输完成complete(&spi_imx->xfer_done);// ↑// 这会唤醒 spi_imx_transfer_one() 中的 wait_for_completion()}returnIRQ_HANDLED;}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 返回路径:层层返回 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━spi_imx_transfer_one()返回 ↓spi_transfer_one_message()继续下一个 transfer 或完成 ↓spi_finalize_current_message()调用完成回调 ↓spi_complete()被调用 ↓complete(&done)// __spi_sync() 中的完成量wait_for_completion(&done)返回// __spi_sync() 中__spi_sync()返回 ↓spi_sync()返回 ↓spi_sync_transfer()返回 ↓spi_write()返回 ↓spi_write_datas()返回// 你的代码

关键连接点

连接点1:spi_device → spi_master

// oled_drv.cstaticstructspi_device*oled;staticintspidev_probe(structspi_device*spi){oled=spi;// spi->master 指向 spi-imx.c 创建的 spi_master}// 调用时spi_write(oled,buf,len);// ↓// struct spi_master *master = oled->master;// ↓// 这个 master 就是 spi-imx.c 中的 master

连接点2:函数指针跳转

// spi-imx.c 注册时设置的staticintspi_imx_probe(structplatform_device*pdev){structspi_master*master;master=spi_alloc_master(...);// 设置函数指针master->transfer_one=spi_imx_transfer_one;// ↑// 这个函数的地址保存在 master 结构中spi_register_master(master);}// SPI 核心层调用时// drivers/spi/spi.cret=master->transfer_one(master,msg->spi,xfer);// ↑// 通过函数指针调用// 实际调用的是 spi_imx_transfer_one()

完整调用链图示

oled_drv.c:spi_write_datas() ↓ 调用 spi.c:spi_write() ↓ 调用 spi.c:spi_sync_transfer() ↓ 调用 spi.c:spi_sync() ↓ 调用 spi.c:__spi_sync() ↓ 调用 spi.c:__spi_queued_transfer() [消息入队] ↓ 返回 spi.c:__spi_pump_messages() [从队列取消息] ↓ 调用 spi.c:spi_transfer_one_message() [遍历 transfers] ↓ 调用 (函数指针) spi-imx.c:spi_imx_transfer_one() ← 进入硬件驱动! ↓ 调用 spi-imx.c:spi_imx_config() [配置硬件寄存器] ↓ 调用 spi-imx.c:spi_imx_push() [写 TX FIFO] ↓ 启用中断 ↓ wait_for_completion() [等待中断] ↑ complete() spi-imx.c:spi_imx_isr() [中断处理] ↓ 返回 spi-imx.c:spi_imx_transfer_one() 返回 ↓ 返回 spi.c:spi_transfer_one_message() 返回 ↓ 调用 spi.c:spi_finalize_current_message() ↓ 调用回调 spi.c:spi_complete() ↓ complete(&done) spi.c:__spi_sync() 的 wait_for_completion() 返回 ↓ 层层返回 oled_drv.c:spi_write_datas() 返回

数据结构关系

// 数据结构的关联关系structspi_device*oled{.master=&imx_spi_master,// 指向 spi-imx.c 创建的 master// ...}↓ 关联structspi_masterimx_spi_master{.transfer_one=spi_imx_transfer_one,// 函数指针.prepare_transfer_hardware=spi_imx_prepare_hardware,.unprepare_transfer_hardware=spi_imx_unprepare_hardware,// ...}↓ 实现// spi-imx.c 中的实际函数staticintspi_imx_transfer_one(...){...}staticintspi_imx_prepare_hardware(...){...}staticintspi_imx_unprepare_hardware(...){...}

总结

步骤函数文件说明
1spi_write_datas()oled_drv.c你的驱动调用
2spi_write()spi.cSPI 核心 API
3spi_sync_transfer()spi.c构造消息
4spi_sync()spi.c同步传输
5__spi_sync()spi.c核心同步逻辑
6__spi_queued_transfer()spi.c消息入队
7__spi_pump_messages()spi.c处理消息队列
8spi_transfer_one_message()spi.c遍历 transfers
9master->transfer_one()函数指针跳转到硬件驱动
10spi_imx_transfer_one()spi-imx.ci.MX 硬件传输
11spi_imx_config()spi-imx.c配置硬件
12spi_imx_push()spi-imx.c写 FIFO
13spi_imx_isr()spi-imx.c中断处理

关键点:从 SPI 核心层到具体硬件驱动的跳转是通过master->transfer_one函数指针实现的!

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

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

相关文章

从0到1成为大模型应用开发工程师:154万年薪岗位全解析

大模型应用开发工程师成为高薪职业&#xff08;154万年薪&#xff09;&#xff0c;因市场需求大而人才稀缺。这类工程师需掌握提示词工程、RAG、模型微调等技术&#xff0c;同时具备工程开发、AI理解和业务洞察的复合能力。文章提供分层学习路径和实战项目建议&#xff0c;帮助…

【物理应用】滑块-曲柄机构Matlab仿真

✅作者简介&#xff1a;热爱数据处理、建模、算法设计的Matlab仿真开发者。&#x1f34e;更多Matlab代码及仿真咨询内容点击 &#x1f517;&#xff1a;Matlab科研工作室&#x1f34a;个人信条&#xff1a;格物致知。&#x1f525; 内容介绍滑块 - 曲柄机构是机械传动领域最基础…

Serv-U+cpolar 让文件远程访问像连 Wi-Fi 一样简单

Serv-U 作为一款成熟的文件服务软件&#xff0c;核心功能围绕文件的共享与传输展开&#xff0c;支持 FTP/FTPS/SFTP 等多种协议&#xff0c;既能实现大文件断点续传&#xff0c;也能精细化分配用户权限&#xff0c;比如给普通员工只读权限、给管理人员修改权限&#xff0c;适配…

救命神器9个AI论文软件,自考学生轻松搞定毕业论文!

救命神器9个AI论文软件&#xff0c;自考学生轻松搞定毕业论文&#xff01; 自考论文写作的救星&#xff1a;AI工具如何帮你轻松应对 对于自考学生而言&#xff0c;撰写毕业论文是一项既复杂又耗时的任务。从选题、收集资料到撰写初稿、反复修改&#xff0c;每一步都可能让人感到…

【YOLO模型导出格式】大全

一行命令即可完成模型格式转换,了解每种格式的设计逻辑才能在实际部署中做出最佳选择。 YOLO模型在训练完成后,我们通常会将其从PyTorch格式导出为多种不同格式。这些格式不仅代表着不同的文件扩展名,更代表着为不同硬件平台和部署场景量身定做的优化策略。 从旨在最大化C…

【Science Advances】“安全可触”的低电压仿生人工肌肉,让机器人更柔、更轻、更安全

在机器人领域&#xff0c;刚性机器人虽然精度高&#xff0c;但在需要柔顺性、抗干扰性或高能效的复杂环境中往往力不从心。为此&#xff0c;科学家们致力于研发仿生机器人&#xff0c;尤其是模仿人体肌肉的“人工肌肉”。其中&#xff0c;电液致动器因具备与哺乳动物肌肉相媲美…

世界棋局:国家、巨头与文明的AI竞赛以及星链的最新发展

第三章&#xff1a;终极棋局&#xff1a;国家、巨头与文明的AI竞赛“当算力成为新军备&#xff0c;数据成为新疆域&#xff0c;星球级的智慧博弈已悄然布子。”在前两章&#xff0c;我们剖析了AI作为新物种的觉醒与其产业狩猎的逻辑。现在&#xff0c;让我们将视野拉升到星球尺…

【粉丝福利社】驾驭Gemini 3与Nano Banana:人人都是AI产品创客

你好&#xff0c;未来的创造者&#xff01; 2025 年&#xff0c;AI 编程已成爆发之势—— Cursor 年收入破 10 亿美元&#xff0c;斯坦福学生“不写一行代码”就能交作业…… 这背后&#xff0c;是一个明确的信号&#xff1a;编程的核心&#xff0c;正从“写代码”转向“定义需…

NLP技术视角下的论文优化:2026主流降重平台算法与效果深度横评 - 品牌观察员小捷

在AIGC检测算法全面升级的2026年,解决“哪个降重平台效果最好”的问题,已不再是简单的同义词替换(Synonym Replacement),而是涉及困惑度(Perplexity)对抗、语义重构(Semantic Refactoring)以及命名实体识别(…

如何下载Spring源码 - 详解

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

2. C语言核心语法 - 实践

2. C语言核心语法 - 实践2026-01-20 19:35 tlnshuju 阅读(0) 评论(0) 收藏 举报pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; fo…

Linux驱动学习:验证MasterDriverDevice三方匹配成功

实验日志&#xff1a; [root100ask:/proc/device-tree]# find -name "oled" ./soc/aips-bus02000000/spba-bus02000000/ecspi02008000/oled [root100ask:/proc/device-tree]# cd /root/ [root100ask:~]# insmod oled_drv.ko [ 119.745706] 100ask_spi_oled_drv spi0…

华为笔记本安装Ubuntu系统,声卡没有声音的处理

从网站:https://github.com/Smoren/huawei-ubuntu-sound-fix/,下载安装包,并安装即可

必看!AI架构师珍藏手册:1.5万字深度解析如何把AI关进确定性系统笼子

文章提出AI系统架构"四大生理系统"框架&#xff0c;将大模型从全能指挥官降级为心脏&#xff0c;系统逻辑接管决策。强调架构设计应遵循祛魅、解耦、归因三大法则&#xff0c;把概率性AI关进确定性系统。提供基于不确定性的技术选型指南&#xff0c;包含组件边界判定…

必收藏!基于模板-定理图谱的LLM数学推理增强技术,性能提升超乎想象!

本文提出了一种基于模板-定理图谱的数学推理增强框架&#xff0c;通过结构化关联问题模板与数学定理&#xff0c;模仿人类联想记忆机制&#xff0c;显著提升LLM在复杂数学问题上的推理能力。该方法利用LLM自动构建高质量知识图谱&#xff0c;设计高效检索与融合机制&#xff0c…

AES加密密钥安全存储、iOS设备管理实现方式Kafka能够实时收集、处理和分析用户行为数据,从而生成动态更新的用户画像AES加密密钥安全存储

AES加密密钥安全存储、iOS设备管理实现方式Kafka能够实时收集、处理和分析用户行为数据&#xff0c;从而生成动态更新的用户画像AES加密密钥安全存储 AES加密密钥安全存储、iOS设备管理实现方式Kafka能够在数据安全日益重要的今天&#xff0c;AES&#xff08;高级加密标准&…

ssm228图书预订 网上书城管理系统vue

目录系统概述核心功能模块技术实现创新点应用价值开发技术源码文档获取/同行可拿货,招校园代理 &#xff1a;文章底部获取博主联系方式&#xff01;系统概述 SSM228网上书城管理系统基于Vue.js前端框架与SSM&#xff08;SpringSpring MVCMyBatis&#xff09;后端架构开发&…

中石化加油卡兑换有隐藏玩法,闲置卡这样处理超划算 - 京顺回收

朋友小林前阵子收拾储物间,意外翻出三张闲置的中石化加油卡,卡里还有不少额度。可他平时开车少,放着怕过期。其实,像小林这样有闲置加油卡困扰的车主不在少数。2025年行业统计表明,超三成车主持有未用完的加油卡,…

【GPR回归预测】基于双向长短期记忆神经网络结合高斯过程回归(BiLSTM-GPR)的多变量回归预测 (多输入单输出)附Matlab代码

✅作者简介&#xff1a;热爱数据处理、建模、算法设计的Matlab仿真开发者。 &#x1f34e;更多Matlab代码及仿真咨询内容点击 &#x1f517;&#xff1a;Matlab科研工作室 &#x1f34a;个人信条&#xff1a;格物致知。 &#x1f525; 内容介绍 一、技术背景与核心目标 多变量…

App自动化测试环境搭建(详细版)

只做记录和注意点&#xff0c;详细内容不做解释 环境&#xff1a;winappium夜神模拟器python 需要用到的工具&#xff1a; 1.java JDK 2. node.js 3. Android SDK 4.Appium-Server 5.Appium-Python-Client 6.appium客户端 7.夜神安卓模拟器 1、java jdk安装 官网下载…