目的
借助keil调试了解stm32 LWIP SOCKET发送流程。
由于LWIP的源码存在大量的通过#name#、调用.h文件进行函数变量的定义,导致编辑器无法精准的定位到变量的位置,同时由于LWIP能够移植到多个平台上使用所以存在很多的条件编译以满足不同平台编译器的要求,这些都为理解源码造成了困难。用户如果有一个能够正常进行网络收发的程序,能够通过使用keil的调试功能,清楚LWIP的函数调用过程。
LWIP SOCKET发送流程
使用socket是调用write(s,dataptr,len)进行数据发送的,其实质调用的是lwip_write(s,dataptr,len),通过#define进行定义,而最终底层是调用static err_t low_level_output(struct netif *netif, struct pbuf *p)让eth的DMA工作进行数据的发送。知道这个过程之后就可以在write和low_level_output打上断点就可以知道socket数据的发送过程。
我这里的是网口在等待发送数据入队,数据入队之后马上进行读出后调用write函数进行数据的发送。让keil进行硬件调试,在write函数和low_level_output函数打入断点,后让程序run。根据自己实际的程序触发write函数的运行。
然后再keil的call stack的栈窗口中可以看到很多函数,从tcpClinentTxTask到low_level_output,中间函数就是一个socket发送会调用到的函数过程,因为在keil中嵌套未执行完的函数都会体现在call stack的窗口上。例如:
void A(void)
{
char a;
if(a); // 打入断点
}
void B(void)
{
char b;
A();
}
void C(void)
{
char c;
}
void main(void)
{
char D;
while(1)
{
C();
B();
}
}
keil运行上面这段程序时,在A函数位置打入断点,当执行到A的位置时call stack窗口会显示的函数为main()–>B()–>A()。函数C()是不会显示的因为C()已经执行完毕的栈数据已经被销毁回收了。总而言之就是函数未运行完毕,函数的栈数据未被销毁就会体现在call stack窗口中。在low_level_output打上断点,call stack窗口显示的函数一样,如果已经完毕的函数是不会显示在在call stack窗口中的。
通过上面的调试方法我们可以知道sock发送数据是:1.调用lwip_write函数后把数据交给netconn发送接口,2.识别网络层的通讯协议为tcp后调用tcp_output函数 3.识别传输层协议为ipv4,调用ipv4_output_if函数 4.查询arp表,目标ip是否有对应的mac地址,如果没有则调用etharp_output进行广播查询,组合成一个完整的网络包 5.调用low_level_output进行数据的发送
放一个正点原子LWIP网口数据发送的流程图(由于截图屏幕限制只能显示这个大小)
完整图片:https://download.csdn.net/download/qq_42603695/90254055?spm=1001.2014.3001.5501
对于网口的接收流程也可以通过上面的方式进行分析。
总结
对于stm32的LWIP协议移植,在通过网络视频学习和查阅了相应资料,了解了基本原理之后,可以通过keil硬件调试的方法加深对LWIP的理解。通过call stack的栈窗口也可以对执行协议的执行过程有个整体的把握。
个人能力有限,有错误的地方请大家指出,对于嵌入式开发有更好的学习工具或者调试方法也欢迎一起分享。
————————————————
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/qq_42603695/article/details/145076809
|