写在前面:
由于时间的不足与学习的碎片化,写博客变得有些奢侈。
但是对于记录学习(忘了以后能快速复习)的渴望一天天变得强烈。
既然如此
不如以天为单位,以时间为顺序,仅仅将博客当做一个知识学习的目录,记录笔者认为最通俗、最有帮助的资料,并尽量总结几句话指明本质,以便于日后搜索起来更加容易。

标题的结构如下:“类型”:“知识点”——“简短的解释”
部分内容由于保密协议无法上传。

点击此处进入学习日记的总目录

2023.4.22

  • 一、51:蜂鸣器
  • 二、51:按键控制蜂鸣器发声时间
  • 三、51:蜂鸣器唱歌——天空之城

一、51:蜂鸣器

  1. 蜂鸣器介绍

  2. 驱动电路

  3. ULN2003


  4. 音符对照频率

  5. 常见乐谱

二、51:按键控制蜂鸣器发声时间

  1. 寻找引脚,很显然是P25

  2. 编写延时函数
    为了实现震荡,首先编写一个延时函数,保证定时震荡
/*** @brief  蜂鸣器私有延时函数,延时500us* @param  无* @retval 无*/
void Buzzer_Delay500us()        //@12.000MHz
{unsigned char i;_nop_();i = 247;while (--i);
}
  1. 编写发声函数
    让蜂鸣器在规定时间内发声
//蜂鸣器端口:
sbit Buzzer=P2^5;/*** @brief  蜂鸣器发声* @param  ms 发声的时长,范围:0~32767* @retval 无*/
void Buzzer_Time(unsigned int ms)
{unsigned int i;for(i=0;i<ms*2;i++){Buzzer=!Buzzer;Buzzer_Delay500us();}
}

Buzzer_Time 函数中,有一个循环语句 for(i=0;i<ms*2;i++)。这个循环的目的是让蜂鸣器在指定的时长内产生一系列的脉冲
当调用 Buzzer_Time(1000) 时,参数 ms 的值为 1000,表示要让蜂鸣器响 1000 毫秒(1秒)的时间。
在循环中,i 的初始值为 0,每次循环结束后 i 增加 1。由于循环条件是 i<ms*2,也就是 i<2000,所以这个循环将会执行 2000次。
在循环体内部,首先会对蜂鸣器状态进行取反,然后调用Buzzer_Delay500us()函数延时约为 500 微秒。因此,每次循环会产生一次蜂鸣器的状态变化和延时,持续约为 500 微秒。
总共有 2000 次循环,所以总的延时时间为 2000 * 500 微秒 = 1000 毫秒(1秒)

三、51:蜂鸣器唱歌——天空之城

  1. 自动重装载值(TH0TL0)计算
    在8051系列微控制器中,定时器0的自动重装载值(Auto-Reload Value)用于设置定时器的计时初值。定时器0是一个8位定时器,其计时范围为0到255。当定时器0计数器达到255时,会自动将计数器重置为自动重装载值,并触发定时器0溢出中断。
计算自动重装载值(TH0,TL0)的方法取决于所需的定时周期和时钟频率。下面是一个示例计算步骤:- 确定所需的定时周期。假设我们希望定时器0每1毫秒触发一次中断。
- 确定系统的时钟频率。假设系统的时钟频率为12.000MHz。
- 计算定时器的计数周期。由于定时器0是一个8位定时器,它可以计数的最大值为255。因此,定时器的计数周期为256。
- 计算所需的计数值。要实现1毫秒的定时周期,需要确定每个计数周期的时间。根据时钟频率和计数周期,可以计算出每个计数周期的时间(以秒为单位)。
- 计数周期时间 = 1 / (时钟频率 / 计数周期) = 1 / (12.000MHz / 256) ≈ 21.33纳秒
注意:以上计算假设时钟频率和计数周期的单位相同,如均为Hz或均为MHz。
- 计算自动重装载值。根据所需的定时周期,可以计算出需要多少个计数周期才能达到该定时周期。然后,将计数周期数减去1,得到自动重装载值。根据上面的例子,每毫秒需要多少个计数周期:
计数周期数 = (1毫秒 / 计数周期时间) = (1毫秒 / 21.33纳秒) ≈ 46882个计数周期
自动重装载值 = 计数周期数 - 1 ≈ 46882 - 1 = 46881
- 因为定时器0是一个8位定时器,所以自动重装载值需要分为高8位(TH0)和低8位(TL0)。
TH0 = 自动重装载值 / 256 = 46881 / 256 ≈ 183
TL0 = 自动重装载值 % 256 = 46881 % 256 = 105

这样,将TH0设置为183,TL0设置为105,定时器0将每1毫秒触发一次中断。

  1. 计算各频率重装载值
//音符与索引对应表,P:休止符,L:低音,M:中音,H:高音,下划线:升半音符号#
#define P   0
#define L1  1
#define L1_ 2
#define L2  3
#define L2_ 4
#define L3  5
#define L4  6
#define L4_ 7
#define L5  8
#define L5_ 9
#define L6  10
#define L6_ 11
#define L7  12
#define M1  13
#define M1_ 14
#define M2  15
#define M2_ 16
#define M3  17
#define M4  18
#define M4_ 19
#define M5  20
#define M5_ 21
#define M6  22
#define M6_ 23
#define M7  24
#define H1  25
#define H1_ 26
#define H2  27
#define H2_ 28
#define H3  29
#define H4  30
#define H4_ 31
#define H5  32
#define H5_ 33
#define H6  34
#define H6_ 35
#define H7  36//索引与频率对照表
unsigned int FreqTable[]={0,63628,63731,63835,63928,64021,64103,64185,64260,64331,64400,64463,64528,   //低频64580,64633,64684,64732,64777,64820,64860,64898,64934,64968,65000,65030,    //中频65058,65085,65110,65134,65157,65178,65198,65217,65235,65252,65268,65283,    //高频
};
  1. 编写 定时器0初始化函数Timer0Init()
/*** @brief  定时器0初始化,1毫秒@12.000MHz* @param  无* @retval 无*/
void Timer0Init(void)
{TMOD &= 0xF0;       // 设置定时器模式TMOD |= 0x01;       // 设置定时器模式TL0 = 0x18;         // 设置定时初值TH0 = 0xFC;         // 设置定时初值TF0 = 0;            // 清除TF0标志TR0 = 1;            // 定时器0开始计时ET0 = 1;            // 允许定时器0中断EA = 1;             // 允许总中断PT0 = 0;            // 设置定时器0中断优先级
}
  • TMOD &= 0xF0;TMOD |= 0x01;:对定时器模式寄存器 TMOD 进行设置,保留高4位的原值,并将低4位设置为 0x01,表示将定时器0设置为 16位工作模式。
  • TL0 = 0x18;TH0 = 0xFC;:分别设置 Timer0 的低字节和高字节的初始值,用于定时器的计时。这些值根据具体的时钟频率和所需的定时周期进行设置。(因为后面会设置,所以此处初始化不存在影响)
  • TF0 = 0;:清除 Timer0 溢出标志,确保 Timer0 中断开始时不会立即触发。
  • TR0 = 1;:启动定时器0,开始计时。
  • ET0 = 1;:允许定时器0中断,使得当定时器0溢出时可以触发中断服务函数。
  • EA = 1;:允许总中断,使得系统能够响应所有中断请求。
  • PT0 = 0;:设置定时器0中断的优先级为最低优先级。

这个函数的目的是将定时器0设置为一个 1 毫秒的定时器,在12.000MHz的时钟频率下工作。通过设置相应的寄存器值和使能中断,可以让定时器0每隔1毫秒触发一次中断,从而执行相应的中断服务函数。

  1. 编写 Timer0 中断的处理函数
void Timer0_Routine() interrupt 1
{if (FreqTable[FreqSelect])    // 如果不是休止符{/* 取对应频率值的重装载值到定时器 */TL0 = FreqTable[FreqSelect] % 256;    // 设置定时初值TH0 = FreqTable[FreqSelect] / 256;    // 设置定时初值Buzzer = !Buzzer;    // 翻转蜂鸣器IO口}
}
  • void Timer0_Routine() interrupt 1:定义了一个中断服务函数 Timer0_Routine(),其中 interrupt 1 表示这个函数是针对中断号 1 的处理函数。
  • if (FreqTable[FreqSelect]):检查 FreqTable[FreqSelect] 的值是否非零,判断是否为休止符(停止蜂鸣器发声)。
  • TL0 = FreqTable[FreqSelect] % 256;TH0 = FreqTable[FreqSelect] / 256;:将 FreqTable[FreqSelect] 的值分别赋值给 TL0 和 TH0 寄存器,以设置定时器的计时初值。其中 % 表示取余操作,/ 表示整数除法。
  • Buzzer = !Buzzer;:通过对 Buzzer 变量进行逻辑取反操作,翻转蜂鸣器的 I/O 口状态。这样可以在定时器中断中控制蜂鸣器的开关,实现发声效果。

该中断服务函数根据 FreqTable[FreqSelect] 的值来控制蜂鸣器的频率和发声,当 FreqTable[FreqSelect] 不为零时,定时器的初值被设置为对应的频率值,同时蜂鸣器的 I/O 口状态被翻转,从而控制蜂鸣器的发声和停止。具体的频率和频率表的定义和赋值在代码中并未给出,你可以根据具体的需求自行添加或修改相应的代码。

  1. 编写main函数
//播放速度,值为四分音符的时长(ms)
#define SPEED   500unsigned char FreqSelect,MusicSelect;void main()
{Timer0Init();while(1){if(Music[MusicSelect]!=0xFF)    //如果不是停止标志位{FreqSelect=Music[MusicSelect]; //选择音符对应的频率MusicSelect++;Delay(SPEED/4*Music[MusicSelect]);   //选择音符对应的时值MusicSelect++;TR0=0;Delay(5); //音符间短暂停顿TR0=1;}else   //如果是停止标志位{TR0=0;while(1);}}
}

在这段代码中,main()函数是程序的主要入口点。以下是main()函数的解释:

  • 首先调用Timer0Init()函数进行定时器0的初始化,该函数会设置定时器0的模式、初值和使能定时器0的中断。
  • 进入一个无限循环(while(1)),用于不断地处理音乐的播放。
  • 在循环中,首先判断当前乐谱位置是否为终止标志(Music[MusicSelect] != 0xFF)。如果不是终止标志,说明还有音符需要播放。
  • 获取当前音符对应的频率值,通过读取Music数组中的值(FreqSelect = Music[MusicSelect])。然后递增MusicSelect的值,用于指向这一个音符的持续时间。
  • 使用延迟函数(Delay(SPEED/4*Music[MusicSelect]))来延迟一段时间,时长为当前音符的时值乘以播放速度。这个延迟函数是根据乐谱中音符的时值来控制音符的持续时间。
  • 在播放一个音符之前,先停止定时器0(TR0 = 0),然后进行一个短暂的停顿(Delay(5)),再重新启动定时器0(TR0 = 1)。这样可以在音符之间产生一个短暂的停顿,以区分不同音符。
  • 如果当前乐谱位置是终止标志,表示音乐播放完毕,这时停止定时器0(TR0 = 0),然后进入一个无限循环(while(1)),程序会一直停在这个循环中。

通过以上步骤,main()函数实现了根据乐谱数组中的音符和时值控制蜂鸣器的播放。不断循环播放乐谱中的音符,直到遇到终止标志为止。

  1. 定义乐谱即可
    这里放一首天空之城

//乐谱
unsigned char code Music[]={//音符,时值,//1P,  4,P,    4,P,    4,M6,   2,M7,   2,H1,   4+2,M7,    2,H1,   4,H3,   4,M7,   4+4+4,M3, 2,M3,   2,//2M6,    4+2,M5,    2,M6, 4,H1, 4,M5,   4+4+4,M3, 4,M4,   4+2,M3,    2,M4,   4,H1,   4,//3M3,    4+4,P, 2,H1,   2,H1,   2,H1,   2,M7,   4+2,M4_,2,M4_,4,M7,    4,M7,   8,P,    4,M6,   2,M7,   2,//4H1,    4+2,M7,    2,H1,   4,H3,   4,M7,   4+4+4,M3, 2,M3,   2,M6,   4+2,M5,    2,M6, 4,H1, 4,//5M5,    4+4+4,M2, 2,M3,   2,M4,   4,H1,   2,M7,   2+2,H1,    2+4,H2,    2,H2,   2,H3,   2,H1,   2+4+4,//6H1,  2,M7,   2,M6,   2,M6,   2,M7,   4,M5_,4,M6, 4+4+4,H1, 2,H2,   2,H3,   4+2,H2,    2,H3,   4,H5,   4,//7H2,    4+4+4,M5, 2,M5,   2,H1,   4+2,M7,    2,H1,   4,H3,   4,H3,   4+4+4+4,//8M6,   2,M7,   2,H1,   4,M7,   4,H2,   2,H2,   2,H1,   4+2,M5,    2+4+4,H4, 4,H3,   4,H3,   4,H1,   4,//9H3,    4+4+4,H3, 4,H6,   4+4,H5,    4,H5,   4,H3,   2,H2,   2,H1,   4+4,P, 2,H1,   2,//10H2,   4,H1,   2,H2,   2,H2,   4,H5,   4,H3,   4+4+4,H3, 4,H6,   4+4,H5,    4+4,//11H3,    2,H2,   2,H1,   4+4,P, 2,H1,   2,H2,   4,H1,   2,H2,   2+4,M7,    4,M6,   4+4+4,P,  4,0xFF  //终止标志
};

(学习日记)2023.4.22相关推荐

  1. 【学习日记2023.4.25】之 前后端分离_前端工程化_Vue组件库Element_Vue路由_打包部署

    文章目录 1. 前后台分离开发 1.1 前后台分离开发介绍 1.2 YAPI 1.2.1 YAPI介绍 1.2.2 接口文档管理 2. 前端工程化 2.1 前端工程化介绍 2.2 前端工程化入门 2. ...

  2. 【学习日记2023.4.9】之释放资源的方式_编解码_字符流(Reader/Writer)及其子类_转换流( [In/Out]putStreamReader)

    文章目录 1. 释放资源的方式 1.1 try-catch-finally 1.1.1 finally 格式 特点 执行时机 1.1.2 处理IO流中的异常 1.2 try-with-resource ...

  3. 【学习日记2023.5.8】之 springboot案例之登录功能(会话技术_JWT令牌_过滤器_拦截器)

    文章目录 1. 案例-登录认证 1. 1登录功能 1.1.1 需求 1.1.2 接口文档 1.1.3 思路分析 1.1.4 功能开发 1.1.5 测试 1.1.6 全后端联调 1.2 登录校验 1.2 ...

  4. 【学习日记2023.6.9】之 SpringCloud入门(认识微服务_服务拆分和远程调用RestTemplate_Eureka注册中心_Ribbon负载均衡_Nacos注册中心)

    文章目录 SpringCloud 1. 认识微服务 1.1 单体架构 1.2 分布式架构 1.3 微服务 1.4 SpringCloud 1.5 总结 2. 服务拆分和远程调用 2.1 服务拆分原则 ...

  5. (学习日记)2023.06.07

    写在前面: 由于时间的不足与学习的碎片化,写博客变得有些奢侈. 但是对于记录学习(忘了以后能快速复习)的渴望一天天变得强烈. 既然如此 不如以天为单位,以时间为顺序,仅仅将博客当做一个知识学习的目录, ...

  6. (学习日记)2023.04.28

    写在前面: 由于时间的不足与学习的碎片化,写博客变得有些奢侈. 但是对于记录学习(忘了以后能快速复习)的渴望一天天变得强烈. 既然如此 不如以天为单位,以时间为顺序,仅仅将博客当做一个知识学习的目录, ...

  7. (学习日记)2023.04.25

    写在前面: 由于时间的不足与学习的碎片化,写博客变得有些奢侈. 但是对于记录学习(忘了以后能快速复习)的渴望一天天变得强烈. 既然如此 不如以天为单位,以时间为顺序,仅仅将博客当做一个知识学习的目录, ...

  8. (学习日记)2023.04.26

    写在前面: 由于时间的不足与学习的碎片化,写博客变得有些奢侈. 但是对于记录学习(忘了以后能快速复习)的渴望一天天变得强烈. 既然如此 不如以天为单位,以时间为顺序,仅仅将博客当做一个知识学习的目录, ...

  9. (学习日记)2023.04.24

    写在前面: 由于时间的不足与学习的碎片化,写博客变得有些奢侈. 但是对于记录学习(忘了以后能快速复习)的渴望一天天变得强烈. 既然如此 不如以天为单位,以时间为顺序,仅仅将博客当做一个知识学习的目录, ...

最新文章

  1. 三个可能会被计算机视觉技术改变的行业
  2. 搭建Ubuntu18.04+Anaconda3.x+Pycharm+SimpleITK(三)
  3. 操作系统原理第五章:CPU调度
  4. 【Android Gradle 插件】ProductFlavor 配置 ( multiDexEnabled 配置 | multiDexKeepFile | multiDexKeepProguard )
  5. ktt算法 约化_推荐系统的多目标优化(4)-PE-LTR
  6. 1660s功耗多少w_1660显卡要多大电源?GTX1660用多大电源合适
  7. 脑电图伪差去除matlab,脑电图伪差的识别方法.ppt
  8. 基于GPU的K-Means聚类算法
  9. 随堂小测冲刺.第19天
  10. 如何做好一位合格qc_如何管理好生产质量?
  11. mysql NOW,CURRENT_TIMESTAMP,SYSDATE 之间的区别
  12. macOS 系统重大安全漏洞:不用密码我也可以玩你的 Macbook
  13. java生成pdf417条形码_python生成417条形码(PDF417)
  14. mysql dump 1449_mysqldump 1449错误解决办法
  15. 加拿大约克大学计算机科学专业怎么样,加拿大约克大学优势专业有哪些?
  16. (找规律)3,5,7,2,4,11,7,5,29,23,? 填什么数字
  17. 幸运大转盘(每天一个python小项目)
  18. 微信小程序详细图文教程-10分钟完成微信小程序开发部署发布(3元获取腾讯云服务器带小程序支持系统)...
  19. android popwindow 使用 转http://www.cnblogs.com/jenson138/p/4335818.html
  20. 第9章 初识STM32固件库—零死角玩转STM32-F429系列

热门文章

  1. 关于数字电视用到的服务器介绍
  2. Android Studio实现推箱子小游戏
  3. 多线程之守护线程daemonVS用户线程
  4. realm教程 android,Realm for Android基本教程
  5. Day19 学习java(网络编程、正则表达式)
  6. 服务器系统survey,GitHub - zhanwen/survey: iWen问卷调查系统
  7. Oracle快速迁移百万级数据量的方法
  8. 面试官:领导让你开车送醉酒客户回家,你送吗?95后:傻子才送
  9. 【ClickHouse源码】ReplicatedMergeTree之数据同步流程
  10. OLED屏显+实践操作