基于STM32的电子琴音乐播放器设计

基于STM32的电子琴/音乐播放器设计


引言

​ 单片微型计算机室大规模集成电路技术发展的产物,属于第四代电子计算机它具有高性能、高速度、体积小、价格低廉、稳定可靠、应用广泛的特点。他的应用必定导致传统的控制技术从根本上发生变革。因此,单片机的开发应用已成为高科技和工程领域的一项重大课题。

​ 电子琴是现代电子科技与音乐结合的产物,是一种新型的键盘乐器。它在现代音乐扮演重要的角色,单片机具有强大的控制功能和灵活的编程实现特性,它已经溶入现代人们的生活中,成为不可替代的一部分。本文的主要内容是用STM32f103rbt6单片机为核心控制元件,设计一个电子琴。

​ 设计核心在于使用STM32单片机内置的

第一章 总体设计

1.1 系统功能

按照设计要求,本系统具有以下功能:

  1. 共有三个基本模式:电子琴模式、录音模式、播放器模式
  2. 电子琴模式下,7个基本按键控制产生7种音调,功能键实现调节音阶和音量
  3. 录音模式可分为录音和放音两个模块,录音状态下会记录弹奏的音调以及时间;放音模式调用音乐播放器某些模块,实现相同的功能。
  4. 音乐播放器模块下,可以实现音乐的播放、暂停、切歌、调速、顺序播放、单曲循环、随机播放、以及进度条显示。
  5. 有两个全局按键中断,可控制模式切换和全局静音/暂停。

1.2 主要技术性能指标

  1. 基本按键:7个;
  2. 功能按键:6个;
  3. 全局中断按键:2个;
  4. 扬声器:1个;
  5. 扬声器功率:1w;
  6. LCD1602:1块;
  7. 主要模式:3个;
  8. 曲库:8首;
  9. 音域范围:262Hz~2217Hz;
  10. 音量阶数:3阶;
  11. 速度阶数:4阶;
  12. 循环模式: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 显示模块

LCD模块

​ 将LCD1602的D0~D7分别连接到单片机的 PA0~7,使能端 E、 RW、 RS分别连接到单片机的 PA8、 PA11、 PA12。

2.2.5 主控模块

STM32f103rbt6芯片

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;	//定时器3通道1句柄

//TIM1 PWM部分初始化 
//arr:自动重装值。
//psc:时钟预分频数
//定时器溢出时间计算方法:Tout=((arr+1)*(psc+1))/Ft us.
//Ft=定时器工作频率,单位:Mhz
void TIM3_PWM_Init(u16 arr,u16 psc)
{  
    TIM3_Handler.Instance=TIM3;         	//定时器1
    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);       	//初始化PWM
    
    TIM3_CH2Handler.OCMode=TIM_OCMODE_PWM1; //模式选择PWM1
    TIM3_CH2Handler.Pulse=arr/2;            //设置比较值,此值用来确定占空比,默认比较值为自动重装载值的一半,即占空比为50%
    TIM3_CH2Handler.OCPolarity=TIM_OCPOLARITY_LOW; //输出比较极性为低 
    HAL_TIM_PWM_ConfigChannel(&TIM3_Handler,&TIM3_CH2Handler,TIM_CHANNEL_2);//配置TIM3通道2
	
    HAL_TIM_PWM_Start(&TIM3_Handler,TIM_CHANNEL_2);//开启PWM通道2
	 	   
}

//定时器底层驱动,时钟使能,引脚配置
//此函数会被HAL_TIM_PWM_Init()调用
//htim:定时器句柄
void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim)
{
	GPIO_InitTypeDef GPIO_Initure;
	
    if(htim->Instance==TIM3)
	{
		__HAL_RCC_TIM3_CLK_ENABLE();			//使能定时器3
		__HAL_AFIO_REMAP_TIM3_ENABLE();		    //TIM3通道引脚完全重映射使能
		__HAL_RCC_GPIOC_CLK_ENABLE();			//开启GPIOC时钟
		
		GPIO_Initure.Pin=GPIO_PIN_7;           	//PC6
		GPIO_Initure.Mode=GPIO_MODE_AF_PP;  	//复用推挽输出
		GPIO_Initure.Pull=GPIO_PULLUP;          //上拉
		GPIO_Initure.Speed=GPIO_SPEED_HIGH;     //高速
		HAL_GPIO_Init(GPIOC,&GPIO_Initure); 	
	}
}

//设置TIM3通道2的占空比
//compare:比较值
void TIM_SetTIM3Compare2(u32 compare)
{
	TIM3->CCR2=compare; 
}

//设置TIM3通道2的arr
void TIM_SetTIM3Autoreload(u32 Autoreload)
{
	TIM3->ARR=Autoreload;
}
//定时器3中断服务函数
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;
//	tNote *p;
	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;//0~2循环
				 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();               //开启GPIOC时钟

    GPIO_Initure.Pin=GPIO_PIN_8|GPIO_PIN_9; 	//PC8、PC9
    GPIO_Initure.Mode=GPIO_MODE_IT_FALLING;     //下降沿触发
    GPIO_Initure.Pull=GPIO_PULLUP;
    HAL_GPIO_Init(GPIOC,&GPIO_Initure);

	//中断线8、9-PC8、9
    HAL_NVIC_SetPriority(EXTI9_5_IRQn,2,1);   	//抢占优先级为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);		//调用中断处理公用函数
}
//中断服务程序中需要做的事情
//在HAL库中所有的外部中断服务函数都会调用此函数
//GPIO_Pin:中断引脚号
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的显示函数,通过tab0tab1的锁存,实现更丰富需求。

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[];
//extern tNote Score1[];
//extern tNote Score1[];



//int next =1;

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)
{
//    u8 len;	
//	u16 times=0;  
	int reflag=0;
	
    HAL_Init();                    	//初始化HAL库    
    Stm32_Clock_Init(RCC_PLL_MUL9); //设置时钟,72M
    delay_init(72);                 //初始化延时函数
	uart_init(115200);				//初始化串口115200
	
    LCD1602_Init();
		TIM3_PWM_Init(0xfffe,8);
		KEY_Init();
		EXTI_Init();
		printf("1231");
    delay_ms(5);
	void musicPlayRE(int len);
	                      /*0123456789ABCDEF*/
		sprintf((char *)tab0," Mode Selection ");
		sprintf((char *)tab1,"Please Press 1~3");
		LCD_SHOW(tab0,tab1);
	  buzzerSound(FM1,1);
		delay_ms(100);
		buzzerQuiet();
		//musicPlay();
	while(1)
	{
		while(mode == MENU)
		{
			
			key = KEY_Scan(0);
			switch(key)
			{
				case KEY0_PRES:
					mode =PIANO;
//					                    /*0123456789ABCDEF*/
//					sprintf((char *)tab0,"   PIANO MODE   ");
//					sprintf((char *)tab1," Tone=%c   Vol=%d ",(tone+67),vol);
//					LCD_SHOW(tab0,tab1);
					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;//0~2循环
					break;
				case KEY12_PRES:
					tone = (tone+1)%3;
					break;
			}
			while(!KEY0)
			{
				buzzerSound(ToneList[tone][0],7-3*vol);//音量用函数转成分频值012---741
			}
			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:

		//	musicPlay(Score1,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;//0~2循环
				 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 实际电路调试

​ 在搭建好实际电路后,各项功能均可正常工作。

​ 同时,根据需要用孔板焊接了一套按键键盘。

屏幕截图 2021-07-16 105801

3.4 功能测试

  1. 单片机下载完成后,显示Mode Selection Please Press 1-3表示初始化完成等待选择模式
  2. 按下key1,进入Piano模式,可以开始弹奏。
  3. 按下key13可进行音量调节,key12可以调节音调
  4. 按下key8切换模式,进入录音模式
  5. 录音模式下,按key0进行开始录音
  6. 录音会记录音调以及持续时间,按下key13停止录音
  7. 录音完成后按下key13放音,可以听到记录的曲子
  8. 按下key13切换模式进入播放器模式
  9. 可以通过key0-key7选择曲目,也可以直接按key13开始播放
  10. 播放时,按下key13下一首,key12调音量,key11调速度,key10调节下一首模式,key9可暂停
  11. 播放时有进度条显示

至此,所有功能均无错误。

3.5 过程中遇到的问题

​ 在设计时,一开始选择使用面包板搭建外围电路,但是发现过于臃肿,所以自己焊接了一块按键键盘,便于操作。

​ 在软件设计时,各种全局变量在不同函数之间的传递,导致逻辑十分混乱,通过画图,慢慢的理清逻辑修改bug

第四章 总结

​ 本设计以实际电路为最后的结果,实现了所有与其功能,具有重要的现实意义。

件,在Proteus中进行原理图绘制,然后把 .hex文件下载到STM32F103RBT6中 进行仿真。 由于网络上 下载的 Proteus中 没有 STM32F103RBT6这个芯片型号,我们又从网上 下载芯片 进行仿真。

3.2 仿真结果

​ 仿真并没有取得预期结果,分析原因可能是因为仿真所使用的系统频率过低,没法实现正常的要求,所以未达到预期效果。

3.3 实际电路调试

​ 在搭建好实际电路后,各项功能均可正常工作。

​ 同时,根据需要用孔板焊接了一套按键键盘。

[外链图片转存中…(img-mnecOLco-1632967473029)]

3.4 功能测试

  1. 单片机下载完成后,显示Mode Selection Please Press 1-3表示初始化完成等待选择模式
  2. 按下key1,进入Piano模式,可以开始弹奏。
  3. 按下key13可进行音量调节,key12可以调节音调
  4. 按下key8切换模式,进入录音模式
  5. 录音模式下,按key0进行开始录音
  6. 录音会记录音调以及持续时间,按下key13停止录音
  7. 录音完成后按下key13放音,可以听到记录的曲子
  8. 按下key13切换模式进入播放器模式
  9. 可以通过key0-key7选择曲目,也可以直接按key13开始播放
  10. 播放时,按下key13下一首,key12调音量,key11调速度,key10调节下一首模式,key9可暂停
  11. 播放时有进度条显示

至此,所有功能均无错误。

3.5 过程中遇到的问题

​ 在设计时,一开始选择使用面包板搭建外围电路,但是发现过于臃肿,所以自己焊接了一块按键键盘,便于操作。

​ 在软件设计时,各种全局变量在不同函数之间的传递,导致逻辑十分混乱,通过画图,慢慢的理清逻辑修改bug

第四章 总结

​ 本设计以实际电路为最后的结果,实现了所有与其功能,具有重要的现实意义。

版权声明:本文为CSDN博主「ℳ๓₯㎕₯㎕ζั」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/TMMXA/article/details/120561491

生成海报
点赞 0

ℳ๓₯㎕₯㎕ζั

我还没有学会写个人说明!

暂无评论

发表评论

相关推荐

RT-Thread Studio移植LAN8720A驱动

RTT网络协议栈驱动移植(霸天虎) 1、新建工程 ​ 工程路径不含中文路径名,工程名用纯英文不含任何符号。 2、用CubeMx配置板子外设 2.1、配置时钟 ​ 按照自己板子配置相应时钟。

Lin总线通信在STM32作为主机代码以及从机程序

距离上次做资料准备已经过去六天了。最近在学车,上周末就没有开电脑。这周开始进行了Lin通信的代码整理,目前是可以正常通信的了,采用的是增强型校验方式。后期再进一步跟进研究。。。更新一博,留

4路红外循迹模块使用教程

4路红外循迹模块使用教程 个人原创博客:点击浏览模块详细信息: 工作电压:DC 3.3V~5V 工作电流:尽量选择1A以上电源供电 工作温度:-10℃~50℃ 安装孔

HAL库串口中断

一,配置串口初始化 void MX_USART1_UART_Init(void) {huart1.Instance USART1;huart1.Init.BaudRate 115200;huart1.Init.WordLen