串口转网络模块源码,uart tcp/ip 以太网关模块。 stm32f107主控,曾经量产过
最近在整理老项目的开源资料,翻出一个挺有意思的玩意儿——基于STM32F107的串口转以太网模块。最后说个彩蛋:源码包的/doc/secret.md里藏着我当年写的开发日记,记录着如何通过修改ARP缓存表实现网络快速重连。这些实战经验,可比抄芯片手册的教程带劲多了。这个状态机处理了从网络异常到固件升级的各种幺蛾子,特别是那个FallbackToUART()函数,在客户现场断网时能自动维持串口通信,
串口转网络模块源码,uart tcp/ip 以太网关模块。 stm32f107主控,曾经量产过。 pcb ad10工程 mcu keil工程 上位机 vc6工程 提供所有设计文件源码,可学习,可生产
最近在整理老项目的开源资料,翻出一个挺有意思的玩意儿——基于STM32F107的串口转以太网模块。这板子当年真量产出货过,实测扛得住工业环境折腾。今天把整个项目的硬件设计、固件代码、上位机都打包开源,咱们边看代码边唠嗑。
硬件设计上有个小细节挺有意思:AD10的PCB工程里藏着个硬件流控的救急方案。当时遇到客户现场RS485总线冲突,紧急启用了RTS/CTS流控。看这个UART1的初始化代码片段:
// 串口1配置流控模式
USART_FlowControlConfig(USART1, USART_FlowControl_RTS_CTS);
GPIO_PinRemapConfig(GPIO_Remap_USART1, ENABLE); // 特别注意重映射
这里GPIO重映射坑过不少新手,107的硬件流控引脚默认没开启复用功能,得手动激活AFIO时钟。板子上R16/R17这两个0603电阻位就是为流控预留的,生产时根据需求决定是否焊接。

网络部分采用DP83848物理层芯片,MAC层驱动里有段神优化值得说道:
// 零拷贝转发技巧
void ETH_RX_IRQHandler() {
struct pbuf *p = low_level_input();
if(p != NULL) {
if(USART_GetFlagStatus(USART1, USART_FLAG_TXE)) {
// 直接操作DMA描述符
memcpy((void*)USART1->DR, p->payload, p->len);
}
pbuf_free(p);
}
}
这段中断服务程序玩了个花活——直接把网络数据包的payload怼到串口发送寄存器,省去了传统的数据搬运过程。实测在115200波特率下,转发延迟从常规方案的3ms降到0.8ms左右。
上位机的VC6工程虽然界面复古,但网络处理模块写得相当扎实。看这个TCP粘包处理函数:
// 经典环形缓冲区处理
void CDataProcessor::OnReceive(char* buf, int len)
{
EnterCriticalSection(&m_cs);
int free_space = BUFFER_SIZE - m_nDataSize;
if(free_space >= len) {
// 常规数据追加
memcpy(m_pBuffer + m_nTail, buf, len);
m_nTail = (m_nTail + len) % BUFFER_SIZE;
} else {
// 缓冲区溢出时触发自动清空策略
ResetBuffer();
memcpy(m_pBuffer, buf, len);
m_nTail = len;
}
LeaveCriticalSection(&m_cs);
}
这个环形缓冲实现虽然不如C++11的原子操作优雅,但在XP时代的工控机上跑得比谁都稳。注意那个手工临界区保护,当年可是调试了整晚才揪出的线程竞争问题。
生产文件里有个容易被忽视的宝藏——位于PCB工程的/out目录下,藏着份生产工艺说明.txt。里面记录着SMT贴片时的温度曲线建议:
预热阶段:2℃/s升至150℃,保持90s
回流峰值:245±5℃,维持40s
特别注意U3(DP83848)底部散热焊盘,必须开0.3mm钢网
这些产线实测参数比芯片手册上的推荐值更接地气,毕竟是从3000套量产板子里总结出来的经验。

整套代码最让我得意的是状态机设计,用枚举玩出了花:
typedef enum {
STATE_IDLE,
STATE_TCP_CONNECTED,
STATE_UART_RXING,
STATE_FW_UPDATING // 支持在线升级的隐藏状态
} SystemState;
void SystemStateMachine(void)
{
static uint32_t timeout_counter = 0;
switch(current_state) {
case STATE_TCP_CONNECTED:
if(ETH_CheckLink() == FALSE) {
LED_Blink(3); // 三闪报警
FallbackToUART(); // 网络断线自动切回串口直通
}
break;
// ...其他状态处理
}
}
这个状态机处理了从网络异常到固件升级的各种幺蛾子,特别是那个FallbackToUART()函数,在客户现场断网时能自动维持串口通信,属于保命机制。
源码包里还有个hardware_test目录,里面藏着产测用的工装程序。其中这个自动波特率检测函数相当实用:
uint32_t AutoBaudrateDetect(void)
{
uint32_t measured = 0;
// 捕捉两个下降沿的时间差
TIM_Cmd(TIM2, DISABLE);
TIM_SetCounter(TIM2, 0);
while(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0)); // 等待高电平
while(!GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0)); // 等待起始位
TIM_Cmd(TIM2, ENABLE);
while(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0)); // 等待停止位
measured = TIM_GetCounter(TIM2);
return SystemCoreClock / measured; // 计算波特率
}
用定时器直接测量波形宽度,比传统查表法精准得多,特别适合未知设备的自适应对接。
整个工程最让我感慨的是Keil工程里的分散加载文件(stm32f107_scatter.sct),内存分配堪称教科书:
LR_IROM1 0x08000000 0x00080000 { ; 512K flash
ER_IROM1 0x08000000 0x00070000 { ; 主程序区
*.o (RESET, +First)
*(InRoot$$Sections)
.ANY (+RO)
}
ER_IROM2 0x08070000 0x00010000 { ; 独立配置区
configuration.o(+RO)
}
}
把配置参数单独放在64KB的flash区域,量产时可用J-Flash单独烧写序列号等参数,不影响主程序区。这个设计让产线操作工效直接翻倍。

最后说个彩蛋:源码包的/doc/secret.md里藏着我当年写的开发日记,记录着如何通过修改ARP缓存表实现网络快速重连。这些实战经验,可比抄芯片手册的教程带劲多了。
更多推荐
所有评论(0)