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

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

HI状态为发送160KHz的状态,LO状态为发送150KHz的状态。如果是1则改变一次(由HI转变成LO或者反过来),如果是0则间隔一次转变一次。
发送编码
当应用需要发送一个字节的数据的时,则需要对其编码才能发送出去,这样可以保证数据的完整性。根据《Qi Wireless Power Transfer System V1.2.3》的规定,Power Transmitter 向 Power Receive 通信时,链路层使用11位异步串行格式。此格式包含一个起始位,8个数据位,一个奇偶校验位以及一个停止位。数据位采用的字节序为LSB,校验位采用奇校验。假设发送0x35,则完整的状态如下:

在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, ¶m);
}
|
定时中断
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;
}
}
|
解码部分
物理层架构图

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(¶m,baudrate);
if (STATUS_FAIL == USCI_A_UART_init(USCI_A0_BASE, ¶m)) {
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,一个字节的数据

粉红线为编码后的输出
蓝色线为周期放大10倍后的输出
绿色线为P1.4的输出
黄色线为串行编码数据