一、典型通讯管理机功能定义
常见需求:
- 多串口(RS232/485)采集(Modbus RTU / 自定义协议)
- 以太网通信(Modbus TCP / MQTT / HTTP)
- 协议转换(RTU ↔ TCP)
- 数据缓存、断点续传
- 远程配置与维护
二、整体软件架构
┌────────────────────────────┐
│ 应用层(业务协议) │
│ ModbusTCP / MQTT / HTTP │
└─────────────▲──────────────┘
│
┌─────────────┴──────────────┐
│ LwIP 协议栈 │
│ TCP / UDP / DHCP / DNS │
└─────────────▲──────────────┘
│
┌─────────────┴──────────────┐
│ ENC28J60 网卡驱动 │
│ SPI + INT + CS │
└─────────────▲──────────────┘
│
┌─────────────┴──────────────┐
│ FreeRTOS 内核 │
│ Task / Queue / Semaphore │
└─────────────▲──────────────┘
│
┌─────────────┴──────────────┐
│ PIC32MX 外设驱动 │
│ UART / SPI / Timer / GPIO │
└────────────────────────────┘
三、FreeRTOS 任务划分
| 任务 | 优先级 | 说明 |
|---|---|---|
ETH_IF_TASK |
高 | ENC28J60 数据包接收 |
LWIP_TCPIP_TASK |
高 | LwIP 核心线程 |
MODBUS_TCP_TASK |
中 | Modbus TCP 处理 |
UART_POLL_TASK |
中 | 串口数据采集 |
PROTOCOL_CONV_TASK |
中 | 协议转换 |
SYS_MONITOR_TASK |
低 | 心跳、看门狗 |
c
#define ETH_IF_TASK_PRIO (tskIDLE_PRIORITY + 4)
#define LWIP_TASK_PRIO (tskIDLE_PRIORITY + 5)
#define MODBUS_TCP_PRIO (tskIDLE_PRIORITY + 3)
四、ENC28J60 + LwIP 移植要点(PIC32MX)
1、SPI 驱动(关键)
ENC28J60 仅支持 SPI 模式 0,0:
c
uint8_t enc28j60_read_op(uint8_t op, uint8_t addr)
{
uint8_t tx[2], rx[2];
tx[0] = (op << 5) | (addr & 0x1F);
tx[1] = 0xFF;
SPI_Transfer(tx, rx, 2);
return rx[1];
}
注意:
- SPI 时钟 ≤ 20MHz(PIC32MX 建议 10MHz)
- CS 必须软件控制
- ISR 中只置位信号量,不要在中断里调用 LwIP API
2、LwIP 配置(lwipopts.h)
c
#define NO_SYS 0
#define SYS_LIGHTWEIGHT_PROT 1
#define MEM_SIZE 8192
#define PBUF_POOL_SIZE 16
#define TCP_MSS 1460
#define TCP_SND_BUF 4096
#define TCP_WND 4096
#define LWIP_NETCONN 1
#define LWIP_SOCKET 1
3、网卡接口注册
c
struct netif enc28j60_netif;
netif_add(&enc28j60_netif,
&ipaddr,
&netmask,
&gw,
NULL,
ethernetif_init,
ethernet_input);
netif_set_default(&enc28j60_netif);
netif_set_up(&enc28j60_netif);
五、通讯管理机核心逻辑示例
1、串口采集任务
c
void UART_Poll_Task(void *arg)
{
while(1)
{
if(UART_Receive(buf, &len))
{
xQueueSend(g_uart_queue, buf, portMAX_DELAY);
}
vTaskDelay(pdMS_TO_TICKS(10));
}
}
2、Modbus TCP 服务
c
static void modbus_tcp_server(void *arg)
{
struct netconn *conn = netconn_new(NETCONN_TCP);
netconn_bind(conn, IP_ADDR_ANY, 502);
netconn_listen(conn);
while(1)
{
struct netconn *newconn;
if(netconn_accept(conn, &newconn) == ERR_OK)
{
modbus_process(newconn);
}
}
}
3、协议转换(RTU → TCP)
UART_RTU → Queue → ProtocolConvert → TCP Send
用 Queue + Buffer 解耦
不在线程中做阻塞操作
参考代码 基于pic32MX、FREERTOS、ENC28J60、LWIP编写的通讯管理机程序 www.youwenfan.com/contentcsv/72502.html
六、关键稳定性设计
ENC28J60 常见问题
| 问题 | 对策 |
|---|---|
| 丢包 | 增大 PBUF_POOL_SIZE |
| 死机 | 独立看门狗 + SPI 超时 |
| 中断风暴 | ISR 只给信号量 |
FreeRTOS 建议
- 使用 heap_4.c
- 所有 LwIP API 必须在 TCPIP 线程或 netconn 线程
- 禁止在 ISR 中 malloc / free
七、工程目录建议
/Project
├── App/
│ ├── modbus_tcp.c
│ ├── protocol_convert.c
│ └── uart_task.c
├── Drivers/
│ ├── enc28j60.c
│ ├── spi.c
│ └── uart.c
├── LwIP/
├── FreeRTOS/
└── main.c