原工程: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)
}
}
}