恒美微站 Logo 恒美微站
  • 首页
  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心
  • 联系我们

单片机电话机设计实战:状态机、DTMF与按键扫描源码解析

  • 首页
  • 资讯中心
  • /
  • 单片机电话机设计实战:状态机、DTMF与按键扫描源码解析

相关资讯

HFSS边界条件本质与工程实战:从物理约束到精度校准 2026/10/9 7:13:22
FB15k上稳定收敛的TransE PyTorch实现指南 2026/10/9 7:08:22
C# MES加工装配模拟系统源码解析:从WinForms到三层架构实战 2026/10/9 7:08:22

最新资讯

Claude Code Mod 魔改实战:从零安装到自定义配置完全指南
Windows 下 Claude Code 安装配置全攻略:从踩坑到高效开发
Vastbase G100 V2.2落地实践:从兼容迁移到主备部署与性能调优
互信息实战指南:从特征筛选到图像配准的完整落地经验
MongoDB实战指南:从文档模型到聚合管道与生产环境避坑
移动端弹幕实现:解析轨道分配与性能优化的关键技术

今日推荐

AI编程智能体实战:从写代码到指挥代码的架构与落地
多模态大模型全栈能力拆解:从数据对齐到弹性推理
大模型Agent开发入门:从工具调用循环到落地避坑指南

本周热门

MR25H40CDF + PIC18F65K40:工业记录仪高可靠存储实战
基于STM32的数控恒压恒流电源设计:从硬件到PID调参全解析
LT9211 MIPI重定时器原理与双路扇出实战指南

本月精选

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证
2026 大模型集体涨价:用 Python 做企业 Token 成本测算与选型避坑(附配置)

单片机电话机设计实战:状态机、DTMF与按键扫描源码解析

发布时间:2026/10/9 7:13:22
单片机电话机设计实战:状态机、DTMF与按键扫描源码解析 电话机这个品类在单片机圈子里算是老古董级别的练手项目了。但有意思的是每年做毕业设计、课程设计的时候总有一大批人绕不开它——不是因为它有多难而是因为它几乎把单片机开发里所有基础但关键的技能点都串了一遍键盘扫描、摘挂机检测、拨号音生成、DTMF收发、振铃控制、串口通信、状态机设计。你把这些搞明白了再去做其他嵌入式项目基本就是换个外设的事。我手上这份用单片机设计的电话机参考源码最早是帮一个学弟看毕设时接触到的。他当时拿着一份网上下的代码跑不起来串口打印全是乱码拨号也没反应。我花了两个晚上帮他把代码从头捋了一遍发现问题的根源不在代码本身而在于原作者的硬件假设和他的板子对不上——P2口接的8个开关在他的板子上是接在P1口的而且上拉电阻的接法也不一样。这件事让我意识到这类参考源码最大的价值不是复制粘贴就能跑而是帮你理解一个完整电话机系统该怎么拆解、怎么组织状态、怎么处理中断和主循环的关系。所以这篇内容我打算从这份源码出发把单片机电话机的完整设计思路拆开来讲。不管你是用51单片机、STC系列还是STM32核心逻辑是相通的。我会重点讲清楚为什么电话机的软件架构必须用状态机、P2口接8个开关的扫描逻辑该怎么写才不会丢键、DTMF编解码在资源受限的单片机上怎么落地、以及那些参考源码里不会告诉你的调试坑。如果你正在做电话机相关的单片机项目或者想找一个综合性的练手项目来提升嵌入式开发能力这篇内容应该能帮你省下不少试错时间。1. 电话机系统的硬件骨架与资源分配逻辑1.1 为什么电话机是单片机外设综合训练的经典载体电话机这个设备拆开来看其实是一堆外设的集合体。它需要人机交互键盘、显示屏、需要信号检测摘挂机、振铃检测、需要信号生成拨号音、DTMF双音多频、需要通信接口与程控交换机或模拟电话线的交互。把这些功能映射到单片机上就变成了GPIO输入输出、定时器中断、PWM或DAC输出、外部中断、串口通信等一系列外设的综合运用。我见过不少初学者做电话机项目时习惯性地把每个功能写成独立的函数然后在主循环里轮询调用。这种写法在功能少的时候没问题但电话机的状态流转非常复杂——摘机、拨号、通话、挂机、振铃、忙音每个状态下的行为都不一样而且状态之间的切换往往由外部事件触发比如突然来电话了、对方挂机了。如果不用状态机来组织代码很快就会变成一团乱麻改一个地方崩三个地方。从资源分配的角度看51单片机这类8位机的资源非常紧张。以经典的STC89C52为例它只有512字节的RAM、8KB的Flash、3个定时器、1个串口。你要在这点资源里塞下一个完整的电话机逻辑就必须精打细算。比如DTMF的生成如果用查表法输出正弦波两个频率各需要一张表每张表按采样率算下来至少几十个点两张表就是上百字节的ROM开销。如果RAM再不够用还得考虑把一些不常用的数据放到外部存储器或者用压缩的方式存储。提示如果你用的是STC8系列或STM32资源会宽裕很多但状态机的设计思路是一样的。不要因为资源多了就放弃状态机那是给自己挖坑。1.2 P2口接8个开关的扫描电路设计与上拉电阻的选择原始项目正文里提到在单片机的P2口接8个开关这其实是一个很典型的矩阵键盘或者独立按键的接法。如果是8个独立按键直接接在P2口每个按键一端接地、一端接P2口的某一位那么P2口内部的上拉电阻51单片机P2口内部有弱上拉就能保证按键未按下时读到高电平按下时读到低电平。但这里有个坑51单片机的P2口内部上拉能力很弱大概只有几十微安到几百微安级别。如果你的按键引线比较长或者环境干扰比较大读到的电平可能会不稳定。我建议在外部加4.7kΩ到10kΩ的上拉电阻这样抗干扰能力会强很多。另外按键两端最好并联一个0.1μF的电容做硬件消抖虽然软件消抖也能用但硬件消抖能减轻CPU的负担。如果是矩阵键盘的接法比如4×4矩阵用8根线控制16个按键那就需要行线和列线配合扫描。电话机的键盘通常有12个键0-9、*、#有些还有重拨、免提等功能键所以4×3或4×4矩阵是比较常见的。矩阵键盘的扫描逻辑比独立按键复杂一些需要逐行输出低电平然后读列线状态通过行列组合确定按下的键。按键接法占用IO口优点缺点适用场景独立按键每个按键1个IO扫描逻辑简单响应快占用IO多按键数量少≤8个4×3矩阵7个IOIO利用率高扫描逻辑稍复杂有鬼键问题电话机12键标准键盘4×4矩阵8个IO可扩展至16键同上带功能键的电话机ADC键盘1个ADC通道只占1个IO需要ADC按键组合有限资源极度紧张时从这份参考源码来看它用的是P2口直接接8个开关的方案说明按键数量不超过8个可能是简化版的电话机只保留了最基本的拨号功能。这种设计的好处是扫描逻辑极其简单一个端口读一次就能获取所有按键状态。但缺点是按键数量受限而且如果8个开关同时按下可能会因为端口驱动能力不足导致电平判断错误。1.3 摘挂机检测与振铃检测的硬件实现细节电话机有两个关键的状态检测点摘挂机检测和振铃检测。摘挂机检测是通过一个机械开关或者光电传感器来判断听筒是否被拿起。在电路上通常是一个开关一端接地、一端接单片机IO并配上拉电阻。摘机时开关闭合IO读到低电平挂机时开关断开IO读到高电平。振铃检测稍微复杂一些。电话线的振铃信号是高压交流信号通常75Vrms左右频率20-25Hz不能直接接到单片机IO上。需要经过降压、整流、光耦隔离之后变成一个低压直流信号或者脉冲信号再送到单片机的外部中断引脚或者普通IO。如果用的是光耦隔离方案振铃时光耦导通IO读到低电平没有振铃时IO读到高电平。这里有个经验振铃检测的光耦输出端最好加一个RC滤波滤掉高频干扰。因为电话线上的干扰信号很复杂不加滤波的话单片机可能会误判为振铃导致程序频繁进入振铃处理逻辑。RC的时间常数一般取10ms到50ms左右既能滤掉干扰又不会影响振铃信号的检测。注意如果你做的是模拟电话线接入的项目一定要做好隔离。电话线上的电压和电流可能会损坏单片机光耦隔离是最基本的保护措施。2. 状态机电话机软件架构的核心骨架2.1 为什么轮询式主循环在电话机项目里必然翻车我见过太多初学者写的电话机代码是这样的主循环里先读按键再读摘挂机状态再读振铃状态然后根据这些状态做一堆if-else判断。刚开始功能少的时候还能跑但一旦加上拨号音生成、DTMF发送、忙音检测这些功能代码就会变得极其臃肿。问题的根源在于电话机的行为是事件驱动的而不是顺序执行的。你无法预测用户什么时候摘机、什么时候按键、什么时候对方挂机。如果主循环里有一个延时函数在生成拨号音那这段时间内按键和振铃检测就全被阻塞了。如果不用延时用定时器中断来生成拨号音那主循环里又该怎么组织这些并行的逻辑状态机就是解决这个问题的标准方案。把电话机的所有可能状态列出来——空闲、摘机、拨号、振铃、通话、忙音——然后定义每个状态下响应哪些事件、执行哪些动作、跳转到哪个状态。主循环只需要做一件事读取当前事件交给状态机处理然后根据状态机的输出执行相应的动作。2.2 电话机状态机的状态划分与迁移条件一个完整的电话机状态机至少包含以下几个状态IDLE空闲挂机状态等待摘机或振铃。DIAL_TONE拨号音摘机后等待交换机发送拨号音或者直接进入拨号状态。DIALING拨号中用户正在按键拨号收集号码。RINGING振铃有来电振铃器响。ANSWERING应答用户摘机接听来电。TALKING通话中双方通话。BUSY忙音对方忙或者线路忙播放忙音。HANGUP挂机用户挂机回到空闲状态。状态之间的迁移条件需要根据实际电话机的行为来定义。比如从IDLE到DIAL_TONE的迁移条件是检测到摘机从DIALING到TALKING的迁移条件是号码拨完且对方应答从RINGING到ANSWERING的迁移条件是检测到摘机。在代码实现上可以用一个枚举类型定义状态用一个switch-case或者状态表来处理迁移。我个人的习惯是用状态表因为状态多了之后switch-case会变得很长而状态表更清晰也更容易扩展。typedef enum { STATE_IDLE, STATE_DIAL_TONE, STATE_DIALING, STATE_RINGING, STATE_ANSWERING, STATE_TALKING, STATE_BUSY, STATE_HANGUP } PhoneState; typedef enum { EVENT_NONE, EVENT_OFFHOOK, EVENT_ONHOOK, EVENT_KEY_PRESS, EVENT_RING_DETECT, EVENT_DIAL_TIMEOUT, EVENT_ANSWER } PhoneEvent; PhoneState currentState STATE_IDLE; void phone_state_machine(PhoneEvent event) { switch (currentState) { case STATE_IDLE: if (event EVENT_OFFHOOK) { currentState STATE_DIAL_TONE; start_dial_tone(); } else if (event EVENT_RING_DETECT) { currentState STATE_RINGING; start_ringing(); } break; case STATE_DIAL_TONE: if (event EVENT_KEY_PRESS) { currentState STATE_DIALING; stop_dial_tone(); add_digit_to_buffer(); } else if (event EVENT_ONHOOK) { currentState STATE_IDLE; stop_dial_tone(); } break; // ... 其他状态的处理 } }2.3 状态机与定时器中断的协作方式状态机本身不产生时间基准它需要依赖定时器中断来驱动。电话机里至少需要两个定时器一个用于生成拨号音和忙音比如用PWM或者方波输出另一个用于按键消抖和状态超时检测。以拨号音的生成为例中国的拨号音是450Hz的连续正弦波。在51单片机上可以用定时器产生一个450Hz的方波然后通过低通滤波器变成近似正弦波。或者用查表法定时器以更高的频率中断每次中断输出一个采样点通过DAC或者PWM加滤波输出。查表法的音质更好但占用资源更多。状态机的主循环和定时器中断之间的数据交互需要特别注意。中断里尽量不要做复杂的逻辑判断只做标志位的设置和数据的采集。主循环里读取标志位然后交给状态机处理。这样可以避免中断嵌套和竞态条件。提示如果状态机里需要处理超时比如拨号后等待对方应答的超时可以用一个软件计数器在定时器中断里递减主循环里检查是否减到零。不要用delay函数来做超时那会阻塞整个系统。3. DTMF编解码在资源受限单片机上的落地3.1 DTMF双音多频信号的频率组合与生成原理DTMF双音多频是电话拨号的标准信号方式。每个按键对应两个频率的组合一个来自低频组697Hz、770Hz、852Hz、941Hz一个来自高频组1209Hz、1336Hz、1477Hz、1633Hz。比如按键1是697Hz1209Hz2是697Hz1336Hz以此类推。在单片机上生成DTMF信号有两种常见方案一种是查表法预先计算好两个正弦波的采样点存在ROM里定时器中断时依次输出另一种是实时计算用定时器产生两个不同频率的方波然后叠加。查表法的音质好但需要较大的ROM空间实时计算法节省ROM但音质差一些而且两个方波的叠加会产生交调失真。以查表法为例假设采样率为8kHz一个DTMF信号的持续时间至少40ms那么需要320个采样点。如果每个采样点用8位表示一张表就是320字节。两个频率各一张表就是640字节。对于只有8KB Flash的51单片机来说这个开销是可以接受的但如果你还要存其他数据就得考虑压缩或者共用表。// DTMF频率表简化示例实际需要根据采样率计算 const unsigned char dtmf_low_697[] {128, 135, 142, ...}; const unsigned char dtmf_high_1209[] {128, 140, 152, ...}; void dtmf_output(unsigned char key) { unsigned int i; for (i 0; i DTMF_SAMPLES; i) { unsigned char sample dtmf_low_table[key][i] dtmf_high_table[key][i]; DAC_output(sample); delay_us(SAMPLE_PERIOD); } }3.2 用定时器中断实现双正弦波叠加输出的实操步骤在实际项目中我更喜欢用定时器中断来输出DTMF而不是在主循环里用delay。因为主循环里用delay会阻塞其他任务而定时器中断可以保证输出的时序精度。具体做法是配置一个定时器中断频率设为采样率比如8kHz。在中断服务程序里维护两个相位累加器分别对应低频和高频。每次中断时相位累加器加上对应的频率步进值然后查表得到两个采样值相加后输出到DAC或PWM。频率步进值的计算公式是step (frequency * table_size) / sample_rate。比如频率697Hz表大小256采样率8kHz那么step (697 * 256) / 8000 ≈ 22.3。取整后为22实际输出频率为22 * 8000 / 256 687.5Hz误差约1.4%。这个误差在电话机里是可以接受的因为DTMF解码器通常有±1.5%到±2%的容差。volatile unsigned int phase_low 0; volatile unsigned int phase_high 0; volatile unsigned char dtmf_active 0; void timer_isr() { if (dtmf_active) { phase_low step_low; phase_high step_high; unsigned char sample sine_table[(phase_low 8) 0xFF] sine_table[(phase_high 8) 0xFF]; DAC_output(sample); } }3.3 DTMF解码用单片机识别电话线送来的拨号信号如果你的项目需要接收对方送来的DTMF信号比如做电话远程控制那就需要DTMF解码。专用的DTMF解码芯片如MT8870是最简单的方案它直接输出4位二进制码单片机只需要读IO就行。但如果想用纯软件解码那就需要用到Goertzel算法或者FFT。Goertzel算法是专门用于检测特定频率的算法计算量比FFT小很多非常适合在单片机上运行。它的原理是对于每个待检测的频率计算一个中间变量然后根据这个变量判断该频率是否存在。在8kHz采样率下检测8个DTMF频率每个频率做一次Goertzel计算总共需要几百次乘加运算51单片机在几十毫秒内可以完成。不过说实话在51单片机上做软件DTMF解码实时性压力比较大。如果你只是做拨号功能不需要解码那就不用考虑这个问题。如果需要解码建议用专用芯片省时省力。解码方案硬件成本软件复杂度实时性适用场景MT8870专用芯片中等低好需要稳定解码的项目Goertzel算法低高一般资源充足、想练手的项目FFT低很高差不推荐在51上使用外部DSP高中很好高端应用4. 参考源码里不会告诉你的调试坑与实战经验4.1 按键扫描丢键和连击问题的根因分析按键扫描看起来简单但实际调试时问题不少。最常见的是丢键和连击。丢键是指用户明明按了键但程序没检测到连击是指按一次键程序识别成了多次。丢键的根因通常是扫描频率太低。如果主循环里有很多延时导致按键扫描的间隔超过了按键按下的持续时间通常至少50ms就会丢键。解决办法是把按键扫描放到定时器中断里保证固定的扫描间隔比如10ms一次。连击的根因是消抖没做好。机械按键在按下和释放时会有抖动通常持续5ms到20ms。如果扫描间隔是10ms而抖动持续20ms那么可能会读到两次按下。解决办法是加消抖逻辑连续两次读到相同的按键状态才认为是有效按键。#define KEY_SCAN_INTERVAL 10 // 10ms扫描一次 #define DEBOUNCE_COUNT 3 // 连续3次相同才确认 unsigned char key_scan() { static unsigned char last_key 0xFF; static unsigned char count 0; unsigned char current_key read_key_port(); if (current_key last_key) { if (count DEBOUNCE_COUNT) { count; } } else { count 0; last_key current_key; } if (count DEBOUNCE_COUNT) { return current_key; } return 0xFF; // 无有效按键 }4.2 拨号音生成时的定时器冲突与优先级处理电话机里通常需要多个定时器一个用于按键扫描一个用于DTMF生成一个用于状态超时。如果定时器不够用就需要复用。比如用同一个定时器在不同的时间段做不同的事情。但复用定时器会带来冲突。比如DTMF生成需要精确的8kHz中断而按键扫描只需要100Hz。如果把它们放在同一个定时器里中断频率就得按最高的来然后在中断里用计数器分频。这样虽然能工作但中断服务程序会变得很长影响实时性。我的建议是如果单片机有3个以上的定时器就各用各的如果只有2个就把按键扫描和状态超时合并到一个定时器里DTMF单独用一个。如果只有1个定时器那就只能分时复用了但要注意中断服务程序的执行时间不能太长。注意51单片机的定时器中断响应需要十几个机器周期如果中断频率太高比如超过20kHzCPU的大部分时间都会花在中断响应上主循环几乎没时间执行。所以DTMF的采样率不要设得太高8kHz足够了。4.3 从参考源码到实际可用项目的移植要点网上的参考源码最大的问题是硬件假设和你的板子不一致。比如源码里假设P2口接按键但你的板子是P1口源码里假设晶振是12MHz但你的板子是11.0592MHz。这些差异会导致代码跑不起来。移植的时候首先要确认硬件连接。把源码里的端口定义、引脚定义全部核对一遍改成你板子的实际连接。然后确认时钟频率把定时器的初值重新计算。最后确认外设的电气特性比如按键是高电平有效还是低电平有效LED是高电平点亮还是低电平点亮。另外参考源码里的延时函数通常是根据特定晶振频率写的换晶振后延时就不准了。建议把延时函数改成基于定时器的精确延时或者用空循环加校准的方式重新计算。移植检查项常见问题解决方法端口定义源码用P2板子用P1修改宏定义或直接改端口晶振频率源码12MHz板子11.0592MHz重新计算定时器初值和延时按键有效电平源码低有效板子高有效修改扫描逻辑或加反相器外设驱动能力源码直接驱动板子需要驱动电路加三极管或驱动芯片中断优先级源码默认优先级板子有特殊要求配置IP寄存器4.4 电话机项目从原型到成品的工程化建议如果你只是做课程设计或者练手参考源码改改能用就行。但如果你想把它做成一个稳定的成品那就需要考虑工程化的问题。首先是电源。电话机通常需要从电话线取电但电话线的供电能力有限而且电压不稳定。建议加一个稳压电路把电话线的电压稳定到5V或3.3V给单片机供电。同时要加保护电路防止电话线上的高压脉冲损坏单片机。其次是PCB布局。电话机里有模拟信号DTMF、拨号音和数字信号单片机IO、时钟两者要分开布局避免数字信号干扰模拟信号。模拟部分的地和数字部分的地要单点连接减少地环路干扰。最后是软件架构。参考源码通常是单文件、面向过程的写法功能一多就难以维护。建议把代码分成多个模块按键驱动、DTMF编解码、状态机、显示驱动、通信协议等。每个模块提供清晰的接口方便单独测试和替换。我在实际项目中还发现一个细节电话机的拨号音和忙音的音量需要根据线路情况调整。如果音量太小对方听不清如果音量太大可能会引起回音。通常的做法是用一个可调电阻或者数字电位器来调节输出幅度在调试时根据实际效果调整。5. 从51到STM32电话机项目的升级路径5.1 什么时候该放弃51单片机换更高级的平台51单片机做电话机最大的瓶颈是资源。8KB Flash、512字节RAM稍微加点功能就不够用了。如果你需要以下功能建议直接上STM32或者类似的32位平台需要同时处理DTMF编解码和语音信号处理需要驱动LCD显示屏显示来电号码、通话时间等信息需要支持免提通话涉及音频功放和回声消除需要存储大量号码簿和通话记录需要支持多种通信协议比如同时支持模拟电话线和IP电话STM32F103系列是性价比很高的选择72MHz主频、64KB Flash、20KB RAM足够跑一个功能完整的电话机。而且STM32的外设更丰富有多个定时器、多个串口、DMA控制器可以大大简化软件设计。5.2 STM32平台上电话机状态机的重构思路在STM32上重构电话机状态机最大的变化是可以用RTOS实时操作系统来管理任务。比如用FreeRTOS把按键扫描、DTMF生成、状态机、显示刷新分别放到不同的任务里通过消息队列和信号量来通信。这样代码结构更清晰也更容易扩展。如果不想用RTOS也可以用STM32的HAL库加中断的方式来实现。STM32的中断优先级配置比51灵活得多可以把DTMF生成放在高优先级中断里按键扫描放在低优先级中断里确保音频输出的实时性。// STM32上使用HAL库的定时器中断示例 void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if (htim htim3) { // DTMF生成定时器 if (dtmf_active) { phase_low step_low; phase_high step_high; uint16_t sample sine_table[(phase_low 8) 0xFF] sine_table[(phase_high 8) 0xFF]; __HAL_TIM_SET_COMPARE(htim2, TIM_CHANNEL_1, sample); } } else if (htim htim4) { // 按键扫描定时器 key_scan(); } }5.3 电话机项目还能怎么玩扩展功能与创意方向电话机这个项目基础功能做完之后还有很多可以扩展的方向。比如来电显示用FSK或者DTMF解码获取来电号码在LCD上显示。自动拨号存储常用号码一键拨出。通话录音用SD卡或者Flash存储通话内容。远程控制通过DTMF解码实现远程控制家电。IP电话用网络模块把语音打包成IP数据包实现网络通话。蓝牙电话用蓝牙模块连接手机实现无线通话。这些扩展功能每一个都可以作为一个独立的子项目来研究。比如来电显示的FSK解码涉及到模拟信号处理和数字信号处理的知识远程控制的DTMF解码涉及到Goertzel算法和状态机的配合。把这些都做一遍你的嵌入式开发能力会有质的提升。我个人觉得电话机项目最大的价值不在于最终做出来的东西有多厉害而在于它逼着你去理解一个完整系统的运作方式。从硬件电路到软件架构从底层驱动到上层应用从单任务到多任务每一个环节都有值得深挖的东西。你把这个项目吃透了再去看其他嵌入式项目会发现很多底层逻辑是相通的。最后分享一个我在调试电话机项目时的小技巧用串口打印状态机的状态迁移日志。每次状态切换时通过串口输出当前状态和目标状态这样在调试时就能清楚地看到程序的实际运行路径比单步调试效率高得多。如果串口不够用也可以用LED闪烁的不同模式来指示当前状态虽然信息量少一些但胜在简单直接。

关于恒美微站

恒美微站专注于为个体商户、工作室提供极简自助建站服务,让每个人都能轻松拥有专业网站。

快速链接

  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心

服务项目

  • 可视化建站
  • 拖拽编辑
  • 主题定制
  • SEO 优化
  • 网站托管

联系方式

  • 📍 地址:北京市朝阳区建国路 88 号
  • 📞 电话:400-888-8888
  • ✉️ 邮箱:info@hmyw.cn
  • 🕐 时间:周一至周日 9:00-18:00

© 2024 恒美微站 hmyw.cn 版权所有 | 京 ICP 备 12345678 号