痞子衡嵌入式:常用的數據差錯控制技術(2)- 奇偶校驗(Parity Check)
大家好,我是痞子衡,是正經搞技術的痞子。今天痞子衡給大家講的是嵌入式里數據差錯控制技術-奇偶校驗。
在系列第一篇文章里,痞子衡給大家介紹了最簡單的校驗法-重復校驗,該校驗法實現簡單,檢錯糾錯能力都還不錯,但傳輸效率實在是不高,在效率至上的大背景下,這種方法是不能容忍的。今天痞子衡繼續給大家介紹另一種也非常簡單但效率較高的校驗法-即奇偶校驗法。
一、奇偶校驗法基本原理
1.1 校驗依據
奇偶校驗法的校驗依據就是判斷一次傳輸的一組二進制數據中bit "1"的奇偶性(奇數個還是偶數個)在傳輸前后是否一致,所以其實奇偶檢驗法有兩個子類:
- 奇校驗:如果以二進制數據中1的個數是奇數為依據,則是奇校驗
- 偶校驗:如果以二進制數據中1的個數是偶數為依據,則是偶校驗
一般在同步傳輸方式中常采用奇校驗,而在異步傳輸方式中常采用偶校驗。
1.2 奇偶校驗位
為了實現奇偶校驗,通常會在傳輸的這組二進制數據中插入一個額外的奇偶校驗位(bit),用它來確保發送出去的這組二進制數據中“1”的個數為奇數或偶數。
劃重點,奇偶校驗位并不是用來標記原始傳輸數據中1的個數是奇數還是偶數,而是用來確保原始數據加上奇偶校驗位后的合成數據中1的個數是奇數或者偶數。
1.3 校驗方法
常用的奇偶校驗共有三種:水平奇偶校驗,垂直奇偶校驗校驗和水平垂直奇偶校驗。以對32位數據:10100101 10111001 10000100 00011010進行校驗為例講解:
- 水平奇偶校驗:對每一種數據的編碼添加校驗位,使信息位與校驗位處于同一行。
| 原始數據 | 水平奇校驗位 | 水平偶校驗位 |
|---|---|---|
| 10100101 | 1 | 0 |
| 10111001 | 0 | 1 |
| 10000100 | 1 | 0 |
| 00011010 | 0 | 1 |
所以加上水平偶校驗位后應傳輸的數據是:101001010 101110011 100001000 000110101
- 垂直奇偶校驗:將數據分為若干組,一組一行,再加上一行校驗位,針對每一列采樣奇校驗或偶校驗。
| 編碼分類 | 垂直奇校驗 | 垂直偶校驗 |
|---|---|---|
| 原始數據 | 10100101 | 10100101 |
| 10111001 | 10111001 | |
| 10000100 | 10000100 | |
| 00011010 | 00011010 | |
| 校驗位 | 01111101 | 10000010 |
所以加上垂直偶校驗位后應傳輸的數據是:10100101 10111001 10000100 0001101010000010
- 水平垂直奇偶校驗:也叫Hamming Code,其是在水平和垂直方向上進行雙校驗,其不僅可以檢測2bit錯誤的具體位置,還可糾正1bit錯誤,常用于NAND Flash里。這部分不屬于本文要討論的內容,痞子衡后續會專門介紹Hamming Code。
1.4 C代碼實現
實際中水平校驗法應用比較多,此處示例代碼以水平奇校驗為例:
安裝包:codeblocks-17.12mingw-setup.exe
集成環境:CodeBlocks 17.12 rev 11256
編譯器:GNU GCC 5.1.0
調試器:GNU gdb (GDB) 7.9.1
// parity_check.c
//////////////////////////////////////////////////////////
#include <stdbool.h>
#include <stdint.h>
/*!
* @brief 判斷當前byte的極性是否為奇
*
* @param byte, 待計算奇偶性的數據.
* @retval ture, byte極性(含1的個數)為奇數.
* @retval false, byte極性(含1的個數)為偶數.
*/
bool is_byte_odd_parity(uint8_t byte)
{
bool parity = false;
// 普通算法-byte逐位異或(需循環8次)
/*
for (uint8_t i = 0; i < 8; i++)
{
parity ^= byte & 0x01u;
byte >>= 1;
}
*/
// 效率較高算法-計數byte中1的個數(需循環n次,n為byte中1的個數)
while (byte)
{
parity = !parity;
byte &= byte - 1;
}
return parity;
}
/*!
* @brief 獲取給定data的水平奇校驗位
*
* @param src, 待計算奇偶性的數據塊.
* @param lenInBytes, 待計算奇偶性的數據塊長度.
* @retval 0, data極性(含1的個數)為奇數.
* @retval 1, data極性(含1的個數)為偶數.
*/
uint32_t get_data_parity(uint8_t *src,
uint32_t lenInBytes)
{
uint32_t result = 0;
// 水平校驗法
// isDataOddParity用于判斷所有data bits的行極性是否為奇
bool isDataOddParity = false;
while (lenInBytes--)
{
isDataOddParity ^= is_byte_odd_parity(*src++);
}
// result為所有data bits的奇校驗位
result = !isDataOddParity;
return result;
}
// main.c
//////////////////////////////////////////////////////////
#include <stdio.h>
#include <stdlib.h>
#include "parity_check.h"
int main(void)
{
uint8_t data[4] = {0x31, 0x33, 0x04, 0x08};
uint32_t parity = get_data_parity(data, sizeof(data));
printf("parity = %d\n", parity);
return 0;
}
1.5 行業應用
奇偶檢驗比較典型的應用是在串口UART上,玩過UART的朋友肯定了解串口奇偶檢驗位的作用,包括下位機MCU UART驅動的編寫,上位機串口調試助手的設置都需要注意奇偶校驗位。下圖是UART傳輸時序圖,奇偶校驗位是可選位,僅當使能時才會生效。不過作為嵌入式開發者,倒不必關注奇偶校驗的具體實現,因為MCU的UART模塊已經在硬件上支持了奇偶檢驗,我們只需要操作UART對應寄存器的控制位去使能奇偶檢驗功能即可。
二、奇偶校驗法失效分析
在現實數據傳輸中,偶爾1位出錯的機會最多,2位及以上發生錯誤的概率比較低,且由于奇偶校驗實現簡單,具有相對理想的檢錯能力,因此得到廣泛使用。但奇偶校驗法有如下2個明顯的缺陷:
- 奇數位誤碼能檢出,偶數位誤碼不能檢出
- 不能糾錯,在發現錯誤后,只能要求重發。
前面講的兩種校驗法實際上更多是針對byte傳輸校驗,而在實際應用中我們校驗的對象往往是數據包packet,有沒有其他比奇偶校驗法更好且針對packet的檢錯方法呢?痞子衡在下篇會繼續聊。
至此,嵌入式里數據差錯控制技術之奇偶校驗痞子衡便介紹完畢了,掌聲在哪里~~~
歡迎訂閱
文章會同時發布到我的 博客園主頁、CSDN主頁、知乎主頁、微信公眾號 平臺上。
微信搜索"痞子衡嵌入式"或者掃描下面二維碼,就可以在手機上第一時間看了哦。

最后歡迎關注痞子衡個人微信公眾號【痞子衡嵌入式】,一個專注嵌入式技術的公眾號,跟著痞子衡一起玩轉嵌入式。
衡杰(痞子衡),目前就職于恩智浦(NXP)半導體MCU系統應用部門,擔任高級嵌入式系統應用工程師。
專欄內所有文章的轉載請注明出處:http://www.rzrgm.cn/henjay724/
與痞子衡進一步交流或咨詢業務合作請發郵件至 hengjie1989@foxmail.com
可以關注痞子衡的Github主頁 https://github.com/JayHeng,有很多好玩的嵌入式項目。
關于專欄文章有任何疑問請直接在博客下面留言,痞子衡會及時回復免費(劃重點)答疑。
痞子衡郵箱已被私信擠爆,技術問題不推薦私信,堅持私信請先掃碼付款(5元起步)再發。
浙公網安備 33010602011771號