STM32F7xx —— LAN8720(FreeRTOS+LWIP)

                                STM32F7xx —— LAN8720

 

 

    STM32F767自带以太网模块,需要外接PHY芯片,完成以太网通信(MII/RMII接口)。LAN8720详细资料看手册。LWIP:1.4.1 FreeRTOS V8.2.3。

#define ETH_CHANNEL               ETH
#define ETH_PREEMPT_PRIO          ETHERNET_PRIORITY
#define ETH_CLK_ENABLE()          __HAL_RCC_ETH_CLK_ENABLE()
#define ETH_IRQ                   ETH_IRQn
#define ETH_IRQ_FUNC              ETH_IRQHandler#define ETH_RESET_PORT            GPIOH
#define ETH_RESET_PIN             GPIO_PIN_11
#define ETH_RESET_CONFIG()        GPIOConfig(ETH_RESET_PORT, ETH_RESET_PIN, GPIO_MODE_OUTPUT_PP, GPIO_NOPULL);
#define ETH_RESET_HIGH()          HAL_GPIO_WritePin(ETH_RESET_PORT, ETH_RESET_PIN, GPIO_PIN_SET)
#define ETH_RESET_LOW()           HAL_GPIO_WritePin(ETH_RESET_PORT, ETH_RESET_PIN, GPIO_PIN_RESET)#define ETH_MDIO_PORT             GPIOA
#define ETH_MDIO_PIN              GPIO_PIN_2
#define ETH_MDIO_AF               GPIO_AF11_ETH
#define ETH_MDIO_CONFIG()         GPIOConfigExt(ETH_MDC_PORT, ETH_MDC_PIN, GPIO_MODE_AF_PP, GPIO_NOPULL, ETH_MDC_AF);#define ETH_MDC_PORT              GPIOC
#define ETH_MDC_PIN               GPIO_PIN_1
#define ETH_MDC_AF                GPIO_AF11_ETH
#define ETH_MDC_CONFIG()          GPIOConfigExt(ETH_MDIO_PORT, ETH_MDIO_PIN, GPIO_MODE_AF_PP, GPIO_NOPULL, ETH_MDIO_AF);#define ETH_RMII_REF_CLK_PORT     GPIOA
#define ETH_RMII_REF_CLK_PIN      GPIO_PIN_1
#define ETH_RMII_REF_CLK_AF       GPIO_AF11_ETH
#define ETH_RMII_REF_CLK_CONFIG() GPIOConfigExt(ETH_RMII_REF_CLK_PORT, ETH_RMII_REF_CLK_PIN, GPIO_MODE_AF_PP, GPIO_NOPULL, ETH_RMII_REF_CLK_AF);#define ETH_RMII_CRS_DV_PORT      GPIOA
#define ETH_RMII_CRS_DV_PIN       GPIO_PIN_7
#define ETH_RMII_CRS_DV_AF        GPIO_AF11_ETH
#define ETH_RMII_CRS_DV_CONFIG()  GPIOConfigExt(ETH_RMII_CRS_DV_PORT, ETH_RMII_CRS_DV_PIN, GPIO_MODE_AF_PP, GPIO_NOPULL, ETH_RMII_CRS_DV_AF);#define ETH_RMII_RXD0_PORT        GPIOC
#define ETH_RMII_RXD0_PIN         GPIO_PIN_4
#define ETH_RMII_RXD0_AF          GPIO_AF11_ETH
#define ETH_RMII_RXD0_CONFIG()    GPIOConfigExt(ETH_RMII_RXD0_PORT, ETH_RMII_RXD0_PIN, GPIO_MODE_AF_PP, GPIO_NOPULL, ETH_RMII_RXD0_AF);#define ETH_RMII_RXD1_PORT        GPIOC
#define ETH_RMII_RXD1_PIN         GPIO_PIN_5
#define ETH_RMII_RXD1_AF          GPIO_AF11_ETH
#define ETH_RMII_RXD1_CONFIG()    GPIOConfigExt(ETH_RMII_RXD1_PORT, ETH_RMII_RXD1_PIN, GPIO_MODE_AF_PP, GPIO_NOPULL, ETH_RMII_RXD1_AF);#define ETH_RMII_TXEN_PORT        GPIOB
#define ETH_RMII_TXEN_PIN         GPIO_PIN_11
#define ETH_RMII_TXEN_AF          GPIO_AF11_ETH
#define ETH_RMII_TXEN_CONFIG()    GPIOConfigExt(ETH_RMII_TXEN_PORT, ETH_RMII_TXEN_PIN, GPIO_MODE_AF_PP, GPIO_NOPULL, ETH_RMII_TXEN_AF);#define ETH_RMII_TXD0_PORT        GPIOG
#define ETH_RMII_TXD0_PIN         GPIO_PIN_13
#define ETH_RMII_TXD0_AF          GPIO_AF11_ETH
#define ETH_RMII_TXD0_CONFIG()    GPIOConfigExt(ETH_RMII_TXD0_PORT, ETH_RMII_TXD0_PIN, GPIO_MODE_AF_PP, GPIO_NOPULL, ETH_RMII_TXD0_AF);#define ETH_RMII_TXD1_PORT        GPIOG
#define ETH_RMII_TXD1_PIN         GPIO_PIN_14
#define ETH_RMII_TXD1_AF          GPIO_AF11_ETH
#define ETH_RMII_TXD1_CONFIG()    GPIOConfigExt(ETH_RMII_TXD1_PORT, ETH_RMII_TXD1_PIN, GPIO_MODE_AF_PP, GPIO_NOPULL, ETH_RMII_TXD1_AF);
lan8720_dev_t lan8720_dev;static void lan8720_var_init(void)
{lan8720_dev.dma_rx = MemAlloc(SRAM_TYPE_DTCM, ETH_RXBUFNB * sizeof(ETH_DMADescTypeDef)); //申请内存lan8720_dev.dma_tx = MemAlloc(SRAM_TYPE_DTCM, ETH_TXBUFNB * sizeof(ETH_DMADescTypeDef)); //申请内存lan8720_dev.rx_buffer = MemAlloc(SRAM_TYPE_DTCM, ETH_RX_BUF_SIZE * ETH_RXBUFNB); //申请内存lan8720_dev.tx_buffer = MemAlloc(SRAM_TYPE_DTCM, ETH_TX_BUF_SIZE * ETH_TXBUFNB); //申请内存
}static void lan8720_gpio_init(void)
{ETH_CLK_ENABLE();ETH_RESET_CONFIG();ETH_MDIO_CONFIG();ETH_MDC_CONFIG();ETH_RMII_REF_CLK_CONFIG();ETH_RMII_CRS_DV_CONFIG();ETH_RMII_RXD0_CONFIG();ETH_RMII_RXD1_CONFIG();ETH_RMII_TXEN_CONFIG();ETH_RMII_TXD0_CONFIG();ETH_RMII_TXD1_CONFIG();
}static void lan8720_mode_init(void)
{uint8_t mac[6] = {0};ETH_RESET_CONFIG();ETH_RESET_HIGH();delay_ms(100);ETH_RESET_LOW();delay_ms(100);mac[0] = lwip_dev.mac[0];mac[1] = lwip_dev.mac[1];mac[2] = lwip_dev.mac[2];mac[3] = lwip_dev.mac[3];mac[4] = lwip_dev.mac[4];mac[5] = lwip_dev.mac[5];lan8720_dev.handle.Instance = ETH;lan8720_dev.handle.Init.AutoNegotiation = ETH_AUTONEGOTIATION_ENABLE; // 使能自协商模式lan8720_dev.handle.Init.Speed = ETH_SPEED_100M;                       // 速度100M,如果开启了自协商模式,此配置就无效lan8720_dev.handle.Init.DuplexMode = ETH_MODE_FULLDUPLEX;             // 全双工模式,如果开启了自协商模式,此配置就无效lan8720_dev.handle.Init.PhyAddress = LAN8720_PHY_ADDRESS;             // LAN8720地址lan8720_dev.handle.Init.MACAddr = mac;                                // MAC地址lan8720_dev.handle.Init.RxMode = ETH_RXINTERRUPT_MODE;                // 中断接收模式lan8720_dev.handle.Init.ChecksumMode = ETH_CHECKSUM_BY_HARDWARE;      // 硬件帧校验lan8720_dev.handle.Init.MediaInterface = ETH_MEDIA_INTERFACE_RMII;    // RMII接口HAL_ETH_Init(&lan8720_dev.handle);
}static void lan8720_nvic_init(void)
{HAL_NVIC_SetPriority(ETH_IRQ, 3, 0); // 优先级必须小于configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITYHAL_NVIC_EnableIRQ(ETH_IRQ);
}uint32_t LAN8720ReadPHY(uint16_t reg)
{uint32_t regval;HAL_ETH_ReadPHYRegister(&lan8720_dev.handle, reg, &regval);return regval;
}void LAN8720WritePHY(uint16_t reg, uint16_t value)
{uint32_t temp = value;HAL_ETH_ReadPHYRegister(&lan8720_dev.handle, reg, &temp);
}static uint32_t lan8720_rx_size(ETH_DMADescTypeDef *dma_rx)
{uint32_t length = 0;if(((dma_rx->Status & ETH_DMARXDESC_OWN) == (uint32_t)RESET) &&((dma_rx->Status & ETH_DMARXDESC_ES) == (uint32_t)RESET) &&((dma_rx->Status & ETH_DMARXDESC_LS) != (uint32_t)RESET)){length = ((dma_rx->Status & ETH_DMARXDESC_FL) >> ETH_DMARXDESC_FRAME_LENGTHSHIFT);}return length;
}void LAN8720Init(void)
{lan8720_var_init();lan8720_gpio_init();lan8720_mode_init();lan8720_nvic_init();
}void ETH_IRQ_FUNC(void)
{while(lan8720_rx_size(lan8720_dev.handle.RxDesc)){LWIPCommProcess(); // 处理以太网数据,即将数据提交给LWIP}//清除中断标志位__HAL_ETH_DMA_CLEAR_IT(&lan8720_dev.handle, ETH_DMA_IT_R);__HAL_ETH_DMA_CLEAR_IT(&lan8720_dev.handle, ETH_DMA_IT_NIS);
}

sys_arch文件:

#ifndef _ARCH_SYS_ARCH_H_
#define _ARCH_SYS_ARCH_H_#include "arch/cc.h"
#include "FreeRTOS.h"
#include "queue.h"
#include "semphr.h"#define MAX_QUEUE_ENTRIES     20  // 每个消息邮箱的大小// LWIP消息邮箱结构体
typedef struct
{QueueHandle_t xQueue;
} lwip_mbox;typedef SemaphoreHandle_t sys_sem_t;   // LWIP使用的信号量
typedef lwip_mbox   sys_mbox_t;       // LWIP使用的消息邮箱,其实就是UCOS中的消息队列
typedef unsigned char  sys_thread_t;  // 线程ID,也就是任务优先级#endif /* _ARCH_SYS_ARCH_H_ */
// 参照lwip_sys.hstatic const uint32_t console_null;// 创建一个消息邮箱
err_t sys_mbox_new(sys_mbox_t *mbox, int size)
{if(size > MAX_QUEUE_ENTRIES){size = MAX_QUEUE_ENTRIES;}mbox->xQueue = xQueueCreate(size, sizeof(void *));if(mbox->xQueue != NULL){return ERR_OK;}else{return ERR_MEM;}
}// 释放并删除一个消息邮箱
void sys_mbox_free(sys_mbox_t *mbox)
{vQueueDelete(mbox->xQueue);mbox->xQueue = NULL;
}// 向消息邮箱中发送一条消息(必须发送成功)
void sys_mbox_post(sys_mbox_t *mbox, void *msg)
{BaseType_t xHigherPriorityTaskWoken = pdFALSE;if(msg == NULL){msg = (void*)&console_null;  //当msg为空时 msg等于pvNullPointer指向的值}if((SCB_ICSR_REG & 0xFF) == 0) //线程执行{while(xQueueSendToBack(mbox->xQueue, &msg, portMAX_DELAY) != pdPASS);//portMAX_DELAY,死等直到发送成功}else{while(xQueueSendToBackFromISR(mbox->xQueue, &msg, &xHigherPriorityTaskWoken) != pdPASS);portYIELD_FROM_ISR(xHigherPriorityTaskWoken);}
}// 尝试向一个消息邮箱发送消息
// 此函数相对于sys_mbox_post函数只发送一次消息,发送失败后不会尝试第二次发送
err_t sys_mbox_trypost(sys_mbox_t *mbox, void *msg)
{BaseType_t xHigherPriorityTaskWoken = pdFALSE;if(msg == NULL){msg = (void*)&console_null;  //当消息为空,则用常量NullMessage的地址替换}if((SCB_ICSR_REG & 0xFF) == 0){if(xQueueSendToBack(mbox->xQueue, &msg, 0) != pdPASS){return ERR_MEM;}}else{if(xQueueSendToBackFromISR(mbox->xQueue, &msg, &xHigherPriorityTaskWoken) != pdPASS){return ERR_MEM;}portYIELD_FROM_ISR(xHigherPriorityTaskWoken);}return ERR_OK;
}// 等待邮箱中的消息
u32_t sys_arch_mbox_fetch(sys_mbox_t *mbox, void **msg, u32_t timeout)
{
#if 0u32_t rtos_timeout;BaseType_t temp;if(timeout == 0){rtos_timeout = 2;}else{rtos_timeout = timeout;temp = xQueueReceive(mbox->xQueue, msg, rtos_timeout);}return rtos_timeout;#elseu32_t rtos_timeout, timeout_new;BaseType_t temp;temp = xQueueReceive(mbox->xQueue, msg, 0);if((temp == pdPASS) && (*msg != NULL)){if(*msg == (void*)&console_null){*msg = NULL;}return 0;}if(timeout != 0){rtos_timeout = (timeout * configTICK_RATE_HZ) / 1000; // 转换为节拍数,因为freertos延时使用的是节拍数,而LWIP是用msif(rtos_timeout < 1){rtos_timeout = 1; // 至少1个节拍}else if(rtos_timeout >= portMAX_DELAY){rtos_timeout = portMAX_DELAY - 1;}}else{rtos_timeout = 0;}timeout = HAL_GetTick(); //获取系统时间if(rtos_timeout != 0){temp = xQueueReceive(mbox->xQueue, msg, rtos_timeout);  // 请求消息队列,等待时限为rtos_timeout}else{temp = xQueueReceive(mbox->xQueue, msg, portMAX_DELAY);  // 为0则无限等}if(temp == errQUEUE_EMPTY){timeout = SYS_ARCH_TIMEOUT; //请求超时*msg = NULL;}else{if(*msg != NULL){if(*msg == (void*)&console_null){*msg = NULL;}}timeout_new = HAL_GetTick();if (timeout_new > timeout){timeout_new = timeout_new - timeout;  //算出请求消息或使用的时间}else{timeout_new = 0xffffffff - timeout + timeout_new;}timeout = timeout_new * 1000 / configTICK_RATE_HZ + 1;}return timeout;
#endif
}// 尝试获取消息
u32_t sys_arch_mbox_tryfetch(sys_mbox_t *mbox, void **msg)
{BaseType_t temp;temp = xQueueReceive(mbox->xQueue, msg, 0);if((temp == pdPASS) && (*msg != NULL)){if(*msg == (void*)&console_null){*msg = NULL;}return 0;}else{return SYS_MBOX_EMPTY;}
}// 检查一个消息邮箱是否有效
// 返回值:1,有效.  0,无效
int sys_mbox_valid(sys_mbox_t *mbox)
{if(mbox->xQueue != NULL){return 1;}return 0;
}// 设置一个消息邮箱为无效
void sys_mbox_set_invalid(sys_mbox_t *mbox)
{mbox->xQueue = NULL;
}// 创建一个信号量
err_t sys_sem_new(sys_sem_t* sem, uint8_t count)
{*sem = xSemaphoreCreateCounting(0xFF, count);if(*sem == NULL){return ERR_MEM;}return ERR_OK;
}// 等待一个信号量
u32_t sys_arch_sem_wait(sys_sem_t *sem, u32_t timeout)
{u32_t rtos_timeout, timeout_new;BaseType_t temp;if(xSemaphoreTake(*sem, 0) == pdPASS){return 0;}if( timeout != 0){rtos_timeout = (timeout * configTICK_RATE_HZ) / 1000; // 转换为节拍数,因为UCOS延时使用的是节拍数,而LWIP是用msif(rtos_timeout < 1){rtos_timeout = 1;}}else{rtos_timeout = 0;}timeout = HAL_GetTick();if(rtos_timeout != 0){temp = xSemaphoreTake(*sem, rtos_timeout);}else{temp = xSemaphoreTake(*sem, portMAX_DELAY);}if(temp != pdPASS){timeout = SYS_ARCH_TIMEOUT; // 请求超时}else{timeout_new = HAL_GetTick();if (timeout_new >= timeout){timeout_new = timeout_new - timeout;}else{timeout_new = 0xffffffff - timeout + timeout_new;}timeout = (timeout_new * 1000 / configTICK_RATE_HZ + 1); // 算出请求消息或使用的时间(ms)}return timeout;
}// 发送一个信号量
void sys_sem_signal(sys_sem_t *sem)
{while(xSemaphoreGive(*sem) != pdTRUE);
}// 释放并删除一个信号量
void sys_sem_free(sys_sem_t *sem)
{vSemaphoreDelete(*sem);*sem = NULL;
}// 查询一个信号量的状态,无效或有效
int sys_sem_valid(sys_sem_t *sem)
{if(*sem != NULL){return 1;}else{return 0;}
}// 设置一个信号量无效
void sys_sem_set_invalid(sys_sem_t *sem)
{*sem = NULL;
}// arch初始化
void sys_init(void)
{// 不做任何事情
}TaskHandle_t os_lwip_handle;sys_thread_t sys_thread_new(const char *name, lwip_thread_fn thread, void *arg, int stacksize, int prio)
{xTaskCreate((TaskFunction_t)thread,(const char*  )name,(uint16_t     )stacksize,(void*        )NULL,(UBaseType_t  )prio,(TaskHandle_t*)&os_lwip_handle);//创建TCP IP内核任务return 0;
}// lwip延时函数
void sys_msleep(u32_t ms)
{delay_ms(ms);
}// 获取系统时间,LWIP1.4.1增加的函数
// 返回值:当前系统时间(单位:毫秒)
u32_t sys_now(void)
{return (HAL_GetTick() * 1000 / configTICK_RATE_HZ + 1); // 将节拍数转换为LWIP的时间MS
}// 用在cc.h的SYS_ARCH_PROTECT(lev)
uint32_t SysCriticalEnter(void)
{if(SCB_ICSR_REG & 0xFF) //在中断里{return taskENTER_CRITICAL_FROM_ISR();}else  // 在任务{taskENTER_CRITICAL();}return 0;
}// 用在cc.YS_ARCH_UNPROTECT(lev)
void SysCriticalExit(uint32_t lev)
{if(SCB_ICSR_REG & 0xFF) // 在中断里{taskEXIT_CRITICAL_FROM_ISR(lev);}else  // 在任务{taskEXIT_CRITICAL();}
}

cpu.h:#define BYTE_ORDER LITTLE_ENDIAN  // 小端模式

cc.h:


#include "cpu.h"
#include "stdio.h"
#include "FreeRTOS.h"
#include "task.h"// LWIP需要的类型重命名
typedef unsigned   char    u8_t;    /* Unsigned 8 bit quantity         */
typedef signed     char    s8_t;    /* Signed    8 bit quantity        */
typedef unsigned   short   u16_t;   /* Unsigned 16 bit quantity        */
typedef signed     short   s16_t;   /* Signed   16 bit quantity        */
typedef unsigned   long    u32_t;   /* Unsigned 32 bit quantity        */
typedef signed     long    s32_t;   /* Signed   32 bit quantity        */
typedef u32_t mem_ptr_t;           /* Unsigned 32 bit quantity        */
typedef int sys_prot_t;// LWIP需要的临界段
#define OS_CRITICAL_METHOD#ifdef OS_CRITICAL_METHOD#define SCB_ICSR_REG    (*((volatile uint32_t * ) 0xe000ed04))extern uint32_t SysCriticalEnter(void);
extern void SysCriticalExit(uint32_t lev);#define SYS_ARCH_DECL_PROTECT(lev)   u32_t lev#define SYS_ARCH_PROTECT(lev)        lev = SysCriticalEnter()#define SYS_ARCH_UNPROTECT(lev)      SysCriticalExit(lev)#endif// 对齐方案
#if defined (__ICCARM__)#define PACK_STRUCT_BEGIN
#define PACK_STRUCT_STRUCT
#define PACK_STRUCT_END
#define PACK_STRUCT_FIELD(x) x
#define PACK_STRUCT_USE_INCLUDES#elif defined (__CC_ARM)#define PACK_STRUCT_BEGIN __packed
#define PACK_STRUCT_STRUCT
#define PACK_STRUCT_END
#define PACK_STRUCT_FIELD(x) x#elif defined (__GNUC__)#define PACK_STRUCT_BEGIN
#define PACK_STRUCT_STRUCT __attribute__ ((__packed__))
#define PACK_STRUCT_END
#define PACK_STRUCT_FIELD(x) x#elif defined (__TASKING__)#define PACK_STRUCT_BEGIN
#define PACK_STRUCT_STRUCT
#define PACK_STRUCT_END
#define PACK_STRUCT_FIELD(x) x#endif#define U16_F "4d"
#define S16_F "4d"
#define X16_F "4x"
#define U32_F "8ld"
#define S32_F "8ld"
#define X32_F "8lx"/*--------------macros--------------------------------------------------------*/
#ifndef LWIP_PLATFORM_ASSERT
#define LWIP_PLATFORM_ASSERT(x) \do \{   printf("Assertion \"%s\" failed at line %d in %s\r\n", x, __LINE__, __FILE__); \} while(0)
#endif#ifndef LWIP_PLATFORM_DIAG
#define LWIP_PLATFORM_DIAG(x) do {printf x;} while(0)
#endif

lwipopts.h

#ifndef _LWIPOPTS_H_
#define _LWIPOPTS_H_// 与FreeRTOS配置类似(FreeRTOSConfig.h)// 线程优先级
#ifndef TCPIP_THREAD_PRIO
#define TCPIP_THREAD_PRIO     (configMAX_PRIORITIES - 1) // 定义内核任务的优先级为最高
#endif
#undef  DEFAULT_THREAD_PRIO
#define DEFAULT_THREAD_PRIO   8#define SYS_LIGHTWEIGHT_PROT    1     // 为1时使用实时操作系统的轻量级保护,保护关键代码不被中断打断
#define NO_SYS                  0     // 使用UCOS操作系统
#define MEM_ALIGNMENT           4     // 使用4字节对齐模式
#define MEM_SIZE                16000 // 内存堆heap大小
#define MEMP_NUM_PBUF           20    // MEMP_NUM_PBUF:memp结构的pbuf数量,如果应用从ROM或者静态存储区发送大量数据时,这个值应该设置大一点
#define MEMP_NUM_UDP_PCB        6     // MEMP_NUM_UDP_PCB:UDP协议控制块(PCB)数量.每个活动的UDP"连接"需要一个PCB.
#define MEMP_NUM_TCP_PCB        10    // MEMP_NUM_TCP_PCB:同时建立激活的TCP数量
#define MEMP_NUM_TCP_PCB_LISTEN 6     // MEMP_NUM_TCP_PCB_LISTEN:能够监听的TCP连接数量
#define MEMP_NUM_TCP_SEG        15    // MEMP_NUM_TCP_SEG:最多同时在队列中的TCP段数量
#define MEMP_NUM_SYS_TIMEOUT    8     // MEMP_NUM_SYS_TIMEOUT:能够同时激活的timeout个数// pbuf内存池
#define PBUF_POOL_SIZE          20    // PBUF_POOL_SIZE:pbuf内存池个数
#define PBUF_POOL_BUFSIZE       512   // PBUF_POOL_BUFSIZE:每个pbuf内存池大小#define LWIP_TCP                1     // 使用TCP
#define TCP_TTL                 255   // 生存时间#undef TCP_QUEUE_OOSEQ
#define TCP_QUEUE_OOSEQ         0     // 当TCP的数据段超出队列时的控制位,当设备的内存过小的时候此项应为0#undef TCPIP_MBOX_SIZE
#define TCPIP_MBOX_SIZE         MAX_QUEUE_ENTRIES // tcpip创建主线程时的消息邮箱大小#undef DEFAULT_TCP_RECVMBOX_SIZE
#define DEFAULT_TCP_RECVMBOX_SIZE       MAX_QUEUE_ENTRIES#undef DEFAULT_ACCEPTMBOX_SIZE
#define DEFAULT_ACCEPTMBOX_SIZE         MAX_QUEUE_ENTRIES#define TCP_MSS                 (1500 - 40) // 最大TCP分段,TCP_MSS = (MTU - IP报头大小 - TCP报头大小
#define TCP_SND_BUF             (4*TCP_MSS) // TCP发送缓冲区大小(bytes).
#define TCP_SND_QUEUELEN        (2* TCP_SND_BUF/TCP_MSS)  //TCP_SND_QUEUELEN: TCP发送缓冲区大小(pbuf).这个值最小为(2 * TCP_SND_BUF/TCP_MSS)
#define TCP_WND                 (2*TCP_MSS)   // TCP发送窗口
#define LWIP_ICMP               1   // 使用ICMP协议
#define LWIP_DHCP               0   // 使用DHCP
#define LWIP_UDP                1   // 使用UDP服务
#define UDP_TTL                 255 // UDP数据包生存时间
#define LWIP_STATS 0
#define LWIP_PROVIDE_ERRNO 1// 帧校验和选项,STM32F7xx允许通过硬件识别和计算IP,UDP和ICMP的帧校验和
#define CHECKSUM_BY_HARDWARE // 定义CHECKSUM_BY_HARDWARE,使用硬件帧校验
#ifdef CHECKSUM_BY_HARDWARE
//CHECKSUM_GEN_IP==0: 硬件生成IP数据包的帧校验和
#define CHECKSUM_GEN_IP                 0
//CHECKSUM_GEN_UDP==0: 硬件生成UDP数据包的帧校验和
#define CHECKSUM_GEN_UDP                0
//CHECKSUM_GEN_TCP==0: 硬件生成TCP数据包的帧校验和
#define CHECKSUM_GEN_TCP                0
//CHECKSUM_CHECK_IP==0: 硬件检查输入的IP数据包帧校验和
#define CHECKSUM_CHECK_IP               0
//CHECKSUM_CHECK_UDP==0: 硬件检查输入的UDP数据包帧校验和
#define CHECKSUM_CHECK_UDP              0
//CHECKSUM_CHECK_TCP==0: 硬件检查输入的TCP数据包帧校验和
#define CHECKSUM_CHECK_TCP              0
//CHECKSUM_CHECK_ICMP==1:硬件检查输入的ICMP数据包帧校验和
#define CHECKSUM_GEN_ICMP               0
#else
//CHECKSUM_GEN_IP==1: 软件生成IP数据包帧校验和
#define CHECKSUM_GEN_IP                 1
// CHECKSUM_GEN_UDP==1: 软件生成UDOP数据包帧校验和
#define CHECKSUM_GEN_UDP                1
//CHECKSUM_GEN_TCP==1: 软件生成TCP数据包帧校验和
#define CHECKSUM_GEN_TCP                1
// CHECKSUM_CHECK_IP==1: 软件检查输入的IP数据包帧校验和
#define CHECKSUM_CHECK_IP               1
// CHECKSUM_CHECK_UDP==1: 软件检查输入的UDP数据包帧校验和
#define CHECKSUM_CHECK_UDP              1
//CHECKSUM_CHECK_TCP==1: 软件检查输入的TCP数据包帧校验和
#define CHECKSUM_CHECK_TCP              1
//CHECKSUM_CHECK_ICMP==1:软件检查输入的ICMP数据包帧校验和
#define CHECKSUM_GEN_ICMP               1
#endif#define LWIP_NETCONN                    1 // LWIP_NETCONN==1:使能NETCON函数(要求使用api_lib.c)
#define LWIP_SOCKET                     1 // LWIP_SOCKET==1:使能Socket API(要求使用sockets.c)
#define LWIP_COMPAT_MUTEX               1
#define LWIP_SO_RCVTIMEO                1 // 通过定义LWIP_SO_RCVTIMEO使能netconn结构体中recv_timeout,使用recv_timeout可以避免阻塞线程// 有关系统的选项
#define TCPIP_THREAD_STACKSIZE          1024  // 内核任务堆栈大小
#define DEFAULT_UDP_RECVMBOX_SIZE       2000
#define DEFAULT_THREAD_STACKSIZE        512//LWIP调试选项
#define LWIP_DEBUG                      0 // 关闭DEBUG选项
#define ICMP_DEBUG                      LWIP_DBG_OFF // 开启/关闭ICMPdebug#endif /* __LWIPOPTS_H__ */

主函数需调用的初始化接口:

lwip_dev_t lwip_dev;            // lwip控制结构体
static struct netif lwip_netif; // 网络接口extern uint32_t memp_get_memorysize(void);  // 在memp.c里面定义
extern uint8_t *memp_memory;                        // 在memp.c里面定义.
extern uint8_t *ram_heap;                           // 在mem.c里面定义.// 释放内存
static void lwip_comm_mem_free(void)
{MemFree(SRAM_TYPE_IN, memp_memory);MemFree(SRAM_TYPE_IN, ram_heap);
}// 分配内存
static uint8_t lwip_comm_mem_malloc(void)
{uint32_t mempsize;uint32_t ramheapsize;mempsize = memp_get_memorysize(); // 得到memp_memory数组大小memp_memory = MemAlloc(SRAM_TYPE_IN, mempsize); // 为memp_memory申请内存ramheapsize = LWIP_MEM_ALIGN_SIZE(MEM_SIZE) + 2 * LWIP_MEM_ALIGN_SIZE(4 * 3) + MEM_ALIGNMENT; // 得到ram heap大小ram_heap = MemAlloc(SRAM_TYPE_IN, ramheapsize); // 为ram_heap申请内存if(!memp_memory || !ram_heap){lwip_comm_mem_free();return 1;}return 0;
}// 网络默认配置
static void lwip_comm_default_ip_set(lwip_dev_t *dev)
{uint32_t sn0;SocIDGet(&sn0, CONFIG_SYSTEM_HARDWARE_TYPE);// MAC地址设置(高三字节固定为:2.0.0,低三字节用STM32唯一ID)dev->mac[0] = 2;dev->mac[1] = 0;dev->mac[2] = 0;dev->mac[3] = (sn0 >> 16) & 0xFF;dev->mac[4] = (sn0 >> 8) & 0xFF;dev->mac[5] = sn0 & 0xFF;// 默认远程IPdev->remoteip[0] = CONFIG_ETHERNET_REMOTE_IP0;dev->remoteip[1] = CONFIG_ETHERNET_REMOTE_IP1;dev->remoteip[2] = CONFIG_ETHERNET_REMOTE_IP2;dev->remoteip[3] = CONFIG_ETHERNET_REMOTE_IP3;// 本地IPdev->ip[0] = CONFIG_ETHERNET_LOCAL_IP0;dev->ip[1] = CONFIG_ETHERNET_LOCAL_IP1;dev->ip[2] = CONFIG_ETHERNET_LOCAL_IP2;dev->ip[3] = CONFIG_ETHERNET_LOCAL_IP3;// 子网掩码dev->netmask[0] = CONFIG_ETHERNET_NETMASK0;dev->netmask[1] = CONFIG_ETHERNET_NETMASK1;dev->netmask[2] = CONFIG_ETHERNET_NETMASK2;dev->netmask[3] = CONFIG_ETHERNET_NETMASK3;// 网关dev->gateway[0] = CONFIG_ETHERNET_GATEWAY0;dev->gateway[1] = CONFIG_ETHERNET_GATEWAY1;dev->gateway[2] = CONFIG_ETHERNET_GATEWAY2;dev->gateway[3] = CONFIG_ETHERNET_GATEWAY3;dev->dhcpstatus = 0; // 没有DHCP
}// LWIP初始化(LWIP启动的时候使用)
uint8_t LWIPCommInit(void)
{struct netif *netif_init;           // 调用netif_add()函数时的返回值,用于判断网络初始化是否成功struct ip_addr ipaddr, netmask, gw; //ip地址  子网掩码  网关if(lwip_comm_mem_malloc()){return LWIP_COMM_STATUS_RAM_ERROR;  //内存申请失败}lwip_comm_default_ip_set(&lwip_dev);  //设置默认IP等信息LAN8720Init(); // 硬件网口初始化tcpip_init(NULL, NULL); // 初始化tcp ip内核,该函数里面会创建tcpip_thread内核任务IP4_ADDR(&ipaddr, lwip_dev.ip[0], lwip_dev.ip[1], lwip_dev.ip[2], lwip_dev.ip[3]);IP4_ADDR(&netmask, lwip_dev.netmask[0], lwip_dev.netmask[1], lwip_dev.netmask[2], lwip_dev.netmask[3]);IP4_ADDR(&gw, lwip_dev.gateway[0], lwip_dev.gateway[1], lwip_dev.gateway[2], lwip_dev.gateway[3]);printf("网卡en的MAC地址%d.%d.%d.%d.%d.%d\r\n", lwip_dev.mac[0], lwip_dev.mac[1], lwip_dev.mac[2], lwip_dev.mac[3], lwip_dev.mac[4], lwip_dev.mac[5]);printf("静态IP地址.....%d.%d.%d.%d\r\n", lwip_dev.ip[0], lwip_dev.ip[1], lwip_dev.ip[2], lwip_dev.ip[3]);printf("子网掩码.......%d.%d.%d.%d\r\n", lwip_dev.netmask[0], lwip_dev.netmask[1], lwip_dev.netmask[2], lwip_dev.netmask[3]);printf("默认网关.......%d.%d.%d.%d\r\n", lwip_dev.gateway[0], lwip_dev.gateway[1], lwip_dev.gateway[2], lwip_dev.gateway[3]);netif_init = netif_add(&lwip_netif, &ipaddr, &netmask, &gw, NULL, &ethernetif_init, &tcpip_input); // 向网卡列表中添加一个网口if(netif_init == NULL){return LWIP_COMM_STATUS_NET_ADD_FAIL;  // 网卡添加失败}else // 网口添加成功后,设置netif为默认值,并且打开netif网口{netif_set_default(&lwip_netif); // 设置netif为默认网口netif_set_up(&lwip_netif);      // 打开netif网口}return LWIP_COMM_STATUS_OK; // OK.
}// 用于以太网中断调用
void LWIPCommProcess(void)
{ethernetif_input(&lwip_netif);
}

ethernetif.c:

// 由ethernetif_init()调用用于初始化硬件
// netif:网卡结构体指针
// 返回值:ERR_OK,正常
//       其他,失败
static err_t low_level_init(struct netif *netif)
{netif->hwaddr_len = ETHARP_HWADDR_LEN; // 设置MAC地址长度,为6个字节// 初始化MAC地址,设置什么地址由用户自己设置,但是不能与网络中其他设备MAC地址重复netif->hwaddr[0] = lwip_dev.mac[0];netif->hwaddr[1] = lwip_dev.mac[1];netif->hwaddr[2] = lwip_dev.mac[2];netif->hwaddr[3] = lwip_dev.mac[3];netif->hwaddr[4] = lwip_dev.mac[4];netif->hwaddr[5] = lwip_dev.mac[5];netif->mtu = 1500; //最大允许传输单元,允许该网卡广播和ARP功能netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP;HAL_ETH_DMATxDescListInit(&lan8720_dev.handle, lan8720_dev.dma_tx, lan8720_dev.tx_buffer, ETH_TXBUFNB); // 初始化发送描述符HAL_ETH_DMARxDescListInit(&lan8720_dev.handle, lan8720_dev.dma_rx, lan8720_dev.rx_buffer, ETH_RXBUFNB); // 初始化接收描述符HAL_ETH_Start(&lan8720_dev.handle); // 开启MAC和DMAreturn ERR_OK;
}// 用于发送数据包的最底层函数(lwip通过netif->linkoutput指向该函数)
// netif:网卡结构体指针
// p:pbuf数据结构体指针
// 返回值:ERR_OK,发送正常
//       ERR_MEM,发送失败
static err_t low_level_output(struct netif *netif, struct pbuf *p)
{err_t errval;struct pbuf *q;uint8_t *buffer = (uint8_t *)(lan8720_dev.handle.TxDesc->Buffer1Addr);__IO ETH_DMADescTypeDef *DmaTxDesc;uint32_t framelength = 0;uint32_t bufferoffset = 0;uint32_t byteslefttocopy = 0;uint32_t payloadoffset = 0;DmaTxDesc = lan8720_dev.handle.TxDesc;bufferoffset = 0;// 从pbuf中拷贝要发送的数据for(q = p; q != NULL; q = q->next){// 判断此发送描述符是否有效,即判断此发送描述符是否归以太网DMA所有if((DmaTxDesc->Status & ETH_DMATXDESC_OWN) != (uint32_t)RESET){errval = ERR_USE;goto error;              // 发送描述符无效,不可用}byteslefttocopy = q->len;   // 要发送的数据长度payloadoffset = 0;// 将pbuf中要发送的数据写入到以太网发送描述符中,有时候我们要发送的数据可能大于一个以太网// 描述符的Tx Buffer,因此我们需要分多次将数据拷贝到多个发送描述符中while((byteslefttocopy + bufferoffset) > ETH_TX_BUF_SIZE ){// 将数据拷贝到以太网发送描述符的Tx Buffer中memcpy((uint8_t*)((uint8_t*)buffer + bufferoffset), (uint8_t*)((uint8_t*)q->payload + payloadoffset), (ETH_TX_BUF_SIZE - bufferoffset));// DmaTxDsc指向下一个发送描述符DmaTxDesc = (ETH_DMADescTypeDef *)(DmaTxDesc->Buffer2NextDescAddr);// 检查新的发送描述符是否有效if((DmaTxDesc->Status & ETH_DMATXDESC_OWN) != (uint32_t)RESET){errval = ERR_USE;goto error;     // 发送描述符无效,不可用}buffer = (uint8_t *)(DmaTxDesc->Buffer1Addr); // 更新buffer地址,指向新的发送描述符的Tx Bufferbyteslefttocopy = byteslefttocopy - (ETH_TX_BUF_SIZE - bufferoffset);payloadoffset = payloadoffset + (ETH_TX_BUF_SIZE - bufferoffset);framelength = framelength + (ETH_TX_BUF_SIZE - bufferoffset);bufferoffset = 0;}// 拷贝剩余的数据memcpy( (uint8_t*)((uint8_t*)buffer + bufferoffset), (uint8_t*)((uint8_t*)q->payload + payloadoffset), byteslefttocopy );bufferoffset = bufferoffset + byteslefttocopy;framelength = framelength + byteslefttocopy;}// 当所有要发送的数据都放进发送描述符的Tx Buffer以后就可发送此帧了HAL_ETH_TransmitFrame(&lan8720_dev.handle, framelength);errval = ERR_OK;
error:// 发送缓冲区发生下溢,一旦发送缓冲区发生下溢TxDMA会进入挂起状态if((lan8720_dev.handle.Instance->DMASR & ETH_DMASR_TUS) != (uint32_t)RESET){// 清除下溢标志lan8720_dev.handle.Instance->DMASR = ETH_DMASR_TUS;// 当发送帧中出现下溢错误的时候TxDMA会挂起,这时候需要向DMATPDR寄存器// 随便写入一个值来将其唤醒,此处我们写0lan8720_dev.handle.Instance->DMATPDR = 0;}return errval;
}// 用于接收数据包的最底层函数
// neitif:网卡结构体指针
// 返回值:pbuf数据结构体指针
static struct pbuf * low_level_input(struct netif *netif)
{struct pbuf *p = NULL;struct pbuf *q;uint16_t len;uint8_t *buffer;__IO ETH_DMADescTypeDef *dmarxdesc;uint32_t bufferoffset = 0;uint32_t payloadoffset = 0;uint32_t byteslefttocopy = 0;uint32_t i = 0;if(HAL_ETH_GetReceivedFrame(&lan8720_dev.handle) != HAL_OK) // 判断是否接收到数据{return NULL;}len = lan8720_dev.handle.RxFrameInfos.length;               // 获取接收到的以太网帧长度buffer = (uint8_t *)lan8720_dev.handle.RxFrameInfos.buffer; // 获取接收到的以太网帧的数据bufferif(len > 0){p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL);  // 申请pbuf}if(p != NULL)                                // pbuf申请成功{dmarxdesc = lan8720_dev.handle.RxFrameInfos.FSRxDesc;  // 获取接收描述符链表中的第一个描述符bufferoffset = 0;for(q = p; q != NULL; q = q->next){byteslefttocopy = q->len;payloadoffset = 0;// 将接收描述符中Rx Buffer的数据拷贝到pbuf中while((byteslefttocopy + bufferoffset) > ETH_RX_BUF_SIZE ){// 将数据拷贝到pbuf中memcpy((uint8_t*)((uint8_t*)q->payload + payloadoffset), (uint8_t*)((uint8_t*)buffer + bufferoffset), (ETH_RX_BUF_SIZE - bufferoffset));// dmarxdesc向下一个接收描述符dmarxdesc = (ETH_DMADescTypeDef *)(dmarxdesc->Buffer2NextDescAddr);// 更新buffer地址,指向新的接收描述符的Rx Bufferbuffer = (uint8_t *)(dmarxdesc->Buffer1Addr);byteslefttocopy = byteslefttocopy - (ETH_RX_BUF_SIZE - bufferoffset);payloadoffset = payloadoffset + (ETH_RX_BUF_SIZE - bufferoffset);bufferoffset = 0;}// 拷贝剩余的数据memcpy((uint8_t*)((uint8_t*)q->payload + payloadoffset), (uint8_t*)((uint8_t*)buffer + bufferoffset), byteslefttocopy);bufferoffset = bufferoffset + byteslefttocopy;}}// 释放DMA描述符dmarxdesc = lan8720_dev.handle.RxFrameInfos.FSRxDesc;for(i = 0; i < lan8720_dev.handle.RxFrameInfos.SegCount; i++){dmarxdesc->Status |= ETH_DMARXDESC_OWN;     //标记描述符归DMA所有dmarxdesc = (ETH_DMADescTypeDef *)(dmarxdesc->Buffer2NextDescAddr);}lan8720_dev.handle.RxFrameInfos.SegCount = 0;          //清除段计数器if((lan8720_dev.handle.Instance->DMASR & ETH_DMASR_RBUS) != (uint32_t)RESET) //接收缓冲区不可用{// 清除接收缓冲区不可用标志lan8720_dev.handle.Instance->DMASR = ETH_DMASR_RBUS;// 当接收缓冲区不可用的时候RxDMA会进去挂起状态,通过向DMARPDR写入任意一个值来唤醒Rx DMAlan8720_dev.handle.Instance->DMARPDR = 0;}return p;
}// 网卡接收数据(lwip直接调用)
// netif:网卡结构体指针
// 返回值:ERR_OK,发送正常
//       ERR_MEM,发送失败
err_t ethernetif_input(struct netif *netif)
{err_t err;struct pbuf *p;p = low_level_input(netif); //调用low_level_input函数接收数据if(p == NULL){return ERR_MEM;}err = netif->input(p, netif); //调用netif结构体中的input字段(一个函数)来处理数据包if(err != ERR_OK){LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_input: IP input error\n"));pbuf_free(p);p = NULL;}return err;
}// 使用low_level_init()函数来初始化网络
// netif:网卡结构体指针
// 返回值:ERR_OK,正常
//       其他,失败
err_t ethernetif_init(struct netif *netif)
{LWIP_ASSERT("netif!=NULL", (netif != NULL));
#if LWIP_NETIF_HOSTNAME     // LWIP_NETIF_HOSTNAME netif->hostname = "lwip"; // 初始化名称
#endifnetif->name[0] = IFNAME0; // 初始化变量netif的name字段netif->name[1] = IFNAME1; // 在文件外定义这里不用关心具体值netif->output = etharp_output; // IP层发送数据包函数netif->linkoutput = low_level_output; // ARP模块发送数据包函数low_level_init(netif);    // 底层硬件初始化函数return ERR_OK;
}

TCP客户端实现:

#ifndef _LWIP_TCP_CLIENT_H_
#define _LWIP_TCP_CLIENT_H_void TCPClientProcess(void);void TCPClientSend(uint8_t *data, uint16_t length);#endif /* _LWIP_TCP_CLIENT_H_ */

TaskHandle_t os_tcp_handle;#define TCP_CLIENT_RX_BUFSIZE 1500  // 接收缓冲区长度typedef struct
{struct netconn *sockfd; // TCP CLIENT网络连接结构体err_t connect_err;err_t recv_err;err_t send_err;// 服务器IP和端口ip_addr_t server_ipaddr;uint16_t server_port;// 客户端IP和端口ip_addr_t loca_ipaddr;uint16_t loca_port;uint8_t state; // 网络连接的状态uint8_t recv_buf[TCP_CLIENT_RX_BUFSIZE];uint16_t recv_len;
} tcp_client_t;static tcp_client_t tcp_client;static void tcp_client_close(void)
{netconn_close(tcp_client.sockfd);netconn_delete(tcp_client.sockfd);printf("服务器	%d.%d.%d.%d断开连接 \r\n", lwip_dev.remoteip[0], lwip_dev.remoteip[1], lwip_dev.remoteip[2], lwip_dev.remoteip[3]);
}static void tcp_client_recv(void)
{struct netbuf *recvbuf;struct pbuf *q;while(1){tcp_client.recv_err = netconn_recv(tcp_client.sockfd, &recvbuf);if(tcp_client.recv_err == ERR_OK) //接收到数据{taskENTER_CRITICAL();tcp_client.recv_len = 0;for(q = recvbuf->p; q != NULL; q = q->next) // 遍历完整个pbuf链表{printf(" %d ...  \r\n", q->len);memcpy(tcp_client.recv_buf + tcp_client.recv_len, q->payload, q->len);tcp_client.recv_len += q->len;}taskEXIT_CRITICAL();LWIPParse(tcp_client.recv_buf, tcp_client.recv_len);netbuf_delete(recvbuf);}else if(tcp_client.recv_err == ERR_CLSD)   //关闭连接{tcp_client_close();break;}// 网线断掉 删除连接tcp_client.state = LAN8720ReadPHY(PHY_BSR);if((tcp_client.state & 0x04) == 0){printf("close\r\n");tcp_client_close();break;}}
}static void task_tcp_client(void *arg)
{tcp_client.server_port = CONFIG_ETHERNET_REMOTE_PORT;IP4_ADDR(&tcp_client.server_ipaddr, lwip_dev.remoteip[0], lwip_dev.remoteip[1], lwip_dev.remoteip[2], lwip_dev.remoteip[3]);while (1){tcp_client.sockfd = netconn_new(NETCONN_TCP); // 创建一个TCP链接tcp_client.connect_err = netconn_connect(tcp_client.sockfd, &tcp_client.server_ipaddr, tcp_client.server_port); //连接服务器if(tcp_client.connect_err != ERR_OK){netconn_delete(tcp_client.sockfd); // 删除tcp_clientconn连接}else{tcp_client.sockfd->recv_timeout = 5;netconn_getaddr(tcp_client.sockfd, &tcp_client.loca_ipaddr, &tcp_client.loca_port, 1); // 获取本地IP主机IP地址和端口号printf("连接上服务器: %d.%d.%d.%d, 本机端口:%d\r\n", lwip_dev.remoteip[0], lwip_dev.remoteip[1], lwip_dev.remoteip[2], lwip_dev.remoteip[3], tcp_client.loca_port);tcp_client_recv();}}
}void TCPClientProcess(void)
{LWIPCommInit();xTaskCreate((TaskFunction_t)task_tcp_client,(const char*  )"tcp_client_task",(uint16_t     )OS_TCP_STK_SIZE,(void*        )NULL,(UBaseType_t  )OS_PRIO_TCP,(TaskHandle_t*)&os_tcp_handle);
}void TCPClientSend(uint8_t *data, uint16_t length)
{tcp_client.send_err = netconn_write(tcp_client.sockfd, data, length, NETCONN_COPY); //发送数据if(tcp_client.send_err != ERR_OK){printf("发送失败\r\n");tcp_client_close();}
}

 

 

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/465595.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

我是在这里学到的Linux知识的

-上图是读者朋友圈照片&#xff0c;已咨询原创作者这是一个群友分享的学习资料&#xff0c;确实很棒。今天在上班&#xff0c;临近下班的时候&#xff0c;我到茶水间打水&#xff0c;当时下比较大的雨&#xff0c;从里面向外面看出去&#xff0c;看到蒙胧胧的一片&#xff0c;春…

c#字符型转化为asc_wap站 utf-8与 gb2312字符编码的转化函数

一般的wap站的编码都是<?xml version"1.0" encoding"utf-8"?>&#xff0c;所以用form 或者a 传递参数时会遇到乱码&#xff0c;在调用页面无法读取正确的中文字符&#xff0c;可以用一下的函数编译&#xff0c;转化成中文&#xff0c;然后调用Func…

Suct(一)

网址&#xff1a;https://github.com/ScutGame/Scut/wiki 优点&#xff1a;免费&#xff0c;教程多&#xff0c;又有Demo 贼好 【服务器框架层次结构图解】 &#xff08;好厉害~&#xff09; 【一、配置好 IIS Web 服务器】 1. 打开 控制面板 -> 程序和功能 -> 打开或关…

MATLAB —— 基础语法

MATLAB —— 基础语法 目录 MATLAB —— 基础语法 一、基础语法 1、常用的运算符 2、常用特殊变量和常数 3、变量 4、向量 5、矩阵 6、帮助手册 二、基础命令&#xff08;与Linux和DOS命令类似&#xff09; 1、会话管理命令 2、系统命令 3、输入输出命令 4、向量&…

VS2010中不可忽视的部分——VSTO

今天是Visual Studio 2010正式发布的日子&#xff0c;刚刚看了相关的新闻和介绍&#xff0c;感觉上相比Visual Studio 2008来说&#xff0c;又有了不少新的改变和创新&#xff0c;不管是炒作概念也好&#xff0c;炒作想法也罢&#xff0c;但都是想法设法地为我们这些辛苦的程序…

网校系统推荐eduline_网校系统的各项功能的用处

网校系统中有许多的子系统&#xff0c;其中众所周知也是相对重要的两个子系统&#xff0c;一个考试系统&#xff0c;一个直播系统&#xff0c;今天未来云校就来讲讲这两个子系统以及这两个子系统和网校系统的联系。在线模拟考试系统与网校系统大家都知道考试相对于学员、老师和…

学习 Linux/C/C++ 必备资料

在这个信息过载的时代&#xff0c;质量往往比数量更为重要&#xff01;接下来给你推荐的&#xff0c;是我私藏很久的几个有态度、有意思、有质量的优质公号。学习 Linux/C/C 必备&#xff01;快来关注&#xff0c;在这里看见另一种可能&#xff01;开发内功修炼公众号「开发内功…

emmc固件开发_UP2开发板简易开箱(二)

在 老狼 的x86开源硬件群里面抽中了一个UP2开发板&#xff0c;时间已经过去半年多了&#xff0c;实在惭愧&#xff0c;其中一部分原因是菜&#xff0c;菜是原罪。现接上一位抽中UP2的大佬 Jiaao Bai 的文章&#xff0c;本文就叫UP2开发板简易开箱&#xff08;二&#xff09;。一…

MATLAB —— 向量,矩阵,数组

MATLAB —— 向量&#xff0c;矩阵&#xff0c;数组 目录 MATLAB —— 向量&#xff0c;矩阵&#xff0c;数组 一、向量 1、向量定义 2、引用向量元素 3、向量运算 二、矩阵 1、矩阵定义 2、引用矩阵 3、矩阵运算 三、数组 一、向量 1、向量定义 向量是数字的一维数…

as的使用技巧及注意点

都是在项目中总结的&#xff0c;经过实践的&#xff0c;在flex2 as3下使用的。as调jsExternalInterface.call("js函数名",参数&#xff1a;数组);&#xff08;可以接收返回值&#xff09;js调asas&#xff1a;ExternalInterface.addCallback("js中使用的函数名&…

vue基础(学习官方文档)

基础 介绍 是什么 是一套用于构建用户界面的渐进式框架声明式渲染 <div id"app">{{ message }}</div>var app new Vue({el: #app,data: {message: Hello Vue!} })vue 实例 var vm new Vue({// 选项 })一个 Vue 应用由一个通过 new Vue 创建的根 Vue 实…

用GPIO口模拟串口通信,它真的来了

你是否遇到过某个MCU串口不够的情况&#xff1f; 这时我们可以考虑用GPIO去模拟&#xff0c;如何具体实现呢&#xff1f;首选我们需要了解串口的传输协议&#xff0c;UART使用异步模式工作&#xff0c;不需要时钟信号&#xff0c;其一般格式为&#xff1a;起始位数据位校验位停…

地壳中元素含量排名记忆口诀_在地球46亿年的历史中,氮气始终是含量最高的气体,为什么?...

人每时每刻都要呼吸&#xff0c;呼吸是生命得以存在的基础。不过人类赖以生存的氧气并不是地球上含量最高的气体&#xff0c;地球上含量最高的气体是氮气。在地球的大气之中&#xff0c;氮气的含量占到了78%&#xff0c;而氧气的含量排名第二&#xff0c;约为21%。我们经常会提…

MATLAB —— 绘图

MATLAB —— 绘图 目录 MATLAB —— 绘图 一、绘图 二、代数 三、转换函数 一、绘图 plot(x, y)&#xff1a;y f(x); 绘制图形 xlabel,ylabel&#xff1a;分别沿x轴&#xff0c;y轴生成标签 title&#xff1a;在图形上生成标题 grid on&#xff1a;将网格线放在图形上…

Cisco无线AP的配置

无线接入点&#xff08;AP&#xff09;在无线网络中充当用户的中心通信点&#xff0c;可以连接有线和无线网络。使用Web浏览器和命令行接口(CLI)都可以完成配置。 一般来说&#xff0c;AP可以配置两个接口 以太网接口和无线端口。 a&#xff1a;配置方式 1&#xff1a;控…

逆袭之旅.DAY08东软实训.多态~

2018年7月4日 转载于:https://www.cnblogs.com/twinkle-star/p/9262075.html

现在的年轻人,正在努力的改变着一些东西

这是前天朋友圈发的一个截图&#xff0c;内容也是从朋友圈来的&#xff0c;很认同这观点&#xff0c;今天的文章不会写很多内容&#xff0c;希望听到读者们对这样内容的想法。发出来后&#xff0c;除了一个当老板的朋友持反对外&#xff0c;大部分是赞同的&#xff0c;不过&…

我们是怎样发出声音的?

文&#xff1a;贰沐编辑&#xff1a;贰沐/子鱼提出“群体免疫”&#xff08;herd immunity&#xff09;的英国首相鲍里斯约翰逊&#xff08;Boris Johnson&#xff09;&#xff0c;上月底新冠确诊&#xff0c;这周初病情加重被送进ICU&#xff0c;英国的疫情也确实越来越严重。…

python3进阶开发-第一个仿博客园的项目(1)

首先我们要设计一下表结构&#xff1a; UserInfo(用户信息表) --------》一对一 -----------》Blog(博客信息表) UserInfo(用户信息表) ---------》一对多 -----------》Article UpDown(点赞表) UserInfo(用户信息表) ---------》一对多 -----------》Article Comment…

mac 安装nodejs_阿里开源——用于前端和nodejs的轻量级任务管理和构建工具Dawn

介绍Dawn 取「黎明、破晓」之意&#xff0c;原为「阿里云业务运营团队」内部的前端构建和工程化工具&#xff0c;现已完全开源。它通过 pipeline 和 middleware 将开发过程抽象为相对固定的阶段和有限的操作&#xff0c;简化并统一了开发人员的日常构建与开发相关的工作。Githu…