本次要实现的是利用ZYNQ-7000板子上自带的16个矩阵键盘来实现每按一次发出一个音符的声音,从而实现电子琴的效果。

一、原理部分:

矩阵键盘:用8位来存储4*4的矩阵键盘的信息,初始化的值为00001111;

先将高四位设为输出,低四位设为输入,当第一行有按键按下时 ,第四位相应的位值变为0,但此时只能判定是哪一行的按键被按下,并不能判断具体是哪一个。

将高四位设为输入,低四位设为输出,对应的那一列的值变为1,从而获取按下的按键值。

扬声器发生原理:计算机中发声普遍使用的是MIDI音乐,MIDI不是具体发声的部件,只是编码来驱动相应发声器件发声的。

本实验中发声的端口是DR6,DR6输出高电平会按照最高频率发声,按照固定频率输出高低电平会发出固定频率的声音。

为了得到准确的频率就需要能够准确的定时得到T/2的时间间隔,DR6在每次定时时间到之后将输出反向,即可得到准确的方波脉冲也就可以得到准确的发声频率。

音乐基本理论:

(1)音调:

音阶为等比数列,即下一个音阶高八度的频率是上一个音阶频率的两倍。国际标准音规定,钢琴的a1的频率是为440Hz;
又规定每相邻半音的频率比值为2^1/12≈1.059463,根据这规定,就可以得出钢琴上每一个琴键音的频率。简谱中的1、2、3、4、5、6、7对应钢琴琴键C、D、E、F、G、A、B由于每个音阶是12个半音所以这七个音调之间不全是半音关系即频率倍数为1.059463,只有E和F以及B和C1之间是半音关系即1.059463倍频率。C、D、E之间会有两个半音标记为#C和#DF、G、A、B之间会有三个半音标记为#F、#G和#A。

(2)节拍:在MIDI演奏中小节可以不作为参数考虑进程序中,而一拍的时间长度由演奏速度决定,如每分钟60拍则代表每拍时长1s。采用定时器进行精确定时用以产生对应频率的声音根据节奏和演奏速度决定某个频率的声音要演奏的时间长度每一个演奏的频率不能占100%的时长应该有间隔用来模仿人演奏时的音符间隔。

二、VIvado部分:

块设计部分:用一个一位的AXI GPIO来接发声的口(设为输出),用一个8位的AXI GPIO的IP来连接矩阵键盘(双向的),允许中断连接到PS上->创建HDL Wrapper->综合->配置管脚约束->生成bit流->export hardware->启动SDK。

I/O ports 在初始化设定pull type时,pullup 默认值为1,pulldown默认值为0,根据我们的设定,我们要将初始化值设为相反的即不输出信号的值。

三、代码实现部分(详解):

钢琴琴键:

#include "xil_exception.h"
//System Timer Tick
#define TIMER_FRQ 333333333          //计数频率同时也是1s的周期数
//钢琴琴键10倍频率,每秒中断间隔次数的5倍
#define KEY_A2   275
#define KEY_SA2  291
#define KEY_B2   309
#define KEY_C1   327
#define KEY_SC1  346
#define KEY_D1   367
#define KEY_SD1  389
#define KEY_E1   412
#define KEY_F1   437
#define KEY_SF1  462
#define KEY_G1   490
#define KEY_SG1  519
#define KEY_A1   550
#define KEY_SA1  583
#define KEY_B1   617
#define KEY_C    654
#define KEY_SC   693
#define KEY_D    734
#define KEY_SD   778
#define KEY_E    824
#define KEY_F    873
#define KEY_SF   925
#define KEY_G    980
#define KEY_SG   1038
#define KEY_A    1100
#define KEY_SA   1165
#define KEY_B    1235
#define KEY_c    1308
#define KEY_Sc   1386
#define KEY_d    1468
#define KEY_Sd   1556
#define KEY_e    1648
#define KEY_f    1746
#define KEY_Sf   1850
#define KEY_g    1960
#define KEY_Sg   2077
#define KEY_a    2200
#define KEY_Sa   2331
#define KEY_b    2469
#define KEY_c1   2616      //中音哆
#define KEY_Sc1  2772
#define KEY_d1   2937
#define KEY_Sd1  3111
#define KEY_e1   3296
#define KEY_f1   3492
#define KEY_Sf1  3700
#define KEY_g1   3920
#define KEY_Sg1  4153
#define KEY_a1   4400
#define KEY_Sa1  4662
#define KEY_b1   4939
#define KEY_c2   5233
#define KEY_Sc2  5544
#define KEY_d2   5873
#define KEY_Sd2  6223
#define KEY_e2   6593
#define KEY_f2   6985
#define KEY_Sf2  7400
#define KEY_g2   7840
#define KEY_Sg2  8306
#define KEY_a2   8800
#define KEY_Sa2  9323
#define KEY_b2   9878
#define KEY_c3   10470
#define KEY_Sc3  11090
#define KEY_d3   11750
#define KEY_Sd3  12450
#define KEY_e3   13190
#define KEY_f3   13970
#define KEY_Sf3  14800
#define KEY_g3   15680
#define KEY_Sg3  16610
#define KEY_a3   17600
#define KEY_Sa3  18650
#define KEY_b3   19760
#define KEY_c4   20930
#define KEY_Sc4  22170
#define KEY_d4   23490
#define KEY_Sd4  24890
#define KEY_e4   26370
#define KEY_f4   27940
#define KEY_Sf4  29600
#define KEY_g4   31360
#define KEY_Sg4  33220
#define KEY_a4   35200
#define KEY_Sa4  37290
#define KEY_b4   39510
#define KEY_c5   41860
#define KEY_NOP  0
/** //几分音符*/
#define Beat_1Q1   1
#define Beat_1Q2   2
#define Beat_1Q4   4
#define Beat_1Q8   8
#define Beat_1Q16 16
//Musical note
typedef struct Music_Note
{u32 Note;u8  Beat;float Dotted;
} Muc_N;

初始化两个GPIO的配置以及初始化定时器的代码解析部分在这里就省略了。

当有键按下时允许全局中断,并且将flag值设为1.

void GpioHandler(void *CallbackRef)
{
XGpio *GpioPtr = (XGpio *)CallbackRef;
XGpio_InterruptGlobalDisable(&Gpio_BTN);
XGpio_InterruptClear(&Gpio_BTN,INTR_MASK);
ReadData=XGpio_DiscreteRead(&Gpio_BTN, LEDS_CHANNEL)&0xF;
if((ReadData&0xF)!=0xF)
{
IntrFlag=1;//全局变量
}
else
{
IntrFlag=0;
XGpio_InterruptGlobalEnable(&Gpio_BTN);
}
}

按下矩阵键盘,获取按下键的值:


XGpio_SetDataDirection(&Gpio_BTN, SEG7_CHANNEL, LOW4_MASK); //将独立按键的4位引脚设置为输入
XGpio_SetDataDirection(&Gpio_BTN, LEDS_CHANNEL, LOW4_MASK); //将矩阵键盘的8位分为高四位输出低四位输入
XGpio_DiscreteWrite(&Gpio_BTN, LEDS_CHANNEL, LOW4_MASK); //在高四位输出'0'
for(int i=0;i<Delay_Time;i++);//延迟再读一次
u8 GpioData=XGpio_DiscreteRead(&Gpio_BTN, LEDS_CHANNEL)&0xF;
u8 Leds_Group;
if(GpioData==ReadData) //如果两次读的一样,就说明是按键而不是抖动产生的,如果不相等则要滤掉
{
XGpio_SetDataDirection(&Gpio_BTN, LEDS_CHANNEL, 0XF0); //将矩阵键盘的8位分为高四位输入低四位输出
XGpio_DiscreteWrite(&Gpio_BTN, LEDS_CHANNEL, 0XF0); //在低四位输出'1'
GpioData=XGpio_DiscreteRead(&Gpio_BTN,LEDS_CHANNEL)&0xF0;

针对每一个按键发出一个音符:

switch(ReadData& 0x0F)
{
case 0x0E:
switch(GpioData& 0xF0)
{
case 0xE0:
LoadIntTime(KEY_c2,Beat_1Q4,0.9);//根据音符设定相应的定时器频率
XScuTimer_LoadTimer(&TimerInstance, ReloadTime);//重装定时器
XScuTimer_Start(&TimerInstance);//启动定时器
/*后面的十五种情况省略*/
}
}

根据设定的音符和节拍,计算出定时器中断的频率以及停止时间:

void LoadIntTime(u32 Note_Play,u8 Beat_Play,float Dotted_Play)
{ReloadTime=CalReloadTime(Note_Play,Beat_Play,Dotted_Play);TimerExpired=CalIntTime(ReloadTime,Beat_Play,Dotted_Play);//ReloadTime=0xFFFFFFFF-100;
}static u32 CalReloadTime(u32 Note_Play,u8 Beat_Play,float Dotted_Play)
{if(Note_Play!=KEY_NOP){return TIMER_FRQ/Note_Play*5*Dotted_Play;}else{return TIMER_FRQ/Beat_Play/PLAY_SPEED*MUS_BEAT*60*Dotted_Play;//}}
static u32 CalIntTime(u32 Note_Play,u8 Beat_Play,float Dotted_Play)
{return CalReloadTime(0,Beat_Play,Dotted_Play)/Note_Play;
}

定时器处理函数,并输出扬声器电平,当TimerExpired减为0时将定时器停下来:

static void TimerIntrHandler(void *CallbackRef)
{
XScuTimer *TimerInstancePtr = (XScuTimer *) CallbackRef;
if (XScuTimer_IsExpired(TimerInstancePtr))
{
XScuTimer_ClearInterruptStatus(TimerInstancePtr);
TimerExpired--;WriteData=~WriteData;if(TimerExpired==0)//该音符已经演奏完毕{XScuTimer_Stop(&TimerInstance);WriteData=0;}XGpio_DiscreteWrite(&Gpio_DEV, 1, WriteData);//输出扬声器电平}
}

基于ZYNQ的嵌入式学习笔记四(矩阵键盘实现电子琴)相关推荐

  1. C51单片机学习笔记之矩阵键盘

    简介 矩阵键盘一般为4×4或4×3的.矩阵键盘的判断方式分按行扫描和按列扫描. 简单说就是给全体一个高电平,然后给一个按键的一端附上低电平,再判断另一端是否为低电平. 原理图 代码部分 #includ ...

  2. 基于STM32G431嵌入式学习笔记——七、定时器定时

    一.题目引入 上述为第13届蓝桥杯省赛节选内容,为了研究定时器的机理并独立书写计时函数,上述内容简化为以下要求: ①按下B4按键,LD1点亮5s后熄灭 ②按下B3按键,LD2以0.1秒为间隔切换亮灭状 ...

  3. 嵌入式学习笔记——基于Cortex-M的单片机介绍

    基于Cortex-M的单片机介绍 前言 1生产厂商及其产品线 1.1ARM单片机的产品线 1.2命名规则 作业1 2习单片机的资料准备 2.1STM32开发所需手册 2.1.1芯片的数据手册 芯片基本 ...

  4. 嵌入式学习笔记——ADC模数转换器

    ADC模数转换器 前言 ADC介绍 ADC概述 ADC的数量 ADC的特性 ADC框图 芯片外部框图 芯片内部框图 转换部分框图 状态输出部分 条件触发框图 寄存器介绍 编程思路 模式选择 规则通道的 ...

  5. 嵌入式学习笔记——STM32的USART通信概述

    文章目录 前言 常用通信协议分类及其特征介绍 通信协议 通信协议分类 1.同步异步通信 2.全双工/半双工/单工 3.现场总线/板级总线 4. 串行/并行通信 5. 有线通信.无线通信 STM32通信 ...

  6. 吴恩达《机器学习》学习笔记四——单变量线性回归(梯度下降法)代码

    吴恩达<机器学习>学习笔记四--单变量线性回归(梯度下降法)代码 一.问题介绍 二.解决过程及代码讲解 三.函数解释 1. pandas.read_csv()函数 2. DataFrame ...

  7. 嵌入式学习笔记——使用寄存器编程操作GPIO

    使用寄存器编程操作GPIO 前言 GPIO相关的寄存器 GPIO 端口模式寄存器 (GPIOx_MODER) (x = A..I) 位操作 GPIO 端口输出类型寄存器 (GPIOx_OTYPER) ...

  8. 华清远见fs4412开发板学习笔记(四)

    fs4412开发板学习笔记(四) 今天的课程安排 1.复习 1.1 VIM 编辑器 [1] vim + filename 打开 [2] 工作模式 命令模式 编辑模式 底行模式 [3] 模式切换 命令- ...

  9. 【http学习笔记四】安全篇

    [http学习笔记四]安全篇 文章目录 [http学习笔记四]安全篇 一.HTTPS 与 SSL/TLS ① 什么是安全? 机密性 完整性 身份认证 不可否认 ② 什么是HTTPS? ③ SSL/TL ...

最新文章

  1. 基于Kaggle的图像分类(CIFAR-10)
  2. python下的scripts有什么用_python安装后无scripts内文件,无法使用pip
  3. python直接使用pyc_Pyc和pyo是怎样一种存在?
  4. 减小程序规模!稀疏数组Sparsearray,数据结构二维数组与稀疏数组转换,Java实现
  5. 初始化环境配置:CentOS 7.4x64 系统安装及基础配置
  6. m3u8合并mp4软件_m3u8格式转mp4究极办法!
  7. Ubuntu-16.04安装Xdebug-2.2.5及相关介绍
  8. Java应用程序的基本框架
  9. NIO与零拷贝和AIO
  10. SQL开发技巧(二) 【转】感觉他写的很好
  11. 简述直方图和柱形图的区别_如何区分直方图与柱形图
  12. XSS后台敏感操作(审计思路实现)
  13. C++模版类List实现
  14. atitit.产品console 日志的aticonsole 方案处理总结
  15. 编程珠玑:位图法排序
  16. 单片机:红外遥控实验(内含红外遥控介绍+硬件原理+软件编程+配置环境)
  17. linux跨平台通信软件,下载:跨平台即时通信工具Pidgin 2.7.5
  18. CF643D Bearish Fanpages
  19. 【Keil C51单片机延时程序】
  20. doevents raiseevent withevents

热门文章

  1. 外仁内圣,以借得天下,以情御英雄
  2. Adobe After Effect (AE) cc2020 安装教程【64位】
  3. TIA博途WinCC PRO中通过脚本控制图层的显示和隐藏
  4. 安装出错:Command line option syntax error.Type Command /? for help.解决方案
  5. STM32F4系列ADC最大转换速率及操作条件(以STM32F407ZGT6为例)
  6. Android应用测试方法总结
  7. 五个最好的DVD播放器下载
  8. 技术总监被开除了....
  9. 网站系统开发公司分析
  10. idea链接阿里云服务器数据库