简介这套资料以51单片机为核心完整呈现心形灯光音乐盒的设计过程适合电子技术初学者、单片机爱好者及嵌入式硬件设计人员用于课程实践或项目参考。资源包共105个文件压缩后约2.57MB包含C语言源程序、电路原理图、设计文档、仿真文件以及辅助工具覆盖从硬件连接、程序控制到音乐播放的完整链路。目前已有500人学习下载内容经过整理易于按需查阅。资料中不仅提供了程序源码与电路图还包含器件选型说明和操作指南能够帮助读者理解LED矩阵的动态显示原理以及单片机与音乐芯片的通信方式从而掌握灯光与音乐同步实现的关键方法。无论是入门自学还是毕业设计借鉴这套资料都具有较强的实用性和参考价值。1. 基于51单片机的心形灯光音乐盒把音符和心跳做成一个看得见的桌面玩具很多人学51单片机都是从流水灯和蜂鸣器嘀嘀嘀开始的搞完就扔一边了。这个心形灯光音乐盒不一样它把一首完整的曲子、16颗LED排成的心形图案、以及按键切换的几种灯效塞进了一块巴掌大的洞洞板里。它不属于那种炫技的项目但恰好是嵌入式硬件入门最该有的样子硬件上有一颗单片机、一组LED、一个蜂鸣器、两个按键代码上有定时器中断、音谱数据结构、状态机切换。你照着原理图焊完、烧完程序它能一边播《小星星》一边让心形灯像心跳一样闪按一下按键还能切换成呼吸灯模式。这篇文章会把这个项目的电路设计、音符播放原理、LED联动逻辑和几个踩坑点全部拆开讲工程文件和相关文档资料在文末下载页可以拿到。2. 项目全貌与技术选型为什么这类项目永远值得做一遍2.1 这个音乐盒到底能干什么曲目、灯效、按键交互先说清楚它的功能边界。默认固件里预置了三首曲子第一次上电自动播放第一首音乐是单音旋律由无源蜂鸣器发出同时16颗LED会根据当前音符实时点亮形成声随光动的效果。按键K1负责切换曲目短按从当前曲跳到下一首三首循环按键K2负责切换灯光模式有静态心形、呼吸渐变、心跳快闪、音符联动四种灯光模式独立于曲目也就是说你在播放任何一首歌时都能切灯效。这个交互逻辑看似简单但它是典型的单片机状态机应用。每首曲子有独立的音谱数据灯光模式有独立的显示函数主循环负责读取按键、刷新LED、驱动蜂鸣器定时器中断负责掐节拍每个功能模块之间通过全局变量通信不用操作系统不用复杂调度。正因为它简单反而适合拿来读代码、改代码——你想加一首歌只需要往音谱表里塞一组数据你想加一种灯效只需要写一个函数然后在按键分支里挂上去。我用一个表格把整机的功能清单列出来这样你拿到工程后可以对照着验证自己是否完全跑起来了功能项实现方式验证方法三首曲目循环播放音谱数组 定时器中断驱动短按K1切换听旋律变化心形LED阵列P0/P2端口驱动16颗LED上电默认全亮心形四种灯光模式主循环模式标志位分支短按K2逐一切换音符联动灯效音符值映射LED亮灭播放时观察LED跟随旋律跳动曲目播完自动重播曲目索引循环等待一首曲播完观察行为2.2 技术选型为什么是STC89C52而不是STM32或Arduino这个项目用的是意法半导体STC89C52RC一颗经典的8051内核单片机工作电压5V12MHz晶振内置8KB Flash、256字节RAM。选择它不是因为性能强而是因为这个场景根本不需要强一个蜂鸣器加16个LEDC52的IO口全都能直接驱动不像STM32那样要先配置一堆时钟树和复用引脚。而且51单片机的寄存器数量少定时器、串口、外部中断全是直接在寄存器上操作Keil里写几行代码就能看见效果这对想弄懂单片机到底是怎么工作的来说是最短路径。对比一下几种常见方案你就明白了。Arduino UNO用AVR芯片写起来确实快但那套Wire库、Serial库把底层全封装掉了你调一个PWM呼吸灯实际上是在调库函数硬件细节隔了一层STM32功能强但GPIO初始化就要五六行定时器还要挂RCC时钟对一个心形音乐盒来说属于杀鸡用牛刀。51单片机的好处在于你的代码和硬件行为是直接对应的往P0口写一个字节LED立刻亮灭往定时器初值寄存器写一个数蜂鸣器音调立刻变化没有中间层。电路设计上也只是最基础的几个元件单片机最小系统晶振、复位、电源滤波、两个按键、一个蜂鸣器驱动三极管、16颗LED加限流电阻。整机成本不超过二十块钱洞洞板焊接一小时能完事。比起各种带屏幕带WiFi的开发板这个项目把单片机最小系统能干什么展示得非常纯粹——它就是一颗芯片加上外围器件没有任何多余部分。2.3 代码工程结构一个main文件跑全场的经典布局这个项目的Keil工程结构是我非常熟悉的经典51布局整个工程只有三个源码文件加一个头文件没有用任何库heart_music_box/ ├── main.c // 主循环、按键扫描、模式切换 ├── music.c // 音谱数据、音符播放控制 ├── timer.c // 定时器0/1初始化与中断服务 ├── leds.c // LED阵列显示函数、四种灯效 ├── delay.c // 毫秒级/微秒级软件延时 ├── reg52.h // 寄存器定义头文件Keil自带 └── heart_music_box.uvproj // Keil工程文件main.c是整个程序的骨架它做的事用一个死循环就能概括扫描按键、根据按键更新状态、刷新LED。真正掐时间的活全在timer.c里定时器0负责产生音符频率的方波定时器1负责10ms节拍中断。music.c只是一堆code关键字修饰的常量数组存放音符频率表的初值和三首歌曲的简谱数据程序运行时不修改这些数据所以全部放在Flash里省RAM。这种把数据、驱动、逻辑分文件放的组织方式对51项目来说已经算结构清晰了。你拿到工程后第一件事应该是打开main.c从上往下扫一遍看到全局变量定义处有几个状态标志位——current_song、light_mode、playing_flag——这几个变量就是整个程序的记忆理解了它们你就能理解全部逻辑。3. 硬件设计拆解心形LED阵列、蜂鸣器驱动与按键电路3.1 心形LED阵列16颗LED排成一个看得懂的心心形图案由16颗红色LED组成布局是上两行少、中间行多、底部收尖的标准心形轮廓。这16颗LED分成两组P0口接上方8颗P2口接下方8颗所有LED采用共阳极接法——每颗LED的阳极通过330Ω限流电阻接到VCC阴极接到单片机的P0或P2引脚单片机引脚输出低电平时LED点亮。这里有个关键设计决策为什么用共阳极而不是共阴极。51单片机的P0口是开漏输出内部没有上拉电阻拉电流能力很弱高电平时基本上供不出电流而P2口虽然内部有上拉但8颗LED同时点亮时电流也偏紧。反过来看灌电流能力51的每个IO口都能承受20mA左右的灌入电流低电平时把LED点亮反而更亮更均匀。所以这个项目让LED阳极统一接正极单片机只需要往下拉就能点亮这也是老51项目里最常见的做法。限流电阻的取值可以算一下电源5V红色LED压降约2V330Ω电阻上的压降是3V电流约9mA16颗全亮总电流约144mA对USB供电或者三节镍氢电池串联来说毫无压力。你如果觉得亮度不够可以把330Ω换成220Ω电流升到约13mA但需要注意总电流不要超过单片机的地线承载能力不然复位的瞬间电压跌落容易导致程序重启。心形图案本身没有存在Flash里而是用一个16位的变量表示高8位对应P0口的8颗LED低8位对应P2口的8颗LED哪个bit是0哪颗灯就亮。比如想让心形上半部分全亮、下半部分全灭只需要给LED_Data赋一个上半全部为0的值然后一次P0、P2口的写操作就完成了。16颗LED看起来多实际上两个端口各写一次字节就能刷新全部状态速度比逐颗扫描快得多。3.2 蜂鸣器驱动电路三极管放大别让单片机直接推蜂鸣器是这个项目声音的来源用的是无源蜂鸣器——注意是无源不是有源。有源蜂鸣器内部自带振荡电路通电就响频率固定没法编程无源蜂鸣器需要外部给它一个方波信号才会发声频率由方波周期决定。我们的音乐播放靠定时器改变方波频率所以必须用无源蜂鸣器。单片机的P1.0引脚输出方波信号经过一个NPN三极管常见型号8050放大后驱动蜂鸣器。三极管的基极串联一个1kΩ电阻接到P1.0集电极接蜂鸣器的负极蜂鸣器正极接VCC发射极直接接地。当P1.0输出高电平时三极管导通蜂鸣器通电发声输出低电平时三极管截止蜂鸣器停止。之所以要加三极管是因为单片机的IO口直接驱动蜂鸣器时电流不够声音发闷还容易把引脚烧掉让三极管当开关去控制蜂鸣器的供电电流是最稳妥的做法。实际焊接时还应该在蜂鸣器两端反并联一个续流二极管比如1N4148因为蜂鸣器内部是线圈断电瞬间会产生反向电动势如果没有续流回路这个尖峰电压可能通过三极管反向冲击单片机引脚。很多初学者焊完发现蜂鸣器响几声之后单片机就死机多半就是没加这个二极管或者是三极管引脚接错了——8050的引脚顺序是E、B、C面对型号面从左到右接反了完全没声音。这个电路的另一个坑是P1.0的初始化状态。单片机复位后所有引脚默认高电平如果程序还没开始跑三极管已经导通蜂鸣器会一直长鸣直到main函数里把P1.0清零才停下。所以固件里启动代码的第一件事就是关蜂鸣器这个细节在避坑章节会再展开。3.3 按键输入电路上拉电阻与消抖的两种做法两个按键K1、K2分别接在P3.2和P3.3引脚上这两个引脚同时也是外部中断0和外部中断1的输入口。按键另一端接地引脚通过10kΩ上拉电阻接VCC默认状态是高电平按下后变成低电平单片机通过读取引脚电平判断按键状态。这个电路可以用两种方式读取。一种是在主循环里轮询扫描每隔一小段时间读一次引脚检测到低电平时软件延时消抖再确认另一种是利用外部中断按键按下时引脚电平跳变触发中断在中断服务函数里处理按键逻辑。这个项目的代码用的是前者——主循环轮询因为它的主循环本身就在不停地刷新LED轮询按键几乎不增加额外负担而且消抖逻辑写起来更直观。消抖的本质是解决机械按键的抖动问题。按键按下和松开的瞬间簧片会回弹几次产生持续几毫秒的不稳定电平跳变如果不处理一次按键可能被识别成四五次。这个项目的软件消抖做法是检测到P3.2变成低电平后先延时10ms再读一次如果仍然是低电平才确认有效按键然后等待按键释放读到高电平才结束这次按键处理。这个确认按下再等释放的流程能顺带解决长按连发的问题——只有短按会触发一次按住不放不会反复切换。如果按键接在P3.2和P3.3上还有个隐藏好处这两个引脚支持上升沿和下降沿触发的外部中断以后你想改成中断方式处理按键硬件电路完全不用动只需要改程序里的中断配置就行。这也是某些老工程师推荐按键优先接P3.2/P3.3的原因属于硬件设计上给后续留的后手。4. 核心代码实现定时器、音谱数据与灯效联动的完整链路4.1 定时器0与音符频率方波是这样产生的音乐播放的核心是让蜂鸣器产生不同频率的方波。12MHz晶振下12T模式51单片机的机器周期是1MHz也就是1微秒。定时器0工作在方式116位定时每计一个数就是1微秒当它从初值计数到65536溢出时就在中断服务函数里把P1.0的电平翻转一次这样蜂鸣器上就得到了一个方波。方波的频率由定时器初值决定。假设我们要产生中音Do频率523Hz它的周期是1/523秒约1.912毫秒半个周期是956微秒。定时器从初值开始累加到溢出正好经过956个数那么初值就是65536减去956等于64580。我把常用音符的初值算好放进频率表用一个数组索引音符编号// 音符频率表12MHz晶振, 12T模式, 机器周期1us // 初值 65536 - (1000000 / (2 * freq)) code unsigned int tune_table[] { 0, // 索引0: 休止符 64580, // 索引1: 中音Do 523Hz 64684, // 索引2: 中音Re 587Hz 64777, // 索引3: 中音Mi 659Hz 64820, // 索引4: 中音Fa 698Hz 64898, // 索引5: 中音Sol 784Hz 64968, // 索引6: 中音La 880Hz 65030, // 索引7: 中音Si 988Hz 65058, // 索引8: 高音Do 1046Hz 65110, // 索引9: 高音Re 1175Hz 65157, // 索引10: 高音Mi 1319Hz 63628, // 索引11: 低音Do 262Hz 63836, // 索引12: 低音Re 294Hz 64021, // 索引13: 低音Mi 330Hz 64260 // 索引14: 低音Sol 392Hz };定时器0的初始化代码放在timer.c里只做两件事设置工作方式、装初值并开中断void Timer0_Init(void) { TMOD 0xF0; // 清零T0的控制位保留T1的配置 TMOD | 0x01; // 设置T0为方式116位定时器 TH0 0xFC; // 装入高8位初值对应64580 TL0 0x44; // 装入低8位初值 ET0 1; // 允许T0中断 EA 1; // 开总中断 TR0 1; // 启动T0 }这里有个容易踩的坑TH0和TL0要分开写因为定时器是16位的初值必须同时装入高8位和低8位。从频率表里取值时注意高位字节是初值除以256取整低位是初值对256取余如果直接按表里的十进制写数字就少了这一步拆字节的动作。中断服务函数每溢出一次翻转一次P1.0void Timer0_ISR(void) interrupt 1 { P1_0 !P1_0; // 翻转蜂鸣器引脚产生方波 TH0 tune_table[current_note] / 256; // 重新装初值 TL0 tune_table[current_note] % 256; }中断服务函数的开销很小只有翻转引脚和重装初值两件事。如果当前演奏的是休止符tune_table[0]的值为0初值也是0定时器直接溢出P1.0快速翻转产生一个刺耳的高频噪声——所以程序里对休止符做了单独处理停在中断服务函数里直接返回不翻转P1.0同时把引脚拉成低电平让蜂鸣器完全安静。这个细节很多初学者会漏掉导致乐谱里的停顿变成一阵啸叫。4.2 定时器1与节拍控制让音乐的速度稳定下来有了音高还不够一首曲子还得有节奏。节奏在代码里体现为每个音符持续的时间长短这个时间由定时器1来掐。定时器1也工作在方式1每10毫秒产生一次中断中断里对一个beat_counter变量做递减计数来控制音符的播放时长。乐谱数据结构里每个音符既包含音高信息也包含节拍信息。为了让数据紧凑这个项目用两个并行的数组一个存音符编号一个存节拍数。以《小星星》前两句为例// 音符序列索引指向tune_table code unsigned char song_star[] { 1, 1, 5, 5, 6, 6, 5, // 一闪一闪亮晶晶 4, 4, 3, 3, 2, 2, 1, // 满天都是小星星 0xFF // 结束标志 }; // 节拍序列每个音符的持续时长单位100ms code unsigned char beat_star[] { 4, 4, 4, 4, 4, 4, 8, // 前三个音各0.4秒第五个音0.8秒 4, 4, 4, 4, 4, 4, 8, // 和上面完全对称 0xFF };定时器1的中断服务函数负责推进节拍void Timer1_ISR(void) interrupt 3 { TH1 0xD8; // 10ms定时初值12MHz下约10000个机器周期 TL1 0xF0; if (playing_flag 1) { beat_counter--; // 每10ms减一次 if (beat_counter 0) { // 当前音符时间到切换到下一个音符 note_index; if (song_data[note_index] 0xFF) { // 整首曲子弹完回到开头重播 note_index 0; } current_note song_data[note_index]; beat_counter beat_array[note_index]; } } }节拍控制的关键在于定时器1中断是固定10ms一次无论主循环在干什么节拍计数都不会被影响。这比在主循环里用delay()函数来掐节拍要准得多——主循环里如果插入了LED刷新、按键扫描这些操作软件延时的时长会被拉伸音乐速度就会忽快忽慢。第一次写完这个项目时我就犯过这个错用两个delay()嵌套控制音符时长结果按键一按旋律就变慢松开又恢复后来改成定时器驱动才稳定下来。4.3 灯光驱动的四种模式从静态心形到音符联动LED控制的入口是一个刷新函数它根据当前灯光模式的标志值决定这16颗LED应该处于什么状态。因为只用P0和P2两个端口一次刷新就是两条赋值语句的事所以它可以被主循环非常频繁地调用。静态心形模式最简单直接把一个预先设计好的心形图案数值赋给端口。所谓图案数值就是16个bit的排列对应心形轮廓上每颗灯的位置。比如高8位给P0对应上半部分低8位给P2对应下半部分如果想上半部分亮两颗、下半部分亮一颗就把对应位置写成0。写一个专门的常量数组存不同的心形阶段图案流水效果就能实现了。呼吸灯模式用PWM方式调亮度但51单片机没有硬件PWM模块只能靠软件模拟。做法是每隔一小段时间把LED数据分成多个档位循环输出——比如亮度分成20档每档点亮的时间占比不同人眼视觉暂留效应下看起来就是亮度从暗到亮再到暗的渐变。这个模式的代码要放在主循环里反复执行并且每执行完一轮要更新一次亮度档位的方向否则会卡在最大或最小亮度上。音符联动模式的逻辑最有趣它直接读取当前播放音符的索引值通过一个映射关系来决定LED的亮法。比如音高越高点亮的LED越多形成一种音量条的效果还可以让音高高的区间点亮心形上半部的灯音高低时点亮下半部的灯听起来像是一颗心在随着旋律上下跳动。映射关系可以自己定义玩法很灵活。4.4 主循环与状态机按键、播放、灯效怎么协作主循环的框架是整个程序的心脏它把按键扫描、LED刷新、播放状态维护这三件事按顺序循环执行。整个程序采用状态机设计思想current_song和light_mode这两个全局变量就是状态变量每一次按键按下只是修改状态变量的值真正的行为在循环体里根据状态决定void main(void) { Timer0_Init(); // 初始化定时器0产生音符频率 Timer1_Init(); // 初始化定时器1产生10ms节拍 led_mode 0; // 默认灯光模式静态心形 song_index 0; // 默认播放第一首曲子 P1_0 0; // 关闭蜂鸣器引脚 while (1) { Key_Scan(); // 扫描按键更新状态变量 Led_Refresh(); // 根据当前模式刷新LED delay_ms(5); // 给主循环一个基本的时间节拍 } }按键扫描函数的逻辑用一句话可以概括检测低电平、延时消抖、确认后更新状态变量。它有一个专门的last_key_state变量记录上一次的按键状态用来识别按下这个边沿动作避免按住不放时连续触发。K1按下让song_index加1并对曲目总数取模K2按下让led_mode加1并对4取模这样在任何状态下按按键都不会越界。主循环里的delay_ms(5)既是一个简单的节拍器也是让整个循环体保持在一个可预测的运行频率上。5毫秒的延时对按键扫描来说足够快对LED刷新来说也足够流畅对蜂鸣器——蜂鸣器靠定时器中断驱动完全不依赖主循环——更是没有任何影响。整个状态机设计里main函数只负责策展真正的演出全在中断服务函数里进行这种职责分离让代码逻辑非常清爽。5. 避坑指南电源、蜂鸣器、LED亮度的四个真实踩坑记录5.1 蜂鸣器不出声程序却跑得欢现象程序烧进去LED的心形图案正常亮灭按键切换灯效也正常但蜂鸣器一点声音都没有。原因当时查了半天发现是蜂鸣器选错了型号手里拿着有源蜂鸣器当无源用。有源蜂鸣器通电就响给它一个固定电平它反而安静只有无源蜂鸣器才需要方波驱动。另一个常见原因是三极管接错引脚8050的E、B、C三脚顺序搞反信号根本送不到蜂鸣器。解决先写一个最简单的测试程序让P1.0引脚输出一个固定频率的方波比如用定时器溢出翻转引脚生成1kHz信号如果这样都不响就查硬件电路响了再回来跑完整程序。顺便检查一下蜂鸣器型号或者标签无源蜂鸣器通常标称3.3V或5V且没有内部振荡电路标识有源蜂鸣器底部会标一个并自带驱动电路。5.2 音乐速度忽快忽慢像卡带一样现象刚上电时音乐速度正常按一下按键切歌之后旋律明显变慢再按几次干脆像卡住了一样过几秒又突然恢复正常。原因第一次写节拍逻辑时用的是软件延时delay_ms()函数里包含了音符时长的等待。按键扫描和LED刷新插在延时函数之间每执行一次按键检测、IES刷新都会让延时的实际时间变长所以音乐速度被主循环的负载拖慢了。更糟的是切歌时延时时间被重置出现卡带效果。解决把节拍计时完全交给定时器1音符切换不在主循环里做而是在10ms中断服务函数里做判断。主循环只管按键和LED无论如何繁忙中断里的计时都是准的。从那以后我做任何带时间控制的项目都会强制让时间基准跑在中断里主循环只做逻辑不做延时。5.3 LED亮度不均匀心形看起来缺了一角现象心形图案正常点亮但部分LED明显比其他亮还有一两个暗到几乎看不出来整体观感很差。原因限流电阻取值不一致。手头330Ω电阻不够有几颗位置用了470Ω电流差了几毫安亮度差异肉眼可见。另一个原因是P0口和P2口的驱动能力不同P0口是开漏输出虽然灌流能力强但接法稍有差异就会和P2口拉开差距。解决把所有限流电阻统一成同一阻值并优先选用低电平点亮的共阳极接法让灌电流均匀分布。焊接前先用万用表把电阻量一遍误差超过5%就换掉。如果你发现某颗特别暗优先检查焊接有没有虚焊——LED引脚焊盘松动会导致接触电阻变大电流变小亮度自然就降了。5.4 按键按下没反应偶尔还跳两下现象按K1切歌经常没反应多按几次又出现一次切两首的情况很不稳定。原因两个问题叠加在一起。一个是按键没有做软件消抖机械抖动导致一次按下被识别成多次另一个是把按键引脚和LED共用了一个P0口LED刷新的时候端口电平毛刺干扰了按键读取。解决按键改到P3口独立引脚和LED彻底分开。软件上加上10ms延时消抖也就是检测到低电平后延时10ms再读一次确认还是低电平才算有效同时加上等待释放逻辑防止按住连发。如果你用的是P3.2/P3.3这种带外部中断的引脚改成中断方式读取也可以但消抖逻辑不能省。6. 二次开发指南把固定曲目扩展成可切换的曲库想给这个音乐盒加歌关键不是改代码而是改数据。现有的曲目数据结构是两组并行数组——音符序列和节拍序列结束标志是0xFF。我建议你把它们封装成曲目表用一个结构体把音符数组和节拍数组捆绑起来这样每首歌就是一个独立的实体切歌逻辑只需要改变指针指向。// 曲目结构体一首歌的音符序列和节拍序列 struct Song { code unsigned char *notes; // 指向音符数组 code unsigned char *beats; // 指向节拍数组 }; // 注册新歌以《欢乐颂》片段为例 code unsigned char notes_ode[] { 3, 3, 4, 5, 5, 4, 3, 2, // 欢乐颂前两句旋律 1, 1, 2, 3, 3, 2, 2, 0xFF }; code unsigned char beats_ode[] { 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 8, 0xFF }; // 曲目表把三首歌统一编排方便按键循环切换 struct Song song_list[] { {song_star, beat_star}, // 第一首小星星 {notes_ode, beats_ode}, // 第二首欢乐颂 {song_birth, beat_birth} // 第三首生日快乐 };切歌逻辑从对数组索引加1变成对song_list数组下标加1并取模整个代码几乎不用动只是把原来直接读音符数组的写法改成先取出当前曲目的结构体再读里面的指针。我实际改的时候花了大约20分钟剩下的时间是抄简谱。灯光联动也值得扩展。现在音符联动模式只是简单的音高越高灯越亮你可以把它改成按旋律走向扫描心形——从心尖开始逐颗点亮音符往高处走就往上亮往低处走就往下灭看起来像一颗心在呼吸。改这个要先在LED刷新函数里加一个note_scan_index变量每次中断切换到新音符时步进这个变量然后按它去查图案数值表。这个效果做出来比原版的声色联动更有感染力。最后提醒一句改完代码一定要用STC-ISP软件重新烧录如果板子用的是老款STC89C52冷启动烧录前需要先断电再上电这个操作细节很多人第一次搞程序员都会卡住。整个项目的原理图和固件工程都可以在下载页拿到我的习惯是每次改版前先把原工程的hex文件备份一份改坏了随时能撤回省得重新焊板子——希望你用得上也希望你在上面改动出一点自己的东西。本文还有配套的精品资源点击获取