一、MTK平台与网络协议栈概述

做嵌入式通信这些年,我接触过不少平台。MTK算是比较特殊的一个——它把应用处理器和通信基带集成在一起,说白了就是一颗芯片搞定所有事。今天咱们就从芯片架构开始聊,再对比几种常见的网络协议栈,最后梳理一下移植的整体流程。

1.1 MTK芯片架构简介

MTK的芯片,我习惯把它分成三个域:

  • 应用处理器域(AP):跑操作系统和用户程序,比如Android或Linux
  • 通信处理器域(CP):处理GSM/WCDMA/LTE等蜂窝协议栈
  • 连接域(Connectivity):负责Wi-Fi、蓝牙、GNSS等短距离通信

这三个域之间通过共享内存和硬件邮箱通信。嗯,这里要注意:AP和CP的内存是物理隔离的,数据交换必须经过专门的通道。

关键点:MTK的网络协议栈通常跑在AP域,但底层的物理层收发依赖CP域的驱动。移植时你得搞清楚数据流是怎么跨域传递的。

我记得第一次做MTK项目时,被这个架构绕晕了。明明在AP侧配好了IP地址,就是ping不通。后来才发现,CP侧的虚拟网卡根本没使能。你想想看,两个域各自为政,中间缺个桥接,能通才怪。

1.2 常见网络协议栈对比

嵌入式领域,LwIP和uIP是两大主流。我个人的经验是:选哪个,取决于你的硬件资源。

特性 LwIP uIP
内存占用 约40KB RAM + 25KB ROM 约2KB RAM + 10KB ROM
协议支持 TCP/UDP/ICMP/DHCP/PPP TCP/UDP/ICMP(精简版)
多线程支持 支持(基于mbox/sem) 不支持(事件驱动)
API风格 Socket-like + Raw API 只有Raw API
典型场景 路由器、物联网网关 传感器节点、低功耗设备

为什么会有这么大差异?说白了,uIP的设计哲学是「够用就好」。它假设你的MCU只有几KB内存,所以连TCP重传都做得极其简单。而LwIP则更「贪心」,它想提供接近Linux socket的体验。

我的建议:如果MTK芯片的RAM在512KB以上,直接用LwIP。低于128KB,老老实实用uIP。别问我怎么知道的——我曾经在一个64KB RAM的项目里硬塞LwIP,结果堆栈溢出,调试了整整三天。

1.3 MTK协议栈移植的整体流程

移植这件事,说难不难,说简单也不简单。我总结了一个四步法:

  1. 硬件抽象层适配:把MTK的MAC/PHY驱动封装成协议栈需要的接口
  2. 内存管理对接:让协议栈使用MTK的内存池,而不是自己malloc
  3. 操作系统封装:把MTK的RTOS线程、信号量、邮箱映射到协议栈的OS抽象层
  4. 网络接口注册:告诉协议栈「这是我的网卡,你发数据就调这个函数」

举个例子,LwIP的底层接口长这样:

// 网卡初始化函数
static err_t mtk_netif_init(struct netif *netif)
{
    netif->output = mtk_netif_output;    // IP层发包
    netif->linkoutput = mtk_netif_linkoutput; // 链路层发包
    netif->mtu = 1500;
    netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP;
    return ERR_OK;
}

// 实际发包函数
static err_t mtk_netif_output(struct netif *netif, struct pbuf *p, const ip4_addr_t *ipaddr)
{
    // 调用MTK的MAC驱动发送数据
    mtk_mac_send(p->payload, p->len);
    return ERR_OK;
}

这里有个坑:MTK的MAC驱动通常要求数据包是4字节对齐的。而LwIP的pbuf可能不对齐。我曾经在这个问题上栽过跟头——数据发出去,对端收不到任何回应。后来加了个memcpy对齐才解决。

避坑指南:移植时一定要先验证底层收发。写一个简单的loopback测试:让网卡收到包后原样发回去。如果这个能通,再往上走。否则你后面调死也找不到问题。

整体流程走下来,我一般需要2-3天。第一天搭框架,第二天调通基本收发,第三天跑TCP/UDP压力测试。嗯,压力测试很重要——很多问题在轻负载下不会暴露,一上量就崩。

好了,这一章先聊到这。下一章我们深入硬件抽象层,手把手教你写MTK的MAC驱动适配层。