工具/软件:
尊敬的 TI 专家
我正在开发 MSP430 FR5739、我尝试使用端口1.1以2500Hz 的频率录制调制正弦波音频。 但我们已经接近2475 (非 恒定)、而不是2500Hz (具有抖动)。 代码如下:
#include
#include //对于 uint16_t、uint8_t
//--------------------------------------------------------------------------------------------------------------------------------
//函数原型
//--------------------------------------------------------------------------------------------------------------------------------
void initClock (void);
void initGPIO (void);
void initADC (void);
void initUART (void);
void initTimerA (void);
//--------------------------------------------------------------------------------------------------------------------------------
//用于存储 ADC 结果的全局变量
//--------------------------------------------------------------------------------------------------------------------------------
易失性 uint16_t ADC_Result;
//--------------------------------------------------------------------------------------------------------------------------------
//主程序
//--------------------------------------------------------------------------------------------------------------------------------
void main (void)
{
WDTCTL = WDTPW | WDTHOLD;//停止看门狗计时器
initClock ();//配置8 MHz、8 MHz、4 MHz
initGPIO ();// P1.1= ADC 输入、P2.0/P2.1=UART
initADC();//配置 ADC10 (计时器 ISR 内的软件‐触发器)
initUART ();//配置 eUSCI_A0 UART @230400波特率(ACLK = 4 MHz)
initTimerA ();//将 Timer_A 配置为以2500Hz 的频率触发(SMCLK)
_enable_interrupt ();//启用全局中断
while (1)
{
__bis_SR_register (LPM0_bits | GIE);//进入 LPM0;将在 ADC 或 UART ISR 上唤醒
__ no_operation ();//对于调试器
}
}
//--------------------------------------------------------------------------------------------------------------------------------
// initClock ()
//–直接‐寄存器代码(无 DriverLib)。
//–DCOFSEL_3确切地设置 DCO = 8 MHz (隐式 DCORSEL=0)。
//–路由 DCOCLK→MCLK/SMCLK/ACLK。
//–设置 ACLK = DCO/2 = 4 MHz、SMCLK = DCO/1 = 8 MHz、MCLK = DCO/1 = 8 MHz。
//--------------------------------------------------------------------------------------------------------------------------------
void initClock (void)
{
CSCTL0_H = CSKEY_H;//解锁 CS 寄存器(KEY = 0xA5 << 8)
//选择 DCORSEL=0 (默认值)的 DCOFSEL_3→DCO 完全等于8 MHz
CSCTL1 = DCOFSEL 3;// DCOFSEL = 0b011→步长(对于8 MHz)
//将 DCOCLK 路由到 ACLK、SMCLK 和 MCLK
CSCTL2 = SELA_DCOCLK // ACLK = DCOCLK
| BES__DCOCLK // SMCLK = DCOCLK
| SELM_DCOCLK;// MCLK = DCOCLK
//将 ACLK 除以2→4 MHz;使8 MHz、8 MHz
CSCTL3 = DIVA__2 // ACLK 分频器/2
| DIVS__1 // SMCLK 分频器/1
| DIVM_1;// MCLK 分频器/1
CSCTL0_H = 0;//再次锁定 CS 寄存器
}
//--------------------------------------------------------------------------------------------------------------------------------
// initGPIO()
//–P1.1作为 ADC10 A1 (模拟输入)。
//–P2.0 = UCA0TXD、P2.1 = UART 的 UCA0RXD。
//--------------------------------------------------------------------------------------------------------------------------------
void initGPIO (void)
{
// P1.1→ADC 输入(A1)
P1SEL0 |= BIT1;
P1SEL1 || BIT1;
// P2.0/P2.1→UCA0TXD/UCA0RXD
P2SEL0 <=~(BIT0 | BIT1);
P2SEL1 |= BIT0 | BIT1;
//退出 GPIO 上电默认高阻抗模式
PM5CTL0且=~LOCKLPM5;
}
//--------------------------------------------------------------------------------------------------------------------------------
// initadc ()
//–软件‐触发了 ADC10。
//–ADC10CLK = SMCLK = 8 MHz→转换时间= 32个周期/ADC10CLK 8 MHz = 4 µs。
//–采样保持时间= 32个 ADC10CLK 周期(ADC10SHT_3)。
//–输入通道= A1 (P1.1)。
//–转换完成时的中断。
//--------------------------------------------------------------------------------------------------------------------------------
void initADC (void)
{
// 1)开启 ADC10、设置 S/H 时间= 32个 ADC10CLK 周期
ADC10CTL0 = ADC10SHT_3 // S/H 时间= 32个周期(ADC10CLK)
| ADC10ON;// ADC10开启
// 2)使用采样计时器、ADC10CLK = SMCLK (ADC10SSEL_2)
//(无硬件触发)
ADC10CTL1 = ADC10SHP //使用采样计时器进行采样/保持
| ADC10SSEL_2;// ADC10CLK 源= SMCLK (8 MHz)
//3) 10位分辨率
ADC10CTL2 = ADC10RES;// ADC10RES = 10位
// 4)选择通道 A1 (P1.1)
ADC10MCTL0 = ADC10INCH_1;// ADC10INCH_1 =通道 A1
// 5)在转换完成时启用中断
ADC10IE |= ADC10IE0;//启用 ADC10IFG 中断
//注意:这里我们不设置 ADC10ENC;我们将在计时器 ISR 中设置/清除 ENC。
}
//--------------------------------------------------------------------------------------------------------------------------------
// inituart ()
//–UART 模式下的 eUSCI_A0、8-N-1。
//–时钟= ACLK = 4 MHz。
//–波特率= 230400→4 MHz UCA/230400→17.36≈UCA0BR0 = 17、UCA0BR1 = 0、UCA0MCTLW = 0x04A0。
//--------------------------------------------------------------------------------------------------------------------------------
void initUART (void)
{
UCA0CTL1 |= UCSWRST;//将 eUSCI 置于复位状态
UCA0CTL1 |= UCSSEL_ACLK;// CLK = ACLK = 4 MHz
UCA0BR0 = 17;// INT/230400≈17.36→4 MHz = 17
UCA0BR1 = 0;//高字节= 0
UCA0MCTLW = 0x04A0;//调制设置(如前所述)
UCA0CTL1且=~UCSWRST;//释放 eUSCI_A0以进行运行
//注意:我们稍后将在 ADC ISR 中启用 UCTXIE 以发送高字节。
}
//--------------------------------------------------------------------------------------------------------------------------------
// initTimerA ()
//–SMCLK = 8 MHz→CCR0 = 3199→CCR/ 8 MHz (3199+1)= 2500Hz 确切值。
//–启用 CCR0中断;在该 ISR 中、我们将启动 ADC10转换。
//--------------------------------------------------------------------------------------------------------------------------------
void initTimerA (void)
{
TA0CCR0 = 3199;// 8 MHz (3199+1)= 2500Hz
TA0CCTL0 = CCIE;//启用 CCR0中断
TA0CTL = TASSEL_SMCLK //使用 SMCLK = 8 MHz
| MC___UP //向上计数模式(计数0→CCR0)
| TACLR;//清除 TAR 以重新开始
}
//--------------------------------------------------------------------------------------------------------------------------------
// Timer_A0 CCR0中断服务例程
//–恰好以2500Hz 的频率触发。
//–如果设置了 ADC10ENC、则清除 ADC10ENC、然后设置 ADC10ENC+ADC10SC 以启动新的转换。
//--------------------------------------------------------------------------------------------------------------------------------
#pragma vector = TIMER0_A0_vector
__interrupt void Timer_A_CC7_ISR (void)
{
//清除 ENC、以便 ADC10SC 实际开始新的转换
ADC10CTL0且=~ADC10ENC;
// Arm 并立即开始 ADC10转换
ADC10CTL0 |= ADC10ENC | ADC10SC;
// ADC 将需要4 µs (32个周期@ 8 MHz)、然后触发 ADC10_vector。
//我们立即返回;LPM0保持设置状态、直到 ADC ISR 唤醒 CPU。
}
//--------------------------------------------------------------------------------------------------------------------------------
// ADC10中断服务例程
//–每次10位转换完成时调用(SC 之后~4 µs)。
//–读取 ADC10MEM0、通过 UART 发送低字节、然后为高字节启用 UCTXIE。
//--------------------------------------------------------------------------------------------------------------------------------
#pragma vector = ADC10_vector
__中断无效 ADC10_ISR (void)
{
开关(_even_in_RANGE (ADC10IV、ADC10IV_ADC10IFG))
{
用例 ADC10IV_NONE:break;//无中断
case ADC10IV_ADC10OVIFG:break;// Overflow (Ignore)
case ADC10IV_ADC10TOVIFG:break;//超时(忽略)
用例 ADC10IV_ADC10HIIFG:BREAK;//窗口高电平(未使用)
用例 ADC10IV_ADC10LOIFG:break;//窗口低电平(未使用)
用例 ADC10IV_ADC10INIFG:break;//窗口内部(未使用)
case ADC10IV_ADC10IFG://转换完成
ADC_Result = ADC10MEM0;//读取10位结果
UCA0TXBUF =(uint8_t)(ADC_Result 和0xFF);//首先发送低字节
UCA0IE |= UCTXIE;//为高字节启用 TX 中断
休息;
默认值:中断;
}
}
//--------------------------------------------------------------------------------------------------------------------------------
// USCI_A0 (UART)中断服务例程
//–UCA0TXBUF 为空时触发(发送低字节后)。
//–发送 ADC_Result 的高字节、然后禁用 UCTXIE、直到下一次转换。
//--------------------------------------------------------------------------------------------------------------------------------
#pragma vector = USCI_A0_vector
__interrupt void USCI_A0_ISR (void)
{
开关(__even_in_RANGE (UCA0IV、USCI_UART_UCTXCPTIFG))
{
case USCI_NONE:中断;
case USCI_UART_UCRXIFG:break;// RX 中断(未使用)
case USCI_UART_UCTXIFG:// TXBUF 已准备好接收下一个字节
UCA0TXBUF =(uint8_t)((ADC_Result >> 8)& 0xFF);//发送高字节
UCA0IE &=~UCTXIE;//在下一次 ADC 转换前禁用 UCTXIE
休息;
用于 USCI_UART_UCSTTIFG:BREAK;
case USCI_UART_UCTXCPTIFG:break;
默认值:中断;
}
}
即使我尝试了备选时钟;ACLK 仍然采样频率和抖动的误差相同。 我 从2个月起就陷入了这一阶段。 我尝试了各种方法、但在记录的信号频率和抖动方面、结果似乎相同。 我有以下问题?
- DCO 时钟(SMCLK 或 ACLK)的稳定情况如何?
- 抖动的原因是什么?
- 我们需要时钟校准吗? 如果是、如何校准时钟以实现稳定频率?
- 我们是否需要外部时钟或晶体?
请给我务实的回应我们截止日期吗?