feat: 实现智能按摩器完整功能 (定时/语音/电机/显示)
- 新增MP3语音模块驱动,集成18个语音提示,支持播放/停止/音量调节 - 实现0/10/20/30分钟循环定时倒计时,实时显示剩余时间,定时结束自动停机 - 优化电机控制:一档力度提升至75%,降档冲击时间延长至800ms,降档时触发100%冲击防卡顿 - 屏幕显示重构为分区动态更新:时间状态(Zone1)/档位(Zone2)/加热(Zone3),性能优化 - 新增LED指示:运行时RUN_LED以500ms周期闪烁,无效操作ERR_LED亮1秒 - 操作逻辑改进:所有操作需先设定时间,否则播放"请先设定时间"并亮ERR_LED - 加热控制由GPIO改为PWM(TIM1_CH1),占空比300 - 调整USART3波特率为9600适配MP3模块 - FreeRTOS任务优化:Motor周期500ms,Screen周期10ms,新增定时器秒Tick处理 - 完善开发文档:MP3集成指南、校验和验证脚本、文件重命名工具等 新增文件:mp3_driver.h/c, 多项开发文档 修改文件:freertos.c, gbk_text.h/c, motor_driver.c, screen.h/c, usart.c, gpio.c等
This commit is contained in:
@@ -0,0 +1,153 @@
|
||||
# MP3语音播放集成总结
|
||||
|
||||
## 语音文件列表
|
||||
|
||||
| 编号 | 文件名 | 语音内容 | 触发场景 |
|
||||
|------|--------|----------|----------|
|
||||
| 001 | 001.mp3 | 欢迎使用智能按摩器 | 系统启动初始化完成 |
|
||||
| 002 | 002.mp3 | 定时10分钟 | TIME_KEY设定10分钟 |
|
||||
| 003 | 003.mp3 | 定时20分钟 | TIME_KEY设定20分钟 |
|
||||
| 004 | 004.mp3 | 定时30分钟 | TIME_KEY设定30分钟 |
|
||||
| 005 | 005.mp3 | 定时已取消 | TIME_KEY取消定时 |
|
||||
| 006 | 006.mp3 | 按摩已停止 | 运行中按TIME_KEY取消 |
|
||||
| 007 | 007.mp3 | 按摩开始 | M_KEY从0档启动 |
|
||||
| 008 | 008.mp3 | 一档 | 升档或降档到1档 |
|
||||
| 009 | 009.mp3 | 二档 | 升档或降档到2档 |
|
||||
| 010 | 010.mp3 | 三档 | 升档到3档 |
|
||||
| 011 | 011.mp3 | 已到最大档位 | 3档时再按M_KEY |
|
||||
| 012 | 012.mp3 | 一档 | 降档从2档到1档 |
|
||||
| 013 | 013.mp3 | 二档 | 降档从3档到2档 |
|
||||
| 014 | 014.mp3 | 按摩停止 | M_KEYC7降档到0档 |
|
||||
| 015 | 015.mp3 | 加热已开启 | HOT_KEY开启加热 |
|
||||
| 016 | 016.mp3 | 加热已关闭 | HOT_KEY关闭加热 |
|
||||
| 017 | 017.mp3 | 请先设定时间 | 未定时操作按键 |
|
||||
| 018 | 018.mp3 | 定时结束,按摩结束 | 倒计时自动结束 |
|
||||
|
||||
## 代码修改位置
|
||||
|
||||
### 1. MP3任务初始化 (freertos.c:734)
|
||||
```c
|
||||
// 播放欢迎语音
|
||||
MP3_Play(1);
|
||||
```
|
||||
|
||||
### 2. TIME_KEY控制 - 时间设定 (freertos.c:502-538)
|
||||
```c
|
||||
// 播放定时语音
|
||||
if (timer_minutes == 10) {
|
||||
MP3_Play(2); // 定时10分钟
|
||||
} else if (timer_minutes == 20) {
|
||||
MP3_Play(3); // 定时20分钟
|
||||
} else if (timer_minutes == 30) {
|
||||
MP3_Play(4); // 定时30分钟
|
||||
} else if (timer_minutes == 0) {
|
||||
MP3_Play(5); // 定时已取消
|
||||
}
|
||||
|
||||
// 运行中取消定时
|
||||
MP3_Play(6); // 按摩已停止
|
||||
```
|
||||
|
||||
### 3. M_KEY控制 - 加档 (freertos.c:437-456)
|
||||
```c
|
||||
// 从0档启动
|
||||
if (Motor_GetGear() == 0) {
|
||||
MP3_Play(7); // 按摩开始
|
||||
} else {
|
||||
// 已到最大档位
|
||||
if (old_gear == 3) {
|
||||
MP3_Play(11); // 已到最大档位
|
||||
} else {
|
||||
// 档位变化语音
|
||||
MP3_Play(8); // 一档
|
||||
MP3_Play(9); // 二档
|
||||
MP3_Play(10); // 三档
|
||||
}
|
||||
}
|
||||
|
||||
// 未定时错误提示
|
||||
MP3_Play(17); // 请先设定时间
|
||||
```
|
||||
|
||||
### 4. M_KEYC7控制 - 降档 (freertos.c:458-477)
|
||||
```c
|
||||
// 降档语音
|
||||
if (new_gear == 0) {
|
||||
MP3_Play(14); // 按摩停止
|
||||
} else if (new_gear == 1) {
|
||||
MP3_Play(12); // 一档
|
||||
} else if (new_gear == 2) {
|
||||
MP3_Play(13); // 二档
|
||||
}
|
||||
|
||||
// 未定时错误提示
|
||||
MP3_Play(17); // 请先设定时间
|
||||
```
|
||||
|
||||
### 5. HOT_KEY控制 - 加热开关 (freertos.c:479-499)
|
||||
```c
|
||||
if (hot_state) {
|
||||
MP3_Play(15); // 加热已开启
|
||||
} else {
|
||||
MP3_Play(16); // 加热已关闭
|
||||
}
|
||||
|
||||
// 未定时错误提示
|
||||
MP3_Play(17); // 请先设定时间
|
||||
```
|
||||
|
||||
### 6. Timer_Tick定时器 (freertos.c:221-245)
|
||||
```c
|
||||
// 时间到自动停止
|
||||
if (remaining_seconds == 0) {
|
||||
MP3_Play(18); // 定时结束,按摩结束
|
||||
}
|
||||
```
|
||||
|
||||
## MP3驱动API
|
||||
|
||||
### 初始化函数
|
||||
```c
|
||||
HAL_StatusTypeDef MP3_Init(void);
|
||||
```
|
||||
- 初始化MP3模块
|
||||
- 设置音量30,选择TF卡音源
|
||||
|
||||
### 播放控制
|
||||
```c
|
||||
HAL_StatusTypeDef MP3_Play(uint16_t index); // 播放指定曲目 (1-9999)
|
||||
HAL_StatusTypeDef MP3_Stop(void); // 停止播放
|
||||
HAL_StatusTypeDef MP3_Pause(void); // 暂停播放
|
||||
```
|
||||
|
||||
### 参数设置
|
||||
```c
|
||||
HAL_StatusTypeDef MP3_SetVolume(uint8_t volume); // 设置音量 (0-30)
|
||||
HAL_StatusTypeDef MP3_SetSource(uint8_t source); // 设置音源 (1=U盘, 2=SD卡)
|
||||
```
|
||||
|
||||
## MP3命令格式
|
||||
|
||||
10字节固定格式:
|
||||
```
|
||||
字节0: 0x7E 帧头
|
||||
字节1: 0xFF 版本号
|
||||
字节2: 0x06 数据长度
|
||||
字节3: 命令码 (0x06=音量, 0x09=音源, 0x12=播放索引)
|
||||
字节4: 0x00 反馈
|
||||
字节5: 数据高字节
|
||||
字节6: 数据低字节
|
||||
字节7: 校验和高位
|
||||
字节8: 校验和低位
|
||||
字节9: 0xEF 帧尾
|
||||
```
|
||||
|
||||
校验和算法: `0x10000 - sum(字节1~6)`
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. 确保TF卡中音频文件命名为001.mp3, 002.mp3, ... 格式
|
||||
2. MP3模块需等待2秒上电稳定
|
||||
3. USART3波特率设置为9600
|
||||
4. 首次播放建议测试`MP3_Play(1)`是否正常工作
|
||||
5. 所有语音播放函数已集成到相应按键处理逻辑中
|
||||
209
Development_Docs/Serial_Screen_Docs/LIST/mp3_usage_example.py
Normal file
209
Development_Docs/Serial_Screen_Docs/LIST/mp3_usage_example.py
Normal file
@@ -0,0 +1,209 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
MP3播放函数使用示例
|
||||
展示如何在代码中使用封装好的MP3播放函数
|
||||
"""
|
||||
|
||||
# ==========================================
|
||||
# C代码使用示例
|
||||
# ==========================================
|
||||
|
||||
example_code = """
|
||||
// 在 freertos.c 中使用 MP3 播放函数
|
||||
|
||||
#include "mp3_driver.h"
|
||||
|
||||
// ==========================================
|
||||
// 1. 初始化MP3模块 (在 MP3 任务中)
|
||||
// ==========================================
|
||||
void MP3(void *argument) {
|
||||
// 等待模块上电稳定
|
||||
osDelay(2000);
|
||||
|
||||
// 初始化MP3模块
|
||||
HAL_StatusTypeDef ret = MP3_Init();
|
||||
if (ret != HAL_OK) {
|
||||
elog_e("MP3", "MP3模块初始化失败");
|
||||
return;
|
||||
}
|
||||
|
||||
// ... 其他代码
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// 2. 播放指定曲目
|
||||
// ==========================================
|
||||
// 在 Sensor 任务中播放语音
|
||||
MP3_Play(1); // 播放第1首: "欢迎使用智能按摩器"
|
||||
MP3_Play(2); // 播放第2首: "定时10分钟"
|
||||
MP3_Play(3); // 播放第3首: "定时20分钟"
|
||||
// ... 依此类推
|
||||
|
||||
// ==========================================
|
||||
// 3. 其他常用函数
|
||||
// ==========================================
|
||||
MP3_Stop(); // 停止播放
|
||||
MP3_Pause(); // 暂停播放
|
||||
MP3_SetVolume(20); // 设置音量 (0-30)
|
||||
MP3_SetSource(MP3_SOURCE_SD_CARD); // 选择TF卡音源
|
||||
|
||||
// ==========================================
|
||||
// 4. 实际应用示例:时间设定
|
||||
// ==========================================
|
||||
if (timer_minutes == 10) {
|
||||
MP3_Play(2); // "定时10分钟"
|
||||
} else if (timer_minutes == 20) {
|
||||
MP3_Play(3); // "定时20分钟"
|
||||
} else if (timer_minutes == 30) {
|
||||
MP3_Play(4); // "定时30分钟"
|
||||
} else if (timer_minutes == 0) {
|
||||
MP3_Play(5); // "定时已取消"
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// 5. 实际应用示例:档位控制
|
||||
// ==========================================
|
||||
if (current_gear == 1) {
|
||||
MP3_Play(8); // "一档"
|
||||
} else if (current_gear == 2) {
|
||||
MP3_Play(9); // "二档"
|
||||
} else if (current_gear == 3) {
|
||||
MP3_Play(10); // "三档"
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// 6. 实际应用示例:加热控制
|
||||
// ==========================================
|
||||
if (hot_state) {
|
||||
MP3_Play(15); // "加热已开启"
|
||||
} else {
|
||||
MP3_Play(16); // "加热已关闭"
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// 7. 实际应用示例:定时结束
|
||||
// ==========================================
|
||||
if (remaining_seconds == 0) {
|
||||
MP3_Play(18); // "定时结束,按摩结束"
|
||||
Motor_SetGear(0); // 停止电机
|
||||
// ... 其他清理工作
|
||||
}
|
||||
"""
|
||||
|
||||
# ==========================================
|
||||
# 语音文件映射表
|
||||
# ==========================================
|
||||
|
||||
voice_mapping = """
|
||||
+------+--------------------------+
|
||||
| 编号 | 语音内容 |
|
||||
+------+--------------------------+
|
||||
| 001 | 欢迎使用智能按摩器 |
|
||||
| 002 | 定时10分钟 |
|
||||
| 003 | 定时20分钟 |
|
||||
| 004 | 定时30分钟 |
|
||||
| 005 | 定时已取消 |
|
||||
| 006 | 按摩已停止 |
|
||||
| 007 | 按摩开始 |
|
||||
| 008 | 一档 |
|
||||
| 009 | 二档 |
|
||||
| 010 | 三档 |
|
||||
| 011 | 已到最大档位 (可选) |
|
||||
| 012 | 一档 (降档用) |
|
||||
| 013 | 二档 (降档用) |
|
||||
| 014 | 按摩停止 |
|
||||
| 015 | 加热已开启 |
|
||||
| 016 | 加热已关闭 |
|
||||
| 017 | 请先设定时间 |
|
||||
| 018 | 定时结束,按摩结束 |
|
||||
+------+--------------------------+
|
||||
"""
|
||||
|
||||
# ==========================================
|
||||
# MP3命令格式说明
|
||||
# ==========================================
|
||||
|
||||
command_format = """
|
||||
MP3命令格式 (10字节):
|
||||
====================================================
|
||||
字节 | 内容 | 说明
|
||||
-----+---------------+---------------------------
|
||||
0 | 0x7E | 帧头 (固定)
|
||||
1 | 0xFF | 版本号 (固定)
|
||||
2 | 0x06 | 数据长度 (固定)
|
||||
3 | 命令码 | 0x06=音量, 0x09=音源, 0x12=播放索引
|
||||
4 | 0x00 | 反馈 (固定)
|
||||
5 | 数据高字节 | 根据命令不同
|
||||
6 | 数据低字节 | 根据命令不同
|
||||
7 | 校验和高位 | 计算得出
|
||||
8 | 校验和低位 | 计算得出
|
||||
9 | 0xEF | 帧尾 (固定)
|
||||
====================================================
|
||||
|
||||
校验和计算方法:
|
||||
1. 从索引1 (0xFF) 开始累加到索引6 (数据低字节)
|
||||
2. 用 0x10000 减去累加结果
|
||||
3. 高位在前,低位在后
|
||||
|
||||
示例 (播放文件1):
|
||||
数据: 0xFF 0x06 0x12 0x00 0x00 0x01
|
||||
求和: 0xFF + 0x06 + 0x12 + 0x00 + 0x00 + 0x01 = 0x0118
|
||||
校验: 0x10000 - 0x0118 = 0xFEE8
|
||||
高位: 0xFE, 低位: 0xE8
|
||||
|
||||
完整命令: 0x7E 0xFF 0x06 0x12 0x00 0x00 0x01 0xFE 0xE8 0xEF
|
||||
"""
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("=" * 70)
|
||||
print("MP3播放函数使用示例".center(70))
|
||||
print("=" * 70)
|
||||
|
||||
print("\n" + "=" * 70)
|
||||
print("1. C代码使用示例")
|
||||
print("=" * 70)
|
||||
print(example_code)
|
||||
|
||||
print("\n" + "=" * 70)
|
||||
print("2. 语音文件映射表")
|
||||
print("=" * 70)
|
||||
print(voice_mapping)
|
||||
|
||||
print("\n" + "=" * 70)
|
||||
print("3. MP3命令格式说明")
|
||||
print("=" * 70)
|
||||
print(command_format)
|
||||
|
||||
print("\n" + "=" * 70)
|
||||
print("4. API 函数列表")
|
||||
print("=" * 70)
|
||||
print("""
|
||||
HAL_StatusTypeDef MP3_Init(void);
|
||||
- 初始化MP3模块
|
||||
- 返回: HAL_OK(成功) 或 HAL_ERROR(失败)
|
||||
|
||||
HAL_StatusTypeDef MP3_Play(uint16_t index);
|
||||
- 播放指定曲目 (索引范围: 1-9999)
|
||||
- 参数: index - 曲目编号
|
||||
- 返回: HAL_OK(成功) 或 HAL_ERROR(失败)
|
||||
|
||||
HAL_StatusTypeDef MP3_Stop(void);
|
||||
- 停止当前播放
|
||||
- 返回: HAL_OK(成功) 或 HAL_ERROR(失败)
|
||||
|
||||
HAL_StatusTypeDef MP3_Pause(void);
|
||||
- 暂停当前播放
|
||||
- 返回: HAL_OK(成功) 或 HAL_ERROR(失败)
|
||||
|
||||
HAL_StatusTypeDef MP3_SetVolume(uint8_t volume);
|
||||
- 设置音量 (范围: 0-30)
|
||||
- 参数: volume - 音量值
|
||||
- 返回: HAL_OK(成功) 或 HAL_ERROR(失败)
|
||||
|
||||
HAL_StatusTypeDef MP3_SetSource(uint8_t source);
|
||||
- 设置音源
|
||||
- 参数: source - MP3_SOURCE_U_DISK(1) 或 MP3_SOURCE_SD_CARD(2)
|
||||
- 返回: HAL_OK(成功) 或 HAL_ERROR(失败)
|
||||
""")
|
||||
print("=" * 70)
|
||||
222
Development_Docs/Serial_Screen_Docs/LIST/rename_mp3_files.py
Normal file
222
Development_Docs/Serial_Screen_Docs/LIST/rename_mp3_files.py
Normal file
@@ -0,0 +1,222 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
音频文件按时间戳排序并重命名工具
|
||||
功能:
|
||||
1. 查找指定路径下的MP3文件
|
||||
2. 按文件创建时间从先到后排序
|
||||
3. 显示排序后的文件列表
|
||||
4. 用户可指定起始编号(如0001或0002)
|
||||
5. 重命名文件为0001.mp3, 0002.mp3等格式
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import glob
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def find_mp3_files(directory):
|
||||
"""查找指定目录下的所有MP3文件"""
|
||||
mp3_files = glob.glob(os.path.join(directory, "*.mp3"))
|
||||
return mp3_files
|
||||
|
||||
|
||||
def is_timestamp_filename(filename):
|
||||
"""判断文件名是否为时间戳格式(纯数字.mp3)"""
|
||||
# 去除扩展名
|
||||
name_without_ext = os.path.splitext(filename)[0]
|
||||
# 检查是否为纯数字且至少为10位(时间戳)
|
||||
return name_without_ext.isdigit() and len(name_without_ext) >= 10
|
||||
|
||||
|
||||
def is_numbered_filename(filename):
|
||||
"""判断文件名是否为编号格式(如0001.mp3)"""
|
||||
# 去除扩展名
|
||||
name_without_ext = os.path.splitext(filename)[0]
|
||||
# 检查是否为纯数字,长度为3-4位(编号格式)
|
||||
return name_without_ext.isdigit() and len(name_without_ext) >= 3 and len(name_without_ext) <= 4
|
||||
|
||||
|
||||
def filter_timestamp_files(files):
|
||||
"""筛选出时间戳格式的MP3文件,跳过编号格式的文件"""
|
||||
timestamp_files = []
|
||||
skipped_files = []
|
||||
|
||||
for file_path in files:
|
||||
filename = os.path.basename(file_path)
|
||||
if is_numbered_filename(filename):
|
||||
skipped_files.append(filename)
|
||||
elif is_timestamp_filename(filename):
|
||||
timestamp_files.append(file_path)
|
||||
else:
|
||||
skipped_files.append(filename)
|
||||
|
||||
return timestamp_files, skipped_files
|
||||
|
||||
|
||||
def sort_files_by_time(files):
|
||||
"""按文件创建时间从先到后排序"""
|
||||
# 获取文件信息并按创建时间排序
|
||||
file_info = []
|
||||
for file_path in files:
|
||||
stat = os.stat(file_path)
|
||||
file_info.append({
|
||||
'path': file_path,
|
||||
'name': os.path.basename(file_path),
|
||||
'ctime': stat.st_ctime # 创建时间
|
||||
})
|
||||
|
||||
# 按创建时间升序排序
|
||||
sorted_files = sorted(file_info, key=lambda x: x['ctime'])
|
||||
return sorted_files
|
||||
|
||||
|
||||
def display_file_list(sorted_files, skipped_files):
|
||||
"""显示排序后的文件列表"""
|
||||
print("\n" + "=" * 80)
|
||||
if skipped_files:
|
||||
print(f"已跳过 {len(skipped_files)} 个非时间戳格式的文件:")
|
||||
for filename in skipped_files:
|
||||
print(f" 跳过: {filename}")
|
||||
print()
|
||||
|
||||
print(f"找到 {len(sorted_files)} 个时间戳格式的MP3文件,按创建时间排序:")
|
||||
print("=" * 80)
|
||||
for i, file_info in enumerate(sorted_files, 1):
|
||||
import time
|
||||
time_str = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(file_info['ctime']))
|
||||
print(f"{i:3d}. {file_info['name']:<50} 创建时间: {time_str}")
|
||||
print("=" * 80)
|
||||
|
||||
|
||||
def get_start_number():
|
||||
"""获取用户指定的起始编号"""
|
||||
while True:
|
||||
try:
|
||||
user_input = input("\n请输入起始编号(如1或2,将转换为0001/0002格式): ").strip()
|
||||
if not user_input:
|
||||
print("输入不能为空,请重新输入!")
|
||||
continue
|
||||
|
||||
start_num = int(user_input)
|
||||
if start_num < 1 or start_num > 9999:
|
||||
print("编号必须在1-9999之间,请重新输入!")
|
||||
continue
|
||||
|
||||
return start_num
|
||||
except ValueError:
|
||||
print("请输入有效的数字!")
|
||||
|
||||
|
||||
def rename_files(sorted_files, start_number):
|
||||
"""重命名文件为编号格式"""
|
||||
print("\n开始重命名文件...")
|
||||
print("=" * 80)
|
||||
|
||||
success_count = 0
|
||||
error_count = 0
|
||||
|
||||
for i, file_info in enumerate(sorted_files, start_number):
|
||||
old_path = file_info['path']
|
||||
directory = os.path.dirname(old_path)
|
||||
new_name = f"{i:04d}.mp3"
|
||||
new_path = os.path.join(directory, new_name)
|
||||
|
||||
try:
|
||||
os.rename(old_path, new_path)
|
||||
print(f"✓ {file_info['name']} -> {new_name}")
|
||||
success_count += 1
|
||||
except Exception as e:
|
||||
print(f"✗ {file_info['name']} 重命名失败: {e}")
|
||||
error_count += 1
|
||||
|
||||
print("=" * 80)
|
||||
print(f"重命名完成! 成功: {success_count}, 失败: {error_count}")
|
||||
|
||||
|
||||
def confirm_operation():
|
||||
"""确认是否执行重命名操作"""
|
||||
while True:
|
||||
user_input = input("\n确认要重命名这些文件吗? (y/n): ").strip().lower()
|
||||
if user_input in ['y', 'yes']:
|
||||
return True
|
||||
elif user_input in ['n', 'no']:
|
||||
return False
|
||||
else:
|
||||
print("请输入 y 或 n")
|
||||
|
||||
|
||||
def main():
|
||||
print("=" * 80)
|
||||
print("音频文件按时间戳排序并重命名工具".center(80))
|
||||
print("=" * 80)
|
||||
|
||||
# 获取目标目录
|
||||
while True:
|
||||
directory = input("\n请输入要处理的MP3文件所在路径: ").strip()
|
||||
directory = directory.strip('"').strip("'") # 去除可能的引号
|
||||
|
||||
if not directory:
|
||||
print("路径不能为空!")
|
||||
continue
|
||||
|
||||
if not os.path.isdir(directory):
|
||||
print(f"路径不存在: {directory}")
|
||||
continue
|
||||
|
||||
break
|
||||
|
||||
# 查找MP3文件
|
||||
mp3_files = find_mp3_files(directory)
|
||||
|
||||
if not mp3_files:
|
||||
print(f"\n在路径 {directory} 中未找到MP3文件!")
|
||||
return
|
||||
|
||||
# 筛选时间戳格式文件,跳过编号格式文件
|
||||
timestamp_files, skipped_files = filter_timestamp_files(mp3_files)
|
||||
|
||||
if not timestamp_files:
|
||||
print(f"\n在路径 {directory} 中未找到时间戳格式的MP3文件!")
|
||||
if skipped_files:
|
||||
print(f"但找到 {len(skipped_files)} 个已编号格式的文件:")
|
||||
for filename in skipped_files:
|
||||
print(f" {filename}")
|
||||
return
|
||||
|
||||
# 按时间排序
|
||||
sorted_files = sort_files_by_time(timestamp_files)
|
||||
|
||||
# 显示文件列表
|
||||
display_file_list(sorted_files, skipped_files)
|
||||
|
||||
# 获取起始编号
|
||||
start_number = get_start_number()
|
||||
|
||||
# 预览重命名结果
|
||||
print("\n重命名预览:")
|
||||
print("-" * 80)
|
||||
for i, file_info in enumerate(sorted_files, start_number):
|
||||
new_name = f"{i:04d}.mp3"
|
||||
print(f"{i:4d}. {file_info['name']} -> {new_name}")
|
||||
print("-" * 80)
|
||||
|
||||
# 确认操作
|
||||
if confirm_operation():
|
||||
rename_files(sorted_files, start_number)
|
||||
else:
|
||||
print("\n操作已取消!")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
main()
|
||||
except KeyboardInterrupt:
|
||||
print("\n\n程序被用户中断!")
|
||||
sys.exit(0)
|
||||
except Exception as e:
|
||||
print(f"\n程序执行出错: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
sys.exit(1)
|
||||
118
Development_Docs/Serial_Screen_Docs/LIST/verify_mp3_checksum.py
Normal file
118
Development_Docs/Serial_Screen_Docs/LIST/verify_mp3_checksum.py
Normal file
@@ -0,0 +1,118 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
MP3命令校验和验证工具
|
||||
验证校验和算法是否正确
|
||||
"""
|
||||
|
||||
def calc_checksum(cmd):
|
||||
"""
|
||||
计算MP3命令校验和
|
||||
算法:从版本号(索引1)开始,到数据结束,所有字节相加后取反再加1
|
||||
返回:高位和低位
|
||||
"""
|
||||
sum_val = 0
|
||||
|
||||
# 从版本号(索引1)开始累加,到数据结束(索引6)
|
||||
for i in range(1, 7):
|
||||
sum_val += cmd[i]
|
||||
|
||||
# 取反加1 (即0x10000 - sum_val)
|
||||
sum_val = 0x10000 - sum_val
|
||||
|
||||
# 高位在前,低位在后
|
||||
high = (sum_val >> 8) & 0xFF
|
||||
low = sum_val & 0xFF
|
||||
|
||||
return high, low
|
||||
|
||||
|
||||
def verify_command(cmd, name):
|
||||
"""验证命令的校验和是否正确"""
|
||||
print(f"\n{'='*60}")
|
||||
print(f"验证命令: {name}")
|
||||
print(f"{'='*60}")
|
||||
|
||||
# 提取原始数据
|
||||
data = cmd[:7]
|
||||
original_high = cmd[7]
|
||||
original_low = cmd[8]
|
||||
|
||||
# 显示命令字节
|
||||
print(f"命令字节: ", end="")
|
||||
for i, byte in enumerate(cmd):
|
||||
print(f"0x{byte:02X} ", end="")
|
||||
if i == 3:
|
||||
print("| ", end="")
|
||||
print()
|
||||
|
||||
# 显示数据部分
|
||||
print(f"数据部分 (索引1-6): ", end="")
|
||||
for i in range(1, 7):
|
||||
print(f"0x{cmd[i]:02X} ", end="")
|
||||
if i == 3:
|
||||
print("| ", end="")
|
||||
print()
|
||||
|
||||
# 显示求和过程
|
||||
sum_val = sum(cmd[1:7])
|
||||
print(f"\n求和: {sum_val} (0x{sum_val:04X})")
|
||||
print(f"取反加1: 0x10000 - 0x{sum_val:04X} = 0x{0x10000 - sum_val:04X}")
|
||||
|
||||
# 计算校验和
|
||||
calc_high, calc_low = calc_checksum(cmd)
|
||||
print(f"\n计算校验和: 高位=0x{calc_high:02X}, 低位=0x{calc_low:02X}")
|
||||
print(f"原始校验和: 高位=0x{original_high:02X}, 低位=0x{original_low:02X}")
|
||||
|
||||
# 验证
|
||||
if calc_high == original_high and calc_low == original_low:
|
||||
print("[OK] 校验和验证通过!")
|
||||
return True
|
||||
else:
|
||||
print("[FAIL] 校验和验证失败!")
|
||||
return False
|
||||
|
||||
|
||||
def main():
|
||||
print("="*60)
|
||||
print("MP3命令校验和验证工具")
|
||||
print("="*60)
|
||||
|
||||
# 命令1:开启声音 (音量30)
|
||||
# 0x7E 0xFF 0x06 0x06 0x00 0x00 0x1E 0xFE 0xD7 0xEF
|
||||
cmd1 = [0x7E, 0xFF, 0x06, 0x06, 0x00, 0x00, 0x1E, 0xFE, 0xD7, 0xEF]
|
||||
verify_command(cmd1, "开启声音 (音量30)")
|
||||
|
||||
# 命令2:选择TF卡
|
||||
# 0x7E 0xFF 0x06 0x09 0x00 0x00 0x02 0xFE 0xF0 0xEF
|
||||
cmd2 = [0x7E, 0xFF, 0x06, 0x09, 0x00, 0x00, 0x02, 0xFE, 0xF0, 0xEF]
|
||||
verify_command(cmd2, "选择TF卡")
|
||||
|
||||
# 命令3:播放文件1
|
||||
# 0x7E 0xFF 0x06 0x12 0x00 0x00 0x01 0xFE 0xE8 0xEF
|
||||
cmd3 = [0x7E, 0xFF, 0x06, 0x12, 0x00, 0x00, 0x01, 0xFE, 0xE8, 0xEF]
|
||||
verify_command(cmd3, "播放文件1")
|
||||
|
||||
# 测试:生成其他曲目的播放命令
|
||||
print(f"\n{'='*60}")
|
||||
print("生成其他曲目的播放命令")
|
||||
print(f"{'='*60}")
|
||||
|
||||
for index in [1, 10, 100, 9999]:
|
||||
play_cmd = [0x7E, 0xFF, 0x06, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEF]
|
||||
play_cmd[5] = (index >> 8) & 0xFF
|
||||
play_cmd[6] = index & 0xFF
|
||||
high, low = calc_checksum(play_cmd)
|
||||
play_cmd[7] = high
|
||||
play_cmd[8] = low
|
||||
|
||||
print(f"\n曲目 {index:4d}: ", end="")
|
||||
for byte in play_cmd:
|
||||
print(f"0x{byte:02X} ", end="")
|
||||
print()
|
||||
|
||||
print(f"\n{'='*60}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -2,21 +2,23 @@
|
||||
text_device_name=智能按摩仪
|
||||
text_loading = 加载中
|
||||
|
||||
text_massage_on = 按摩启动
|
||||
text_massage_off = 按摩停止
|
||||
text_massage_off = 按摩:设备停止
|
||||
|
||||
text_heat_on = 加热启动
|
||||
text_heat_off = 加热停止
|
||||
text_heat_on = 加热:开始加热
|
||||
text_heat_off = 加热:停止加热
|
||||
|
||||
text_gear = %d级
|
||||
text_time = %d分钟
|
||||
text_cont = 持续
|
||||
text_gear_1 =按摩:当前低档位
|
||||
text_gear_2 =按摩:当前中档位
|
||||
text_gear_3 =按摩:当前高档位
|
||||
|
||||
text_remaining = 剩余%d分
|
||||
|
||||
text_running = 运行中
|
||||
text_paused = 已暂停
|
||||
text_ended = 已结束
|
||||
text_time_1 =时间:设定10分钟
|
||||
text_time_2 =时间:设定20分钟
|
||||
text_time_3 =时间:设定30分钟
|
||||
|
||||
text_remaining = 时间:剩余%d分
|
||||
|
||||
text_stop = 机器运行已停止
|
||||
|
||||
|
||||
|
||||
|
||||
BIN
Development_Docs/测试记录/语音列表.png
Normal file
BIN
Development_Docs/测试记录/语音列表.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 69 KiB |
BIN
Development_Docs/测试记录/运行日志.png
Normal file
BIN
Development_Docs/测试记录/运行日志.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 190 KiB |
Reference in New Issue
Block a user