基于EVM_MSPM0L1306开发板制作一个音乐键盘
该项目使用了keil软件、C语言、EVM_MSPM0L1306开发套件,实现了音乐键盘的设计,它的主要功能为:使用矩阵键盘、蜂鸣器、流水灯和电位计设计音乐键盘,让不同按键发出不同音调,电位计用来控制声音大小,流水灯用来显示声音档位。。
标签
ADC
开发板
谢庥泉
更新2024-07-18
西南科技大学
105

任务需求:

使用矩阵键盘、蜂鸣器、流水灯和电位计设计音乐键盘,让不同按键发出不同音调,电位计用来控制声音大小,流水灯用来显示声音档位。

一:环境配置

1.MSPM0-DevKit:

根据武汉大学汉大学电子设计竞赛实验室设计研发的操作流程,可以很顺利的搭建好用keil来开发MSPM0L系列单片机的开发环境(包括MSPM0 SDK和SysCopnfig软件)。网址:主页 (whu-eis-robotics.github.io)

外:CCS的开发环境适合带有官方DAP的开发板,由于我使用的是keil流以及板载的CMSIS-DAP,所以在此不做赘述。

二:程序实现

程序均使用C语言编写,由于我是32单片机的小白(之前一直是使用51单片机),所以对中断以及Sysconfig软件的配置可能存在错误操作,还请见谅。

1.设计思路:

根据任务需求,该项目实现需要用到矩阵键盘、蜂鸣器、流水灯和电位计等模块,所以需要用到的模块是按键输入模块、ADC模块、蜂鸣器模块、Delay模块和LED模块。

2.硬件框图:


在硬禾学堂的直播课里给出了硬件框图,这里就偷懒一点直接借用了(≧∀≦)ゞ



image.png

3.模块介绍:

1.无源蜂鸣器(PWM)模块:

无源蜂鸣器内部没有激励源,只有给它一定频率的方波信号,才能让蜂鸣器的振动装置起振,从而实现发声,同时,输入的方波频率不同,发出的声音也不同(无源蜂鸣器可以模拟曲调实现音乐效果)

image.png

无源蜂鸣器的原理图


image.png

蜂鸣器使能端口配置

Sysconfig中蜂鸣器使能端口GPIO配置:

1.配置Direction为Output

2.配置Intial Value为Cleared

3.配置PinMux为PA16

4.其余保持默认,保存并更新ti_msp_dl_config.c和ti_msp_dl_config.h文件

5.根据蜂鸣器使能端口的配置,顺手就把LED的也配置好了,如下图:

image.png

LED使能端口配置


代码实现:

uint16_t music[]={13,1,2,3,4,5,6,7,8,9,10,11,12};//音符选择数组
uint16_t tone[] = {247, 262, 294, 330, 349, 392, 440, 494, 523, 587, 659, 698, 784, 1000};//音符频率
uint32_t time_up;//高电平时间
uint32_t time_down;//低电平时间
uint16_t duty;//占空比变量
void Sound(uint16_t frq)
{
uint32_t time;
if (frq != 1000)
{
time = 32000000 / (2 * (uint32_t)frq);
time_up=time*duty/100;
time_down=time*(100-duty)/100;
DL_GPIO_setPins(Beep_PORT, Beep_A16_PIN);//将Beep引脚置1
delay_cycles(time_up); //延时time_up个时钟周期
DL_GPIO_clearPins(Beep_PORT, Beep_A16_PIN);//将Beep引脚置0
delay_cycles(time_down); //延时time_down个时钟周期
}
else
{
delay_cycles(32000);
}
}//蜂鸣器演奏函数

代码说明:

由于不会用可变频率和占空比的硬件PWM波调控,所以就用简单的软件延时对每个音节做了处理,音调通过定义好的频率数组直接选用,响度则通过占空比变量duty调节。

这样我们在main函数中直接调用Sound()函数即可演奏想要演奏的音符

2.按键输入模块:


image.png

矩阵按键原理图

image.png

矩阵按键配置图

来到Sysconfig工具里配置矩阵按键所需要的引脚

1.将H1 H2 H3 H4的Direction设置为Output,Initial Value设置为Cleared

2.将V1 V2 V3 V4的Direction设置为Input

3.最后对应各自的引脚配置好,保存并更新ti_msp_dl_config.c和ti_msp_dl_config.h文件

代码实现:

#include "key_board.h"
int getKeyValue(void)
{
int h_arr[4] = {KEYBOARD_H1_PIN, KEYBOARD_H2_PIN, KEYBOARD_H3_PIN, KEYBOARD_H4_PIN};//纵坐标数组
int v_arr[4] = {KEYBOARD_V1_PIN, KEYBOARD_V2_PIN, KEYBOARD_V3_PIN, KEYBOARD_V4_PIN};//横坐标数组
int i, j = 0;
int key_value = 0;
for (i = 0; i < 4; i++)
{
delay_cycles(1000);

DL_GPIO_setPins(KEYBOARD_PORT, h_arr[i]);
DL_GPIO_clearPins(KEYBOARD_PORT, h_arr[(i + 1) % 4]);
DL_GPIO_clearPins(KEYBOARD_PORT, h_arr[(i + 2) % 4]);
DL_GPIO_clearPins(KEYBOARD_PORT, h_arr[(i + 3) % 4]);

delay_cycles(100000);

for (j = 0; j < 4; j++)
{
if (DL_GPIO_readPins(KEYBOARD_PORT, v_arr[j]) != 0)
{
key_value = j * 4 + i + 1;
}
delay_cycles(1000);
}

delay_cycles(1000);
}

return key_value; // 无输入则返回0
}

代码说明:

偷懒用硬禾学堂的直播课给的封装好的矩阵按键扫描底层驱动,原理的话相信大家都明白(~ ̄▽ ̄)~

在主函数里可以直接调用getKeyValue()函数来获取按键扫描得到的值。

3.ADC模块

image.png


在Sysconfig里配置ADC12

1.将Sample Clock Configuration中的ADC Clock Source设置为ULPCLK 8分频(别的时钟源也行),Conversion Mode为Single 触发方式为软件触发 其余保持默认

2.将Advanced Configuration的Power Down Mode设置为125us,也就是将采样时间维持在125us(我也不知道为啥是125us 好像是硬性要求),其余保持默认

3.将Interrupt Configuration 中的Enable Interrupt设置为MEM0 result lodaed interrupt,这样在ADC完成读取之后可以触发中断,其余保持默认

4.在PinMux中配置ADC12采集的引脚,即PA27,其余保持默认

5.保存之后再将ti_msp_dl_config.c和ti_msp_dl_config.h文件更新


下面来到keil里配置ADC中断服务函数,如下:

/******ADC中断服务函数*********/
void ADC12_0_INST_IRQHandler(void)
{
switch(DL_ADC12_getPendingInterrupt(ADC12_0_INST)){
case DL_ADC12_IIDX_MEM0_RESULT_LOADED:
ADC_Flag = true;
break;
default:
break;
}
}

4.main函数模块

int main(void)
{
SYSCFG_DL_init();//系统初始化
NVIC_EnableIRQ(ADC12_0_INST_INT_IRQN);//ADC中断使能
ADC_Flag = false;//ADC标志位关闭
while (1) {

ADC_Flag = false;
DL_ADC12_startConversion(ADC12_0_INST);//ADC转换开始
while(ADC_Flag == false);//等待ADC转化结束
ADC_Val = DL_ADC12_getMemResult(ADC12_0_INST,ADC12_0_ADCMEM_0);//读取ADC转换结果
DL_ADC12_enableConversions(ADC12_0_INST);//使能ADC转换


V=10*3.3*ADC_Val / 4096;
mode=(unsigned char)V%10;
/*******************LED灯以及蜂鸣器占空比模式******************/
switch(mode){
case 0:
duty=1;
DL_GPIO_setPins(LED_PORT,LED_A8_PIN);
DL_GPIO_setPins(LED_PORT,LED_A9_PIN);
DL_GPIO_setPins(LED_PORT,LED_A10_PIN);
DL_GPIO_setPins(LED_PORT,LED_A11_PIN);
DL_GPIO_setPins(LED_PORT,LED_A2_PIN);
DL_GPIO_setPins(LED_PORT,LED_A3_PIN);
DL_GPIO_setPins(LED_PORT,LED_A15_PIN);
DL_GPIO_setPins(LED_PORT,LED_A4_PIN);
break;
case 1:
duty=2;
DL_GPIO_clearPins(LED_PORT,LED_A8_PIN);
DL_GPIO_setPins(LED_PORT,LED_A9_PIN);
DL_GPIO_setPins(LED_PORT,LED_A10_PIN);
DL_GPIO_setPins(LED_PORT,LED_A11_PIN);
DL_GPIO_setPins(LED_PORT,LED_A2_PIN);
DL_GPIO_setPins(LED_PORT,LED_A3_PIN);
DL_GPIO_setPins(LED_PORT,LED_A15_PIN);
DL_GPIO_setPins(LED_PORT,LED_A4_PIN);
break;
case 2:
duty=3;
DL_GPIO_clearPins(LED_PORT,LED_A8_PIN);
DL_GPIO_clearPins(LED_PORT,LED_A9_PIN);
DL_GPIO_setPins(LED_PORT,LED_A10_PIN);
DL_GPIO_setPins(LED_PORT,LED_A11_PIN);
DL_GPIO_setPins(LED_PORT,LED_A2_PIN);
DL_GPIO_setPins(LED_PORT,LED_A3_PIN);
DL_GPIO_setPins(LED_PORT,LED_A15_PIN);
DL_GPIO_setPins(LED_PORT,LED_A4_PIN);
break;
case 3:
duty=4;
DL_GPIO_clearPins(LED_PORT,LED_A8_PIN);
DL_GPIO_clearPins(LED_PORT,LED_A9_PIN);
DL_GPIO_clearPins(LED_PORT,LED_A10_PIN);
DL_GPIO_setPins(LED_PORT,LED_A11_PIN);
DL_GPIO_setPins(LED_PORT,LED_A2_PIN);
DL_GPIO_setPins(LED_PORT,LED_A3_PIN);
DL_GPIO_setPins(LED_PORT,LED_A15_PIN);
DL_GPIO_setPins(LED_PORT,LED_A4_PIN);
break;
case 4:
duty=5;
DL_GPIO_clearPins(LED_PORT,LED_A8_PIN);
DL_GPIO_clearPins(LED_PORT,LED_A9_PIN);
DL_GPIO_clearPins(LED_PORT,LED_A10_PIN);
DL_GPIO_clearPins(LED_PORT,LED_A11_PIN);
DL_GPIO_setPins(LED_PORT,LED_A2_PIN);
DL_GPIO_setPins(LED_PORT,LED_A3_PIN);
DL_GPIO_setPins(LED_PORT,LED_A15_PIN);
DL_GPIO_setPins(LED_PORT,LED_A4_PIN);
break;
case 5:
duty=6;
DL_GPIO_clearPins(LED_PORT,LED_A8_PIN);
DL_GPIO_clearPins(LED_PORT,LED_A9_PIN);
DL_GPIO_clearPins(LED_PORT,LED_A10_PIN);
DL_GPIO_clearPins(LED_PORT,LED_A11_PIN);
DL_GPIO_clearPins(LED_PORT,LED_A2_PIN);
DL_GPIO_setPins(LED_PORT,LED_A3_PIN);
DL_GPIO_setPins(LED_PORT,LED_A15_PIN);
DL_GPIO_setPins(LED_PORT,LED_A4_PIN);
break;
case 6:
duty=7;
DL_GPIO_clearPins(LED_PORT,LED_A8_PIN);
DL_GPIO_clearPins(LED_PORT,LED_A9_PIN);
DL_GPIO_clearPins(LED_PORT,LED_A10_PIN);
DL_GPIO_clearPins(LED_PORT,LED_A11_PIN);
DL_GPIO_clearPins(LED_PORT,LED_A2_PIN);
DL_GPIO_clearPins(LED_PORT,LED_A3_PIN);
DL_GPIO_setPins(LED_PORT,LED_A15_PIN);
DL_GPIO_setPins(LED_PORT,LED_A4_PIN);
break;
case 7:
duty=8;
DL_GPIO_clearPins(LED_PORT,LED_A8_PIN);
DL_GPIO_clearPins(LED_PORT,LED_A9_PIN);
DL_GPIO_clearPins(LED_PORT,LED_A10_PIN);
DL_GPIO_clearPins(LED_PORT,LED_A11_PIN);
DL_GPIO_clearPins(LED_PORT,LED_A2_PIN);
DL_GPIO_clearPins(LED_PORT,LED_A3_PIN);
DL_GPIO_clearPins(LED_PORT,LED_A15_PIN);
DL_GPIO_setPins(LED_PORT,LED_A4_PIN);
break;
case 8:
duty=9;
DL_GPIO_clearPins(LED_PORT,LED_A8_PIN);
DL_GPIO_clearPins(LED_PORT,LED_A9_PIN);
DL_GPIO_clearPins(LED_PORT,LED_A10_PIN);
DL_GPIO_clearPins(LED_PORT,LED_A11_PIN);
DL_GPIO_clearPins(LED_PORT,LED_A2_PIN);
DL_GPIO_clearPins(LED_PORT,LED_A3_PIN);
DL_GPIO_clearPins(LED_PORT,LED_A15_PIN);
DL_GPIO_clearPins(LED_PORT,LED_A4_PIN);
break;
default:
duty=duty;
break;
}
key=getKeyValue();//按键读取
for(i=0;i<=300;i++)
{
Sound(tone[music[key]]); //演奏音符
}
}
}

代码说明:在主函数里配置好ADC12中断的开启和转换顺序,并将采样的值用变量V存储,然后因为是8个LED一共9种显示模式,所以就将电压VDD(3.3v)的十分位作为模式的选择,用switch语句分为九种模式,LED亮的数量和duty的值逐级递增,接下来读取按键的值,最后调用Sound()函数演奏音符

三、实现的功能及图片展示



2602c87fffba2cd2f5d2ba1bb998b816.jpg

如上图可以看出,在LED亮一个的情况下(模式1),占空比约为2.111%


9c11b4cb83f2edf18831607d6ba15d2c.jpeg


如上图可以看出,在LED亮两个的情况下(模式2),占空比约为3.064%


a4991a6048da2a5b2f25673c3aec08ed.jpg

如上图可以看出,在LED亮三个的情况下(模式3),占空比约为3.911%

6ad08c3e5fbbb8a6474081a509b1db41.jpeg


如上图可以看出,在LED亮八个的情况下(模式8),占空比约为8.914%


当然还有其它测量值,这里只验证了音符4(fa)的频率,其他均可通过代码类比验证


由于只设置了14个音符,所以从S1到S13有声音(S14为高音音符不发音 我没学过音乐我也不懂),S15和S16为空按键


四、后记

给的底板的PCB没咋看懂,反正跳线帽接下方3 4引脚的时候LED指示灯亮了,看了看PCB发现蜂鸣器的PA15跟LED6的重了,所以索性将PA16飞线到蜂鸣器使能端,就不用设计中断,虽然有点不太美观,但是实用省事(‾◡◝)

做完之后才发现PWM有个中断服务函数可以调整占空比和频率,具体在TI库函数的说明里,由于时间比较紧所以也没时间改进代码了。

下面是我收集的Sysconfig和MSPM0的使用指南

MSPM0 工具指南 — MSPM0 Tools Guide 2.01.00.03 documentation---MSPM0工具指南

MSPM0 SDK 示例指南 — MSPM0 SDK User Guide 2.01.00.03 documentation---MSPM0 SDK示例指南

software-dl.ti.com/msp430/esd/MSPM0-SDK/latest/docs/chinese/MSPM0_SDK_Documentation_Overview_CN.html---MSPM0 SDK 概述

附件下载
ADC.zip
项目代码
团队介绍
个人
团队成员
谢庥泉
评论
0 / 100
查看更多
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号