(转)今年的电赛对TI
公司生产的MSP430
系列的单片机进行了初步的学习,第一次参加电赛,知识量不足,所以在此对资料进行了总结,旨在留存一下知识,以便在今后的学习中,可以有所回忆,减少时间的消耗,算是见见同志第一个菜鸟篇学习日志,菜鸟先飞,也希望能帮助初学MSP430单片机的同学,仅仅适合初学者,且只介绍我涉及到的方面,谢谢大家支持原创。
我使用的是Launchpad MSP430G2553
开发板:有14
个I/O
口和10
位A/D
一:编译软件
编译软件可以使用IAR
和CCS
两种,CCS
体积较大,IAR
针对性强,见见同志使用的是IAR for MSP430
相关的官方下载链接如下:
http://www.iar.com/en/Service-Center/Downloads/;
下载过后使用注册机破解一下,注册机上百度搜索一个适合版本的,找一个就行;具体的IAR
操作方法没办法附加在附件中,
给个IAR说明的下载链接:http://download.csdn.net/detail/tushuguan2/3986147 ,名字:《手把手教你使用TI MSP430 LaunchPad
》
或者上新浪的下载链接http://ishare.iask.sina.com.cn/f/23840199.html?from=dl,要用Launchpad要设置一下IAR,看链接的说明就行,我就不一一介绍了。
TI公司生产的MSP430系列单片机与普通51单片机没有很大的不同,但是其内部含有大量的寄存器,功能比较细化,可能会造成混乱,与51对比学习,可以加快学习进度;
程序编写的格式:
#include
Void main()
{
WDTCTL=WDTPW+WDTHOLD;//关闭看门狗。
**(程序)
}
二:通用I/O方面
P
口端口寄存器:
(1)
、PxDIR
输入/
输出方向寄存器(0
:输入模式 1
:输出模式)
(2)
、PxIN
输入寄存器输入寄存器是只读寄存器,用户不能对其写入,只能通过读取该寄存器的内容知道I/O
口的输入信号。
(3)
、PxOUT
输出寄存器寄存器内的内容不会受引脚方向改变的影响。
(4)
、PxIFG
中断标志寄存器(0
:没有中断请求 1
:有中断请求),该寄存器有8
个标志位,对应相应的引脚是否有待处理的中断请求;这8
个中断标志共用一个中断向量,中断标志不会自动复位,必须软件复位;外部中断事件的时间必须>=1.5
倍的MCLK
的时间,以保证中断请求被接受;
(5)
、PxIES
中断触发沿选择寄存器(0
:上升沿中断 1
:下降沿中断)
(6)
、PxSEL
功能选择寄存器(0
:选择引脚为I/O
端口 1
:选择引脚为外围模块功能)
(7)
、PxREN
上拉/
下拉电阻使能寄存器(0
:禁止 1
:使能)
基本操作:
(1)
、所有P
口都可作为通用IO
口使用
(2)
、所有P
口都可进行字节操作和位操作
基本操作:
(1)
、所有P
口都可作为通用IO
口使用
(2)
、所有P
口都可进行字节操作和位操作
按字节操作:
传统51:
P1=0X01
;
输入输出直接赋值
例 : P1DIR=0xff; //将P1口作为输出口
PIOUT=0x20
; // P1
口输出0x20
P1DIR=0x00
; //
将P1
口作为输入口
data=P1IN //
读取P1
口外部输入值
按位操作:
传统51:
sbit P1_0=P1^0
;
输入输出直接赋值
例: P1DIR=BIT0; //将P1.0作为输出口
P1OUT|=BIT0; //P1.0
输出1
P1OUT&=~BIT0; //P1.0
输出0
P1DIR&=~BIT0 //
将P1.0
口作为输入
data=P1IN&BIT0 //
读取P1.0
口外部输入值
举例:闪烁LED。
LaunchPad 上面自带有2 个LED,一个接在P1.0 上,一个接在P1.6 上。
我们用2 个交替闪烁。
#include
void main();第一个字母小写
{
WDTCTL=WDTPW+WDTHOLD;
P1DIR|=BIT0+BIT6;//设置P1.0 和P1.6 为输出
P1OUT|=BIT0;//线让LED0 亮。
while(1)
{
unsigned int i=50000;
while(i--);
P1OUT^=0x41;//对P1.0 和P1.6 取反,所以LED0 和LED1 会交替闪烁。
}
}
MSP430
不能和51
一样直接按位操作,只能通过与、或、非等逻辑运算对寄存器进行操作。这里是最大的不同,用习惯了51
,再用MSP430
就会感觉特别的麻烦,只要习惯就行了,没什么好办法。
二:中断
中断的 一般设置:
(1)、打开、关闭局部中断:
打开局部中断一般是给想关的特殊功能寄存器相关位置1
以P1口外部中断为例:
打开局部中断:
P1IE|=BIT0;//打开P1.0外部中断 ,BIT0的值为0x01,即把P1IE的第一位置1
关闭局部中断一般是给想关的特殊功能寄存器相关位置0
同样以P1口外部中断为例:
关闭局部中断:
P1IE&=~BIT0;//关闭P1.0外部中断
(2)、打开、关闭全局中断:
_EINT();//打开总中断,相当于51的EA=1;
_DINT();//关闭总中断,相当于51的EA=0;
(3)、各中断向量Interrupt Vectors:
#define BASICTIMER_VECTOR (0 * 2u) /* 0xFFE0 Basic Timer */
#define PORT2_VECTOR (1 * 2u) /* 0xFFE2 Port 2 */
#define USART1TX_VECTOR (2 * 2u) /* 0xFFE4 USART 1 Transmit */
#define USART1RX_VECTOR (3 * 2u) /* 0xFFE6 USART 1 Receive */
#define PORT1_VECTOR (4 * 2u) /* 0xFFE8 Port 1 */
#define TIMERA1_VECTOR (5 * 2u) /* 0xFFEA Timer A CC1-2, TA */
#define TIMERA0_VECTOR (6 * 2u) /* 0xFFEC Timer A CC0 */
#define ADC12_VECTOR (7 * 2u) /* 0xFFEE ADC */
#define USART0TX_VECTOR (8 * 2u) /* 0xFFF0 USART 0 Transmit */
#define USART0RX_VECTOR (9 * 2u) /* 0xFFF2 USART 0 Receive */
#define WDT_VECTOR (10 * 2u) /* 0xFFF4 Watchdog Timer */
#defineCOMPARATORA_VECTOR (11 * 2u) /* 0xFFF6Comparator A */
#define TIMERB1_VECTOR (12 * 2u) /* 0xFFF8 Timer B CC1-6, TB */
#define TIMERB0_VECTOR (13 * 2u) /* 0xFFFA Timer B CC0 */
#define NMI_VECTOR (14 * 2u) /* 0xFFFC Non-maskable */
#define RESET_VECTOR (15 * 2u) /* 0xFFFE Reset [HighestPriority] */
(4)、中断的嵌套:
当同时有多个中断来的时候才有优先级的考虑(优先级顺序可查看向量表)
实现中断嵌套需要注意以下几点:
1)430默认的是关闭中断嵌套的,一定要中断嵌套的话,就必须在中断服务程序中打开总中断
msp430的指令中,_DINT()和_EINT()分别指关和开总中断。
2)当进入中断服务程序时,只要不在中断服务程序中再次开中断,则总中断是关闭的,此时来中断不管是比当前中断的优先级高还是低都不执行;
3)若在中断服务程序A中开了总中断,则可以响应后来的中断B(不管B的优先级比A高还是低),B执行完再继续执行A。注意:进入中断服务程序B后总中断同样也会关闭,如 果B中断程序执行时需响应中断C,则此时也要开总中断,若不需响应中断,则不用开中断,B执行完后跳出中断程序进入A程序时,总中断会自动打开;
4)若在中断服务程序中开了总中断,后来的中断同时有多个,则会按优先级来执行,即中断优先级只有在多个中断同时到来时才起做用!中断服务不执行抢先原则。
5)对于单源中断,只要响应中断,系统硬件自动清中断标志位,对于TA/TB定时器的比较/捕获中断,只要访问TAIV/TBIV,标志位倍被自动清除;
对于多源中断要手动清标志位,比如P1/P2口中断,要手工清除相应的标志,如果在这种中断用"EINT();"开中断,而在打开中断前没有清标志,就会 有相同的中断不断嵌入,而导致堆栈溢出引起复位,所以在这类中断中必须先清标志再打开中断开关.
举例:1:
中断应用程序举例(外部中断):
void interrupt_initial()
{
P1DIR&=~BIT7; //P1.7为输入
P1IE|=0x80; //P1.7中断允许
P1IES|=0x00; //P1.7上升沿触发
P1IFG=0; //P1.7中断标志清除,对于多源中断必须先清中断标志再打开中断
_EINT(); //总中断允许
}
#pragmavector=PORT1_VECTOR
__interruptvoid Port_1(void)
{
P1IFG&=~BIT7; //P1.7中断标志清除
/*在此写中断服务子程序*/
}
如果想写中断的嵌套,或者几个中断一起作用,我电赛就是写的按键中断、定时器中断:
举例2
:(部分)
//------------------------按键初始化-----------------------------------------------------------------
voidKey_init(void)
{
P1REN |= BIT3; //
打开上拉,电路板上没有上拉电阻,触发边沿是从高电平到低电平
P1IES |= BIT3; //
选择触发边沿,从高电平到低电平
P1IFG &= ~BIT3; //
清除P1.3
的中断标志位(可以不清除,为了确保初始化之后为标志位不会触发中断)
P1IE |= BIT3; //
打开P1
的中断
P1REN |= BIT2; //
打开上拉,电路板上没有上拉电阻,触发边沿是从高电平到低电平
P1IES |= BIT2; //
选择触发边沿,从高电平到低电平
P1IFG &= ~BIT2; //
清除P1.2
的中断标志位(可以不清除,为了确保初始化之后为标志位不会触发中断)
P1IE |= BIT2; //
打开P1
的中断
}
//
函数名称:main()
//
功 能:主函数
//-----------------------------------------------------------------------------------------
void main(void)
{
//uchar i,j,t;
Initial(); //
初始化子程序
Ht1621_Init(); //
上电初始化LCD
Key_init(); //
调用IO
中断初始化函数
init_ADC10();
TACTL=TASSEL1+MC1+TACLR;//
定时器时钟源为SMCLK
,up,
不分频,清零
CCTL0|=CCIE;//
使能比较器中断
CCR0=50000;//
计数器终值
__enable_interrupt();//
使能全局中断,C
编译器中的内部函数
_EINT();
start_ADC10();
display_jiaodu();
LPM1; //
进入低功耗模式1
}
void init_ADC10()
{
//P1SEL|= 0xFF;
ADC10AE0|= 0x70;//
使用P1.0,1.1,1,2 AD
转换
ADC10CTL0= ADC10ON + MSC + SREF_0 + REFON;
//
开AD
内核,选择电源为参考电压
ADC10CTL1= ADC10SSEL_0 + CONSEQ_0; //
采用单通道多次采用
_EINT(); //
使能中断
}
void start_ADC10()
{
ADC10CTL0&= ~(ADC10SC + ENC);
ADC10CTL1&= ~INCH_3;
ADC10CTL1|= INCH_4;
ADC10CTL0|= ADC10SC + ENC;
while(ADC10CTL1 & ADC10BUSY != 0)
;
result[0]= ADC10MEM;
ADC10CTL0&= ~(ADC10SC + ENC);
ADC10CTL1&= ~INCH_4;
ADC10CTL1|= INCH_5;
ADC10CTL0|= ADC10SC + ENC;
while(ADC10CTL1 & ADC10BUSY != 0)
;
result[1]= ADC10MEM;
ADC10CTL0&= ~(ADC10SC + ENC);
ADC10CTL1&= ~INCH_5;
ADC10CTL1|= INCH_6;
ADC10CTL0|= ADC10SC + ENC;
while(ADC10CTL1 & ADC10BUSY != 0)
;
result[2]= ADC10MEM;
ADC10CTL0&= ~(ADC10SC + ENC);
ADC10CTL1&= ~INCH_6;
ADC10CTL1|= INCH_7;
ADC10CTL0|= ADC10SC + ENC;
while(ADC10CTL1 & ADC10BUSY != 0)
;
if (result[0]<=483)
{
result[0]=483;
}
if (result[0]>=738)
{
result[0]=738;
}
if (result[1]<=530)
{
result[1]=530;
}
if (result[1]>=776)
{
result[1]=776;
}
if (result[2]>=698)
{
result[2]=698;
}
if (result[2]<=458)
{
result[2]=458;
}
}
#pragma vector=TIMER0_A0_VECTOR
__interrupt void Timer(void)
{
//PushKey=P1IFG&BIT3;
if(flag==0)
{
i++;
if(i==100)//
定时5s
{
i=0;
start_ADC10();
display_jiaodu();
}
}
}
#pragma vector = PORT1_VECTOR //
中断向量声明
__interrupt void Key_interrput(void)
{
if(P1IFG&(BIT2+BIT3)) //
判断是不是P1.3
这个IO
口产生了中断
{
if(P1IFG&BIT3)
{
start_ADC10();
display_jiaodu();
P1IFG&=~BIT3;
P1IFG &= ~BIT3;//
清除中断标志位,这一步一定要有。
}
if(P1IFG&BIT2)
{
start_ADC10();
display();
P1IFG&=~BIT2;
P1IFG &= ~BIT2;//
清除中断标志位,这一步一定要有。
}
}
else
{
P1IFG = 0x00;
}
}
提到中断,就不得不提到MSP430
的五种低功耗模式: LPM0
、LPM1
,LPM2
,LMP3
,LMP4
MSP430
具有一种运行模式及5
种可利用软件来选择的低功耗操作模式。一个中断事件能够将器件从任一低功耗模式唤醒、处理请求、并在接收到来自中断程序的返回信号时恢复至低功耗模式。
以下6
种操作模式可利用软件来配置:
1
、激活模式(AM)
–
所有时钟处于激活状态
2
、低功耗模式0 (LPM0)
– CPU
被禁用
– ACLK
和SMCLK
仍然有效,MCLK
被禁用
3
、低功耗模式1 (LPM1)
– CPU
被禁用
– ACLK
和SMCLK
仍然有效,MCLK
被禁用
–
如果DCO
不是在激活模式下被使用,则DCO
的dc
生成器被禁用
3
、低功耗模式2 (LPM2)
– CPU
被禁用
– MCLK
和SMCLK
被禁用
– DCO
的dc
生成器保持启用
– ACLK
保持激活
4
、低功耗模式3 (LPM3)
– CPU
被禁用
– MCLK
和SMCLK
被禁用
– DCO
的dc
生成器保持启用
– ACLK
保持激活
5
、低功耗模式4 (LPM4)
– CPU
被禁用
– ACLK
被禁用
– MCLK
和SMCLK
被禁用
– DCO
的dc
生成器保持启用
–
晶体振荡器被停止
有两篇博文写的特别好,我把链接发一下,强烈推荐大家看一下,比看我这个强多了:一个是对低功耗与中断的总结,一个是全面总结
1
:http://blog.sina.com.cn/s/blog_6cd2030b01017x71.html(这个还有背景音乐)
2
:http://blog.sina.com.cn/s/blog_76790d7d01012tir.html
三:10
位AD
MSP430G2553
可以8
路采集AD
后的电压信号,采集端口是P1.0~P1.7
;
粘贴一下10
位AD 8
路采样模块化程序8
路AD
的结果存储在result[]
数组里;然后根据自己的程序对result[]
进行计算或者显示,这个程序是从坛友幻灵那里弄来的,留给读者自己研究吧:
voidinit_ADC10()
{
P1SEL |= 0xFF;
ADC10AE0 |= 0xF0;
ADC10CTL0 = ADC10ON + MSC + SREF_0 + REFON;
//
开AD
内核,选择电源为参考电压
ADC10CTL1 = ADC10SSEL_0 + CONSEQ_0; //
采用单通道多次采用
}
voidstart_ADC10()
{
ADC10CTL1 |= INCH_0;
ADC10CTL0 |= ADC10SC + ENC; //
开始转换 开转换允许
while (ADC10CTL1 & ADC10BUSY != 0)
; //
判断是否转换完毕
result[0] = ADC10MEM;
ADC10CTL0 &= ~(ADC10SC + ENC); //
关转换允许才能选择通道
ADC10CTL1 &= ~INCH_0; //
通道清0
ADC10CTL1 |= INCH_1;
ADC10CTL0 |= ADC10SC + ENC;
while (ADC10CTL1 & ADC10BUSY != 0)
;
result[1] = ADC10MEM;
ADC10CTL0 &= ~(ADC10SC + ENC);
ADC10CTL1 &= ~INCH_1;
ADC10CTL1 |= INCH_2;
ADC10CTL0 |= ADC10SC + ENC;
while (ADC10CTL1 & ADC10BUSY != 0)
;
result[2] = ADC10MEM;
ADC10CTL0 &= ~(ADC10SC + ENC);
ADC10CTL1 &= ~INCH_2;
ADC10CTL1 |= INCH_3;
ADC10CTL0 |= ADC10SC + ENC;
while (ADC10CTL1 & ADC10BUSY != 0)
;
/*result[3] = ADC10MEM;
ADC10CTL0 &= ~(ADC10SC + ENC);
ADC10CTL1 &= ~INCH_3;
ADC10CTL1 |= INCH_4;
ADC10CTL0 |= ADC10SC + ENC;
while (ADC10CTL1 & ADC10BUSY != 0)
;
result[4] = ADC10MEM;
ADC10CTL0 &= ~(ADC10SC + ENC);
ADC10CTL1 &= ~INCH_4;
ADC10CTL1 |= INCH_5;
ADC10CTL0 |= ADC10SC + ENC;
while (ADC10CTL1 & ADC10BUSY != 0)
;
result[5] = ADC10MEM;
ADC10CTL0 &= ~(ADC10SC + ENC);
ADC10CTL1 &= ~INCH_5;
ADC10CTL1 |= INCH_6;
ADC10CTL0 |= ADC10SC + ENC;
while (ADC10CTL1 & ADC10BUSY != 0)
;
result[6] = ADC10MEM;
ADC10CTL0 &= ~(ADC10SC + ENC);
ADC10CTL1 &= ~INCH_6;
ADC10CTL1 |= INCH_7;
ADC10CTL0 |= ADC10SC + ENC;
while (ADC10CTL1 & ADC10BUSY != 0)
;
result[7] = ADC10MEM;
ADC10CTL0 &= ~(ADC10SC + ENC);
ADC10CTL1 &= ~INCH_7;*/
}