1. 下载源码
建议使用 GIT 下载源码,命令如下:
git clone https://github.com/Ai-Thinker-Open/emMCP.git▫️目录结构
emMCP
├── example ------> 示例代码文件夹
│ ├── 9Mod_MCPBorad ------> 九章 MCP 验证板 STM32F103 示例
│ ├── STM32F40xRTOS_XiaoZhiAI ------> STM32F407 FreeRTOS 小智AI示例
│ └── ...
├── port ------> emMCP 移植接口文件夹
│ ├── uartPort.h ------> emMCP 移植接口头文件(含配置系统)
│ ├── uartPort.c ------> emMCP 移植接口源文件(双缓冲接收)
│ ├── emMCP_port_config_example.h ------> 配置示例
│ ├── emMCP_port_config_template.h ------> 配置模板
│ └── README_PORT.md ------> 移植配置说明
└── uart-mcp ------> emMCP 串口通讯核心
├── cJSON ------> cJSON 库(裁剪优化版)
│ ├── cJSON.h ------> cJSON 头文件
│ └── cJSON.c ------> cJSON 源文件
├── emMCP.h ------> emMCP 主头文件
├── emMCP.c ------> emMCP 主源文件
└── emMCPLOG.h ------> 日志头文件3. 引入 emMCP 库
将 port 和 uart-mcp 文件夹复制到你的项目当中,并引用 emMCP.h 文件使用 emMCP 的所有资源。
新版变更
v1.0.1 引入新的配置系统: port/port.h 已移除,配置宏定义迁移到 uartPort.h,支持三种配置方式(详见下文)。同时新增双缓冲接收机制,提升串口数据稳定性。
示例中提供了 STM32F103 的 CMake 示例(9Mod_MCPBorad),你可以参考它来配置你的项目。
4. 移植接口
emMCP 是一个适用于串口通讯的库,因此你只需要实现串口通讯的底层函数。
▫️配置移植宏(v1.0.1 新的配置系统)
emMCP v1.0.1 将平台相关宏定义从 port/port.h 迁移到 uartPort.h,并提供三种配置方式:
方式 1:直接定义宏(简单项目) 在包含 uartPort.h 之前定义:
#define emMCP_printf log_printf // 打印函数
#define emMCP_malloc pvPortMalloc // 内存分配
#define emMCP_free vPortFree // 内存释放
#define emMCP_delay osDelay // 延时函数
#define emMCP_uart_send HAL_UART_Transmit // 串口发送
#include "uartPort.h"方式 2:创建配置文件(推荐) 在项目目录创建 emMCP_port_config.h,uartPort.h 会自动通过 __has_include 检测并包含。配置内容参考 port/emMCP_port_config_example.h。
方式 3:CMake 编译定义
target_compile_definitions(your_target PRIVATE
emMCP_printf=log_printf
emMCP_malloc=pvPortMalloc
emMCP_free=vPortFree
emMCP_delay=osDelay
EMCP_USER_CONFIG_FILE="path/to/your_config.h"
)▫️实现串口发送和接收
重要
请务必自行完成MCU的串口驱动,并确保串口接收函数能够正确接收数据,建议使用DMA+IDLE方式接收串口数据。发送函数能够正常发送完整的数据包。
▫️移植 emMCP
- 首先打开
port/uartPort.c文件 - 函数
uartPortSendData()已内置宏调用emMCP_uart_send(data, len),你只需确保emMCP_uart_send宏正确定义即可。例如配置为:
#define emMCP_uart_send HAL_UART_Transmit- 当然你也可以直接在函数体中写具体实现:
int uartPortSendData(char *data, int len)
{
if (data == NULL || len <= 0) return -1;
HAL_UART_Transmit(&huart2, (uint8_t *)data, len, 100);
return 0;
}- 在 MCU 的串口接收中断或回调中调用
uartPortRecvData()(新版会自动使用双缓冲存储数据):
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{
if (huart->Instance == USART2) {
HAL_UARTEx_ReceiveToIdle_DMA(huart, (uint8_t *)rxBuffer, sizeof(rxBuffer));
uartPortRecvData((char *)rxBuffer, Size);
__HAL_DMA_ENABLE_IT(&hdma_usart2_rx, DMA_IT_TC);
}
}- 在主循环或任务中调用
uartPortGetRxData()获取接收到的数据(线程安全):
char *data = uartPortGetRxData();
if (data != NULL) {
// 处理数据...
uartPortClearRxData(); // 标记已处理
}5. 初始化 emMCP
你可在任何未执行主函数的地方初始化 emMCP,例如在 main() 函数之前,或者在一个单独的初始化函数中。初始化 emMCP 的代码示例如下:
static emMCP_t emMCP_dev;
int main(void)
{
emMCP_Init(&emMCP_dev);
while(1)
{
//主循环执行
}
}6. 循环执行 emMCP_TickHandler()
emMCP 内置了一个简单的状态机,需要循环地调用 emMCP_TickHandler() 处理数据,以减轻当使用中断函数时的负担。推荐在主循环中调用 emMCP_TickHandler(),例如:
static emMCP_t emMCP_dev;
int main(void)
{
emMCP_Init(&emMCP_dev);
while(1)
{
emMCP_TickHandler(10);
}
}7. 是否移植成功?
完成以上步骤之后,编译你的项目,确保编译成功,并且没有报错。按照以下步骤验证是否移植OK:
- 下载程序到MCU
- 对 AI 模组说“你好小安”
- 观察串口输出,如果看到类似以下输出,则表示 emMCP 已经成功移植到你的 MCU 上:
[DEBUG] emMCP_EventCallback:78: emMCP_EventCallback: event:8,type:4,param:2.WakeUP
