STM32的藍牙通訊(HAL庫)

藍牙基礎知識(了解即可):

1.是一種利用低功率無線電,支持設備短距離通信的無線電技術,能在包括移動電話、PDAQ、無線耳機、筆記本電腦、相關外設等眾多設備之間進行無線信息交換,藍牙工作在全球通用的2.4 GHz(2.4 至 2.485 GH)ISM(即工業、科學、醫學)頻段(和WI-FI頻段一樣就能結合起來用),使用IEEE802.11協議。

第一~三藍牙重心都往高速傳輸速率發展,但是由于WI-FI已經發展的很好了,所以沒必要將發展理念和WI-FI重合到來第4代就將重心向低功耗靠攏

第四代藍牙:主推Low Energy低功耗,BLE(Bluetooth Low Energy)低功耗功能。

第五代藍牙開啟物聯網時代大門,在低功耗模式下具備更快更遠的傳輸能力,為我們提供兩種選擇:低功耗遠距離傳輸/高速近距離傳輸。(物聯網廣泛應用)

第六代藍牙:新增加了信道功能(就是可以定位連接了藍牙的設備,提高到1cm的精度)

藍牙技術類型:

藍牙協議包括兩種技術:BR(Basic Rate經典藍牙)和LE(Low Energy)都是使用2.4G無線電頻率因此雙模設備可以共享同一個天線。

這兩種技術都包括搜索(discovery)管理、連接(connection)管理等機制,但它們是相互獨立的,不能互通的技術!

廠商如果只實現了一種,那么只能與同樣實現該技術的設備互通。如果廠商要確保能和所有的藍牙設備互通,那么就只能同時實現兩種技術,而不去管是否真的需要。

2.市場上常見藍牙架構:

SOC藍牙單芯片方案

此類芯片一般可以直接做為MCU用,這類產品一般用于消費類電子,集成度很高,調一調參數可以直接使用,常見的有藍牙耳機,音響,藍牙心率等產品。

SOC藍牙+MCU方案

接外部主機,WI-FI就是這種架構藍牙芯片內置射頻模塊,電路和物理層的各種支持,但是自己不燒寫應用邏輯,那么我們就可以通過調用它給定的規則來調用它的功能符合我們的項目設想(藍牙手表上網):

在集成好的藍牙芯片基礎上,通過特定的接口(UART居多),發送自定義的command來達到想要的功能,比如發送0x01代表搜索周圍設備。

外設一個單芯片方案,其自定義指令包含藍牙芯片(BT Controller)、微控制單元(MCU)以及藍牙協議棧(BT Host)。

此部分的應用,將藍牙作為一個外設使用,用于遠程通信,例如網上賣的一些藍牙串口。

比如我們后面應用就是采用的這種方案。 STM32作為MCU使用,ESP32作為藍牙芯片使用。

藍牙host+controller分開方案

單獨將協議給功能更加強大的MCU處理目的就是為了實現更加強大的功能:

這種應用算是藍牙最復雜的應用,客戶需要使用藍牙的場景有很多,涉及的藍牙協議也有很多,需要將Host與Controller分開,集成更多的藍牙協議,比如藍牙電話(HFP)、藍牙音頻(A2DP)、藍牙音樂控制(AVRCP)、藍牙電話本(PBAP)、藍牙短信(MAP),手機藍牙等。

此部分應用,將定制藍牙的各種服務,實現藍牙多功能需求。

3.藍牙協議棧:很復雜,廠家規定且打包好了,我們會用它的函數或者指令就行(一般都有用戶指南,可以根據提示自己內測)

藍牙芯片架構:

藍牙的核心系統,由一個Host和一個或多個Controller組成。

(1)BT Host:邏輯實體(控制軟件架構),在HCI(Host Controller Interface(host和Controller的接口層))的上層。

(2)BT Controller:邏輯實體(控制偏底層和硬件),在HCI(Host Controller Interface)的下層。

根據Host與Controller的組成關系,常見的藍牙芯片也分為以下幾種:

(1)單模藍牙芯片:單一傳統藍牙的芯片,單一低功耗藍牙的芯片。即1個Host結合1個Controller。

(2)雙模藍牙芯片:同時支持傳統藍牙和低功耗藍牙的芯片。即1個Host結合多個Controller

對于BT Host而言雖然下層有不同的BT Controller但是我們都可以用軟件來分開控制,寫代碼而已,所以就一個BT Host就行。

藍牙協議是通信協議的一種,一般而言,我們把某個協議的實現代碼稱為協議棧(protocol stack),BLE協議棧就是實現低功耗藍牙協議的代碼。

4.BLE低功耗藍牙協議棧框架

要實現一個BLE應用,首先需要一個支持BLE射頻的芯片,然后還需要提供一個與此芯片配套的BLE協議棧,最后在協議棧上開發自己的應用

可以看出BLE協議棧是連接芯片和應用的橋梁,是實現整個BLE應用的關鍵。

簡單來說,因為是由上到下的層層協議,所以BLE協議棧主要用來對你的應用數據進行層層封包(類似與CAN協議的控制段,應答段等),以生成一個滿足BLE協議的空中數據包,也就是說,把應用數據包裹在一系列的幀頭(header)和幀尾(tail)中。

藍牙協議規定了兩個層次的協議,分別為藍牙核心協議(Bluetooth Core)和藍牙應用層協議(Bluetooth Application)。

藍牙核心協議關注對藍牙核心技術的描述和規范,它只提供基礎的機制,并不關心如何使用這些機制;

藍牙應用層協議,是在藍牙核心協議的基礎上,根據具體的應用需求,百花齊放,定義出各種各樣的策略,如FTP、文件傳輸、局域網等等。

ESP32-C3中的藍牙功能:

ESP32-C3支持Bluetooth 5(LE)。下載好固件之后(我們前面下載的固件已經支持Wi-Fi和藍牙了),STM32仍然可以通過AT指令操作藍牙。

1.BLE角色劃分

在 Bluetooth LE 協議棧中不同的層級有不同的角色劃分。這些角色劃分互不影響。

LL:設備可以劃分為主機和從機,從機廣播,主機可以發起連接。

GAP:定義了4種特定角色:廣播者、觀察者、外圍設備和中心設備。(我們在編程不會涉及它)

GATT:設備可以分為服務端和客戶端。定義了數據的組織結構和服務器端提供哪些服務,客戶端就可以調用哪些服務。如果其為主機廣播信號發起連接,本質底層是聯網操作和上層是否是服務器或客戶端無關,如果對方連接了主機,對方就是服務器,主機就是客戶端,就好比你用手機監聽到藍牙,連上藍牙不就是將手機數據發到藍牙上,我們就可以戴藍牙耳機聽音樂了

2.BLE的地址(主機和從機建立連接后就知曉對方地址)

分為公共地址和隨機地址。公共地址:BLE的公共地址,就類似于我們日常的身份證號碼,是全球唯一的且不可改變的。為了保證BLE公共地址的全球唯一性,其需要向IEEE購買,然后IEEE組織就會對應地分配公共地址給買家。BLE的公共地址是全球唯一的,且在BLE設備的整個生命周期都不會改變。總長度為6個字節,共48位。

隨機地址除了公共地址類型之外,還有一個隨機地址類型,提供額外的隱私保護,其又分為靜態地址(Static Address)和私有地址(Private Address),它們之間主要通過最高的2位有效位來區分。

(1)靜態地址。總長度也是48位,但是最高位的2位必須是0b11。隨機部分至少有一個位是0和1。也就是說不能全部是0或全部是1。設備重啟之前不會改變。

(2)私有地址。總長度也是48位。最高位是00/01。隨機部分至少有一個位是0和1。也就是說不能全部是0或全部是1。定時更新改變。

3.藍牙掃描和通訊,藍牙結點要廣播信息通知別人進行連接

咱們藍牙就作為LL的從機角色和GATT的服務端角色每各一段時間就發送廣播信號,告訴別的設備咱們的存在。藍牙可以使用40多個信道,但是廣播信號只允許在37,38,39三個信道發出去,所以我們就能配是按順序依次發出去,還是單獨選一信道給發出去

通過掃描,主機能收到從機的廣播包和回應數據包,然后給這些從機設備發連接請求,連接上后就能通信了。?這里還能配藍牙能不能掃到,掃到能不能連,只能誰來連.......都能設,可以看用戶指南

通訊可以配的:一個從機設備包括一個或者多個服務;一個服務中又可以包括一條或者多條特征值,每個特征值都有自己的屬性 Property,屬性的取值有:可讀 Read、可寫 Write、通知 Notify、指示Indicate。每個服務和特征值都有自己的唯一標識 UUID,標準UUID為128位,藍牙協議棧中一般采用16位,也就是兩個字節的UUID格式。

藍牙通訊案例:透傳模式下收發數據(選的芯片ESP32-C3)

用戶指南翻到Bluetooth LE AT實例就有教怎么配的,我們選這個例子

.設置藍牙透傳模式:

透傳模式:設備 A 發送一串字節流,通過藍牙透傳模式,設備 B 接收到的就是完全相同的字節流,就如同這兩個設備直接用一根線連接起來進行數據傳輸一樣,ESP32無需先知道數據有多長也不用做一堆命令解析的操作了,避免了普通數據傳輸模式的很多麻煩。

針對藍牙的操作:

1.在手機端下載Bluetooth LE(隨便在你手機的商城那搜索藍牙助手,隨便下個就行都能用)

2.初始化藍牙

    printf("設置模塊角色為:服務器...\n");uint8_t *cmd = "AT+BLEINIT=2\r\n";ESP32_SendCmd(cmd, strlen((char *)cmd));

3.創建藍牙服務

    // 3. 服務端創建服務printf("服務端創建服務...\n");cmd = "AT+BLEGATTSSRVCRE\r\n";ESP32_SendCmd(cmd, strlen((char *)cmd));

4.開啟藍牙服務

    printf("服務端開啟服務...\n");cmd = "AT+BLEGATTSSRVSTART\r\n";ESP32_SendCmd(cmd, strlen((char *)cmd));

5.獲取/設置藍牙自己的MAC地址,廠家就給設備買了唯一的MAC地址,我們直接查詢就行,避免我們藍牙和別的藍牙名稱一致分不出來

    printf("獲取服務端MAC地址...\n");cmd = "AT+BLEADDR?\r\n";ESP32_SendCmd(cmd, strlen((char *)cmd));

6.設置廣播參數,這內容太長了,大家去翻用戶指南看會更清晰

    printf("設置藍牙廣播參數...\n");cmd = "AT+BLEADVPARAM=50,50,0,0,7,0\r\n";ESP32_SendCmd(cmd, strlen((char *)cmd));

7.設置廣播數據

    printf("設置廣播數據...\n");cmd = "AT+BLEADVDATAEX=\"mydevice\",\"A123\",\"0102030405\",1\r\n";ESP32_SendCmd(cmd, strlen((char *)cmd));

8.開啟藍牙廣播

    // 8. 開始廣播printf("開始廣播...\n");cmd = "AT+BLEADVSTART\r\n";ESP32_SendCmd(cmd, strlen((char *)cmd));

9.查詢藍牙給連接的設備提供了什么服務,可寫也可以不寫

10.配置藍牙 SPP

?

    printf("配置 BLE SPP 參數...\n");cmd = "AT+BLESPPCFG=1,1,7,1,5\r\n";ESP32_SendCmd(cmd, strlen((char *)cmd));

11.使能藍牙 SPP(反饋OK,就說明能給手機發數據和收數據了)和手機連上了就開啟,斷開了就關閉

11.1使能SPP,和手機連上了就開啟,斷開了就關閉,要接收變化狀態就給SYSMSG配成100就是4唄

    printf("設置系統提示信息...\n");cmd = "AT+SYSMSG=4\r\n";ESP32_SendCmd(cmd, strlen((char *)cmd));

static uint8_t BLE_IsConnChanged(uint8_t msg[])
{// 1. 如果是BLE建立連接,就進入SPP透傳模式if (strstr((char *)msg, "+BLECONN:") != NULL){printf("有BLE客戶端連接,馬上進入SPP模式...\n");uint8_t * cmd = "AT+BLESPP\r\n";ESP32_SendCmd(cmd, strlen((char *)cmd));return 1;}// 2. 如果是BLE斷開連接,就退出透傳模式else if (strstr((char *)msg, "+BLEDISCONN:") != NULL){printf("BLE客戶端斷開連接,即將退出SPP模式...\n");// 直接串口發送 +++ 退出透傳模式HAL_UART_Transmit(&huart2, "+++", 3, 1000);// 延時等待HAL_Delay(2000);return 1;}// 3. 如果是其它類型的連接變化(Wi-Fi),不做任何處理,返回1else if(strstr((char *)msg, "WIFI CONNECTED") != NULL|| strstr((char *)msg, "WIFI GOT IP") != NULL|| strstr((char *)msg, "+DIST_STA_IP:") != NULL){return 1;}// 4. 其它情況默認是正常數據return 0;
}

11.2STM32通過串口給ESP32發什么,藍牙就會通過電磁波向外發送一樣的數據

// 發送數據
void BLE_SendData(uint8_t txBuff[], uint16_t txLen)
{// 直接通過串口發送數據HAL_UART_Transmit(&huart2, txBuff, txLen, 1000);
}

11.3接收數據,沒有進入SPP模式時接收到的都是藍牙的內部數據,不是手機發來的,所以要判斷連沒連上手機

// 接收(讀取)數據
void BLE_ReadData(uint8_t rxBuff[], uint16_t *rxLen)
{// 從串口接收數據或狀態改變信息HAL_UARTEx_ReceiveToIdle(&huart2, rxBuff, 1024, rxLen, 1000);// 如果是空數據,rxLen = 0,就直接返回if (*rxLen == 0){return;}// 如果是連接變化信息,執行相應的SPP操作,并清零len,表示不是正常數據if ( BLE_IsConnChanged(rxBuff) ){*rxLen = 0;}
}

針對手機的操作:

1.藍牙開啟廣播后,手機就能在藍牙助手那連藍牙了

2.連上后,要勾選需要的服務(點單),勾上藍牙就能上菜了

3.通過串口助手給藍牙發數據

ble.h

#ifndef __BLE_H
#define __BLE_H#include "esp32.h"// 初始化
void BLE_Init(void);// 發送數據
void BLE_SendData(uint8_t txBuff[], uint16_t txLen);// 接收(讀取)數據
void BLE_ReadData(uint8_t rxBuff[], uint16_t *rxLen);#endif

ble.c


#include "ble.h"// 初始化
void BLE_Init(void)
{// 1. 初始化ESP32ESP32_Init();// 2. 設置藍牙模塊角色:2-serverprintf("設置模塊角色為:服務器...\n");uint8_t *cmd = "AT+BLEINIT=2\r\n";ESP32_SendCmd(cmd, strlen((char *)cmd));// 3. 服務端創建服務printf("服務端創建服務...\n");cmd = "AT+BLEGATTSSRVCRE\r\n";ESP32_SendCmd(cmd, strlen((char *)cmd));// 4. 服務端開啟服務printf("服務端開啟服務...\n");cmd = "AT+BLEGATTSSRVSTART\r\n";ESP32_SendCmd(cmd, strlen((char *)cmd));// 5. 獲取服務端MAC地址printf("獲取服務端MAC地址...\n");cmd = "AT+BLEADDR?\r\n";ESP32_SendCmd(cmd, strlen((char *)cmd));// 6. 設置藍牙廣播參數printf("設置藍牙廣播參數...\n");cmd = "AT+BLEADVPARAM=50,50,0,0,7,0\r\n";ESP32_SendCmd(cmd, strlen((char *)cmd));// 7. 設置廣播數據printf("設置廣播數據...\n");cmd = "AT+BLEADVDATAEX=\"mydevice\",\"A123\",\"0102030405\",1\r\n";ESP32_SendCmd(cmd, strlen((char *)cmd));// 8. 開始廣播printf("開始廣播...\n");cmd = "AT+BLEADVSTART\r\n";ESP32_SendCmd(cmd, strlen((char *)cmd));// 9. 配置 BLE SPP參數printf("配置 BLE SPP 參數...\n");cmd = "AT+BLESPPCFG=1,1,7,1,5\r\n";ESP32_SendCmd(cmd, strlen((char *)cmd));// 10. 配置在透傳模式下,連接發生變化時會傳輸一個提示信息printf("設置系統提示信息...\n");cmd = "AT+SYSMSG=4\r\n";ESP32_SendCmd(cmd, strlen((char *)cmd));
}// 發送數據
void BLE_SendData(uint8_t txBuff[], uint16_t txLen)
{// 直接通過串口發送數據HAL_UART_Transmit(&huart2, txBuff, txLen, 1000);
}// 自定義函數,判斷是否有連接變化信息;如果有則返回1,沒有返回0
static uint8_t BLE_IsConnChanged(uint8_t msg[]);// 接收(讀取)數據
void BLE_ReadData(uint8_t rxBuff[], uint16_t *rxLen)
{// 從串口接收數據或狀態改變信息HAL_UARTEx_ReceiveToIdle(&huart2, rxBuff, 1024, rxLen, 1000);// 如果是空數據,rxLen = 0,就直接返回if (*rxLen == 0){return;}// 如果是連接變化信息,執行相應的SPP操作,并清零len,表示不是正常數據if ( BLE_IsConnChanged(rxBuff) ){*rxLen = 0;}
}static uint8_t BLE_IsConnChanged(uint8_t msg[])
{// 1. 如果是BLE建立連接,就進入SPP透傳模式if (strstr((char *)msg, "+BLECONN:") != NULL){printf("有BLE客戶端連接,馬上進入SPP模式...\n");uint8_t * cmd = "AT+BLESPP\r\n";ESP32_SendCmd(cmd, strlen((char *)cmd));return 1;}// 2. 如果是BLE斷開連接,就退出透傳模式else if (strstr((char *)msg, "+BLEDISCONN:") != NULL){printf("BLE客戶端斷開連接,即將退出SPP模式...\n");// 直接串口發送 +++ 退出透傳模式HAL_UART_Transmit(&huart2, "+++", 3, 1000);// 延時等待HAL_Delay(2000);return 1;}// 3. 如果是其它類型的連接變化(Wi-Fi),不做任何處理,返回1else if(strstr((char *)msg, "WIFI CONNECTED") != NULL|| strstr((char *)msg, "WIFI GOT IP") != NULL|| strstr((char *)msg, "+DIST_STA_IP:") != NULL){return 1;}// 4. 其它情況默認是正常數據return 0;
}

main.c

// 全局變量,接收數據緩沖區、長度
uint8_t rxBuff[1024];
uint16_t rxLen;int main(void)
{/* USER CODE BEGIN 1 *//* USER CODE END 1 *//* MCU Configuration--------------------------------------------------------*//* Reset of all peripherals, Initializes the Flash interface and the Systick. */HAL_Init();/* USER CODE BEGIN Init *//* USER CODE END Init *//* Configure the system clock */SystemClock_Config();/* USER CODE BEGIN SysInit *//* USER CODE END SysInit *//* Initialize all configured peripherals */MX_GPIO_Init();MX_USART1_UART_Init();// MX_USART2_UART_Init();/* USER CODE BEGIN 2 */// 藍牙初始化BLE_Init();/* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){// 讀取數據BLE_ReadData(rxBuff, &rxLen);// 如果接收到數據,就原樣發回去if (rxLen > 0){printf("接收到數據!數據長度 = %d, 內容 = %.*s\n", rxLen, rxLen, rxBuff);BLE_SendData(rxBuff, rxLen);// 將長度清0rxLen = 0;}/* USER CODE END WHILE *//* USER CODE BEGIN 3 */}/* USER CODE END 3 */
}

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

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

相關文章

方案B,version1

我們重新設計起步階段的步驟,目標是:通過運行PowerShell腳本和配置GitHub Actions工作流(deploy.yml)來實現自動化部署。 要求: 用私有倉庫(my-website-source-SSH)存儲源碼。 通過GitHub Actions自動構建(這里只是簡單的Hello World,所以構建步驟可以簡化為復制文件…

Linux --- 進程

一、進程概念 在 Linux 系統中,??進程(Process)?? 是程序執行的動態實例,是操作系統進行資源分配和調度的基本單位。 ??1. 程序 vs 進程?? ??程序(Program)??:是靜態的代碼集合&…

Cgroup 控制組學習(三)在容器中使用 CGroups

一、CGroups 關于mememory的限制操作 cgroup關于cpu操作 關于memeory cgroup的幾個要點 ① memeory限額類 1、memory.limit_bytes:硬限制--> 限制最大內存使用量,單位有k、m、g三種,填-1則代表無限制,默認是字節2、memory.soft_limi…

SpringBoot面試基礎知識

SpringBoot 是面試中后端開發崗位的高頻考點,以下是核心考點整理:1. SpringBoot 基礎概念- 定義:SpringBoot 是 Spring 框架的簡化版,通過“自動配置”“起步依賴”等特性,簡化 Spring 應用的搭建和開發,減…

Java面試全方位解析:從基礎到AI的技術交鋒

Java面試全方位解析:從基礎到AI的技術交鋒 面試場景:互聯網大廠Java工程師崗位面試 面試官:您好,我是今天的面試官,接下來我們將進行三輪技術面試。 謝飛機:您好您好!我是謝飛機,特別…

Web Worker:解鎖瀏覽器多線程,提升前端性能與體驗

目錄 一、Web Worker 是什么? 核心特性 類型 二、為什么需要 Web Worker?(單線程的痛點) 三、Web Worker 的典型使用場景 四、一個簡單的代碼示例 (專用 Worker) 五、使用 Web Worker 的注意事項 六、總結 一、Web Worker 是什么? 簡…

LabVIEW命令行調用與傳參功能

該功能一方面借助 Formatinto String 構建命令行字符串,實現LabVIEW 環境下命令行調用 VI 并傳參;另一方面,針對 Mac 平臺,通過解析應用 Info.plist 文件,處理 LabVIEW 可執行文件路徑,完善跨平臺命令行調用…

使用FRP搭建內網穿透工具,自己公網服務器獨享內外網端口轉發

內網穿透,也即 NAT 穿透,進行 NAT 穿透是為了使具有某一個特定源 IP 地址和源端口號的數據包不被 NAT 設備屏蔽而正確路由到內網主機。簡單來說,就是讓互聯網(外網)設備能訪問局域網(內網)設備提…

JavaWeb01——基礎標簽及樣式(黑馬視頻筆記)

1.如何用VScode寫html代碼 1. 首先在vscode上安裝一些插件,插件如下: 2.打開你要寫入的html文件的文件夾,然后右擊“ 新建文件”,命名 “xxx.html”, 3.如果是寫 css文件,那么也是右擊“新建文件”,命名“x…

在2G大小的文件中,找出高頻top100的單詞

將 2GB 的大文件分割為 2048 個大小為 512KB 的小文件,采用流式讀取方式處理,避免一次性加載整個文件導致內存溢出。初始化一個長度為 2048 的哈希表數組,用于分別統計各個小文件中單詞的出現頻率。利用多線程并行處理機制遍歷所有 2048 個小…

基于LNMP分布式個人云存儲

1.準備工作a.關閉兩臺虛擬機的安全軟件客戶端:[rootmaster ~]# systemctl stop firewalld [rootmaster ~]# systemctl disable firewalld [rootmaster ~]# systemctl status firewalld ○ firewalld.service - firewalld - dynamic firewall daemonLoaded: loaded (…

指針運算全攻略:加減、比較與排序

常見的指針指針運算說明1.指針與整數的加減運算對指針可以進行加法運算&#xff0c;即p n或者p - n。其結果依舊是一個是一個指針&#xff0c;新的指針是在原來的地址值基礎上加上/減去n *(sizeof(指針指向的數據類型)&#xff09;個字節。示例&#xff1a;#include<stdio.…

物聯網安裝調試-物聯網網關

物聯網網關作為連接終端設備與云平臺的核心樞紐,其分類與選型需結合功能定位、硬件性能、連接方式及應用場景等多維度考量。以下從分類體系和產品推薦兩方面系統梳理,助您高效決策: ?? 一、物聯網網關分類體系 1. 按功能定位劃分 類型 核心能力 典型場景 代表產品 邊緣計…

Jenkins教程(自動化部署)

Jenkins教程(自動化部署) 1. Jenkins是什么&#xff1f; Jenkins是一個開源的、提供友好操作界面的持續集成(CI)工具&#xff0c;廣泛用于項目開發&#xff0c;具有自動化構建、測試和部署等功能。Jenkins用Java語言編寫&#xff0c;可在Tomcat等流行的servlet容器中運行&…

linux 驅動驗證是否成功 之 查看moudle信息

這些是 Linux 內核模塊&#xff08;.ko&#xff09;中的元信息&#xff08;metadata&#xff09;&#xff0c;可以通過如下方式查看&#xff1a;? 1. 使用 modinfo 命令查看已加載或已編譯模塊信息 示例&#xff1a; modinfo aw2013.ko輸出內容大概如下&#xff1a; filename:…

瀏覽器關閉之前請求接口到后端

2025.07.24今天我學習了如何在瀏覽器關閉之前請求一個接口返回到后端。可以用performance.navigation判斷是瀏覽器關閉&#xff0c;還是瀏覽器刷新&#xff0c;因為我這邊只需要瀏覽器關閉的時候才去觸發1. 利用performance API&#xff08;刷新檢測&#xff09; 刷新頁面時&am…

MySQL批量數據處理與事務管理

MySQL是一種廣泛應用的關系型數據庫管理系統&#xff0c;尤其在數據分析和業務邏輯處理方面具有重要地位。在數據量龐大的業務場景中&#xff0c;批量數據處理和事務管理是提高效率和保障數據一致性的重要手段。掌握高效的批量數據操作方法與事務管理技巧&#xff0c;不僅能夠提…

iOS網絡之異步加載

為什么你的圖片要異步加載&#xff1f;在仿寫天氣預報時&#xff0c;我們常常需要從網絡加載天氣圖標&#xff0c;例如顯示某個小時的天氣狀態圖標。這看似簡單的事情&#xff0c;如果處理不當&#xff0c;卻很容易造成界面卡頓&#xff0c;甚至影響整個 App 的用戶體驗。錯誤做…

C#值類型屬性的典型問題

問題復現&#xff1a;值類型屬性的副本問題以下代碼展示了值類型屬性的典型問題&#xff1a;struct Point {public int X;public int Y; }class MyClass {public Point Position {get; set;} }// 使用屬性修改結構體&#xff08;無效&#xff01;&#xff09; var obj new MyC…

機器學習基礎-k 近鄰算法(從辨別水果開始)

一、生活中的 "分類難題" 與 k 近鄰的靈感 你有沒有這樣的經歷&#xff1a;在超市看到一種從沒見過的水果&#xff0c;表皮黃黃的&#xff0c;拳頭大小&#xff0c;形狀圓滾滾。正當你猶豫要不要買時&#xff0c;突然想起外婆家的橘子好像就是這個樣子 —— 黃色、圓…