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 一个新的状态,设置进去这里为2
count = 0; //开关打开,方便下次跳转到这时创建对象
}
//按下1按键,滑动到3状态
else if(m_command == 2){
delete s->getCase(); //回收状态管理者的此时BaseCase指针
s->setCase(new StateCase3); //new 一个新的状态,设置进去这里为3
count = 0; //开关打开,方便下次跳转到这时创建对象
}
//5键为确定,跳转到状态5
else if(m_command == 5){
delete s->getCase(); //回收状态管理者的此时BaseCase指针
s->setCase(new StateCase4); //new 一个新的状态,设置进去这里为3
count = 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 一个新的状态,设置进去这里为1
count = 0; //开关打开,方便下次跳转到这时创建对象
}
else if(m_command == 5){
delete s->getCase(); //回收状态管理者的此时BaseCase指针
s->setCase(new StateCase5); //new 一个新的状态,设置进去这里为5
count = 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模块显示可视化界面,对应操作不同器件了,本次设计利用状态者模式进行设计,为程序的可扩展性提供了很大便利。