|   大家好,我是痞子衡,是正經(jīng)搞技術(shù)的痞子。今天痞子衡給大家講的是嵌入式里數(shù)據(jù)差錯控制技術(shù)-奇偶校驗。   在系列第一篇文章里,痞子衡給大家介紹了最簡單的校驗法-重復(fù)校驗,該校驗法實現(xiàn)簡單,檢錯糾錯能力都還不錯,但傳輸效率實在是不高,在效率至上的大背景下,這種方法是不能容忍的。今天痞子衡繼續(xù)給大家介紹另一種也非常簡單但效率較高的校驗法-即奇偶校驗法。 一、奇偶校驗法基本原理1.1 校驗依據(jù)  奇偶校驗法的校驗依據(jù)就是判斷一次傳輸?shù)囊唤M二進(jìn)制數(shù)據(jù)中bit '1'的奇偶性(奇數(shù)個還是偶數(shù)個)在傳輸前后是否一致,所以其實奇偶檢驗法有兩個子類:   一般在同步傳輸方式中常采用奇校驗,而在異步傳輸方式中常采用偶校驗。 1.2 奇偶校驗位  為了實現(xiàn)奇偶校驗,通常會在傳輸?shù)倪@組二進(jìn)制數(shù)據(jù)中插入一個額外的奇偶校驗位(bit),用它來確保發(fā)送出去的這組二進(jìn)制數(shù)據(jù)中“1”的個數(shù)為奇數(shù)或偶數(shù)。劃重點,奇偶校驗位并不是用來標(biāo)記原始傳輸數(shù)據(jù)中1的個數(shù)是奇數(shù)還是偶數(shù),而是用來確保原始數(shù)據(jù)加上奇偶校驗位后的合成數(shù)據(jù)中1的個數(shù)是奇數(shù)或者偶數(shù)。
 1.3 校驗方法  常用的奇偶校驗共有三種:水平奇偶校驗,垂直奇偶校驗校驗和水平垂直奇偶校驗。以對32位數(shù)據(jù):10100101 10111001 10000100 00011010進(jìn)行校驗為例講解: | 原始數(shù)據(jù) | 水平奇校驗位 | 水平偶校驗位 | 
|---|
 | 10100101 | 1 | 0 |  | 10111001 | 0 | 1 |  | 10000100 | 1 | 0 |  | 00011010 | 0 | 1 | 
   所以加上水平偶校驗位后應(yīng)傳輸?shù)臄?shù)據(jù)是:101001010 101110011 100001000 000110101 | 編碼分類 | 垂直奇校驗 | 垂直偶校驗 | 
|---|
 | 原始數(shù)據(jù) | 10100101 | 10100101 |  | 10111001 | 10111001 |  | 10000100 | 10000100 |  | 00011010 | 00011010 |  | 校驗位 | 01111101 | 10000010 | 
   所以加上垂直偶校驗位后應(yīng)傳輸?shù)臄?shù)據(jù)是:10100101 10111001 10000100 0001101010000010 1.4 C代碼實現(xiàn)  實際中水平校驗法應(yīng)用比較多,此處示例代碼以水平奇校驗為例: 安裝包:codeblocks-17.12mingw-setup.exe集成環(huán)境:CodeBlocks 17.12 rev 11256
 編譯器:GNU GCC 5.1.0
 調(diào)試器:GNU gdb (GDB) 7.9.1
 // parity_check.c//////////////////////////////////////////////////////////
 #include <stdbool.h>
 #include <stdint.h>
 
 /*!
 * @brief 判斷當(dāng)前byte的極性是否為奇
 *
 * @param byte, 待計算奇偶性的數(shù)據(jù).
 * @retval ture, byte極性(含1的個數(shù))為奇數(shù).
 * @retval false, byte極性(含1的個數(shù))為偶數(shù).
 */
 bool is_byte_odd_parity(uint8_t byte)
 {
 bool parity = false;
 // 普通算法-byte逐位異或(需循環(huán)8次)
 /*
 for (uint8_t i = 0; i < 8; i++)
 {
 parity ^= byte & 0x01u;
 byte >>= 1;
 }
 */
 // 效率較高算法-計數(shù)byte中1的個數(shù)(需循環(huán)n次,n為byte中1的個數(shù))
 while (byte)
 {
 parity = !parity;
 byte &= byte - 1;
 }
 return parity;
 }
 
 /*!
 * @brief 獲取給定data的水平奇校驗位
 *
 * @param src, 待計算奇偶性的數(shù)據(jù)塊.
 * @param lenInBytes, 待計算奇偶性的數(shù)據(jù)塊長度.
 * @retval 0, data極性(含1的個數(shù))為奇數(shù).
 * @retval 1, data極性(含1的個數(shù))為偶數(shù).
 */
 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 行業(yè)應(yīng)用  奇偶檢驗比較典型的應(yīng)用是在串口UART上,玩過UART的朋友肯定了解串口奇偶檢驗位的作用,包括下位機(jī)MCU UART驅(qū)動的編寫,上位機(jī)串口調(diào)試助手的設(shè)置都需要注意奇偶校驗位。下圖是UART傳輸時序圖,奇偶校驗位是可選位,僅當(dāng)使能時才會生效。不過作為嵌入式開發(fā)者,倒不必關(guān)注奇偶校驗的具體實現(xiàn),因為MCU的UART模塊已經(jīng)在硬件上支持了奇偶檢驗,我們只需要操作UART對應(yīng)寄存器的控制位去使能奇偶檢驗功能即可。 
 二、奇偶校驗法失效分析  在現(xiàn)實數(shù)據(jù)傳輸中,偶爾1位出錯的機(jī)會最多,2位及以上發(fā)生錯誤的概率比較低,且由于奇偶校驗實現(xiàn)簡單,具有相對理想的檢錯能力,因此得到廣泛使用。但奇偶校驗法有如下2個明顯的缺陷:   前面講的兩種校驗法實際上更多是針對byte傳輸校驗,而在實際應(yīng)用中我們校驗的對象往往是數(shù)據(jù)包packet,有沒有其他比奇偶校驗法更好且針對packet的檢錯方法呢?痞子衡在下篇會繼續(xù)聊。   至此,嵌入式里數(shù)據(jù)差錯控制技術(shù)之奇偶校驗痞子衡便介紹完畢了,掌聲在哪里~~~ |