Skip to content

1. 下载源码

建议使用 GIT 下载源码,命令如下:

bash
git clone https://github.com/Ai-Thinker-Open/emMCP.git

▫️目录结构

bash
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 库

portuart-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 之前定义:

c
#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.huartPort.h 会自动通过 __has_include 检测并包含。配置内容参考 port/emMCP_port_config_example.h

方式 3:CMake 编译定义

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 宏正确定义即可。例如配置为:
c
#define emMCP_uart_send  HAL_UART_Transmit
  • 当然你也可以直接在函数体中写具体实现:
c
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()(新版会自动使用双缓冲存储数据):
c
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() 获取接收到的数据(线程安全):
c
char *data = uartPortGetRxData();
if (data != NULL) {
    // 处理数据...
    uartPortClearRxData();  // 标记已处理
}

5. 初始化 emMCP

你可在任何未执行主函数的地方初始化 emMCP,例如在 main() 函数之前,或者在一个单独的初始化函数中。初始化 emMCP 的代码示例如下:

c
static emMCP_t emMCP_dev;

int main(void)
{
	emMCP_Init(&emMCP_dev);
	while(1)
	{
		//主循环执行
	}
}

6. 循环执行 emMCP_TickHandler()

emMCP 内置了一个简单的状态机,需要循环地调用 emMCP_TickHandler() 处理数据,以减轻当使用中断函数时的负担。推荐在主循环中调用 emMCP_TickHandler(),例如:

c
static emMCP_t emMCP_dev;

int main(void)
{
	emMCP_Init(&emMCP_dev);
	while(1)
	{
		emMCP_TickHandler(10);
	}
}

7. 是否移植成功?

完成以上步骤之后,编译你的项目,确保编译成功,并且没有报错。按照以下步骤验证是否移植OK:

  • 下载程序到MCU
  • 对 AI 模组说“你好小安”
  • 观察串口输出,如果看到类似以下输出,则表示 emMCP 已经成功移植到你的 MCU 上:
bash
[DEBUG] emMCP_EventCallback:78: emMCP_EventCallback: event:8,type:4,param:2.WakeUP

8. emMCP 更多使用请参考:如何使用?

Released under the MIT License.