基于M5Stick-C-Plus实现电子沙漏并且显示倒计时
利用M5Stick-C-Plus来实现对LED灯板的控制从而实现电子沙漏的效果,并且在LCD屏幕上输出倒计时
标签
嵌入式系统
MPU
ESP32
2022暑假在家一起练
M5Stick-C-Plus
xiaoyu555
更新2022-09-02
华北电力大学
646

基于M5Stick-C-Plus实现电子沙漏并在LCD屏幕上显示倒计时

一.项目介绍:

   通过Arduino编程实现倒计时的功能,然后在LED灯板上呈现出沙漏的效果,并且在LCD屏幕上显示倒计时。用户可以水平放置M5STICK,然后倾斜M5STICK来进行数字和确认按键的选中。同时,计时完成后可以再次输入所需的倒计时时长。

二.设计思路:

该项目通过三个部分相互连接来实现对应的功能,这三个部分分别是:界面设计LED灯板控制按钮选定

1.界面设计:通过arduino M5Unified库的LCD类来对LCD屏幕进行操作,首先初始化,然后利用二维数组储存12个矩形的方位,并用变量指定这12个矩形

2.LED灯板控制:利用取模软件,获取LED灯板各个状态,然后用二维数组储存一个748列的数组数据,然后不断的遍历某一行的数据,每一行的第一个数据都是第一行的数据,第二个为第二行的,其他也一样,当不断地遍历某一行的数据,利用视觉暂留的原理,便可以令灯板停留在某一个状态,当改变状态时,只要选取数组的其他行的数据即可。

3.按钮选定:利用M5STICK自带的姿态传感器,使得当倾斜设备的时候,改变指定12个矩形的变量,而通过改变指定12个矩形的变量,将对应的矩形标红,因为有储存方位,所以很方便。

4.相互连接:首先改变按钮,按下按钮计算时间,当计算完时间按下C-confirm确认键后,开启定时器,此时在定时器里使得展示在LCD屏里的时间不断变化,同时让LED灯板的状态发生改变。当倒计时结束时,利用倒计时变量控制进入的代码,实现归零效果。

三:实现过程

1.程序流程图:

Fp_7Fh4XAgbcw4jAU8DkawifPzjt

2.硬件方面:

   1.74HC595

//储存LED灯板的各个状态的二维数组
char pic[74][8]={
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x80,0x00,0x20,0x00,0x08,0x00,0x02,0x01,
    0x80,0x00,0x20,0x00,0x00,0x00,0x00,0x03,
    0x80,0x00,0x20,0x00,0x08,0x00,0x01,0x03,
    0x00,0x40,0x00,0x10,0x00,0x04,0x01,0x07,
    0x80,0x00,0x20,0x00,0x08,0x00,0x03,0x07,
    0x00,0x40,0x00,0x10,0x00,0x05,0x03,0x07,
    0x80,0x00,0x20,0x00,0x08,0x01,0x03,0x0F,
    0x00,0x40,0x00,0x10,0x00,0x05,0x07,0x0F,
    0x80,0x00,0x20,0x00,0x08,0x03,0x07,0x0F,
    0x80,0x00,0x20,0x00,0x09,0x03,0x07,0x0F,
    0x00,0x40,0x00,0x10,0x09,0x03,0x07,0x1F,
    0x80,0x00,0x20,0x00,0x09,0x03,0x0F,0x1F,
    0x80,0x40,0x00,0x10,0x01,0x07,0x0F,0x1F,
    0x80,0x00,0x20,0x00,0x0B,0x07,0x0F,0x1F,
    0x00,0x40,0x00,0x11,0x03,0x07,0x0F,0x1F,
    0x80,0x00,0x20,0x01,0x0B,0x07,0x0F,0x3F,
    0x00,0x40,0x00,0x11,0x03,0x07,0x1F,0x3F,
    0x80,0x40,0x20,0x11,0x03,0x0F,0x1F,0x3F,
    0x00,0x40,0x00,0x11,0x07,0x0F,0x1F,0x3F,
    0x80,0x00,0x20,0x13,0x07,0x0F,0x1F,0x3F,
    0x80,0x00,0x21,0x13,0x07,0x0F,0x1F,0x3F,
    0x00,0x40,0x01,0x13,0x07,0x0F,0x1F,0x7F,
    0x80,0x00,0x21,0x03,0x07,0x0F,0x3F,0x7F,
    0x80,0x40,0x01,0x13,0x07,0x1F,0x3F,0x7F,
    0x00,0x40,0x01,0x13,0x0F,0x1F,0x3F,0x7F,
    0x80,0x00,0x21,0x17,0x0F,0x1F,0x3F,0x7F,
    0x00,0x40,0x03,0x17,0x0F,0x1F,0x3F,0x7F,
    0x80,0x01,0x23,0x07,0x0F,0x1F,0x3F,0x7F,
    0x00,0x41,0x03,0x17,0x0F,0x1F,0x3F,0xFF,
    0x80,0x01,0x23,0x07,0x0F,0x1F,0x7F,0xFF,
    0x00,0x41,0x03,0x17,0x0F,0x3F,0x7F,0xFF,
    0x80,0x01,0x23,0x17,0x1F,0x3F,0x7F,0xFF,
    0x00,0x41,0x03,0x1F,0x1F,0x3F,0x7F,0xFF,
    0x80,0x01,0x27,0x0F,0x1F,0x3F,0x7F,0xFF,
    0x80,0x43,0x27,0x0F,0x1F,0x3F,0x7F,0xFF,
    0x01,0x03,0x07,0x0F,0x1F,0x3F,0x7F,0xFF,

    0x01,0x03,0x07,0x0F,0x1F,0x3F,0x7F,0xFF,
    0x01,0x03,0x07,0x0F,0x1F,0x3F,0x7F,0x7F,
    0x01,0x03,0x07,0x0F,0x1F,0x3F,0x3F,0x7F,
    0x01,0x03,0x07,0x0F,0x1F,0x1F,0x3F,0x7F,
    0x01,0x03,0x07,0x0F,0x0F,0x1F,0x3F,0x7F,
    0x01,0x03,0x07,0x07,0x0F,0x1F,0x3F,0x7F,
    0x01,0x03,0x03,0x07,0x0F,0x1F,0x3F,0x7F,
    0x01,0x01,0x03,0x07,0x0F,0x1F,0x3F,0x7F,
    0x00,0x01,0x03,0x07,0x0F,0x1F,0x3F,0x7F,
    0x00,0x01,0x03,0x07,0x0F,0x1F,0x3F,0x3F,
    0x00,0x01,0x03,0x07,0x0F,0x1F,0x1F,0x3F,
    0x00,0x01,0x03,0x07,0x0F,0x0F,0x1F,0x3F,
    0x00,0x01,0x03,0x07,0x07,0x0F,0x1F,0x3F,
    0x00,0x01,0x03,0x03,0x07,0x0F,0x1F,0x3F,
    0x00,0x01,0x01,0x03,0x07,0x0F,0x1F,0x3F,
    0x00,0x00,0x01,0x03,0x07,0x0F,0x1F,0x3F,
    0x00,0x00,0x01,0x03,0x07,0x0F,0x1F,0x1F,
    0x00,0x00,0x01,0x03,0x07,0x0F,0x0F,0x1F,
    0x00,0x00,0x01,0x03,0x07,0x07,0x0F,0x1F,
    0x00,0x00,0x01,0x03,0x03,0x07,0x0F,0x1F,
    0x00,0x00,0x01,0x01,0x03,0x07,0x0F,0x1F,
    0x00,0x00,0x00,0x01,0x03,0x07,0x0F,0x1F,
    0x00,0x00,0x00,0x01,0x03,0x07,0x0F,0x0F,
    0x00,0x00,0x00,0x01,0x03,0x07,0x07,0x0F,
    0x00,0x00,0x00,0x01,0x03,0x03,0x07,0x0F,
    0x00,0x00,0x00,0x01,0x01,0x03,0x07,0x0F,
    0x00,0x00,0x00,0x00,0x01,0x03,0x07,0x0F,
    0x00,0x00,0x00,0x00,0x01,0x03,0x07,0x07,
    0x00,0x00,0x00,0x00,0x01,0x03,0x03,0x07,
    0x00,0x00,0x00,0x00,0x01,0x01,0x03,0x07,
    0x00,0x00,0x00,0x00,0x00,0x01,0x03,0x07,
    0x00,0x00,0x00,0x00,0x00,0x01,0x03,0x03,
    0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x03,
    0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x03,
    0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
};
//下面这个是根据重力感应,颠倒了灯板之后的数组,也是利用取模软件一个一个画的
char pic2[74][8]={
0xFF,0xFE,0xFC,0xF8,0xF0,0xE0,0xC0,0x80,//36
0xFF,0xFE,0xFC,0xF8,0xF0,0xE0,0xC0,0x00,//35
0xFF,0xFE,0xFC,0xF8,0xF0,0xE0,0x80,0x00,//34
0xFF,0xFE,0xFC,0xF8,0xF0,0xC0,0x80,0x00,//33
0xFF,0xFE,0xFC,0xF8,0xE0,0xC0,0x80,0x00,//32
0xFF,0xFE,0xFC,0xF0,0xE0,0xC0,0x80,0x00,//31
0xFF,0xFE,0xF8,0xF0,0xE0,0xC0,0x80,0x00,//30
0xFF,0xFC,0xF8,0xF0,0xE0,0xC0,0x80,0x00,//29
0xFE,0xFC,0xF8,0xF0,0xE0,0xC0,0x80,0x00,//28
0xFE,0xFC,0xF8,0xF0,0xE0,0xC0,0x00,0x00,//27
0xFE,0xFC,0xF8,0xF0,0xE0,0x80,0x00,0x00,//26
0xFE,0xFC,0xF8,0xF0,0xC0,0x80,0x00,0x00,//25
0xFE,0xFC,0xF8,0xE0,0xC0,0x80,0x00,0x00,//24    
0xFE,0xFC,0xF0,0xE0,0xC0,0x80,0x00,0x00,//23
0xFE,0xF8,0xF0,0xE0,0xC0,0x80,0x00,0x00,//22
0xFC,0xF8,0xF0,0xE0,0xC0,0x80,0x00,0x00,//21
0xFC,0xF8,0xF0,0xE0,0xC0,0x00,0x00,0x00,//20
0xFC,0xF8,0xF0,0xE0,0x80,0x00,0x00,0x00,
0xFC,0xF8,0xF0,0xC0,0x80,0x00,0x00,0x00,
0xFC,0xF8,0xE0,0xC0,0x80,0x00,0x00,0x00,
0xFC,0xF0,0xE0,0xC0,0x80,0x00,0x00,0x00,
0xF8,0xF0,0xE0,0xC0,0x80,0x00,0x00,0x00,
0xF8,0xF0,0xE0,0xC0,0x00,0x00,0x00,0x00,
0xF8,0xF0,0xE0,0x80,0x00,0x00,0x00,0x00,
0xF8,0xF0,0xC0,0x80,0x00,0x00,0x00,0x00,
0xF8,0xE0,0xC0,0x80,0x00,0x00,0x00,0x00,
0xF0,0xE0,0xC0,0x80,0x00,0x00,0x00,0x00,
0xF0,0xE0,0xC0,0x00,0x00,0x00,0x00,0x00,
0xF0,0xE0,0x80,0x00,0x00,0x00,0x00,0x00,
0xF0,0xC0,0x80,0x00,0x00,0x00,0x00,0x00,
0xE0,0xC0,0x80,0x00,0x00,0x00,0x00,0x00,
0xE0,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,//5
0xE0,0x80,0x00,0x00,0x00,0x00,0x00,0x00,//4
0xC0,0x80,0x00,0x00,0x00,0x00,0x00,0x00,//3
0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,//2
0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,//1
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,//0

0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,//0
0x80,0x40,0x00,0x10,0x00,0x04,0x00,0x01,//1
0x80,0xC0,0x20,0x00,0x08,0x00,0x02,0x01,//2
0xC0,0x80,0x20,0x00,0x08,0x04,0x00,0x01,//3
0xC0,0x80,0xA0,0x10,0x00,0x04,0x00,0x01,
0xC0,0xC0,0xA0,0x00,0x08,0x00,0x02,0x00,
0xE0,0xC0,0xA0,0x00,0x08,0x00,0x02,0x01,
0xE0,0xC0,0xA0,0x80,0x08,0x04,0x00,0x01,
0xE0,0xC0,0xE0,0x90,0x00,0x04,0x00,0x01,
0xE0,0xE0,0xE0,0x90,0x08,0x00,0x02,0x01,
0xF0,0xE0,0xE0,0x80,0x08,0x00,0x02,0x01,
0xF0,0xE0,0xC0,0x90,0x80,0x04,0x00,0x01,
0xF0,0xE0,0xC0,0xD0,0x88,0x00,0x02,0x01,
0xF0,0xE0,0xE0,0xD0,0x88,0x00,0x02,0x00,
0xF0,0xF0,0xE0,0xD0,0x80,0x04,0x00,0x01,
0xF8,0xF0,0xE0,0xD0,0x88,0x04,0x00,0x00,
0xF8,0xF0,0xE0,0xD0,0x80,0x84,0x00,0x01,
0xF8,0xF0,0xE0,0xD0,0xC8,0x80,0x02,0x00,
0xF8,0xF0,0xE0,0xF0,0xC0,0x84,0x00,0x01,
0xF8,0xF0,0xF0,0xE0,0xC8,0x80,0x02,0x00,
0xF8,0xF8,0xF0,0xF0,0xC0,0x84,0x00,0x01,
0xFC,0xF8,0xF0,0xE0,0xC8,0x80,0x02,0x00,
0xFC,0xF8,0xF0,0xF0,0xC0,0x84,0x80,0x01,
0xFC,0xF8,0xF0,0xE0,0xC8,0xC0,0x82,0x00,
0xFC,0xF8,0xF0,0xF0,0xE0,0xC4,0x80,0x01,
0xFC,0xF8,0xF0,0xF0,0xE8,0xC0,0x82,0x00,
0xFC,0xF8,0xF8,0xF0,0xE8,0xC4,0x80,0x01,
0xFC,0xFC,0xF8,0xF0,0xE8,0xC0,0x82,0x00,
0xFE,0xFC,0xF8,0xF0,0xE8,0xC4,0x80,0x01,
0xFE,0xFC,0xF8,0xF0,0xE8,0xC0,0x82,0x81,
0xFE,0xFC,0xF8,0xF0,0xE8,0xC4,0xC2,0x81,
0xFE,0xFC,0xF8,0xF0,0xE8,0xC0,0xC2,0x81,
0xFE,0xFC,0xF8,0xF0,0xE0,0xE4,0xC0,0x81,
0xFE,0xFC,0xF8,0xF0,0xF8,0xE0,0xC2,0x80,
0xFE,0xFC,0xF8,0xF8,0xF8,0xE4,0xC0,0x81,
0xFE,0xFC,0xFC,0xF8,0xF8,0xE0,0xC2,0x81,
0xFF,0xFE,0xFC,0xF8,0xF0,0xE0,0xC0,0x80,
};

void MatrixLedShowColumn(int Row,char ColumnData1,char ColumnData2)
{
       //给上升沿锁存和上升沿移位置为低电平
    digitalWrite(SRCLK_Pin,LOW);
    digitalWrite(RCLK_Pin,LOW);

    //利用shiftout函数填充74HC595
    //先填充行
    shiftOut(Ser_Pin,SRCLK_Pin,MSBFIRST,B00000001<<(Row-1));//并且高位先行
    shiftOut(Ser_Pin,SRCLK_Pin,LSBFIRST,ColumnData1);
    shiftOut(Ser_Pin,SRCLK_Pin,MSBFIRST,B00000001<<(Row-1));//并且高位先行
    shiftOut(Ser_Pin,SRCLK_Pin,LSBFIRST,ColumnData2);
    //
    digitalWrite(RCLK_Pin,HIGH);
    digitalWrite(RCLK_Pin,LOW);
    

}


//LedNum-控制LED灯板的状态
//不断地循环一个状态中的每一行灯的数据,利用视觉暂留,便可以使LED灯板保持某种状态。
//LedNum-控制LED灯板的状态,根据flag决定漏斗的方向,到时候根据传感改变flag,就可改变漏斗方向
void PicMatrixLedConfig(int LedNum,uint8_t flag)
{
    int i=LedNum;
    if(flag==1) //正的
    {
        for(int j=0;j<8;j++)
        {
            MatrixLedShowColumn(j+1,pic[i][j],pic[i+37][j]);

        } 
    }

    //颠倒时的状态的接口
    else
    {   
        for(int j=0;j<8;j++)
        {
            MatrixLedShowColumn(j+1,pic2[i][j],pic2[i+37][j]);//注意这里是pic2

        }
    }
}
  • 这次灯板采用的是74HC595,它是一个将8位串行输入,并行输出的移位缓存器,换句话说,就是Ser_Pin每个时钟传入一个数据,然后将SRCLK_Pin置高电平,Ser_Pin上一个传入的数据就会往下移,当RCLK引脚置高电平时,就会将储存的8位数据一并传输出去,而如果不断传入,而不将其输出,多余的数据就会存放在QH中。而这也使得该灯板先传入的数据会先到后一个中,然后再填满第一个,这样也可以将多个灯板进行串联,同时,第一个传入的8位数据会到最后一个74HC595芯片中,而通过三极管放大器的压降等东西,当行和列同时给1时会亮,不同时给1时就不亮,我们可以利用这个,先用取模软件取好数据,然后将其传入一个二维数组中,每一行为一个状态,每一行中的每一个数据为每一行灯的数据,这样将会很方便的去控制LED灯板的状态。Arduino有一个函数为shiftout,它可以将一个字节的数据通过移位输出的方式逐位输出。数据可以从最高位(最左位)或从最低位(最右位)输出。刚好可以用来控制74HC595芯片。其他更具体的可以看硬禾学堂2022暑假在家一起练项目中的灯板的电路图,可以更好的理解。

3.代码:

   1.setup()

void setup() {
    //开启M5设备
    M5.begin();
    M5.Imu.begin();  //IMU为姿态传感器
    InterfaceDesign();//初始化LCD屏的界面
    MatrixLed_Init();//初始化74HC595芯片的配置
}
  • 首先在main.cppsetup()函数里初始化M5设备,并且初始化M5的姿态传感器,方便接下来实现陀螺仪参数的获取。同时利用Interface.cpp里的InterfaceDesign()函数来初始化LCD屏幕的界面。

   2.InterfaceDesign()

//矩形方位
uint8_t RectOrder[12][2]={
    0,85,//1
    50,85,//2
    100,85,//3
    0,125,//4
    50,125,//5
    100,125,//6
    0,165,//7
    50,165,//8
    100,165,//9
    0,205,//Clear
    50,205,//0
    100,205//Delete
    };

//界面设计
void InterfaceDesign(void)
{
    M5.Lcd.fillScreen(BLACK);//设定背景色为黑色
    M5.Lcd.setTextColor(WHITE);
    M5.Lcd.setTextSize(2);
    for(int i=0;i<12;i++)
    {

        M5.Lcd.drawRoundRect(RectOrder[i][0],RectOrder[i][1],35,35,5,WHITE);
        if(i<9)
        {
            M5.Lcd.setCursor(RectOrder[i][0]+15,RectOrder[i][1]+15);
            M5.Lcd.println(i+1);
        }
    }
    
    M5.Lcd.setCursor(15,220);
    M5.Lcd.print("C");
    M5.Lcd.setCursor(65,220);
    M5.Lcd.println(0);
    M5.Lcd.setCursor(115,220);
    M5.Lcd.printf("D");
}
  • 里面通过数组储存了12个矩形的方位,然后再在InterfaceDesign()这个函数里面利用储存好的方位在LCD屏幕上绘画12个35*35,弧度为5°的圆角矩形。

   3.loop()

void loop() {

        // put your main code here, to run repeatedly:
        //获取姿态传感器数值,并进行判断
        M5.Imu.getAccel(&accX1,&accY1,&accZ1);
        if(countdownflag==0)
        {
        JudgeDirection(accX1,accY1);
        //通过判断改变按钮数值
        Selectbutton();
        CalculateTime();
        }
        //当中间大按钮按下,进行选中
        if((digitalRead(37) == LOW))
        {
            //如果等于Confirm键且倒计时没有开始
            if(buttonnum==10&&countdownflag==0)
            {
                countdown=TimeNum;
                if(TimeNum<36)
                {
                    LedNum=36-TimeNum;
                }
                delay(100);
                if(TimeFlag==0)
                {
                //使定时器开始工作,开始设定定时器
                timer = timerBegin(0, 80, true);

                // Attach onTimer function to our timer.
                timerAttachInterrupt(timer, &onTimer, true);

                // Set alarm to call onTimer function every second (value in microseconds).
                // Repeat the alarm (third parameter)
                timerAlarmWrite(timer, 500, true);//这个为1us为一次,1000000us=1s

                // Start an alarm
                timerAlarmEnable(timer);
                TimeFlag=1;
                }
                countdownflag=1;
            }
        }
        // If button is pressed
        if(countdown==0)
        {
            if(countdownflag==1)
            {
            delay(100);
            countdownflag=0;//
            ShowCountdown(0);
            PicMatrixLedConfig(36,flag);//最终为0之后让它变为最后一个状态
            ResetDevice();//并且对所有参数进行初始化
            }
            delay(100);
        }
        if(countdownflag==1)
        {
            if(ischange==1)//如果数值改变
            {
                M5.Lcd.fillRect(0,0,135,84,BLACK);
                ShowCountdown(countdown);
                ischange=0;
            }
            //如果使用的时候颠倒了之后
            if (accX1 > 0.8 && accX1 < 1&&flag==2){flag=1;countflag=1;}
            if (accX1 > -1.0 && accX1 < -0.8&&flag==1){flag=2;countflag=1;}
            if (countflag==1)
            {   

                LedNum=36-LedNum;
                if(TimeNum<36)
                {
                    countdown=36-countdown;
                }
                else
                {
                    countdown=TimeNum-countdown;
                }
                ShowCountdown(countdown);
                
                
                ischange=1;
                countflag=0;
            }
        }
    }

  • 该函数为main.cpp里的主循环,可以说是整个代码的最核心部分,接下来将会详细解释

      1.首先获取姿态传感器传来的数据,并且在倒计时没有开始-即没有按下confirm键时,不断的对矩形的选定进行改变,而我这里设定了变量countdownflag来对倒计时是否开始进行判断。

      2.判定选定的按钮和对按钮进行选定-Interface.cpp-JudgeDirectionSelectbutton

//方向的判定
/*  
    accelx增大时为向左
    accely增大时为向下摆动
*/

uint8_t JudgeDirection(float accx1,float accy1)
{
  
    if(accx1>0.40)
    {
        if(buttonnum>1)
        {
            buttonnum--;
            Ischange=1;
        }
        delay(500);
    }
    else if(accx1<-0.40)
    {
        if(buttonnum<12)
        {
            buttonnum++;
            Ischange=1;
        }
        delay(500);
    }
    else if(accy1>0.40)
    {
        if(buttonnum<=9)
        {
            buttonnum+=3;
            Ischange=1;
        }
        delay(500);
    }
    else if(accy1<-0.40)
    {   
        if(buttonnum>3)
       {
            buttonnum-=3;
            Ischange=1;
       }
        delay(500);
    }
    return 1;
        
}

//对按钮进行选择,选中的标红
void Selectbutton()
{
    if(Ischange==1)//如果方位进行改变,再对方块进行刷新,对被选定的方块标红
    {
        for(int i=0;i<12;i++)
        {
            M5.Lcd.drawRoundRect(RectOrder[i][0],RectOrder[i][1],35,35,5,WHITE);
        }
        M5.Lcd.drawRoundRect(RectOrder[buttonnum-1][0],RectOrder[buttonnum-1][1],35,35,5,RED);
        //更新变量
        Ischange=0;
    }
}

将主循环里获取到的姿态传感器的数据传入JudgeDirection中,并且当改变时,改变buttonnum的值,该值从1-12,对应着十二个矩形,当改变了之后,通过Selectbutton函数对所有的矩形重新变白,再对选定的矩形进行标红,实现刷新的效果。

       3.当倒计时没有开始且当设备中间大按钮按下时,将对对应的矩形进行操作和判断,当1-9的矩形被选中时,通过buttonflag判断为第几位数来执行对应的数学运算。这个运算在下面的Calculation函数中,当没有开始倒计时时,会不断的进行这个运算,如果按钮按下,会进行运算并且将运算结果显示到LCD屏幕上。

      4.当利用姿态传感器选中了C按钮也就是Confirm键后,令buttonflag=1,利用这个变量和if语句,就可以实现倒计时开始后的功能。

      5.当倒计时归零后,即countdown==0时,直接让灯板显示最后一个状态和让LCD屏幕显示0,这样就能够使倒计时和沙漏同步。

      6.下面这段代码是用来实现根据重力改变灯板方向的,最终拟合出一个真正的沙漏的效果。

首先,先判断方位,如果是正的,flag=1,反的flag=2,然后countflag是用来判断方向是否改变的。

      当方向改变了,此时若总计时大于等于36,则此时下面的灯数等于36-LedNum,并且下面灯板代表的时间应该为设定的时间减去倒计时的时间。此时同时改变LedNum和countdown(倒计时剩余时间)的状态,就能改变灯板和屏幕的状态。

      如果总计时小于36,由于小于36秒的时候,我设定每一个灯代表一秒,所以此时下面灯板的灯数应该为36-上面的灯数,为了满足实际的效果,所以倒计时的时间应该等于实际的灯数,所以令countdown=36-countdown。

 //如果使用的时候颠倒了之后
            if (accX1 > 0.8 && accX1 < 1&&flag==2){flag=1;countflag=1;}
            if (accX1 > -1.0 && accX1 < -0.8&&flag==1){flag=2;countflag=1;}
            if (countflag==1)
            {   

                LedNum=36-LedNum;
                if(TimeNum<36)
                {
                    countdown=36-countdown;
                }
                else
                {
                    countdown=TimeNum-countdown;
                }
                ShowCountdown(countdown);
                
                
                ischange=1;
                countflag=0;
            }
        }

   4.onTimer()

//定时器中断函数
void ARDUINO_ISR_ATTR onTimer(){

    //可以在这里写中断内容
    if(countdownflag==1)//countdownflag==1代表开始计时,因为每500us进一次定时器,当count2=1000000/500时为1s
    {
        count1++;
        if(TimeNum>=36)
        {
            if(count1==(int)(TimeNum/36*1000000/500))//最多用了36个灯,当count1加到一定数,LedNum++
            {
                LedNum++;
                LedNum%=37;
                count1=0;
            }
        }
        else //当计数小于36s,只亮计数值个数的灯数,并且每个灯代表1s
        {
            
            if(count1==1000000/500)
            {
                LedNum++;
                LedNum%=37;
                count1=0;
            }
        }
        count2++;
        if(count2==1000000/500)
        {
            countdown--;
            ischange=1;
            count2%=1000000/500;
        }
        PicMatrixLedConfig(LedNum,flag);//灯数不断改变,传入该函数
    }

}

      1.当按下C键后,倒计时开始,此时会开启定时器,设定变量判断定时器是不是第一次开启,如果是第一次开启,即会开启定时器,如果不是第一次开启,就不再开启,否则重复开启定时器将会使设备卡死,造成硬件重启。

      2.当开启了定时器之后,我设定每500us即会进入一次onTimer函数中,此时在onTimer函数中编写关于倒计时的代码即可

      3.当进入倒计时状态后,设定变量count1和count2,并使其不断地增加,每500us增加一次,此时只要在里面设定count1增加到某个值就改变LED灯板的状态即可,而LED灯板的状态如何改变,在设计思路里有说。然后设定count2即是改变LCD屏幕上的倒计时,设定每一秒改变刷新LCD屏幕上的倒计时。

      4.count1的上限值与时间的取值有关,LED灯板我只使用了36个灯,因为感觉这样更加的符合沙漏的形状,所以当倒计时取值小于36时十分简单,只要让led灯板亮对应的灯数,然后每一秒减少一个灯即可,所以这时count1的值为100000us/500us。而当倒计时取值大于36时,需要计算每个灯所代表的时间,然后每过这个时间变换一个状态,使这个灯灭即可。而这个计算可以看代码。

   5.flag在loop()中进行更改,可以控制灯板的状态

四.不足

1.没有实现在倒计时的时候暂停的功能,因为定时器不稳定,不断的暂停和开启会导致硬件卡死

2.灯板显示有问题,最后一排特别亮,第一排有微弱的亮

3.颠倒的选中不是很灵敏,需要左右摆

五.改进

更换其他的定时器的函数库即可。

六.效果展示

1.首先利用姿态传感器,左右上下摆动M5Stick,便可对按钮实现选定,当按下中间大按钮时就会进行加法运算,将数值显示到屏幕上,按错可以按D键进行清除

Fst6FS6oTDo_eVjE5RYnjGzzjBEc

2.当选中了C键之后可以开始倒计时,此时灯板会根据时间显示对应的灯

(1)当时间小于36s时,上方的灯板只会亮倒计时的数字的灯数,并且每一秒减少一个灯,当减少完成后

Fp8Ww7LeTdPdTJryT_mGbwsRtY_sFj3LAxIZcUUkkJgjmIukWB09wz4N

(2)当时间大于36s时,上方的灯板会亮36个灯,即占用了灯板的一半,并且每倒计时/36秒减少一个灯。

FhNs4HfC2FaIwJqVlyf5dn0lGJG_FtGeNzf23jCa51Vwoa0NYXCT43Ww

3.当将M5Stick右摆后,灯板会从左灯板-右灯板变成右灯板到左灯板(也可以改变姿态传感器的参数变成上下颠倒),为了拟合真实的沙漏,当摆过来后,上下两个灯板的灯数不变,倒计时的数目也会发生改变-即每个灯代表的秒数不变。

如当设定为10秒时,走了4s后,上方灯板应该还剩6个灯,下方灯板有30个灯,那么倒过来之后倒计时就是30.

FlqZN_u69mxd1LthydbjNGzKboDkFvEJjtMpUn38_hibNEqaAPXqeC4T

 

      

附件下载
M5Stick.zip
百度网盘:链接:https://pan.baidu.com/s/1s50Y4_jsTRAycqT-IBU42w?pwd=k1qh 提取码:k1qh
团队介绍
一名学生
团队成员
xiaoyu555
评论
0 / 100
查看更多
目录
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2023 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号