目前共有4篇帖子。
【教程】使用lwip自帶的sys_check_timeouts函數簡化主循環中的程序,並用STM32F103的RTC時鐘實現sys_now函數
1楼 巨大八爪鱼 2017-4-5 22:51
原工程:https://zh.arslanbar.net/post.php?t=24589
【1】首先按照下面的五角星處的提示修改main.c文件中的代碼:
#include <stdio.h>
#include <stm32f10x.h>
#include "lwip/apps/netbiosns.h"
#include "lwip/dhcp.h"
#include "lwip/dns.h"
#include "lwip/init.h" // lwip_init函數所在的頭文件
#include "lwip/timeouts.h" // ★sys_check_timeouts函數所在的頭文件
#include "netif/ethernet.h" // ethernet_input函數所在頭文件
#include "ENC28J60.h"

err_t ethernetif_init(struct netif *netif);
void ethernetif_input(struct netif *netif);
void init_http(void);

int fputc(int ch, FILE *fp)
{
    if (fp == &__stdout)
    {
        if (ch == '\n')
        {
            USART1->DR = '\r';
            while ((USART1->SR & USART_SR_TXE) == 0);
        }
        USART1->DR = ch;
        while ((USART1->SR & USART_SR_TXE) == 0);
    }
    return ch;
}

void dnsfound(const char *name, const ip_addr_t *ipaddr, void *callback_arg)
{
    printf("DNS Found IP: %s\n", ip4addr_ntoa(ipaddr));
}

void test_dns(struct netif *netif)
{
    struct ip4_addr dnsip;
    err_t err = dns_gethostbyname("zh.arslanbar.net", &dnsip, dnsfound, NULL);
    if (err == ERR_OK)
        printf("In cache! IP: %s\n", ip4addr_ntoa(&dnsip));
    else
        printf("Not in cache! err=%d\n", err);
}

void show_addr(struct netif *netif)
{
    struct dhcp *dhcp = netif_dhcp_data(netif);
    static uint8_t displayed = 0;
    if (dhcp_supplied_address(netif))
    {
        if (displayed == 0)
        {
            printf("DHCP分配成功!\n");
            printf("IP位址: %s\n", ip4addr_ntoa(&dhcp->offered_ip_addr));
            printf("子網掩碼: %s\n", ip4addr_ntoa(&dhcp->offered_sn_mask));
            printf("網關: %s\n", ip4addr_ntoa(&dhcp->offered_gw_addr));
            printf("DNS服務器: %s\n", ip4addr_ntoa(dns_getserver(0)));
            displayed = 1;
            test_dns(netif);
        }
    }
    else
        displayed = 0;
}

// ★RTC時間轉化為毫秒數
uint32_t sys_now(void)
{
    uint32_t tmp = (RTC->CNTH << 16) | RTC->CNTL; // 秒
    tmp = tmp * 1000 + (32767 - RTC->DIVL) * 1000 / 32768; // 毫秒
    return tmp;
}

int main(void)
{
    struct netif enc28j60;
    uint16_t last_check = 0; // ★
   
    // ★配置RTC時鐘
    RCC->APB1ENR = RCC_APB1ENR_PWREN | RCC_APB1ENR_BKPEN;
    PWR->CR = PWR_CR_DBP; // 允許寫入後備寄存器
    if ((RCC->BDCR & RCC_BDCR_RTCEN) == 0)
    {
        // 若RTC還沒有打開, 則初始化RTC
        if ((RCC->BDCR & RCC_BDCR_LSEON) == 0)
            RCC->BDCR |= RCC_BDCR_LSEON; // 開外部低速晶振LSE
        RCC->BDCR |= RCC_BDCR_RTCEN; // 打開RTC外設, 但暫不開始走時
       
        RTC->CRL |= RTC_CRL_CNF; // 進入RTC配置模式
        RTC->PRLH = 0;
        RTC->PRLL = 32767; // 定時1s (PRLH和PRLL寄存器只能寫不能讀)
        //RTC->CNTH = 0;
        //RTC->CNTL = 50; // 初始時間
        RTC->CRL &= ~RTC_CRL_CNF; // 保存設置
       
        RCC->BDCR |= RCC_BDCR_RTCSEL_0; // 選LSE作為RTC時鐘, 準備走時
        while ((RTC->CRL & RTC_CRL_RTOFF) == 0); // 等待設置生效
        // RTC從此處開始走時
    }
   
    // 配置PA口
    RCC->APB2ENR = RCC_APB2ENR_IOPAEN;
    GPIOA->CRH = 0x000004bb; // ★PA8為開發板上的一個LED燈(設為復用推挽輸出, CRH=b)
    GPIOA->CRL = 0xb4bb0080;
    GPIOA->BSRR = GPIO_BSRR_BS1; // PA1為網卡中斷輸出
   
    // 配置SPI
    RCC->APB2ENR |= RCC_APB2ENR_SPI1EN;
    SPI1->CR1 = SPI_CR1_MSTR | SPI_CR1_BR_1; // 主機模式, 時鐘至少需要8分頻(BR=010), 也就是72MHz/8=9MHz
    SPI1->CR2 = SPI_CR2_SSOE; // 開CS片選輸出
   
    // 配置串口
    RCC->APB2ENR |= RCC_APB2ENR_USART1EN;
    USART1->BRR = 0x271; // 波特率: 115200
    USART1->CR1 = USART_CR1_UE | USART_CR1_TE;
   
    // ★用定時器1的通道1來自動控制LED指示燈PA8
    RCC->APB2ENR |= RCC_APB2ENR_TIM1EN;
    TIM1->ARR = 9999; // 10000*0.5ms=5s
    TIM1->PSC = 35999; // 定時基準0.5ms
    TIM1->CCR1 = 0; // 輸出比較值
    TIM1->CCMR1 = TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_0; // 匹配時翻轉PA8的電平
    TIM1->CCER = TIM_CCER_CC1P | TIM_CCER_CC1E; // 開通道1的輸出比較, 使用相反的電平
    TIM1->BDTR = TIM_BDTR_MOE; // 開總輸出
    TIM1->EGR = TIM_EGR_UG; // 保存設置
   
    lwip_init(); // 這裡面會自動調用dns_init和sys_timeouts_init函數
    // 千萬不能再在這裡調用sys_timeouts_init函數, 否則會出現內存不足的錯誤!
    netif_add(&enc28j60, IP_ADDR_ANY, IP_ADDR_ANY, IP_ADDR_ANY, NULL, ethernetif_init, ethernet_input);
    netif_set_default(&enc28j60); // 設為默認網卡
    netif_set_up(&enc28j60);
   
    netbiosns_set_name("ENC28J60"); // 設置計算機名
    netbiosns_init();
    enc28j60.hostname = "ENC28J60_Device"; // 路由器DHCP頁面中顯示的設備名
   
    dhcp_start(&enc28j60); // 啟動DHCP
    init_http(); // 初始化網頁服務器
   
    TIM1->CR1 |= TIM_CR1_CEN; // 開定時器1, 指示燈開始自動閃爍
    while (1)
    {
        if (ENC28J60_GetPacketNum() != 0)
        {
            TIM1->EGR |= TIM_EGR_UG; // 收到數據包時改變LED燈的狀態(定時器清零)
            ethernetif_input(&enc28j60);
        }
        show_addr(&enc28j60); // 如果DHCP分配地址成功就顯示IP位址
       
        // ★sys_check_timeouts函數千萬不能調用的太頻繁, 否則會出錯!(後果就是開機後要等1~2分鐘DHCP才能分配到IP位址)
        if ((uint16_t)(TIM1->CNT - last_check) >= 200) // 如果距離上次檢測超過200*0.5ms=100ms
        {
            last_check = TIM1->CNT;
            sys_check_timeouts(); // 則再檢測一次
            // 當TIM1->CNT < last_check時也能正常判斷, 因為兩者都是無符號數
            // 比如12-9985的結果是55563, 這相當於計算65536+(12-9985)
        }
    }
}
2楼 巨大八爪鱼 2017-4-5 22:52
【2】按五角星提示修改lwipopts.h函數:
#ifndef __LWIPOPTS_H__
#define __LWIPOPTS_H__

/**
 * SYS_LIGHTWEIGHT_PROT==1: if you want inter-task protection for certain
 * critical regions during buffer allocation, deallocation and memory
 * allocation and deallocation.
 */
#define SYS_LIGHTWEIGHT_PROT 0

/**
 * NO_SYS==1: Provides VERY minimal functionality. Otherwise,
 * use lwIP facilities.
 */
#define NO_SYS 1 // 無操作系統
/**
 * NO_SYS_NO_TIMERS==1: Drop support for sys_timeout when NO_SYS==1
 * Mainly for compatibility to old versions.
 */
//#define NO_SYS_NO_TIMERS 1 // ★將這一行注釋掉, 然後自己實現sys_now函數

/* ---------- Memory options ---------- */
/* MEM_ALIGNMENT: should be set to the alignment of the CPU for which
   lwIP is compiled. 4 byte alignment -> define MEM_ALIGNMENT to 4, 2
   byte alignment -> define MEM_ALIGNMENT to 2. */
#define MEM_ALIGNMENT 4

/* MEM_SIZE: the size of the heap memory. If the application will send
a lot of data that needs to be copied, this should be set high. */
#define MEM_SIZE (5 * 1024)

/* MEMP_NUM_PBUF: the number of memp struct pbufs. If the application
   sends a lot of data out of ROM (or other static memory), this
   should be set high. */
#define MEMP_NUM_PBUF 10
/* MEMP_NUM_UDP_PCB: the number of UDP protocol control blocks. One
   per active UDP "connection". */
#define MEMP_NUM_UDP_PCB 6
/* MEMP_NUM_TCP_PCB: the number of simulatenously active TCP
   connections. */
#define MEMP_NUM_TCP_PCB 10
/* MEMP_NUM_TCP_PCB_LISTEN: the number of listening TCP
   connections. */
#define MEMP_NUM_TCP_PCB_LISTEN 6
/* MEMP_NUM_TCP_SEG: the number of simultaneously queued TCP
   segments. */
#define MEMP_NUM_TCP_SEG 12
/* MEMP_NUM_SYS_TIMEOUT: the number of simulateously active
   timeouts. */
//#define MEMP_NUM_SYS_TIMEOUT 10 // ★這個數字應該改成較大的數, 否則會報編譯錯誤
// 或者直接注釋掉, 讓opt.h自動計算最佳數值

/* ---------- Pbuf options ---------- */
/* PBUF_POOL_SIZE: the number of buffers in the pbuf pool. */
#define PBUF_POOL_SIZE 10

/* PBUF_POOL_BUFSIZE: the size of each pbuf in the pbuf pool. */
#define PBUF_POOL_BUFSIZE 1500

/* ---------- TCP options ---------- */
#define LWIP_TCP 1
#define TCP_TTL 255

/* Controls if TCP should queue segments that arrive out of
   order. Define to 0 if your device is low on memory. */
#define TCP_QUEUE_OOSEQ 0

/* TCP Maximum segment size. */
#define TCP_MSS (1500 - 40) /* TCP_MSS = (Ethernet MTU - IP header size - TCP header size) */

/* TCP sender buffer space (bytes). */
#define TCP_SND_BUF (2 * TCP_MSS)

/* TCP sender buffer space (pbufs). This must be at least = 2 *
   TCP_SND_BUF/TCP_MSS for things to work. */
#define TCP_SND_QUEUELEN (6 * TCP_SND_BUF) / TCP_MSS

/* TCP receive window. */
#define TCP_WND (2 * TCP_MSS)

/* ---------- ICMP options ---------- */
#define LWIP_ICMP 1

/* ---------- DHCP options ---------- */
/* Define LWIP_DHCP to 1 if you want DHCP configuration of
   interfaces. DHCP is not implemented in lwIP 0.5.1, however, so
   turning this on does currently not work. */
#define LWIP_DHCP 1

/* ---------- UDP options ---------- */
#define LWIP_UDP 1
#define UDP_TTL 255

/* ---------- Statistics options ---------- */
#define LWIP_STATS 0
#define LWIP_PROVIDE_ERRNO 1

// LWIP_NETCONN==1: Enable Netconn API (require to use api_lib.c)
#define LWIP_NETCONN 0

// LWIP_SOCKET==1: Enable Socket API (require to use sockets.c)
#define LWIP_SOCKET 0

#define LWIP_NETIF_HOSTNAME 1

#define LWIP_DNS 1
//#define LWIP_DHCP_PROVIDE_DNS_SERVERS 1 // ★需要將這個注釋掉, 這個選項不再需要
#define LWIP_RAND() ((u32_t)rand())

// 以下為調試選項
//#define LWIP_DEBUG
//#define DHCP_DEBUG LWIP_DBG_ON
//#define MEMP_DEBUG LWIP_DBG_ON
//#define TIMERS_DEBUG LWIP_DBG_ON
//#define LWIP_DEBUG_TIMERNAMES 1

#endif /* __LWIPOPTS_H__ */
3楼 巨大八爪鱼 2017-4-5 22:53
【3】編譯並運行程序
經過大約5~8秒鐘,串口輸出如下:
DHCP分配成功!
IP位址: 192.168.1.116
子網掩碼: 255.255.255.0
網關: 192.168.1.1
DNS服務器: 192.168.1.1
Not in cache! err=-5
DNS Found IP: 106.186.126.193
4楼 巨大八爪鱼 2017-4-5 23:20
主循環里的延時不能保證每次都是100ms,因為計數值是存在上限的。可以換成自己寫的delay函數。反正就是必須要延時,要不然sys_check_timeouts函數始終都在執行,那麼當網卡沒有數據進來的時候,算出來的時間差diff始終為0,調度程序根本就不知道要調度哪個定時器函數執行,這樣就有可能導致dhcp處理超時的函數幾分鐘都得不到執行,而其他定時器函數卻執行了很多遍,搞得開機後幾分鐘才能獲取到IP位址。

回复帖子

内容:
用户名: 您目前是匿名发表
验证码:
 
 
©2010-2025 Arslanbar [手机版] [桌面版]
除非另有声明,本站采用知识共享署名-相同方式共享 3.0 Unported许可协议进行许可。