基于EVM_MSPM0L1306开发板实现音乐键盘
该项目使用了EVM_MSPM0L1306开发板,实现了音乐键盘的设计,它的主要功能为:矩阵键盘选频,电位器adc调音量,扬声器发声,oled屏幕和led灯显示参数。
标签
MPU
PWM
硬禾活动作业
EVM_MSPM0L1306
音乐键盘
tanlanmaoxianjia
更新2024-07-22
山东大学
29

项目总结报告

项目介绍

本项目使用EVM_MSPM0L1306开发套件-备战电赛训练平台,实现了音乐键盘的设计,它的主要功能为:矩阵键盘选频,电位器adc调音量,扬声器发声,oled屏幕和led灯显示参数

硬件介绍

EVM_MSPM0L1306开发板专为电赛设计,它搭载了MSPM0L1306微控制器,该芯片基于高性能的Arm® Cortex®-M0+内核,兼具低功耗与高效能特性,为电赛训练提供稳定而强大的处理核心.它搭配1.3寸OLED显示屏,板载丰富的外设接口,无论是构建复杂的传感器网络,实现精准的数据采集,还是进行高效的通信交互,都能游刃有余

如上两图我们可以知道,底板上板载有矩阵键盘,oled显示屏,8位led,调试器,电位器分压,无源蜂鸣器,可以直接使用来进行音乐键盘制作

项目设计思路

本项目使用定时器驱动pwm,得到可调频率和占空比的方波,从而实现驱动扬声器发出不同音调和音量的声音,通过傅里叶变换我们可以知道,当pwm波形占空比为50%时,正弦基波幅值最大,当pwm波形占空比为0%或者100%时,正弦基波幅值最小为0,所以可以通过调节占空比近似调整音量

本项目可以根据硬件分成以下几个模块

1.gpio中断检测矩阵键盘频率输入

2.adc中断检测电位器adc音量输入

3.pwm驱动喇叭音乐输出

4.8位led灯组低四位显示矩阵键盘当前键位

5.oled屏幕显示当前参数


如图为软件流程图



具体程序设计

1.gpio中断检测矩阵键盘频率输入

void GROUP1_IRQHandler()
{
int8_t temp;
temp=getKeyValue();
if(Key_Value!=temp)
{
Key_Value=temp;
Key_Update=true;
}
DL_GPIO_clearInterruptStatus(GPIO_Keyboard_V_PORT, GPIO_Keyboard_V_V1_A13_PIN | GPIO_Keyboard_V_V2_A14_PIN |
GPIO_Keyboard_V_V3_A17_PIN | GPIO_Keyboard_V_V4_A18_PIN); // 清除中断请求
}

"key.c"

#include "key.h"

int8_t getKeyValue(void)
{
uint32_t col_pin = 0;
int8_t key_value=Key_Value;
int8_t col = -1;
int8_t row = 0;
switch (DL_GPIO_getPendingInterrupt(GPIO_Keyboard_V_PORT)) // 通过中断引脚获取矩阵键盘列
{
case GPIO_Keyboard_V_V1_A13_IIDX:
col = 0;
col_pin = GPIO_Keyboard_V_V1_A13_PIN;
break;
case GPIO_Keyboard_V_V2_A14_IIDX:
col = 1;
col_pin = GPIO_Keyboard_V_V2_A14_PIN;
break;
case GPIO_Keyboard_V_V3_A17_IIDX:
col = 2;
col_pin = GPIO_Keyboard_V_V3_A17_PIN;
break;
case GPIO_Keyboard_V_V4_A18_IIDX:
col = 3;
col_pin = GPIO_Keyboard_V_V4_A18_PIN;
break;
default:
break;
}
if (col != -1)
{
delay_cycles(CPUCLK_FREQ/100); // 按键消抖
DL_GPIO_setPins(GPIO_Keyboard_H_PORT, GPIO_Keyboard_H_H1_A0_PIN | GPIO_Keyboard_H_H2_A1_PIN); // 0000->1100
delay_cycles(100000);// 等待引脚置位
if (!DL_GPIO_readPins(GPIO_Keyboard_V_PORT, col_pin))
{
row=row|2; // 高两行row+2
}
DL_GPIO_clearPins(GPIO_Keyboard_H_PORT, GPIO_Keyboard_H_H1_A0_PIN | GPIO_Keyboard_H_H2_A1_PIN); // 1100->0000
DL_GPIO_setPins(GPIO_Keyboard_H_PORT, GPIO_Keyboard_H_H1_A0_PIN | GPIO_Keyboard_H_H3_A7_PIN); // 0000->1010
delay_cycles(100000);// 等待引脚置位复位
if (!DL_GPIO_readPins(GPIO_Keyboard_V_PORT, col_pin))
{
row=row|1; // 偶数行row+1
}
row=row;//得到row=按下行值-1
DL_GPIO_clearPins(GPIO_Keyboard_H_PORT, GPIO_Keyboard_H_H1_A0_PIN | GPIO_Keyboard_H_H3_A7_PIN); // 1010->0000
delay_cycles(CPUCLK_FREQ/100); // 防止重置行输出后再次进入中断
key_value = col * 4 + row;
}
return key_value;
}

2.adc中断检测电位器adc音量输入

void ADC12_Speaker_INST_IRQHandler()
{
double_t temp;
switch (DL_ADC12_getPendingInterrupt(ADC12_Speaker_INST))
{
case DL_ADC12_IIDX_MEM0_RESULT_LOADED://ADC内存空间全部装载完
temp=DL_ADC12_getMemResult(ADC12_Speaker_INST, DL_ADC12_MEM_IDX_0);
if(ADC_Value!=temp)
{
ADC_Value=temp;
ADC_Update=true;
}
break;
default:
break;
}
}

3.pwm驱动喇叭音乐输出

"speaker.c"

#include "speaker.h"
uint8_t Speaker_Code_Past=0;
void setSpeaker(int8_t Key_Code,double_t Duty)
{
Speaker_Code_Past=Key_Code;
if (Speaker_Code_Past==0)
{
DL_Timer_stopCounter(PWM0_TimG0_Speaker_INST);
return;
}
uint16_t temp;
temp=CPUCLK_FREQ/8/Speaker_Data[Speaker_Code_Past].Speaker_Frequency;
DL_TimerG_setLoadValue(PWM0_TimG0_Speaker_INST,temp);//设置频率
DL_Timer_setCaptureCompareValue(PWM0_TimG0_Speaker_INST, temp*Duty, DL_TIMER_CC_0_INDEX);
DL_Timer_startCounter(PWM0_TimG0_Speaker_INST);//设置占空比
return;
};

4.8位led灯组低四位显示矩阵键盘当前键位

#include "led.h"
void LED_ShowNum2(uint8_t data)
{
DL_GPIO_writePinsVal(GPIO_LED_PORT,
GPIO_LED_LED0_A8_PIN | GPIO_LED_LED1_A9_PIN| GPIO_LED_LED2_A10_PIN | GPIO_LED_LED3_A11_PIN,
~(data<<8));//A8是第8位所以data要左移8位
}

5.oled屏幕显示当前参数和pwm更新

OLED_Init();
OLED_ShowString(0,0,"Duty=");
OLED_ShowString(0,2,"Scale=");
OLED_ShowString(0,4,"Fq=");
OLED_ShowString(0,6,"Key=");
while (1) {
if(Key_Update)
{
setSpeaker(Key_Value,ADC_Value/4095.0);
LED_ShowNum2(Key_Value);
OLED_ShowNum(60,0,ADC_Value,5,16);
OLED_ShowString(60,2,Speaker_Data[Key_Value].Speaker_Scale);
OLED_ShowNum(60,4,(uint16_t)(Speaker_Data[Key_Value].Speaker_Frequency),5,16);
OLED_ShowNum(60,6,Key_Value,5,16);
Key_Update=false;
}
if(ADC_Update)
{
setSpeaker(Key_Value,ADC_Value/4095.0);
OLED_ShowNum(60,0,ADC_Value,5,16);
ADC_Update=false;
}
}
}


遇到的主要难题及解决方法

1.在程序编写前,搭建环境是费时费力又非常重要的基础工作,可以参照https://blog.csdn.net/Jonshen/article/details/131038223?spm=1001.2014.3001.5502完成环境搭建和基础配置

2.gpio在写入输出值到输出指定电压这段时间,可能会因为外接电路阻抗作用延长,使用keil仿真调试,可以确定是逻辑问题还是时序问题,方便进一步的处理

if (col != -1)
{
delay_cycles(CPUCLK_FREQ/100); // 按键消抖
DL_GPIO_setPins(GPIO_Keyboard_H_PORT, GPIO_Keyboard_H_H1_A0_PIN | GPIO_Keyboard_H_H2_A1_PIN); // 0000->1100
delay_cycles(100000);// 等待引脚置位
if (!DL_GPIO_readPins(GPIO_Keyboard_V_PORT, col_pin))
{
row=row|2; // 高两行row+2
}
DL_GPIO_clearPins(GPIO_Keyboard_H_PORT, GPIO_Keyboard_H_H1_A0_PIN | GPIO_Keyboard_H_H2_A1_PIN); // 1100->0000
DL_GPIO_setPins(GPIO_Keyboard_H_PORT, GPIO_Keyboard_H_H1_A0_PIN | GPIO_Keyboard_H_H3_A7_PIN); // 0000->1010
delay_cycles(100000);// 等待引脚置位复位
if (!DL_GPIO_readPins(GPIO_Keyboard_V_PORT, col_pin))
{
row=row|1; // 偶数行row+1
}
row=row;//得到row=按下行值-1
DL_GPIO_clearPins(GPIO_Keyboard_H_PORT, GPIO_Keyboard_H_H1_A0_PIN | GPIO_Keyboard_H_H3_A7_PIN); // 1010->0000
delay_cycles(CPUCLK_FREQ/100); // 防止重置行输出后再次进入中断
key_value = col * 4 + row;

这一段中就遇到并且通过延时解决了这个问题


实物展示

实物接线

PA16-蜂鸣器

PA21-电位器ADC

屏幕显示介绍

duty/4096=占空比,表示发声音量

scale显示音位,和一般钢琴相同

fq表示发声频率

key表示按下的按键键码-1,如按下s3则key=2

使用说明

按下不同按键,发出不同频率的声音,通过调节电位器调节占空比

未来改进计划

使用dac生成正弦波替代pwm方波,减少高次谐波影响,让pwm占空比导致的高次谐波问题削弱

附件下载
MusicKeyboard.zip
团队介绍
山东大学 岳昊 一人独立团队
团队成员
岳昊
山东大学控制学院学生
评论
0 / 100
查看更多
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号