[资料分享] MSP430f149单片机的简单秒表

fish001   2018-3-11 18:09 楼主

实现功能:按下一个按键,计时开始,再次按下该按键计时暂停,即由一个按键实现暂停看开始功能,设置另一个按键,按下该按键三秒以上,秒表清零。要求计时精度,10毫秒。

  该实验用到了MSP430单片机的timerA的定时功能,显示设备而用到了LCD1602.

  代码如下:



[csharp] view plain copy
#include   
#include "Config.h"  
int second = 0, minute = 0, count = 0, flag = 0, a = 0, b = 0, c = 0;  
unsigned char FlagLcd;  
//*************************************************************************  
//          初始化IO口子程序  
//*************************************************************************  
void Port_init()  
{  
        P4SEL = 0x00;  
        P4DIR = 0xFF;                   //数据口输出模式  
        P5SEL = 0x00;  
        P5DIR|= BIT5 + BIT6 + BIT7;     //控制口设置为输出模式  
        P1SEL = 0x00;                   //P1普通IO功能  
        P1DIR = 0xF0;                   //P10~P13输入模式,外部电路已接上拉电阻  
        P1IE  = 0x0f;                   //开启 位中断  
        P1IES = 0x00;                   //上升沿触发中断  
        P1IFG = 0x00;                   //软件清零中断标志寄存器  
}  

//***********************************************************************  
//  显示屏命令写入函数  
//***********************************************************************  
void LCD_write_com(unsigned char com)   
{     
    RS_CLR;  
    RW_CLR;  
    EN_SET;  
    DataPort = com;                 //命令写入端口  
    delay_ms(5);  
    EN_CLR;  
}  

//***********************************************************************  
//  显示屏数据写入函数  
//***********************************************************************  
void LCD_write_data(unsigned char data)   
{  
    RS_SET;  
    RW_CLR;  
    EN_SET;  
    DataPort = data;                //数据写入端口  
    delay_ms(5);  
    EN_CLR;  
}  

//***********************************************************************  
//  显示屏清空显示  
//***********************************************************************  
void LCD_clear(void)   
{  
    LCD_write_com(0x01);            //清屏幕显示  
    delay_ms(5);  
}  

//***********************************************************************  
//  显示屏字符串写入函数  
//***********************************************************************  
void LCD_write_str(unsigned char x,unsigned char y,int w)   
{  

    if (y == 0)   
    {  
        LCD_write_com(0x80 + x);        //第一行显示  
    }  
    else   
    {  
        LCD_write_com(0xC0 + x);        //第二行显示  
    }  

    LCD_write_data(48+w);  

}  

//***********************************************************************  
//  显示屏单字符写入函数  
//***********************************************************************  
void LCD_write_char(unsigned char x,unsigned char y,unsigned char data)   
{  

    if (y == 0)   
    {  
        LCD_write_com(0x80 + x);        //第一行显示  
    }  
    else   
    {  
        LCD_write_com(0xC0 + x);        //第二行显示  
    }  

    LCD_write_data( data);   
}  

//***********************************************************************  
//  显示屏初始化函数  
//***********************************************************************  
void LCD_init(void)   
{  
    LCD_write_com(0x38);        //显示模式设置   
    delay_ms(5);  
    LCD_write_com(0x08);        //显示关闭  
    delay_ms(5);  
    LCD_write_com(0x01);        //显示清屏  
    delay_ms(5);  
    LCD_write_com(0x06);        //显示光标移动设置  
    delay_ms(5);  
    LCD_write_com(0x0C);        //显示开及光标设置  
    delay_ms(5);  
}  
//***********************************************************************  
//             TIMERA初始化,设置为UP模式计数  
//***********************************************************************  
void TIMERA_Init(void)                                   //UP模式计数,计数周期为CCR0+1  
{  
  TACTL |= TASSEL1 + TACLR + ID0 + ID1 + MC0 + TAIE;     //SMCLK做时钟源,8分频,增加计数模式,开中断  
  TACCR0 = 9999;                                         //CCR0=9999,10ms中断一次  
}  
//***********************************************************************  
//             关闭计时,暂停计数  
//***********************************************************************  
void TimerA_end(void)  
{  
    TACTL &= 0xfffd;  
}  
//**********************************************************************  
//  扫描按键P1^2是否长按  
//**********************************************************************  
void GetKey()//长按,返回2;短按,返回1。  
{  
    unsigned char keyRetu=0; //返回的按键值  
    static unsigned char s_keyState=0,keyTime=0; //按键状态,按键按下的时间  
    switch (s_keyState)  
    {  
    case 0:  
      {  
        if((P1IN&0x02)==0x00) //检测到有按键,转到状态1,相当于是消抖过程。  
        {  
            s_keyState=1;  
        }   
      }  
       break;  
    case 1:  
      {  
        if((P1IN&0x02)==0x00) //再次检测到有按键,转到状态2  
        {  
            s_keyState=2;  
            keyTime=0; //清零按键时间计数器  
        }  
        else  
        {  
            s_keyState=0; //没有检测到按键,说明状态0检测到是一个抖动,重新转到状态0  
        }  
      }  
        break;  
    case 2:  
      {  
        if((P1IN&0x02)==0x02) //检测到按键松开  
        {  
            s_keyState=0; //状态转到状态0  
            keyRetu=1; //输出1  
        }  
        else  
        {  
            if(++keyTime>=150) //按下时间>1s  
                {  
                    s_keyState=3; //转到状态3  
                    keyTime=0; //清零按键时间计数器  
                    keyRetu=2; // 输出2  
                }  
        }  
      }  
        break;  
    case 3:  
      {  
        if((P1IN&0x02)==0x02) //检测到按键松开  
        {  
            s_keyState=0; //状态转到状态0  
        }  
        else  
        {  
            s_keyState=3; //转到状态3  
        }  
      }  
        break;  
    }  
    if(keyRetu==2)  
    {  
      a = 0;  
      b = 0;  
      c = 0;  
      count = 0;  
      second = 0;  
      minute = 0;   
    }  
}  

//***********************************************************************  
//             TIMERA中断服务程序,需要判断中断类型  
//***********************************************************************  
#pragma vector = TIMERA1_VECTOR  
__interrupt void Timer_A(void)  
{  
  switch(TAIV)                                  //需要判断中断的类型  
  {  
  case 2:break;  
  case 4:break;  
  case 10:count++;break;                         //设置标志位Flag  
  }  
  if(count==100)                                 //100次为1秒  
  {  
    second++;  
    count=0;                        
  }  
  if(second == 60)  
  {  
    minute++;  
    second = 0;  
  }  
  GetKey();  
}  
//**********************************************************************  
//  P1口中断服务程序,需要判断  
//**********************************************************************  
#pragma vector = PORT1_VECTOR  
__interrupt void P1_IRQ(void)  
{  
  switch(P1IFG&0x0F)  
  {  
  case 0x01: {  

                  flag++;  
                  P1IFG=0x00;  
              }  
              break;  
  default:P1IFG = 0x00;break;                     
  }  
}  
//***********************************************************************  
//      主程序  
//***********************************************************************  
void main(void)  
{  
     WDT_Init();                        //看门狗设置                          
     Clock_Init();                       //系统时钟设置  
     Port_init();                        //系统初始化,设置IO口属性  
     delay_ms(100);                      //延时100ms  
     LCD_init();                         //液晶参数初始化设置  
     LCD_clear();                        //清屏  
     TIMERA_Init();  
     _EINT();  
     while (1)   
      {  
                if(flag%2==0)  
                {  
                  LCD_write_str(0,1,c/10);  
                  LCD_write_str(1,1,c%10);  
                  LCD_write_char(2,1,0x3a);  
                  LCD_write_str(3,1,b/10);  
                  LCD_write_str(4,1,b%10);  
                  LCD_write_char(5,1,0x3a);  
                  LCD_write_str(6,1,a/10);  
                  LCD_write_str(7,1,a%10);  
                }  
                else  
                {  
                  count = a;  
                  second = b;  
                  minute = c;  
                  LCD_write_str(0,1,minute/10);  
                  LCD_write_str(1,1,minute%10);  
                  LCD_write_char(2,1,0x3a);  
                  LCD_write_str(3,1,second/10);  
                  LCD_write_str(4,1,second%10);  
                  LCD_write_char(5,1,0x3a);  
                  LCD_write_str(6,1,count/10);  
                  LCD_write_str(7,1,count%10);  
                  a = count;  
                  b = second;  
                  c = minute;  
                }  
      }  
}  

  配置文件Config.h

[csharp] view plain copy
********************************************************************/  
//延时函数,IAR自带,经常使用到  
#define CPU_F ((double)8000000)   //外部高频晶振8MHZ  
//#define CPU_F ((double)32768)   //外部低频晶振32.768KHZ  
#define delay_us(x) __delay_cycles((long)(CPU_F*(double)x/1000000.0))   
#define delay_ms(x) __delay_cycles((long)(CPU_F*(double)x/1000.0))   

//自定义数据结构,方便使用  
#define uchar unsigned char  
#define uint  unsigned int  
#define ulong unsigned long  

//8个LED灯,连接在P6口,可通过断开电源停止使用,ADC使用时断开电源  
#define LED8DIR         P6DIR  
#define LED8            P6OUT                             //P6口接LED灯,8个  

//4个独立按键连接在P10~P13  
#define KeyPort         P1IN                              //独立键盘接在P10~P13  

//串口波特率计算,当BRCLK=CPU_F时用下面的公式可以计算,否则要根据设置加入分频系数  
#define baud           9600                                //设置波特率的大小  
#define baud_setting   (uint)((ulong)CPU_F/((ulong)baud))  //波特率计算公式  
#define baud_h         (uchar)(baud_setting>>8)            //提取高位  
#define baud_l         (uchar)(baud_setting)               //低位  

//RS485控制管脚,CTR用于控制RS485处于收或者发状态  
#define RS485_CTR1      P5OUT |= BIT2;          //控制线置高,RS485发送状态  
#define RS485_CTR0      P5OUT &= ~BIT2;         //控制线置低,RS485接收状态  

//2.8寸TFT彩屏显示控制相关硬件配置  
#define RS_CLR          P5OUT &= ~BIT5           //RS置低  
#define RS_SET          P5OUT |=  BIT5           //RS置高  

#define RW_CLR          P5OUT &= ~BIT6           //RW置低  
#define RW_SET          P5OUT |=  BIT6           //RW置高  

#define RD_CLR          P5OUT &= ~BIT7           //E置低  
#define RD_SET          P5OUT |=  BIT7           //E置高  

#define CS_CLR          P5OUT &= ~BIT0            //CS置低  
#define CS_SET          P5OUT |=  BIT0            //CS置高  

#define RST_CLR         P5OUT &= ~BIT3            //RST置低  
#define RST_SET         P5OUT |=  BIT3            //RST置高  

#define LE_CLR          P5OUT &= ~BIT1            //LE置低  
#define LE_SET          P5OUT |=  BIT1            //LE置高  

//2.8寸TFT彩屏触摸屏控制相关硬件配置  
#define PEN_CLR         P2OUT &= ~BIT0           //PEN置低,触碰触摸屏时,Penirq引脚由未触摸时的高电平变为低电平  
#define PEN_SET         P2OUT |=  BIT0           //PEN置高  
#define PEN             (P2IN & 0x01)            //P2.0输入的值  

#define TPDO_CLR    P2OUT &= ~BIT1           //TPDO置低  
#define TPDO_SET    P2OUT |=  BIT1           //TPDO置高  
#define TPDOUT          ((P2IN>>1)&0x01)         //P2.1输入的值  

#define BUSY_CLR    P2OUT &= ~BIT3           //BUSY置低  
#define BUSY_SET    P2OUT |=  BIT3           //BUSY置高  

#define TPDI_CLR    P2OUT &= ~BIT4            //TPDI置低  
#define TPDI_SET    P2OUT |=  BIT4            //TPDI置高  

#define TPCS_CLR    P2OUT &= ~BIT5            //TPCS置低  
#define TPCS_SET    P2OUT |=  BIT5            //TPCS置高  

#define TPCLK_CLR   P2OUT &= ~BIT6            //TPCLK置低  
#define TPCLK_SET   P2OUT |=  BIT6            //TPCLK置高  

//彩屏/12864液晶/1602液晶的数据口,三液晶共用  
#define DataDIR         P4DIR                     //数据口方向  
#define DataPort        P4OUT                     //P4口为数据口  

//12864/1602液晶控制管脚  
#define RS_CLR          P5OUT &= ~BIT5           //RS置低  
#define RS_SET          P5OUT |=  BIT5           //RS置高  

#define RW_CLR          P5OUT &= ~BIT6           //RW置低  
#define RW_SET          P5OUT |=  BIT6           //RW置高  

#define EN_CLR          P5OUT &= ~BIT7           //E置低  
#define EN_SET          P5OUT |=  BIT7           //E置高  

#define PSB_CLR         P5OUT &= ~BIT0            //PSB置低,串口方式  
#define PSB_SET         P5OUT |=  BIT0            //PSB置高,并口方式  

#define RESET_CLR   P5OUT &= ~BIT1            //RST置低  
#define RESET_SET   P5OUT |= BIT1             //RST置高  

//12864应用指令集  
#define CLEAR_SCREEN    0x01                  //清屏指令:清屏且AC值为00H  
#define AC_INIT     0x02                  //将AC设置为00H。且游标移到原点位置  
#define CURSE_ADD   0x06                  //设定游标移到方向及图像整体移动方向(默认游标右移,图像整体不动)  
#define FUN_MODE    0x30                  //工作模式:8位基本指令集  
#define DISPLAY_ON  0x0c                  //显示开,显示游标,且游标位置反白  
#define DISPLAY_OFF 0x08                  //显示关  
#define CURSE_DIR   0x14                  //游标向右移动:AC=AC+1  
#define SET_CG_AC   0x40                  //设置AC,范围为:00H~3FH  
#define SET_DD_AC   0x80                      //设置DDRAM AC  
#define FUN_MODEK   0x36                  //工作模式:8位扩展指令集  

//颜色代码,TFT显示用  
#define White          0xFFFF                                                               //显示颜色代码  
#define Black          0x0000  
#define Blue           0x001F  
#define Blue2          0x051F  
#define Red            0xF800  
#define Magenta        0xF81F  
#define Green          0x07E0  
#define Cyan           0x7FFF  
#define Yellow         0xFFE0  

//NRF2401模块控制线  
#define  RF24L01_CE_0        P1OUT &=~BIT5         //CE在P15           
#define  RF24L01_CE_1        P1OUT |= BIT5         

#define  RF24L01_CSN_0       P2OUT &=~BIT7         //CS在P27  
#define  RF24L01_CSN_1       P2OUT |= BIT7      

#define  RF24L01_SCK_0       P3OUT &=~BIT3         //SCK在P33  
#define  RF24L01_SCK_1       P3OUT |= BIT3     

#define  RF24L01_MISO_0      P3OUT &=~BIT2         //MISO在P32  
#define  RF24L01_MISO_1      P3OUT |= BIT2  

#define  RF24L01_MOSI_0      P3OUT &=~BIT1         //MOSI在P31  
#define  RF24L01_MOSI_1      P3OUT |= BIT1  

#define  RF24L01_IRQ_0       P1OUT &=~BIT4         //IRQ在P14      
#define  RF24L01_IRQ_1       P1OUT |= BIT4  

//DS18B20控制脚,单脚控制  
#define DQ_IN           P1DIR &= ~BIT7        //设置输入,DS18B20接单片机P53口  
#define DQ_OUT          P1DIR |= BIT7         //设置输出  
#define DQ_CLR          P1OUT &= ~BIT7            //置低电平  
#define DQ_SET          P1OUT |= BIT7             //置高电平  
#define DQ_R            P1IN & BIT7       //读电平  

//红外接收头H1838控制脚,单脚控制  
#define RED_IN          P1DIR &= ~BIT6            //设置输入,红外接收头接单片机PE3口  
#define RED_OUT         P1DIR |=  BIT6            //设置输出  
#define RED_L           P1OUT &= ~BIT6            //置低电平  
#define RED_H           P1OUT |= BIT6             //置高电平  
#define RED_R           (P1IN & BIT6)             //读电平  

//***********************************************************************  
//                   系统时钟初始化,外部8M晶振  
//***********************************************************************  
void Clock_Init()  
{  
  uchar i;  
  BCSCTL1&=~XT2OFF;                 //打开XT2振荡器  
  BCSCTL2|=SELM1+SELS;              //MCLK为8MHZ,SMCLK为8MHZ  
  do{  
    IFG1&=~OFIFG;                   //清楚振荡器错误标志  
    for(i=0;i<100;i++)  
       _NOP();  
  }  
  while((IFG1&OFIFG)!=0);           //如果标志位1,则继续循环等待  
  IFG1&=~OFIFG;   
}  

//***********************************************************************  
//                   系统时钟初始化,内部RC晶振  
//***********************************************************************  
void Clock_Init_Inc()  
{  
  uchar i;  

// DCOCTL = DCO0 + DCO1 + DCO2;              // Max DCO  
// BCSCTL1 = RSEL0 + RSEL1 + RSEL2;          // XT2on, max RSEL  

  DCOCTL = 0x60 + 0x00;                       //DCO约3MHZ,3030KHZ  
  BCSCTL1 = DIVA_0 + 0x07;  
  BCSCTL2 = SELM_2 + DIVM_0 + SELS + DIVS_0;  
}  

//***********************************************************************  
//                   系统时钟初始化,外部32.768K晶振  
//***********************************************************************  
void Clock_Init_Ex32768()  
{  
  uchar i;  

  BCSCTL2|=SELM1 + SELM0 + SELS;    //MCLK为32.768KHZ,SMCLK为8MHZ  
  do{  
    IFG1&=~OFIFG;                   //清楚振荡器错误标志  
    for(i=0;i<100;i++)  
       _NOP();  
  }  
  while((IFG1&OFIFG)!=0);           //如果标志位1,则继续循环等待  
  IFG1&=~OFIFG;   
}  

//***********************************************************************  
//               MSP430内部看门狗初始化  
//***********************************************************************  
void WDT_Init()  
{  
   WDTCTL = WDTPW + WDTHOLD;       //关闭看门狗  
}  

回复评论

暂无评论,赶紧抢沙发吧
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 京公网安备 11010802033920号
    写回复