手撕I2C和SPI协议实现

news2025/5/24 4:39:03

手撕I2C和SPI协议实现

目录

  1. I2C协议原理
  2. I2C位操作实现
  3. I2C驱动代码编写
  4. SPI协议原理
  5. SPI位操作实现
  6. SPI驱动代码编写

I2C协议原理

I2C(Inter-Integrated Circuit)是一种串行通信总线,使用两根线:SCL(时钟线)和SDA(数据线)。

基本特性

  • 主从架构
  • 双向半双工通信
  • 每个设备都有唯一地址
  • 支持多主设备
  • 通信速率通常为100kHz(标准模式)、400kHz(快速模式)或1MHz以上(高速模式)

信号状态

  • 空闲状态:SCL和SDA均为高电平
  • 起始信号(START):SCL高电平时,SDA从高变低
  • 停止信号(STOP):SCL高电平时,SDA从低变高
  • 数据位:SCL低电平时,准备数据;SCL高电平时,采样数据
  • 应答信号(ACK):接收方在第9个时钟周期将SDA拉低表示接收成功

通信流程

  1. 主设备发送起始信号(START)
  2. 发送从设备地址(7位)和读/写位(1位)
  3. 从设备发送应答(ACK)
  4. 数据传输(8位一组),每组后跟应答位
  5. 主设备发送停止信号(STOP)

I2C位操作实现

首先需要实现基本的I2C底层函数:

GPIO配置

// 配置GPIO为开漏输出模式
void I2C_GPIO_Config(void) {
    // 使能GPIO时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
    
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;  // SCL: PB6, SDA: PB7
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;        // 开漏输出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
    
    // 空闲状态,均为高电平
    GPIO_SetBits(GPIOB, GPIO_Pin_6 | GPIO_Pin_7);
}

I2C基本操作函数

// SCL和SDA控制函数
#define SCL_H    GPIO_SetBits(GPIOB, GPIO_Pin_6)
#define SCL_L    GPIO_ResetBits(GPIOB, GPIO_Pin_6)
#define SDA_H    GPIO_SetBits(GPIOB, GPIO_Pin_7)
#define SDA_L    GPIO_ResetBits(GPIOB, GPIO_Pin_7)
#define SDA_READ GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_7)

// 延迟函数
void I2C_Delay(void) {
    uint8_t i = 10;  // 可根据时钟频率调整
    while(i--);
}

// 起始信号
void I2C_Start(void) {
    SDA_H;
    SCL_H;
    I2C_Delay();
    SDA_L;         // SDA从高到低,产生起始信号
    I2C_Delay();
    SCL_L;         // 钳住I2C总线,准备发送或接收数据
}

// 停止信号
void I2C_Stop(void) {
    SDA_L;
    SCL_H;
    I2C_Delay();
    SDA_H;         // SDA从低到高,产生停止信号
    I2C_Delay();
}

// 等待应答
uint8_t I2C_WaitAck(void) {
    uint8_t ack;
    
    SDA_H;         // 释放SDA
    I2C_Delay();
    SCL_H;         // 产生时钟脉冲
    I2C_Delay();
    ack = SDA_READ; // 读取SDA状态
    SCL_L;
    return ack;    // 返回0表示有应答
}

// 发送应答
void I2C_Ack(void) {
    SDA_L;         // SDA拉低,表示ACK
    I2C_Delay();
    SCL_H;
    I2C_Delay();
    SCL_L;
    SDA_H;         // 释放SDA
}

// 发送非应答
void I2C_NAck(void) {
    SDA_H;         // SDA保持高电平,表示NACK
    I2C_Delay();
    SCL_H;
    I2C_Delay();
    SCL_L;
}

// 发送一个字节
void I2C_SendByte(uint8_t byte) {
    uint8_t i = 8;
    
    while(i--) {
        SCL_L;
        I2C_Delay();
        if(byte & 0x80)
            SDA_H;
        else
            SDA_L;
        byte <<= 1;
        I2C_Delay();
        SCL_H;
        I2C_Delay();
    }
    SCL_L;
}

// 读取一个字节
uint8_t I2C_ReadByte(uint8_t ack) {
    uint8_t i = 8;
    uint8_t byte = 0;
    
    SDA_H;         // 释放SDA,准备读取数据
    while(i--) {
        byte <<= 1;
        SCL_L;
        I2C_Delay();
        SCL_H;
        I2C_Delay();
        if(SDA_READ)
            byte |= 0x01;
    }
    SCL_L;
    
    if(ack)
        I2C_Ack();  // 发送应答
    else
        I2C_NAck(); // 发送非应答
    
    return byte;
}

I2C驱动代码编写

基于上面的底层函数,实现设备读写操作:

// 写入一个字节到指定设备的指定寄存器
uint8_t I2C_WriteReg(uint8_t DevAddr, uint8_t RegAddr, uint8_t data) {
    I2C_Start();
    I2C_SendByte(DevAddr << 1);  // 设备地址 + 写位(0)
    if(I2C_WaitAck()) {
        I2C_Stop();
        return 1;  // 无应答,失败
    }
    
    I2C_SendByte(RegAddr);       // 寄存器地址
    if(I2C_WaitAck()) {
        I2C_Stop();
        return 1;
    }
    
    I2C_SendByte(data);          // 写入数据
    if(I2C_WaitAck()) {
        I2C_Stop();
        return 1;
    }
    
    I2C_Stop();
    return 0;  // 成功
}

// 从指定设备的指定寄存器读取一个字节
uint8_t I2C_ReadReg(uint8_t DevAddr, uint8_t RegAddr) {
    uint8_t data;
    
    I2C_Start();
    I2C_SendByte(DevAddr << 1);  // 设备地址 + 写位(0)
    if(I2C_WaitAck()) {
        I2C_Stop();
        return 0xFF;  // 无应答,失败
    }
    
    I2C_SendByte(RegAddr);       // 寄存器地址
    if(I2C_WaitAck()) {
        I2C_Stop();
        return 0xFF;
    }
    
    I2C_Start();                 // 重复起始
    I2C_SendByte((DevAddr << 1) | 0x01);  // 设备地址 + 读位(1)
    if(I2C_WaitAck()) {
        I2C_Stop();
        return 0xFF;
    }
    
    data = I2C_ReadByte(0);      // 读取数据,发送非应答
    I2C_Stop();
    
    return data;
}

实际应用示例:MPU6050读取数据

#define MPU6050_ADDR 0x68  // MPU6050设备地址

void MPU6050_Init() {
    I2C_WriteReg(MPU6050_ADDR, 0x6B, 0x00);  // 唤醒MPU6050
    I2C_WriteReg(MPU6050_ADDR, 0x19, 0x07);  // 采样率设置
    I2C_WriteReg(MPU6050_ADDR, 0x1A, 0x06);  // 配置数字低通滤波器
    I2C_WriteReg(MPU6050_ADDR, 0x1B, 0x18);  // 陀螺仪量程:±2000dps
    I2C_WriteReg(MPU6050_ADDR, 0x1C, 0x01);  // 加速度计量程:±2g
}

void MPU6050_GetAcceleration(int16_t *ax, int16_t *ay, int16_t *az) {
    uint8_t buf[6];
    
    // 读取加速度计数据
    buf[0] = I2C_ReadReg(MPU6050_ADDR, 0x3B);
    buf[1] = I2C_ReadReg(MPU6050_ADDR, 0x3C);
    buf[2] = I2C_ReadReg(MPU6050_ADDR, 0x3D);
    buf[3] = I2C_ReadReg(MPU6050_ADDR, 0x3E);
    buf[4] = I2C_ReadReg(MPU6050_ADDR, 0x3F);
    buf[5] = I2C_ReadReg(MPU6050_ADDR, 0x40);
    
    *ax = (buf[0] << 8) | buf[1];
    *ay = (buf[2] << 8) | buf[3];
    *az = (buf[4] << 8) | buf[5];
}

SPI协议原理

SPI(Serial Peripheral Interface)是一种同步串行通信接口,使用四根线:

基本特性

  • MOSI (Master Out Slave In):主设备发送,从设备接收
  • MISO (Master In Slave Out):主设备接收,从设备发送
  • SCK (Serial Clock):时钟信号,由主设备产生
  • SS/CS (Slave Select/Chip Select):片选信号,用于选择从设备

工作模式

SPI有四种工作模式,由CPOL(时钟极性)和CPHA(时钟相位)决定:

  • 模式0:CPOL=0, CPHA=0,空闲时SCK低电平,第一个边沿采样
  • 模式1:CPOL=0, CPHA=1,空闲时SCK低电平,第二个边沿采样
  • 模式2:CPOL=1, CPHA=0,空闲时SCK高电平,第一个边沿采样
  • 模式3:CPOL=1, CPHA=1,空闲时SCK高电平,第二个边沿采样

通信流程

  1. 主设备将对应从设备的CS线拉低(激活)
  2. 主设备通过SCK产生时钟信号
  3. 数据通过MOSI和MISO线同时双向传输
  4. 传输完成后,主设备将CS线拉高(释放)

SPI位操作实现

GPIO配置

// 配置SPI GPIO
void SPI_GPIO_Config(void) {
    // 使能GPIO时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    
    GPIO_InitTypeDef GPIO_InitStructure;
    
    // 配置SCK、MOSI为推挽输出
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_7;  // SCK: PA5, MOSI: PA7
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    
    // 配置MISO为浮空输入
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;  // MISO: PA6
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    
    // 配置CS为推挽输出
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;  // CS: PA4
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    
    // 初始状态:CS高,SCK低
    GPIO_SetBits(GPIOA, GPIO_Pin_4);   // CS高电平,不选中从设备
    GPIO_ResetBits(GPIOA, GPIO_Pin_5); // SCK低电平,模式0初始状态
}

SPI基本操作函数

// SPI引脚定义
#define SPI_CS_H   GPIO_SetBits(GPIOA, GPIO_Pin_4)
#define SPI_CS_L   GPIO_ResetBits(GPIOA, GPIO_Pin_4)
#define SPI_SCK_H  GPIO_SetBits(GPIOA, GPIO_Pin_5)
#define SPI_SCK_L  GPIO_ResetBits(GPIOA, GPIO_Pin_5)
#define SPI_MOSI_H GPIO_SetBits(GPIOA, GPIO_Pin_7)
#define SPI_MOSI_L GPIO_ResetBits(GPIOA, GPIO_Pin_7)
#define SPI_MISO   GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_6)

// 延迟函数
void SPI_Delay(void) {
    uint8_t i = 2;
    while(i--);
}

// SPI发送并接收一个字节(模式0)
uint8_t SPI_ReadWriteByte(uint8_t data) {
    uint8_t i;
    uint8_t temp = 0;
    
    for(i = 0; i < 8; i++) {
        // 准备发送数据
        if(data & 0x80)
            SPI_MOSI_H;
        else
            SPI_MOSI_L;
        data <<= 1;  // 左移一位,准备下一位
        SPI_Delay();
        
        SPI_SCK_H;   // 时钟上升沿,从设备采样MOSI
        SPI_Delay();
        
        temp <<= 1;  // 左移一位,为接收新的数据位腾出空间
        if(SPI_MISO)
            temp++;  // 如果MISO为高,则置1
            
        SPI_SCK_L;   // 时钟下降沿,主设备采样MISO
        SPI_Delay();
    }
    
    return temp;     // 返回接收到的数据
}

SPI驱动代码编写

基于上面的底层函数,实现设备读写操作:

// 向指定寄存器写入一个字节
void SPI_WriteReg(uint8_t reg, uint8_t value) {
    SPI_CS_L;                 // 使能片选
    SPI_ReadWriteByte(reg);   // 发送寄存器地址
    SPI_ReadWriteByte(value); // 发送数据
    SPI_CS_H;                 // 禁用片选
}

// 从指定寄存器读取一个字节
uint8_t SPI_ReadReg(uint8_t reg) {
    uint8_t value;
    
    SPI_CS_L;                   // 使能片选
    SPI_ReadWriteByte(reg | 0x80); // 发送寄存器地址(最高位置1表示读操作)
    value = SPI_ReadWriteByte(0xFF); // 发送任意值,读取结果
    SPI_CS_H;                   // 禁用片选
    
    return value;
}

// 从指定寄存器读取多个字节
void SPI_ReadMulti(uint8_t reg, uint8_t *buf, uint8_t len) {
    SPI_CS_L;                   // 使能片选
    SPI_ReadWriteByte(reg | 0x80); // 发送寄存器地址(最高位置1表示读操作)
    
    while(len--) {
        *buf = SPI_ReadWriteByte(0xFF);
        buf++;
    }
    
    SPI_CS_H;                   // 禁用片选
}

实际应用示例:读取W25Q64闪存

// W25Q64命令定义
#define W25Q64_READ_ID       0x90
#define W25Q64_READ_DATA     0x03
#define W25Q64_WRITE_ENABLE  0x06
#define W25Q64_PAGE_PROGRAM  0x02
#define W25Q64_ERASE_SECTOR  0x20
#define W25Q64_READ_STATUS   0x05

// 读取W25Q64芯片ID
uint16_t W25Q64_ReadID(void) {
    uint16_t id = 0;
    
    SPI_CS_L;
    SPI_ReadWriteByte(W25Q64_READ_ID);  // 发送读取ID命令
    SPI_ReadWriteByte(0x00);            // 发送3个虚拟地址
    SPI_ReadWriteByte(0x00);
    SPI_ReadWriteByte(0x00);
    id |= SPI_ReadWriteByte(0xFF) << 8; // 读取厂商ID
    id |= SPI_ReadWriteByte(0xFF);      // 读取设备ID
    SPI_CS_H;
    
    return id;
}

// 读取W25Q64状态寄存器
uint8_t W25Q64_ReadStatus(void) {
    uint8_t status;
    SPI_CS_L;
    SPI_ReadWriteByte(W25Q64_READ_STATUS);
    status = SPI_ReadWriteByte(0xFF);
    SPI_CS_H;
    return status;
}

// 等待W25Q64操作完成
void W25Q64_WaitBusy(void) {
    while((W25Q64_ReadStatus() & 0x01) == 0x01);
}

// 读取W25Q64数据
void W25Q64_ReadData(uint32_t addr, uint8_t *buf, uint16_t len) {
    SPI_CS_L;
    SPI_ReadWriteByte(W25Q64_READ_DATA);  // 发送读取命令
    SPI_ReadWriteByte((addr >> 16) & 0xFF); // 发送地址
    SPI_ReadWriteByte((addr >> 8) & 0xFF);
    SPI_ReadWriteByte(addr & 0xFF);
    
    while(len--) {
        *buf = SPI_ReadWriteByte(0xFF);
        buf++;
    }
    
    SPI_CS_H;
}

总结

I2C协议实现要点

  1. 使用开漏输出模式配置GPIO
  2. 实现起始、停止、发送、接收、应答等基本信号操作
  3. 按照协议时序编写读写函数
  4. 注意时钟速率控制和时序延迟

SPI协议实现要点

  1. 配置MOSI、SCK为输出,MISO为输入
  2. 确定使用的SPI模式(时钟极性和相位)
  3. 实现基本的读写字节函数
  4. 根据具体设备实现寄存器读写操作

注意事项

  1. 时序要严格遵循协议规范
  2. 延时函数需根据实际系统时钟频率调整
  3. 注意不同设备可能有特殊的地址或命令要求
  4. 调试时可以使用示波器观察信号波形
  5. 加入错误处理和超时机制提高鲁棒性

通过以上步骤,您可以实现对I2C和SPI协议的"手撕",即从底层位操作实现完整的通信协议,而不依赖于硬件外设。这种方式虽然占用CPU资源较多,但灵活性高,适用于不需要高速通信的场景或硬件外设不足的情况。

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

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

相关文章

人工智能+:职业价值的重构与技能升级

当“人工智能”成为产业升级的标配时&#xff0c;一个令人振奋的就业图景正在展开——不是简单的岗位替代&#xff0c;而是职业价值的重新定义。这场变革的核心在于&#xff0c;AI并非抢走工作机会&#xff0c;而是创造了人类与技术协作的全新工作范式。理解这一范式转换的逻辑…

JVM部分内容

1.JVM内存区域划分 为什么要划分内存区域&#xff0c;JAVA虚拟机是仿照真实的操作系统进行设计的&#xff0c;JVM也就仿照了它的情况&#xff0c;进行了区域划分的设计。 JAVA进程也就是JAVA虚拟机会从操作系统申请内存空间给进程使用&#xff0c;JVM内存空间划分&#xff0c…

python-leetcode 68.有效的括号

题目&#xff1a; 给定一个只包括“&#xff08;”)&#xff0c;{&#xff0c;}&#xff0c;[&#xff0c;] 的字符串 s &#xff0c;判断字符串是否有效。 有效字符串需满足&#xff1a;左括号必须用相同类型的右括号闭合&#xff1b;左括号必须以正确的顺序闭合&#xff0c…

NLP学习路线图(四):Python编程语言

引言 自然语言处理&#xff08;Natural Language Processing, NLP&#xff09;是人工智能领域最引人注目的分支之一。从智能客服到机器翻译&#xff0c;从舆情分析到聊天机器人&#xff0c;NLP技术正在重塑人机交互的边界。本文将结合Python编程语言&#xff0c;带您走进NLP的…

Serverless爬虫架构揭秘:动态IP、冷启动与成本优化

一、问题背景&#xff1a;旧技术的瓶颈 在传统爬虫架构中&#xff0c;我们通常部署任务在本地机器或虚拟机中&#xff0c;搭配定时器调度任务。虽然这种方式简单&#xff0c;但存在以下明显缺陷&#xff1a; 固定IP易被封禁&#xff1a;目标网站如拼多多会通过IP频率监控限制…

从单体到分布式:深入解析Data Mesh架构及其应用场景与价值

Data Mesh&#xff08;数据网格&#xff09;是一种新兴的数据架构范式&#xff0c;旨在解决传统集中式数据平台的可扩展性、敏捷性和治理问题。它强调领域驱动的分布式数据所有权、自助数据平台以及跨组织的协作&#xff0c;使数据成为产品&#xff0c;并通过去中心化的方式提高…

AI大模型ms-swift框架实战指南(十三):Agent智能体能力构建指南

系列篇章&#x1f4a5; No.文章1AI大模型ms-swift框架实战指南&#xff08;一&#xff09;&#xff1a;框架基础篇之全景概览2AI大模型ms-swift框架实战指南&#xff08;二&#xff09;&#xff1a;开发入门之环境准备3AI大模型ms-swift框架实战指南&#xff08;三&#xff09…

LLM最后怎么输出值 解码语言模型:从权重到概率的奥秘

LM Head Weights&#xff08;语言模型头部权重&#xff09;&#xff1a;左侧的“LM Head Weights”表示语言模型头部的权重矩阵&#xff0c;它是模型参数的一部分。权重矩阵与输入数据进行运算。Logits&#xff08;未归一化对数概率&#xff09;&#xff1a;经过与LM Head Weig…

Leetcode百题斩-回溯

回溯是一个特别经典的问题&#xff0c;也被排在了百题斩的第一部分&#xff0c;那么我们接下来来过一下这个系列。 这个系列一共八道题&#xff0c;偶然间发现我两年前还刷到这个系列的题&#xff0c;回忆起来当时刚经历淘系大变动与jf出走海外事件&#xff0c;大量同事离职闹…

超小多模态视觉语言模型MiniMind-V 训练

简述 MiniMind-V 是一个超适合初学者的项目&#xff0c;让你用普通电脑就能训一个能看图说话的 AI。训练过程就像教小孩&#xff1a;先准备好图文材料&#xff08;数据集&#xff09;&#xff0c;教它基础知识&#xff08;预训练&#xff09;&#xff0c;再教具体技能&#xf…

边缘云的定义、实现与典型应用场景!与传统云计算的区别!

一、什么是边缘云&#xff1f;‌ 边缘云是一种‌分布式云计算架构‌&#xff0c;将计算、存储和网络资源部署在‌靠近数据源或终端用户的网络边缘侧‌&#xff08;如基站、本地数据中心或终端设备附近&#xff09;&#xff0c;而非传统的集中式云端数据中心。 ‌核心特征‌&…

Scrapy爬取heima论坛所有页面内容并保存到MySQL数据库中

前期准备&#xff1a; Scrapy入门_win10安装scrapy-CSDN博客 新建 Scrapy项目 scrapy startproject mySpider # 项目名为mySpider 进入到spiders目录 cd mySpider/mySpider/spiders 创建爬虫 scrapy genspider heima bbs.itheima.com # 爬虫名为heima &#xff0c;爬…

com.alibaba.fastjson2 和com.alibaba.fastjson 区别

1&#xff0c;背景 最近发生了一件很奇怪的事&#xff1a;我们的服务向第三方发送请求参数时&#xff0c;第三方接收到的字段是首字母大写的 AppDtoList&#xff0c;但我们需要的是小写的 appDtoList。这套代码是从其他项目A原封不动复制过来的&#xff0c;我们仔细核对了项目…

了解Android studio 初学者零基础推荐(2)

在kotlin中编写条件语句 if条件语句 fun main() {val trafficLight "gray"if (trafficLight "red") {println("Stop!")} else if (trafficLight "green") {println("go!")} else if (trafficLight "yellow")…

C# 初学者的 3 种重构模式

(Martin Fowlers Example) 1. 积极使用 Guard Clause&#xff08;保护语句&#xff09; "如果条件不满足&#xff0c;立即返回。将核心逻辑放在最少缩进的地方。" 概念定义 Guard Clause&#xff08;保护语句&#xff09; 是一种在函数开头检查特定条件是否满足&a…

MySQL 数据类型深度全栈实战,天花板玩法层出不穷!

在 MySQL 数据库的世界里&#xff0c;数据类型是构建高效、可靠数据库的基石。选择合适的数据类型&#xff0c;不仅能节省存储空间&#xff0c;还能提升数据查询和处理的性能 目录 ​编辑 一、MySQL 数据类型总览 二、数值类型 三、字符串类型 四、日期时间类型 五、其他…

前端vscode学习

1.安装python 打开Python官网&#xff1a;Welcome to Python.org 一定要点PATH&#xff0c;要不然要自己设 点击install now,就自动安装了 键盘winR 输入cmd 点击确定 输入python&#xff0c;回车 显示这样就是安装成功了 2.安装vscode 2.1下载软件 2.2安装中文 2.2.1当安…

Index-AniSora技术升级开源:动漫视频生成强化学习

B站升级动画视频生成模型Index-AniSora技术并开源&#xff0c;支持番剧、国创、漫改动画、VTuber、动画PV、鬼畜动画等多种二次元风格视频镜头一键生成&#xff01; 整个工作技术原理基于B站提出的 AniSora: Exploring the Frontiers of Animation Video Generation in the So…

ubuntu24.04+RTX5090D 显卡驱动安装

初步准备 Ubuntu默认内核太旧&#xff0c;用mainline工具安装新版&#xff1a; sudo add-apt-repository ppa:cappelikan/ppa sudo apt update && sudo apt full-upgrade sudo apt install -y mainline mainline list # 查看可用内核列表 mainline install 6.13 # 安装…

MATLAB贝叶斯超参数优化LSTM预测设备寿命应用——以航空发动机退化数据为例

原文链接&#xff1a;tecdat.cn/?p42189 在工业数字化转型的浪潮中&#xff0c;设备剩余寿命&#xff08;RUL&#xff09;预测作为预测性维护的核心环节&#xff0c;正成为数据科学家破解设备运维效率难题的关键。本文改编自团队为某航空制造企业提供的智能运维咨询项目成果&a…