Arduino UNO是基于ATmega328P的Arduino开发板。它有14个数字输入/输出引脚(其中6个可用于PWM输出)、6个模拟输入引脚,一个16 MHz的晶体振荡器,一个USB接口,一个DC接口,一个ICSP接口,一个复位按钮。Arduino一词意味着该款开发板可以使用Arduino官方和第三方库文件,简化开发流程,可以让我们在不了解单片机底层原理下对其进行充分开发,因此Arduino获得了众多初级开发者的喜爱。
一、总体设计
本次项目采用模块化编程的设计方式,因为在实际设计中,我们利用状态者模式隔离不同模式下的功能操作,如下:
程序整体设计思路来自于状态者模式,对于初学者并不友好,使用C++语言设计,参考代码如下:
#include "State.h"//状态1,继承重写doThing
void StateCase1::doThing(CurState *s){//静态变量作为开关,防止重复创建对象static int count = 0;//进行开关判断,创建过就不再创建if(count == 0){s->getOled()->setLogo(new ServoP(s->getOled()));count = 1; }//按下1按键,滑动到2状态if(m_command == 1){delete s->getCase(); //回收状态管理者的此时指针s->setCase(new StateCase2); //new 一个新的状态,设置进去这里为2count = 0; //开关打开,方便下次跳转到这时创建对象}//按下1按键,滑动到3状态else if(m_command == 2){delete s->getCase(); //回收状态管理者的此时BaseCase指针s->setCase(new StateCase3); //new 一个新的状态,设置进去这里为3count = 0; //开关打开,方便下次跳转到这时创建对象}//5键为确定,跳转到状态5else if(m_command == 5){delete s->getCase(); //回收状态管理者的此时BaseCase指针s->setCase(new StateCase4); //new 一个新的状态,设置进去这里为3count = 0; //开关打开,方便下次跳转到这时创建对象 }
}void StateCase2::doThing(CurState *s){//静态变量作为开关,放置重复创建对象static int count = 0;//进行开关判断,创建过就不再创建if(count == 0){s->getOled()->setLogo(new MusicP(s->getOled()));count = 1;}//按下1按键,滑动到1状态else if(m_command == 2){delete s->getCase(); //回收状态管理者的此时BaseCase指针s->setCase(new StateCase1); //new 一个新的状态,设置进去这里为1count = 0; //开关打开,方便下次跳转到这时创建对象 } else if(m_command == 5){delete s->getCase(); //回收状态管理者的此时BaseCase指针s->setCase(new StateCase5); //new 一个新的状态,设置进去这里为5count = 0; //开关打开,方便下次跳转到这时创建对象 }
}//以下状态剩余类推
void StateCase3::doThing(CurState *s){static int count = 0;if(count == 0){s->getOled()->setLogo(new ClockP(s->getOled()));count = 1;}if(m_command == 1){delete s->getCase();s->setCase(new StateCase1);count = 0;}else if(m_command == 5){delete s->getCase();s->setCase(new StateCase7);count = 0;}
}void StateCase4::doThing(CurState *s){static int count = 0;static int angle=0;if(count == 0){s->getOled()->setLogo(new Servo_fun(s->getOled()));count = 1;}s->getOled()->printW(48, 32, 2, angle);if(m_command == 3){delete s->getCase();s->setCase(new StateCase1);angle = 0;s->getServo()->write(0);count = 0;}else if(m_command == 1){angle += 30;if(angle>180){angle = 180;}s->getServo()->write(angle);}else if(m_command == 2){angle -= 30;if(angle < 0){angle = 0;}s->getServo()->write(angle);}
}void StateCase5::doThing(CurState *s){static int count = 0;static int pos = 16;s->getOled()->printW(100, pos, 2, "*");if(count == 0){s->getOled()->setLogo(new Playlist(s->getOled()));count = 1;} if(m_command == 3){delete s->getCase();s->setCase(new StateCase2);count = 0;}else if(m_command == 1){pos -= 16;if(pos<16){pos = 16;}}else if(m_command == 2){pos += 16;if(pos>32){pos = 32;}}else if(m_command == 5 && pos==16){delete s->getCase();s->setCase(new StateCase6);count = 0; }
}void StateCase6::doThing(CurState *s){static int count = 0;if(count == 0){s->getOled()->setLogo(new Playing(s->getOled()));count = 1;}s->getBuzzer()->on();if(m_command == 3){delete s->getCase();s->setCase(new StateCase5);s->getBuzzer()->off();count = 0; }
}void StateCase7::doThing(CurState *s){static int count = 0;static int Time_ms = 0, Time_s = 0;static int time_flag = 0;if(time_flag ==1){delay(10);Time_ms++;if(Time_ms >= 100){Time_ms = 0;Time_s++;}}s->getOled()->printW(48, 32, 2, Time_s);s->getOled()->printW(80, 32, 2, Time_ms);if(count == 0){s->getOled()->setLogo(new ClockCount(s->getOled()));count = 1;} if(m_command == 3){delete s->getCase();s->setCase(new StateCase3);Time_ms = 0;Time_s = 0;time_flag = 0;count = 0; }if(m_command == 5){time_flag = 1;}if(m_command == 2){time_flag = 0;}
}
#pragma once
#include "UiLogo.h"
#include "MyOledH.h"
#include <Servo.h>
#include "Buzzer.h"
#include <Arduino.h>//提前声明,方便下面BaseCase类的声明
class CurState;//定义基类并设置虚函数,由于setCommand都一样,这个就不重写
class BaseCase{
public://获取指令,来源于状态管理者的set方法,而状态管理者来指令源于按键virtual void setCommand(int command){m_command = command;}virtual void doThing(CurState *s) = 0;
protected:int m_command;
};//状态管理者
class CurState{
public://各种set和get方法void setOled(MyOled *oled){m_oled = oled;}void setCase(BaseCase *Case){m_case = Case;}MyOled *getOled(void){return m_oled;}BaseCase *getCase(void){return m_case;}void setCommand(int command){m_case->setCommand(command);}void setServo(Servo *servo){m_servo = servo;}Servo *getServo(void){return m_servo;}void setBuzzer(Buzzer *buzzer){m_buzzer = buzzer;}Buzzer *getBuzzer(void){return m_buzzer;}//doThing调用m_case的doThing并将自身指针传入void doThing(void){m_case->doThing(this);}
private://各种指针,方便m_case通过get方法获取进行操作。BaseCase *m_case;MyOled *m_oled;Servo *m_servo;Buzzer *m_buzzer;
};//以下是7种状态的声明
class StateCase1: public BaseCase{
public:void doThing(CurState *s);
};class StateCase2: public BaseCase{
public:void doThing(CurState *s);
};class StateCase3: public BaseCase{
public:void doThing(CurState *s);
};class StateCase4: public BaseCase{
public:void doThing(CurState *s);
};class StateCase5: public BaseCase{
public:void doThing(CurState *s);
};class StateCase6: public BaseCase{
public:void doThing(CurState *s);
};class StateCase7: public BaseCase{
public:void doThing(CurState *s);
};
本次设计所使用的图形化界面,通过取字模的方式得到,参考如下:
#pragma once
#include <Arduino.h>
#include "MyOledH.h"//各个图标获取到的16进制字模
static const unsigned char PROGMEM music_pic[] =
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0xFF,0xFF,0xFC,0x00,0x07,0xFF,0xFF,0xFC,0x00,0x04,0x00,0x00,0x04,
0x00,0x04,0x00,0x00,0x04,0x00,0x04,0x00,0x00,0x04,0x00,0x04,0x00,0x00,0x04,0x00,0x04,0x00,0x00,0x04,0x00,0x04,0x00,0x00,
0x04,0x00,0x04,0x00,0x00,0x04,0x00,0x04,0x00,0x00,0x04,0x00,0x04,0x00,0x00,0x04,0x00,0x04,0x00,0x00,0x04,0x00,0x04,0x00,
0x00,0x04,0x00,0x04,0x00,0x00,0x04,0x00,0x04,0x00,0x00,0x04,0x00,0x04,0x00,0x00,0x04,0x00,0x04,0x00,0x00,0x04,0x00,0x04,
0x00,0x00,0x04,0x00,0x04,0x00,0x00,0x04,0x00,0x04,0x00,0x00,0x04,0x00,0x04,0x00,0x00,0x04,0x00,0x04,0x00,0x00,0x04,0x00,
0x04,0x00,0x00,0x04,0x00,0x04,0x00,0x00,0x04,0x00,0x04,0x00,0x00,0x04,0x00,0x04,0x00,0x00,0x04,0x00,0x04,0x00,0x00,0x04,
0x00,0x7C,0x00,0x00,0x04,0x00,0xFC,0x00,0x00,0x7C,0x00,0xFC,0x00,0x00,0xFC,0x00,0xF8,0x00,0x00,0xFC,0x00,0x70,0x00,0x00,
0xF8,0x00,0x00,0x00,0x00,0x70,0x00,0x00,0x00,0x00,0x00,0x00};/*"未命名文件",0*/static const unsigned char PROGMEM servo_pic[] =
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7F,0xFF,0xFE,0x00,0x00,0x00,
0x00,0x00,0x40,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x46,0x00,0x62,0x00,0x00,0x00,0x00,0x00,0x49,0x00,0x92,0x00,0x00,0x00,
0x00,0x00,0x49,0x00,0x92,0x00,0x00,0x00,0x00,0x00,0x46,0x00,0x62,0x00,0x00,0x00,0x00,0x00,0x40,0x7C,0x02,0x00,0x00,0x00,
0x00,0x00,0x46,0xFE,0x02,0x00,0x00,0x00,0x00,0x00,0x43,0x81,0x82,0x00,0x00,0x00,0x00,0x00,0x42,0x3F,0xFF,0xFF,0xF8,0x00,
0x00,0x00,0x44,0x7F,0xFF,0xFF,0xFC,0x00,0x00,0x00,0x48,0xFF,0xFF,0xFF,0xFE,0x00,0x00,0x00,0x49,0xFF,0xFF,0xFF,0xFF,0x00,
0x00,0x00,0x49,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x48,0xFF,0xFF,0xFF,0xFE,0x00,0x00,0x00,0x44,0x7F,0xFF,0xFF,0xFC,0x00,
0x00,0x00,0x42,0x3F,0xFF,0xFF,0xF8,0x00,0x00,0x00,0x41,0x01,0x02,0x00,0x00,0x00,0x00,0x00,0x40,0xFE,0x02,0x00,0x00,0x00,
0x00,0x00,0x40,0x7C,0x02,0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x46,0x00,0x62,0x00,0x00,0x00,
0x00,0x00,0x49,0x00,0x92,0x00,0x00,0x00,0x00,0x00,0x49,0x00,0x92,0x00,0x00,0x00,0x00,0x00,0x46,0x00,0x62,0x00,0x00,0x00,
0x00,0x00,0x40,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x5F,0xFF,0xFA,0x00,0x00,0x00,0x00,0x00,0x50,0x00,0x0A,0x00,0x00,0x00,
0x00,0x00,0x50,0x00,0x0A,0x00,0x00,0x00,0x00,0x00,0x50,0x00,0x0A,0x00,0x00,0x00,0x00,0x00,0x50,0x00,0x0A,0x00,0x00,0x00,
0x00,0x00,0x50,0x00,0x0A,0x00,0x00,0x00,0x00,0x00,0x50,0x00,0x0A,0x00,0x00,0x00,0x00,0x00,0x50,0x00,0x0A,0x00,0x00,0x00,
0x00,0x00,0x50,0x00,0x0A,0x00,0x00,0x00,0x00,0x00,0x50,0x00,0x0A,0x00,0x00,0x00,0x00,0x00,0x50,0x00,0x0A,0x00,0x00,0x00,
0x00,0x00,0x5F,0xFF,0xFA,0x00,0x00,0x00,0x00,0x00,0x40,0x24,0x02,0x00,0x00,0x00,0x00,0x00,0x40,0x24,0x02,0x00,0x00,0x00,
0x00,0x00,0x40,0x24,0x02,0x00,0x00,0x00,0x00,0x00,0x40,0x24,0x02,0x00,0x00,0x00,0x00,0x00,0x40,0x3C,0x02,0x00,0x00,0x00,
0x00,0x00,0x46,0x00,0x62,0x00,0x00,0x00,0x00,0x00,0x49,0x00,0x92,0x00,0x00,0x00,0x00,0x00,0x49,0x00,0x92,0x00,0x00,0x00,
0x00,0x00,0x46,0x00,0x62,0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x7F,0xFF,0xFE,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};/*"未命名文件",0*/static const unsigned char PROGMEM clock_pic[] =
{0x00,0x3F,0xFF,0xC0,0x00,0x00,0x40,0x00,0x20,0x00,0x00,0x80,0x00,0x10,0x00,0x01,0x00,0x00,0x08,0x00,0x02,0x00,0x00,0x04,
0x00,0x04,0x00,0x00,0x02,0x00,0x08,0x00,0x00,0x01,0x00,0x10,0x00,0x00,0x00,0x80,0x20,0x00,0x00,0x00,0x40,0x40,0x00,0x00,
0x00,0x20,0x80,0x00,0x00,0x00,0x10,0x80,0x00,0x00,0x00,0x10,0x80,0x00,0x00,0x00,0x10,0x80,0x00,0x00,0x00,0x10,0x80,0x00,
0x00,0x00,0x10,0x80,0x00,0x00,0x00,0x10,0x80,0x00,0xF8,0x00,0x10,0x80,0x00,0xFF,0xF8,0x10,0x80,0x00,0xFF,0xF8,0x10,0x80,
0x00,0xF8,0x00,0x10,0x80,0x00,0x20,0x00,0x10,0x80,0x00,0x20,0x00,0x10,0x80,0x00,0x20,0x00,0x10,0x80,0x00,0x20,0x00,0x10,
0x80,0x00,0x20,0x00,0x10,0x80,0x00,0x20,0x00,0x10,0x40,0x00,0x20,0x00,0x20,0x20,0x00,0x20,0x00,0x40,0x10,0x00,0x20,0x00,
0x80,0x08,0x00,0x20,0x01,0x00,0x04,0x00,0x20,0x02,0x00,0x02,0x00,0x20,0x04,0x00,0x01,0x00,0x00,0x08,0x00,0x00,0x80,0x00,
0x10,0x00,0x00,0x40,0x00,0x20,0x00,0x00,0x3F,0xFF,0xC0,0x00};/*"未命名文件",0*/static const unsigned char PROGMEM cursor_u[] =
{0x01,0x80,0x03,0xC0,0x07,0xE0,0x0F,0xF0,0x1F,0xF8,0x3F,0xFC,0x7F,0xFE,0xFF,0xFF,0x07,0xE0,0x07,0xE0,0x07,0xE0,0x07,0xE0,
0x07,0xE0,0x07,0xE0,0x07,0xE0,0x07,0xE0};/*"上箭头",0*/static const unsigned char PROGMEM cursor_d[] =
{0x07,0xE0,0x07,0xE0,0x07,0xE0,0x07,0xE0,0x07,0xE0,0x07,0xE0,0x07,0xE0,0x07,0xE0,0xFF,0xFF,0x7F,0xFE,0x3F,0xFC,0x1F,0xF8,
0x0F,0xF0,0x07,0xE0,0x03,0xC0,0x01,0x80};/*"下箭头",0*///继承重写LogoPicture,形成很多子类,方便扩展,符合开放封闭原则//音乐图标
class MusicP: public LogoPicture{
public:MusicP(MyOled *oled);~MusicP(void);void showLogo(void);
private:MyOled *m_oled;
};class ServoP: public LogoPicture{
public:ServoP(MyOled *oled);~ServoP(void);void showLogo(void);
private:MyOled *m_oled;
};//时钟图标
class ClockP: public LogoPicture{
public:ClockP(MyOled *oled);~ClockP(void);void showLogo(void);
private:MyOled *m_oled;
};//舵机图标
class Servo_fun: public LogoPicture{
public:Servo_fun(MyOled *oled);~Servo_fun(void);void showLogo(void);
private:MyOled *m_oled;
};//音乐播放界面
class Playlist: public LogoPicture{
public:Playlist(MyOled *oled);~Playlist(void);void showLogo(void);
private:MyOled *m_oled;
};//演奏中
class Playing: public LogoPicture{
public:Playing(MyOled *oled);~Playing(void);void showLogo(void);
private:MyOled *m_oled;
};//时钟计时界面
class ClockCount: public LogoPicture{
public:ClockCount(MyOled *oled);~ClockCount(void);void showLogo(void);
private:MyOled *m_oled;
};
二、功能演示
程序运行,我们可以看到图形化界面如下,可按上下进行功能切换:
点击进入音乐界面,按上下按键可以调节舵机旋转角度,并且舵机跟着转动,如图:
点击英语界面,选择第一个音乐,蜂鸣器鸣叫模拟音乐
点击进入时钟界面,此时可以进行计时操作,如下:
三、设计总结
本次设计使用Arduino IDE开发Arduino UNO,利用oled模块显示可视化界面,对应操作不同器件了,本次设计利用状态者模式进行设计,为程序的可扩展性提供了很大便利。