电赛MSP430知识小结

2019-04-13 13:04发布

(转)今年的电赛对TI公司生产的MSP430系列的单片机进行了初步的学习,第一次参加电赛,知识量不足,所以在此对资料进行了总结,旨在留存一下知识,以便在今后的学习中,可以有所回忆,减少时间的消耗,算是见见同志第一个菜鸟篇学习日志,菜鸟先飞,也希望能帮助初学MSP430单片机的同学,仅仅适合初学者,且只介绍我涉及到的方面,谢谢大家支持原创。 我使用的是Launchpad MSP430G2553开发板:有14I/O口和10A/D 一:编译软件 编译软件可以使用IARCCS两种,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 上面自带有LED,一个接在P1.0 上,一个接在P1.6 上。 我们用个交替闪烁。 #include void main();第一个字母小写 { WDTCTL=WDTPW+WDTHOLD; P1DIR|=BIT0+BIT6;//设置P1.0 P1.6 为输出 P1OUT|=BIT0//线让LED0 亮。 while1 { 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;//定时器时钟源为SMCLKup,不分频,清零    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的五种低功耗模式:   LPM0LPM1LPM2LMP3LMP4 MSP430 具有一种运行模式及种可利用软件来选择的低功耗操作模式。一个中断事件能够将器件从任一低功耗模式唤醒、处理请求、并在接收到来自中断程序的返回信号时恢复至低功耗模式。 以下种操作模式可利用软件来配置: 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 生成器保持启用 – 晶体振荡器被停止 有两篇博文写的特别好,我把链接发一下,强烈推荐大家看一下,比看我这个强多了:一个是对低功耗与中断的总结,一个是全面总结 1http://blog.sina.com.cn/s/blog_6cd2030b01017x71.html(这个还有背景音乐) 2http://blog.sina.com.cn/s/blog_76790d7d01012tir.html    三:10AD MSP430G2553可以8路采集AD后的电压信号,采集端口是P1.0~P1.7 粘贴一下10AD  8路采样模块化程序8AD的结果存储在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;*/   }