串口通信學習

不需要校驗位就選8位,需要校驗位就選9位!

USRT

USART框圖

STM32的外設引腳

這是USART的基本結構。

數據幀,八位是

這個公式還是很重要的!

如果在編輯器里面使用printf打印漢字的話,會出現亂碼的話,前提是你的編碼格式使用的UTF8,就在keil5里面這里加上這個--no-multibyte-chars

HEX 數據包這個概念在不同領域有不同的含義,但核心思想是一樣的:
它指的是用十六進制(Hexadecimal)形式表示的一個數據包,數據包包含通信所需的頭部、數據區和校驗等信息

1.“HEX”是什么意思?

  • HEX十六進制 的縮寫。

  • 在計算機和嵌入式通信中,二進制數據通常用十六進制表示,因為它更簡潔、人類更容易閱讀。

  • 例如:

    • 二進制:1010 1111

    • 十六進制:0xAF

2.“數據包”是什么意思?

  • 數據包(Data Packet)是通信雙方傳輸的完整數據單元

  • 一個數據包通常包含:

    1. 幀頭 / 起始標志(Start Byte / Header)
      用來標識一個包的開始,例如 0xAA 0x55

    2. 長度字段(Length)
      表示數據區的字節數

    3. 命令字 / 功能碼(Command)
      表示這個包的用途(如讀取、寫入、狀態查詢)

    4. 數據區(Data)
      實際要傳輸的內容

    5. 校驗碼(Checksum / CRC)
      用來檢測數據是否損壞

    6. 幀尾 / 結束標志(End Byte)
      表示包的結束(可選)

3.HEX 數據包的定義示例

假設我們設計一個用于串口通信的 HEX 數據包格式:

[0]   幀頭1         1 byte    固定為 0xAA
[1]   幀頭2         1 byte    固定為 0x55
[2]   長度           1 byte    數據區+命令字的總長度
[3]   命令字         1 byte    例如 0x01 表示讀取數據
[4..n]數據區         N byte    實際數據
[n+1] 校驗碼         1 byte    所有字節異或和或 CRC
[n+2] 幀尾           1 byte    固定為 0x0D

舉例:

AA 55 04 01 10 20 35 0D
  • AA 55?幀頭

  • 04?長度(后面 4 個字節:01 10 20 35)

  • 01?命令字(讀取數據)

  • 10 20?數據區(兩個字節的數據)

  • 35?校驗碼

  • 0D?幀尾

4. 為什么要用 HEX 表示數據包?

  • 可讀性好
    十六進制每兩個字符正好表示一個字節

  • 方便調試
    串口調試助手、邏輯分析儀等都用 HEX 格式

  • 跨平臺兼容
    HEX 表示的是原始二進制,不受編碼格式影響

文本數據包(Text Data Packet)指的是以文本形式(可讀字符)來組織和傳輸的一個完整數據單元,它和 HEX 數據包最大的區別是:

  • HEX 數據包里每個字節是二進制,調試時常用十六進制顯示

  • 文本數據包直接用可見字符(ASCII/UTF-8等編碼)表示內容,例如 "TEMP=25.6;HUM=78%\n"

1.文本數據包的核心定義

一個文本數據包一般包含以下部分:

  • 起始標志(Start Flag)

    • 用于標識數據包的開始

    • 例如 "$$", "<START>", "#"

  • 數據內容(Payload / Body)

    • 全部是可見字符(字母、數字、符號)

    • 一般使用分隔符分割字段,例如 ,;| 或空格

  • 結束標志(End Flag)

    • 表示數據包結束

    • 常用 \n(換行符)、\r\n(回車換行)、"<END>"

  • 可選校驗(Checksum)

    • 校驗可以直接用十進制數字或十六進制字符串表示

    • 放在數據末尾,方便檢測數據完整性

2. 文本數據包示例

串口發送傳感器數據

$TEMP=25.6,HUM=78%,BAT=3.7V*
  • $?起始標志

  • TEMP=25.6,HUM=78%,BAT=3.7V?數據區(用逗號分隔字段)

  • *?結束標志

帶校驗的例子(NMEA GPS 協議風格)

$GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M,,*47
  • $GPGGA?起始標志+數據類型

  • 逗號分隔的多個字段

  • *47?末尾 * 后是校驗值(XOR 校驗)

自定義協議例子

<START>ID=001;CMD=READ;TEMP=25.6;HUM=78;<END>
  • <START>?起始標志

  • ID=001;CMD=READ;TEMP=25.6;HUM=78;?數據區,字段以 ; 分隔

  • <END>?結束標志

3.文本數據包的優缺點

優點:

  • 人類可直接閱讀、調試方便(用串口助手就能看懂)

  • 跨平臺性好,不依賴字節序

  • 可直接使用字符串處理函數解析

缺點:

  • 占用帶寬較大(字符比原始二進制長)

  • 解析速度慢于固定結構的 HEX 數據包

  • 對浮點數等類型需要額外轉換(ASCII ? 數值)

4.文本數據包的典型應用

  • 串口調試協議(如 AT 命令、NMEA GPS 數據)

  • HTTP、MQTT 等網絡應用層協議

  • 傳感器調試輸出

  • 物聯網設備日志與命令傳輸

寄存器

在計算機和單片機(包括 STM32、51 單片機等)中,寄存器(Register)是位于 CPU 內部的一種容量極小、速度極快的存儲單元,用來臨時保存和控制數據、指令以及硬件狀態。

你可以把它想象成 CPU 手邊的“超高速便利貼”

  • 內存(RAM)像是在隔壁房間的倉庫,取數據需要跑過去

  • 寄存器就在 CPU 旁邊,一伸手就能拿到

1.寄存器的分類

寄存器按用途大致分為兩大類:

① 通用寄存器

  • 作用:臨時保存運算數據、中間結果

  • 例子:x86 架構的 EAXEBX,ARM 架構的 R0 ~ R12

  • 特點:編譯器和匯編程序可以自由使用

② 特殊功能寄存器(SFR, Special Function Register)

  • 作用:控制硬件外設、反映狀態

  • 這些寄存器直接映射到硬件電路中,通過它們就能控制 GPIO、定時器、串口等功能

  • 在 STM32 中,這些寄存器是內存映射寄存器,用地址訪問,比如:

  • GPIOA->ODR = 0x01; // 讓 PA0 輸出高電平
    

    這里的 ODR(Output Data Register)就是 GPIO 的輸出數據寄存器。

2. 寄存器的特點

  • 速度極快(比 RAM 還快)

  • 容量很小(幾十到幾百個寄存器)

  • 與 CPU/外設直接連接

  • 通過位(bit)控制硬件功能

3. 寄存器在單片機中的例子

以 STM32F103 為例,假設要點亮 PA5 引腳上的 LED:

RCC->APB2ENR |= (1 << 2); // 開啟 GPIOA 時鐘
GPIOA->CRL &= ~(0xF << 20); // 清空 PA5 模式位
GPIOA->CRL |=  (0x1 << 20); // 設置 PA5 為推挽輸出
GPIOA->ODR |=  (1 << 5);    // 置位 PA5 輸出高電平
  • RCC->APB2ENR:外設時鐘使能寄存器

  • GPIOA->CRL:端口配置寄存器低位(控制 PA0~PA7)

  • GPIOA->ODR:輸出數據寄存器

這些寄存器本質上都是內存地址,比如 GPIOA->ODR 實際是:

0x4001080C

往這個地址寫 1,就等于給 PA5 腳送高電平。

4.用簡單比喻理解

  • 寄存器:CPU 桌上的小便簽,拿取速度最快(直接操作)

  • RAM:隔壁房間的文件柜(速度較慢)

  • 硬盤:地下倉庫(速度最慢)

C語言可變參數

C 語言可變參數(Variable Arguments)指的是一個函數在聲明時參數的數量不固定,可以根據調用時的需要傳入不同數量的實參。

最典型的例子就是標準庫中的 printf() 函數:

printf("Hello %s, age %d\n", "Tom", 18);

printf 的第一個參數是固定的格式化字符串,后面跟多少參數由格式字符串決定,這就是可變參數的用法。

一、、可變參數函數的聲明方式

在函數形參列表的末尾使用省略號 ... 表示:

#include <stdarg.h> // 必須包含的頭文件void myFunc(int count, ...); // count 表示后面有多少參數
  • 固定參數:省略號前的部分,必須有至少一個固定參數(方便定位可變參數起點)。

  • 可變參數:省略號 ... 表示數量和類型在編譯期不固定。

二、可變參數的原理

在 C 語言中,可變參數通過 棧傳遞stdarg.h 提供了訪問它們的宏:

  • va_list?—— 保存參數信息的變量類型

  • va_start?—— 初始化 va_list,定位到可變參數起點

  • va_arg?—— 取出一個參數

  • va_end?—— 清理工作

三、可變參數函數實現示例

例如寫一個求任意數量整數和的函數:

#include <stdio.h>
#include <stdarg.h>// sum(count, ...): 傳入 count 個整數,返回它們的和
int sum(int count, ...) {va_list args;           // 定義參數列表變量va_start(args, count);  // 初始化,從 count 后的參數開始取int total = 0;for (int i = 0; i < count; i++) {total += va_arg(args, int); // 每次取出一個 int 參數}va_end(args);           // 清理return total;
}int main() {printf("%d\n", sum(3, 10, 20, 30)); // 輸出 60printf("%d\n", sum(5, 1, 2, 3, 4, 5)); // 輸出 15return 0;
}

四、注意事項

  • 類型安全性差
    編譯器無法檢查可變參數類型是否正確,比如 va_arg(args, int) 和實際類型不匹配會導致錯誤行為。

  • 必須依賴固定參數來控制讀取數量
    否則無法知道何時停止讀取。

  • 跨平臺注意數據對齊
    參數在棧上的對齊方式可能和平臺架構有關。

  • 宏和可變參數
    宏中也能用 ... 表示可變參數(C99 及之后)。

#include <stdio.h>
#include <stdarg.h>void show(int count, ...) {va_list args;va_start(args, count); // 定位到第一個可變參數for (int i = 0; i < count; i++) {int val = va_arg(args, int); // 依次取出一個 intprintf("%d\n", val);}va_end(args);
}int main() {show(3, 10, 20, 30);return 0;
}

內存棧圖示(調用 show(3, 10, 20, 30) 時)

假設我們是 x86 棧向下增長 的情況(地址從高到低),函數調用時的棧大致如下:

高地址
┌───────────────────────┐
│    返回地址            │ ← main 調用 show 后返回的地址
├───────────────────────┤
│ count = 3             │ ← 固定參數
├───────────────────────┤
│ 10                    │ ← 第1個可變參數
├───────────────────────┤
│ 20                    │ ← 第2個可變參數
├───────────────────────┤
│ 30                    │ ← 第3個可變參數
└───────────────────────┘
低地址

va_start(args, count)

  • va_start 的作用是:
    args 指針指向 count 后面的第一個可變參數(10)

  • 底層會用 count 在棧上的地址 + 它的大小(sizeof(count)) 來得到可變參數的起點。

  • args ──? 10
    

    va_arg(args, int)

  • va_arg 做了兩件事:

    1. 取出 args 當前指向位置的值(比如第一次是 10)

    2. args 移動到下一個參數的位置(加上 sizeof(int)

  • 取值過程:

  • 第1次:args=10 → 返回10 → args指向20
    第2次:args=20 → 返回20 → args指向30
    第3次:args=30 → 返回30 → args指向結束位置
    

    va_end(args)

  • va_end 主要是做清理,防止野指針問題(實際可能什么都不做,但必須寫)

? 總結:

  • va_start:定位到第一個可變參數

  • va_arg:取值并移動指針

  • va_end:結束可變參數處理

  • 棧上參數是連續存放的,所以可以用指針依次取出

定時器中斷

定時器中斷其實就是利用單片機(或 CPU)里的定時器硬件模塊,在設定的時間間隔自動觸發中斷服務函數,讓你在固定時間做某件事。

它結合了兩個東西:

  1. 定時器(硬件計時器)

  2. 中斷機制(硬件事件觸發 CPU 自動跳到某段代碼執行)

1.基本原理

可以把它想成一個廚房的鬧鐘

  • 你在鬧鐘上設定“10分鐘”

  • 鬧鐘(定時器硬件)開始計時

  • 時間一到,鬧鐘“叮”一下(產生中斷信號)

  • 你(CPU)放下手里的事,去處理鬧鐘(執行中斷函數)

  • 處理完再繼續原來的工作

在 STM32 或 51 單片機中:

  • 定時器寄存器 控制定時周期

  • 中斷控制器(NVIC)接收到定時器溢出事件后調用中斷服務函數(ISR)

2.定時器中斷的觸發流程

  • 配置定時器參數

    • 預分頻器(Prescaler):降低時鐘頻率

    • 自動重裝值(ARR):定時器計數到這個值時溢出

  • 使能定時器中斷

    • 設置定時器的 UIE(更新中斷使能)位

    • NVIC 使能對應的中斷通道

  • 啟動定時器

  • 計數溢出 → 觸發中斷請求(IRQ)

  • 執行中斷服務函數(ISR)

    • 在 ISR 中處理任務(如 LED 翻轉、計時器變量++ 等)

  • 清除中斷標志

    • 防止中斷反復觸發

3. STM32 定時器中斷示例

#include "stm32f10x.h"void TIM2_IRQHandler(void) {if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET) {TIM_ClearITPendingBit(TIM2, TIM_IT_Update); // 清除中斷標志GPIOA->ODR ^= (1 << 5); // 翻轉 PA5}
}void Timer2_Init(void) {RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);GPIO_InitTypeDef gpio;gpio.GPIO_Pin = GPIO_Pin_5;gpio.GPIO_Mode = GPIO_Mode_Out_PP;gpio.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &gpio);TIM_TimeBaseInitTypeDef tim;tim.TIM_Period = 9999; // ARRtim.TIM_Prescaler = 7199; // PSCtim.TIM_ClockDivision = TIM_CKD_DIV1;tim.TIM_CounterMode = TIM_CounterMode_Up;TIM_TimeBaseInit(TIM2, &tim);TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);NVIC_EnableIRQ(TIM2_IRQn);TIM_Cmd(TIM2, ENABLE);
}int main(void) {Timer2_Init();while (1) {// 主循環可做其他事}
}

上面例子里:

  • 定時器頻率 = 72MHz / (PSC+1) / (ARR+1) = 72MHz / 7200 / 10000 = 1Hz

  • 每秒進一次中斷,ISR 里翻轉一次 LED

4.定時器中斷的應用

  • 周期性任務調度(實時操作系統里的節拍)

  • LED 閃爍

  • 傳感器采樣定時

  • 電機 PWM 更新

  • 超時檢測

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

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

相關文章

面試經典150題[001]:合并兩個有序數組(LeetCode 88)

合并兩個有序數組&#xff08;LeetCode 88&#xff09; https://leetcode.cn/problems/merge-sorted-array/?envTypestudy-plan-v2&envIdtop-interview-150 1. 題目背景 你有兩個已經排好序的數組&#xff1a; nums1&#xff1a;前面是有效數字&#xff0c;后面是空位&…

快速安裝達夢8測試庫

計劃&#xff1a;數據庫名實例名PORT_NUMMAL_INST_DW_PORTMAL_HOSTMAL_PORTMAL_DW_PORTDMDWDBINST_1533615101192.168.207.612510135101*****[2025-08-11 15:14:34]***** Last login: Fri Jul 25 17:36:04 2025 from 192.168.88.48 [rootdm01 ~]# ip a 1: lo: <LOOPBACK,UP,…

Hive中優化問題

一、小文件合并優化Hive中的小文件分為Map端的小文件和Reduce端的小文件。(1)、Map端的小文件優化是通過CombineHiveInputFormat操作。相關的參數是&#xff1a;set hive.input.formatorg.apache.hadoop.hive.ql.io.CombineHiveInputFormat;(2)、Reduce端的小文件合并Map端的小…

tlias智能學習輔助系統--Maven高級-繼承

目錄 一、打包方式與應用場景 二、父子工程繼承關系 1. 父工程配置 2. 子工程配置 三、自定義屬性與引用屬性 1. 定義屬性 2. 在 dependencyManagement 中引用 3. 子工程中引用 四、dependencyManagement 與 dependencies 的區別 五、項目結構示例 六、小結 在實際開…

把 AI 押進“小黑屋”——基于 LLM 的隱私對話沙盒設計與落地

標簽&#xff1a;隱私計算、可信執行環境、LLM、沙盒、內存加密、TEE、SGX、Gramine ---- 1. 背景&#xff1a;甲方爸爸一句話&#xff0c;“數據不能出機房” 我們給某三甲醫院做智能問診助手&#xff0c;模型 70 B、知識庫 300 GB。 甲方只給了兩條鐵律&#xff1a; 1. 患者…

Java 大視界 -- Java 大數據在智能教育學習效果評估指標體系構建與精準評估中的應用(394)

Java 大視界 -- Java 大數據在智能教育學習效果評估指標體系構建與精準評估中的應用&#xff08;394&#xff09;引言&#xff1a;正文&#xff1a;一、傳統學習評估的 “數字陷阱”&#xff1a;看不全、說不清、跟不上1.1 評估維度的 “單行道”1.1.1 分數掩蓋的 “學習真相”…

Dubbo 3.x源碼(33)—Dubbo Consumer接收服務調用響應

基于Dubbo 3.1&#xff0c;詳細介紹了Dubbo Consumer接收服務調用響應 此前我們學習了Dubbo Provider處理服務調用請求的流程&#xff0c;現在我們來學習Dubbo Consumer接收服務調用響應流程。 實際上接收請求和接收響應同屬于接收消息&#xff0c;它們的流程的很多步驟是一樣…

棧和隊列:數據結構中的基礎與應用?

棧和隊列&#xff1a;數據結構中的基礎與應用在計算機科學的領域中&#xff0c;數據結構猶如大廈的基石&#xff0c;支撐著各類復雜軟件系統的構建。而棧和隊列作為兩種基礎且重要的數據結構&#xff0c;以其獨特的特性和廣泛的應用&#xff0c;在程序設計的舞臺上扮演著不可或…

服務端配置 CORS解決跨域問題的原理

服務端配置 CORS&#xff08;跨域資源共享&#xff09;的原理本質是 瀏覽器與服務器之間的安全協商機制。其核心在于服務器通過特定的 HTTP 響應頭聲明允許哪些外部源&#xff08;Origin&#xff09;訪問資源&#xff0c;瀏覽器根據這些響應頭決定是否放行跨域請求。以下是詳細…

Unity筆記(五)知識補充——場景切換、退出游戲、鼠標隱藏鎖定、隨機數、委托

寫在前面&#xff1a;寫本系列(自用)的目的是回顧已經學過的知識、記錄新學習的知識或是記錄心得理解&#xff0c;方便自己以后快速復習&#xff0c;減少遺忘。主要是C#代碼部分。十七、場景切換和退出游戲1、場景切換場景切換使用方法&#xff1a; SceneManager.LoadScene()&a…

用 Spring 思維快速上手 DDD——以 Kratos 為例的分層解讀

用 Spring 思維理解 DDD —— 以 Kratos 為參照 ? 在此前的學習工作中&#xff0c;使用的開發框架一直都是 SpringBoot&#xff0c;對 MVC 架構幾乎是肌肉記憶&#xff1a;Controller 接請求&#xff0c;Service 寫業務邏輯&#xff0c;Mapper 操作數據庫&#xff0c;這套套路…

docspace|Linux|使用docker完全離線化部署onlyoffice之docspace文檔協作系統(全網首發)

一、 前言 書接上回&#xff0c;Linux|實用工具|onlyoffice workspace使用docker快速部署&#xff08;離線和定制化部署&#xff09;-CSDN博客&#xff0c;如果是小公司或者比如某個項目組內部使用&#xff0c;那么&#xff0c;使用docspace這個文檔協同系統是非常合適的&…

【教程】如何高效提取胡蘿卜塊根形態和顏色特征?

胡蘿卜是全球不可或缺的健康食材和重要的經濟作物&#xff0c; 從田間到餐桌&#xff0c;從鮮食到深加工&#xff0c;胡蘿卜在現代人的飲食和健康中扮演著極其重要的角色&#xff0c;通過量化塊根形態和色澤均勻性&#xff0c;可實現對高產優質胡蘿卜品種的快速篩選。工具/材料…

Python初學者筆記第二十四期 -- (面向對象編程)

第33節課 面向對象編程 1. 面向對象編程基礎 1.1 什么是面向對象編程面向過程&#xff1a;執行者 耗時 費力 結果也不一定完美 面向對象&#xff1a;指揮者 省時 省力 結果比較完美面向對象編程(Object-Oriented Programming, OOP)是一種編程范式&#xff0c;它使用"對象&…

Go 語言 里 `var`、`make`、`new`、`:=` 的區別

把 Go 語言 里 var、make、new、: 的區別徹底梳理一下。1?? var 作用&#xff1a;聲明變量&#xff08;可以帶初始值&#xff0c;也可以不帶&#xff09;。語法&#xff1a; var a int // 聲明整型變量&#xff0c;默認值為 0 var b string // 默認值 ""…

計算機網絡---IP(互聯網協議)

一、IP協議概述 互聯網協議&#xff08;Internet Protocol&#xff0c;IP&#xff09;是TCP/IP協議族的核心成員&#xff0c;位于OSI模型的網絡層&#xff08;第三層&#xff09;&#xff0c;負責將數據包從源主機傳輸到目標主機。它是一種無連接、不可靠的協議&#xff0c;提供…

DataFun聯合開源AllData社區和開源Gravitino社區將在8月9日相聚數據治理峰會論壇

&#x1f525;&#x1f525; AllData大數據產品是可定義數據中臺&#xff0c;以數據平臺為底座&#xff0c;以數據中臺為橋梁&#xff0c;以機器學習平臺為中層框架&#xff0c;以大模型應用為上游產品&#xff0c;提供全鏈路數字化解決方案。 ?杭州奧零數據科技官網&#xff…

【工具】通用文檔轉換器 推薦 Markdown 轉為 Word 或者 Pdf格式 可以批量或者通過代碼調用

【工具】通用文檔轉換器 推薦 可以批量或者通過代碼調用 通用文檔轉換器 https://github.com/jgm/pandoc/ Pandoc - index 下載地址 https://github.com/jgm/pandoc/releases 使用方法: 比如 Markdown 轉為 Word 或者 Pdf格式 pandoc -s MANUAL.txt -o example29.docx …

【UEFI系列】Super IO

文章目錄一、什么是Super IO二、Super IO的作用常見廠商三、邏輯設備控制如何訪問SIO邏輯設備的配置寄存器具體配置數值四、硬件監控&#xff08;hardware monitor&#xff09;一、什么是Super IO Super Input/Output超級輸入輸出控制器。 通過LPC&#xff08;low pin count&a…

飛算 JavaAI 2.0.0 測評:自然語言編程如何顛覆傳統開發?

一、前言 在AI技術高速發展的今天&#xff0c;編程方式正在經歷一場革命。傳統的“手寫代碼”模式逐漸被AI輔助開發取代&#xff0c;而飛算JavaAI 2.0.0的推出&#xff0c;更是讓自然語言編程成為現實。 作為一名長期使用Java開發的程序員&#xff0c;我決定深度體驗飛算Java…