其他分享
首页 > 其他分享> > msp430使用OLED实现数字钟

msp430使用OLED实现数字钟

作者:互联网

在这里可以下载完整的工程与原理图:
https://download.csdn.net/download/qq_45467083/12087894

本次实验使用msp430及一块扩展板实现数字钟系统,扩展板上有一块OLED屏幕与4x4矩阵键盘,此外还有一些led指示灯,如下图所示:
在这里插入图片描述
最终实现的效果如下图所示:
在这里插入图片描述
可以看到有一个动态的开机界面,细心的同学还会发现右上角是一个月亮图案,白天可是会变成太阳哦~
在这里插入图片描述
系统采用了层级设置界面的方式,使用状态标志记录当前显示界面。将显示与数据运算分层,使代码逻辑清晰易懂。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
main函数代码如下:

#include <msp430f6638.h>

#include "lib\interfacePaint.h"
#include "lib\init.h"

#include "lib\oled\oled.h"
#include "lib\oled\bmp.h"

void Key_Head();
void run_time();
void run_date();
void make_date_legal();

int time_d = 0;//分频计数器

int h = 23, m = 59, s = 50;//时分秒
int y = 2017, mon = 12, d = 31;//出生日期为该天,计算多少天后的天数只需要用 (年-2018)+月天数+日天数 即可

//闹钟存储数组,5个闹钟,
//分别存储 h,m,s,off(0)/on(1)s
int alarms[5][4] = { {0,0,0,0},
                     {0,0,0,0},
                     {0,0,0,0},
                     {0,0,0,0},
                     {0,0,0,0} };
//界面字符串缓存,init时即初始化,闹钟设置选择界面使用
char set_alarm_string[25] = "1OFF 2OFF 3OFF 4OFF 5OFF";
int alarm_on = 0;
int alarm_count = 0;

//状态标志:0:走钟;1:设置;2:时间设置界面;3:日期设置界面;4:闹钟设置选择界面;41~45:第x个闹钟设置界面
unsigned int state = 0;
int time_mode = 1; // 12/24显示模式切换,1:表示24小时,0:表示12小时

int row_max = 1;//最大行数,移动行时使用
int choice_row = 1;//标志行,设置是标志位置使用

int key_delay = 0;//按键消抖,每10ms扫描键盘,一旦按键,计时开始,150ms后恢复
int reading = 0;//是否按下键

int scount = 0;//动画移动计数
unsigned char clearbmp[768]={0};

void main(void) {
    WDTCTL = WDTPW+WDTHOLD;

    //测试时钟频率使用
    P1DIR |= BIT1;
    P1OUT |= BIT1;

    init_clock_XT2();
    init_timerA();

    init_keyboard();

    init_led();

    init_oled();

    //初始化显示日期,因为日期刷新不频繁,故需要在此、在返回主界面、设置完毕、run_date()后执行
    show_date(y, mon, d);
    //使时间与日期同时出现
    show_time(h,m,s);

    OLED_DrawBMP(0, 1, 98, 24, clouds);
    if(h<6 || h>21)
        OLED_DrawBMP(110, 0, 126, 16, moon);
    else
        OLED_DrawBMP(110, 0, 126, 16, sun);

    _enable_interrupts();

    LPM0;
    //while(1);
}

//timer_a定时器中断,中断时间是0.001s
#pragma vector = TIMER0_A0_VECTOR
__interrupt void TIMER0_A0_ISR(void){

    //动画效果
    if(state == 0 && time_d%21 == 0){
        if(scount == 20){
            scount=0;
            OLED_DrawBMP(0, 1, 98, 24, clouds);
            OLED_DrawBMP(98, 0, 130, 24, clearbmp);
            if(h<6 || h>21)
                OLED_DrawBMP(110, 0, 126, 16, moon);
            else
                OLED_DrawBMP(110, 0, 126, 16, sun);
        }else{
            OLED_DrawBMP(0+scount, 1, 98+scount, 24, clouds);
            if(h<6 || h>21)
                OLED_DrawBMP(110, 0, 126, 16, moon);
            else
                OLED_DrawBMP(110, 0, 126, 16, sun);
        }
        scount++;
    }

    //1s计时器
    if(time_d == 100){
        run_time();

        switch(state){
        case 0://时钟界面
            if(time_mode)
                show_time(h,m,s);
            else
                show_time12(h,m,s);
            break;
        case 1:setting_glisten(choice_row);break;//设置界面,闪烁选中行
        case 2:time_set_glisten(choice_row,h,m,s);break;//时间设置界面,闪烁选中数字
        case 3:date_set_glisten(choice_row,y,mon,d);break;//日期设置界面,闪烁选中数字
        case 4:show_alarm_set_glisten(choice_row);break;//闹钟设置选择界面
        case 41:case 42:case 43:case 44: case 45:
            alarm_set_glisten(choice_row, alarms, state);
            break;
        default:break;
        }

        if(h==0 && m==0 && s==0){
            run_date();
            if(state == 0)
                show_date(y, mon, d);
        }

        if(alarms[0][3] && alarms[0][0] == h && alarms[0][1] == m && alarms[0][2] == s){
            alarm_on = 1; alarm_count = 0;
        }
        if(alarms[1][3] && alarms[1][0] == h && alarms[1][1] == m && alarms[1][2] == s){
            alarm_on = 2; alarm_count = 0;
        }
        if(alarms[2][3] && alarms[2][0] == h && alarms[2][1] == m && alarms[2][2] == s){
            alarm_on = 3; alarm_count = 0;
        }
        if(alarms[3][3] && alarms[3][0] == h && alarms[3][1] == m && alarms[3][2] == s){
            alarm_on = 4; alarm_count = 0;
        }
        if(alarms[4][3] && alarms[4][0] == h && alarms[4][1] == m && alarms[4][2] == s){
            alarm_on = 5; alarm_count = 0;
        }

        time_d = 0;
        //测试时钟频率使用
        P1OUT ^= BIT1;
    }

    //闹钟闪烁
    if(alarm_on != 0 && time_d%50 == 0){
        alarm_count++;
        switch(alarm_on){
        case 1:P6OUT ^= BIT0;break;
        case 2:P6OUT ^= BIT1;break;
        case 3:P6OUT ^= BIT2;break;
        case 4:P6OUT ^= BIT3;break;
        case 5:P6OUT ^= BIT4;break;
        default:break;
        }
        if(alarm_count == 120){
            alarm_count = 0;//计数清零
            alarm_on = 0;//关闭闪烁
            P6OUT |= BIT0 + BIT1 + BIT2 + BIT3 + BIT4 + BIT5 + BIT6 + BIT7;//熄灯
        }
    }

    //每10ms扫描键盘,但读取后等待200ms后才能重新读取
    if(key_delay > 20){ key_delay=0; reading = 0;}
    //键盘扫描 ,f=100Hz
    Key_Head();
    if(reading == 1) key_delay++;

    //分频计数器
    time_d++;
}


volatile unsigned char KeyVal; //键值
volatile unsigned char CF[4], Cont[4];
const unsigned char KeyOut[4] = { 0xef, 0xdf, 0xbf, 0x7f }; //4X4按输出端控制

void Key_Head() {
    static unsigned int ReadData[4];
    unsigned int i;
    for (i = 0; i < 4; i++) {
        P4OUT = KeyOut[i] | 0x0f; //忽略低4位
        ReadData[i] = (P4IN | 0xf0) ^ 0xff;

      if(key_delay == 0){//按键消抖
          reading=1;//按下标志

        /*按键设置:
        * A:前移                      10
        * B:后移                      11
        * C:停止闹钟              12
        * D:12/24切换、闹钟on/off切换    13
        * E:确认(进入设置界面)                      14
        * F:返回                      15
        * 其他:数字输入        */
        KeyVal = (ReadData[i]&0x01)*0 + (ReadData[i]&0x02)/2 + (ReadData[i]&0x04)/2 + (ReadData[i]&0x08)/8*3 + i*4;
        if(ReadData[i]){
            switch(KeyVal){
            case 0:case 1:case 2:case 3:case 4:case 5:case 6:case 7:case 8:case 9:
                if(state == 2){//设置时间
                    switch(choice_row){
                    case 1:
                        if(KeyVal>2) KeyVal=2;
                        h=h%10+KeyVal*10;
                        break;
                    case 2:
                        if(h/10 == 2 && KeyVal > 3) KeyVal=3;
                        h=h/10*10+KeyVal;
                        break;
                    case 3:
                        if(KeyVal>5) KeyVal=5;
                        m=m%10+KeyVal*10;
                        break;
                    case 4:m=m/10*10+KeyVal;break;
                    case 5:
                        if(KeyVal>5) KeyVal=5;
                        s=s%10+KeyVal*10;
                        break;
                    case 6:s=s/10*10+KeyVal;break;
                    default:break;
                    }
                    //自增
                    choice_row++;
                    if(choice_row>row_max){
                        choice_row = 1;
                    }
                }else if(state == 3){//设置日期
                    switch(choice_row){
                    case 1:y=y%1000+KeyVal*1000;break;
                    case 2:y=y/1000*1000+KeyVal*100+y%100;break;
                    case 3:y=y/100*100+KeyVal*10+y%10;break;
                    case 4:y=y/10*10+KeyVal;break;
                    case 5:mon=mon%10+KeyVal*10;break;
                    case 6:mon=mon/10*10+KeyVal;break;
                    case 7:d=d%10+KeyVal*10;break;
                    case 8:d=d/10*10+KeyVal;break;
                    default:break;
                    }
                    //自增
                    choice_row++;
                    if(choice_row>row_max){
                        choice_row = 1;
                    }
                }else if(state/10 == 4){//设置闹钟时间
                    switch(choice_row){
                    case 1:
                        if(KeyVal>2) KeyVal=2;
                        alarms[state%10-1][0] = alarms[state%10-1][0]%10+KeyVal*10;
                        break;
                    case 2:
                        if(h/10 == 2 && KeyVal > 3) KeyVal=3;
                        alarms[state%10-1][0] = alarms[state%10-1][0]/10*10+KeyVal;
                        break;
                    case 3:
                        if(KeyVal>5) KeyVal=5;
                        alarms[state%10-1][1] = alarms[state%10-1][1]%10+KeyVal*10;
                        break;
                    case 4:alarms[state%10-1][1] = alarms[state%10-1][1]/10*10+KeyVal;break;
                    case 5:
                        if(KeyVal>5) KeyVal=5;
                        alarms[state%10-1][2] = alarms[state%10-1][2]%10+KeyVal*10;
                        break;
                    case 6:alarms[state%10-1][2] = alarms[state%10-1][2]/10*10+KeyVal;break;
                    default:break;
                    }
                    //自增
                    choice_row++;
                    if(choice_row>row_max){
                        choice_row = 1;
                    }
                }
                break;
            case 10://前移键,row循环减一
                    choice_row--;
                    if(choice_row<1){
                        choice_row = row_max;
                    }
                break;
            case 11://后移键,row循环加一
                choice_row++;
                if(choice_row>row_max){
                    choice_row = 1;
                }
                break;
            case 12://停止闹钟
                    if(alarm_on){
                        alarm_count = 0;//计数清零
                        alarm_on = 0;//关闭闪烁
                        P6OUT |= BIT0 + BIT1 + BIT2 + BIT3 + BIT4 + BIT5 + BIT6 + BIT7;//熄灯
                    }
                    break;
            case 13://12/24切换
                if(state == 0)
                    time_mode = !time_mode;
                else if(state == 4){
                    if(alarms[choice_row-1][3] == 0){
                        set_alarm_string[(choice_row-1)*5+1] = 'O';
                        set_alarm_string[(choice_row-1)*5+2] = 'N';
                        set_alarm_string[(choice_row-1)*5+3] = ' ';

                        alarms[choice_row-1][3] = 1;
                    }else{
                        set_alarm_string[(choice_row-1)*5+1] = 'O';
                        set_alarm_string[(choice_row-1)*5+2] = 'F';
                        set_alarm_string[(choice_row-1)*5+3] = 'F';

                        alarms[choice_row-1][3] = 0;
                    }
                    //重绘
                    int i;
                    for(i=0;i<14;i++){
                        OLED_ShowChar(8+8*i,3,set_alarm_string[i]);
                    }
                    for(i=14;i<24;i++){
                        OLED_ShowChar(8*(i-14),5,set_alarm_string[i]);
                    }
                }
                break;
            case 14://确认键
                        switch(state){
                        case 0://主界面
                            //进入设置界面
                            state = 1;
                            choice_row = 1;row_max = 3;
                            show_setting();
                            break;
                        case 1://设置界面
                                switch(choice_row){
                                case 1://进入时间设置
                                    state = 2;
                                    choice_row = 1;
                                    row_max = 6;
                                    time_set(h,m,s);
                                    break;
                                case 2://进入日期设置
                                    state = 3;
                                    choice_row = 1;
                                    row_max = 8;
                                    date_set(h,mon,s);
                                    break;
                                case 3://进入闹钟设置选择界面
                                    state = 4;
                                    choice_row = 1;
                                    row_max = 5;
                                    show_alarm_set();
                                    break;
                                default:break;
                                }
                            break;
                        case 2://时间设置界面
                            //清屏返回主界面
                            OLED_Clear();
                            state = 0;choice_row = 1;row_max = 1;
                            show_time(h,m,s);
                            show_date(y, mon, d);
                            break;
                        case 3://日期设置界面
                            //判断输入的年份是否合法,不合法则初始化修改日,切不返回主界面
                            if(!is_date_legal(y, mon, d)){
                                y = 2017;
                                mon = 12;
                                d = 31;
                            }else{
                                //清屏返回主界面
                                OLED_Clear();
                                state = 0;choice_row = 1;row_max = 1;
                                show_time(h,m,s);
                                show_date(y, mon, d);
                            }
                            break;
                        case 4://闹钟设置选择界面
                            state = 40 + choice_row;
                            choice_row = 1;
                            row_max = 6;
                            alarm_set(alarms, state);
                            break;
                        case 41:case 42:case 43:case 44: case 45:
                            //清屏返回主界面
                            OLED_Clear();
                            state = 0;choice_row = 1;row_max = 1;
                            show_time(h,m,s);
                            show_date(y, mon, d);
                            break;
                        default:break;
                        }
                    break;
            case 15://返回键
                        switch(state){
                        case 1:OLED_Clear();state = 0;show_time(h,m,s);show_date(y, mon, d);break;//返回主界面
                        case 2:/*state = 1;choice_row = 1;row_max = 3;show_setting();*/break;//不能返回设置界面
                        case 3:/*state = 1;choice_row = 1;row_max = 3;show_setting();*/break;//不能返回设置界面
                        case 4:state = 1;choice_row = 1;row_max = 3;show_setting();break;
                        case 41:case 42:case 43:case 44: case 45://返回闹钟设置选择界面
                            state = 4;
                            choice_row = 1;
                            row_max = 5;
                            show_alarm_set();
                            break;
                        default:break;
                        }
                    break;
            default:break;
            }
        }
      }
    }
}

//时间运行函数,需要全局变量h、m、s
void run_time(){
    s++;
    if(s==60){
        m++;s=0;
        if(m==60){
           h++;m=0;
           if(h==24)
               h=0;
        }
    }
}
//日期运行函数,需要全局变量y、mon、d
void run_date(){
    //run day
    d++;
    //run month
    if(d == 32){ d=1; mon++; }
    else if((mon==4||mon==6||mon==9||mon==11)&&d==31){ d=1; mon++; }
    else if((y%4==0&&y%100!=0)||(y%400==0)){
        if(d==30){ d=1; mon++; }
    }else{
        if(d==29){ d=1; mon++; }
    }
    //run year
    if(mon == 13){ mon = 1; y++;}
}
//判断输入的日期是否合法。  0表示不合法,1表示合法
int is_date_legal(int year, int month, int day){
    if(!(y&&mon&&d)) return 0;

    if(month>12) return 0;
    else if(day>31) return 0;
    else if((month==2||month==4||month==6||month==9||month==11)&&day==31) return 0;
    else if(month==2&&day==30) return 0;
    else if(month==2&&day==29){
        if((year%4==0&&year%100!=0)||(year%400==0)) return 1;
        else return 0;
    }else
        return 1;
}
 

交流QQ群:【技术斋】646258285
关注公众号【技术斋】,阅读更多内容。我会定期对博客内容进行整理,用简单的语言发布到公众号上,适合休闲阅读。

在这里插入图片描述

标签:case,10,KeyVal,数字钟,break,OLED,alarms,row,msp430
来源: https://blog.51cto.com/u_15262460/2883014