是时候写一波了,调了一个星期的裸机以太网通信。最终成功在stm32f103zet6+enc28j60上移植lwip,成功建立裸机端的TCP客户端,和PC端的TCP服务器进行通信。
开头说点虚的。
1、人生可能本身就是平淡无趣的,习惯于此,无大喜也无大悲。但追求的那点意义莫不过不甘心如此的碌碌无为,强行经历更大的痛苦、纠结、甚至无望,浪费时间和精力,在最终实现目标的时刻,豁然开怀,万分感谢这一切的浪费和投入都如此值得,安心而去。
2、起点和终点是很重要的。就像是盗梦空间里的小李子,不管深入多少层的梦境,记得紧握那个让你清醒来路和去向的陀螺。也许你一开始的出发还目标明确,但走得越来越远之后,便不知自己所云。就像是你写了好几层 的函数调用,突然发现自己不知道应该返回什么。
3、成熟的标志真的是从一个小本开始吗?你知道很多东西不可能一蹴而就,开始谨慎计划和执行,尊重现实的取舍。
虚的结束。
说一下关键步骤吧:
第一步:配置好单片机stm32和以太网控制器enc28j60的通信。enc28j60自带SPI接口,应该明确你的stm32的板子哪些引脚和enc28j60的引脚相连,所使用SPIx中的x是多少。比如我的事SPI2,硬件连接如下:
* PD2——ENC28J60-INT (未使用)
* PB14-SPI2-MISO——ENC28J60-SO
* PB15-SPI2-MOSI——ENC28J60-SI
* PB13-SPI2-SCK——ENC28J60-SCK
* PG8-SPI1-NSS——ENC28J60-CS
* PG6——ENC2860-RST(未使用)
void SPI_Enc28j60_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
SPI_InitTypeDef SPI_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOG, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOG, &GPIO_InitStructure);
GPIO_SetBits(GPIOG, GPIO_Pin_8);
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;//È«Ë«¹¤Ä£Ê½
SPI_InitStructure.SPI_Mode = SPI_Mode_Master;//×÷ΪÖ÷»ú
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;//Êý¾Ý³¤¶ÈΪ8
SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;
SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;//ʱÖÓÏàλÉèÖÃ
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;//Èí¼þÉèÖÃNSS¹¦ÄÜ
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4;
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_InitStructure.SPI_CRCPolynomial = 7;
SPI_Init(SPI2, &SPI_InitStructure);
SPI_Cmd(SPI2, ENABLE);
}
unsigned char SPI2_ReadWrite(unsigned char writedat)
{
/* Loop while DR register in not emplty */
while(SPI_I2S_GetFlagStatus(SPI2,SPI_I2S_FLAG_TXE) == RESET);
/* Send byte through the SPI2 peripheral */
SPI_I2S_SendData(SPI2, writedat);
/* Wait to receive a byte */
while(SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET);
/* Return the byte read from the SPI bus */
return SPI_I2S_ReceiveData(SPI2);
}
第二步:初始化ENC28J60,书写发送和接收函数
注意这里:
ENC28J60_CSL();
这里需要更改,这里我很长时间才发现,有点坑。
#define ENC28J60_CSL() (GPIOG->ODR &= ~(1<<8))
#define ENC28J60_CSH() (GPIOG->ODR |= 1<<8)
第三步:初始IP协议栈和建立TCP客户端
void tcp_client_init(void)
{
struct tcp_pcb *pcb;
struct ip_addr ipaddr;
IP4_ADDR(&ipaddr,169,254,42,250);
pcb = tcp_new();
tcp_bind(pcb,IP_ADDR_ANY,23);
if(pcb!=NULL)
{
tcp_connect(pcb,&ipaddr,18060,tcp_client_connect);
}
err_t tcp_client_connect(void *arg,struct tcp_pcb *tpcb,err_t err)
{
tcp_recv(tpcb, tcp_client_recv);
tcp_poll(tpcb, tcp_client_poll, 1); //250ms
return ERR_OK;
}
static err_t tcp_client_recv(void *arg, struct tcp_pcb *pcb,struct pbuf *p,err_t err)
{
if(err==ERR_OK&&p!=NULL)
{
printf("%s",p->payload);
pbuf_free(p);
}
tcp_recved(pcb, p->tot_len);
return ERR_OK;
}
static err_t tcp_client_poll(void *arg, struct tcp_pcb *tpcb)
{
if(usart2_received ==1) //发送准备好标志
{
tcp_write(tpcb,sendbuffer,sendbuffer_len,0);
tcp_output(tpcb);
usart2_received =0;
}
return ERR_OK;
}
第四步:主函数编写,设置轮询函数
while (1)
{
LwIP_Periodic_Handle(LocalTime);//轮询函数
}
void LwIP_Periodic_Handle(__IO uint32_t localtime)
{
//err_t err;
if(localtime - INPUT_Timer >= INPUT_TMR_INTERVAL)
{
/* Read a received packet from the Ethernet buffers and send it to the lwIP for handling */
ethernetif_input(&enc28j60); //检查是否收到数据
// err = ethernetif_input(&enc28j60); //
// if (err !=ERR_OK )
// {
//
//
// }
}
Delay_s(0xfffff); //这里很关键,必要的延时!!!!!没有它,接收会出现问题
/* TCP periodic process every 250 ms */
if (localtime - TCPTimer >= TCP_TMR_INTERVAL)
{
TCPTimer = localtime;
tcp_tmr(); //
}
/* ARP periodic process every 5s */
if (localtime - ARPTimer >= ARP_TMR_INTERVAL)
{
ARPTimer = localtime;
etharp_tmr(); //
}
#if LWIP_DHCP
/* Fine DHCP periodic process every 500ms */
if (localtime - DHCPfineTimer >= DHCP_FINE_TIMER_MSECS)
{
DHCPfineTimer = localtime;
dhcp_fine_tmr();
}
/* DHCP Coarse periodic process every 60s */
if (localtime - DHCPcoarseTimer >= DHCP_COARSE_TIMER_MSECS)
{
DHCPcoarseTimer = localtime;
dhcp_coarse_tmr();
}
#endif
}