前言
特点
msp430系列单片机是16位,51单片机为8位,stm32系列为32位。 位数越高代表着该单片机处理数据的能力越快,性能也就越高。32位机器处理性能好,8位机器廉价性价比高,16位机器超低功耗。就是有一个低功耗模式,可以长时间极少耗损地待机,定时唤醒cpu进行工作.
学习路线
详细理解时钟初始化配置。
熟悉基本操作IO口。
学习定时器三大功能。
学习中断的原理。
操作各种外设,模块。
MSP430内部结构
在接触一款单片机,首先应该了解其总体框架,了解其内部结构,方便后期进行学习理解。
系统时钟
在MSP430F149单片机中一共有三个时钟源:
(1)LFXT1CLK,为低速/高速晶振源,通常接32.768kHz,也可以接(400kHz~16Mhz);
(2)XT2CLK,可选高频振荡器,外接标准高速晶振,通常是接8Mhz,也可以接(400kHz~16Mhz);
(3)DCOCLK,数控振荡器,为内部晶振,由RC震荡回路构成;
在MSP430F149内部一共有三个时钟系统:
(1)ACLK(Auxiliary Clock)辅助时钟,通常由LFXT1CLK或VLOCLK作为时钟源,可以通过软件控制更改时钟的分频系数;
(2)MCLK(Master Clock)系统主时钟单元,为系统内核提供时钟,它可以通过软件从四个时钟源选择;
(3)SMCLK(Sub-Main Clock)系统子时钟,也是可以由软件选择时钟源。
MSP430F149的时钟设置相关寄存器
DCOCTL
DCO控制寄存器,地址为56H,初始值为60H
DCO0~DCO2: 定义了8种频率之一,而频率由注入直流发生器的电流定义。
MOD0~MOD4: Modulation Bit,频率的微调。
该寄存器在不需要DCO的场合保持默认初始值
BCSCTL1
时钟配置寄存器 1,地址为57H,初始值为84H
RSEL0~RSEL2: 选择某个内部电阻以决定标称频率.0最低,7最高。
XT5V: 1.
DIVA0~DIVA1:选择ACLK的分频系数。DIVA=0,1,2,3,ACLK的分频系数分别是1,2,4,8;
XTS: 选择LFXT1工作在低频晶体模式(XTS=0)还是高频晶体模式(XTS=1)。
XT2OFF: 控制XT2振荡器的开启(XT2OFF=0)与关闭(XT2OFF=1)。
正常情况下把XT2OFF复位.
BCSCTL2
时钟配置寄存器2,地址为58H,初始值为00H
DCOR: 0,选择内部电阻;1,选择外部电阻
DIVS0~1: DIVS=0,1,2,3对应SMCLK的分频因子为1,2,4,8
SELS: 选择SMCLK的时钟源, 0:DCOCLK; 1:XT2CLK/LFXTCLK.
DIVM0~1: 选择MCLK的分频因子, DIVM=0,1,2,3对应分频因子为1,2,4,8.
SELM0~1: 选择MCLK的时钟源, 0与1:DCOCLK, 2:XT2CLK, 3:LFXT1CLK
下面是我自己进行的一段时钟系统初始化配置
void Clock_Init()
{
unsigned char i;
BCSCTL1 = RSEL0 + RSEL1 + RSEL2; // 采用最高频率7 ACLK = XT2
BCSCTL1&=~XT2OFF; //打开XT2振荡器
do
{
IFG1 &= ~OFIFG; // 清除振荡器失效标志
for (i = 255; i > 0; i--); // 延时,等待XT2起振
}
while ((IFG1 & OFIFG) != 0); // 判断XT2是否起振
BCSCTL2 |= SELS+SELM_2; // SMCLK = MCLK = XT2
}
接下来开始学习IO口配置
I/O
’|=‘,’&=~‘赋值操作
P1DIR &= ~BIT1; //P1.1端口设置为输入模式
P1SEL |= BIT1; //P1.1端口设置为功能复用
P1DIR |= BIT0; //P1.0端口设置为输出模式
P1OUT |= BIT0; //P1.0端口输出高电平
相关函数
跑马灯(延时函数)
#include <msp430x14x.h>
void Port_Init()
{
P4DIR |= 0xff; //P4端口配置为输出模式
}
void Clock_Init()
{
unsigned char i;
BCSCTL1 = RSEL0 + RSEL1 + RSEL2; // 采用最高频率7 ACLK = XT2
BCSCTL1&=~XT2OFF; //打开XT2振荡器
do
{
IFG1 &= ~OFIFG; // 清除振荡器失效标志
for (i = 255; i > 0; i--); // 延时,等待XT2起振
}
while ((IFG1 & OFIFG) != 0); // 判断XT2是否起振
BCSCTL2 |= SELS+SELM_2; // SMCLK = MCLK = XT2
}
void Delay_ms(unsigned int time)
{
unsigned int i;
for(i =0 ;i <1000 ;i++ )
for( ; time >0 ;time--)
_NOP();
}
void main(void)
{
unsigned int i;
WDTCTL = WDTPW + WDTHOLD;
Port_Init();
Clock_Init();
while(1)
{
for(i = 0;i<8;i++)
{
P4OUT = ~(0x01<<i);
Delay_ms(60000);
}
}
}
接下来使用定时器来实现跑马灯功能,这时候就要引入定时器这个概念了
定时器
内部集成有5种定时器:看门狗定时器WDT,基本定时器BT,定时器TimerA,定时器TimerB,实时时钟RTC.我们主要学习看门狗定时器WDT与定时器TimerA,B。
看门狗定时器WDT
作用
在程序死锁或着系统异常的情况下完成强制复位
特性
1.含有定时器与时钟源(定时器计数一定程度产生特定信号)
2.定时器计数过程中,可以通过某些信号清零定时器(简称喂狗)
3.可以禁止看门狗功能
相关寄存器:
WDTCTL:
控制看门狗的工作模式 时钟源 定时器时间间隔选择
WDTPW:看门狗密码设置
WDTHOLD:看门狗定时器挂起
WDTNMIES:看门狗定时器NMI边缘选择
WDTNMI:NMI功能选择位,控制RST/NMI引脚功能。
WDTTMSEL:看门狗功能模式选择。
WDTCNTCL:看门狗定时器清零。
WDTSSEL:看门狗定时器时钟选择。
IE1(系统中断控制寄存器):
控制看门狗电路中断信息
NMIIE:NMI中断使能控制位。
1:允许NMI中断; 0∶禁止NMI中断。
WDTIE:看门狗定时器中断允许位,控制当看门狗电路工作在周期定时模式时WDTIFG的中断使能。
1∶允许EDTIFG中断; 0:禁止WDTIFG中断。
IFG1(系统中断标志寄存器):
保留系统中断信息
NMIIFG:NMI中断请求标志位,用户必须软件清除该中断请求。
WDTIFG:看门狗定时器中断请求标志位。需要特别注意,在看门狗模式下该位必须由用户在软件中清除,而在周期定时模式下该位是可以被自动清除的。
程序中使用模式举例
1.禁止使用WDT:
WDTCTL = WDTPW +WDTHOL;//禁止看门狗定时器的使用
2.使用WDT模式:
#include <msp430x14x.h>
void main()//看门狗定时器产生方波
{
WDTCTL = WDT_MDLY_32; //看门狗定时器选用32ms的定时周期
IE1 |= WDTIE; //打开看门狗定时中断允许位
P1DIR |= BIT1; //设置方波输出引脚为输出方向
_EINT(); //打开总中断使能
}
#pragma vector=WDT_VECTOR //进入中断服务程序必须添加的语句
__interrupt void Watchdog(void)
{
P1OUT ^= BIT1; //看门狗定时器中断响应后将方波输出引脚电平取反,达到方波效果
}
定时器Timer
特点
16位定时器、4种工作模式;
多种可选择的计数器时钟源;
多个可配置输入端的捕获/比较寄存器;
8种输出模式的可配置输出单元
相关寄存器
TACTL:
16位计数器中的控制位和状态位;
TACCTLx
控制捕获/比较寄存器和比较器;
TACCRx
该寄存器使用最简单,可读可写,在PWM输出中CCR0常用作周期,CCR1中用作占空比。
TAR
16位计数器的技术执行单元,保存计数器内容
TAIV
保存了中断请求的中断源。
四种工作模式
停止模式
默认功能
增计数模式(产生两个中断标志)
计数到TACCR0值时,返回0,重新计数。此时计数到TACCR0的同时产生一个中断标志CCIFG,而当计数器溢出返回零的同时又同时产生一个中断标志TAIFG。
连续计数模式(产生一个中断标志)
计数器将直接计数到计数器所能计数的最大值0FFFFH之后重新返回零,再次计数。返回零的同时产生一个TAIFG中断标志。
如果相应中断位允许,每当一个定时间隔到来都会产生中断请求,在连续计数模式下,须将下一事件发生的时间在当前中断程序中加到CCRx中。
增减计数模式(产生两个中断标志)
当计数器计数到跟TACCR0一样的之后,然后从TACCR0开始又减少,直到为零,然后又开始增。当计数跟TACCR0一样的时候产生一个中断标志CCIFG,当减到为零的时候又产生一个中断标志TAIFG。
三种功能:
16位定时计数器
这是定时器的默认模式,当在比较模式下的时候,与捕获模式相关的硬件停止工作,如果这个时候开启定时器中断,然后设置定时器终值(将终值写入TACCRx),开启定时器,当TAR的值增到TACCRx的时候,中断标志位CCIFGx置1,同时产生中断。若中断允许未开启则只将中断标志位CCIFGx置1。
例子:能够软件设置定时间隔来产生中断处理一些事情,如键盘扫描,也可以结合信号输出产生时序脉冲发生器,PWM信号发生器。如:不断装载TACCRx,启动定时器,TAR和TACCRx比较产生中断处理。
//可以用来做跑马灯实验
#include <msp430x14x.h>
void Port_Init()
{
P4DIR |= 0xff; //P4端口设置为输出模式
}
void Clock_Init()
{
unsigned char i;
BCSCTL1 = RSEL0 + RSEL1 + RSEL2; // 采用最高频率7 ACLK = XT2
BCSCTL1&=~XT2OFF; //打开XT2振荡器
do
{
IFG1 &= ~OFIFG; // 清除振荡器失效标志
for (i = 255; i > 0; i--); // 延时,等待XT2起振
}
while ((IFG1 & OFIFG) != 0); // 判断XT2是否起振
BCSCTL2 |= SELS+SELM_2; // SMCLK = MCLK = XT2
}
void Delay_ms(unsigned int time)
{
unsigned int i;
for(i =0 ;i <1000 ;i++ )
for( ; time >0 ;time--)
_NOP();
}
void TimerA_Init()
{
CCTL0 = CCIE; // 捕获/比较中断使能
CCR0 = 6000; // 比较值为8000,即从0计数到8000产生一次中断
TACTL = TASSEL_2 + MC_1+TACLR; // 使用SMCLK时钟,使用增计数模式,清零TACCR寄存器
}
void main(void)
{
WDTCTL = WDTPW + WDTHOLD; //关闭看门狗
Port_Init();
Clock_Init();
TimerA_Init();
_BIS_SR(LPM0_bits + GIE); // _BIS_SR(LPM0_bits + GIE) 具有同时打开全局中断和进入LPM0; 等同于:_EINT(); LPM0;
}
#pragma vector=TIMERA0_VECTOR
__interrupt void Timer_A (void) //定时器A中断服务程序
{
unsigned int i;
for(i=0;i<8;i++)
{
P4OUT=~(0x01<<i);
Delay_ms(60000);
}
}
捕获功能
捕获模式:
利用外部信号的上升沿、下降沿或上升下降沿触发来测量外部或内部事件,也可以由软件停止。捕获源可以由CCISx选择CCIxA,CCIxB,GND,VCC。完成捕获后相应的捕获标志位CCIFGx置1
捕获模式的应用:
利用捕获源的来触发捕获TAR的值,并将每次捕获的值都保存到TACCRx中,可以随时读取TACCRx的值,TACCRx是个16位的寄存器,捕获模式用于事件的精确定位。如测量时间、频率、速度等
例子:利用两次捕获的值来测量脉冲的宽度。或捕获选择任意沿,CCISx=”11“(输入选择VCC),这样即当VCC与GND发生切换时产生
#include <msp430x14x.h>
void Port_Init()
{
P1DIR &= ~BIT1; //P1.1端口设置为输入模式
P1SEL |= BIT1; //P1.1端口设置为功能复用
P1DIR |= BIT0; //P1.0端口设置为输出模式
P1OUT |= BIT0;
}
void Clock_Init()
{
unsigned char i;
BCSCTL1 = RSEL0 + RSEL1 + RSEL2; // 采用最高频率7 ACLK = XT2
BCSCTL1&=~XT2OFF; //打开XT2振荡器
do
{
IFG1 &= ~OFIFG; // 清除振荡器失效标志
for (i = 255; i > 0; i--); // 延时,等待XT2起振
}
while ((IFG1 & OFIFG) != 0); // 判断XT2是否起振
BCSCTL2 |= SELS+SELM_2; // SMCLK = MCLK = XT2
}
void TimerA_Init()
{
//定时模式示例
//CCTL0 = CCIE; // 捕获/比较中断使能
//CCR0 = 6000; // 比较值为8000,即从0计数到8000产生一次中断
//TACTL = TASSEL_2 + MC_1+TACLR; // 使用SMCLK时钟,使用增计数模式,清零TACCR寄存器
//捕获模式示例
TACTL = TACLR + TASSEL_2 + ID_2 + MC_3; //清零TACCR寄存器,使用SMCLK时钟,8分频,使用增减计数模式
TACCTL0 = CM_2+ CCIS_0 + SCS + CAP + CCIE; //设置为 上升下降沿捕获,捕获通道0,同步捕获,捕捉模式,捕获/比较中断使能
//TACCTL1 = CM_3 + CCIS_1 + SCS + CAP + CCIE; //设置为 上升下降沿捕获,捕获通道1,同步捕获,捕捉模式,捕获/比较中断使能
}
void main(void)
{
WDTCTL = WDTPW + WDTHOLD; //关闭看门狗
Port_Init();
Clock_Init();
TimerA_Init();
_BIS_SR(LPM0_bits + GIE); // _BIS_SR(LPM0_bits + GIE) 具有同时打开全局中断和进入LPM0; 等同于:_EINT(); LPM0;
// 如果只使用定时器之类的功能时,可以使用低功耗模式,而不使用while(1)
}
#pragma vector=TIMERA0_VECTOR
__interrupt void Timer_A (void) //定时器A中断服务程序
{
P1OUT ^= BIT0;
}
输出PWM功能
#include <msp430x14x.h>
void Port_Init()
{
P6DIR = 0XFF;
P6OUT = 0Xff;
}
void Clock_Init()
{
unsigned char i;
BCSCTL1 = RSEL0 + RSEL1 + RSEL2; // 采用最高频率7 ACLK = XT2
BCSCTL1&=~XT2OFF; //打开XT2振荡器
do
{
IFG1 &= ~OFIFG; // 清除振荡器失效标志
for (i = 255; i > 0; i--); // 延时,等待XT2起振
}
while ((IFG1 & OFIFG) != 0); // 判断XT2是否起振
BCSCTL2 |= SELS+SELM_2; // SMCLK = MCLK = XT2
}
void Delay_ms(unsigned int time)
{
unsigned int i;
for(i =0 ;i <1000 ;i++ )
for( ; time >0 ;time--)
_NOP();
}
void TimerA_Init()
{
//定时模式示例
//CCTL0 = CCIE; // 捕获/比较中断使能
//CCR0 = 6000; // 比较值为8000,即从0计数到8000产生一次中断
//TACTL = TASSEL_2 + MC_1+TACLR; // 使用SMCLK时钟,使用增计数模式,清零TACCR寄存器
//捕获模式示例
//TACTL = TACLR + TASSEL_2 + ID_3 + MC_3; //清零TACCR寄存器,使用SMCLK时钟,8分频,使用增减计数模式
//TACCTL0 = CM_2+ CCIS_0 + SCS + CAP + CCIE; //设置为 下降沿捕获,捕获通道0,同步捕获,捕捉模式,捕获/比较中断使能
//TACCTL1 = CM_3 + CCIS_1 + SCS + CAP + CCIE; //设置为 上升下降沿捕获,捕获通道1,同步捕获,捕捉模式,捕获/比较中断使能
//PWM输出模式示例
TACTL |= TASSEL_2 + ID_3 + MC_2 + TACLR + TAIE; //使用SMCLK时钟,8分频,使用连续计数模式,清零TACCR寄存器,定时器溢出中断
TACCTL0= CCIE;
CCR0 = 5000;
}
void main(void)
{
WDTCTL = WDTPW + WDTHOLD; //关闭看门狗
Port_Init();
Clock_Init();
TimerA_Init();
_BIS_SR(LPM0_bits + GIE); // _BIS_SR(LPM0_bits + GIE) 具有同时打开全局中断和进入LPM0; 等同于:_EINT(); LPM0;
// 如果只使用定时器之类的功能时,可以使用低功耗模式,而不使用while(1)
}
#pragma vector=TIMERA0_VECTOR
__interrupt void Timer_A (void) //定时器A中断服务程序
{
P6OUT ^= BIT0;
CCR0 = CCR0 + 5000;
}