之前已经对MSP430F4250的SD16_A进行了入门级的了解和设置,但由于项目有要求:
AD——模拟信号频率范围100~20kHz,采样率40kHz,16bit采样
所以,需要设置采样率为40KHz,百转千回后得到了如下结论:
1.需要配置FFL+:
主要寄存器:
(1)FLL_CTL0:
一般官方给出的代码是
FLL_CTL0 |= XCAP14PF;
复制代码
但是其实还有一位关于时钟频率比较重要的:DCOPLUS
为什么要设置它?
因为DCO的频率是直接给MCLK和SMCLK的,那么这一位的含义是什么?
参考MSP430X4XX User's Guide
比较重要的说明是,默认情况下DCOPLUS = 0并且D = 2,其实D的含义是(Divider的意思),所以总的来说,默认情况下你的DCO时钟频率=晶振频率*(N+1)/2,为了提高时钟频率,设置DCOPLUS = 1,即DCO output is not divided,不进行默认的分频,可提高2倍时钟频率。
(2)SCFQCTL和SCFI0:
之所以把它们放在一起讲,因为User's Guide手册上给出的官方倍频代码为:
BIC #GIE,SR ; Disable interrupts
MOV.B #(64−1),&SCFQCTL ; MCLK = 64 * ACLK, DCOPLUS=0
MOV.B #FN_2,&SCFI0 ; Select DCO range
BIS #GIE,SR ; Enable interrupts
复制代码
具体含义是SCFQCTL为倍频因子,SCFI0用于设定频率范围,具体参考User's Guide手册吧。
2.已经得到了系统时钟频率MCLK和外设时钟频率SMCLK,在AD上具体还能如何设置:
SD16CTL = SD16REFON+SD16SSEL0; // 1.2V ref, SMCLK
SD16INCTL0 = SD16INTDLY_3;
SD16CCTL0 |= SD16OSR_32 + SD16IE; // Enable interrupt
复制代码
首先设置SD16CTL,主要用于选择时钟和选择分频因子,SD16SSEL0就是选择SMCLK(已经是最快了),设置SD16INCTL0选择通道0每次采样结束产生中断(事实上,测量中设置每4次采样产生一次中断,中断频率也没有任何变化,暂时不知如何解释),设置SD16CCTL0的过采样率SD16OSR_32,设置过采样率为最低也能提高采样率,但是会降低采样精度。参考Use's Guide:
更具体的AD时钟再参考时钟部分的框图:
其实fm就是SMCLK通过层层分频(上述笔者代码设置的分频系数为0,不分频,具体参考SD16CTL寄存器说明)得到的频率,User's Guide在贴图文字中提到Sample frequency fs = fm/OSR,那么自然,笔者把SD16CCTL0的OSR位设置为最低的32,就能得到最高的采样频率了。
除了时钟之外,通过SD16_A的框图还能了解到它内部存在的高阻抗Buffer和前置放大器(过采样数字滤波之前讨论fs时的截图中也有说明),放大器很容易理解,那么高阻抗Buffer有什么用(User's Guide):
总结来说高阻抗Buffer可以抗采集到的输入数据的锯齿现象,但是它的设置和fm的频率有关,虽然笔者没有使用,但是却是SD16_A中一个重要组成部分。
首先解释前一篇对MSP430 AD研究的博文代码:
fm = 1MHz,fs = 1MHz/256 = 3.9KHz,约256us会产生一次中断,最后得到的结果为224us,误差0.125,还是有说明价值的。(纯属个人见解)
最后是笔者得到接近200KHz采样率的代码:
#include <msp430x42x0.h>
#define Num_of_Results 80
/* Arrays to store SD16_A conversion results */
/* NOTE: arrays need to be global to */
/* prevent removal by compiler */
static unsigned int results[Num_of_Results];
void main(void)
{
volatile unsigned int i; // Use volatile to prevent removal
// by compiler optimization
WDTCTL = WDTPW + WDTHOLD; // Stop WDT
FLL_CTL0 |= XCAP14PF+DCOPLUS; // Configure load caps
SCFQCTL = SCFQ_4M;
SCFI0 |= FN_8;//MCLK = SMCLK = 8MHz
for (i = 0; i < 10000; i++); // Delay for 32 kHz crystal to
// stabilize
P1SEL = 0; //GPIO
P1DIR = 0xff; //out
SD16CTL = SD16REFON+SD16SSEL0; // 1.2V ref, SMCLK
SD16INCTL0 = SD16INTDLY_3;
SD16CCTL0 |= SD16OSR_32 + SD16IE; // Enable interrupt
for (i = 0; i < 0x3600; i++); // Delay for 1.2V ref startup
_EINT(); // Enable general interrupts
SD16CCTL0 |= SD16SC; // Set bit to start conversion
//_BIS_SR(LPM0_bits); // Enter LPM0
while(1);
}
#pragma vector=SD16_VECTOR
__interrupt void SD16ISR(void)
{
static unsigned int index = 0;
switch (SD16IV)
{
case 2: // SD16MEM Overflow
break;
case 4: // SD16MEM0 IFG
results[index] = SD16MEM0; // Save CH0 results (clears IFG)
P1OUT = 0x03; //Pin 0,1 out 1
if (++index == Num_of_Results)
{
index = 0; // SET BREAKPOINT HERE
P1OUT = 0x00; //Pin 0,1 out 0
}
break;
}
}
复制代码
代码仔细说明:
设置了MCLK = SMCLK = 8MHz(具体参考用户手册和笔者之前对时钟的设置说明看看就能明白),设置了不分频的时钟给AD,即fm = 8MHz,fs = 8/32 = 250KHz,所以每250KHz就会进入一次中断,当采到80个AD值时,使P1.0和P1.1=0,然后清除计数,再下一次触发中断,P1.1和P1.0会再次置1,通过测量低电平持续时间可以得到AD的Sample frequency. |