本文共 45226 字,大约阅读时间需要 150 分钟。
STM32驱动W5100实现udp通信
本文博客链接:,作者:jdh,转载请注明.
环境:
主机:WIN7
开发环境:MDK4.72
MCU:STM32F103ZE
源代码:
驱动层代码:
drv_w5100.h
/********************************************************************** w5100驱动层头文件* (c)copyright 2013,jdh* All Right Reserved*文件名:drv_w5100.h*程序员:jdh*修改日期:2013/10/22* 2013/10/23* 2013/10/24**********************************************************************//*********************************************************************硬件连接说明电路标号 单片机引脚 特殊功能W5100_MISO PB4 SPI3_MISOW5100_MOSI PB5 SPI3_MOSIW5100_CS PA15 SPI3_NSSW5100_SCLK PB3 SPI3_SCK/RESET PD3 /INT PD6**********************************************************************/#ifndef _DRV_W5100_H_#define _DRV_W5100_H_/********************************************************************** 头文件**********************************************************************/#include "stm32f10x.h"#include "stm32f10x_spi.h"/********************************************************************** 宏定义**********************************************************************//********************************************************************** 读写操作**********************************************************************///写操作#define WRITE_COMMAND 0xf0 //读操作#define READ_COMMAND 0x0f /********************************************************************** 基地址**********************************************************************/#define COMMON_BASE 0x0000 /********************************************************************** 寄存器**********************************************************************///-----------------------------------------------------------------------------// Common register//-----------------------------------------------------------------------------//Mode register, R/W, Default=0x00#define SOCKET0 0#define SOCKET1 1#define SOCKET2 2#define SOCKET3 3#define TCP_SERVER 0#define TCP_CLIENT 1#define UDP 2#define W5100_MODE COMMON_BASE #define MODE_RST 0x80 //Software reset #define MODE_PB 0x10 //Ping block mode #define MODE_PPPOE 0x08 //PPOE mode #define MODE_AI 0x02 //Address auto increment in indirect bus I/F #define MODE_IND 0x01 //Indirect bus I/F mode//Gateway address register, R/W, default=0x00#define W5100_GAR COMMON_BASE+0x01//Subnet mask address, R/W, default=0x00#define W5100_SUBR COMMON_BASE+0x05//Source hardware address, R/W, default=0x00#define W5100_SHAR COMMON_BASE+0x09//Source IP address, R/W, default=0x00#define W5100_SIPR COMMON_BASE+0x0f//Interrupt and interrupt mask register#define W5100_IR COMMON_BASE+0x15 //RO, Default=0x00 #define IR_CONFLICT 0x80 //IP conflict #define IR_UNREACH 0x40 //Destination unreachable #define IR_PPPOE 0x20 //PPOE close #define IR_S3_INT 0x08 //Occurrence of socket 3 socket interrupt #define IR_S2_INT 0x04 //Occurrence of socket 2 socket interrupt #define IR_S1_INT 0x02 //Occurrence of socket 1 socket interrupt #define IR_S0_INT 0x01 //Occurrence of socket 0 socket interrupt#define W5100_IMR COMMON_BASE+0x16 //R/W, Default=0x00 #define IMR_CONFLICT 0x80 //IP conflict #define IMR_UNREACH 0x40 //Destination unreachable #define IMR_PPPOE 0x20 //PPOE close #define IMR_S3_INT 0x08 //Occurrence of socket 3 socket interrupt #define IMR_S2_INT 0x04 //Occurrence of socket 2 socket interrupt #define IMR_S1_INT 0x02 //Occurrence of socket 1 socket interrupt #define IMR_S0_INT 0x01 //Occurrence of socket 0 socket interrupt//Retry time value. Value 1 means 100us, R/W, default=0x07D0#define W5100_RTR COMMON_BASE+0x17//Retry count, R/W, Default=0x08#define W5100_RCR COMMON_BASE+0X19//RX memory size register, R/W, default=0x55//-------------------------------------------------------- S1 S0 Memory size// Socket3 | Socket2 | Socket1 | Socket0 | 0 0 1KB//-------------|-------------|-------------|-------------| 0 1 2KB//S1 S0 |S1 S0 |S1 S0 |S1 S0 | 1 0 4KB//-------------------------------------------------------- 1 1 8KB#define W5100_RMSR COMMON_BASE+0x1a//TX memory size register, R/W, default=0x55#define W5100_TMSR COMMON_BASE+0x1b//Authentication type in PPPOE mode, R, default=0x0000#define W5100_PATR COMMON_BASE+0x1c//PPP LCP request timer register, R/W, default=0x28//Value 1 is about 25ms#define W5100_PTIMER COMMON_BASE+0x28//PPP LCP magic number register, R/W, default=0x00#define W5100_PMAGIC COMMON_BASE+0x29//Unreachable IP address, RO, default=0x00#define W5100_UIPR COMMON_BASE+0x2a//Unreachable port register, RO, default=0x0000#define W5100_UPORT COMMON_BASE+0x2e//-----------------------------------------------------------------------------// Socket register//-----------------------------------------------------------------------------//Socket mode register, R/W, default=0x00#define W5100_S0_MR COMMON_BASE+0x0400 //Socket 0#define W5100_S1_MR COMMON_BASE+0x0500 //Socket 1#define W5100_S2_MR COMMON_BASE+0x0600 //Socket 2#define W5100_S3_MR COMMON_BASE+0x0700 //Socket 3 #define S_MR_MULTI 0x80 //Multcasting #define S_MR_MC 0x20 //Multcast // P3 P2 P1 P0 Meaning #define S_MR_CLOSED 0x00 // 0 0 0 0 Closed #define S_MR_TCP 0x01 // 0 0 0 1 TCP #define S_MR_UDP 0x02 // 0 0 1 0 UDP #define S_MR_IPRAW 0x03 // 0 0 1 1 IPRAW #define S_MR_MACRAW 0x04 // 0 1 0 0 MACRAW #define S_MR_PPPOE 0x05 // 0 1 0 1 PPPOE//Socket command register, R/W, default=0x00#define W5100_S0_CR COMMON_BASE+0x0401 //Socket 0#define W5100_S1_CR COMMON_BASE+0x0501 //Socket 1#define W5100_S2_CR COMMON_BASE+0x0601 //Socket 2#define W5100_S3_CR COMMON_BASE+0x0701 //Socket 3 #define S_CR_OPEN 0x01 //It is used to initialize the socket #define S_CR_LISTEN 0x02 //In TCP mode, it waits for a connection request from any remote peer(Client) #define S_CR_CONNECT 0x04 //In TCP mode, it sends a connection request to remote peer(SERVER) #define S_CR_DISCON 0x08 //In TCP mode, it sends a connection termination request #define S_CR_CLOSE 0x10 //Used to close the socket #define S_CR_SEND 0x20 //It transmit the data as much as the increase size of write pointer #define S_CR_SEND_MAC 0x21 //In UDP mode, same as SEND #define S_CR_SEND_KEEP 0x22 //In TCP mode #define S_CR_RECV 0x40 //Receiving is processed including the value of socket RX read //pointer register /* Definition for PPPOE */ #define S_CR_PCON 0x23 //Start of ADSL connection #define S_CR_PDISCON 0x24 //End of ADSL connection #define S_CR_PCR 0x25 //Send REQ message ineach phase #define S_CR_PCN 0x26 //Send NAK message in each phase #define S_CR_PCJ 0x27 //Senf REJECT message in each phase//Socket interrup register, RO, default=0x00#define W5100_S0_IR COMMON_BASE+0x0402 //Socket 0#define W5100_S1_IR COMMON_BASE+0x0502 //Socket 1#define W5100_S2_IR COMMON_BASE+0x0602 //Socket 2#define W5100_S3_IR COMMON_BASE+0x0702 //Socket 3 #define S_IR_SENDOK 0x10 //Send data complete #define S_IR_TIMEOUT 0x08 //Set if timeout occurs during connection or termination //or data transmission #define S_IR_RECV 0x04 //Set if data is received #define S_IR_DISCON 0x02 //Set if receiv connection termination request #define S_IR_CON 0x01 //Set if connetion is established /* Definition for PPPOE */ #define S_IR_PRECV 0x80 //Indicate receiving no support option data #define S_IR_PFAIL 0x40 //Indicate PAP Authentication fail #define S_IR_PNEXT 0x20 //Go next phase//Socket status register, RO, default=0x00#define W5100_S0_SSR COMMON_BASE+0x0403 //Socket 0#define W5100_S1_SSR COMMON_BASE+0x0503 //Socket 1#define W5100_S2_SSR COMMON_BASE+0x0603 //Socket 2#define W5100_S3_SSR COMMON_BASE+0x0703 //Socket 3 #define S_SSR_CLOSED 0x00 //In case that OPEN command are given to Sn_CR, Timeout interrupt //is asserted or connection is terminated #define S_SSR_INIT 0x13 //In case that Sn_MR is set as TCP and OPEN commands are given to Sn_CR #define S_SSR_LISTEN 0x14 //In case that under the SOCK_INIT status, LISTEN commands //are given to Sn_CR #define S_SSR_ESTABLISHED 0x17 //In case that connection is established #define S_SSR_CLOSE_WAIT 0x1c //In case that connection temination request is received #define S_SSR_UDP 0x22 //In case that OPEN command is given to Sn_CR when Sn_MR is set as UDP #define S_SSR_IPRAW 0x32 //In case that OPEN command is given to Sn_CR when Sn_MR is set as IPRAW #define S_SSR_MACRAW 0x42 //In case that OPEN command is given to S0_CR when S0_MR is set as MACRAW #define S_SSR_PPPOE 0x5f //In case that OPEN command is given to S0_CR when S0_MR is set as PPPOE //Below is shown in the status change, and does not need much attention #define S_SSR_SYNSEND 0x15 #define S_SSR_SYNRECV 0x16 #define S_SSR_FIN_WAIT 0x18 #define S_SSR_CLOSING 0x1a #define S_SSR_TIME_WAIT 0x1b #define S_SSR_LAST_ACK 0x1d #define S_SSR_ARP0 0x11 #define S_SSR_ARP1 0x21 #define S_SSR_ARP2 0x31//Socket Source port register, R/W, default=0x00#define W5100_S0_PORT COMMON_BASE+0x0404 //Socket 0#define W5100_S1_PORT COMMON_BASE+0x0504 //Socket 1#define W5100_S2_PORT COMMON_BASE+0x0604 //Socket 2#define W5100_S3_PORT COMMON_BASE+0x0704 //Socket 3//Socket destination hardware address register, R/W, default=0x00#define W5100_S0_DHAR COMMON_BASE+0x0406 //Socket 0#define W5100_S1_DHAR COMMON_BASE+0x0506 //Socket 1#define W5100_S2_DHAR COMMON_BASE+0x0606 //Socket 2#define W5100_S3_DHAR COMMON_BASE+0x0706 //Socket 3//Socket destination IP address register, R/W, default=0x00#define W5100_S0_DIPR COMMON_BASE+0x040c //Socket 0#define W5100_S1_DIPR COMMON_BASE+0x050c //Socket 1#define W5100_S2_DIPR COMMON_BASE+0x060c //Socket 2#define W5100_S3_DIPR COMMON_BASE+0x070c //Socket 3//Socket destionation port register, R/W, default=0x00#define W5100_S0_DPORT COMMON_BASE+0x0410 //Socket 0#define W5100_S1_DPORT COMMON_BASE+0x0510 //Socket 1#define W5100_S2_DPORT COMMON_BASE+0x0610 //Socket 2#define W5100_S3_DPORT COMMON_BASE+0x0710 //Socket 3//Socket maximum segment size register, R/W, default=0x00#define W5100_S0_MSS COMMON_BASE+0x0412 //Socket 0#define W5100_S1_MSS COMMON_BASE+0x0512 //Socket 1#define W5100_S2_MSS COMMON_BASE+0x0612 //Socket 2#define W5100_S3_MSS COMMON_BASE+0x0712 //Socket 3//Socket IP protocol register, R/W, default=0x00//See http://www.iana.org/assignments/protocol-number#define W5100_S0_PROTO COMMON_BASE+0x0414 //Socket 0#define W5100_S1_PROTO COMMON_BASE+0x0514 //Socket 1#define W5100_S2_PROTO COMMON_BASE+0x0614 //Socket 2#define W5100_S3_PROTO COMMON_BASE+0x0714 //Socket 3//Socket IP type of servce register, R/W, default=0x00#define W5100_S0_TOS COMMON_BASE+0x0415 //Socket 0#define W5100_S1_TOS COMMON_BASE+0x0515 //Socket 1#define W5100_S2_TOS COMMON_BASE+0x0615 //Socket 2#define W5100_S3_TOS COMMON_BASE+0x0715 //Socket 3//Socket IP time to live register, R/W, default=0x80#define W5100_S0_TTL COMMON_BASE+0x0416 //Socket 0#define W5100_S1_TTL COMMON_BASE+0x0516 //Socket 1#define W5100_S2_TTL COMMON_BASE+0x0616 //Socket 2#define W5100_S3_TTL COMMON_BASE+0x0716 //Socket 3//Socket TX free size register, RO, default=0x0800//should read upper byte first and lower byte later#define W5100_S0_TX_FSR COMMON_BASE+0x0420 //Socket 0#define W5100_S1_TX_FSR COMMON_BASE+0x0520 //Socket 1#define W5100_S2_TX_FSR COMMON_BASE+0x0620 //Socket 2#define W5100_S3_TX_FSR COMMON_BASE+0x0720 //Socket 3//Socket TX read pointer register, RO, default=0x0000//should read upper byte first and lower byte later#define W5100_S0_TX_RR COMMON_BASE+0x0422 //Socket 0#define W5100_S1_TX_RR COMMON_BASE+0x0522 //Socket 1#define W5100_S2_TX_RR COMMON_BASE+0x0622 //Socket 2#define W5100_S3_TX_RR COMMON_BASE+0x0722 //Socket 3//Socket TX write pointer register, R/W, default=0x0000//should read upper byte first and lower byte later#define W5100_S0_TX_WR COMMON_BASE+0x0424 //Socket 0#define W5100_S1_TX_WR COMMON_BASE+0x0524 //Socket 1#define W5100_S2_TX_WR COMMON_BASE+0x0624 //Socket 2#define W5100_S3_TX_WR COMMON_BASE+0x0724 //Socket 3//Socket RX size register, RO, default=0x0000//should read upper byte first and lower byte later#define W5100_S0_RX_RSR COMMON_BASE+0x0426 //Socket 0#define W5100_S1_RX_RSR COMMON_BASE+0x0526 //Socket 1#define W5100_S2_RX_RSR COMMON_BASE+0x0626 //Socket 2#define W5100_S3_RX_RSR COMMON_BASE+0x0726 //Socket 3//Socket RX read pointer register, R/W, default=0x0000//should read upper byte first and lower byte later#define W5100_S0_RX_RR COMMON_BASE+0x0428 //Socket 0#define W5100_S1_RX_RR COMMON_BASE+0x0528 //Socket 1#define W5100_S2_RX_RR COMMON_BASE+0x0628 //Socket 2#define W5100_S3_RX_RR COMMON_BASE+0x0728 //Socket 3//Socket RX read pointer register, R/W, default=0x0000//should read upper byte first and lower byte later#define W5100_S0_RX_WR COMMON_BASE+0x042A //Socket 0#define W5100_S1_RX_WR COMMON_BASE+0x052A //Socket 1#define W5100_S2_RX_WR COMMON_BASE+0x062A //Socket 2#define W5100_S3_RX_WR COMMON_BASE+0x072A //Socket 3//TX memory#define W5100_TX COMMON_BASE+0x4000//RX memory#define W5100_RX COMMON_BASE+0x6000/********************************************************************** 数据结构**********************************************************************//********************************************************************** 缓冲区结构**********************************************************************/struct _W5100_Buf{ //缓冲区大小,单位字节 uint16_t size; //基地址 uint16_t base_addr; //屏蔽地址 uint16_t mask_addr;};/********************************************************************** 全局变量声明**********************************************************************//********************************************************************** 发送缓存区参数**********************************************************************/extern struct _W5100_Buf W5100_Tx_Buf[4];/********************************************************************** 接收缓存区参数**********************************************************************/extern struct _W5100_Buf W5100_Rx_Buf[4];/********************************************************************** 函数**********************************************************************//********************************************************************** 初始化w5100*参数:ip:本机ip,4字节* mac:本机mac,6字节* submask:掩码:4字节* gateway:网关,4字节* socket_tx_size:socket发送缓存大小,4字节* socket_rx_size:socket接收缓存大小,4字节**********************************************************************/void drv_w5100_init(uint8_t *ip,uint8_t *mac,uint8_t *submask,uint8_t *gateway,\ uint8_t *socket_tx_size,uint8_t *socket_rx_size);/********************************************************************** 初始化io*说明:初始化/INT和/RESET引脚**********************************************************************/void drv_w5100_init_io(void);/********************************************************************** 初始化spi**********************************************************************/void drv_w5100_init_spi(void);/********************************************************************** 初始化中断**********************************************************************/void drv_w5100_init_irq(void);/********************************************************************** 复位w5100**********************************************************************/void drv_w5100_reset(void);/********************************************************************** 使能芯片*参数:enable:1使能,0关闭**********************************************************************/void drv_w5100_enable(uint8_t enable);/********************************************************************** spi口发送一个字节*参数:byte:发送的字节*返回:收到的字节**********************************************************************/uint8_t drv_w5100_spi_tx_byte(uint8_t byte);/********************************************************************** w5100写入一个字节*参数:addr:地址* byte:字节**********************************************************************/void drv_w5100_tx_byte(uint16_t addr,uint8_t byte);/********************************************************************** w5100写缓存*参数:addr:w5100缓存开始地址* buf:缓存区地址* len:缓存长度**********************************************************************/void drv_w5100_tx_buf(uint16_t addr,uint8_t *buf,uint16_t len);/********************************************************************** w5100读取一个字节*参数:addr:地址*返回:读取的字节**********************************************************************/uint8_t drv_w5100_rx_byte(uint16_t addr);/********************************************************************** w5100读取缓存*参数:addr:w5100缓存开始地址* buf:读取存放的缓存区地址* len:读取长度**********************************************************************/void drv_w5100_rx_buf(uint16_t addr,uint8_t *buf,uint16_t len);/********************************************************************** w5100设置网关*参数:addr:需要设置的网关,4字节**********************************************************************/void drv_w5100_set_gateway(uint8_t *addr);/********************************************************************** 读取w5100网关*参数:addr:读取的网关存放地址,4字节长度**********************************************************************/void drv_w5100_get_gateway(uint8_t *addr);/********************************************************************** w5100设置子网掩码*参数:addr:需要设置的子网掩码,4字节**********************************************************************/void drv_w5100_set_submask(uint8_t *addr);/********************************************************************** 读取w5100子网掩码*参数:addr:读取的子网掩码,4字节**********************************************************************/void drv_w5100_get_submask(uint8_t *addr);/********************************************************************** w5100设置mac地址*参数:mac:需要设置的mac,6字节**********************************************************************/void drv_w5100_set_mac(uint8_t *mac);/********************************************************************** 读取w5100的mac地址*参数:addr:读取mac地址,6字节**********************************************************************/void drv_w5100_get_mac(uint8_t *mac);/********************************************************************** w5100设置本机ip*参数:ip:需要设置的ip,4字节**********************************************************************/void drv_w5100_set_ip(uint8_t *ip);/********************************************************************** 读取w5100的ip地址*参数:addr:读取ip地址,4字节**********************************************************************/void drv_w5100_get_ip(uint8_t *ip);/********************************************************************** w5100设置端口的端口号*参数:socket:端口:0-3* port:端口号**********************************************************************/void drv_w5100_set_socket_port(uint8_t socket,uint16_t port);/********************************************************************** 读取w5100端口的端口号*参数:socket:端口:0-3*返回:端口号**********************************************************************/uint16_t drv_w5100_get_socket_port(uint8_t socket);/********************************************************************** w5100设置端口的目的mac地址*参数:socket:端口:0-3* mac:需要设置的mac,6字节**********************************************************************/void drv_w5100_set_socket_mac(uint8_t socket,uint8_t *mac);/********************************************************************** 读取w5100端口的目的mac地址*参数:socket:端口:0-3* mac:读取的目的mac,6字节**********************************************************************/void drv_w5100_get_socket_mac(uint8_t socket,uint8_t *mac);/********************************************************************** w5100设置端口的目的ip地址*参数:socket:端口:0-3* ip:需要设置的ip,4字节**********************************************************************/void drv_w5100_set_socket_dst_ip(uint8_t socket,uint8_t *ip);/********************************************************************** 读取w5100端口的目的ip地址*参数:socket:端口:0-3* ip:读取的目的ip,4字节**********************************************************************/void drv_w5100_get_socket_dst_ip(uint8_t socket,uint8_t *ip);/********************************************************************** w5100设置端口的目的端口号*参数:socket:端口:0-3* port:目的端口号**********************************************************************/void drv_w5100_set_socket_dst_port(uint8_t socket,uint16_t port);/********************************************************************** 读取w5100端口的目的端口号*参数:socket:端口:0-3*返回:端口号**********************************************************************/uint16_t drv_w5100_get_socket_dst_port(uint8_t socket);/********************************************************************** 设置w5100端口缓存大小*说明:4个端口发送总缓存为8KB,接收总缓存为8KB*参数:tx_size:发送缓存大小数组,4字节长度,值只能取1,2,4,8这4个.单位为KB* rx_size:发送缓存大小数组,4字节长度,值只能取1,2,4,8这4个.单位为KB**********************************************************************/void drv_w5100_set_socket_buf_size(uint8_t *tx_size,uint8_t *rx_size);/********************************************************************** 读取w5100端口的发送存储器剩余空间寄存器*参数:socket:端口:0-3*返回:发送存储器剩余空间**********************************************************************/uint16_t drv_w5100_get_socket_tx_fsr(uint8_t socket);/********************************************************************** 读取w5100端口的接收数据字节数寄存器*参数:socket:端口:0-3*返回:接收数据字节数**********************************************************************/uint16_t drv_w5100_get_socket_rx_rsr(uint8_t socket);/********************************************************************** 读取w5100端口接收的数据*参数:socket:端口:0-3* buf:接收的数据存放的缓存* len:读取接收数据长度**********************************************************************///void drv_w5100_socket_rx(uint8_t socket,uint8_t *buf,uint16_t len);/********************************************************************** 读取w5100端口接收的数据*参数:socket:端口:0-3* buf:接收的数据存放的缓存* len:读取接收数据长度* ip:接收数据的ip* port:接收数据的端口**********************************************************************/void drv_w5100_socket_rx(uint8_t socket,uint8_t *buf,uint16_t len,uint8_t *ip,uint16_t *port);/********************************************************************** w5100端口发送数据*参数:socket:端口:0-3* buf:发送的数据存放的缓存* len:发送数据长度**********************************************************************/void drv_w5100_socket_tx(uint8_t socket,uint8_t *buf,uint16_t len);#endifdrv_w5100.c
/********************************************************************** w5100驱动层文件* (c)copyright 2013,jdh* All Right Reserved*文件名:drv_w5100.c*程序员:jdh*修改日期:2013/10/22* 2013/10/23* 2013/10/24**********************************************************************//********************************************************************** 头文件**********************************************************************/#include "drv_w5100.h"#include "drv_delay.h"/********************************************************************** 全局变量定义**********************************************************************//********************************************************************** 发送缓存区参数**********************************************************************/struct _W5100_Buf W5100_Tx_Buf[4];/********************************************************************** 接收缓存区参数**********************************************************************/struct _W5100_Buf W5100_Rx_Buf[4];/********************************************************************** 函数**********************************************************************//********************************************************************** 初始化w5100*参数:ip:本机ip,4字节* mac:本机mac,6字节* submask:掩码:4字节* gateway:网关,4字节* socket_tx_size:socket发送缓存大小,4字节* socket_rx_size:socket接收缓存大小,4字节**********************************************************************/void drv_w5100_init(uint8_t *ip,uint8_t *mac,uint8_t *submask,uint8_t *gateway,\ uint8_t *socket_tx_size,uint8_t *socket_rx_size){ uint8_t value = 0; //初始化IO drv_w5100_init_io(); //SPI初始化 drv_w5100_init_spi(); //中断初始化 drv_w5100_init_irq(); //复位w5100 drv_w5100_reset(); //软复位 drv_w5100_tx_byte(W5100_MODE,MODE_RST); drv_delay_ms(300); //设置ip drv_w5100_set_ip(ip); //设置mac drv_w5100_set_mac(mac); //设置掩码 drv_w5100_set_submask(submask); //设置网关 drv_w5100_set_gateway(gateway); //端口缓存设置 drv_w5100_set_socket_buf_size(socket_tx_size,socket_rx_size); //设置中断 value = drv_w5100_rx_byte(W5100_IMR); value |= IMR_CONFLICT | IMR_UNREACH | \ IMR_S0_INT | IMR_S1_INT | IMR_S2_INT | IMR_S3_INT; drv_w5100_tx_byte(W5100_IMR,value);}/********************************************************************** 初始化io*说明:初始化/INT和/RESET引脚**********************************************************************/void drv_w5100_init_io(void){ //定义IO初始化结构体 GPIO_InitTypeDef GPIO_InitStructure; //初始化时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD,ENABLE); //管脚初始化 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3; //选择GPIO响应速度 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //设置为输出 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //初始化 GPIO_Init(GPIOD, &GPIO_InitStructure); //初始化时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD,ENABLE); //管脚初始化 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; //选择GPIO响应速度 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //设置为输入 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //初始化 GPIO_Init(GPIOD, &GPIO_InitStructure); }/********************************************************************** 初始化spi**********************************************************************/void drv_w5100_init_spi(void){ //定义IO初始化结构体 GPIO_InitTypeDef GPIO_InitStructure ; //定义SPI初始化结构体 SPI_InitTypeDef SPI_InitStructure ; //配置其他引脚 //配置CS //开启时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE ) ; //配置SPI引脚CSN GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15 | GPIO_Pin_12; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz ; GPIO_Init( GPIOA, &GPIO_InitStructure ); //PA15默认是JTDI脚,重映射关闭JTDI功能 GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE); //关闭w5100 drv_w5100_enable(0); drv_w5100_enable(1); drv_w5100_enable(0); //初始化SPI //关闭SPI SPI_Cmd(SPI3,DISABLE); //初始化SPI时钟 RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI3,ENABLE); //设置IO口时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE); //配置SPI3引脚:SCK, MISO and MOSI GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP ; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz ; GPIO_Init( GPIOB, &GPIO_InitStructure ); // SPI配置 SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex ; SPI_InitStructure.SPI_Mode = SPI_Mode_Master ; SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b ; SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low ; SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge ; SPI_InitStructure.SPI_NSS = SPI_NSS_Soft ; //SPI波特率分频设置:72M-18M,64M-16M SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4 ; //SPI设置成LSB模式 SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB ; SPI_InitStructure.SPI_CRCPolynomial = 7 ; SPI_Init( SPI3, &SPI_InitStructure ) ; //启动SPI SPI_Cmd(SPI3,ENABLE);}/********************************************************************** 初始化中断**********************************************************************/void drv_w5100_init_irq(void){ //定义中断结构体 NVIC_InitTypeDef NVIC_InitStructure ; //定义外部中断结构体 EXTI_InitTypeDef EXTI_InitStructure; //初始化中断脚PD6时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE); //配置中断源为PD6 GPIO_EXTILineConfig(GPIO_PortSourceGPIOD,GPIO_PinSource6); // 配置EXTI_Line6下降沿触发 EXTI_ClearITPendingBit(EXTI_Line6); EXTI_InitStructure.EXTI_Line = EXTI_Line6; EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling; EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; EXTI_InitStructure.EXTI_LineCmd = ENABLE; EXTI_Init(&EXTI_InitStructure); //打开NTRX中断 NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQn; //通道设置为外部中断线 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //中断抢占先等级 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //中断响应优先级 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //打开中断 NVIC_Init(&NVIC_InitStructure); //初始化 //清中断 //EXTI_ClearITPendingBit(EXTI9_5_IRQn); //EXTI_Line0中断允许 //EXTI_GenerateSWInterrupt(EXTI_Line6);}/********************************************************************** 复位w5100**********************************************************************/void drv_w5100_reset(void){ GPIO_ResetBits(GPIOD,GPIO_Pin_3); drv_delay_ms(20); GPIO_SetBits(GPIOD,GPIO_Pin_3); }/********************************************************************** 使能芯片*参数:enable:1使能,0关闭**********************************************************************/void drv_w5100_enable(uint8_t enable){ if (enable) { //使能 GPIO_ResetBits(GPIOA,GPIO_Pin_15); } else { //关闭 GPIO_SetBits(GPIOA,GPIO_Pin_15); }}/********************************************************************** spi口发送一个字节*参数:byte:发送的字节*返回:收到的字节**********************************************************************/uint8_t drv_w5100_spi_tx_byte(uint8_t byte){ while (SPI_I2S_GetFlagStatus(SPI3, SPI_I2S_FLAG_TXE) == RESET); SPI_I2S_SendData(SPI3,byte); while (SPI_I2S_GetFlagStatus(SPI3, SPI_I2S_FLAG_RXNE) == RESET); return SPI_I2S_ReceiveData(SPI3); }/********************************************************************** w5100写入一个字节*参数:addr:地址* byte:字节**********************************************************************/void drv_w5100_tx_byte(uint16_t addr,uint8_t byte){ //使能w5100 drv_w5100_enable(1); drv_w5100_spi_tx_byte(WRITE_COMMAND); drv_w5100_spi_tx_byte(addr / 256); drv_w5100_spi_tx_byte(addr % 256); drv_w5100_spi_tx_byte(byte); //关闭w5100 drv_w5100_enable(0);}/********************************************************************** w5100写缓存*参数:addr:w5100缓存开始地址* buf:缓存区地址* len:缓存长度**********************************************************************/void drv_w5100_tx_buf(uint16_t addr,uint8_t *buf,uint16_t len){ uint16_t i; for (i = 0; i < len; i++) { drv_w5100_tx_byte(addr + i,*buf++); } }/********************************************************************** w5100读取一个字节*参数:addr:地址*返回:读取的字节**********************************************************************/uint8_t drv_w5100_rx_byte(uint16_t addr){ uint8_t byte = 0; //使能w5100 drv_w5100_enable(1); drv_w5100_spi_tx_byte(READ_COMMAND); drv_w5100_spi_tx_byte(addr / 256); drv_w5100_spi_tx_byte(addr % 256); byte = drv_w5100_spi_tx_byte(0); //关闭w5100 drv_w5100_enable(0); return byte;}/********************************************************************** w5100读取缓存*参数:addr:w5100缓存开始地址* buf:读取存放的缓存区地址* len:读取长度**********************************************************************/void drv_w5100_rx_buf(uint16_t addr,uint8_t *buf,uint16_t len){ uint16_t i; for (i = 0; i < len; i++) { *buf++ = drv_w5100_rx_byte(addr++); } } /********************************************************************** w5100设置网关*参数:addr:需要设置的网关,4字节**********************************************************************/void drv_w5100_set_gateway(uint8_t *addr){ uint8_t i = 0; for (i = 0;i < 4;i++) { drv_w5100_tx_byte(W5100_GAR + i,addr[i]); }}/********************************************************************** 读取w5100网关*参数:addr:读取的网关存放地址,4字节长度**********************************************************************/void drv_w5100_get_gateway(uint8_t *addr){ uint8_t i = 0; for (i = 0;i < 4;i++) { addr[i] = drv_w5100_rx_byte(W5100_GAR + i); }}/********************************************************************** w5100设置子网掩码*参数:addr:需要设置的子网掩码,4字节**********************************************************************/void drv_w5100_set_submask(uint8_t *addr){ uint8_t i = 0; for (i = 0;i < 4;i++) { drv_w5100_tx_byte(W5100_SUBR + i,addr[i]); }}/********************************************************************** 读取w5100子网掩码*参数:addr:读取的子网掩码,4字节**********************************************************************/void drv_w5100_get_submask(uint8_t *addr){ uint8_t i = 0; for (i = 0;i < 4;i++) { addr[i] = drv_w5100_rx_byte(W5100_SUBR + i); }}/********************************************************************** w5100设置mac地址*参数:mac:需要设置的mac,6字节**********************************************************************/void drv_w5100_set_mac(uint8_t *mac){ uint8_t i = 0; for (i = 0;i < 6;i++) { drv_w5100_tx_byte(W5100_SHAR + i,mac[i]); }}/********************************************************************** 读取w5100的mac地址*参数:addr:读取mac地址,6字节**********************************************************************/void drv_w5100_get_mac(uint8_t *mac){ uint8_t i = 0; for (i = 0;i < 6;i++) { mac[i] = drv_w5100_rx_byte(W5100_SHAR + i); }}/********************************************************************** w5100设置本机ip*参数:ip:需要设置的ip,4字节**********************************************************************/void drv_w5100_set_ip(uint8_t *ip){ uint8_t i = 0; for (i = 0;i < 4;i++) { drv_w5100_tx_byte(W5100_SIPR + i,ip[i]); }}/********************************************************************** 读取w5100的ip地址*参数:addr:读取ip地址,4字节**********************************************************************/void drv_w5100_get_ip(uint8_t *ip){ uint8_t i = 0; for (i = 0;i < 4;i++) { ip[i] = drv_w5100_rx_byte(W5100_SIPR + i); }}/********************************************************************** w5100设置端口的端口号*参数:socket:端口:0-3* port:端口号**********************************************************************/void drv_w5100_set_socket_port(uint8_t socket,uint16_t port){ uint16_t reg = 0; //根据端口号选择寄存器地址 reg = W5100_S0_PORT + socket * 0x100; //设置端口号 drv_w5100_tx_byte(reg,(port >> 8) & 0xff); drv_w5100_tx_byte(reg + 1,port & 0xff);}/********************************************************************** 读取w5100端口的端口号*参数:socket:端口:0-3*返回:端口号**********************************************************************/uint16_t drv_w5100_get_socket_port(uint8_t socket){ uint16_t reg = 0; uint16_t port = 0; //根据端口号选择寄存器地址 reg = W5100_S0_PORT + socket * 0x100; //设置端口号 port = drv_w5100_rx_byte(reg); port = (port << 8) + drv_w5100_rx_byte(reg + 1); return port;}/********************************************************************** w5100设置端口的目的mac地址*参数:socket:端口:0-3* mac:需要设置的mac,6字节**********************************************************************/void drv_w5100_set_socket_mac(uint8_t socket,uint8_t *mac){ uint8_t i = 0; uint16_t reg = 0; //根据端口号选择寄存器地址 reg = W5100_S0_DHAR + socket * 0x100; //设置mac for (i = 0;i < 6;i++) { drv_w5100_tx_byte(reg + i,mac[i]); }}/********************************************************************** 读取w5100端口的目的mac地址*参数:socket:端口:0-3* mac:读取的目的mac,6字节**********************************************************************/void drv_w5100_get_socket_mac(uint8_t socket,uint8_t *mac){ uint8_t i = 0; uint16_t reg = 0; //根据端口号选择寄存器地址 reg = W5100_S0_DHAR + socket * 0x100; //设置mac for (i = 0;i < 6;i++) { mac[i] = drv_w5100_rx_byte(reg + i); }}/********************************************************************** w5100设置端口的目的ip地址*参数:socket:端口:0-3* ip:需要设置的ip,4字节**********************************************************************/void drv_w5100_set_socket_dst_ip(uint8_t socket,uint8_t *ip){ uint8_t i = 0; uint16_t reg = 0; //根据端口号选择寄存器地址 reg = W5100_S0_DIPR + socket * 0x100; //设置ip for (i = 0;i < 4;i++) { drv_w5100_tx_byte(reg + i,ip[i]); }}/********************************************************************** 读取w5100端口的目的ip地址*参数:socket:端口:0-3* ip:读取的目的ip,4字节**********************************************************************/void drv_w5100_get_socket_dst_ip(uint8_t socket,uint8_t *ip){ uint8_t i = 0; uint16_t reg = 0; //根据端口号选择寄存器地址 reg = W5100_S0_DIPR + socket * 0x100; //读取ip for (i = 0;i < 4;i++) { ip[i] = drv_w5100_rx_byte(reg + i); }}/********************************************************************** w5100设置端口的目的端口号*参数:socket:端口:0-3* port:目的端口号**********************************************************************/void drv_w5100_set_socket_dst_port(uint8_t socket,uint16_t port){ uint16_t reg = 0; //根据端口号选择寄存器地址 reg = W5100_S0_DPORT + socket * 0x100; //设置端口号 drv_w5100_tx_byte(reg,(port >> 8) & 0xff); drv_w5100_tx_byte(reg + 1,port & 0xff);}/********************************************************************** 读取w5100端口的目的端口号*参数:socket:端口:0-3*返回:端口号**********************************************************************/uint16_t drv_w5100_get_socket_dst_port(uint8_t socket){ uint16_t reg = 0; uint16_t port = 0; //根据端口号选择寄存器地址 reg = W5100_S0_DPORT + socket * 0x100; //设置端口号 port = drv_w5100_rx_byte(reg); port = (port << 8) + drv_w5100_rx_byte(reg + 1); return port;}/********************************************************************** 设置w5100端口缓存大小*说明:4个端口发送总缓存为8KB,接收总缓存为8KB*参数:tx_size:发送缓存大小数组,4字节长度,值只能取1,2,4,8这4个.单位为KB* rx_size:发送缓存大小数组,4字节长度,值只能取1,2,4,8这4个.单位为KB**********************************************************************/void drv_w5100_set_socket_buf_size(uint8_t *tx_size,uint8_t *rx_size){ uint8_t value = 0; uint8_t i = 0; uint8_t tx_reg[4] = { 0}; uint8_t rx_reg[4] = { 0}; for (i = 0;i < 4;i++) { switch (tx_size[i]) { case 1: { tx_reg[i] = 0; break; } case 2: { tx_reg[i] = 1; break; } case 4: { tx_reg[i] = 2; break; } case 8: { tx_reg[i] = 3; break; } } switch (rx_size[i]) { case 1: { rx_reg[i] = 0; break; } case 2: { rx_reg[i] = 1; break; } case 4: { rx_reg[i] = 2; break; } case 8: { rx_reg[i] = 3; break; } } } //设置写缓存大小 value = (tx_reg[3] << 6) + (tx_reg[2] << 4) + \ (tx_reg[1] << 2) + tx_reg[0]; drv_w5100_tx_byte(W5100_TMSR, value); //设置读缓存大小 value = (rx_reg[3] << 6) + (rx_reg[2] << 4) + \ (rx_reg[1] << 2) + rx_reg[0]; drv_w5100_tx_byte(W5100_RMSR, value); //设置缓冲区参数 for (i = 0;i < 4;i++) { //缓冲区大小 W5100_Tx_Buf[i].size = tx_size[i] * 1024; W5100_Rx_Buf[i].size = rx_size[i] * 1024; //屏蔽地址 W5100_Tx_Buf[i].mask_addr = W5100_Tx_Buf[i].size - 1; W5100_Rx_Buf[i].mask_addr = W5100_Rx_Buf[i].size - 1; //基地址 if (i == 0) { W5100_Tx_Buf[i].base_addr = W5100_TX; W5100_Rx_Buf[i].base_addr = W5100_RX; } else { W5100_Tx_Buf[i].base_addr = W5100_Tx_Buf[i - 1].base_addr + W5100_Tx_Buf[i - 1].size; W5100_Rx_Buf[i].base_addr = W5100_Rx_Buf[i - 1].base_addr + W5100_Tx_Buf[i - 1].size; } }}/********************************************************************** 读取w5100端口的发送存储器剩余空间寄存器*参数:socket:端口:0-3*返回:发送存储器剩余空间**********************************************************************/uint16_t drv_w5100_get_socket_tx_fsr(uint8_t socket){ uint16_t reg = 0; uint16_t size1 = 0; uint16_t size2 = 0; //根据端口号选择寄存器地址 reg = W5100_S0_TX_FSR + socket * 0x100; while (1) { //读取中断寄存器 size1 = drv_w5100_rx_byte(reg); size1 = (size1 << 8) + drv_w5100_rx_byte(reg + 1); if (size1 != 0) { size2 = drv_w5100_rx_byte(reg); size2 = (size2 << 8) + drv_w5100_rx_byte(reg + 1); if (size1 == size2) { break; } } } return size1;}/********************************************************************** 读取w5100端口的接收数据字节数寄存器*参数:socket:端口:0-3*返回:接收数据字节数**********************************************************************/uint16_t drv_w5100_get_socket_rx_rsr(uint8_t socket){ uint16_t reg = 0; uint16_t size1 = 0; uint16_t size2 = 0; //根据端口号选择寄存器地址 reg = W5100_S0_RX_RSR + socket * 0x100; while (1) { //读取中断寄存器 size1 = drv_w5100_rx_byte(reg); size1 = (size1 << 8) + drv_w5100_rx_byte(reg + 1); if (size1 != 0) { size2 = drv_w5100_rx_byte(reg); size2 = (size2 << 8) + drv_w5100_rx_byte(reg + 1); if (size1 == size2) { break; } } } return size1;}/********************************************************************** 读取w5100端口接收的数据*参数:socket:端口:0-3* buf:接收的数据存放的缓存* len:读取接收数据长度* ip:接收数据的ip* port:接收数据的端口**********************************************************************/void drv_w5100_socket_rx(uint8_t socket,uint8_t *buf,uint16_t len,uint8_t *ip,uint16_t *port){ uint16_t shift = 0; uint16_t addr = 0; uint16_t reg = 0; uint16_t size = 0; uint16_t i = 0; uint8_t header[8] = { 0}; //读取接收缓冲区偏移量 reg = W5100_S0_RX_RR + socket * 0x100; shift = drv_w5100_rx_byte(reg); shift = (shift << 8) + drv_w5100_rx_byte(reg + 1); //实际的偏移量 shift &= W5100_Rx_Buf[socket].mask_addr; //物理地址 addr = W5100_Rx_Buf[socket].base_addr + shift; /* for (i = 0;i < len;i++) { if (shift >= W5100_Rx_Buf[socket].size) { addr = W5100_Rx_Buf[socket].base_addr; shift = 0; } buf[i] = drv_w5100_rx_byte(addr++); shift++; } */ /* //读取正文 if (shift + len > W5100_Rx_Buf[socket].size) { size = W5100_Rx_Buf[socket].size - shift; drv_w5100_rx_buf(addr, buf, size); drv_w5100_rx_buf(W5100_Rx_Buf[socket].base_addr, buf + size, len - size); } else { drv_w5100_rx_buf(addr, buf, len); } */ //读取帧头 if (shift + 8 > W5100_Rx_Buf[socket].size) { size = W5100_Rx_Buf[socket].size - shift; drv_w5100_rx_buf(addr, header, size); drv_w5100_rx_buf(W5100_Rx_Buf[socket].base_addr, header + size, 8 - size); shift = 8 - size; } else { drv_w5100_rx_buf(addr, header, 8); shift += 8; } //保存帧头 //读取ip ip[0] = header[0]; ip[1] = header[1]; ip[2] = header[2]; ip[3] = header[3]; //端口 *port = (header[4] << 8) + header[5]; //实际地址更改 addr = W5100_Rx_Buf[socket].base_addr + shift; //接收数据长度更改 len -= 8; //读取正文 if (shift + len > W5100_Rx_Buf[socket].size) { size = W5100_Rx_Buf[socket].size - shift; drv_w5100_rx_buf(addr, buf, size); drv_w5100_rx_buf(W5100_Rx_Buf[socket].base_addr, buf + size, len - size); } else { drv_w5100_rx_buf(addr, buf, len); } //计算下一次偏移量 shift = drv_w5100_rx_byte(reg); shift = (shift << 8) + drv_w5100_rx_byte(reg + 1); shift += len + 8; drv_w5100_tx_byte(reg,(shift >> 8) & 0xff); drv_w5100_tx_byte(reg + 1,shift & 0xff); //接收处理完成 reg = W5100_S0_CR + socket * 0x100; drv_w5100_tx_byte(reg,S_CR_RECV);}/********************************************************************** w5100端口发送数据*参数:socket:端口:0-3* buf:发送的数据存放的缓存* len:发送数据长度**********************************************************************/void drv_w5100_socket_tx(uint8_t socket,uint8_t *buf,uint16_t len){ uint16_t shift = 0; uint16_t addr = 0; uint16_t reg = 0; uint16_t size = 0; //根据端口号选择寄存器地址 reg = W5100_S0_TX_WR + socket * 0x100; //获得接收缓冲区读指针地址 shift = drv_w5100_rx_byte(reg); shift = (shift << 8) + drv_w5100_rx_byte(reg + 1); //偏移地址 shift &= W5100_Tx_Buf[socket].mask_addr; //实际地址 addr = W5100_Tx_Buf[socket].base_addr + shift; //发送数据 if (shift + len > W5100_Tx_Buf[socket].size) { size = W5100_Tx_Buf[socket].size - shift; drv_w5100_tx_buf(addr, buf, size); drv_w5100_tx_buf(W5100_Tx_Buf[socket].base_addr, buf + size, len - size); } else { drv_w5100_tx_buf(addr, buf, len); } //发送指针偏移 shift = drv_w5100_rx_byte(reg); shift = (shift << 8) + drv_w5100_rx_byte(reg + 1); shift += len; drv_w5100_tx_byte(reg, (shift >> 8) & 0xff); drv_w5100_tx_byte(reg + 1, shift & 0xff); //根据端口号选择寄存器地址 reg = W5100_S0_CR + socket * 0x100; //发送 drv_w5100_tx_byte(reg, S_CR_SEND);}接口层文件:
inf_w5100.h
/********************************************************************** w5100接口层头文件* (c)copyright 2013,jdh* All Right Reserved*文件名:inf_w5100.h*程序员:jdh*修改日期:2013/10/24**********************************************************************/#ifndef _INF_W5100_H_#define _INF_W5100_H_/********************************************************************** 头文件**********************************************************************///驱动层头文件#include "drv_w5100.h"/********************************************************************** 数据结构**********************************************************************//********************************************************************** 数据结构**********************************************************************//********************************************************************** w5100端口中断信息**********************************************************************/struct _W5100_Socket_Irq_Msg{ //端口中断 uint8_t socket_int; //数据发送完成 uint8_t send_ok; //在连接或终止,数据发送等过程超时 uint8_t timeout; //当端口接收到数据时 uint8_t recv; //当收到终止连接请求或终止连接过程已结束 uint8_t discon; //当连接成功时 uint8_t con; //接收ip uint8_t ip[4]; //接收端口 uint16_t port; //接收数据的字节数 uint16_t size;};/********************************************************************** w5100中断信息**********************************************************************/struct _W5100_Irq_Msg{ //ip冲突 uint8_t confict; //无法到达地址 uint8_t unreach; //PPPoe连接关闭 uint8_t pppoe; //端口中断信息 struct _W5100_Socket_Irq_Msg socket_msg[4];};/********************************************************************** 函数************** 再分享一下我老师大神的人工智能教程吧。零基础!通俗易懂!风趣幽默!还带黄段子!希望你也加入到我们人工智能的队伍中来!