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

SPI子系统源码剖析--(2)Spi_Master驱动框架

1. spi_masterspi_master对应spi控制器是对引脚的管理同时可以通过cs引脚选择从设备发送消息2. SPI传输概述1.1 数据组织方式使用SPI传输时最小的传输单位是spi_transfer对于一个设备可以发起多个spi_transfer这些spi_transfer会放入一个spi_message里 内部通过struct list_head 结构体构成queue可以通过of_container()函数找到spi_transfer结构体的首地址进而调用spi_transfer指定tx_buf、rx_buf、len同一个SPI设备的spi_transfer使用spi_message来管理同一个SPI Master下的spi_message放在一个队列里所以反过来SPI传输的流程是这样的从spi_master的队列里取出每一个spi_message从spi_message的队列里取出一个spi_transfer处理spi_transfer1.2 SPI控制器数据结构有两套传输方法3. SPI传输两种方法的源码剖析第一种对于这种老方法的传输我们要去提供master-transfer()函数的具体实现。其次对于_spi_sync()执行完spi_async_locked()后就开始休眠等待消息发送完成内核线程会发送完一个一个的xfer结束之后就会产生中断 将其唤醒3.1 第一种方法的代码实现如下#include linux/module.h #include linux/kernel.h #include linux/sched.h #include linux/errno.h #include linux/timer.h #include linux/delay.h #include linux/list.h #include linux/workqueue.h #include linux/interrupt.h #include linux/platform_device.h #include linux/io.h #include linux/spi/spi.h static struct spi_master *g_virtual_master; static struct work_struct g_virtual_ws; static const struct of_device_id spi_virtual_dt_ids[] { { .compatible 100ask,virtual_spi_master, }, { /* sentinel */ } }; static void spi_virtual_work(struct work_struct *work) { struct spi_message *mesg; while (!list_empty(g_virtual_master-queue)) { mesg list_entry(g_virtual_master-queue.next, struct spi_message, queue); list_del_init(mesg-queue); /* 假装硬件传输已经完成 */ mesg-status 0; if (mesg-complete) mesg-complete(mesg-context); } } static int spi_virtual_transfer(struct spi_device *spi, struct spi_message *mesg) { #if 0 /* 方法1: 直接实现spi传输 */ /* 假装传输完成, 直接唤醒 status 要指定为已经结束传输否则会出错*/ mesg-status 0; mesg-complete(mesg-context); return 0; #else /* 方法2: 使用工作队列启动SPI传输、等待完成 */ /* 把消息放入队列 */ mesg-actual_length 0; mesg-status -EINPROGRESS; list_add_tail(mesg-queue, spi-master-queue); /* 启动工作队列 */ schedule_work(g_virtual_ws); /* 直接返回 */ return 0; #endif } static int spi_virtual_probe(struct platform_device *pdev) { struct spi_master *master; int ret; /* 分配/设置/注册spi_master */ g_virtual_master master spi_alloc_master(pdev-dev, 0); if (master NULL) { dev_err(pdev-dev, spi_alloc_master error.\n); return -ENOMEM; } // 设置传输函数在_sync_transfer中会调用 master-transfer spi_virtual_transfer; INIT_WORK(g_virtual_ws, spi_virtual_work); // 指定of_node节点否则master下的从设备无法构造出device // 把“设备树信息”从 platform_device 传递给 spi_master master-dev.of_node pdev-dev.of_node; ret spi_register_master(master); if (ret 0) { printk(KERN_ERR spi_register_master error.\n); spi_master_put(master); return ret; } return 0; } static int spi_virtual_remove(struct platform_device *pdev) { /* 反注册spi_master */ spi_unregister_master(g_virtual_master); return 0; } static struct platform_driver spi_virtual_driver { .probe spi_virtual_probe, .remove spi_virtual_remove, .driver { .name virtual_spi, .of_match_table spi_virtual_dt_ids, }, }; static int virtual_master_init(void) { return platform_driver_register(spi_virtual_driver); } static void virtual_master_exit(void) { platform_driver_unregister(spi_virtual_driver); } module_init(virtual_master_init); module_exit(virtual_master_exit); MODULE_DESCRIPTION(Virtual SPI bus driver); MODULE_LICENSE(GPL); MODULE_AUTHOR(www.100ask.net);注意 要给master指定of_node节点否则master下的从设备无法构造出device其次对于传输状态位status的操作。第二种方法总体调用关系在进入_syn_spi_register_master()之后调用关系如下下面是对上面的函数调用的仔细分析static int __spi_sync(struct spi_device *spi, struct spi_message *message) { DECLARE_COMPLETION_ONSTACK(done); // 设定完成量实现同步等待 int status; struct spi_master *master spi-master; unsigned long flags; status __spi_validate(spi, message); // 校验 SPI 消息的合法性 if (status ! 0) return status; message-complete spi_complete; //绑定回调函数 message-context done; //消息传输完成触发 spi_complete唤醒 done 完成量解除阻塞 message-spi spi; //内核 / 用户可通过统计信息监控 SPI 传输性能 SPI_STATISTICS_INCREMENT_FIELD(master-statistics, spi_sync); SPI_STATISTICS_INCREMENT_FIELD(spi-statistics, spi_sync); // 会在spi_register_master函数中进行判断初始化 if (master-transfer spi_queued_transfer) { // 队列式传输 bus_lock_spinlock是SPI 主控制器的总线锁避免多线程并发提交消息 spin_lock_irqsave(master-bus_lock_spinlock, flags); trace_spi_message_submit(message); // 将消息加入主控制器的传输队列不立即执行 status __spi_queued_transfer(spi, message, false); // 解锁恢复中断 spin_unlock_irqrestore(master-bus_lock_spinlock, flags); } else { status spi_async_locked(spi, message); } if (status 0) { /* Push out the messages in the calling context if we * can. */ if (master-transfer spi_queued_transfer) { SPI_STATISTICS_INCREMENT_FIELD(master-statistics, spi_sync_immediate); SPI_STATISTICS_INCREMENT_FIELD(spi-statistics, spi_sync_immediate); __spi_pump_messages(master, false); // “泵出”队列中的消息触发硬件传输 } /* 调用者会停在这里直到 * 硬件完成所有 SPI 消息的传输 * 触发 spi_complete 回调 * complete(done) 唤醒完成量 */ wait_for_completion(done); status message-status; } message-context NULL; return status; }_spi_pump_message()函数static void __spi_pump_messages(struct spi_master *master, bool in_kthread) { unsigned long flags; bool was_busy false; int ret; /* Lock queue */ spin_lock_irqsave(master-queue_lock, flags); /* Make sure we are not already running a message 指向当前正在处理的 SPI 消息*/ if (master-cur_msg) { spin_unlock_irqrestore(master-queue_lock, flags); return; } /* If another context is idling the device then defer */ if (master-idling) { kthread_queue_work(master-kworker, master-pump_messages); spin_unlock_irqrestore(master-queue_lock, flags); return; } /* Check if the queue is idle */ if (list_empty(master-queue) || !master-running) { if (!master-busy) { // 控制器完全空闲 → 直接退出 spin_unlock_irqrestore(master-queue_lock, flags); return; } /* Only do teardown in the thread 非线程上下文 → 提交任务到专属 kworker 线程*/ //收尾操作释放内存、硬件反初始化是耗时且可能休眠的 if (!in_kthread) { kthread_queue_work(master-kworker, master-pump_messages); spin_unlock_irqrestore(master-queue_lock, flags); return; } // 执行控制器收尾清理 master-busy false; master-idling true; spin_unlock_irqrestore(master-queue_lock, flags); kfree(master-dummy_rx); master-dummy_rx NULL; kfree(master-dummy_tx); master-dummy_tx NULL; // 反初始化 SPI 硬件 比如关闭 SPI 时钟、释放片选引脚、重置硬件寄存器 if (master-unprepare_transfer_hardware master-unprepare_transfer_hardware(master)) dev_err(master-dev, failed to unprepare transfer hardware\n); // 标记 SPI 控制器最后一次忙碌的时间然后调触发“自动挂起”仅保留必要的寄存器供电 if (master-auto_runtime_pm) { pm_runtime_mark_last_busy(master-dev.parent); pm_runtime_put_autosuspend(master-dev.parent); } trace_spi_master_idle(master); spin_lock_irqsave(master-queue_lock, flags); master-idling false; // 标记“空闲化处理”完成 spin_unlock_irqrestore(master-queue_lock, flags); return; } /* Extract head of queue */ master-cur_msg list_first_entry(master-queue, struct spi_message, queue); list_del_init(master-cur_msg-queue); if (master-busy)//记录并更新忙状态 was_busy true; else master-busy true; spin_unlock_irqrestore(master-queue_lock, flags); mutex_lock(master-io_mutex); if (!was_busy master-auto_runtime_pm) { ret pm_runtime_get_sync(master-dev.parent); //唤醒SPI控制器并上电 if (ret 0) { dev_err(master-dev, Failed to power device: %d\n, ret); mutex_unlock(master-io_mutex); return; } } //如果控制器从空闲转忙, 首次传输前设置硬件即可 if (!was_busy) trace_spi_master_busy(master); if (!was_busy master-prepare_transfer_hardware) { ret master-prepare_transfer_hardware(master); if (ret) { dev_err(master-dev, failed to prepare transfer hardware\n); if (master-auto_runtime_pm) pm_runtime_put(master-dev.parent); mutex_unlock(master-io_mutex); return; } } trace_spi_message_start(master-cur_msg); // SPI 控制器的单消息个性化配置函数调整传输速率、数据位宽8 位 / 16 位 if (master-prepare_message) { ret master-prepare_message(master, master-cur_msg); if (ret) { dev_err(master-dev, failed to prepare message: %d\n, ret); master-cur_msg-status ret; spi_finalize_current_message(master); goto out; } master-cur_msg_prepared true; } // 内核核心函数将 SPI 消息的缓冲区映射为硬件可访问的地址如 DMA 物理地址 // 解决 “内核虚拟地址硬件无法直接访问” 的问题 ret spi_map_msg(master, master-cur_msg); if (ret) { master-cur_msg-status ret; spi_finalize_current_message(master); goto out; } // SPI 控制器驱动必须实现的硬件传输核心函数 // 连接 “软件逻辑” 到 “硬件操作” ret master-transfer_one_message(master, master-cur_msg); if (ret) { dev_err(master-dev, failed to transfer one message from queue\n); goto out; } out: mutex_unlock(master-io_mutex); /* Prod the scheduler in case transfer_one() was busy waiting “如果 transfer_one_message 是‘忙等’实现就触发调度器让步” 传输成功 让出 CPU让其他线程 / 进程运行” */ if (!ret) cond_resched(); }transfer_one_message() 会在这个spi_master_initialize_queue中对master-transfer_one_message spi_transfer_one_message;赋值进而调用下面的函数spi_transfer_one_message()/* * spi_transfer_one_message - Default implementation of transfer_one_message() * * This is a standard implementation of transfer_one_message() for * drivers which implement a transfer_one() operation. It provides * standard handling of delays and chip select management. */ static int spi_transfer_one_message(struct spi_master *master, struct spi_message *msg) { struct spi_transfer *xfer; bool keep_cs false; int ret 0; unsigned long long ms 1; struct spi_statistics *statm master-statistics; // 主控制器的统计信息总传输量 struct spi_statistics *stats msg-spi-statistics; // 当前SPI设备的统计信息 // 1. 选中目标SPI设备 spi_set_cs(msg-spi, true); SPI_STATISTICS_INCREMENT_FIELD(statm, messages); // 主控制器的“总消息数”1 SPI_STATISTICS_INCREMENT_FIELD(stats, messages); // 遍历当前 spi_message 中的所有 spi_transfer 节点 // 注意: 使用struct list_head 链接整个链表通过container_of反推结构体地址 list_for_each_entry(xfer, msg-transfers, transfer_list) { trace_spi_transfer_start(msg, xfer); spi_statistics_add_transfer_stats(statm, xfer, master); spi_statistics_add_transfer_stats(stats, xfer, master); if (xfer-tx_buf || xfer-rx_buf) { //重置SPI 控制器的 “完成量” 状态 前提是要先初始化 reinit_completion(master-xfer_completion); // 调用 SPI 控制器驱动的 transfer_one 函数为 Bitbang 模式 // 若为硬件 SPI 模式这里会把 tx_buf 的物理地址写入DMA寄存器 ret master-transfer_one(master, msg-spi, xfer); if (ret 0) { SPI_STATISTICS_INCREMENT_FIELD(statm, errors); SPI_STATISTICS_INCREMENT_FIELD(stats, errors); dev_err(msg-spi-dev, SPI transfer failed: %d\n, ret); goto out; } //ret 0异步传输 if (ret 0) { ret 0; // 计算“理论传输时间”毫秒 // 公式(8位/字节 * 1000毫秒/秒 * 传输字节数) / SPI速率Hz ms 8LL * 1000LL * xfer-len; do_div(ms, xfer-speed_hz); ms ms 200; /* some tolerance */ // 加 2 倍理论时间 200ms 容错是为了兼容硬件延迟 if (ms UINT_MAX) ms UINT_MAX; // 异步等待 ms wait_for_completion_timeout(master-xfer_completion, msecs_to_jiffies(ms)); } if (ms 0) { SPI_STATISTICS_INCREMENT_FIELD(statm, timedout); SPI_STATISTICS_INCREMENT_FIELD(stats, timedout); dev_err(msg-spi-dev, SPI transfer timed out\n); msg-status -ETIMEDOUT; } } else { if (xfer-len) dev_err(msg-spi-dev, Bufferless transfer has length %u\n, xfer-len); } trace_spi_transfer_stop(msg, xfer); if (msg-status ! -EINPROGRESS) goto out; // 微秒级延迟 if (xfer-delay_usecs) udelay(xfer-delay_usecs); if (xfer-cs_change) { if (list_is_last(xfer-transfer_list, msg-transfers)) { keep_cs true; } else { // 在两个 transfer 之间产生“帧边界” 让设备知道这是两次独立通信 spi_set_cs(msg-spi, false); udelay(10); spi_set_cs(msg-spi, true); } } msg-actual_length xfer-len; } out: if (ret ! 0 || !keep_cs) spi_set_cs(msg-spi, false); // 正常传输时 msg-status 初始是 -EINPROGRESS 正在进行中 if (msg-status -EINPROGRESS) msg-status ret; if (msg-status master-handle_err) master-handle_err(master, msg); spi_res_release(master, msg); // 清空 master-cur_msg 重置完成量 保证下一次传输时状态干净。 spi_finalize_current_message(master); return ret; }按照上面的函数调用transfer_one()函数会在 spi-bitbang.c 中的spi_bitbang_start()函数中进行与spi_bitbang_transfer_one()函数绑定static int spi_bitbang_transfer_one(struct spi_master *master, struct spi_device *spi, struct spi_transfer *transfer) { struct spi_bitbang *bitbang spi_master_get_devdata(master); int status 0; //根据这个 spi_device 和 transfer 的参数 配置本次传输环境 if (bitbang-setup_transfer) { status bitbang-setup_transfer(spi, transfer); if (status 0) goto out; } if (transfer-len) status bitbang-txrx_bufs(spi, transfer); if (status transfer-len) status 0; else if (status 0) status -EREMOTEIO; out: spi_finalize_current_transfer(master); //调用complete(master-xfer_completion); return status; }3.2 第二种方法的代码实现如下#include linux/module.h #include linux/kernel.h #include linux/sched.h #include linux/errno.h #include linux/timer.h #include linux/delay.h #include linux/list.h #include linux/workqueue.h #include linux/interrupt.h #include linux/platform_device.h #include linux/io.h #include linux/spi/spi.h #include linux/spi/spi_bitbang.h static struct spi_master *g_virtual_master; static struct spi_bitbang *g_virtual_bitbang; static struct completion g_xfer_done; static const struct of_device_id spi_virtual_dt_ids[] { { .compatible 100ask,virtual_spi_master, }, { /* sentinel */ } }; /* xxx_isr() { complete(g_xfer_done) } */ static int spi_virtual_transfer(struct spi_device *spi, struct spi_transfer *transfer) { int timeout; #if 1 /* 1. init complete */ reinit_completion(g_xfer_done); /* 2. 启动硬件传输 */ complete(g_xfer_done); /* 3. wait for complete */ timeout wait_for_completion_timeout(g_xfer_done, 100); if (!timeout) { dev_err(spi-dev, I/O Error in PIO\n); return -ETIMEDOUT; } #endif return transfer-len; } static void spi_virtual_chipselect(struct spi_device *spi, int is_on) { } static int spi_virtual_probe(struct platform_device *pdev) { struct spi_master *master; int ret; /* 分配/设置/注册spi_master */ g_virtual_master master spi_alloc_master(pdev-dev, sizeof(struct spi_bitbang)); if (master NULL) { dev_err(pdev-dev, spi_alloc_master error.\n); return -ENOMEM; } // 从 master里取出这个 bitbang控制器对应的私有结构体 g_virtual_bitbang spi_master_get_devdata(master); init_completion(g_xfer_done); /* 怎么设置spi_master? * 1. spi_master使用默认的函数 * 2. 分配/设置 spi_bitbang结构体: 主要是实现里面的txrx_bufs函数 * 3. spi_master要能找到spi_bitbang */ g_virtual_bitbang-master master; g_virtual_bitbang-txrx_bufs spi_virtual_transfer; g_virtual_bitbang-chipselect spi_virtual_chipselect; master-dev.of_node pdev-dev.of_node; #if 0 ret spi_register_master(master); if (ret 0) { printk(KERN_ERR spi_register_master error.\n); spi_master_put(master); return ret; } #else // 包含了注册以及bitbang结构体中函数的初始化操作 ret spi_bitbang_start(g_virtual_bitbang); if (ret) { printk(bitbang start failed with %d\n, ret); return ret; } #endif return 0; } static int spi_virtual_remove(struct platform_device *pdev) { #if 0 /* 反注册spi_master */ spi_unregister_master(g_virtual_master); #endif spi_bitbang_stop(g_virtual_bitbang); spi_master_put(g_virtual_master); return 0; } static struct platform_driver spi_virtual_driver { .probe spi_virtual_probe, .remove spi_virtual_remove, .driver { .name virtual_spi, .of_match_table spi_virtual_dt_ids, }, }; static int virtual_master_init(void) { return platform_driver_register(spi_virtual_driver); } static void virtual_master_exit(void) { platform_driver_unregister(spi_virtual_driver); } module_init(virtual_master_init); module_exit(virtual_master_exit); MODULE_DESCRIPTION(Virtual SPI bus driver); MODULE_LICENSE(GPL); MODULE_AUTHOR(www.100ask.net);1. 在probe函数中分配 设置和注册spi_master, 需要注意的点在第一个方法中提过2. 要实现txrx_bufs() chipselect() 。两个必须的函数实现。

相关文章:

SPI子系统源码剖析--(2)Spi_Master驱动框架

1. spi_masterspi_master对应spi控制器,是对引脚的管理,同时可以通过cs引脚选择从设备发送消息2. SPI传输概述1.1 数据组织方式使用SPI传输时,最小的传输单位是"spi_transfer",对于一个设备,可以发起多个spi…...

速看!!安全员ABC证靠谱的查询方式有哪几种?分别是怎么查询呢?

很多人报考安全员都会有疑虑,担心自己考的安全员不是正规的,考出来没有用,不能在正规网站查询到,今天星禾智慧老师告诉您安全员ABC靠谱的查询方式,保证你拿到的证书不再有假🍎🧤一、湖北安全员A…...

软件综合项目-mqtt

依赖的第三方库https:CURL库SQLite:SQLite库MQTT库:Paho库MQTT属于应用层协议,支持其实现的传输协议为TCPHTTPS适用于传输的数据量比较大情况,传输方式为字符单向传输MQTT传输数量比较小,二进制传输&#x…...

2026人事系统排行榜:一体化+AI,11家企业谁是TOP选手?

2026年一体化AI人事系统TOP11深度评测:谁领跑AI原生时代?2026年,HR SaaS行业已全面迈入AI原生架构全链路一体化的竞争新阶段。企业对人系统的核心诉求,从“功能叠加AI”进化为“从底层架构融入AI”,要求AI能贯穿招聘、…...

ssm+java2026年毕设社区疫情管理系统【源码+论文】

本系统(程序源码)带文档lw万字以上 文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容一、选题背景关于社区公共卫生应急管理问题的研究,现有研究主要以宏观层面的城市公共卫生体系构建、重大疫情应急响应机制为主&…...

【亲测好用】数据权限管理能力演示

导言: 作为一名企业管理者或业务人员,您是否曾遇到过这样的烦恼:(1)销售人员看到了不该看的财务数据?(2)合作伙伴访问了超出约定范围的信息?(3)不…...

Paperzz AI 毕业论文写作:从选题到成文,本科论文高效交付的智能解决方案

Paperzz-AI官网免费论文查重复率AIGC检测/开题报告/文献综述/论文初稿paperzz - 毕业论文-AIGC论文检测-AI智能降重-ai智能写作https://www.paperzz.cc/dissertation 在本科毕业论文的创作路上,从确定选题到完成初稿,从文献梳理到格式规范,每…...

Paperzz AI 初稿引擎:重构本科毕业论文写作,从选题到终稿一站式高效通关

Paperzz-AI官网免费论文查重复率AIGC检测/开题报告/文献综述/论文初稿paperzz - 毕业论文-AIGC论文检测-AI智能降重-ai智能写作https://www.paperzz.cc/dissertation 引言 本科毕业论文的创作,是大学生学业生涯的收官之战,也是对专业知识与学术能力的综…...

泰思特电子分享_EMC测试电流探头选型差异性及影响因素探讨

本文主要进行EMC测试电流探头选型差异性及影响因素探讨,围绕电流探头核心技术指标进行介绍,根据EMC测试不同标准、不同测试项目对电流探头的需求差异,分析了电流探头选型的关键影响因素以及国内外主流厂家不同型号产品的对比,为EM…...

基于能量分配的光伏混合储能系统仿真模型 ①光伏:采用mppt控制实现最大功率跟踪 ②蓄电池与超...

基于能量分配的光伏混合储能系统仿真模型 ①光伏:采用mppt控制实现最大功率跟踪 ②蓄电池与超级电容:构成混合储能系统,电池实现连续功率供应,超级电容提供瞬态功率供应 ③拓扑:光伏DC/DC采用boost变换器,混…...

APM使用LUA脚本发送实现遥控器PWM信号输出CAN协议信号

需求:由于舵机是CAN总线舵机,需实现APM开源飞控遥控器输入PWM通道到CAN的发送。 方法1:修改APM固件源码,编译,运行,测试。实现复杂。 方法2:使用lua脚本。实现简单 目前采用方法2,使…...

LangGraph 核心概念

LangGraph是LangChain 生态的 “进阶编排框架”,是 AgentExecutor升级版,基于图结构解决复杂工作流 / 多智能体问题,兼容 LangChain 所有组件。AgentExecutor 是「单智能体固定循环执行器」,适合简单线性任务;LangGrap…...

零基础搭建免费IP代理池:从原理到实战的保姆级指南

在数据驱动型业务中,很多企业都会接触到“IP代理池”这一概念。尤其是在进行公开数据整合、市场信息监测等场景时,单一IP往往难以支撑持续稳定的请求需求,这时代理池就成为重要基础设施。但对于初学者来说,“搭建代理池”听起来复…...

努力学习了一辈子,突然发现学习没什么用了

从小,我是众人眼中的 “学习标兵”。到现在,每天一节法语,一篇英语阅读,依然雷打不动:但最近几个月,随着老杨的“眨眼猫会务智能体”中对报名、签到、查座、AI会务助理的全面 “AI化改造”,老杨…...

大模型的那点事儿

大模型参数调优完全指南:从模型选择到参数配置 作者:虾兵一号 发布时间:2026-03-17 关键词:大模型参数、模型选择、Temperature、Top P、推理参数、LLM调优 一、前言 在使用大模型 API 时,两个问题最让人头疼&#xf…...

python-web自动化-selenium(1)

目录 资源 驱动器下载流程 设置、创建启动浏览器 设置浏览器Options() 创建启动浏览器webdriver.Chrome() 完整代码 打开网页,关闭标签页,关闭浏览器 打开网址get() 关闭当前标签页close() 完整代码 最大化最小化 最大化maximize_window() 最…...

AI智能水库图像识别数据集 水面漂浮物识别 水面分割识别 河道护栏分割数据集 YOLO格式数据集第10573期

数据集文档数据集概览 本数据集为实例分割场景专用数据集,聚焦于水处理场景下的关键目标识别与分割任务,为工业视觉算法提供高质量标注数据支撑。项目内容类别数量及中文名称4类:水面、粗格栅1、粗格栅2、悬浮物数据总量100张数据集格式YOLO核…...

关于密码破解的方式

当重启虚拟机或者开启虚拟机时,当界面跳出时,快速将鼠标点进虚拟机中,按向下或者向上箭头防止界面跳转,并按如下步骤进行:1 在界面中选择第二个选项2 按e键进入如下界面,按向下向上键将光标移动到quiet单词后面&#x…...

SAP 系统配置、落地即用的《SAP 成本分摊循环配置清单》,包含事务码、主数据、循环结构、分配 / 分摊规则、计算公式、案例数据

SAP 系统配置、落地即用的《SAP 成本分摊循环配置清单》,包含事务码、主数据、循环结构、分配 / 分摊规则、计算公式、案例数据一、通用主数据(所有案例共用,先建好)1. 成本中心(标准示例)成本中心名称类型…...

基于MATLAB_SIMULINK_SIMSCAPE建模的用于组件尺寸的电动和混合动力飞机模型

基于MATLAB/SIMULINK/SIMSCAPE建模的用于组件尺寸的电动和混合动力飞机模型第一步:主脚本 (AircraftSizingMain.m) 在 MATLAB 中运行此脚本,它将定义参数并启动仿真。 matlab 编辑 1%% Aircraft Component Sizing Simulation Script 2% 适用于电动 (AE) …...

C 语言03:结构体——自定义数据类型的万能基石

结构体(struct)是 C 语言的核心自定义数据类型,用于将不同类型的数据(如姓名、年龄、日期)打包成一个整体,极大简化了复杂数据的管理。本文从定义到使用,极简解析结构体的核心用法。一、结构体类…...

SAP 成本分摊逻辑与案例(含具体数据)

SAP 成本分摊核心是通过 ** 分配(Allocation)与分摊(Assessment)** 两种循环,将间接成本中心归集的费用,按预设规则(统计指标、比例、作业量等)结转至直接成本中心、生产订单、内部订…...

C语言当中的字符函数

字符分类函数可以很好的帮助我们进行字符的分类其中头文件为<ctype.h>现在举个列子&#xff0c;进行大小写转换islower运用函数的代码int i0&#xff1b;char str【】“CBASJDHsfjaf”&#xff1b;char c;while(str[i]){cstr[i];if(islower(c))c-32;putchar(c)i;}远高于平…...

思特威SC1220IOT——为AI眼镜量身打造的超低功耗影像之心

导读: 随着AI大模型向端侧迁移,AI智能眼镜被视为继智能手机之后最具潜力的下一代个人计算平台。然而,要在极其有限的轻量化镜架中塞进高性能的“眼睛”,同时保证全天候续航与即时响应,对核心的CMOS图像传感器提出了严苛挑战。近日,国内技术先进的CMOS图像传感器供应商思特…...

算法设计与分析-习题8.3

目录 1.完成本节构造最优二叉查找树的例题中余下的计算。 2. a.算法OptimalBST的时间效率为什么是立方级的? b.算法 OptimalBST 的空间效率为什么是平方级的? 3.写一个线性时间算法的伪代码&#xff0c;来从根表中生成最优二叉查找树。 4.请设计一种在常量时间(每个求和…...

把数据交给松鼠,把安全留给自己(二):异地同步——把第二份数据放在灾害够不到的地方

2021年郑州暴雨&#xff0c;某科技公司办公室进水&#xff0c;服务器和放在同一房间的备份硬盘一起泡水&#xff0c;十年积累的研发数据全部损毁。 这不是孤例&#xff0c;而是无数中小企业灾备盲区的真实写照。 火灾、水灾、盗窃、雷击……这些“小概率”事件&#xff0c;一旦…...

【RaddbitMQ 概述】消息中间件核心概念

文章目录1. 前言2. 什么是 MQ2.1 同步通信2.2 异步通信3. MQ 的作用3.1 异步解耦3.2 流量削峰3.3 消息分发3.4 延迟通知4. 为什么选择 RabbitMQ4.1 Kafka4.2 RocketMQ4.3 RabbitMQ5. RabbitMQ介绍1. 前言 Rabbit&#xff0c;兔子的意思。 互联网行业很多公司&#xff0c;都喜…...

前端构建部署优化

前端构建部署优化&#xff1a;提升效率的关键策略 在当今快节奏的互联网开发中&#xff0c;前端构建和部署的效率直接影响产品的迭代速度和用户体验。随着项目规模扩大&#xff0c;构建时间变长、资源加载缓慢等问题逐渐凸显。如何通过优化手段提升构建部署效率&#xff0c;成…...

手把手教你部署Fun-ASR语音识别:Web界面操作,小白也能快速上手

手把手教你部署Fun-ASR语音识别&#xff1a;Web界面操作&#xff0c;小白也能快速上手 1. 引言 1.1 学习目标 今天咱们来聊聊一个特别实用的工具——Fun-ASR语音识别模型。你可能听说过语音识别&#xff0c;但总觉得这东西离自己很远&#xff0c;要么需要复杂的编程&#xf…...

LiuJuan20260223Zimage效果可视化:生成图分辨率、细节还原度、风格一致性实测报告

LiuJuan20260223Zimage效果可视化&#xff1a;生成图分辨率、细节还原度、风格一致性实测报告 1. 引言&#xff1a;当AI画笔遇见特定风格 你有没有想过&#xff0c;让AI帮你生成特定人物的图片&#xff0c;而且每次生成的效果都高度一致&#xff1f;这听起来像是为设计师或内…...