2025-08-21 08:15:01 +00:00
<!DOCTYPE html>
< html >
< head >
< title > README.md< / title >
< meta http-equiv = "Content-type" content = "text/html;charset=UTF-8" >
< style >
/* https://github.com/microsoft/vscode/blob/master/extensions/markdown-language-features/media/markdown.css */
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
body {
font-family: var(--vscode-markdown-font-family, -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "Ubuntu", "Droid Sans", sans-serif);
font-size: var(--vscode-markdown-font-size, 14px);
padding: 0 26px;
line-height: var(--vscode-markdown-line-height, 22px);
word-wrap: break-word;
}
#code-csp-warning {
position: fixed;
top: 0;
right: 0;
color: white;
margin: 16px;
text-align: center;
font-size: 12px;
font-family: sans-serif;
background-color:#444444;
cursor: pointer;
padding: 6px;
box-shadow: 1px 1px 1px rgba(0,0,0,.25);
}
#code-csp-warning:hover {
text-decoration: none;
background-color:#007acc;
box-shadow: 2px 2px 2px rgba(0,0,0,.25);
}
body.scrollBeyondLastLine {
margin-bottom: calc(100vh - 22px);
}
body.showEditorSelection .code-line {
position: relative;
}
body.showEditorSelection .code-active-line:before,
body.showEditorSelection .code-line:hover:before {
content: "";
display: block;
position: absolute;
top: 0;
left: -12px;
height: 100%;
}
body.showEditorSelection li.code-active-line:before,
body.showEditorSelection li.code-line:hover:before {
left: -30px;
}
.vscode-light.showEditorSelection .code-active-line:before {
border-left: 3px solid rgba(0, 0, 0, 0.15);
}
.vscode-light.showEditorSelection .code-line:hover:before {
border-left: 3px solid rgba(0, 0, 0, 0.40);
}
.vscode-light.showEditorSelection .code-line .code-line:hover:before {
border-left: none;
}
.vscode-dark.showEditorSelection .code-active-line:before {
border-left: 3px solid rgba(255, 255, 255, 0.4);
}
.vscode-dark.showEditorSelection .code-line:hover:before {
border-left: 3px solid rgba(255, 255, 255, 0.60);
}
.vscode-dark.showEditorSelection .code-line .code-line:hover:before {
border-left: none;
}
.vscode-high-contrast.showEditorSelection .code-active-line:before {
border-left: 3px solid rgba(255, 160, 0, 0.7);
}
.vscode-high-contrast.showEditorSelection .code-line:hover:before {
border-left: 3px solid rgba(255, 160, 0, 1);
}
.vscode-high-contrast.showEditorSelection .code-line .code-line:hover:before {
border-left: none;
}
img {
max-width: 100%;
max-height: 100%;
}
a {
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
a:focus,
input:focus,
select:focus,
textarea:focus {
outline: 1px solid -webkit-focus-ring-color;
outline-offset: -1px;
}
hr {
border: 0;
height: 2px;
border-bottom: 2px solid;
}
h1 {
padding-bottom: 0.3em;
line-height: 1.2;
border-bottom-width: 1px;
border-bottom-style: solid;
}
h1, h2, h3 {
font-weight: normal;
}
table {
border-collapse: collapse;
}
table > thead > tr > th {
text-align: left;
border-bottom: 1px solid;
}
table > thead > tr > th,
table > thead > tr > td,
table > tbody > tr > th,
table > tbody > tr > td {
padding: 5px 10px;
}
table > tbody > tr + tr > td {
border-top: 1px solid;
}
blockquote {
margin: 0 7px 0 5px;
padding: 0 16px 0 10px;
border-left-width: 5px;
border-left-style: solid;
}
code {
font-family: Menlo, Monaco, Consolas, "Droid Sans Mono", "Courier New", monospace, "Droid Sans Fallback";
font-size: 1em;
line-height: 1.357em;
}
body.wordWrap pre {
white-space: pre-wrap;
}
pre:not(.hljs),
pre.hljs code > div {
padding: 16px;
border-radius: 3px;
overflow: auto;
}
pre code {
color: var(--vscode-editor-foreground);
tab-size: 4;
}
/** Theming */
.vscode-light pre {
background-color: rgba(220, 220, 220, 0.4);
}
.vscode-dark pre {
background-color: rgba(10, 10, 10, 0.4);
}
.vscode-high-contrast pre {
background-color: rgb(0, 0, 0);
}
.vscode-high-contrast h1 {
border-color: rgb(0, 0, 0);
}
.vscode-light table > thead > tr > th {
border-color: rgba(0, 0, 0, 0.69);
}
.vscode-dark table > thead > tr > th {
border-color: rgba(255, 255, 255, 0.69);
}
.vscode-light h1,
.vscode-light hr,
.vscode-light table > tbody > tr + tr > td {
border-color: rgba(0, 0, 0, 0.18);
}
.vscode-dark h1,
.vscode-dark hr,
.vscode-dark table > tbody > tr + tr > td {
border-color: rgba(255, 255, 255, 0.18);
}
< / style >
< style >
/* Tomorrow Theme */
/* http://jmblog.github.com/color-themes-for-google-code-highlightjs */
/* Original theme - https://github.com/chriskempson/tomorrow-theme */
/* Tomorrow Comment */
.hljs-comment,
.hljs-quote {
color: #8e908c;
}
/* Tomorrow Red */
.hljs-variable,
.hljs-template-variable,
.hljs-tag,
.hljs-name,
.hljs-selector-id,
.hljs-selector-class,
.hljs-regexp,
.hljs-deletion {
color: #c82829;
}
/* Tomorrow Orange */
.hljs-number,
.hljs-built_in,
.hljs-builtin-name,
.hljs-literal,
.hljs-type,
.hljs-params,
.hljs-meta,
.hljs-link {
color: #f5871f;
}
/* Tomorrow Yellow */
.hljs-attribute {
color: #eab700;
}
/* Tomorrow Green */
.hljs-string,
.hljs-symbol,
.hljs-bullet,
.hljs-addition {
color: #718c00;
}
/* Tomorrow Blue */
.hljs-title,
.hljs-section {
color: #4271ae;
}
/* Tomorrow Purple */
.hljs-keyword,
.hljs-selector-tag {
color: #8959a8;
}
.hljs {
display: block;
overflow-x: auto;
color: #4d4d4c;
padding: 0.5em;
}
.hljs-emphasis {
font-style: italic;
}
.hljs-strong {
font-weight: bold;
}
< / style >
< style >
/*
* Markdown PDF CSS
*/
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "Ubuntu", "Droid Sans", sans-serif, "Meiryo";
padding: 0 12px;
}
pre {
background-color: #f8f8f8;
border: 1px solid #cccccc;
border-radius: 3px;
overflow-x: auto;
white-space: pre-wrap;
overflow-wrap: break-word;
}
pre:not(.hljs) {
padding: 23px;
line-height: 19px;
}
blockquote {
background: rgba(127, 127, 127, 0.1);
border-color: rgba(0, 122, 204, 0.5);
}
.emoji {
height: 1.4em;
}
code {
font-size: 14px;
line-height: 19px;
}
/* for inline code */
:not(pre):not(.hljs) > code {
color: #C9AE75; /* Change the old color so it seems less like an error */
font-size: inherit;
}
/* Page Break : use < div class = "page" / > to insert page break
-------------------------------------------------------- */
.page {
page-break-after: always;
}
< / style >
< script src = "https://unpkg.com/mermaid/dist/mermaid.min.js" > < / script >
< / head >
< body >
< script >
mermaid.initialize({
startOnLoad: true,
theme: document.body.classList.contains('vscode-dark') || document.body.classList.contains('vscode-high-contrast')
? 'dark'
: 'default'
});
< / script >
< h1 id = "%E5%BC%95%E8%84%9A%E6%8E%92%E5%88%97" > 引脚排列< / h1 >
< p > < img src = "./pin" alt = "引脚定义" > < / p >
2025-08-27 16:29:07 +00:00
< h1 id = "%E4%BD%BF%E7%94%A8%E6%B3%A8%E6%84%8F" > 使用注意:< / h1 >
< pre > < code > 1. TF卡和SPI Flash只支持一个, 下单请注意备注。
2. 开机有提示音“咚”。
3. 默认循环方式为不循环,播放一次后结束。
4. BUSY引脚指示当前播放状态, 低电平为正在播放, 高电平为未播放, 也可使用此引脚控制功放的MUTE, 但需实测是否有呑字现象。
< / code > < / pre >
2025-08-21 08:15:01 +00:00
< h2 id = "uart-%E9%80%9A%E8%AE%AF%E5%8D%8F%E8%AE%AE-115200-n-8-1" > UART 通讯协议 115200 N 8 1< / h2 >
< ul >
< li > 发送
< table >
< thead >
< tr >
< th > head< / th >
< th > length< / th >
< th > CMD< / th >
< th > DATA< / th >
< th > XOR< / th >
< th > tail< / th >
< / tr >
< / thead >
< tbody >
< tr >
< td > 02< / td >
< td > 04< / td >
< td > A0< / td >
< td > 01< / td >
< td > FF< / td >
< td > 03< / td >
< / tr >
< / tbody >
< / table >
< / li >
< li > 回复
< table >
< thead >
< tr >
< th > head< / th >
< th > length< / th >
< th > CMD< / th >
< th > ERR< / th >
< th > XOR< / th >
< th > tail< / th >
< / tr >
< / thead >
< tbody >
< tr >
< td > 02< / td >
< td > 04< / td >
< td > A0< / td >
< td > 00< / td >
< td > FF< / td >
< td > 03< / td >
< / tr >
< / tbody >
< / table >
< / li >
< li > 解释:
< ul >
< li > heat: 帧头 一个字节 0x02< / li >
< li > tail: 帧尾 一个字节 0x03< / li >
< li > length: 长度 一个字节 从长度到校验的长度,包含长度和校验< / li >
< li > CMD: 命令字 一个字节< / li >
< li > DATA: 数据区, 不定长< / li >
< li > XOR: 校验, 一个字节的xor校验,调试可以填写一个字节FF, 当此字节为FF时, 为不校验< / li >
< / ul >
< / li >
< li > 指令列表:
< ul >
< li > 0xA0 播放< / li >
< li > 0xA1 暂停< / li >
< li > 0xA2 播放/暂停< / li >
< li > 0xA3 下一曲< / li >
< li > 0xA4 上一曲< / li >
< li > 0xA5 停止< / li >
< li > 0xA6 音量加< / li >
< li > 0xA7 音量减< / li >
< li > 0xA8 设置音量< / li >
< li > 0xA9 静音< / li >
< li > 0xAA 解除静音< / li >
< li > 0xAB 播放指定曲目(通配符)< / li >
< li > 0xAC 播放指定曲目(带扩展名)< / li >
< li > 0xAD 播放指定曲目(数字)< / li >
2025-08-27 16:29:07 +00:00
< li > 0xAE 播放循环< / li >
< li > 0xB0 获取当前磁盘文件总数< / li >
< li > 0xB1 获取当前磁盘文件夹数量< / li >
< li > 0xB2 获取当前播放文件夹内文件数量< / li >
< li > 0xB3 获取当前播放曲目总时间< / li >
< li > 0xB4 获取当前播放曲目时间< / li >
2025-08-21 08:15:01 +00:00
< / ul >
< / li >
< / ul >
< h2 id = "%E6%8C%87%E4%BB%A4%E8%AF%A6%E8%BF%B0" > 指令详述< / h2 >
< ul >
2025-08-27 16:29:07 +00:00
< li > 播放控制指令: 0xA0/A1/A2/A3/A4/A5/A6/A7/A9/AA
2025-08-21 08:15:01 +00:00
< table >
< thead >
< tr >
< th > HEAD< / th >
< th > LENGTH< / th >
< th > CMD< / th >
< th > CHK< / th >
< th > TAIL< / th >
< / tr >
< / thead >
< tbody >
< tr >
< td > 0x02< / td >
< td > 1Byte< / td >
< td > 0xA0< / td >
< td > 1Byte< / td >
< td > 0x03< / td >
< / tr >
< / tbody >
< / table >
< / li >
2025-08-27 16:29:07 +00:00
< li > 播放循环: 0xAE
2025-08-21 08:15:01 +00:00
< table >
< thead >
< tr >
< th > HEAD< / th >
< th > LENGTH< / th >
< th > CMD< / th >
2025-08-27 16:29:07 +00:00
< th > CYCLE< / th >
2025-08-21 08:15:01 +00:00
< th > CHK< / th >
< th > TAIL< / th >
< / tr >
< / thead >
< tbody >
< tr >
< td > 0x02< / td >
< td > 1Byte< / td >
2025-08-27 16:29:07 +00:00
< td > 0xAE< / td >
< td > 1Byte< / td >
2025-08-21 08:15:01 +00:00
< td > 1Byte< / td >
< td > 0x03< / td >
< / tr >
< / tbody >
< / table >
2025-08-27 16:29:07 +00:00
< ul >
< li > CYCLE : 0-不循环, 1-全部循环 2-单曲循环, 3-文件夹循环, 4-随机< / li >
< / ul >
2025-08-21 08:15:01 +00:00
< / li >
< li > 设置音量: 0xA8
< table >
< thead >
< tr >
< th > HEAD< / th >
< th > LENGTH< / th >
< th > CMD< / th >
< th > VOLUME< / th >
< th > CHK< / th >
< th > TAIL< / th >
< / tr >
< / thead >
< tbody >
< tr >
< td > 0x02< / td >
< td > 1Byte< / td >
< td > 0xA8< / td >
< td > 0-30< / td >
< td > 1Byte< / td >
< td > 0x03< / td >
< / tr >
< / tbody >
< / table >
< ul >
< li > VOLUME : 0-30< / li >
< / ul >
< / li >
< li > 播放指定曲目(通配符): AB
< table >
< thead >
< tr >
< th > HEAD< / th >
< th > LENGTH< / th >
< th > CMD< / th >
< th > STA< / th >
< th > NAME< / th >
< th > END< / th >
< th > CHK< / th >
< th > TAIL< / th >
< / tr >
< / thead >
< tbody >
< tr >
< td > 0x02< / td >
< td > 1Byte< / td >
< td > 0xAB< / td >
< td > '$'< / td >
< td > 'abc'< / td >
< td > '$'< / td >
< td > 1Byte< / td >
< td > 0x03< / td >
< / tr >
< / tbody >
< / table >
< ul >
< li > STA :名字起始,字符'$'(0x24)。< / li >
< li > END : 名字结束,字符'$'(0x24)。< / li >
< li > NAME :名字字符串,支持通配符,注:会自动增加扩展名, 实例为3字节‘ abc’ ( 61 62 63) ,< code > 实际上是播放 " abc.*" , 程序自动增加 " .*" 通配符扩展名," ab*" 也是合法的,会播放" ab*.*" < / code > < / li >
< li > 文件必须存在否则播放失败< / li >
< / ul >
< / li >
< li > 播放指定曲目(全路径): AC
< table >
< thead >
< tr >
< th > HEAD< / th >
< th > LENGTH< / th >
< th > CMD< / th >
< th > STA< / th >
< th > NAME< / th >
< th > END< / th >
< th > CHK< / th >
< th > TAIL< / th >
< / tr >
< / thead >
< tbody >
< tr >
< td > 0x02< / td >
< td > 1Byte< / td >
< td > 0xAC< / td >
< td > '$'< / td >
< td > 'abc.mp3'< / td >
< td > '$'< / td >
< td > 1Byte< / td >
< td > 0x03< / td >
< / tr >
< / tbody >
< / table >
< ul >
< li > STA :名字起始,字符'$'(0x24)。< / li >
< li > END : 名字结束,字符'$'(0x24)。< / li >
2025-08-27 16:29:07 +00:00
< li > NAME :名字字符串,必须写全文件名, 例如: 文件名采用8+3格式, 例如: 01.mp3和001.mp3是不同的< / li >
2025-08-21 08:15:01 +00:00
< li > 文件必须存在否则播放失败< / li >
< / ul >
< / li >
< li > 播放指定曲目(文件号): AD
< table >
< thead >
< tr >
< th > HEAD< / th >
< th > LENGTH< / th >
< th > CMD< / th >
< th > FH< / th >
< th > FL< / th >
< th > CHK< / th >
< th > TAIL< / th >
< / tr >
< / thead >
< tbody >
< tr >
< td > 0x02< / td >
< td > 1Byte< / td >
< td > 0xAD< / td >
< td > 1Byte< / td >
< td > 1Byte< / td >
< td > 1Byte< / td >
< td > 0x03< / td >
< / tr >
< / tbody >
< / table >
< ul >
2025-08-27 16:29:07 +00:00
< li > FH, FL: 组合成一个16bit的整形, 例如 FH=0x00 FL=0x02 实际上是播放0x0002曲目, 曲目的顺序号是拷贝进磁盘的顺序< / li >
2025-08-21 08:15:01 +00:00
< li > 文件必须存在否则播放失败< / li >
< / ul >
< / li >
2025-08-27 16:29:07 +00:00
< li > 播放信息: B0/B1/B2/B3/B4
2025-08-21 08:15:01 +00:00
< table >
< thead >
< tr >
< th > HEAD< / th >
< th > LENGTH< / th >
< th > CMD< / th >
< th > CHK< / th >
< th > TAIL< / th >
< / tr >
< / thead >
< tbody >
< tr >
< td > 0x02< / td >
< td > 1Byte< / td >
2025-08-27 16:29:07 +00:00
< td > 0xB0< / td >
< td > 1Byte< / td >
< td > 0x03< / td >
< / tr >
< / tbody >
< / table >
< / li >
< li > ACK:
< table >
< thead >
< tr >
< th > HEAD< / th >
< th > LENGTH< / th >
< th > CMD< / th >
< th > ERR< / th >
< th > DH< / th >
< th > DL< / th >
< th > CHK< / th >
< th > TAIL< / th >
< / tr >
< / thead >
< tbody >
< tr >
< td > 0x02< / td >
< td > 1Byte< / td >
< td > 0xB0< / td >
< td > 1Byte< / td >
< td > 1Byte< / td >
2025-08-21 08:15:01 +00:00
< td > 1Byte< / td >
< td > 1Byte< / td >
< td > 0x03< / td >
< / tr >
< / tbody >
< / table >
< ul >
2025-08-27 16:29:07 +00:00
< li > DH,DL: 回复数据, 组合成一个16bit的整形, 例如 DH=0x00 DL=0x02 则值是0x0002< / li >
2025-08-21 08:15:01 +00:00
< / ul >
< / li >
< / ul >
< h2 id = "%E9%99%84%E5%BD%951%E6%A0%A1%E9%AA%8C" > 附录1: 校验< / h2 >
< ul >
< li > 校验方式为校验和, 计算方式为数据累计和取反加1的方式< / li >
< li > 举例发送系统复位指令A0: 0x02 0x02 0xA0 CheckSum 0x03
< ul >
< li > 其中头尾分别为02 03< / li >
< li > 计算: 第二个02 + 指令A0, 相加, 结果取反再+1, 得到CheckSum< / li >
< li > 校验:数据段+校验后的值为0< / li >
< li > 详细算法< pre class = "hljs" > < code > < div > < span class = "hljs-keyword" > uint8_t< / span > buf[< span class = "hljs-number" > 16< / span > ];
< span class = "hljs-keyword" > int< / span > index = < span class = "hljs-number" > 0< / span > ;
buf[index++] = < span class = "hljs-number" > 0x02< / span > ; < span class = "hljs-comment" > //头< / span >
buf[index++] = < span class = "hljs-number" > 0x00< / span > ; < span class = "hljs-comment" > //长度, 临时赋值0< / span >
buf[index++] = < span class = "hljs-number" > 0xA0< / span > ; < span class = "hljs-comment" > //指令< / span >
buf[< span class = "hljs-number" > 1< / span > ] = index - < span class = "hljs-number" > 1< / span > ; < span class = "hljs-comment" > //数据填充完成后,重新赋值长度(0x02)< / span >
< span class = "hljs-keyword" > uint16_t< / span > sum = < span class = "hljs-number" > 0< / span > ;
< span class = "hljs-keyword" > for< / span > (< span class = "hljs-keyword" > int< / span > i = < span class = "hljs-number" > 0< / span > ; i < index - < span class = "hljs-number" > 1< / span > ; i++)
{
sum += buf[< span class = "hljs-number" > 1< / span > +i]; < span class = "hljs-comment" > //从第一个字节开始累加,去除头,< / span >
}
< span class = "hljs-comment" > //此时累加完成, sum: 0x02 + 0xA0 = 0xA2< / span >
sum = ~sum + < span class = "hljs-number" > 1< / span > ; < span class = "hljs-comment" > //取反加1< / span >
< span class = "hljs-comment" > //sum = 0xff5D + 1 = 0xff5E< / span >
buf[index++] = (u8)sum; < span class = "hljs-comment" > //强制转换成uint8_t类型,截断后为 0x5E< / span >
buf[index++] = < span class = "hljs-number" > 0x03< / span > ; < span class = "hljs-comment" > //尾< / span >
< span class = "hljs-comment" > //此时buf = {0x02,0x02,0xA0,0x5E,0x03}< / span >
< span class = "hljs-comment" > //下面校验< / span >
< span class = "hljs-comment" > //0x02 + 0xA0 + 0x5E = 0x0100< / span >
< span class = "hljs-comment" > //将0x100强转为uint8_t后, 截断后 = 0, 校验通过< / span >
< / div > < / code > < / pre >
< / li >
< / ul >
< / li >
< / ul >
< h2 id = "%E9%99%84%E5%BD%952-errcode-%E9%94%99%E8%AF%AF%E5%AE%9A%E4%B9%89" > 附录2 : ERRCODE 错误定义:< / h2 >
< pre > < code > - 0: 指令正确, 数据正确, 且执行正确
- 1: 一个通用的错误, 可能是一个未定义的错误
- 2: 未识别的指令
- 3: 参数错误
- 4: 帧错误, 此包数据未找到帧头, 或者未找到帧尾
- 5: 长度错误
- 6: 校验错误< / code > < / pre >
< / body >
< / html >