基于STM32的电子琴/音乐播放器设计
引言
? 单片微型计算机室大规模集成电路技术发展的产物,属于第四代电子计算机它具有高性能、高速度、体积小、价格低廉、稳定可靠、应用广泛的特点。他的应用必定导致传统的控制技术从根本上发生变革。因此,单片机的开发应用已成为高科技和工程领域的一项重大课题。
? 电子琴是现代电子科技与音乐结合的产物,是一种新型的键盘乐器。它在现代音乐扮演重要的角色,单片机具有强大的控制功能和灵活的编程实现特性,它已经溶入现代人们的生活中,成为不可替代的一部分。本文的主要内容是用STM32f103rbt6单片机为核心控制元件,设计一个电子琴。
? 设计核心在于使用STM32单片机内置的
第一章 总体设计
1.1 系统功能
按照设计要求,本系统具有以下功能:
- 共有三个基本模式:电子琴模式、录音模式、播放器模式
- 电子琴模式下,7个基本按键控制产生7种音调,功能键实现调节音阶和音量
- 录音模式可分为录音和放音两个模块,录音状态下会记录弹奏的音调以及时间;放音模式调用音乐播放器某些模块,实现相同的功能。
- 音乐播放器模块下,可以实现音乐的播放、暂停、切歌、调速、顺序播放、单曲循环、随机播放、以及进度条显示。
- 有两个全局按键中断,可控制模式切换和全局静音/暂停。
1.2 主要技术性能指标
- 基本按键:7个;
- 功能按键:6个;
- 全局中断按键:2个;
- 扬声器:1个;
- 扬声器功率:1w;
- LCD1602:1块;
- 主要模式:3个;
- 曲库:8首;
- 音域范围:262Hz~2217Hz;
- 音量阶数:3阶;
- 速度阶数:4阶;
- 循环模式:3种;
第二章 系统设计
2.1 系统设计
? 总体系统设计上在硬件上共分为3个区域:基本按键区、功能按键区、LCD显示区。在软件的设计上共分为3个主要模式:电子琴模式、录音模式、播放器模式。主控模块选择使用STM32f103rbt6芯片,进行编程、控制、实现电子琴以及播放器功能。
2.2 硬件设计
2.2.1 整体仿真图
2.2.2 按键模块
? 按键模块分为两部分:基本按键和功能按键
俩个部分按键分别接在单片机的PC0-PC6以及PC8-PC13接口上。
2.2.3 扬声器模块
扬声器模块接在单片机的PC07接口上。
2.2.4 显示模块
? 将LCD1602的D0~D7分别连接到单片机的 PA0~7,使能端 E、 RW、 RS分别连接到单片机的 PA8、 PA11、 PA12。
2.2.5 主控模块
2.3 软件设计
2.3.1 主要工作原理
? 设计的主要工作原理是利用STM32所内置的定时器TIM3产生一个PWM信号驱动扬声器产生特定频率的声音。通过改变定时器TIM3的分频预置数改变PWM信号的频率从而产生不同音调的声音。通过改变占空比,从而产生不同音量的声音。
? 相关流程图如下:
Created with Rapha?l 2.3.0
开始
各模块初始化
模式选择输入
模式=1?
钢琴模式
模式=2?
录音模式
模式=3?
播放器模式
等待模式选择
yes
no
yes
no
yes
no
2.3.2 PWM发生器
#include "pwm.h"
TIM_HandleTypeDef TIM3_Handler;
TIM_OC_InitTypeDef TIM3_CH2Handler;
void TIM3_PWM_Init(u16 arr,u16 psc)
{
TIM3_Handler.Instance=TIM3;
TIM3_Handler.Init.Prescaler=psc;
TIM3_Handler.Init.CounterMode=TIM_COUNTERMODE_UP;
TIM3_Handler.Init.Period=arr;
TIM3_Handler.Init.ClockDivision=TIM_CLOCKDIVISION_DIV1;
TIM3_Handler.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
HAL_TIM_PWM_Init(&TIM3_Handler);
TIM3_CH2Handler.OCMode=TIM_OCMODE_PWM1;
TIM3_CH2Handler.Pulse=arr/2;
TIM3_CH2Handler.OCPolarity=TIM_OCPOLARITY_LOW;
HAL_TIM_PWM_ConfigChannel(&TIM3_Handler,&TIM3_CH2Handler,TIM_CHANNEL_2);
HAL_TIM_PWM_Start(&TIM3_Handler,TIM_CHANNEL_2);
}
void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim)
{
GPIO_InitTypeDef GPIO_Initure;
if(htim->Instance==TIM3)
{
__HAL_RCC_TIM3_CLK_ENABLE();
__HAL_AFIO_REMAP_TIM3_ENABLE();
__HAL_RCC_GPIOC_CLK_ENABLE();
GPIO_Initure.Pin=GPIO_PIN_7;
GPIO_Initure.Mode=GPIO_MODE_AF_PP;
GPIO_Initure.Pull=GPIO_PULLUP;
GPIO_Initure.Speed=GPIO_SPEED_HIGH;
HAL_GPIO_Init(GPIOC,&GPIO_Initure);
}
}
void TIM_SetTIM3Compare2(u32 compare)
{
TIM3->CCR2=compare;
}
void TIM_SetTIM3Autoreload(u32 Autoreload)
{
TIM3->ARR=Autoreload;
}
void TIM3_IRQHandler(void)
{
HAL_TIM_IRQHandler(&TIM3_Handler);
}
2.3.3 music播放器模块
? music模块包含了产生声音,静音,音乐播放,音乐切换,进度条展示等相关函数,全都由本人编写
静音模块:通过调用TIM_SetTIM3Compare2() 函数让占空比为0,进而达到静音效果。
void buzzerQuiet(void)
{
TIM_SetTIM3Compare2(0);
}
发声函数:通过调用TIM_SetTIM3Autoreload 设置TIM3的自动装载值实现产生特定频率PWM信号,传入的参数为声音频率和音量参数。
void buzzerSound(unsigned short usFraq,unsigned char volume_level)
{
unsigned long Autoreload;
if((usFraq<=122)||(usFraq>20000))
{
buzzerQuiet();
}
else
{
Autoreload=(8000000/usFraq)-1;
TIM_SetTIM3Autoreload(Autoreload);
TIM_SetTIM3Compare2(Autoreload>>volume_level);
}
}
进度条显示函数:可以显示播放进度以及全局状态,如当前曲目、暂停状态、音量、播放速度等。
void playstate(int a,char n)
{
switch(a)
{
case 0:
sprintf((char *)tab1,"%c%c- %c%d",n,pausename[pauseflag],speedname[speed],vol+1);
break;
case 1:
sprintf((char *)tab1,"%c%c-- %c%d",n,pausename[pauseflag],speedname[speed],vol+1);
break;
case 2:
sprintf((char *)tab1,"%c%c--- %c%d",n,pausename[pauseflag],speedname[speed],vol+1);
break;
case 3:
sprintf((char *)tab1,"%c%c---- %c%d",n,pausename[pauseflag],speedname[speed],vol+1);
break;
case 4:
sprintf((char *)tab1,"%c%c----- %c%d",n,pausename[pauseflag],speedname[speed],vol+1);
break;
case 5:
sprintf((char *)tab1,"%c%c------ %c%d",n,pausename[pauseflag],speedname[speed],vol+1);
break;
case 6:
sprintf((char *)tab1,"%c%c------- %c%d",n,pausename[pauseflag],speedname[speed],vol+1);
break;
case 7:
sprintf((char *)tab1,"%c%c-------- %c%d",n,pausename[pauseflag],speedname[speed],vol+1);
break;
case 8:
sprintf((char *)tab1,"%c%c--------- %c%d",n,pausename[pauseflag],speedname[speed],vol+1);
break;
case 9:
sprintf((char *)tab1,"%c%c---------- %c%d",n,pausename[pauseflag],speedname[speed],vol+1);
break;
case 10:
sprintf((char *)tab1,"%c%c----------- %c%d",n,pausename[pauseflag],speedname[speed],vol+1);
break;
case 11:
sprintf((char *)tab1,"%c%c------------%c%d",n,pausename[pauseflag],speedname[speed],vol+1);
break;
case 12:
sprintf((char *)tab1,"%c%c------------%c%d",n,pausename[pauseflag],speedname[speed],vol+1);
break;
}
}
下一首函数:根据传入nextmode参数不同进行顺序,单曲,随机下一首。
void nextsong(int nextmode,int i)
{
int h;
if(nextmode==1)
next=(next+1)%8;
else if(nextmode==2)
next=next;
else if(nextmode==0)
{
h=i%6+1;
next = (next+h)%8;
}
}
音乐播放函数:传入乐谱(由结构体数组实现),调用弹奏音符函数,实现音乐自动播放。
void play_node(int tone,int time)
{
buzzerSound(tone,volume);
delay_ms(time/((speed+1)*0.5));
buzzerQuiet();
delay_ms(10);
}
void musicPlay(tNote song[],int n)
{
void play_node(int tone,int time);
u8 i=1;
int playflag=1;
int len = song[0].mName;
while((song[i].mName||song[i].mTime)&&playflag&&mode==2)
{
key1 = KEY_Scan(0);
switch(key1)
{
case KEY13_PRES:
playflag=0;
break;
case KEY12_PRES:
vol=(vol+1)%3;
break;
case KEY11_PRES:
speed=(speed+1)%4;
break;
case KEY10_PRES:
nextmode=(nextmode+1)%3;
switch(nextmode)
{
case 0:
sprintf((char *)tab0," SHUFFLE PLAY ");
LCD_SHOW(tab0,0);
break;
case 1:
sprintf((char *)tab0," ORDER PLAY ");
LCD_SHOW(tab0,0);
break;
case 2:
sprintf((char *)tab0," SINGLE CYCLE ");
LCD_SHOW(tab0,0);
}
break;
}
if(pauseflag)
{
play_node(song[i].mName,song[i].mTime);
i++;
}
playstate(12*i/len,n+48);
LCD_SHOW(0,tab1);
}
nextsong(nextmode,i);
}
2.3.4 exti外部中断
? 本设计使用了PC8,PC9口的按键作为两个外部中断,控制全局切换模式,以及全局暂停/静音
#include "exti.h"
#include "delay.h"
#include "key.h"
#include "music.h"
#define NEXT_MODE() (mode=(mode+1)%3)
#define PAUSE() (pauseflag=!pauseflag)
extern int mode;
extern int pauseflag;
void EXTI_Init(void)
{
GPIO_InitTypeDef GPIO_Initure;
__HAL_RCC_GPIOC_CLK_ENABLE();
GPIO_Initure.Pin=GPIO_PIN_8|GPIO_PIN_9;
GPIO_Initure.Mode=GPIO_MODE_IT_FALLING;
GPIO_Initure.Pull=GPIO_PULLUP;
HAL_GPIO_Init(GPIOC,&GPIO_Initure);
HAL_NVIC_SetPriority(EXTI9_5_IRQn,2,1);
HAL_NVIC_EnableIRQ(EXTI9_5_IRQn);
}
void EXTI9_5_IRQHandler(void)
{
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_8);
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_9);
}
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
delay_ms(10);
switch(GPIO_Pin)
{
case GPIO_PIN_8:
if(KEY8==0)
{
buzzerSound(FM2,1);
delay_ms(200);
buzzerQuiet();
NEXT_MODE();
}
break;
case GPIO_PIN_9:
if(KEY9==0)
{
PAUSE();
}
break;
}
}
2.3.5 按键相关驱动
? 按键初始化相关代码采用例程,在此不列出,只列出关键代码:
static u8 key_up=1;
u8 KEY_Scan(u8 mode)
{
if(mode)key_up=1;
if(key_up&&(KEY0==0||KEY1==0||KEY2==0||KEY3==0||KEY4==0||KEY5==0||KEY6==0||KEY8==0||KEY9==0||KEY10==0||KEY11==0||KEY12==0||KEY13==0))
{
delay_ms(10);
key_up=0;
if(KEY0==0)return KEY0_PRES;
else if(KEY1==0)return KEY1_PRES;
else if(KEY2==0)return KEY2_PRES;
else if(KEY3==0)return KEY3_PRES;
else if(KEY4==0)return KEY4_PRES;
else if(KEY5==0)return KEY5_PRES;
else if(KEY6==0)return KEY6_PRES;
else if(KEY8==0)return KEY8_PRES;
else if(KEY9==0)return KEY9_PRES;
else if(KEY10==0)return KEY10_PRES;
else if(KEY11==0)return KEY11_PRES;
else if(KEY12==0)return KEY12_PRES;
else if(KEY13==0)return KEY13_PRES;
}else if(KEY0==1&&KEY1==1&&KEY2==1&&KEY3==1&&KEY4==1&&KEY5==1&&KEY6==1&&KEY8==1&&KEY9==1&&KEY10==1&&KEY11==1&&KEY12==1&&KEY13==1)key_up=1;
return 0;
}
? 通过函数判断按键值,mode参数可调节是否支持连按。
2.3.6 LCD1602驱动
? LCD初始化相关代码采用例程,在此不列出,只列出关键代码:
void LCD_SHOW(u8* tab0,u8* tab1)
{
if(tab0)
LCD1602_Show_Str(0, 0, tab0);
if(tab1)
LCD1602_Show_Str(0, 1, tab1);
}
? 定义函数LCD_SHOW,传入字符串显示,在避免直接调用LCD的显示函数,通过tab0 和tab1 的锁存,实现更丰富需求。
2.3.7 主函数相关设计
#include "sys.h"
#include "main.h"
#include "delay.h"
#include "usart.h"
#include "key.h"
#include "lcd1602.h"
#include "music.h"
#include "pwm.h"
#include "exti.h"
#define MENU 8
#define PIANO 0
#define RECORD 1
#define PLAYER 2
#define volume (7-3*vol)
void LCD_SHOW(u8* tab0,u8* tab1);
int nextmode=1;
int mode = 8;
int vol = 2;
int tone =1;
u8 tab0[16];
u8 tab1[16];
int startflag=0;
int finishflag=0;
int psite=0;
int pauseflag=1;
vu8 key=0;
int speed=1;
int relen=0;
char pausename[2]={'P','S'};
char speedname[4]={'L','N','M','H'};
extern tNote Score1[];
extern tNote Score2[];
extern tNote Score3[];
extern tNote Score4[];
extern tNote Score5[];
extern tNote Score6[];
extern tNote Score7[];
extern tNote Score8[];
int ToneList[3][7]=
{
{262,294,330,349,392,440,494},
{523,587,659,698,784,880,988},
{1047,1175,1319,1397,1568,1760,1976}
};
char ToneName[3]=
{
'L','M','H'
};
tNote RecordScord[100];
int main(void)
{
int reflag=0;
HAL_Init();
Stm32_Clock_Init(RCC_PLL_MUL9);
delay_init(72);
uart_init(115200);
LCD1602_Init();
TIM3_PWM_Init(0xfffe,8);
KEY_Init();
EXTI_Init();
printf("1231");
delay_ms(5);
void musicPlayRE(int len);
sprintf((char *)tab0," Mode Selection ");
sprintf((char *)tab1,"Please Press 1~3");
LCD_SHOW(tab0,tab1);
buzzerSound(FM1,1);
delay_ms(100);
buzzerQuiet();
while(1)
{
while(mode == MENU)
{
key = KEY_Scan(0);
switch(key)
{
case KEY0_PRES:
mode =PIANO;
break;
case KEY1_PRES:
mode = RECORD;
break;
case KEY2_PRES:
mode = PLAYER;
break;
default:
delay_ms(10);
}
}
while(mode == PIANO)
{
sprintf((char *)tab0," PIANO MODE ");
sprintf((char *)tab1," Tone=C%c Vol=%d ",ToneName[tone],vol+1);
LCD_SHOW(tab0,tab1);
key = KEY_Scan(0);
switch(key)
{
case KEY13_PRES:
vol=(vol+1)%3;
break;
case KEY12_PRES:
tone = (tone+1)%3;
break;
}
while(!KEY0)
{
buzzerSound(ToneList[tone][0],7-3*vol);
}
while(!KEY1)
{
buzzerSound(ToneList[tone][1],7-3*vol);
}
while(!KEY2)
{
buzzerSound(ToneList[tone][2],7-3*vol);
}
while(!KEY3)
{
buzzerSound(ToneList[tone][3],7-3*vol);
}
while(!KEY4)
{
buzzerSound(ToneList[tone][4],7-3*vol);
}
while(!KEY5)
{
buzzerSound(ToneList[tone][5],7-3*vol);
}
while(!KEY6)
{
buzzerSound(ToneList[tone][6],7-3*vol);
}
buzzerQuiet();
}
while(mode == RECORD)
{
LCD_SHOW(tab0,tab1);
if(!startflag)
{
finishflag=0;
psite=0;
key = KEY_Scan(0);
sprintf((char *)tab0," RECORD MODE ");
sprintf((char *)tab1," Key0 to Start ");
LCD_SHOW(tab0,tab1);
if(key == KEY0_PRES)
{
startflag=1;
sprintf((char *)tab1," Come On! GO! ");
}
if(key == KEY13_PRES)
{
if(relen)
musicPlayRE(relen);
else
{
sprintf((char *)tab1," NO RECORDED ");
LCD_SHOW(tab0,tab1);
delay_ms(1000);
}
}
delay_ms(5);
break;
}
if(finishflag)
startflag=0;
key = KEY_Scan(0);
switch(key)
{
case KEY13_PRES:
finishflag=1;
sprintf((char *)tab1," RECORD FINISH ");
RecordScord[psite+1].mName=0;
RecordScord[psite+1].mTime=0;
LCD_SHOW(0,tab1);
delay_ms(1000);
break;
case KEY12_PRES:
tone = (tone+1)%3;
break;
}
RecordScord[psite].mName=0;
RecordScord[psite].mTime=0;
reflag=0;
while(!KEY0)
{
buzzerSound(ToneList[tone][0],1);
reflag=1;
RecordScord[psite].mName=ToneList[tone][0];
RecordScord[psite].mTime++;
delay_ms(1);
}
while(!KEY1)
{
buzzerSound(ToneList[tone][1],1);
reflag=1;
RecordScord[psite].mName=ToneList[tone][1];
RecordScord[psite].mTime++;
delay_ms(1);
}
while(!KEY2)
{
buzzerSound(ToneList[tone][2],1);
reflag=1;
RecordScord[psite].mName=ToneList[tone][2];
RecordScord[psite].mTime++;
delay_ms(1);
}
while(!KEY3)
{
buzzerSound(ToneList[tone][3],1);
reflag=1;
RecordScord[psite].mName=ToneList[tone][3];
RecordScord[psite].mTime++;
delay_ms(1);
}
while(!KEY4)
{
buzzerSound(ToneList[tone][4],1);
reflag=1;
RecordScord[psite].mName=ToneList[tone][4];
RecordScord[psite].mTime++;
delay_ms(1);
}
while(!KEY5)
{
buzzerSound(ToneList[tone][4],1);
reflag=1;
RecordScord[psite].mName=ToneList[tone][4];
RecordScord[psite].mTime++;
delay_ms(1);
}
while(!KEY6)
{
buzzerSound(ToneList[tone][6],1);
reflag=1;
RecordScord[psite].mName=ToneList[tone][6];
RecordScord[psite].mTime++;
delay_ms(1);
}
buzzerQuiet();
if(reflag)
{
psite++;
relen=psite+1;
sprintf((char *)tab1,"P=%d key13 over %c",psite,ToneName[tone]);
if(psite>98)
{
finishflag=1;
sprintf((char *)tab1," SCORD FULL ");
RecordScord[psite+1].mName=0;
RecordScord[psite+1].mTime=0;
LCD_SHOW(0,tab1);
delay_ms(1000);
}
}
}
while(mode == PLAYER)
{
sprintf((char *)tab0," PLAYER MODE ");
sprintf((char *)tab1," Key13 to start ");
LCD_SHOW(tab0,tab1);
key = KEY_Scan(0);
switch(key)
{
case KEY13_PRES:
nextsong(1,0);
break;
case 0:
break;
case 1:
musicPlay(Score2,1);
break;
case 2:
musicPlay(Score3,2);
break;
case 3:
musicPlay(Score4,3);
break;
case 4:
musicPlay(Score5,4);
break;
case 5:
musicPlay(Score6,5);
break;
case 6:
musicPlay(Score7,6);
break;
case 7:
musicPlay(Score8,7);
break;
}
}
}
}
void LCD_SHOW(u8* tab0,u8* tab1)
{
if(tab0)
LCD1602_Show_Str(0, 0, tab0);
if(tab1)
LCD1602_Show_Str(0, 1, tab1);
}
void musicPlayRE(int len)
{
void play_node(int tone,int time);
u8 i=0;
int playflag=1;
while((RecordScord[i].mName||RecordScord[i].mTime)&&playflag&&mode==1)
{
key = KEY_Scan(0);
switch(key)
{
case KEY13_PRES:
playflag=0;
break;
case KEY12_PRES:
vol=(vol+1)%3;
break;
case KEY11_PRES:
speed=(speed+1)%4;
break;
}
if(pauseflag)
{
play_node(RecordScord[i].mName,RecordScord[i].mTime);
i++;
}
playstate(12*i/len,'R');
LCD_SHOW(0,tab1);
}
}
第三章 系统调试
3.1 仿真调试
? 本设计利用 Keil uVsion5进行程序编写,编译,调试,生成 .hex文件,在Proteus中进行原理图绘制,然后把 .hex文件下载到STM32F103RBT6中 进行仿真。 由于网络上 下载的 Proteus中 没有 STM32F103RBT6这个芯片型号,我们又从网上 下载芯片 进行仿真。
3.2 仿真结果
? 仿真并没有取得预期结果,分析原因可能是因为仿真所使用的系统频率过低,没法实现正常的要求,所以未达到预期效果。
3.3 实际电路调试
? 在搭建好实际电路后,各项功能均可正常工作。
? 同时,根据需要用孔板焊接了一套按键键盘。
3.4 功能测试
- 单片机下载完成后,显示
Mode Selection Please Press 1-3 表示初始化完成等待选择模式 - 按下key1,进入Piano模式,可以开始弹奏。
- 按下key13可进行音量调节,key12可以调节音调
- 按下key8切换模式,进入录音模式
- 录音模式下,按key0进行开始录音
- 录音会记录音调以及持续时间,按下key13停止录音
- 录音完成后按下key13放音,可以听到记录的曲子
- 按下key13切换模式进入播放器模式
- 可以通过key0-key7选择曲目,也可以直接按key13开始播放
- 播放时,按下key13下一首,key12调音量,key11调速度,key10调节下一首模式,key9可暂停
- 播放时有进度条显示
至此,所有功能均无错误。
3.5 过程中遇到的问题
? 在设计时,一开始选择使用面包板搭建外围电路,但是发现过于臃肿,所以自己焊接了一块按键键盘,便于操作。
? 在软件设计时,各种全局变量在不同函数之间的传递,导致逻辑十分混乱,通过画图,慢慢的理清逻辑修改bug
第四章 总结
? 本设计以实际电路为最后的结果,实现了所有与其功能,具有重要的现实意义。
件,在Proteus中进行原理图绘制,然后把 .hex文件下载到STM32F103RBT6中 进行仿真。 由于网络上 下载的 Proteus中 没有 STM32F103RBT6这个芯片型号,我们又从网上 下载芯片 进行仿真。
3.2 仿真结果
? 仿真并没有取得预期结果,分析原因可能是因为仿真所使用的系统频率过低,没法实现正常的要求,所以未达到预期效果。
3.3 实际电路调试
? 在搭建好实际电路后,各项功能均可正常工作。
? 同时,根据需要用孔板焊接了一套按键键盘。
[外链图片转存中…(img-mnecOLco-1632967473029)]
3.4 功能测试
- 单片机下载完成后,显示
Mode Selection Please Press 1-3 表示初始化完成等待选择模式 - 按下key1,进入Piano模式,可以开始弹奏。
- 按下key13可进行音量调节,key12可以调节音调
- 按下key8切换模式,进入录音模式
- 录音模式下,按key0进行开始录音
- 录音会记录音调以及持续时间,按下key13停止录音
- 录音完成后按下key13放音,可以听到记录的曲子
- 按下key13切换模式进入播放器模式
- 可以通过key0-key7选择曲目,也可以直接按key13开始播放
- 播放时,按下key13下一首,key12调音量,key11调速度,key10调节下一首模式,key9可暂停
- 播放时有进度条显示
至此,所有功能均无错误。
3.5 过程中遇到的问题
? 在设计时,一开始选择使用面包板搭建外围电路,但是发现过于臃肿,所以自己焊接了一块按键键盘,便于操作。
? 在软件设计时,各种全局变量在不同函数之间的传递,导致逻辑十分混乱,通过画图,慢慢的理清逻辑修改bug
第四章 总结
? 本设计以实际电路为最后的结果,实现了所有与其功能,具有重要的现实意义。
|