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