Contents

在msp430上实现差分双向编码的编解码

前言

根据《Qi Wireless Power Transfer System V1.2.3》的规定,Power TransmitterPower Receive 通信时,物理层使用基于FSK的差分双向编码。 调制频率定为fmod = 160KHz,工作频率定为fop = 150KHz,传输时钟fclk = 2000KHz。

编码部分

物理层架构图

/images/encode_1.png

由TA0进行基本的150KHz或者160KHz的PWM波形输出,TA1工作在2000KHz时钟下,然后每经过256个周期产生一次定时中断,中断中根据需要发送的数据来决定是否改变P1.4的输出频率。

物理层逻辑状态对应图

/images/encode_2.png

HI状态为发送160KHz的状态,LO状态为发送150KHz的状态。如果是1则改变一次(由HI转变成LO或者反过来),如果是0则间隔一次转变一次。

发送编码

当应用需要发送一个字节的数据的时,则需要对其编码才能发送出去,这样可以保证数据的完整性。根据《Qi Wireless Power Transfer System V1.2.3》的规定,Power TransmitterPower Receive 通信时,链路层使用11位异步串行格式。此格式包含一个起始位,8个数据位,一个奇偶校验位以及一个停止位。数据位采用的字节序为LSB,校验位采用奇校验。假设发送0x35,则完整的状态如下:

/images/encode_3.png

在MSP430F5529上实现

PWM生成

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
void PwmInit()
{
    //TA0.3 PWM     P1.4
    GPIO_setAsPeripheralModuleFunctionOutputPin(GPIO_PORT_P1,GPIO_PIN4);

    Timer_A_outputPWMParam param = {0};
    param.clockSource = TIMER_A_CLOCKSOURCE_SMCLK;  //SMCLK=4MHz
    param.clockSourceDivider = TIMER_B_CLOCKSOURCE_DIVIDER_2;
    param.timerPeriod = kBaseFreqTick-1;
    param.compareRegister = TIMER_B_CAPTURECOMPARE_REGISTER_3;
    param.compareOutputMode = TIMER_A_OUTPUTMODE_TOGGLE_SET;
    param.dutyCycle = kBaseFreqTick/2;
    Timer_A_outputPWM(TIMER_A0_BASE, &param);

}

定时中断

 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
void TimerInit()
{

    Timer_A_initUpModeParam initUpParam = {0};
    initUpParam.clockSource = TIMER_A_CLOCKSOURCE_SMCLK;
    initUpParam.clockSourceDivider = TIMER_A_CLOCKSOURCE_DIVIDER_2;
    initUpParam.timerPeriod = kHalfBitTick - 1;
    initUpParam.timerInterruptEnable_TAIE = TIMER_A_TAIE_INTERRUPT_ENABLE;
    initUpParam.captureCompareInterruptEnable_CCR0_CCIE =
        TIMER_A_CAPTURECOMPARE_INTERRUPT_DISABLE;
    initUpParam.timerClear = TIMER_A_DO_CLEAR;
    Timer_A_initUpMode(TIMER_A1_BASE, &initUpParam);

    Timer_A_clearTimerInterrupt(TIMER_A1_BASE);//clear TAIFG

    Timer_A_startCounter(TIMER_A1_BASE,TIMER_A_UP_MODE);

}

void TIMER1_A1_ISR (void)
{

    //Any access, read or write, of the TAIV register automatically resets the
    //highest "pending" interrupt flag
    switch ( __even_in_range(TA1IV,14) ){
        case  0: break;                          //No interrupt
        case  2: break;                          //CCR1
        case  4:  break;                        //CCR2
        case  6: break;                           //CCR3
        case  8: break;                          //CCR4 not used
        case 10: break;                          //CCR5 not used
        case 12: break;                          //CCR6 not used
        case 14:                                // overflow
            {
                if(transmit.status == TRANSMIT_STATUS_STOP)
                {
                    break;
                }

                if(transmit.tick_count <= transmit.max_len)
                {
                    if((transmit.raw_buffer[transmit.tick_count/2] == 1) ||
                            (transmit.tick_count%2 == 0))
                    {
                        //ONE 2 transitions
                        transmit.output_status = transmit.output_status?0:1;
                        if(transmit.output_status)
                        {
                            TA0CCR3 = kModulateFreqTick/2;
                            TA0CCR0 = kModulateFreqTick - 1;
                        }
                        else
                        {
                            TA0CCR3 = kBaseFreqTick/2;
                            TA0CCR0 = kBaseFreqTick - 1;
                        }
                    }
                    transmit.tick_count++;
                }
                else
                {
                    //back to base frequency
                    TA0CCR3 = kBaseFreqTick/2;
                    TA0CCR0 = kBaseFreqTick - 1;

                    transmit.status = TRANSMIT_STATUS_STOP;
                    transmit.tick_count = 0;
                    transmit.output_status = 0;
                }
            }
            break;
        default: break;
    }
}

串行格式生成

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
void SendByte(uint8_t byte)
{
    int i =0;
    int val = 0;

    transmit.raw_buffer[0] = 0;
    if(!(transmit.status)) //in stop status
    {
        for(;i<8;i++)
        {
            transmit.raw_buffer[1+i] = (byte >> i) & 0x01;
            val ^= transmit.raw_buffer[1+i];
        }
        transmit.raw_buffer[i+1] = val?0:1;
        transmit.raw_buffer[10] = 1;
        transmit.max_len = 22;
        transmit.status = 1;
        transmit.tick_count = 0;
    }
}

解码部分

物理层架构图

/images/encode_4.png

P1.6的输入来源于编码部分的P1.4输出。TA1的主要功能是,10个CLK上升沿后在CCR1输出通道中产生一次输出电平跳变。为何引入TA1这个部分呢?160KHz的PWM波的半周期为3.125us,MSP430时钟捕捉测量功能的中断处理时间约为4.4us,相对于来说太长不利于MCU的运行。故需要放大输入的PWM波的周期。TA0将从TA1输出的放大的波形信号预处理,分辨出是150KHz还是160KHz。如果是150KHz则输出0,如果是160KHz则输出1。然后把这部分信号输入到CCR2通道中,解码出原始的串行编码数据信息。最后将串行编码数据输入到UART0中,最终解出发送的数据。

各部分在MSP430F5529上实现

TA1部分

 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

void CounterInit()
{
    //TA1CLK INPUT   P1.6
    //TA1.1  OUTPUT   P2.0
    GPIO_setAsPeripheralModuleFunctionInputPin(GPIO_PORT_P1,GPIO_PIN6);
    GPIO_setAsPeripheralModuleFunctionOutputPin(GPIO_PORT_P2,GPIO_PIN0);


    Timer_A_initUpModeParam initUpParam = {0};
    //external signal
    initUpParam.clockSource = TIMER_A_CLOCKSOURCE_EXTERNAL_TXCLK;
    initUpParam.clockSourceDivider = TIMER_A_CLOCKSOURCE_DIVIDER_1;
    initUpParam.timerPeriod = COUNTER_NUM*2 - 1;
    initUpParam.timerInterruptEnable_TAIE = TIMER_A_TAIE_INTERRUPT_DISABLE;
    initUpParam.captureCompareInterruptEnable_CCR0_CCIE =
        TIMER_A_CAPTURECOMPARE_INTERRUPT_DISABLE;
    initUpParam.timerClear = TIMER_A_DO_CLEAR;
    Timer_A_initUpMode(TIMER_A1_BASE, &initUpParam);

    //Initiaze compare mode,generate pwm
    Timer_A_initCompareModeParam initInterParam = {0};
    initInterParam.compareRegister = TIMER_A_CAPTURECOMPARE_REGISTER_1;
    initInterParam.compareInterruptEnable = TIMER_A_CAPTURECOMPARE_INTERRUPT_DISABLE;
    initInterParam.compareOutputMode = TIMER_A_OUTPUTMODE_TOGGLE_RESET;
    initInterParam.compareValue = COUNTER_NUM;
    Timer_A_initCompareMode(TIMER_A1_BASE, &initInterParam);

    Timer_A_startCounter(TIMER_A1_BASE, TIMER_A_UP_MODE);
}

TA0部分

  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
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
void TimerInit()
{
    //TA0.1 INPUT   P1.2
    //TA0.2 INPUT   P1.3

    GPIO_setAsPeripheralModuleFunctionInputPin(GPIO_PORT_P1,GPIO_PIN2);
    GPIO_setAsPeripheralModuleFunctionInputPin(GPIO_PORT_P1,GPIO_PIN3);


    Timer_A_initContinuousModeParam initContiParam = {0};
    initContiParam.clockSource = TIMER_A_CLOCKSOURCE_SMCLK;
    initContiParam.clockSourceDivider = TIMER_A_CLOCKSOURCE_DIVIDER_1;
    initContiParam.timerInterruptEnable_TAIE = TIMER_A_TAIE_INTERRUPT_DISABLE;
    initContiParam.timerClear = TIMER_A_DO_CLEAR;
    initContiParam.startTimer = false;
    Timer_A_initContinuousMode(TIMER_A0_BASE, &initContiParam);

    Timer_A_initCaptureModeParam initCapParam = {0};
    initCapParam.captureRegister = TIMER_A_CAPTURECOMPARE_REGISTER_1;
    initCapParam.captureMode = TIMER_A_CAPTUREMODE_RISING_AND_FALLING_EDGE;
    initCapParam.captureInputSelect = TIMER_A_CAPTURE_INPUTSELECT_CCIxA;
    initCapParam.synchronizeCaptureSource = TIMER_A_CAPTURE_SYNCHRONOUS;
    initCapParam.captureInterruptEnable = TIMER_A_CAPTURECOMPARE_INTERRUPT_ENABLE;
    initCapParam.captureOutputMode = TIMER_A_OUTPUTMODE_OUTBITVALUE;
    Timer_A_initCaptureMode(TIMER_A0_BASE, &initCapParam);


    Timer_A_initCaptureModeParam initCap2Param = {0};
    initCap2Param.captureRegister = TIMER_A_CAPTURECOMPARE_REGISTER_2;
    initCap2Param.captureMode = TIMER_A_CAPTUREMODE_RISING_AND_FALLING_EDGE;
    initCap2Param.captureInputSelect = TIMER_A_CAPTURE_INPUTSELECT_CCIxA;
    initCap2Param.synchronizeCaptureSource = TIMER_A_CAPTURE_SYNCHRONOUS;
    initCap2Param.captureInterruptEnable = TIMER_A_CAPTURECOMPARE_INTERRUPT_ENABLE;
    initCap2Param.captureOutputMode = TIMER_A_OUTPUTMODE_OUTBITVALUE;
    Timer_A_initCaptureMode(TIMER_A0_BASE, &initCap2Param);

    Timer_A_clearCaptureCompareInterrupt(TIMER_A0_BASE,
        TIMER_A_CAPTURECOMPARE_REGISTER_2);

    Timer_A_clearCaptureCompareInterrupt(TIMER_A0_BASE,
        TIMER_A_CAPTURECOMPARE_REGISTER_1);

    Timer_A_startCounter(TIMER_A0_BASE, TIMER_A_CONTINUOUS_MODE);

}

int input_level = 0;
void SampleLevel(uint16_t val)
{
    if(RANGE(val,kModuFreqTick,2))
    {
        //160KHz
        if(input_level) //twice sample
        {
            GPIO_setOutputHighOnPin(LOGICAL_LEVEL_OUTPUT_PORT,LOGICAL_LEVEL_OUTPUT_PIN);
        }
        input_level = 1;
    }
    else if(RANGE(val,kBaseFreqTick,2))
    {
        //150KHz
        if(!input_level)
        {
            GPIO_setOutputLowOnPin(LOGICAL_LEVEL_OUTPUT_PORT,LOGICAL_LEVEL_OUTPUT_PIN);
        }
        input_level = 0;
    }
    else
    {
        //discard
    }

}

void TIMER0_A1_ISR (void)
{
    //Any access, read or write, of the TAIV register automatically resets the
    //highest "pending" interrupt flag
    switch ( __even_in_range(TA0IV,14) ){
        case  0: break;                          //No interrupt
        case  2:                           //CCR1
            {
                    uint16_t now = TA0CCR1;
                    uint16_t val;
                    if(now < last_stamp)
                    {
                        val = 0xFFFF - last_stamp + now;
                    }
                    else
                    {
                        val = now - last_stamp;
                    }
                    last_stamp = now;

                    SampleLevel(val);
            }
            break;
        case  4:                         //CCR2
            {
                uint16_t now =  Timer_A_getCaptureCompareCount(TIMER_A0_BASE,
                                     TIMER_A_CAPTURECOMPARE_REGISTER_2);
                uint16_t val;
                if(now < another_stamp)
                {
                    val = 0xFFFF - another_stamp + now;
                }
                else
                {
                    val = now - another_stamp;
                }
                another_stamp = now;
                if(RANGE(val,kHalfBitTick,kBitTimeDiffTick)) //312-512us
                {
                    if(first_half_level == 1)
                    {
                        first_half_level = 0;
                        GPIO_setOutputHighOnPin(ENCODE_OUTPUT_PORT,ENCODE_OUTPUT_PIN);
                    }
                    else
                    {
                        first_half_level = 1;
                    }
                }
                else if(RANGE(val,kOneBitTick,kBitTimeDiffTick))   //824-1204us
                {
                    first_half_level = 0;
                    GPIO_setOutputLowOnPin(ENCODE_OUTPUT_PORT,ENCODE_OUTPUT_PIN);
                }
            }
            break;
        case  6: break;                           //CCR3
        case  8: break;                          //CCR4 not used
        case 10: break;                          //CCR5 not used
        case 12: break;                          //CCR6 not used
        case 14:                  // overflow
            break;
        default: break;
    }
}

UART0部分

 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
90
91
92
93
94
95
96
97
98
99
//val = raw * 100
uint32_t Round(uint32_t val)
{
    int res = 0;
    if(val%100 >= 50)
    {
        res = val/100 + 1;
    }
    else
    {
        res = val/100;
    }
    return res;
}

#define UART_CLOCK      (4000000UL)  //Hz

void BaudrateConfig(USCI_A_UART_initParam *param,uint32_t baudrate)
{
    //Fclk = 4MHz,max baudrate = 460800
    if(UART_CLOCK/baudrate > 1024)
    {
        param->overSampling = USCI_A_UART_OVERSAMPLING_BAUDRATE_GENERATION;
        /*
         * N = Fclk/baudrate
         * UCBRx = INT(N)
         * UCBRSx = round( [N/16 - INT(N/16)] X 16 )
         *
         * */
        param->clockPrescalar = UART_CLOCK/baudrate/16;
        param->firstModReg = Round(((UART_CLOCK*100/baudrate/16) -
                                    (param->clockPrescalar * 100UL))*16);
        param->secondModReg = 0;
    }
    else
    {
        param->overSampling = USCI_A_UART_LOW_FREQUENCY_BAUDRATE_GENERATION;
        /*
         * N = Fclk/baudrate
         * UCBRx = INT(N)
         * UCBRSx = round( [N - INT(N)] X 8 )
         *
         * */
        param->clockPrescalar = UART_CLOCK/baudrate;
        param->firstModReg = Round(((UART_CLOCK*100/baudrate) -
                                       (param->clockPrescalar * 100))*8);
        param->secondModReg = 0;
    }
}

void UartInit()
{
    uint32_t baudrate = 9600;
    //Configure UART pins
    GPIO_setAsPeripheralModuleFunctionInputPin(
            GPIO_PORT_P3,
            GPIO_PIN3 | GPIO_PIN4
    );

    //Configure UART
    //baud rate = 500k/512 = 977bps
    USCI_A_UART_initParam param = {0};
    param.selectClockSource = USCI_A_UART_CLOCKSOURCE_SMCLK;

    param.parity = USCI_A_UART_ODD_PARITY;
    param.msborLsbFirst = USCI_A_UART_LSB_FIRST;
    param.numberofStopBits = USCI_A_UART_ONE_STOP_BIT;
    param.uartMode = USCI_A_UART_MODE;

    baudrate = Round((50000UL * TRANSMIT_FREQ) / HALF_BIT_DUTY_CYCLE);
    BaudrateConfig(&param,baudrate);
    if (STATUS_FAIL == USCI_A_UART_init(USCI_A0_BASE, &param)) {
        return;
    }

   USCI_A_UART_enable(USCI_A0_BASE);

   //Enable Receive Interrupt
   USCI_A_UART_clearInterrupt(USCI_A0_BASE,USCI_A_UART_RECEIVE_INTERRUPT);
   USCI_A_UART_enableInterrupt(USCI_A0_BASE,USCI_A_UART_RECEIVE_INTERRUPT);

}

void USCI_A0_ISR(void)
{
    uint8_t rx_data;
    switch(__even_in_range(UCA0IV,4))
    {
        case USCI_NONE: break;
        case USCI_UCRXIFG:
            {
                rx_data = UCA0RXBUF;
                PutChar(rx_data);
            }
            break;
       case USCI_UCTXIFG: break;
       default:break;
    }
}

运行结果

发送0xAA,一个字节的数据

/images/encode_5.png

  1. 粉红线为编码后的输出

  2. 蓝色线为周期放大10倍后的输出

  3. 绿色线为P1.4的输出

  4. 黄色线为串行编码数据