|
|
【程序移植】STM32F103C8+ENC28J60网卡也能运行lwip协议栈! |
一派护法 十九级 |
STM32F103C8的Flash容量是64KB,RAM容量为20KB。这样小的芯片上也能运行lwip协议栈! 移植前的源程序是运行在STM32F103RC芯片上的: https://zh.arslanbar.net/post.php?t=24571 程序修改步骤: 【1】由于STM32F103C8没有定时器6,只有TIM1~TIM4四个定时器,因此打开main.c文件,把文件中所有的TIM6都替换为TIM4。 【2】打开lwipopts.h文件,把MEM_SIZE的值修改为512,减小内存开销。 【3】简化HTTP协议的响应过程。打开httptest.c,修改http_recv函数的实现: err_t http_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err) { const char *str = "HTTP/1.1 200 OK\r\nContent-Length: 19\r\nKeep-Alive: timeout=5, max=100\r\nConnection: Keep-Alive\r\nContent-Type: text/html\r\n\r\n<b>Hello World!</b>"; if (p != NULL) { tcp_write(tpcb, str, strlen(str), 0); pbuf_free(p); } return ERR_OK; } 【4】在项目属性的Device选项卡里选择STM32F103C8芯片,点击OK保存设置后,可以发现启动文件已变成startup_stm32f10x_md.s:
|
一派护法 十九级 |
另外,由于本人的开发板上接的LED灯是在PC13口上,因此在源程序中添加: // 配置PC口 RCC->APB2ENR = RCC_APB2ENR_IOPCEN; GPIOC->CRH = 0x00300000; // PC13为开发板上的一个LED灯 并且,把LED灯状态翻转语句改为: GPIOC->ODR ^= GPIO_ODR_ODR13; (原来的语句是GPIOA->ODR ^= GPIO_ODR_ODR8;,全部替换成上面的语句)
|
一派护法 十九级 |
【程序编译结果】 *** Using Compiler 'V5.06 update 1 (build 61)', folder: 'C:\Keil_v5\ARM\ARMCC\Bin' Build target 'Target 1' compiling main.c... linking... Program Size: Code=40136 RO-data=980 RW-data=156 ZI-data=19732 FromELF: creating hex file... ".\Objects\lwip_test2.axf" - 0 Error(s), 0 Warning(s). Build Time Elapsed: 00:00:01
|
一派护法 十九级 |
占用的Flash大小=Code+RO+RW=40136+980+156=41272(容量:65536) 占用的RAM大小=RW+ZI=156+19732=19888(容量:20480)
|
一派护法 十九级 |
由于程序过大,Keil本身已经无法完成程序的下载。所以改用J-Link官方的SEGGER J-Flash V6.10n软件下载。下载前首先要生成hex文件,在Keil中的项目属性里的Output选项卡中勾选Create HEX File,生成的hex文件位于Objects文件夹中。
|
一派护法 十九级 |
【程序运行时串口输出的内容】
|
一派护法 十九级 |
能够ping通,并且开发板上的LED灯正常闪烁:
|
一派护法 十九级 |
网页也能正常打开:
|
一派护法 十九级 |
对于简化前的http_recv函数: err_t error[4]; error[0] = tcp_write(tpcb, STR_AND_SIZE("HTTP/1.1 200 OK\r\nContent-Length: "), TCP_WRITE_FLAG_MORE); error[1] = tcp_write(tpcb, STR_AND_SIZE(len), TCP_WRITE_FLAG_COPY | TCP_WRITE_FLAG_MORE); error[2] = tcp_write(tpcb, STR_AND_SIZE("\r\nKeep-Alive: timeout=5, max=100\r\nConnection: Keep-Alive\r\nContent-Type: text/html\r\n\r\n"), TCP_WRITE_FLAG_MORE); error[3] = tcp_write(tpcb, STR_AND_SIZE(str), TCP_WRITE_FLAG_COPY); // 发送HTML内容 printf("tpcb->snd_buf=%d, err0=%d, err1=%d, err2=%d, err3=%d\n", tpcb->snd_buf, error[0], error[1], error[2], error[3]); 在STM32F103C8单片机上执行,输出如下: tpcb->snd_buf=2802, err0=0, err1=-1, err2=0, err3=-1 因此第二个和第四个tcp_write执行出现了错误,返回值为-1,也就是ERR_MEM,这是内存不足的错误。
|
一派护法 十九级 |
解决办法很简单,把第二步要发送的len字符串和第四步的str字符串改成全局变量,然后去掉TCP_WRITE_FLAG_COPY属性就行了: char len[10]; // 存放HTML内容的长度 char str[200]; // 存放HTML内容
err_t http_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err) { char name[100]; char *pstr; uint8_t i = 0; err_t error[4]; if (p != NULL) { // 提取页面名称 pstr = (char *)p->payload; while (*pstr++ != ' '); while (*pstr != ' ') name[i++] = *pstr++; name[i] = '\0'; // 不要忘了结束name字符串 tcp_recved(tpcb, p->tot_len); // 生成HTML内容 sprintf(str, "<meta charset=\"gb2312\"><title>获取网页名称</title><div style=\"font-family:Arial\"><b>请求的网页文件名称是: </b>%s</div>", name); sprintf(len, "%d", strlen(str)); // HTML内容的长度 error[0] = tcp_write(tpcb, STR_AND_SIZE("HTTP/1.1 200 OK\r\nContent-Length: "), TCP_WRITE_FLAG_MORE); error[1] = tcp_write(tpcb, STR_AND_SIZE(len), TCP_WRITE_FLAG_MORE); error[2] = tcp_write(tpcb, STR_AND_SIZE("\r\nKeep-Alive: timeout=5, max=100\r\nConnection: Keep-Alive\r\nContent-Type: text/html\r\n\r\n"), TCP_WRITE_FLAG_MORE); error[3] = tcp_write(tpcb, STR_AND_SIZE(str), 0); // 发送HTML内容 printf("tpcb->snd_buf=%d, err0=%d, err1=%d, err2=%d, err3=%d\n", tpcb->snd_buf, error[0], error[1], error[2], error[3]); pbuf_free(p); } return ERR_OK; }
|
一派护法 十九级 |
程序运行时输出: Hello World! 进入主循环... tpcb->snd_buf=2682, err0=0, err1=0, err2=0, err3=0 tpcb->snd_buf=2667, err0=0, err1=0, err2=0, err3=0 tpcb->snd_buf=2667, err0=0, err1=0, err2=0, err3=0 tpcb->snd_buf=2667, err0=0, err1=0, err2=0, err3=0 tpcb->snd_buf=2667, err0=0, err1=0, err2=0, err3=0 tpcb->snd_buf=2667, err0=0, err1=0, err2=0, err3=0 tpcb->snd_buf=2667, err0=0, err1=0, err2=0, err3=0 tpcb->snd_buf=2667, err0=0, err1=0, err2=0, err3=0 tpcb->snd_buf=2667, err0=0, err1=0, err2=0, err3=0 并且网页也能正常打开。
|
一派护法 十九级 |
【cnt变量自加测试】 #include <string.h> #include "lwip/tcp.h" // 一般情况下需要包含的头文件
#define STR_AND_SIZE(str) (str), strlen(str)
char len[10]; // 存放HTML内容的长度 char str[200]; // 存放HTML内容
err_t http_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err) { char name[100]; char *pstr; uint8_t i = 0; static uint32_t cnt = 0; if (p != NULL) { // 提取页面名称 pstr = (char *)p->payload; while (*pstr++ != ' '); while (*pstr != ' ') name[i++] = *pstr++; name[i] = '\0'; // 不要忘了结束name字符串 tcp_recved(tpcb, p->tot_len); // 生成HTML内容 sprintf(str, "<meta charset=\"gb2312\"><title>获取网页名称</title><div style=\"font-family:Arial\"><b>请求的网页文件名称是: </b>%s</div><div style=\"color:blue\"><b>cnt</b>=%d</div>", name, cnt); cnt++; sprintf(len, "%d", strlen(str)); // HTML内容的长度 tcp_write(tpcb, STR_AND_SIZE("HTTP/1.1 200 OK\r\nContent-Length: "), TCP_WRITE_FLAG_MORE); tcp_write(tpcb, STR_AND_SIZE(len), TCP_WRITE_FLAG_MORE); tcp_write(tpcb, STR_AND_SIZE("\r\nKeep-Alive: timeout=5, max=100\r\nConnection: Keep-Alive\r\nContent-Type: text/html\r\n\r\n"), TCP_WRITE_FLAG_MORE); tcp_write(tpcb, STR_AND_SIZE(str), 0); // 发送HTML内容 pbuf_free(p); } return ERR_OK; }
err_t http_accept(void *arg, struct tcp_pcb *newpcb, err_t err) { tcp_recv(newpcb, http_recv); return ERR_OK; }
void init_http(void) { struct tcp_pcb *tpcb = tcp_new(); tcp_bind(tpcb, IP_ADDR_ANY, 80); tpcb = tcp_listen(tpcb); tcp_accept(tpcb, http_accept); }
|
|
|
|