一、原理部分
之前用外設都是直接用的硬件自帶的庫,雖然以前有學過原理和時序,但是因為工作其實也很少會有需要gpio模擬串口的情況,但總會有串口用完,但是需要一個類似打印串口的情況。今天也是開整,然后用硬件的庫對比一下。
1、協議要點
①、異步通訊:
????????通信雙方沒有共享時鐘信號。依靠預先約定好的波特率 (Baud Rate) 來同步。
②、數據幀格式 (常見):
????????起始位 (Start Bit): 1位,低電平 (邏輯0)。標志著數據幀的開始,用于同步接收方。
????????數據位 (Data Bits): 5-9位(通常8位)。從最低有效位 (LSB) 開始發送/接收。
????????校驗位 (Parity Bit): 可選,1位(奇校驗、偶校驗或無校驗)。用于簡單的錯誤檢測。
????????停止位 (Stop Bit): 1位、1.5位或2位,高電平 (邏輯1)。標志數據幀的結束,并為下一幀提供緩沖空間。
????????空閑狀態 (Idle State): 當沒有數據傳輸時,數據線保持高電平。
????????波特率: 定義了每秒傳輸的符號(位)數。例如,9600 bps 表示每秒傳輸9600位數據。位周期 (Bit Time) 是波特率的倒數:T_bit = 1 / BaudRate。這是軟件模擬時最關鍵的參數。
③、時間計算
例如現在波特率為9600,T_bit = 1 / 9600 ≈ 0.0001041667 秒 = 104.1667 微秒 (μs)
用我們最常見的格式,8為數據位,1位起始位,1位停止位,即10位
計算傳輸一個字節 (10位) 所需的總時間 (T_frame):
T_frame = 10 位 * T_bit = 10 * 104.1667 μs ≈ 1041.667 μs ≈ 1.042 ms
可以理解為 大約 1 毫秒 發送一個字節。
發送端要求:需要在 104.17 μs 的整數倍時間點精確切換GPIO電平。
接收端要求:需要在起始位下降沿后等待1.5*T_bit ≈156.25us進行第一次采樣(定位到數據位中間),之后每隔104.17us?采樣一次后續位
波特率115200的話即速度是 9600 波特率的 12 倍,這里就不再寫那么長
2、GPIO 模擬發送 (TX)?
①、配置:
將選定的GPIO引腳配置為推挽輸出模式,初始狀態設置為高電平(空閑狀態)。
②、發送一個字節的流程:
a. 起始位:
將GPIO引腳拉低,并保持1個完整的位周期 (T_bit)。這告訴接收方數據開始傳輸。
b. 數據位 (LSB First):
從要發送字節的最低位 (bit 0) 開始。
根據該位的值是0還是1,將GPIO引腳設置為低電平或高電平。
保持這個電平狀態1個完整的位周期 (T_bit)。
接著發送下一位 (bit 1),重復上述電平設置和延時過程。
依此類推,直到發送完指定的數據位數 (通常是8位)。
c. 校驗位 (如果啟用):
計算已發送數據位的奇偶性 (奇校驗或偶校驗)。
根據計算結果,將GPIO引腳設置為相應的電平 (0或1)。
保持這個電平狀態1個完整的位周期 (T_bit)。
d. 停止位:
將GPIO引腳拉高。
保持高電平狀態1個 (或1.5/2個) 完整的位周期 (T_bit)。這標志著一幀數據的結束,并使線路恢復到空閑狀態。
需要注意的點為:
精確的時序: 每個位(起始位、數據位、校驗位、停止位)的電平持續時間必須嚴格等于位周期 (T_bit)。這是模擬成功的最核心要求。任何累積的時序誤差都會導致通信失敗。
位順序: 嚴格遵守協議約定的位順序(通常是LSB first)。
中斷的影響這里我就先不考慮了,因為我只是回顧下原理,不是真的要用模擬gpio來實現項目需求
3、GPIO 模擬接收 (RX)?
①、配置:
將選定的GPIO引腳配置為浮空輸入或上拉輸入模式(確保空閑時為高電平)。
②、檢測起始位:
持續輪詢(或在中斷中檢測)GPIO引腳狀態。
當檢測到引腳從高電平變為低電平(下降沿)時,可能是一個起始位開始。
③、同步與采樣:
檢測到下降沿后,不能立即采樣數據。需要避開信號邊沿可能不穩定的區域。
關鍵延時: 等待 1.5個位周期 (1.5 * T_bit)。這個延時將采樣點定位到第一個數據位 (LSB) 的中間位置。這是提高抗噪性和采樣穩定性的常用技巧。
④、讀取數據位:
在預期的采樣點(每個數據位的中間位置),讀取GPIO引腳的電平狀態。
將讀取到的電平值 (0或1) 存入接收字節的對應位(從LSB開始)。
每讀取完一位,等待1個位周期 (T_bit) 到達下一位的采樣點。
重復這個過程,讀取完所有數據位 (如8位)。
⑤、讀取校驗位 (如果啟用):
在下一個采樣點(等待1個位周期后),讀取GPIO引腳的電平作為校驗位。
根據協議(奇/偶校驗)和收到的數據位計算期望的校驗位,與實際收到的校驗位比較,進行錯誤檢查(可選)。
⑥、檢測停止位:
在下一個采樣點(等待1個位周期后),讀取GPIO引腳的電平。期望是高電平 (1)。
如果檢測到高電平,說明停止位有效,一幀數據接收基本完成(可考慮校驗結果)。
如果檢測到低電平,說明發生了幀錯誤(Frame Error)
需要注意的點為:1.5 * T_bit 和后續的 1 * T_bit 延時必須非常準確。采樣點定位在位的中間是最佳實踐。
二、配置工程
我的板子是APM32F107VC EVAL_V1.0
因為之前看是pin to pin,我以為是完全兼容,所以用cubemx生成了工程,后來程序一直跑不了,查了一下要用極海官方的sdk才行。
在官方下載sdk和pack即可
之前都是用IAR,現在公司用keil,就用回keil
我用的是他串口中斷的例程做base
APM32F107_EVAL_SDK_v1.0\Examples\USART\USART_Interrupt\Project\MDK
如上圖復制到自己工程的文件夾
再將路徑改一下
然后這里每個都要重新添加一下
編譯成功,跑了一下程序OK
三、修改程序
1、定義的結構體和宏
用PD13做TX,PD14做RX
我這里做的波特率9600,沒有做太高的
tx rx初始化,其中rx配置為中斷
2、初始化
其中rx配置為外部中斷
兩個定時器,定時器5做幀間隔的計時,定時器6為判斷數據是否結束(跟之前stm32接收不定長數據差不多STM32+Cubemx+Esp8266(一)串口接收不定長數據_stm32cubemx esp8266-CSDN博客)
2、發送部分
單個字符
字符串
發送數組
這里我是直接延時硬等,可以再加一個定時器用作發送。
3、接收部分
在接收首個低電平的時候,即為起始幀,然后打開定時器5處理后邊的數據
定時器5的中斷就是數據處理
這里我是1為起始位,8位數據位,1位停止位,無校驗,依次處理就好,用UART_TEST_Handle.rxBitCount作判斷當前在第幾位。數據保存到UART_TEST_Handle.rxBuffer這個buff,
然后每收到一個字節都打開定時器6,定時器6做了11位的時間,若是定時器6中斷溢出則說明這幀數據結束,標志位置1
4、主函數
檢測標志位是否置1,置1則發送rx的buffer,相當于回顯
實際上我這里雖然做了發送隊列,但我接收buff沒做,所以也是只能發一條而已。這個是之前打印其他比較多的時候做的隊列
四、驗證程序:
成功
然后發0x55,用邏輯分析儀看看