使用 MSP430 硬件实现等精度测频

使用DMA和Timer构建32位的定时计数器

 

初始化基准时钟: 使能晶体振荡器, 使用4MHz晶振作为基准源.

配置定时器和DMA: F5529三个TimerA分别使用SMCLK, TACLK和ACLK作为时钟源, 配置时钟源频率SMCLK为4MHz, ACLK为32768Hz, 配置门定时器, 定时1秒作为门信号, 开门定时器中断. 配置三个DMA触发源分别为三个定时器溢出信号, DMA计数器开满. 启动门定时器, 等待上升沿启动计数定时器, 关闭CPU, 等待门定时器中断. 等待上升沿停止计数定时器, 读取DMAxSZ和TAxR作为计数结果进行计算. 使用1MHz的频率对SMCLK频率进行校准

这种方式测得的数据最大范围至少为120MHz, 该频率下精度低于0.1%, 最小频率为0.01Hz, 精度1%, 在0.005Hz的情况下出现5%的误差. 相比使用定时器+中断构建位数更高的计数器这种方法的精度和测量范围都有明显提高.

附源代码

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
#pragma once
#include <msp430.h>
#include <stdint.h>
#include "../TIDriver/ucs.h"
#include "../vscode.h"
uint16_t CounterOV;
double FreqMeasure()
{
volatile uint8_t temp0 = 0, temp1 = 0;
volatile uint8_t temp2 = 0, temp3 = 0;
volatile const uint8_t P2SEL_D = BIT1;
volatile double result;
volatile uint32_t ctx, gtx;
burnDCO(); // fuck dco to 33MHz
P2SEL = 0X00; // TA2CLK
P2IE = 0X00;
UCS_initClockSignal(UCS_SMCLK, UCS_XT2CLK_SELECT, UCS_CLOCK_DIVIDER_1);
UCS_initClockSignal(UCS_ACLK, UCS_XT1CLK_SELECT, UCS_CLOCK_DIVIDER_1);

TA2CTL = TASSEL__TACLK | ID_0 | MC__STOP | TACLR;
TA1CTL = TASSEL__ACLK | ID_0 | MC__STOP | TACLR;
TA0CTL = TASSEL__SMCLK | ID_0 | MC__STOP | TACLR; // Timer Ready State
TA0CCTL0 = CM_1 + SCS + SCCI ;
TA1CCTL0 = CM_1 + SCS + SCCI ;
TA2CCTL0 = CM_1 + SCS + SCCI ;
TA1CCR0 = 32768;
TA2CCR0 = 0XFFFF;
TA0CCR0 = 0XFFFF;

DMACTL0 = DMA0TSEL__TA2CCR0 | DMA1TSEL__TA0CCR0; // 32bit timer
DMACTL4 = DMARMWDIS;
__data20_write_long((uintptr_t)&DMA0SA, (uintptr_t)&temp0);
__data20_write_long((uintptr_t)&DMA0DA, (uintptr_t)&temp1);
__data20_write_long((uintptr_t)&DMA1SA, (uintptr_t)&temp2);
__data20_write_long((uintptr_t)&DMA1DA, (uintptr_t)&temp3);
DMA0CTL = DMADT_4 + DMADSTBYTE + DMASRCBYTE + DMAEN;
DMA1CTL = DMADT_4 + DMADSTBYTE + DMASRCBYTE + DMAEN;
DMA0SZ = 0XFFFF;
DMA1SZ = 0XFFFF;
DMACTL1 = DMA2TSEL__TA1CCR0; // Gate Interrupt control
__data20_write_long((uintptr_t)&DMA2SA, (uintptr_t)&P2SEL_D);
__data20_write_long((uintptr_t)&DMA2DA, (uintptr_t)&P2SEL);
DMA2CTL = DMADT_5 + DMADSTBYTE + DMASRCBYTE + DMAEN + DMAIE;
DMA2SZ = 1;
P2IFG = 0X00;
while (!(P2IFG & BIT2));
P2SEL = BIT2; // TA2CLK
TA0CTL = TASSEL__SMCLK | ID_0 | MC__UP | TACLR;
TA1CTL = TASSEL__ACLK | ID_0 | MC__UP | TACLR;
TA2CTL = TASSEL__TACLK | ID_0 | MC__UP | TACLR;
LPM0; // nothing to do, halt CPU. RTOS: wait for signal.
while (!(P2IFG & BIT2));
TA2CTL = TASSEL__TACLK | ID_0 | MC__STOP;
TA1CTL = TASSEL__ACLK | ID_0 | MC__STOP | TACLR;
TA0CTL = TASSEL__SMCLK | ID_0 | MC__STOP; // Timer Ready State
ctx = ~DMA0SZ;
gtx = ~DMA1SZ;
ctx <<= 16;
gtx <<= 16;
ctx |= TA2R;
gtx |= TA0R;
ctx += 1;
result = (double)ctx * 4000000.0f / (double)gtx;
return result;
}
1
2
3
4
5
6
7
8
#pragma vector = DMA_VECTOR
interrupt void DMA_ISR()
{
P2IFG = 0X00;
TA1CTL = TASSEL__ACLK | ID_0 | MC__STOP | TACLR;
DMA2CTL &=~ DMAIFG;
LPM0_EXIT;
}