目前共有12篇帖子。 內容轉換:不轉換▼
 
點擊 回復
705 11
【程序移植】STM32F103C8+ENC28J60网卡也能运行lwip协议栈!
一派護法 十九級
1樓 發表于:2017-3-19 20:19
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:
一派護法 十九級
2樓 發表于:2017-3-19 20:22
另外,由于本人的开发板上接的LED灯是在PC13口上,因此在源程序中添加:
// 配置PC口
RCC->APB2ENR = RCC_APB2ENR_IOPCEN;
GPIOC->CRH = 0x00300000; // PC13为开发板上的一个LED灯
并且,把LED灯状态翻转语句改为:
GPIOC->ODR ^= GPIO_ODR_ODR13;
(原来的语句是GPIOA->ODR ^= GPIO_ODR_ODR8;,全部替换成上面的语句)
一派護法 十九級
3樓 發表于:2017-3-19 20:22
【程序编译结果】
*** 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
一派護法 十九級
4樓 發表于:2017-3-19 20:24
占用的Flash大小=Code+RO+RW=40136+980+156=41272(容量:65536)
占用的RAM大小=RW+ZI=156+19732=19888(容量:20480)
一派護法 十九級
5樓 發表于:2017-3-19 20:27
由于程序过大,Keil本身已经无法完成程序的下载。所以改用J-Link官方的SEGGER J-Flash V6.10n软件下载。下载前首先要生成hex文件,在Keil中的项目属性里的Output选项卡中勾选Create HEX File,生成的hex文件位于Objects文件夹中。
一派護法 十九級
6樓 發表于:2017-3-19 20:29
【程序运行时串口输出的内容】
一派護法 十九級
7樓 發表于:2017-3-19 20:30
能够ping通,并且开发板上的LED灯正常闪烁:
一派護法 十九級
8樓 發表于:2017-3-19 20:30
网页也能正常打开:
一派護法 十九級
9樓 發表于:2017-3-20 10:07
对于简化前的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,这是内存不足的错误。
一派護法 十九級
10樓 發表于:2017-3-20 10:11
解决办法很简单,把第二步要发送的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;
}
一派護法 十九級
11樓 發表于:2017-3-20 10:11
程序运行时输出:
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
并且网页也能正常打开。
一派護法 十九級
12樓 發表于:2017-3-20 18:27
【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);
}

回復帖子

內容:
用戶名: 您目前是匿名發表
驗證碼:
(快捷鍵:Ctrl+Enter)
 

本帖信息

點擊數:705 回複數:11
評論數: ?
作者:巨大八爪鱼
最後回復:巨大八爪鱼
最後回復時間:2017-3-20 18:27
 
©2010-2024 Arslanbar Ver2.0
除非另有聲明,本站採用創用CC姓名標示-相同方式分享 3.0 Unported許可協議進行許可。