单词间隔重复算法

news2024/11/15 13:32:05

间隔重复算法

理论背景

遗忘曲线是一种描述记忆遗忘率的模型,艾宾浩斯在其著作《记忆:实验心理学的贡献》中首次详细描述了遗忘曲线,他使用了一些无意义的字母组合作为记忆对象,通过在不同的时间间隔后检查记忆的遗忘程度,得出了这一遗忘曲线。如图所示,就是典型的遗忘曲线。艾宾浩斯指出信息的遗忘速度随时间而非线性递减,遗忘最初非常迅速,然后逐渐放缓。基于这一发现,他提出了间隔效应(Spacing Effect),将复习活动分散到不同的日子进行,相比集中在一天内复习,可以显著减少所需的复习次数,并提高长期记忆的效率。这一发现对后续的研究产生了深远影响,启发了对间隔重复技术深入研究和应用的兴趣。

遗忘曲线

间隔重复(Spaced Repetition)是一种利用心理学间隔效应,通过不断复习所学内容并逐步增加复习期间的间隔来提升效率的学习方法。间隔重复方法适用于多种学习情境,特别是学习者需要牢固记忆大量知识的情形,比如外语词汇的学习,尤其是在目标语言的基础词汇数量很大的时候。将间隔重复应用到学习上的观点最初见于塞西尔·阿莱克·梅斯(Cecil Alec Mace)1932的著作《学习心理学》中,首次提出复习周期的概念:“Acts of revision should be spaced in gradually increasing intervals, roughly intervals of one day, two days, four days, eight days, and so on.” 在2009年的一篇名为《Spaced Repetition For Efficient Learning》的文章中,Gwern Branwen详细介绍了间隔重复技术在实现高效学习过程中的重要性。这篇文章讨论了间隔重复技术的原理和应用,旨在帮助学习者更有效地掌握知识和提高记忆效率,而非简单地死记硬背。
复习周期

算法设计

本系统基于赫尔曼·艾宾浩斯(Hermann Ebbinghaus)遗忘曲线公式实现简易间隔重复模型,使用指数衰减模型Math.exp(-x)根据用户标识的单词状态(已掌握、认识、模糊、忘记)来动态调整复习间隔,以提高用户的学习效率。材料以抽认卡的形式呈现,正面是单词,背面是释义。用户可以通过翻转抽认卡的方式来检查自己的记忆情况,并选择相应的记忆状态。

算法实现流程:

  1. 查询需要复习的单词:从数据库中查询到今日需要复习的单词列表。
  2. 状态评估:用户选择单词记忆的相应状态,如已掌握、认识、模糊、忘记。
  3. 间隔调整:根据单词的新状态,计算调整因子,并更新复习间隔。
  4. 更新数据库:将新的复习间隔和状态保存到数据库中,为下次复习做准备。

具体的流程如图所示。

间隔重复算法流程图

调整因子的计算基于以下公式:

factor = e^{-forgettingRate}

其中,遗忘率forgettingRate与用户选择的单词记忆状态相关联,状态越好,遗忘率越低,调整因子越大,复习间隔相应延长。

每个单词根据用户的记忆表现被分类为以下四种状态,并分配相应的遗忘率:

  • 忘记(Forgotten):遗忘率设置为 0.5,表示记忆较差,需要短时间内复习。
  • 模糊(Blurry):遗忘率设置为 0.3,表示记忆有一定基础,但不够牢固。
  • 熟悉(Known):遗忘率设置为 0.1,表示记忆相对稳定,可以延长复习间隔。
  • 掌握(Mastered):遗忘率为 0.0,表示单词已被完全掌握,无需进一步复习。

复习间隔的动态调整根据每个单词当前的复习间隔和计算得到的调整因子,动态调整下一次复习的时间。具体实现步骤如下:对于每个需要复习的单词,根据其当前状态查找其复习间隔。使用从状态对应的遗忘率计算出的调整因子来修改复习间隔。更新单词的复习间隔和预计下次复习时间。

这样通过公式计算后,调整因子factor的值在e^{-0.5}e^{-0.0}之间,即0.6065到1.0之间,再将其乘上默认的复习间隔"30,180,720,1440,2880,5760,10080,21600"(单位:分钟),即可得到新的复习间隔,并更新间隔索引。

对于计算用户的遗忘曲线,对数函数有助于模拟记忆衰减的减缓速度,具体计算公式如下:

forgettingRate = log((minutesUntilNextReview / FORGETTING_RATE_ADJUSTMENT) + 1)

其中,FORGETTING_RATE_ADJUSTMENT=30.0为遗忘率调整常量固定值,目前不考虑引入单词的难度级别或者是用户的记忆能力等因素。minutesSinceLastReview是距离上次复习以来的时间间隔,minutesUntilNextReview是距离下次复习的时间间隔,二者单位都为分钟。

最后,根据计算出的结果,通过以下公式计算出用户的记忆保留率:

retentionRate = e^(-forgetingRate * (minutesSinceLastReview / minutesUntilNextReview))

计算后将结果保存在数据库中,用于后续计算用户整体的遗忘曲线。

具体后端代码

间隔算法

controller
WordReviewController.java 调用服务层实现的方法对算法进行调整

package com.example.englishhub.controller;

import com.example.englishhub.entity.LearningPlans;
import com.example.englishhub.entity.WordReview;
import com.example.englishhub.entity.WordReviewVO;
import com.example.englishhub.service.WordReviewService;
import com.example.englishhub.utils.Result;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;
import java.util.Map;

/**
 * @Author: hahaha
 * @Date: 2024/4/8 15:13
 */

@RestController
@RequestMapping("/wordReview")
@Tag(name = "单词复习管理")
public class WordReviewController {

    @Autowired
    private WordReviewService wordReviewService;

    /**
     * 调整复习
     * @body wordReview
     */
    @Operation(summary = "调整复习")
    @PostMapping("/adjust")
    public Result adjustReviewIntervals(@RequestBody Map<String, Integer> body) {
    Integer wordId = body.get("wordId");
    Integer wordBookId = body.get("wordBookId");
    Integer status = body.get("status");
//    System.out.println("wordId = " + wordId);
//    System.out.println("wordBookId = " + wordBookId);
//    System.out.println("status = " + status);
    wordReviewService.adjustReviewIntervals(wordId, wordBookId, status);
    Result result = new Result();
    result.success("调整复习成功");
    return result;
}

    /**
     * 获取当天需学单词
     * @param wordBookId 单词书ID
     * @param dailyNewWords 每日新学单词数
     * @param dailyReviewWords 每日复习单词数
     * @return 复习单词列表
     */
    @Operation(summary = "获取当天需学单词")
    @GetMapping("/getToday")
    public Result getToday(Integer wordBookId, Integer dailyNewWords, Integer dailyReviewWords) {
        Result result = new Result();
        List<WordReviewVO> wordReviews = wordReviewService.getWordsToday(wordBookId, dailyNewWords, dailyReviewWords);
        result.setData(wordReviews);
        result.success("获取当天复习单词成功");
        return result;
    }
}

service
WordReviewService.java 定义各个方法

package com.example.englishhub.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.example.englishhub.entity.LearningPlans;
import com.example.englishhub.entity.WordReview;
import com.example.englishhub.entity.WordReviewVO;

import java.util.List;

/**
 * @Author: hahaha
 * @Date: 2024/4/8 15:14
 */
public interface WordReviewService extends IService<WordReview> {

    void adjustReviewIntervals(Integer wordId, Integer wordBookId, Integer status);

    List<WordReviewVO> getWordsToday(Integer wordBookId, Integer dailyNewWords, Integer dailyReviewWords);

    List<WordReview> getAllWordsForUser(Integer userId);

    List<WordReview> getWordsByUserId();
}

mapper
WordReviewMapper.java

package com.example.englishhub.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.englishhub.entity.WordReview;
import com.example.englishhub.entity.WordReviewVO;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;

import java.util.List;

/**
 * <p>
 * 单词复习表 Mapper 接口
 * </p>
 * @Author: hahaha
 * @Date: 2024/4/8 15:14
 */

@Mapper
public interface WordReviewMapper extends BaseMapper<WordReview> {

    @Select("SELECT w.id, w.word, w.phonetic_uk, w.phonetic_us, w.definition," +
            " wd.audio_url, wd.video_url, wd.definition AS definitionDetail, wd.subtext" +
            " FROM word_relation wr" +
            " JOIN word w ON wr.word_id = w.id" +
            " JOIN words wd ON w.words_id = wd.id" +
            " LEFT JOIN word_review wrv ON wrv.word_id = w.id AND wrv.user_id = #{userId}" +
            " WHERE wr.word_book_id = #{wordBookId} AND wrv.id IS NULL" +
            " ORDER BY w.id ASC" +
            " LIMIT #{dailyNewWords}")
    List<WordReviewVO> getNewWordsToday(Integer userId, Integer wordBookId, Integer dailyNewWords);

    @Select("SELECT w.id, w.word, w.phonetic_uk, w.phonetic_us, w.definition," +
            " wd.audio_url, wd.video_url, wd.definition AS definitionDetail, wd.subtext" +
            " FROM word_review wr" +
            " JOIN word w ON wr.word_id = w.id" +
            " JOIN words wd ON w.words_id = wd.id" +
            " WHERE wr.user_id = #{userId} AND wr.word_book_id = #{wordBookId}" +
            " AND wr.next_review_time <= CURRENT_TIMESTAMP" +
            " ORDER BY wr.next_review_time ASC" +
            " LIMIT #{dailyReviewWords}")
    List<WordReviewVO> getReviewWordsToday(Integer userId, Integer wordBookId, Integer dailyReviewWords);
}

impl
WordReviewServiceImpl.java

package com.example.englishhub.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.englishhub.entity.ForgettingCurve;
import com.example.englishhub.entity.WordReview;
import com.example.englishhub.entity.WordReviewVO;
import com.example.englishhub.mapper.WordReviewMapper;
import com.example.englishhub.service.ForgettingCurveService;
import com.example.englishhub.service.LearningPlansService;
import com.example.englishhub.service.WordReviewService;
import com.example.englishhub.utils.JwtUtil;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.time.temporal.Temporal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Author: hahaha
 * @Date: 2024/4/8 15:18
 */

@Service
public class WordReviewServiceImpl extends ServiceImpl<WordReviewMapper, WordReview> implements WordReviewService {

    @Autowired
    private WordReviewMapper wordReviewMapper;

    @Autowired
    private HttpServletRequest request;

    @Override
    public void adjustReviewIntervals(Integer wordId, Integer wordBookId, Integer status) {
        String token = request.getHeader("token");
        String userId = JwtUtil.validateToken(token);
        QueryWrapper<WordReview> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("word_id", wordId);
        queryWrapper.eq("user_id", Integer.parseInt(userId));
        queryWrapper.eq("word_book_id", wordBookId);

        WordReview wordReview = this.getOne(queryWrapper);

        if (wordReview == null) {
            // 新学单词,使用默认的复习间隔并新增记录
            String defaultIntervals = "30,180,720,1440,2880,5760,10080,21600";
            List<Integer> intervals = Arrays.stream(defaultIntervals.split(","))
                    .map(Integer::parseInt)
                    .collect(Collectors.toList());

            wordReview = new WordReview();
            wordReview.setUserId(Integer.parseInt(userId));
            wordReview.setWordId(wordId);
            wordReview.setWordBookId(wordBookId);
            wordReview.setStatus(status);
            wordReview.setReviewIntervals(defaultIntervals);
            wordReview.setReviewIntervalIndex(0);

            // 计算调整因子
            double factor = calculateAdjustmentFactor(status);
            // 调整复习间隔,将intervals列表中的每个元素乘以调整因子,然后将结果转换为整数。
            List<Integer> adjustedIntervals = intervals.stream()
                    .map(interval -> (int) (interval * factor))
                    .collect(Collectors.toList());
            wordReview.setReviewIntervals(adjustedIntervals.stream()
                    .map(Object::toString)
                    .collect(Collectors.joining(",")));

            // 设置下次复习时间
            if (status == 4) { // 单词已掌握
                wordReview.setNextReviewTime(null);
            } else {
                int newIndex = 0;
                wordReview.setReviewIntervalIndex(newIndex);
                wordReview.setNextReviewTime(LocalDateTime.now().plusMinutes(adjustedIntervals.get(newIndex)));
            }
            this.save(wordReview);
        } else {
            List<Integer> intervals = Arrays.stream(wordReview.getReviewIntervals().split(","))
                    .map(Integer::parseInt)
                    .collect(Collectors.toList());

            if (status == 4) { // 单词已掌握
                // 不再进行复习
                wordReview.setNextReviewTime(null);
            } else {
                double factor = calculateAdjustmentFactor(status);
                // 调整复习间隔,将intervals列表中的每个元素乘以调整因子,然后将结果转换为整数。
                List<Integer> adjustedIntervals = intervals.stream()
                        .map(interval -> (int) (interval * factor))
                        .collect(Collectors.toList());
                // 更新复习间隔,将adjustedIntervals列表中的每个元素转换为字符串,然后使用逗号(,)将这些字符串连接起来。
                wordReview.setReviewIntervals(adjustedIntervals.stream()
                        .map(Object::toString)
                        .collect(Collectors.joining(",")));

                // 确保索引在合理范围内
                int newIndex = (wordReview.getReviewIntervalIndex() + 1) % adjustedIntervals.size();
                wordReview.setReviewIntervalIndex(newIndex);
                wordReview.setNextReviewTime(LocalDateTime.now().plusMinutes(adjustedIntervals.get(newIndex)));

                this.updateById(wordReview);
            }
        }
    }

    private WordReview getByWordId(Integer wordId) {
        QueryWrapper<WordReview> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("wordId", wordId);
        return this.getOne(queryWrapper);
    }


    @Override
    public List<WordReviewVO> getWordsToday(Integer wordBookId, Integer dailyNewWords, Integer dailyReviewWords) {
        // 调用mapper层方法获取今天的所有复习记录
        String token = request.getHeader("token");
        String userId = JwtUtil.validateToken(token);

        List<WordReviewVO> reviewWords = wordReviewMapper.getReviewWordsToday(Integer.parseInt(userId), wordBookId, dailyReviewWords);
        List<WordReviewVO> newWords = wordReviewMapper.getNewWordsToday(Integer.parseInt(userId), wordBookId, dailyNewWords);
        reviewWords.addAll(newWords);
        return reviewWords;
    }

    @Override
    public List<WordReview> getAllWordsForUser(Integer userId) {
//        String token = request.getHeader("token");
//        String userId = JwtUtil.validateToken(token);
        QueryWrapper<WordReview> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("userId", userId);
        return this.list(queryWrapper);
    }

    public List<WordReview> getWordsByUserId() {
        String token = request.getHeader("token");
        String userId = JwtUtil.validateToken(token);

        return getAllWordsForUser(Integer.parseInt(userId));
    }

    public double calculateAdjustmentFactor(int status) {
        double forgettingRate;
        switch (status) {
            case 1: // 忘记forgotten
                forgettingRate = 0.5;
                break;
            case 2: // 模糊 blurry
                forgettingRate = 0.3;
                break;
            case 3: // 熟悉 known
                forgettingRate = 0.1;
                break;
            default: // 已掌握 mastered
                forgettingRate = 0.0;
        }
        // 使用指数函数来计算调整因子,遗忘率越大,调整因子越小
        // 原型是e^(-x)
        return Math.exp(-forgettingRate);
    }

}

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

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

相关文章

论文学习_Getafix: learning to fix bugs automatically

1. 引言 研究背景:现代生产代码库极其复杂并且不断更新。静态分析器可以帮助开发人员发现代码中的潜在问题(在本文的其余部分中称为错误),这对于在这些大型代码库中保持高代码质量是必要的。虽然通过静态分析尽早发现错误是有帮助的,但修复这些错误的问题在实践中仍然主要…

浅谈化工厂环保管理的痛点、智慧环保的必要性及EHS系统的实现路径

在全球环保意识日益增强的背景下&#xff0c;化工厂作为工业领域的重要组成部分&#xff0c;其环保管理显得尤为重要。然而&#xff0c;化工厂在追求经济效益的同时&#xff0c;也面临着诸多环保管理的痛点。本文将围绕化工厂环保管理的痛点、化工厂为何需要智慧环保以及如何借…

一阶线性微分方程应用实例:并联RC电路恒定电流求解电压

对于并联RC电路&#xff0c;我们可以通过求解微分方程来找出电压 V(t)。 微分方程求解 我们开始于给定的表达式&#xff1a; 重写方程&#xff1a; 将方程的形式调整为标准的线性微分方程形式&#xff1a; 这是一个一阶线性微分方程&#xff0c;我们可以使用积分因子法来解…

ROS服务通信自定义srv

服务通信自定义srv 流程:创建ROS功能包按照固定格式创建srv文件编译配置文件编译生成中间文件 流程: srv 文件内的可用数据类型与 msg 文件一致&#xff0c;且定义 srv 实现流程与自定义 msg 实现流程类似&#xff0c;需查阅msg文件的可以浏览ROS话题通信流程自定义数据msg格式…

红日靶场----(三)2.漏洞利用

上期的通过一句话木马实现对目标主机的持久后门 我使用的是蚁剑&#xff0c;蚁剑安装及使用参考&#xff1a; 下载地址&#xff1a; GitHub - AntSwordProject/AntSword-Loader: AntSword 加载器 安装即使用&#xff1a; 1. 快速入门 语雀 通过YXCMS的后台GETSHELL 利用…

【测开能力提升-fastapi框架】介绍简单使用

0. 前期说明 立了很多flag(开了很多专题)&#xff0c;但坚持下来的没几个。也干了很多测试工作(起初是硬件(Acoustic方向)测试 - 业务功能测试 - 接口测试 - 平台功能测试 - 数据库测试 - py自动化测试 - 性能测试 - 嵌入式测试 - 到最后的python测试开发)&#xff0c;最终还是…

链表---头插法+尾插法

本博客介绍了单链表的实现&#xff0c;以及头插法尾插法的代码实现 1.定义一个结点类 class ListNode{int value;ListNode next;public ListNode(int value) {super();this.value value;}Overridepublic String toString() {return "ListNode{" "value" …

Android 自定义Edittext 和TextView 提示文字和填入内容不同的粗细组件

近期项目中又EditText 以及TextView 这两个组件需要用到提示文字 以及 填入文字要保持不同的粗细程度,所以记录一下 首先 是EditText 组件的自定义 BLEditText 继承的这个组件是一个三方的组件,可以在很大程度上减少drawable的编写,有兴趣的可以去相关的Git去看一下 点击查看,…

excel有条件提取单元格特定文本(筛选纯文字的单元格或含有数字的单元格、单元格提取不同的文本长度)

实际工作背景 需要对导出的银行流水中的数十个村以及对应的村小组进行分组统计&#xff0c;但是初始的表格中村和小组是混在一起的&#xff0c;如下图所示&#xff1a; 目的&#xff1a;将大树村和大树村小组名称分别筛选出来 1.观察发现&#xff0c;大树村小组的单元格第4…

构建与操作顺序栈

归纳编程学习的感悟, 记录奋斗路上的点滴, 希望能帮到一样刻苦的你! 如有不足欢迎指正! 共同学习交流! 🌎欢迎各位→点赞 👍+ 收藏⭐ + 留言​📝黑暗的笼罩更会凸显光明的可贵! 顺序栈是数据结构中栈(Stack)的一种具体实现方式,它使用一段地址连续的内存空间(通…

电子电气架构 --- 关于DoIP的一些闲思 下

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 屏蔽力是信息过载时代一个人的特殊竞争力,任何消耗你的人和事,多看一眼都是你的不对。非必要不费力证明自己,无利益不试图说服别人,是精神上的节…

空中交通新动能!2024深圳eVTOL展动力电池展区核心内容抢先看!

空中交通新动能&#xff01;2024深圳eVTOL展动力电池展区核心内容抢先看&#xff01; 关键词&#xff1a;2024深圳eVTOL展 动力电池 高能量密度电池 高性能电池材料 作为2024深圳eVTOL展重要组成部分&#xff0c;2024深圳eVTOL动力电池展将于9月23-25日在深圳坪山燕子湖国际会…

Mac的系统数据怎么删除 cleanmymac会乱删东西吗 cleanmymac有用吗

作为一款专业级的苹果电脑清理软件&#xff0c;CleanMyMac可以精准识别系统垃圾&#xff0c;有效防止Mac系统数据被误删。软件可以深入系统底层&#xff0c;清理无用的系统数据&#xff0c;优化苹果电脑设置&#xff0c;提升Mac系统性能。有关Mac的系统数据可以删吗&#xff0c…

基于用户画像及协同过滤算法的音乐推荐系统,采用Django框架、bootstrap前端,MySQL数据库

研究背景 随着互联网和移动设备的普及&#xff0c;音乐流媒体服务成为人们获取音乐的主要方式。这些平台如Spotify、Apple Music和网易云音乐等&#xff0c;提供了海量的音乐资源&#xff0c;使用户能够随时随地聆听各种类型的音乐。然而&#xff0c;如何在海量的音乐资源中快…

笔试算法刷题

猿辅导2021校园招聘笔试&#xff08;算法一&#xff09; 牛客网 - 找工作神器|笔试题库|面试经验|实习招聘内推&#xff0c;求职就业一站解决_牛客网 (nowcoder.com) 第一眼看到这个题想到的是蓝桥杯飞机降落&#xff0c;贪心题。但是这样算的是最大不相交区间数量&#xff0…

米家立式学习灯怎么样?书客、米家、孩视宝三款护眼大路灯巅峰PK!

米家立式学习灯怎么样?不知从什么时候开始&#xff0c;青少年成为了近视重灾区&#xff0c;主要促成近视的原因有长时间接触电子产品、学习时的不正确姿势、不良的灯光环境等&#xff0c;除了减少电子产品的使用以及多室外活动之外&#xff0c;剩下的就是室内孩子经常学习的光…

致远CopyFile文件复制漏洞

复现版本 V8.0SP2 漏洞范围 V5&G6_V6.1至V8.0SP2全系列版本、V5&G6&N_V8.1至V8.1SP2全系列版本。 漏洞复现 上传文件 POST /seeyon/ajax.do?methodajaxAction&managerNameportalCssManager&rnd57507 HTTP/1.1 Accept: */* Content-Type: applicatio…

GD32F303之CAN通信

1、CAN时钟 GD32F303主时钟频率最大是120Mhz,然后APB1时钟最大是60Mhz,APB2时钟最大是120Mhz,CAN挂载在APB1总线上面 所以一般CAN的时钟频率是60Mhz,这个频率和后面配置波特率有关 2、GD32F303时钟配置 首先我们知道芯片有几个时钟 HXTAL&#xff1a;高速外部时钟&#xff1…

Python打开Excel文档并读取数据

Python 版本 目前 Python 3 版本为主流版本&#xff0c;这里测试的版本是&#xff1a;Python 3.10.5。 常用库说明 Python 操作 Excel 的常用库有&#xff1a;xlrd、xlwt、xlutils、openpyxl、pandas。这里主要说明下 Excel 文档 .xls 格式和 .xlsx 格式的文档打开和读取。 …

STMF4 硬件IIC(天空星开发板)

前言&#xff1a;笔记参考立创开发文档&#xff0c;连接放在最后 #IIC概念介绍 #IIC介绍 IIC通信协议&#xff0c;一种常见的串行通信协议&#xff0c;英文全程是 Inter-Integrated Circuit 使用这种通信方式的模块&#xff0c;通常有SCL&#xff08;Serial Clock Line&…