【Bluedroid】蓝牙 HID DEVICE 初始化流程源码解析

news2025/5/14 13:51:03

本文深入剖析Android蓝牙协议栈中HID设备(BT-HD)服务的初始化与启用流程,从接口初始化、服务掩码管理、服务请求路由到属性回调通知,完整展现蓝牙HID服务激活的技术路径。通过代码逻辑梳理,揭示服务启用的核心机制,包括服务状态掩码更新、跨模块调用链及线程安全的属性传递设计。

一、概述

蓝牙 HID 设备(如键盘、鼠标)的服务启用与状态同步是蓝牙协议栈的核心功能之一。整个流程涉及多个模块协作,包括服务初始化、协议栈请求分发、属性获取及跨线程通知,具体可分为以下阶段:

1.1 服务初始化与启用

  1. 初始化接口:通过init函数完成 BT-HD 接口初始化,配置回调函数并调用btif_enable_service(BTA_HIDD_SERVICE_ID)启用 HID 服务。

  2. 服务掩码更新btif_enable_service将目标服务(BTA_HIDD_SERVICE_ID)添加到全局服务掩码(btif_enabled_services),若蓝牙已激活(btif_is_enabled()),则触发服务启用请求。

1.2 协议栈请求分发

  1. 服务请求执行btif_dm_enable_service调用btif_in_execute_service_request执行服务启用请求。若成功,触发属性变更通知流程。

  2. 请求路由btif_in_execute_service_request根据服务类型分发请求:SDP 服务特殊处理,其他服务通过toggleProfile路由到具体服务模块(如 HID 服务由btif_hd_execute_service处理)。

1.3 服务状态同步与属性通知

  1. 属性填充与获取

    1. 通过宏BTIF_STORAGE_FILL_PROPERTY初始化bt_property_t结构体,指定属性类型(如BT_PROPERTY_UUIDS)。

    2. btif_storage_get_adapter_property根据属性类型从控制器或存储中获取具体值(如蓝牙地址、已配对设备列表、已启用服务的 UUID 列表)。

  2. 跨线程回调通知invoke_adapter_properties_cb通过深拷贝(property_deep_copy_array)确保属性数据线程安全,结合do_in_jni_thread将变更事件异步传递到 JNI 线程,触发上层回调(如 Java 层)并释放资源。

二、源码解析

init

packages/modules/Bluetooth/system/btif/src/btif_hd.cc
/*******************************************************************************
 *
 * Function        init
 *
 * Description     Initializes BT-HD interface
 *
 * Returns         BT_STATUS_SUCCESS
 *
 ******************************************************************************/
btif_hd_cb_t btif_hd_cb;

static bt_status_t init(bthd_callbacks_t* callbacks) {
  log::verbose("");

  bt_hd_callbacks = callbacks;
  memset(&btif_hd_cb, 0, sizeof(btif_hd_cb));

  btif_enable_service(BTA_HIDD_SERVICE_ID); // 启用 HID 服务

  return BT_STATUS_SUCCESS;
}

初始化蓝牙 HID 设备(BT-HD)接口,配置回调函数并启用 HID 服务。

btif_enable_service(BTA_HIDD_SERVICE_ID)

packages/modules/Bluetooth/system/btif/src/btif_core.cc
/*******************************************************************************
 *
 * Function         btif_enable_service
 *
 * Description      Enables the service 'service_ID' to the service_mask.
 *                  Upon BT enable, BTIF core shall invoke the BTA APIs to
 *                  enable the profiles
 *
 ******************************************************************************/
void btif_enable_service(tBTA_SERVICE_ID service_id) {
  btif_enabled_services |= (1 << service_id); // 服务掩码更新

  log::verbose("current services:0x{:x}", btif_enabled_services);

  if (btif_is_enabled()) {
    btif_dm_enable_service(service_id, true);
  }
}

将指定的蓝牙服务添加到已启用服务掩码中,并在蓝牙已激活时立即启动该服务。

btif_dm_enable_service

packages/modules/Bluetooth/system/btif/src/btif_dm.cc
void btif_dm_enable_service(tBTA_SERVICE_ID service_id, bool enable) {
  // 执行服务请求
  bt_status_t status = btif_in_execute_service_request(service_id, enable);
  if (status == BT_STATUS_SUCCESS) {
    // 准备 UUID 属性
    bt_property_t property;
    Uuid local_uuids[BT_MAX_NUM_UUIDS]; // 存储设备支持的 UUID 列表

    /* Now send the UUID_PROPERTY_CHANGED event to the upper layer */
    // 从存储中获取当前设备的 UUID 属性
    BTIF_STORAGE_FILL_PROPERTY(&property, BT_PROPERTY_UUIDS,
                               sizeof(local_uuids), local_uuids);
    btif_storage_get_adapter_property(&property);
    
    // 通过回调通知上层应用适配器属性已变更
    GetInterfaceToProfiles()->events->invoke_adapter_properties_cb(
        BT_STATUS_SUCCESS, 1, &property);
  }
  return;
}

通过蓝牙协议栈执行服务启用 / 禁用请求,并在成功时通知上层应用。

btif_in_execute_service_request

packages/modules/Bluetooth/system/btif/src/btif_dm.cc
bt_status_t btif_in_execute_service_request(tBTA_SERVICE_ID service_id,
                                            bool b_enable) {
  log::verbose("service_id:{}", service_id);

  if (service_id == BTA_SDP_SERVICE_ID) { // SDP 服务特殊处理
    btif_sdp_execute_service(b_enable);
    return BT_STATUS_SUCCESS;
  }

  // 通用服务处理
  return GetInterfaceToProfiles()->toggleProfile(service_id, b_enable);
}

将上层的服务启用 / 禁用请求路由到对应的处理模块。

toggleProfile(BTA_HIDD_SERVICE_ID)
packages/modules/Bluetooth/system/btif/src/bluetooth.cc
  bt_status_t toggleProfile(tBTA_SERVICE_ID service_id, bool enable) override {
    /* Check the service_ID and invoke the profile's BT state changed API */
    switch (service_id) {
      case BTA_HFP_SERVICE_ID:
      case BTA_HSP_SERVICE_ID: {
        bluetooth::headset::ExecuteService(enable);
      } break;
      case BTA_A2DP_SOURCE_SERVICE_ID: {
        btif_av_source_execute_service(enable);
      } break;
      case BTA_A2DP_SINK_SERVICE_ID: {
        btif_av_sink_execute_service(enable);
      } break;
      case BTA_HID_SERVICE_ID: {
        btif_hh_execute_service(enable);
      } break;
      case BTA_HFP_HS_SERVICE_ID: {
        btif_hf_client_execute_service(enable);
      } break;
      case BTA_HIDD_SERVICE_ID: {
        btif_hd_execute_service(enable);
      } break;
      case BTA_PBAP_SERVICE_ID:
      case BTA_PCE_SERVICE_ID:
      case BTA_MAP_SERVICE_ID:
      case BTA_MN_SERVICE_ID: {
        /**
         * Do nothing; these services were started elsewhere. However, we need
         * to flow through this codepath in order to properly report back the
         * local UUIDs back to adapter properties in Java. To achieve this, we
         * need to catch these service IDs in order for {@link
         * btif_in_execute_service_request} to return {@code BT_STATUS_SUCCESS},
         * so that in {@link btif_dm_enable_service} the check passes and the
         * UUIDs are allowed to be passed up into the Java layer.
         */
      } break;
      default:
        log::error("Unknown service {} being {}", service_id,
                   (enable) ? "enabled" : "disabled");
        return BT_STATUS_FAIL;
    }
    return BT_STATUS_SUCCESS;
  }

将通用的服务启用 / 禁用请求转发到具体的服务处理模块。

btif_hd_execute_service
packages/modules/Bluetooth/system/btif/src/btif_hd.cc
/*******************************************************************************
 *
 * Function         btif_hd_execute_service
 *
 * Description      Enabled/disables BT-HD service
 *
 * Returns          BT_STATUS_SUCCESS
 *
 ******************************************************************************/
bt_status_t btif_hd_execute_service(bool b_enable) {
  log::verbose("b_enable={}", b_enable);

  if (!b_enable) BTA_HdDisable();

  return BT_STATUS_SUCCESS;
}

控制蓝牙 HID 设备服务的开启与关闭。

BTIF_STORAGE_FILL_PROPERTY

/modules/Bluetooth/system/btif/include/btif_storage.h
#define BTIF_STORAGE_FILL_PROPERTY(p_prop, t, l, p_v) \
  do {                                                \
    (p_prop)->type = (t);                             \
    (p_prop)->len = (l);                              \
    (p_prop)->val = (p_v);                            \
  } while (0)

初始化bt_property_t结构体的三个核心字段。

btif_storage_get_adapter_property

packages/modules/Bluetooth/system/btif/src/btif_storage.cc
/*******************************************************************************
 *
 * Function         btif_storage_get_adapter_property
 *
 * Description      BTIF storage API - Fetches the adapter property->type
 *                  from NVRAM and fills property->val.
 *                  Caller should provide memory for property->val and
 *                  set the property->val
 *
 * Returns          BT_STATUS_SUCCESS if the fetch was successful,
 *                  BT_STATUS_FAIL otherwise
 *
 ******************************************************************************/
bt_status_t btif_storage_get_adapter_property(bt_property_t* property) {
  /* Special handling for adapter address and BONDED_DEVICES */
  if (property->type == BT_PROPERTY_BDADDR) { // 蓝牙地址获取
    RawAddress* bd_addr = (RawAddress*)property->val;
    /* Fetch the local BD ADDR */
    const controller_t* controller = controller_get_interface();
    if (!controller->get_is_ready()) {
      log::error("Controller not ready! Unable to return Bluetooth Address");
      *bd_addr = RawAddress::kEmpty;
      return BT_STATUS_FAIL;
    } else {
      log::info("Controller ready!");
      *bd_addr = *controller->get_address(); // 从蓝牙控制器获取本地设备地址
    }
    property->len = RawAddress::kLength;
    return BT_STATUS_SUCCESS;
  } else if (property->type == BT_PROPERTY_ADAPTER_BONDED_DEVICES) { // 已配对设备列表获取
    btif_bonded_devices_t bonded_devices;

    // 获取已配对设备列表
    btif_in_fetch_bonded_devices(&bonded_devices, 0);

    log::verbose(
        "BT_PROPERTY_ADAPTER_BONDED_DEVICES: Number of bonded devices={}",
        bonded_devices.num_devices);

    property->len = bonded_devices.num_devices * RawAddress::kLength;
    // 将设备地址复制到属性值缓冲区
    memcpy(property->val, bonded_devices.devices, property->len);

    /* if there are no bonded_devices, then length shall be 0 */
    return BT_STATUS_SUCCESS;
  } else if (property->type == BT_PROPERTY_UUIDS) { // UUID 列表生成
    /* publish list of local supported services */
    Uuid* p_uuid = reinterpret_cast<Uuid*>(property->val);
    uint32_t num_uuids = 0;
    uint32_t i;

    tBTA_SERVICE_MASK service_mask = btif_get_enabled_services_mask();
    log::info("Service_mask=0x{:x}", service_mask);
    // 根据已启用服务掩码生成对应的 UUID 列表
    for (i = 0; i < BTA_MAX_SERVICE_ID; i++) {
      /* This should eventually become a function when more services are enabled
       */
      if (service_mask & (tBTA_SERVICE_MASK)(1 << i)) {
        switch (i) {
          case BTA_HFP_SERVICE_ID: {
            *(p_uuid + num_uuids) =
                Uuid::From16Bit(UUID_SERVCLASS_AG_HANDSFREE);
            num_uuids++;
          }
            FALLTHROUGH_INTENDED; /* FALLTHROUGH */
          /* intentional fall through: Send both BFP & HSP UUIDs if HFP is
           * enabled */
          case BTA_HSP_SERVICE_ID: {
            *(p_uuid + num_uuids) =
                Uuid::From16Bit(UUID_SERVCLASS_HEADSET_AUDIO_GATEWAY);
            num_uuids++;
          } break;
          case BTA_A2DP_SOURCE_SERVICE_ID: {
            *(p_uuid + num_uuids) =
                Uuid::From16Bit(UUID_SERVCLASS_AUDIO_SOURCE);
            num_uuids++;
          } break;
          case BTA_A2DP_SINK_SERVICE_ID: {
            *(p_uuid + num_uuids) = Uuid::From16Bit(UUID_SERVCLASS_AUDIO_SINK);
            num_uuids++;
          } break;
          case BTA_PBAP_SERVICE_ID: {
            *(p_uuid + num_uuids) = Uuid::From16Bit(UUID_SERVCLASS_PBAP_PSE);
            num_uuids++;
          } break;
          case BTA_HFP_HS_SERVICE_ID: {
            *(p_uuid + num_uuids) =
                Uuid::From16Bit(UUID_SERVCLASS_HF_HANDSFREE);
            num_uuids++;
          } break;
          case BTA_MAP_SERVICE_ID: {
            *(p_uuid + num_uuids) =
                Uuid::From16Bit(UUID_SERVCLASS_MESSAGE_ACCESS);
            num_uuids++;
          } break;
          case BTA_MN_SERVICE_ID: {
            *(p_uuid + num_uuids) =
                Uuid::From16Bit(UUID_SERVCLASS_MESSAGE_NOTIFICATION);
            num_uuids++;
          } break;
          case BTA_PCE_SERVICE_ID: {
            *(p_uuid + num_uuids) = Uuid::From16Bit(UUID_SERVCLASS_PBAP_PCE);
            num_uuids++;
          } break;
        }
      }
    }
    property->len = (num_uuids) * sizeof(Uuid);
    return BT_STATUS_SUCCESS;
  }

  /* fall through for other properties */
  // 通用属性处理
  if (!cfg2prop(NULL, property)) {
    return btif_dm_get_adapter_property(property);
  }
  return BT_STATUS_SUCCESS;
}

根据属性类型从 NVRAM 或控制器中获取适配器属性值。

invoke_adapter_properties_cb

/packages/modules/Bluetooth/system/btif/src/bluetooth.cc
void invoke_adapter_properties_cb(bt_status_t status, int num_properties,
                                  bt_property_t* properties) {
  do_in_jni_thread(FROM_HERE,
                   base::BindOnce( // 创建一次性回调(避免重复执行)
                       [](bt_status_t status, int num_properties,
                          bt_property_t* properties) {
                         // 步骤1:触发上层回调(HAL层注册的回调)
                         HAL_CBACK(bt_hal_cbacks, adapter_properties_cb, status,
                                   num_properties, properties);
                         if (properties) {
                           // 步骤2:释放深拷贝的内存(避免泄漏)
                           osi_free(properties);
                         }
                       },
                       status, num_properties,
                       property_deep_copy_array(num_properties, properties))); //深拷贝属性数组
}

// callback reporting helpers

bt_property_t* property_deep_copy_array(int num_properties,
                                        bt_property_t* properties) {
  bt_property_t* copy = nullptr;
  if (num_properties > 0) {
    // 1:计算所有属性值的总长度(用于分配内存)
    size_t content_len = 0;
    for (int i = 0; i < num_properties; i++) {
      auto len = properties[i].len;
      if (len > 0) {
        content_len += len;
      }
    }

    //2:一次性分配连续内存(结构体数组 + 属性值内容)
    copy = (bt_property_t*)osi_calloc((sizeof(bt_property_t) * num_properties) + content_len);
    ASSERT(copy != nullptr);
    
    // 3:复制属性结构体,并将属性值指向新分配的内容区
    uint8_t* content = (uint8_t*)(copy + num_properties);    
    for (int i = 0; i < num_properties; i++) {
      auto len = properties[i].len;
      copy[i].type = properties[i].type;
      copy[i].len = len;
      if (len <= 0) {
        continue;
      }
      copy[i].val = content;  // 指向新内容区
      memcpy(content, properties[i].val, len);
      content += len; // 移动到下一个属性值位置
    }
  }
  return copy;
}

蓝牙适配器属性回调的跨线程调用机制,确保从 HAL 层到 JNI 层的属性通知能够正确处理。

三、时序图

四、总结

①模块化设计

  • 流程严格分层:接口初始化→服务管理→请求路由→具体实现→属性通知,各模块职责单一(如btif_hd专注HID逻辑,btif_dm处理通用服务管理)。

②状态驱动机制

  • 通过服务掩码btif_enabled_services全局追踪服务状态,结合btif_is_enabled()判断实时激活状态,避免无效操作。

③线程安全与内存管理

  • 属性回调采用深拷贝(property_deep_copy_array)与osi_free释放,确保跨线程(如JNI)数据传递安全性。

④扩展性设计

  • 服务ID与UUID映射表(如BTA_HFP_SERVICE_IDUUID_SERVCLASS_AG_HANDSFREE)支持快速扩展新服务,仅需修改映射逻辑。

通过服务掩码与回调机制的协同,实现蓝牙服务的动态启用与上层应用的实时同步,为HID设备(如键盘、游戏手柄)的即插即用提供底层支持。

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

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

相关文章

iOS创建Certificate证书、制作p12证书流程

一、创建Certificates 1、第一步得先在苹果电脑上创建一个.certSigningRequest的文件。首先打开钥匙串&#xff0c;使用快捷键【command空格】——输入【钥匙串】回车&#xff08;找不到就搜一下钥匙串访问使用手册&#xff09; 2、然后在苹果电脑的左上角菜单栏选择【钥匙串…

curl发送数据不为null,但是后端接收到为null

curl -X POST http://localhost:8080/xiaozhi/test --header "Content-Type: application/json" -d "{\"age\":123}"经过检查发现注解导入错误 正确的应该是 import org.springframework.web.bind.annotation.RequestBody;

blazor与硬件通信实现案例

在网页接入硬件交互通信方案这篇博客中,曾经提到了网页中接入各种硬件操作的方法,即通过Windows Service作为指令的中转,并建立websocket通信连接,进而实现接入硬件的各种操作。这篇博客就以实际的案例来讲解具体怎么实现。 一、建立Windows Service项目 比如我就建立了一…

Linux下mysql的安装与远程链接

linux安装mysql 01下载依赖&#xff1a; 找到网址/download下&#xff1a; 最下面MySQL Community&#xff08;mysql社区版&#xff09; 选择MySQL Community Server 选择对应的mysql版本 操作系统版本选择 根据操作系统的版本选择具体版本号 下载离线版本 安装包详情 0…

【HT周赛】T3.二维平面 题解(分块:矩形chkmax,求矩形和)

题意 需要维护 n n n \times n nn 平面上的整点&#xff0c;每个点 ( x , y ) (x, y) (x,y) 有权值 V ( x , y ) V(x, y) V(x,y)&#xff0c;初始都为 0 0 0。 同时给定 n n n 次修改操作&#xff0c;每次修改给出 x 1 , x 2 , y 1 , y 2 , v x_1, x_2, y_1, y_2, v x…

qemu热迁移后内存占用突增问题

1.问题描述 虚拟机配置了memoryBackingmemfd的情况下&#xff0c;热迁移虚拟机后&#xff0c;在目的节点 qemu-kvm 进程占用 rss 会突增很多。 如果去掉这个配置没这个现象。 <memoryBacking><source typememfd/> </memoryBacking>2.问题现象 2.1 不配置…

鸿蒙 Core File Kit(文件基础服务)之简单使用文件

查看常用的沙箱目录 应用沙箱文件访问关系图 应用文件目录结构图 查看常用的沙箱目录 Entry Component struct Index {build() {Button(查看常用的沙箱目录).onClick(_>{let ctx getContext() // UI下只能使用这个方法&#xff0c;不能 this.contextconsole.log(--应用缓存…

基于Qt的app开发第七天

写在前面 笔者是大一下计科生&#xff0c;标题这个项目是笔者这个学期的课设&#xff0c;与学长共创&#xff0c;我负责客户端部分&#xff0c;现在已经实现了待办板块的新建、修改。 这个项目目前已经走上正轨了&#xff0c;博主也实现了主要功能的从无到有&#xff…

目标检测任务常用脚本1——将YOLO格式的数据集转换成VOC格式的数据集

在目标检测任务中&#xff0c;不同框架使用的标注格式各不相同。常见的框架中&#xff0c;YOLO 使用 .txt 文件进行标注&#xff0c;而 PASCAL VOC 则使用 .xml 文件。如果你需要将一个 YOLO 格式的数据集转换为 VOC 格式以便适配其他模型&#xff0c;本文提供了一个结构清晰、…

NLTK库: 数据集3-分类与标注语料(Categorized and Tagged Corpora)

NLTK库: 数据集3-分类与标注语料&#xff08;Categorized and Tagged Corpora&#xff09; 1.二分类语料 主要是电影语料&#xff0c;和情绪(积极消极、主观客观)有关&#xff0c;有以下2个语料&#xff1a; 1.1 movie_reviews: IMDb 影评 IMDb&#xff08;Internet Movie …

uni-app学习笔记五-vue3响应式基础

一.使用ref定义响应式变量 在组合式 API 中&#xff0c;推荐使用 ref() 函数来声明响应式状态&#xff0c;ref() 接收参数&#xff0c;并将其包裹在一个带有 .value 属性的 ref 对象中返回 示例代码&#xff1a; <template> <view>{{ num1 }}</view><vi…

ElasticSeach快速上手笔记-入门篇

由来 Elasticsearch 是一个基于 Apache Lucene 构建的分布式、高扩展、近实时的搜索与数据分析引擎&#xff0c;能够高效处理结构化和非结构化数据的全文检索及复杂分析 搜索&#xff0c;即用户在平台如百度进行输入关键词&#xff0c;由后端给出搜索结果数据进行返回&#x…

《ffplay 读线程与解码线程分析:从初始化到 seek 操作,对比视频与音频解码的差异》

1 read-thread 1.1 初始化部分 1.分配. avformat_alloc_context 创建上下⽂ ic avformat_alloc_context();if (!ic) {av_log(NULL, AV_LOG_FATAL, "Could not allocate context.\n");ret AVERROR(ENOMEM);goto fail;}2 ic->interrupt_callback.callback deco…

MySQL推荐书单:从入门到精通

给大家介绍一些 MySQL 从入门到精通的经典书单&#xff0c;可以基于不同学习阶段的需求进行选择。 入门 MySQL必知必会 这本书继承了《SQL必知必会》的优点&#xff0c;专门针对 MySQL 用户&#xff0c;没有过多阐述数据库基础理论&#xff0c;而是紧贴实战&#xff0c;直接从…

【Nacos】env NACOS_AUTH_TOKEN must be set with Base64 String.

【Nacos】env NACOS_AUTH_TOKEN must be set with Base64 String. 问题描述 env NACOS_AUTH_TOKEN must be set with Base64 String.原因分析 从错误日志中可以看出&#xff0c;Nacos 启动失败的原因是缺少必要的环境变量 NACOS_AUTH_TOKEN。 NACOS_AUTH_TOKEN: Nacos 用于生…

秋招准备——2.跨时钟相关

格雷码异步FIFO跨时钟域处理 格雷码 一、格雷码规律 相邻性&#xff1a;相邻两个数的格雷码只有一位不同&#xff0c;例如&#xff1a; 0000 → 0001&#xff08;仅最低位变化&#xff09;0001 → 0011&#xff08;仅次低位变化&#xff09;0011 → 0010&#xff08;仅最低位…

激光打印机常见打印故障简单处理意见

一、 问题描述&#xff1a; 给打印机更换新的硒鼓时拉开硒鼓封条时有微量碳粉带出&#xff1b; 原因&#xff1a; 出厂打印测试时&#xff0c;可能会有微量碳粉在磁辊上或者磁辊仓&#xff1b; 解决方法&#xff1a; 擦干净即可正常使用&#xff1b; 二、 问题描述&…

【2025最新】Windows系统装VSCode搭建C/C++开发环境(附带所有安装包)

文章目录 为什么选择VSCode作为C/C开发工具&#xff1f;一、VSCode安装过程&#xff08;超简单&#xff01;&#xff09;二、VSCode中文界面设置&#xff08;再也不用对着英文发愁&#xff01;&#xff09;三、安装C/C插件&#xff08;编程必备神器&#xff01;&#xff09;四、…

MYSQL 查询去除小数位后多余的0

MYSQL 查询去除小数位后多余的0 在MySQL中&#xff0c;有时候我们需要去除存储在数据库中的数字字段小数点后面多余的0。这种情况通常发生在处理金额或其他需要精确小数位的数据时。例如&#xff0c;数据库中存储的是decimal (18,6)类型的数据&#xff0c;但在页面展示时不希望…

基于GF域的多进制QC-LDPC误码率matlab仿真,译码采用EMS算法

目录 1.算法仿真效果 2.算法涉及理论知识概要 3.MATLAB核心程序 4.完整算法代码文件获得 1.算法仿真效果 matlab2022a仿真结果如下&#xff08;完整代码运行后无水印&#xff09;&#xff1a; 本课题实现的是四进制QC-LDPC 仿真操作步骤可参考程序配套的操作视频。 2.算…