Contents

ARINC-429

ARINC-429 定义了商用飞机航空电子系统之间(局域网)数字数据传输的标准要求和协议。设备制造商遵循这些标准,从而实现航空电子设备的互换性.

ARINC 429 也称为 Mark 33 数字信息传输系统 (DITS) 总线。虽然主要用于航空电子领域,但这些总线也用于地面车辆、武器系统和其他商业和军事设备领域。

ARINC-429 文件组织结构

  • PART1 : 提供了ARINC 429功能的基本描述以及支持的物理和电气接口。还提供了数据字格式、标准标签和地址分配以及示例。
  • PART2: 定义了ARINC 429离散量字和按标签顺序的位分配。
  • PART3: 描述了ARINC 429数据传输协议和以大块和/或文件格式传输数据的消息定义。
  • PART4: 是多年来发表的ARINC 429第1部分补编(1至17)的档案。它是作为ARINC 429第18号补编更新的一部分而推出的(2012年)。

A429特点

ARINC 429 数据传输的独特之处在于其简单的单向总线通信数据流。虽然典型的数据总线在一组电线上的各个总线点之间提供多向数据传输。 ARINC-429 并非如此, 传说是为了系统可靠性。

1
2
3
4
5
6
                ┌──────────┐      ┌─────────┐      ┌────────────┐
                │RECEIVER_1│      │   ...   │      │ RECEIVER_N │
                └──────────┘      └─────────┘      └────────────┘
┌─────────┐           ▲                ▲                  ▲      
│ SENDER  │───────────┴────────────────┴──────────────────┘      
└─────────┘                                                      
  • Sender可以支持最高20个Receiver

  • 要想双向传输, 再添加一根线缆, Receiver变Sender

  • Send和Receive通道在不同的端口上

  • 以 32 位(bit)为一个数据字(WORD), 每个字代表一个工程单位,例如高度或气压

  • 传输通道分为高速和低速两种, 前者100kb/s , 后者 12.5kb/s

  • LRU 没有由 ARINC 429 分配的地址,而是一个设备 ID 号,允许设备管理和设备的文件传输被分组到系统中。

  • 一个至少4位的空或零电压可以区分连续的字。通过使用字与字之间的空间隔,就不需要单独的时钟信号了。这就是为什么这个信号被称为自锁信号。

数据字结构

1
2
3
4
5
6
┌──┬─────┬───────────────────────┬─────┬───────────────────────┐
│P │ SSM │         DATA          │ SDI │         LABLE         │
└──┴─────┴───────────────────────┴─────┴───────────────────────┘
┌──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┐
│32│31│30│29│28│27│..│14│13│12│11│10│9 │8 │7 │6 │5 │4 │3 │2 │1 │
└──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┘

数据以 32 位字通过 ARINC-429 总线发送,每个字代表一个工程单位,例如高度或气压。消息的不同部分如上图所示.

  • LABEL: 8 位标签用于解释消息的其他字段——每种类型的设备都有一组由标签编号标识的标准参数,而与制造商无关。例如,任何航向参考系统的标签 372 将提供风向,任何空气数据计算机的标签 203 将提供气压高度, 312表示地面速度
  • SDI: 2位, 是源/目的地标识符 (Source Destination Identifiers),由连接到多个接收器的发送器使用,以确定应由哪个接收器处理消息。如果不需要,这些位可以用于数据。
  • DATA: 19位数据
  • SSM: Sign-Status Matrix 符号状态矩阵
  • P: 奇偶校验位 : 使用奇数奇偶校验作为错误检查,以确保精确的数据接收。每个字中传输的逻辑1的数量是一个奇数,通过设置或清除第32位来获得奇数。ARINC 429没有规定纠错的方法,只规定了错误检测。

SSM 符号状态矩阵

根据LABEL的不同, SSM的意思可能不同, 比如可以表示错误状态

  • 00 (0):故障,
  • 01 (1):无计算数据或输出无效,
  • 10 (2):功能实验,
  • 11 (3):正常
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
#include <stdio.h>


typedef unsigned int uint32_t;


uint32_t getSSM(uint32_t x){
    // 获取bit30和bit31的值
    uint32_t ssm = (x >> 29) & 0x3;
    return ssm;
}

uint32_t setSSM(uint32_t x, uint32_t ssm){

    if (ssm > 3) {
        printf("ssm is out of range, should be 0 or 1 or 2 or 3\n");
        return x;
    }

    // 设置bit30和bit31的值
    uint32_t mask = 0x3 << 29;
    x = x & (~mask);
    x = x | (ssm << 29);
    return x;
}

//打印出每一位
void print_bits(uint32_t x){
    uint32_t i=0;
    for (i=0; i<32; i++){
        printf("%d", (x & 0x80000000) >> 31);
        x = x << 1;
    }
}



int main(){
    uint32_t x = 0x40012E92;
    print_bits(x);
    printf(" ==> %d\n", getSSM(x));

    x = 0x60012E92;
    print_bits(x);
    printf(" ==> %d\n", getSSM(x));

    x = 0x20012E92;
    print_bits(x);
    printf(" ==> %d\n", getSSM(x));

    x = 0x12E92;
    print_bits(x);
    printf(" ==> %d\n", getSSM(x));

    x = 0x1234;
    print_bits(setSSM(x, 0));
    printf(" <== %d\n",0);

    print_bits(setSSM(x, 1));
    printf(" <== %d\n",1);

    print_bits(setSSM(x, 2));
    printf(" <== %d\n",2);

    print_bits(setSSM(x, 3));
    printf(" <== %d\n",3);


    return 0;
}

输出

1
2
3
4
5
6
7
8
01000000000000010010111010010010 ==> 2
01100000000000010010111010010010 ==> 3
00100000000000010010111010010010 ==> 1
00000000000000010010111010010010 ==> 0
00000000000000000001001000110100 <== 0
00100000000000000001001000110100 <== 1
01000000000000000001001000110100 <== 2
01100000000000000001001000110100 <== 3

奇数奇偶校验

设置校验位

A429采用的是奇校验 让整个32位中, 1的个数为奇数

1
2
3
4
5
6
初始值WORD的第32位为0

if  WORD中1的个数为偶数
	 tempWORD = WORD
	 将tempWORD的第32位设置为1, 使其有奇数个1
	 return tempWORD
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
typedef unsigned int uint32_t;


// 获取位1的个数
int getBit1Count(uint32_t x){
    uint32_t i=0, count = 0, temp = x;

    for (i=0; i<32; i++){
        if (1 == (temp &  0x00000001) ){
            count++;
        }
        temp = temp >> 1;
    }

    return count;
}

// 设置校验位
uint32_t setCheckBit(uint32_t x){
    //如果为偶数个1
    if (getBit1Count(x) % 2 == 0){
        x = x ^ 0x80000000; // 0x80000000 = 10000000 00000000 00000000 00000000
    }else{
        x = x ^ 0x00000000;
    }

    return x;
}

校验和还原

校验就是看位上的值为1的个数是否为奇数

还原, 将第32位删除即可

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
//检查校验位并还原
uint32_t checkAndRestore(uint32_t x){

    uint32_t bit1Count = getBit1Count(x);
    if (bit1Count % 2 == 0){
        printf("错误! ");
        return 0;
    }else{
    //x = x ^ 0x80000000;
        printf("正确! ");
    }

    //删掉最后一位校验位
    x = x & 0x7FFFFFFF;

    return x;
}

完整代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
#include <stdio.h>


typedef unsigned int uint32_t;

// 获取位1的个数
int getBit1Count(uint32_t x){
    uint32_t i=0, count = 0, temp = x;

    for (i=0; i<32; i++){
        if (1 == (temp &  0x00000001) ){
            count++;
        }
        temp = temp >> 1;
    }

    return count;
}

// 设置校验位
uint32_t setCheckBit(uint32_t x){
    //如果为偶数个1
    if (getBit1Count(x) % 2 == 0){
        x = x ^ 0x80000000; // 0x80000000 = 10000000 00000000 00000000 00000000
    }else{
        x = x ^ 0x00000000;
    }

    return x;
}

//打印出每一位
void print_bits(uint32_t x){
    uint32_t i=0;
    for (i=0; i<32; i++){
        printf("%d", (x & 0x80000000) >> 31);
        x = x << 1;
    }
}

//检查校验位并还原
uint32_t checkAndRestore(uint32_t x){

    uint32_t bit1Count = getBit1Count(x);
    if (bit1Count % 2 == 0){
        printf("错误! ");
        return 0;
    }else{
    //x = x ^ 0x80000000;
        printf("正确! ");
    }

    //删掉最后一位校验位
    x = x & 0x7FFFFFFF;

    return x;
}

int main(){

    uint32_t data[] = {0, 100, 101, 1001, 0x23456};
    uint32_t i=0, set;
    for (i=0; i<5; i++){
        printf("设置校验位之前: ");
        print_bits(data[i]);
        printf(",%d个1, 0x%x\n", getBit1Count(data[i]), data[i]);

        set = setCheckBit(data[i]);
        printf("设置校验位之后: ");
        print_bits(set);
        printf(",%d个1, 0x%x\n",getBit1Count(set), set);
        printf("校验和还原:0x%x\n", checkAndRestore(set));


        printf("-------\n");
    }



    return 0;

}

输出

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
设置校验位之前: 00000000000000000000000000000000,0个1, 0x0
设置校验位之后: 10000000000000000000000000000000,1个1, 0x80000000
正确! 校验和还原:0x0
-------
设置校验位之前: 00000000000000000000000001100100,3个1, 0x64
设置校验位之后: 00000000000000000000000001100100,3个1, 0x64
正确! 校验和还原:0x64
-------
设置校验位之前: 00000000000000000000000001100101,4个1, 0x65
设置校验位之后: 10000000000000000000000001100101,5个1, 0x80000065
正确! 校验和还原:0x65
-------
设置校验位之前: 00000000000000000000001111101001,7个1, 0x3e9
设置校验位之后: 00000000000000000000001111101001,7个1, 0x3e9
正确! 校验和还原:0x3e9
-------
设置校验位之前: 00000000000000100011010001010110,8个1, 0x23456
设置校验位之后: 10000000000000100011010001010110,9个1, 0x80023456
正确! 校验和还原:0x23456
-------

LABLE

1
2
3
4
5
6
7
8
9
      ┌────────────────────┬────────────────────┬─────────────┐       
      │        ones        │        tens        │  hundreds   │       
      └────────────────────┴────────────────────┴─────────────┘       
      ┌──────┬──────┬──────┬──────┬──────┬──────┬──────┬──────┐       
LSB   │  8   │  7   │  6   │  5   │  4   │  3   │  2   │  1   │   MSB 
      └──────┴──────┴──────┴──────┴──────┴──────┴──────┴──────┘      
      
LSB:	最低有效位
MSB:	最高有效位

注意到, 上面的最低有效位和最高有效位 和我们平时定义的是相反的

并且, 8位被分成了三段, 百位数多为二进制11也就是3, 十位数和个位数最多为二进制111也就是7. 所以上面实际就是将8位分成3段, 每段来表示一个8进制数, 并且高低位相反. 这称为反向八进制

可以看出, Label最大为377

Label编码

举例: 312表示飞机的ground speed, 如何进行编码:

  1. 取百位数3, 用两位二进制表示为11, 调转高低位后为11
  2. 取十位数1, 用三位二进制表示为001, 调转高低位后为100
  3. 取个位数2, 用三位二进制表示为010,调转高低位后为010
1
2
3
4
5
6
7
      ┌────────────────────┬────────────────────┬─────────────┐       
      │        ones        │        tens        │  hundreds   │       
      ├────────────────────┼────────────────────┼─────────────┤       
      │         2          │         1          │      3      │       
      ├──────┬──────┬──────┼──────┬──────┬──────┼──────┬──────┤       
MSB   │  0   │  1   │  0   │  1   │  0   │  0   │  1   │  1   │   LSB 
      └──────┴──────┴──────┴──────┴──────┴──────┴──────┴──────┘       

对照ARINC429P1中的 ATTACHMENT 6中的值, 的确如此

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
#include <stdio.h>

typedef unsigned int uint32_t;

//打印出每一位
void print_bits(uint32_t x)
{
    uint32_t i = 0;
    for (i = 0; i < 32; i++)
    {
        printf("%d", (x & 0x80000000) >> 31);
        x = x << 1;
    }
}

//翻转指定的数的指定位
//from: 从哪一位开始翻转(包括), 从低位开始, 从0开始计数
//to: 结束于哪一位(包括)
// 比如 reverse_bits(1, 0, 2)
// 将 00000000000000000000000000000001 中的 0,1,2位 进行翻转
// 得到 00000000000000000000000000000100
uint32_t reverse_bits(uint32_t x, uint32_t from, uint32_t to)
{
    uint32_t result = 0;
    for (uint32_t i = from; i <= to; i++)
    {
        result += (x >> i & 1) << (to - i);
    }
    return result;
}

uint32_t encodeLabel(uint32_t word, uint32_t x)
{
    if (x > 377)
    {
        printf("out of range");
        return 0;
    }

    //将word的前8位清零
    word &= 0xffffff00;

    //以x为312为例:

    //得到百位数3 (00000000000000000000000000000011)
    uint32_t hundreds = (x % 1000) / 100;
    //翻转0000000000000000000000000000011的低两位,
    //得到0000000000000000000000000000011
    hundreds = reverse_bits(hundreds, 0, 1);
    //将 11 填入第0,1位
    word = word | hundreds;

    //得到十位数1 (00000000000000000000000000000001)
    uint32_t tens = (x % 100) / 10;
    //翻转00000000000000000000000000000001的低三位,
    //得到00000000000000000000000000000100
    tens = reverse_bits(tens, 0, 2);
    //将100填入第2,3,4位
    word = word | (tens << 2);

    //得到个位数2 (00000000000000000000000000000010)
    uint32_t ones = x % 10;
    //翻转00000000000000000000000000000010的低三位,
    //得到 00000000000000000000000000000010
    ones = reverse_bits(ones, 0, 2);
    //将010填入第5,6,7位
    word = word | (ones << 5);

    return word;
}

int main()
{
    print_bits(reverse_bits(0x4, 0, 2)); //100 -> 001
    printf("\n");
    print_bits(reverse_bits(0x1, 0, 2)); //001 -> 100
    printf("\n");
    print_bits(reverse_bits(0x1, 0, 31)); //1 -> 10000000000000000000000000000000

    printf("\n------------\n");

    uint32_t word = 0x22849800;
    print_bits(encodeLabel(word, 313));
    printf("\n");
    print_bits(encodeLabel(word, 323));
    printf("\n");

    return 0;
}

输出

1
2
3
4
5
6
00000000000000000000000000000001
00000000000000000000000000000100
10000000000000000000000000000000
------------
00100010100001001001100011010011
00100010100001001001100011001011

Lable 解码

理解了编码, 解码就很简单了

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
uint32_t decodeLabel(uint32_t word)
{
    //获取低8位
    word &= 0xff;

    //得到百位数
    uint32_t hundreds = word & 0x3;
    hundreds = reverse_bits(hundreds, 0, 1);

    //得到十位数
    uint32_t tens = (word >> 2) & 0x7;
    tens = reverse_bits(tens, 0, 2);

    //得到个位数
    uint32_t ones = (word >> 5) & 0x7;
    ones = reverse_bits(ones, 0, 2);

    return hundreds * 100 + tens * 10 + ones;
}

DATA

可传输的数据类型

  • BNR 二进制补码小数
  • BCD 二进制编码十进制表示法
  • IOS5 IOS5编码的字母与数字
  • 离散量
  • 图形: 在地图和类似显示器上使用的线、圆、随机定位的字母/数字文本和其他符号。用于此目的的技术基本上类似于用于 ISO 5 字母/数字数据传输的技术。 ARINC Characteristic 744A:具有图形功能的全格式打印机提供了可以使用 ARINC 429 传输的附加信息和示例图形字符。

[TODO]…