STM32F103C8T6与DM9051以太网接口实现方案
一、系统概述
该方案采用DM9051单芯片以太网控制器(集成MAC/PHY,支持SPI/并口),配合STM32F103C8T6微控制器(Cortex-M3内核,72MHz主频,64KB闪存,20KB内存)通过SPI总线构建网络通信模块。系统集成LwIP协议栈实现TCP/IP功能,可完成数据传输、远程控制及信息采集等应用,适用于工业物联网终端设备开发。
二、硬件架构
1. 系统拓扑
graph TD A[STM32F103C8T6] -- SPI --> B[DM9051网络模块] B -- MII --> C[HR911105A变压器] C -- RJ45 --> D[以太网] A -- I2C --> E[传感单元] A -- GPIO --> F[状态指示] G[3.3V电源] --> A,B,C
2. 电路连接
(1)核心接口配置
| DM9051引脚 | 功能说明 | STM32引脚 | 连接要点 |
|---|---|---|---|
| VDD | 3.3V供电 | 3.3V | 并联100nF去耦电容 |
| GND | 接地 | GND | 与主控共地 |
| CSN | 片选信号 | PA4 | 低电平有效 |
| SCLK | 时钟信号 | PA5 | 模式0时序(CPOL=0, CPHA=0) |
| MOSI | 主发从收 | PA7 | 数据传输线 |
| MISO | 主收从发 | PA6 | 数据传输线 |
| INT | 中断输出 | PB0 | 下降沿触发中断 |
| RESET | 硬件复位 | PB1 | 低电平复位 |
(2)物理层设计
- 变压器接口:HR911105A模块连接TXP/TXN和RXN/RXP引脚,中心抽头通过100nF电容接地
- RJ45接口:配置TVS保护管(SMAJ5.0A)防静电,LED指示灯连接DM9051的LED引脚
3. 电路设计规范
- 电源滤波:VDD引脚采用10μF电解电容+100nF陶瓷电容组合
- SPI时序:配置主模式,8位数据帧,最大时钟频率10MHz(DM9051支持20MHz)
- 中断处理:INT引脚连接EXTI0中断线,在ISR中读取接收缓冲区数据
三、软件实现
1. 开发环境配置
- IDE:Keil MDK-ARM V5.38
- 协议栈:LwIP 2.1.3
- 外设库:STM32标准外设库 V3.5.0
2. 软件架构
graph TD A[应用层] -- API调用 --> B[LwIP协议栈] B -- 数据交互 --> C[DM9051驱动] C -- SPI通信 --> D[STM32硬件层] D -- GPIO/EXTI --> E[DM9051芯片] E -- MII --> F[网络变压器]
3. 核心代码实现
(1)初始化流程
#include "dm9051.h"
#include "spi.h"
#include "stm32f10x_gpio.h"
// 寄存器地址定义
#define REG_NCR 0x00
#define REG_TCR 0x01
#define REG_RCR 0x05
#define REG_MAC 0x0A
#define REG_PHY 0x10
void Init_Dm9051(void) {
// 硬件复位
GPIO_ResetBits(GPIOB, GPIO_PIN_1);
Delay_ms(10);
GPIO_SetBits(GPIOB, GPIO_PIN_1);
Delay_ms(100);
// 校验芯片ID
uint16_t chip_id = Read_Reg(VIDL) | (Read_Reg(VIDH) << 8);
if(chip_id != 0x9051) { /* 错误处理 */ }
// 配置MAC地址
uint8_t mac_addr[6] = {0x00, 0xE0, 0x4C, 0x12, 0x34, 0x56};
for(int i=0; i<6; i++) {
Write_Reg(REG_MAC + i, mac_addr[i]);
}
// 设置接收控制寄存器
Write_Reg(REG_RCR, 0x2F);
// 设置发送控制寄存器
Write_Reg(REG_TCR, 0x00);
// 使能接收中断
Write_Reg(IMR, 0x01);
}
(2)SPI通信函数
// 寄存器写操作
void Write_Reg(uint8_t addr, uint8_t data) {
GPIO_ResetBits(GPIOA, GPIO_PIN_4);
SPI_SendData(addr | 0x80);
SPI_SendData(data);
GPIO_SetBits(GPIOA, GPIO_PIN_4);
}
// 寄存器读操作
uint8_t Read_Reg(uint8_t addr) {
GPIO_ResetBits(GPIOA, GPIO_PIN_4);
SPI_SendData(addr & 0x7F);
uint8_t val = SPI_ReceiveData();
GPIO_SetBits(GPIOA, GPIO_PIN_4);
return val;
}
(3)LwIP配置示例
// lwipopts.h配置
#define MEM_SIZE 8192
#define TCP_MSS 1460
#define IP_ADDR "192.168.1.100"
#define NETMASK "255.255.255.0"
#define GW_ADDR "192.168.1.1"
// 网络接口初始化
void Init_LwIP(void) {
lwip_init();
struct ip_addr ipaddr, netmask, gw;
IP4_ADDR(&ipaddr, 192,168,1,100);
IP4_ADDR(&netmask, 255,255,255,0);
IP4_ADDR(&gw, 192,168,1,1);
netif_add(&netif, &ipaddr, &netmask, &gw, NULL, ðernetif_init, &tcpip_input);
netif_set_default(&netif);
netif_set_up(&netif);
}
(4)TCP服务器实现
#include "lwip/tcp.h"
// 数据接收回调
err_t Server_Recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err) {
if(p != NULL) {
tcp_write(tpcb, p->payload, p->len, 1);
tcp_output(tpcb);
pbuf_free(p);
} else {
tcp_close(tpcb);
}
return ERR_OK;
}
// 服务器初始化
void Start_Server(void) {
struct tcp_pcb *pcb = tcp_new();
tcp_bind(pcb, IP_ADDR_ANY, 8080);
pcb = tcp_listen(pcb);
tcp_accept(pcb, Server_Recv);
}
四、测试验证
1. 测试工具
- 网络分析仪(如Wireshark)
- 命令行工具(Ping, Telnet)
- 协议分析软件(NetAssist)
2. 验证步骤
- 电源检测:确认3.3V供电及SPI信号波形
- 芯片识别:读取VID寄存器验证通信
- 协议栈验证:通过netif结构体检查IP配置
- 通信测试:建立TCP连接并验证数据收发
五、优化建议
1. 性能提升
- 提升SPI时钟至18MHz
- 启用静态内存分配(MEM_LIBC_MALLOC=0)
- 设置中断优先级为抢占式1级
2. 故障排查
- 连接异常:检查MAC地址配置及TX/RX交叉
- 数据丢失:增加接收缓冲区大小(PBUF_POOL_SIZE)
- 通信失败:使用示波器验证SPI时序是否符合要求
六、应用拓展
本方案可扩展为Web服务器或MQTT客户端,通过HTTP协议或云平台实现远程监控,适用于工业数据采集终端、智能网关等应用场景。