您的位置:首页 > 其它

使用MSP430实现温控(一)

2017-06-07 07:47 375 查看
这部分博客主要是对以前做的使用MSP430进行温控的经验进行整理,以防后来资料遗失的情况。

对于MSP430而言,实现温控模块以下几个模块互相配合。将分为下面几个部分进行讲解:

时钟源部分

ADC部分

中断部分

温控中代码逻辑的初步调试

UART的初步调试

一:时钟源部分:

现在项目中,需要使用msp430单片机作为控制端,所以,要先对msp430的时钟源进行了解和分析。

msp430拥有unified clock system (统一时钟系统),

-FLL(锁频环) for frequency stablization

-VLO(内部时钟源)

-内部参考源(REFO)

-32kHZ晶振

-高达32MHz的高频时钟

The clock system is supported by the Unified Clock System (UCS) module that includes support for a 32-kHz watch crystal oscillator (或者时钟XT1低频态 XT1 LF mode), an internal very-low-power low-frequency oscillator (内部低频低功耗振荡器VLO), an internal trimmed low-frequency oscillator (内部平衡低频晶振REFO), an integrated internal digitally controlled oscillator 内部数字控制振荡器(DCO), and a high frequency crystal oscillator (高频晶振XT1 HF mode or XT2). The UCS module is designed to meet the requirements of both low system cost and low power consumption.

The UCS module features digital frequency locked loop (FLL)hardware that, in conjunction with a digital modulator, stabilizes the DCO frequency to a programmable multiple of the selected FLL reference frequency. The internal DCO provides a fast turn-on clock source and stabilizes in less than 5 μs. The UCS module provides the following clock signals:

• Auxiliary clock (ACLK), sourced from a 32-kHz watch crystal, a high-frequency crystal, the internal lowfrequency

oscillator (VLO), the trimmed low-frequency oscillator (REFO), or the internal digitally controlled

oscillator DCO.

• Main clock (MCLK), the system clock used by the CPU. MCLK can be sourced by same sources made

available to ACLK.

• Sub-Main clock (SMCLK), the subsystem clock used by the peripheral modules. SMCLK can be sourced by

same sources made available to ACLK.

• ACLK/n, the buffered output of ACLK, ACLK/2, ACLK/4, ACLK/8, ACLK/16, ACLK/32.

因为不接外部晶振,所以本文主要是就DCO的设置进行研究。

msp430F5438A datasheet上给出的设置是:

可以见,DCORESL的频率调节范围大致如下:

DCORSEL = 0的调节范围约为0.20~0.70MHZ;

DCORSEL= 1的调节范围约为0.36~1.47MHZ;

DCORSEL = 2的调节范围约为0.75~3.17MHZ;

DCORSEL = 3的调节范围约为1.51~6.07MHZ;

DCORSEL = 4的调节范围约为3.2~12.3MHZ;

DCORSEL = 5的调节范围约为6.0~23.7MHZ;

DCORSEL = 6的调节范围约为10.7~39.7MHZ;

DCORSEL = 7的调节范围约为19.6~60MHZ。

可以看出有重叠的部分,理解了这些,就可以理解Ti官方代码了:

if (fsystem <= 630) // fsystem < 0.63MHz

UCSCTL1 = DCORSEL_0;

else if (fsystem < 1250) // 0.63MHz < fsystem < 1.25MHz

UCSCTL1 = DCORSEL_1;

else if (fsystem < 2500) // 1.25MHz < fsystem < 2.5MHz

UCSCTL1 = DCORSEL_2;

else if (fsystem < 5000) // 2.5MHz < fsystem < 5MHz

UCSCTL1 = DCORSEL_3;

else if (fsystem < 10000) // 5MHz < fsystem < 10MHz

UCSCTL1 = DCORSEL_4;

else if (fsystem < 20000) // 10MHz < fsystem < 20MHz

UCSCTL1 = DCORSEL_5;

else if (fsystem < 40000) // 20MHz < fsystem < 40MHz

UCSCTL1 = DCORSEL_6;

else

UCSCTL1 = DCORSEL_7;

DCO模块运行需要参考时钟REFCLK,REFCLK可以来自REFOCLK、XT1CLK和XT2CLK,通过UCSCTL3的SELREF选择,默认使用的XT1CLK,但如果XT1CLK不可用则使用REFOCLK。

DCO模块有两个输出时钟信号,级DCOCLK和DCOCLKDIV,其中,倍频计算公式如下:

DCOCLK = D*(N+1)*(REFCLK/n)

DCOCLKDIV = (N+1)*(REFCLK/n)

其中:

n即REFCLK输入时钟分频,可以通过UCSCTL3中的FLLCLKDIV设定,默认为0,也就是不分频;

D可以通过UCSCTL2中的FLLD来设定,默认为1,也就是2分频;

N可以通过UCSCTL2中的FLLN来设定,默认值为31。

为了验证上述的陈述,可以编写以下代码:

P1DIR |= BIT0;

P1SEL |= BIT0; //1口输出辅助时钟

UCSCTL3 |= SELREF_2; // FLLref = REFO 为32.768K

UCSCTL4 = SELM__DCOCLKDIV + SELS__DCOCLKDIV + SELA__DCOCLKDIV;// 时钟来源:主系统时钟来源DCOCLKDIV;子系统时钟来源DCOCLKDIV;辅助系统时钟来源DCOCLKDIV

UCSCTL5 |= DIVM__1 + DIVS__1 + DIVA__1; // 分频:主系统时钟1分频;子系统时钟1分频;辅助系统时钟1分频

所以由计算,DCOCLKDIV =32*32.768K = 1.048576M,也就是ACLK的时钟源(不分频)

根据测试结果我们可知T接近1ms,所以DCOCLKDIV差不多为1M,符合计算结果。

那么如果我要设置别的频率又该怎么办呢?

比如我现在需要大约5M的频率,那么由计算得知,当N=151时,大致满足要求。

那么代码写为:

P1DIR |= BIT0;
P1SEL |= BIT0;                                                           //1口输出辅助时钟
UCSCTL3 |= SELREF_2;                                               // FLLref = REFO 为32.768K
UCSCTL4 = SELM__DCOCLKDIV + SELS__DCOCLKDIV + SELA__DCOCLKDIV;// 时钟来源:主系统时钟来源DCOCLKDIV;子系统时钟来源DCOCLKDIV;辅助系统时钟来源DCOCLKDIV
UCSCTL1 = DCORSEL_4                                          //   5M < Fdco < 10M
UCSCTL2 |= FLLD__1 + 151;                                    //DCOCLKDIV=5M 为 Fdco/2
UCSCTL5 |= DIVM__1 + DIVS__1 + DIVA__1; // 分频:主系统时钟1分频;子系统时钟1分频;辅助系统时钟1分频


运行,用示波器测P1.0的输出信号。

测出来的信号周期约为200ns,所以输出的ACLK信号就是约为5M,所以实验证实了想法。

二:ADC部分:

由于项目中需要使用到ADC模块,那么就有必要对ADC模块进行研究了。

这是msp430F5428A datasheet首页上面对于ADC的描述:

12-Bit Analog-to-Digital Converter (ADC)

– Internal Reference

– Sample-and-Hold

– Autoscan Feature

– 14 External Channels, 2 Internal Channels

ADC12_A (Link to User’s Guide)

The ADC12_A module supports fast 12-bit analog-to-digital conversions. The module implements a 12-bit SAR

core, sample select control, reference generator, and a 16-word conversion-and-control buffer. The conversionand-

control buffer allows up to 16 independent ADC samples to be converted and stored without any CPU

intervention.

也就是说ADC12_A可以同时转换16路的模拟信号。(没有cpu的介入)

把Msp430f5438A的ADC12_A的重要特点总结如下:

(1) 转换速率可以为200K/s

(2)12bit转换 无错码

(3) 由软件或计数器控制着的采样时间和保持时间

(4)内部有参考电压1.5V,2.0V及2.5V

(5)可以使用外接参考源,是否选择外部参考源由寄存器决定。

(6)有单沟道采样,重复单沟道采样,序列采样,重复序列采样4种模式可选(Single-channel, repeat-single-channel, sequence (autoscan), and repeat-sequence (repeated autoscan) conversion modes)

(7)16位转换结果存储寄存器ADC12MEMx,和ADC12转换结果存储寄存器ADC12MCTLx。

写下列程序:

void main(void)

{

WDTCTL = WDTPW+WDTHOLD; // Stop watchdog timer

ADC12CTL0 = ADC12ON+ADC12MSC+ADC12SHT0_15;// Turn on ADC12, set sampling time

ADC12CTL1 = ADC12SHP+ADC12CONSEQ_0; // Use sampling timer, single

ADC12MCTL0 =ADC12SREF_2 + ADC12INCH_3; // ref+=Veref=2.5V, channel = A3

// ADC12MCTL1 = ADC12INCH_2+ADC12EOS; // ref+=AVcc, channel = A9, end seq.

ADC12IE = 0x02; // Enable ADC12IFG.1, end channel

ADC12CTL0 |= ADC12ENC; // Enable conversions

ADC12CTL0 |= ADC12SC; // Start convn - software trigger

}


当在A3脚上加电压为1.404 V, 在debug界面观测到的为:

也就是说转换结果为08B3,对应电压为

[(8*256+11*16+3)/4096]*2.5V=1.359V

所以ADC成功将A3口上的信号转换为数字量并存储在ADC12MEM0中。

三:中断部分:

对于单片机系统,中断是非常重要的一个特性,也是很值得去研究的。

中断的定义:出现需要时,CPU暂时停止当前程序的执行转而执行处理新情况的程序和执行过程。

MSP430F5438A的很多部分都可以产生中断,比如IO口(只有P1,P2口),ADC,Timer_A 等,但是当多种中断在同时等待时,它们之间内部的优先级决定了哪个中断应该先被处理,哪个中断应该后处理。中断处理优先级和中断向量BASEADDRESS如下所示:

暂时只检测只有一个中断的情况,即不用考虑中断优先级的情况。以ADC12_A为中断源.

ADC12_A的中断标志位寄存器可以记为ADC12IFGx,ADC12IFG0是ADC12IFGx的最低位,当ADC12MEM0装载了转换结果后,这个位置1,而当ADC12MEM0存取后,这个位被重置,它也可以由软件重置。

在IAR_workbench 里面写#include “msp430x54x.h”

volatile unsigned int result; // Needs to be global in this example,Otherwise, the compiler removes it

// because it is not used for anything.

void InitADC12_A()
{
ADC12CTL0 = ADC12ON+ADC12MSC+ADC12SHT0_15;// Turn on ADC12, set sampling time
ADC12CTL1 = ADC12SHP+ADC12CONSEQ_0; // Use sampling timer, single
ADC12MCTL0 = ADC12SREF_2 + ADC12INCH_3; // ref+=Veref=2.5V, channel = A3
ADC12IE = ADC12IE0; // Enable ADC12IFG.0
ADC12CTL0 |= ADC12ENC; // Enable conversions
}
#pragma vector=ADC12_VECTOR
__interrupt void ADC12ISR (void)
{
switch(__even_in_range(ADC12IV,34))
{
case 0: break; // Vector 0: No interrupt
case 2: break; // Vector 2: ADC overflow
case 4: break; // Vector 4: ADC timing overflow
case 6: // Vector 6: ADC12IFG0
result = ADC12MEM0; // Move results

__bic_SR_register_on_exit(LPM4_bits); // Exit active CPU, SET BREAKPOINT HERE
case 8: break; // Vector 8: ADC12IFG1
case 10: break; // Vector 10: ADC12IFG2
case 12: break; // Vector 12: ADC12IFG3
case 14: break; // Vector 14: ADC12IFG4
case 16: break; // Vector 16: ADC12IFG5
case 18: break; // Vector 18: ADC12IFG6
case 20: break; // Vector 20: ADC12IFG7
case 22: break; // Vector 22: ADC12IFG8
case 24: break; // Vector 24: ADC12IFG9
case 26: break; // Vector 26: ADC12IFG10
case 28: break; // Vector 28: ADC12IFG11
case 30: break; // Vector 30: ADC12IFG12
case 32: break; // Vector 32: ADC12IFG13
case 34: break; // Vector 34: ADC12IFG14
default: break;
}
}
void main(void)
{
InitADC12_A();

WDTCTL = WDTPW+WDTHOLD; // Stop watchdog timer
ADC12CTL0 |= ADC12SC; // Start convn - software trigger

__bis_SR_register(LPM4_bits + GIE); // Enter LPM4, Enable interrupts
__no_operation(); // For debugger在A3口接入电压为1.407V,然后debug,在debug页面发现: 功转换,而且通过中断将结果写入了result里面。
中断的作用说到底只是用来实现突发事件的检测和运行,例如我们也可以实现,运用ADC12的中断实现P11.0上的红色LED灯实现点亮的操作。clude "msp430x54x.h"
volatile unsigned int result; // Needs to be global in this example,Otherwise, the compiler removes it
// because it is not used for anything.
void InitADC12_A()
{
ADC12CTL0 = ADC12ON+ADC12MSC+ADC12SHT0_15;// Turn on ADC12, set sampling time
ADC12CTL1 = ADC12SHP+ADC12CONSEQ_0; // Use sampling timer, single
ADC12MCTL0 = ADC12SREF_2 + ADC12INCH_3; // ref+=Veref=2.5V, channel = A3
ADC12IE = ADC12IE0; // Enable ADC12IFG.0
ADC12CTL0 |= ADC12ENC; // Enable conversions
}


pragma vector=ADC12_VECTOR

__interrupt void ADC12ISR (void)
{
switch(__even_in_range(ADC12IV,34))
{
case 0: break; // Vector 0: No interrupt
case 2: break; // Vector 2: ADC overflow
case 4: break; // Vector 4: ADC timing overflow
case 6: // Vector 6: ADC12IFG0
result = ADC12MEM0; // Move results

P11OUT &= ~BIT0; //light red LED

__bic_SR_register_on_exit(LPM4_bits);
case 8: break; // Vector 8: ADC12IFG1
case 10: break; // Vector 10: ADC12IFG2
case 12: break; // Vector 12: ADC12IFG3
case 14: break; // Vector 14: ADC12IFG4
case 16: break; // Vector 16: ADC12IFG5
case 18: break; // Vector 18: ADC12IFG6
case 20: break; // Vector 20: ADC12IFG7
case 22: break; // Vector 22: ADC12IFG8
case 24: break; // Vector 24: ADC12IFG9
case 26: break; // Vector 26: ADC12IFG10
case 28: break; // Vector 28: ADC12IFG11
case 30: break; // Vector 30: ADC12IFG12
case 32: break; // Vector 32: ADC12IFG13
case 34: break; // Vector 34: ADC12IFG14
default: break;
}
}
void main(void)
{
InitADC12_A();

WDTCTL = WDTPW+WDTHOLD; // Stop watchdog timer
ADC12CTL0 |= ADC12SC; // Start convn - software trigger

P11DIR |= BIT0; //设置P11.0为输出

P11OUT |= BIT0;

__bis_SR_register(LPM4_bits + GIE); // Enter LPM4, Enable interrupts
__no_operation(); // For debugger

}


结果成功实现了A4通道的转换,将结果写到了result里面,并且点亮了红色LED灯。

四:温控程序逻辑调试:

(1)验证单时钟可以实现ADC采样和PWM输出的多功能

既然时钟系统,ADC系统和定时器系统的功能都已经得到了验证。那么接下来就要着手确定程序的逻辑了,我们可以尝试着从以下的方式来控温:

(1)使用TA1计时器计时10ms,每到10ms进行ADC采样。

(2)将ADC12采集到温度值使用PID算法得到控制量,然后再送到电路板上的P7.3/TA1.2的管脚上(这个管脚连接在温控MOS管的栅极),由此便可以实现温控控制。

通过以上的想法得知,这个需要while(1)语句,即要一直运行。但是需要时钟中断,从而实现每隔10ms就可以进行ADC采样。

先验证一下可不可以只通过一个计时器如TA1,就实现每隔10ms进入中断(使用LED灯来展示效果),同时在中断内部实现PWM的电压输出(设置占空比越来越小)。

/*这个范例的主要可以用来实现几个功能

1:每隔10ms产生一个中断使得P11.0处的LED灯开始闪烁

2:产生PWM的信号(56管脚 P7.3/TA1.2 为了验证进了中断,每次进入中断后会实时改变PWM的占空比)。

其实主要的作用是验证我的温控程序里面只需要使用一个定时器就可以实现功能

*/

#include "io430x54xA.h"
//这个函数用来产生5MHz的时钟信号
void InitClock()
{
P1DIR |= BIT0;
P1SEL |= BIT0; //ACLK output,这时候可以使用示波器观察时钟信号
UCSCTL3 |= SELREF_2; // FLLref = REFO
UCSCTL4 = SELM__DCOCLKDIV + SELS__DCOCLKDIV + SELA__DCOCLKDIV;// 时钟来源:主系统时钟来源DCOCLKDIV;子系统时钟来源DCOCLKDIV;辅助系统时钟来源DCOCLKDIV
UCSCTL5 |= DIVM__1 + DIVS__1 + DIVA__1; // 分频:主系统时钟1分频;子系统时钟1分频;辅助系统时钟1分频
__bis_SR_register(SCG0); // Disable FLL
UCSCTL1 = DCORSEL_6; // 10.7MHz<Fdco<39MHz
UCSCTL2 |= FLLD__2 +151 ; // 5MHz DCOCLKDIV Fdco/4
__bic_SR_register(SCG0); // Enable FLL
// 等待错误标志清除,振动器稳定
do
{
UCSCTL7 &= ~(XT2OFFG + XT1LFOFFG + XT1HFOFFG + DCOFFG); // Clear XT2,XT1,DCO fault flags
SFRIFG1 &= ~OFIFG; // Clear fault flags
}while (SFRIFG1&OFIFG); // Test oscillator fault flag
}

//初始化Timer_A
void Init_Timer_A()
{
P7DIR |= 0x08; // P7.3 output
P7SEL |= 0x08; // P7.3 options select
P11DIR |= 0x01; // P11.0 output red_LED
TA1CCTL0 = CCIE; // CCR0 interrupt enabled
TA1CCR0 = 50000; //设置周期为10ms
TA1CCTL2 = OUTMOD_3; // CCR1 set/reset
TA1CCR2 = 40000; // CCR1 original PWM duty cycle 1/5
TA1CTL = TASSEL_2 + MC_1 + TACLR; // SMCLK, upmode, clear TAR
}

void main(void)
{
WDTCTL = WDTPW + WDTHOLD; // Stop WDT

InitClock();

Init_Timer_A();
__bis_SR_register(LPM0_bits + GIE); // Enter LPM0 ,Interrupt enabled
__no_operation(); // For debugger
}
#pragma vector=TIMER1_A0_VECTOR
__interrupt void TIMER1_A0_ISR(void)
{

P11OUT ^= 0x01;

TA1CCR2 -= 100;

}


将源代码下载进msp430中可以发现,LED灯闪烁(如果不闪烁可以适当延长定时器时间)PWM的输出信号确实是占空比在增加,周期为10ms。所以实现了题目中所需要的TA1既要实现定时中断,又要实现PWM的输出。

(2)温控逻辑

既然已经确定了只用一个时钟就可以实现很多功能,那么接下来就涉及到问题了,可不可以在每隔10ms的一个时钟中断内实现ADC12的转换并且将ADC12转换后的值取出并赋给变量呢?

这个问题很重要,因为以前做ADC的转换的时候,需要将转换结果赋值时,还需要使用ADC的一个内部的中断。那么这个问题难道还要涉及到中断的嵌套之类的问题吗?

先看了下msp430f5438A上的关于中断优先级的讲述:

所以说ADC12_A的优先级是高于TA1,所以使用中断优先级的方法先完成10ms的定时中断,然后再完成ADC12_A的取样赋值貌似并不是很靠谱。(其实我并不是很清楚中断优先级这些玩意)

那么有没有可能在Timer_A的中断内部再使能一个ADC12_A的中断呢?

话不多说,开始实验:

一:代码如下

volatile unsigned int result; // Needs to be global in this example,Otherwise, the compiler removes it
// because it is not used for anything.
void InitADC12_A()
{
  ADC12CTL0 = ADC12ON+ADC12MSC+ADC12SHT0_15; // Turn on ADC12, set sampling time
  ADC12CTL1 = ADC12SHP+ADC12CONSEQ_2; // Use sampling timer, repeat single channel (我觉得这一个设定可能会比较关键)
  ADC12MCTL0 = ADC12SREF_2 + ADC12INCH_3; // ref+=Veref=2.5V, channel = A3
  ADC12IE = ADC12IE0; // Enable ADC12IFG.0
  ADC12CTL0 |= ADC12ENC; // Enable conversions
}
#pragma vector=ADC12_VECTOR
__interrupt void ADC12ISR (void)
{
  switch(__even_in_range(ADC12IV,34))
  {
    case 0: break; // Vector 0: No interrupt
    case 2: break; // Vector 2: ADC overflow
    case 4: break; // Vector 4: ADC timing overflow
    case 6: // Vector 6: ADC12IFG0
    result = ADC12MEM0; // Move results, IFG is cleared

    P11OUT ^= BIT0; //light red LED

    __bic_SR_register_on_exit(LPM4_bits);
    case 8: break; // Vector 8: ADC12IFG1
    case 10: break; // Vector 10: ADC12IFG2
    case 12: break; // Vector 12: ADC12IFG3
    case 14: break; // Vector 14: ADC12IFG4
    case 16: break; // Vector 16: ADC12IFG5
    case 18: break; // Vector 18: ADC12IFG6
    case 20: break; // Vector 20: ADC12IFG7
    case 22: break; // Vector 22: ADC12IFG8
    case 24: break; // Vector 24: ADC12IFG9
    case 26: break; // Vector 26: ADC12IFG10
    case 28: break; // Vector 28: ADC12IFG11
    case 30: break; // Vector 30: ADC12IFG12
    case 32: break; // Vector 32: ADC12IFG13
    case 34: break; // Vector 34: ADC12IFG14
    default: break;
  }
}
void main(void)
{
  InitADC12_A();

  WDTCTL = WDTPW+WDTHOLD; // Stop watchdog timer

  ADC12CTL0 |= ADC12SC; // Start convn - software trigger
  P11DIR |= BIT0; //设置P11.0为输出
  P11OUT &= ~BIT0; //点亮LED灯 设置这两行的目的其实有观察ADC12MEM0寄存器的作用。
  __bis_SR_register(LPM4_bits + GIE); // Enter LPM4, Enable interrupts
  __no_operation(); // For debugger

}


然后debug.

然后可以发现,当程序运行到ADC12CTL0 |= ADC12SC; // Start convn - software trigger时:

ADC12MEM0寄存器的值是0x0897

当运行到P11DIR |= BIT0; //设置P11.0为输出时:

ADC12MEM0寄存器的值为 0x08C6

当运行到P11OUT &= ~BIT0时,ADC12MEM0寄存器的值为 0x08C7 ,ADC12IFG0 = 1;

当运行到__bis_SR_register(LPM4_bits + GIE);时,ADC12MEM0寄存器的值为 0x08BD。ADC12IFG0 = 1;

当运行到 result = ADC12MEM0时,ADC12MEM0寄存器的值为 0x08D9,result的值为2239 。ADC12IFG0 = 1;

当运行到__bic_SR_register_on_exit(LPM4_bits);ADC12MEM0寄存器的值为 0x08C3 , result的值为2239,ADC12IFG0 = 1。

当一直运行时我们发现,这个程序就一直没有从中断中出来过。

从这些运行结果我们可以看出,ADC12MEM0寄存器的值一直都在改变,而自从转换后ADC12IFG0=1就一直没变过。这也就导致了程序一直被困在中断中不能返回主函数。

既然这样,那么在Timer_A中进入ADC12后,由于ADC12IFG0=1,所以就再也回不到Timer_A产生的中断了。这是由ADC12CONSEQx决定的。

二:猜想将ADC12CONSEQx设为0时,那么会有:

ADC12CTL1 = ADC12SHP+ADC12CONSEQ_2; // Use sampling timer, repeat single channel (我觉得这一个设定可能会比较关键)

改为:ADC12CTL1 = ADC12SHP+ADC12CONSEQ_0; // Use sampling timer, single single channel (我觉得这一个设定可能会比较关键)

那么debug结果为:

当程序运行到ADC12CTL0 |= ADC12SC; // Start convn - software trigger时:

ADC12MEM0寄存器的值是0x08C1,ADC12IFG0 = 1;

当运行到P11DIR |= BIT0; //设置P11.0为输出时:

ADC12MEM0寄存器的值为 0x08C6,ADC12IFG0 = 1;

当运行到P11OUT &= ~BIT0时,ADC12MEM0寄存器的值为 0x08C1 ,ADC12IFG0 = 1;

当运行到__bis_SR_register(LPM4_bits + GIE);时,ADC12MEM0寄存器的值为 0x08C1。ADC12IFG0 = 1;

当运行到 result = ADC12MEM0时,ADC12MEM0寄存器的值为 0x08D9,result的值为2241 。ADC12IFG0 = 0;

当运行到__bic_SR_register_on_exit(LPM4_bits);ADC12MEM0寄存器的值为 0x08C1 , result的值为2241,ADC12IFG0 = 0。

再debug,会发现程序回到了主函数,然后结束运行。

三:回到原来的那个问题,我应该怎么样才能实现温控呢?

尝试着写以下代码:

include “io430.h”

//这个函数用来产生5MHz的时钟信号

void InitClock()
{
P1DIR |= BIT0;
P1SEL |= BIT0; //ACLK output,这时候可以使用示波器观察时钟信号
UCSCTL3 |= SELREF_2; // FLLref = REFO
UCSCTL4 = SELM__DCOCLKDIV + SELS__DCOCLKDIV + SELA__DCOCLKDIV;// 时钟来源:主系统时钟来源DCOCLKDIV;子系统时钟来源DCOCLKDIV;辅助系统时钟来源DCOCLKDIV
UCSCTL5 |= DIVM__1 + DIVS__4 + DIVA__1; // 分频:主系统时钟1分频;子系统时钟16分频;辅助系统时钟1分频
__bis_SR_register(SCG0); // Disable FLL
UCSCTL1 = DCORSEL_6; // 10.7MHz<Fdco<39MHz
UCSCTL2 |= FLLD__2 +151 ; // 约5MHz DCOCLKDIV Fdco/4
__bic_SR_register(SCG0); // Enable FLL
// 等待错误标志清除,振动器稳定
do
{
UCSCTL7 &= ~(XT2OFFG + XT1LFOFFG + XT1HFOFFG + DCOFFG); // Clear XT2,XT1,DCO fault flags
SFRIFG1 &= ~OFIFG; // Clear fault flags
}while (SFRIFG1&OFIFG); // Test oscillator fault flag
}

//初始化Timer_A
void Init_Timer_A()
{
P7DIR |= 0x08; // P7.3 output
P7SEL |= 0x08; // P7.3 options select
P11DIR |= 0x01; // P11.0 output red_LED

P11OUT &= ~BIT0; //输出低电平 LED灯亮
TA1CCTL0 = CCIE; // CCR0 interrupt enabled

TA1CCR0 = 39062; //设置周期为250ms
TA1CCTL2 = OUTMOD_3; // CCR2 set/reset
TA1CCR2 = 40000; // CCR2 original PWM duty cycle 1/5
TA1CTL = TASSEL_2 + MC_1 + TACLR; // SMCLK, upmode, clear TAR
}
// 初始化ADC12
void InitADC12()
{
P6SEL = 0x08; // Enable P6.3 as A/D channel input
ADC12CTL0 = ADC12ON+ADC12MSC+ADC12SHT0_2; // Turn on ADC12, set sampling time, 16ADC12CLK cycles
ADC12CTL1 = ADC12SHP+ADC12CONSEQ_0+ADC12SSEL_2; // Use sampling timer, single time single channel,MCLK=5M
ADC12MCTL0 = ADC12SREF_2+ADC12INCH_3; // ref+ = VeREF+, ref- = AVSS, channel = A4
ADC12IE = 0x01; // Enable ADC12IFG.0
ADC12CTL0 |= ADC12ENC; // Enable conversions
}
volatile unsigned result ;
void main(void)
{
WDTCTL = WDTPW + WDTHOLD; // Stop WDT

InitClock();
Init_Timer_A();

P11OUT ^= 0x01; // Toggle触发 P11.0

while(1)
{
__bis_SR_register(LPM0_bits + GIE);

__no_operation();

}

}
#pragma vector=TIMER1_A0_VECTOR
__interrupt void TIMER1_A0_ISR(void)
{
P11OUT ^= 0x01; // Toggle触发 P11.0

InitADC12();

ADC12CTL0 |= ADC12SC;

__bis_SR_register(LPM0_bits + GIE);

__no_operation();
}

#pragma vector=ADC12_VECTOR
__interrupt void ADC12ISR (void)
{

switch(__even_in_range(ADC12IV,34))
{
case 0: break; // Vector 0: No interrupt
case 2: break; // Vector 2: ADC overflow
case 4: break; // Vector 4: ADC timing overflow
case 6: // Vector 6: ADC12IFG0
result = ADC12MEM0; // Move result, IFG置0

__bic_SR_register_on_exit(LPM0_bits); // Exit active CPU, SET BREAKPOINT HERE
case 8: break; // Vector 8: ADC12IFG1
case 10: break; // Vector 10: ADC12IFG2
case 12: break; // Vector 12: ADC12IFG3
case 14: break; // Vector 14: ADC12IFG4
case 16: break; // Vector 16: ADC12IFG5
case 18: break; // Vector 18: ADC12IFG6
case 20: break; // Vector 20: ADC12IFG7
case 22: break; // Vector 22: ADC12IFG8
case 24: break; // Vector 24: ADC12IFG9
case 26: break; // Vector 26: ADC12IFG10
case 28: break; // Vector 28: ADC12IFG11
case 30: break; // Vector 30: ADC12IFG12
case 32: break; // Vector 32: ADC12IFG13
case 34: break; // Vector 34: ADC12IFG14
default: break;
}
}


这个代码很好理解,先是程序进入Timer_A1的中断,然后初始化ADC12,再实现模数转换,进入ADC12的中断,将ADC采集到的数据传到result,然后再进入Timer_A1的中断。

调试过程没有问题,得到了定时采集ADC值的效果。

同时观察到红色LED灯闪烁,说明确实是隔了一段定时的时间进入中断。

到此为止,温控的程序逻辑算是调试结束了。

五:UART口调试

UART口调试部分没有完成,所以没有进行总结,仅将现在遇到的问题描述一下:

测试程序如下,意将一段语句“I’m MSP430 ”重复输出。

#include "msp430x54x.h"
unsigned char buffer[] = {"I'm MSP430!\n"};
void delay(unsigned int n)
{
unsigned int i,j;
for(i=0;i<n;i++)
for(j=0;j<1000;j++);
}

void main(void)
{
unsigned int i;
WDTCTL = WDTPW + WDTHOLD;          // 关狗

P10SEL = 0x30;                            // P5_6和P5_7第二功能打开,设置方向
P10DIR = 0x10;

UCA3CTL1 |= UCSWRST;          // 首先使RST位置位,只有这样后面的设置才有效
UCA3CTL1 |= UCSSEL_2;                  // SMCLK,为系统时钟1048576Hz
UCA3BR0 = 9;                            // 1MHz 115200
UCA3BR1 = 0;                            // 1MHz 115200
UCA3MCTL |= UCBRS_1 + UCBRF_0;        // 设置调整参数UCBRSx=1, UCBRFx=0
UCA3CTL1 &= ~UCSWRST;                // RST复位
UCA3IE |= UCTXIE;              // 使能发送中断允许
while(1)
{
while (!(UCA3IFG&UCTXIFG));  //等待BUF区准备好,当IFG=1时就表示准备好了
for(i=0; i<sizeof(buffer); i++)
{
UCA3TXBUF = buffer[i];      //向串口发送数据
delay(20);                   //延时

}
}
}


但是最后这段代码仅会在重新上电复位的时候输出:

不能实现我需要的输出。

软件我已经调试很多天了,现在我怀疑可能是串口本身的问题,重新焊一块测试板是下一步的方案。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  单片机 温控 调试