Zephyr OS藍牙廣播(Advertising)功能實現

目錄

概述

1?Advertising功能介紹

1.1?實現原理

?1.2?廣播類型

?1.3?廣播數據格式

1.4?優化建議

?1.5?常見問題和解決方法

2?Nordic 藍牙廣播(Advertising)功能實現

2.1?環境準備與SDK基礎

2.2 廣播功能實現

2.3?廣播優化與最佳實踐

3?實際應用案例

3.1?iBeacon實現

3.2?環境傳感器廣播

3.3?功耗測量優化

3.4?廣播性能優化技巧

4?常見問題解決

4.1?廣播不可見

4.2?數據更新失敗

4.3?高功耗問題


概述

本文主要介紹Zephyr RTOS藍牙廣播(Advertising)功能實現,Zephyr RTOS為Nordic芯片提供了強大的藍牙支持,使開發者能夠高效實現BLE功能。下面我將詳細介紹在Zephyr OS上實現藍牙廣播的完整流程。

1?Advertising功能介紹

藍牙廣播是低功耗藍牙(BLE)中設備向外發送信息的主要方式,適用于信標、傳感器數據廣播、設備發現等場景。下面我將詳細介紹藍牙廣播的實現原理和具體方法。

1.1?實現原理

藍牙廣播的核心機制是:

  1. 設備周期性地在三個廣播信道(37, 38, 39)上發送廣播包

  2. 廣播包包含設備信息、服務標識和自定義數據

  3. 附近設備通過掃描接收這些廣播信息

  4. 設備可以設置廣播參數(間隔、類型、數據等)

?1.2?廣播類型

廣播類型描述適用場景
ADV_IND0可連接的非定向廣播通用設備發現
ADV_DIRECT_IND1可連接的定向廣播快速重連
ADV_SCAN_IND2可掃描的非定向廣播廣播數據
ADV_NONCONN_IND3不可連接的非定向廣播信標設備
ADV_DIRECT_IND_LOW4低占空比的定向廣播節能設備

?1.3?廣播數據格式

1)合理組織AD結構

// 推薦的廣播數據結構
[Flags][Service UUID][Device Name][Tx Power][Manufacturer Data]

2)控制數據長度

廣播數據:最大31字節掃描響應數據:最大31字節優先放置重要信息在廣播數據中

1.4?優化建議

1)功耗優化

  • 適當增加廣播間隔(100ms-1s)

  • 使用不可連接廣播類型(ADV_NONCONN_IND)

  • 在不需要時停止廣播

2)?數據壓縮

  • 使用緊湊的數據格式

  • 避免重復信息

  • 使用自定義二進制格式替代文本

?3)安全考慮

  • 避免廣播敏感信息

  • 使用隨機設備地址

  • 實現廣播數據加密(如Eddystone-EID)

?1.5?常見問題和解決方法

1)廣播數據被截斷

  • 檢查數據長度是否超過31字節

  • 優先放置重要數據在開頭

  • 使用掃描響應數據補充信息

2)?設備無法被發現

  • 確認使用了正確的廣播類型

  • 檢查廣播信道是否被干擾

  • 驗證廣播間隔設置是否合理

3)?功耗過高

  • 增加廣播間隔

  • 減少廣播數據量

  • 使用更節能的廣播類型

?4)兼容性問題

  • 避免使用BLE 5.0特有功能與舊設備通信

  • 包含基本的BLE 4.0兼容數據

  • 提供多種廣播數據格式

2?Nordic 藍牙廣播(Advertising)功能實現

2.1?環境準備與SDK基礎

1)?開發環境

  • 硬件:nRF52系列開發板(如nRF52832/nRF52840)

  • 軟件

    • nRF Connect SDK(最新版本)

    • Segger Embedded Studio 或 VSCode + nRF Connect插件

    • nRF Command Line Tools

2)?SDK關鍵組件

  • SoftDevice:藍牙協議棧

  • BLE Advertising Module:廣播功能核心模塊

  • BLE Services:GAP和GATT服務

2.2 廣播功能實現

1)基礎廣播實現

#include <zephyr/kernel.h>
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/gap.h>/* 廣播參數配置 */
static const struct bt_le_adv_param *adv_param = BT_LE_ADV_PARAM(BT_LE_ADV_OPT_CONNECTABLE | BT_LE_ADV_OPT_USE_NAME, // 可連接且包含設備名800,   // 最小廣播間隔: 800*0.625ms=500ms1600,  // 最大廣播間隔: 1600*0.625ms=1000msNULL); // 對等設備地址(定向廣播使用)/* 廣播數據結構 */
static const struct bt_data ad[] = {BT_DATA_BYTES(BT_DATA_FLAGS, BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR),BT_DATA(BT_DATA_NAME_COMPLETE, CONFIG_BT_DEVICE_NAME, sizeof(CONFIG_BT_DEVICE_NAME) - 1),
};/* 掃描響應數據 */
static const struct bt_data sd[] = {BT_DATA(BT_DATA_UUID16_ALL, BT_UUID_16_ENCODE(BT_UUID_DIS_VAL)),
};void main(void)
{int err;/* 初始化藍牙協議棧 */err = bt_enable(NULL);if (err) {printk("Bluetooth init failed (err %d)\n", err);return;}printk("Bluetooth initialized\n");/* 啟動廣播 */err = bt_le_adv_start(adv_param, ad, ARRAY_SIZE(ad), sd, ARRAY_SIZE(sd));if (err) {printk("Advertising failed to start (err %d)\n", err);return;}printk("Advertising successfully started\n");/* 主循環 */while (1) {k_sleep(K_SECONDS(10));// 在此添加廣播更新邏輯}
}

2)高級廣播功能

- 2.1)?制造商特定數據廣播

/* 制造商特定數據結構 */
#define MANUFACTURER_ID 0x0059 // Nordic的廠商IDstatic uint8_t mfg_data[5] = {0x01, 0x02, 0x03, 0x04, 0x05}; // 自定義數據static struct bt_data custom_ad[] = {BT_DATA_BYTES(BT_DATA_FLAGS, BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR),BT_DATA(BT_DATA_MANUFACTURER_DATA, mfg_data, sizeof(mfg_data)),
};/* 更新廣播數據 */
void update_advertising_data(void)
{// 動態更新制造商數據mfg_data[0] = (k_uptime_get_32() >> 24) & 0xFF;mfg_data[1] = (k_uptime_get_32() >> 16) & 0xFF;// 停止當前廣播bt_le_adv_stop();// 使用新數據重新啟動廣播bt_le_adv_start(adv_param, custom_ad, ARRAY_SIZE(custom_ad), NULL, 0);
}

-2.2) 擴展廣播 (BLE 5.0+)

#if defined(CONFIG_BT_EXT_ADV)
/* 擴展廣播參數 */
static const struct bt_le_adv_param ext_adv_param = {.id = 0,.sid = 0,.secondary_max_skip = 0,.options = BT_LE_ADV_OPT_EXT_ADV | BT_LE_ADV_OPT_USE_NAME |BT_LE_ADV_OPT_CONNECTABLE,.interval_min = BT_GAP_ADV_FAST_INT_MIN_2, // 30ms.interval_max = BT_GAP_ADV_FAST_INT_MAX_2, // 50ms.peer = NULL,
};/* 配置擴展廣播 */
void start_extended_advertising(void)
{struct bt_le_ext_adv *ext_adv;int err;/* 創建擴展廣播實例 */err = bt_le_ext_adv_create(&ext_adv_param, NULL, &ext_adv);if (err) {printk("Failed to create extended advertising set (err %d)\n", err);return;}/* 設置廣播數據 */err = bt_le_ext_adv_set_data(ext_adv, ad, ARRAY_SIZE(ad), sd, ARRAY_SIZE(sd));if (err) {printk("Failed to set advertising data (err %d)\n", err);return;}/* 啟動擴展廣播 */err = bt_le_ext_adv_start(ext_adv, BT_LE_EXT_ADV_START_DEFAULT);if (err) {printk("Failed to start extended advertising (err %d)\n", err);return;}printk("Extended advertising started\n");
}
#endif /* CONFIG_BT_EXT_ADV */

3. 動態廣播間隔調整

/* 動態調整廣播間隔 */
void adjust_advertising_interval(uint16_t min_interval, uint16_t max_interval)
{// 創建新的廣播參數const struct bt_le_adv_param *new_param = BT_LE_ADV_PARAM(BT_LE_ADV_OPT_CONNECTABLE | BT_LE_ADV_OPT_USE_NAME,min_interval,max_interval,NULL);// 停止當前廣播bt_le_adv_stop();// 使用新參數啟動廣播bt_le_adv_start(new_param, ad, ARRAY_SIZE(ad), sd, ARRAY_SIZE(sd));printk("Advertising interval adjusted to %d-%d ms\n",min_interval * 5 / 8, max_interval * 5 / 8);
}/* 在低功耗模式下延長廣播間隔 */
void enter_low_power_mode(void)
{// 設置長間隔:2000-4000 msadjust_advertising_interval(3200, 6400);
}/* 在活躍模式下縮短廣播間隔 */
void enter_active_mode(void)
{// 設置短間隔:100-200 msadjust_advertising_interval(160, 320);
}

2.3?廣播優化與最佳實踐

1) 廣播數據壓縮技巧

/* 緊湊廣播數據結構 */
static const struct bt_data compressed_ad[] = {// 標志位 (1字節)BT_DATA_BYTES(BT_DATA_FLAGS, BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR),// 短名稱 (4字節)BT_DATA(BT_DATA_NAME_SHORTENED, "Zeph", 4),// 服務UUID (2字節)BT_DATA_BYTES(BT_DATA_UUID16_SOME, BT_UUID_16_ENCODE(BT_UUID_HRS_VAL)),// 制造商數據 (5字節: 廠商ID + 3字節自定義)BT_DATA(BT_DATA_MANUFACTURER_DATA, (uint8_t[]){0x59, 0x00, 0x01, 0x02, 0x03}, 5),// 發射功率 (2字節)BT_DATA_BYTES(BT_DATA_TX_POWER, 0xF4) // -12 dBm
};

2)?廣播功耗優化

/* 優化廣播參數 */
static const struct bt_le_adv_param low_power_adv_param = BT_LE_ADV_PARAM(BT_LE_ADV_OPT_CONNECTABLE | BT_LE_ADV_OPT_LOW_POWER,1600,   // 1000ms3200,   // 2000msNULL);/* 降低發射功率 */
void reduce_tx_power(void)
{int err;// 設置-20dBm的發射功率err = bt_le_set_tx_power(BT_LE_TX_POWER_N4); // -20dBmif (err) {printk("Failed to set TX power (err %d)\n", err);} else {printk("TX power reduced to -20dBm\n");}
}/* 配置廣播睡眠模式 */
void configure_advertising_scheduler(void)
{// 僅在白天活動 (示例)k_work_schedule(&adv_work, K_HOURS(6));  // 6AM開始廣播k_work_schedule(&sleep_work, K_HOURS(18)); // 6PM停止廣播
}static void start_adv_work(struct k_work *work)
{bt_le_adv_start(adv_param, ad, ARRAY_SIZE(ad), sd, ARRAY_SIZE(sd));printk("Advertising started\n");
}static void stop_adv_work(struct k_work *work)
{bt_le_adv_stop();printk("Advertising stopped\n");
}K_WORK_DELAYABLE_DEFINE(adv_work, start_adv_work);
K_WORK_DELAYABLE_DEFINE(sleep_work, stop_adv_work);

3?實際應用案例

3.1?iBeacon實現

#include <zephyr/sys/byteorder.h>/* iBeacon數據格式 */
void configure_ibeacon(void)
{const struct bt_data ibeacon_ad[] = {BT_DATA_BYTES(BT_DATA_FLAGS, BT_LE_AD_NO_BREDR),BT_DATA_BYTES(BT_DATA_MANUFACTURER_DATA,0x4c, 0x00, // Apple ID0x02, 0x15, // iBeacon類型// UUID0xE2, 0xC5, 0x6D, 0xB5, 0xDF, 0xFB, 0x48, 0xD2,0xB0, 0x60, 0xD0, 0xF5, 0xA7, 0x10, 0x96, 0xE0,// Major0x00, 0x01,// Minor0x00, 0x02,// Tx Power0xC5)};// 使用不可連接廣播const struct bt_le_adv_param *ibeacon_param = BT_LE_ADV_PARAM(BT_LE_ADV_OPT_NONE, 160,   // 100ms320,   // 200msNULL);// 啟動iBeacon廣播bt_le_adv_start(ibeacon_param, ibeacon_ad, ARRAY_SIZE(ibeacon_ad), NULL, 0);printk("iBeacon advertising started\n");
}

3.2?環境傳感器廣播

/* 傳感器數據結構 */
struct sensor_data {int16_t temperature; // 溫度 (0.01°C)uint16_t humidity;   // 濕度 (0.01%)uint8_t battery;     // 電池電量 (0-100%)
};/* 更新傳感器廣播數據 */
void update_sensor_advertising(struct sensor_data *data)
{uint8_t mfg_data[7];// 廠商ID (Nordic)mfg_data[0] = 0x59;mfg_data[1] = 0x00;// 溫度 (小端序)sys_put_le16(data->temperature, &mfg_data[2]);// 濕度 (小端序)sys_put_le16(data->humidity, &mfg_data[4]);// 電池電量mfg_data[6] = data->battery;// 更新廣播數據const struct bt_data sensor_ad[] = {BT_DATA_BYTES(BT_DATA_FLAGS, BT_LE_AD_NO_BREDR),BT_DATA(BT_DATA_MANUFACTURER_DATA, mfg_data, sizeof(mfg_data))};// 停止并重新啟動廣播以更新數據bt_le_adv_stop();bt_le_adv_start(adv_param, sensor_ad, ARRAY_SIZE(sensor_ad), NULL, 0);printk("Sensor data updated: T=%.2fC, H=%.2f%%, Bat=%d%%\n",data->temperature / 100.0, data->humidity / 100.0, data->battery);
}

3.3?功耗測量優化

/* 功耗測量代碼片段 */
void measure_power_consumption(void)
{uint32_t start_time = k_uptime_get_32();uint32_t start_cycles = k_cycle_get_32();// 執行廣播操作bt_le_adv_start(/* 參數 */);k_sleep(K_MSEC(5000));bt_le_adv_stop();uint32_t duration_ms = k_uptime_get_32() - start_time;uint64_t total_cycles = k_cycle_get_32() - start_cycles;// 計算平均電流(需要硬件測量校準)float avg_current = (total_cycles * 1000.0) / (duration_ms * sys_clock_hw_cycles_per_sec());printk("Advertising power: %.2f mA\n", avg_current);
}

3.4?廣播性能優化技巧

  1. 數據壓縮:使用自定義二進制格式而非文本

  2. 廣播間隔:根據應用場景動態調整間隔

    • 發現階段:短間隔(100-200ms)

    • 穩定狀態:長間隔(1-2秒)

  3. 廣播信道:優先使用信道37(最少干擾)

  4. 數據分片:對大型數據使用掃描響應

  5. 廣播時間窗口:僅在需要時廣播

4?常見問題解決

4.1?廣播不可見

// 在代碼中添加驗證
if (!bt_le_adv_is_enabled()) {printk("Advertising not enabled!\n");
}// 檢查廣播參數
if (adv_param->options & BT_LE_ADV_OPT_SCANNABLE) {printk("Broadcast is scannable\n");
}

4.2?數據更新失敗

void safe_update_advertising(void)
{static struct k_mutex adv_mutex;k_mutex_lock(&adv_mutex, K_FOREVER);bt_le_adv_stop();// 更新數據bt_le_adv_start(/* 新參數 */);k_mutex_unlock(&adv_mutex);
}

4.3?高功耗問題

  • 動態調整廣播間隔

  • 使用-20dBm到-12dBm的發射功率

  • 實現廣播睡眠計劃

// 1. 降低發射功率
bt_le_set_tx_power(BT_LE_TX_POWER_N8); // -8dBm// 2. 增加廣播間隔
adjust_advertising_interval(3200, 6400); // 2-4秒// 3. 使用不可連接廣播
const struct bt_le_adv_param *non_conn_param = BT_LE_ADV_PARAM(BT_LE_ADV_OPT_NONE, 1600, 3200, NULL);

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/88668.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/88668.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/88668.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

服務器不支持PUT,DELETE 的解決方案

nginx 的更改&#xff1a; set $method $request_method; if ($http_X_HTTP_Method_Override ~* PUT|DELETE) { set $method $http_X_HTTP_Method_Override; } proxy_method $method; axios 的更改&#xff1a; const method config.me…

從0開始學習計算機視覺--Day04--線性分類

從宏觀來看&#xff0c;卷積網絡可以看做是由一個個不同的神經網絡組件組合而成&#xff0c;就像積木一樣通過不同類型的組件搭建形成&#xff0c;其中線性分類器是一個很重要的組件&#xff0c;在很多卷積網絡中都有用到&#xff0c;所以了解清楚它的工作原理對我們后續的學習…

基于ComfyUI與Wan2.1模型的本地化視頻生成環境搭建指南

文章目錄 前言1.軟件準備1.1 ComfyUI1.2 文本編碼器1.3 VAE1.4 視頻生成模型2.整合配置3. 本地運行測試4. 公網使用Wan2.1模型生成視頻4.1 創建遠程連接公網地址5. 固定遠程訪問公網地址總結前言 各位小伙伴們,今天我們將為您展示一套創新的人工智能應用方案!本次教程將指導…

Vue 2 項目中內嵌 md 文件

推薦方案&#xff1a;raw-loader marked 解析 Markdown 第一步&#xff1a;安裝依賴 npm install marked --save npm install raw-loader --save-dev第二步&#xff1a;配置 webpack 支持 .md 文件 打開 vue.config.js 或 webpack.config.js&#xff0c;添加以下配置&#…

Spring AI初識及簡單使用,快速上手。

Spring AI簡介 在當今這樣一個快速發展的技術時代&#xff0c;人工智能&#xff08;AI&#xff09;已經成為各行各業的一種標配。而作為一款主流的Java應用開發框架Spring&#xff0c;肯定會緊跟時代的潮流&#xff0c;所以&#xff0c;推出了Spring AI框架。 官網描述&#…

Flask中的render_template與make_response:生動解析與深度對比

文章目錄 Flask中的render_template與make_response&#xff1a;生動解析與深度對比一、&#x1f31f; 核心概念速覽二、&#xfffd; render_template - 網頁內容的主廚特點與內部機制適用場景高級用法示例 三、&#x1f381; make_response - 響應的包裝專家核心功能解析適用…

WordPress目錄說明

在WordPress建站過程中&#xff0c;理解服務器目錄結構是非常重要的。以下是一個基礎的WordPress服務器目錄指南&#xff1a; /wp-admin/ &#xff1a;這個目錄包含了WordPress網站的所有管理功能&#xff0c;包括用于處理網站后臺的所有PHP文件。 /wp-includes/ &#xff1a;…

HTTP面試題——緩存技術

目錄 HTTP緩存技術有哪些&#xff1f; 什么是強制緩存&#xff1f; 什么是協商緩存&#xff1f; HTTP緩存技術有哪些&#xff1f; 對于一些具有重復性的HTTP請求&#xff0c;比如每次請求得到的數據都是一樣的&#xff0c;我們可以把這對 請求-響應的數據都緩存在本地&#x…

virtual box 不能分配 USB設備 IFX DAS JDS TriBoard TC2X5 V2.0 [0700] 到虛擬電腦 win10

VirtualBox: Failed to attach the USB device to the virtual machine – Bytefreaks.net ISSUE&#xff1a; virtual box 不能分配 USB設備 IFX DAS JDS TriBoard TC2X5 V2.0 [0700] 到虛擬電腦 win10. USB device IFX DAS JDS TriBoard TC2X5 V2.0 with UUID {91680aeb-e1…

Deepoc大模型重構核工業智能基座:混合增強架構與安全增強決策技術?

面向復雜系統的高可靠AI賦能體系構建 Deepoc大模型通過多維度技術突破&#xff0c;顯著提升核工業知識處理與決策可靠性。經核能行業驗證&#xff0c;其生成內容可驗證性提升68%&#xff0c;關鍵參數失真率<0.3%&#xff0c;形成覆蓋核能全鏈條的定制化方案&#xff0c;使企…

第12章:冰箱里的CT掃描儀——計算機視覺如何洞穿食材的“生命密碼“

第11章:冰箱里的CT掃描儀——計算機視覺如何成為食材健康的"超級診斷官" “糟了!冰箱里草莓長出了白色絨毛,雞胸肉滲出了可疑的粉紅色液體!” 這揭示了廚房生存的更基本挑戰:如何像經驗豐富的主廚一樣,一眼洞穿食材的健康密碼? 本章將揭示計算機視覺技術如何賦…

虛幻基礎:窗口——重定向

能幫到你的話&#xff0c;就給個贊吧 &#x1f618; 文章目錄 重定向&#xff1a;給骨架添加兼容骨架。使得不同模型間復用動畫資源 重定向&#xff1a;給骨架添加兼容骨架。使得不同模型間復用動畫資源

CSS 逐幀動畫

CSS 逐幀動畫實現指南 逐幀動畫(frame-by-frame animation)是一種通過快速連續顯示一系列靜態圖像來創造運動效果的技術。以下是使用CSS實現逐幀動畫的幾種方法。 1. 使用 steps() 計時函數 這是實現逐幀動畫最常用的方法&#xff0c;通過animation-timing-function的steps(…

高版本IDEA如何開發低版本jdk項目

問題描述 我這個人比較喜歡新的東西&#xff0c;比如使用idea的時候&#xff0c;我就喜歡最新版本。 但是有個問題&#xff0c;最新版本的idea好像不支持jdk1.6&#xff0c;導致我無法去用新版本idea開發項目。 直到有一天&#xff0c;idea給了我一個提示如下&#xff0c;之…

Java設計模式->責任鏈模式的介紹

目錄 1、責任鏈模式概念 1.1、定義介紹 1.2、流程圖 1.3、優缺點 2、實現 3、應用場景 3.1、Springmvc流程 3.2、mybatis的執行流程 3.3、Spring的過濾器和攔截器 3.4、sentinel限流熔斷 3.5、aop的加載和使用 4、舉例 前言 是一種 行為型設計模式&#xff0c;它通…

【Bluedroid】藍牙啟動之 btm_acl_device_down 流程源碼解析

本文詳細分析Android藍牙協議棧在設備故障時的處理流程。當藍牙設備發生硬件故障或系統異常時,協議棧通過btm_acl_device_down觸發多層次的資源清理和狀態重置,包括ACL連接終止、L2CAP通道釋放、SCO連接清理、BLE拓撲更新、設備數據庫重置等關鍵操作,確保系統安全恢復。 一、…

隨記:WebMvcConfigurationSupport 和WebMvcConfigurer 的區別

WebMvcConfigurationSupport &#xff08;抽象類&#xff09; 他是一個完整的 MVC 配置基類&#xff0c;他會禁用所有自動配置。默認靜態資源映射也沒有了。默認消息轉換器&#xff08;json、xml&#xff09;也沒有了。錯誤處理頁默認的error也沒有了。 WebMvcConfigurer &am…

npm run dev報錯

1. 引言 1.1 什么是npm run dev npm run dev 是一個在 Node.js 項目中常用的命令&#xff0c;它允許開發者運行一個預定義的腳本&#xff0c;通常用于啟動開發服務器或者執行開發環境的構建任務。這個命令是通過 package.json 文件中的 scripts 部分定義的&#xff0c;例如&…

Kotlin環境搭建與基礎語法入門

目標&#xff1a;完成開發環境配置&#xff0c;編寫第一個Kotlin程序&#xff0c;理解變量、數據類型和基本輸出。 1. 環境搭建 步驟1&#xff1a;安裝JDK 下載并安裝 JDK 17&#xff08;Kotlin兼容性最佳版本&#xff09;。 配置環境變量 JAVA_HOME&#xff0c;并在終端驗證…

CLion開發Qt桌面程序_git的簡單使用_小團體

OS&#xff1a;Windows Qt&#xff1a;6.8.1&#xff08;6.x&#xff09; Eg&#xff1a;學生信息管理系統 前言 Qt Creator編寫代碼不是太方便&#xff0c;使用CLion編寫代碼或許是個不錯的主意&#xff0c;CLion在此處主要是用于后端和測試的開發&#xff0c;界面方面還是…