From 402f25a2fd7ef8a44862f389978dee95af632b4f Mon Sep 17 00:00:00 2001 From: Allenn Date: Wed, 13 Dec 2023 10:48:18 +0800 Subject: [PATCH 1/3] fix: fix ch32v307 cannot enter shell --- .../risc-v/ch32v307vct6/User/ch32v30x_it.c | 29 ++- .../XiZi_IIoT/arch/risc-v/ch32v307vct6/tick.c | 2 +- .../third_party_driver/uart/connect_uart.c | 186 +++++++++--------- 3 files changed, 101 insertions(+), 116 deletions(-) diff --git a/Ubiquitous/XiZi_IIoT/arch/risc-v/ch32v307vct6/User/ch32v30x_it.c b/Ubiquitous/XiZi_IIoT/arch/risc-v/ch32v307vct6/User/ch32v30x_it.c index dfdb9587f..fcc2ab4da 100644 --- a/Ubiquitous/XiZi_IIoT/arch/risc-v/ch32v307vct6/User/ch32v30x_it.c +++ b/Ubiquitous/XiZi_IIoT/arch/risc-v/ch32v307vct6/User/ch32v30x_it.c @@ -1,16 +1,16 @@ /********************************** (C) COPYRIGHT ******************************* -* File Name : ch32v10x_it.c -* Author : WCH -* Version : V1.0.0 -* Date : 2020/04/30 -* Description : Main Interrupt Service Routines. -* Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd. -* SPDX-License-Identifier: Apache-2.0 -*******************************************************************************/ + * File Name : ch32v10x_it.c + * Author : WCH + * Version : V1.0.0 + * Date : 2020/04/30 + * Description : Main Interrupt Service Routines. + * Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd. + * SPDX-License-Identifier: Apache-2.0 + *******************************************************************************/ /************************************************* File name: ch32v30x_it.c -Description: include peripheral supports for ch32v30x -History: +Description: include peripheral supports for ch32v30x +History: 1. Date: 2022-08-09 Author: AIIT XUOS Lab Modification: @@ -20,10 +20,8 @@ Modification: #include "board.h" #include - - -void NMI_Handler(void) __attribute__((interrupt("WCH-Interrupt-fast"))); -void HardFault_Handler(void) __attribute__((interrupt("WCH-Interrupt-fast"))); +void NMI_Handler(void) __attribute__((interrupt())); +void HardFault_Handler(void) __attribute__((interrupt())); /********************************************************************* * @fn NMI_Handler @@ -56,6 +54,3 @@ void HardFault_Handler(void) isrManager.done->decCounter(); FREE_INT_SP(); } - - - diff --git a/Ubiquitous/XiZi_IIoT/arch/risc-v/ch32v307vct6/tick.c b/Ubiquitous/XiZi_IIoT/arch/risc-v/ch32v307vct6/tick.c index 518f2f632..49c27966b 100644 --- a/Ubiquitous/XiZi_IIoT/arch/risc-v/ch32v307vct6/tick.c +++ b/Ubiquitous/XiZi_IIoT/arch/risc-v/ch32v307vct6/tick.c @@ -19,7 +19,7 @@ extern void KTaskOsAssignAfterIrq(void *); -void SysTick_Handler(void) __attribute__((interrupt("WCH-Interrupt-fast"))); +void SysTick_Handler(void) __attribute__((interrupt())); void SysTick_Handler(void) { GET_INT_SP(); diff --git a/Ubiquitous/XiZi_IIoT/board/ch32v307vct6/third_party_driver/uart/connect_uart.c b/Ubiquitous/XiZi_IIoT/board/ch32v307vct6/third_party_driver/uart/connect_uart.c index 23d6fa900..f36a7722d 100644 --- a/Ubiquitous/XiZi_IIoT/board/ch32v307vct6/third_party_driver/uart/connect_uart.c +++ b/Ubiquitous/XiZi_IIoT/board/ch32v307vct6/third_party_driver/uart/connect_uart.c @@ -1,35 +1,35 @@ /* -* Copyright (c) 2020 AIIT XUOS Lab -* XiUOS is licensed under Mulan PSL v2. -* You can use this software according to the terms and conditions of the Mulan PSL v2. -* You may obtain a copy of Mulan PSL v2 at: -* http://license.coscl.org.cn/MulanPSL2 -* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, -* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, -* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -* See the Mulan PSL v2 for more details. -*/ + * Copyright (c) 2020 AIIT XUOS Lab + * XiUOS is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ /** -* @file connect_usart.c -* @brief support ch32v307 vct6 uart function and register to bus framework -* @version 1.0 -* @author AIIT XUOS Lab -* @date 2022-08-01 -*/ + * @file connect_usart.c + * @brief support ch32v307 vct6 uart function and register to bus framework + * @version 1.0 + * @author AIIT XUOS Lab + * @date 2022-08-01 + */ -#include -#include "ch32v30x.h" -#include "xsconfig.h" #include "connect_uart.h" +#include "ch32v30x.h" #include "ch32v30x_usart.h" +#include "xsconfig.h" +#include /* uart driver */ static void SerialCfgParamCheck(struct SerialCfgParam* serial_cfg_default, struct SerialCfgParam* serial_cfg_new) { - struct SerialDataCfg *data_cfg_default = &serial_cfg_default->data_cfg; - struct SerialDataCfg *data_cfg_new = &serial_cfg_new->data_cfg; + struct SerialDataCfg* data_cfg_default = &serial_cfg_default->data_cfg; + struct SerialDataCfg* data_cfg_new = &serial_cfg_new->data_cfg; if ((data_cfg_default->serial_baud_rate != data_cfg_new->serial_baud_rate) && (data_cfg_new->serial_baud_rate)) { data_cfg_default->serial_baud_rate = data_cfg_new->serial_baud_rate; @@ -64,40 +64,39 @@ static void SerialCfgParamCheck(struct SerialCfgParam* serial_cfg_default, struc } } -static void UartIsr(struct SerialDriver *serial_drv, struct SerialHardwareDevice *serial_dev) +static void UartIsr(struct SerialDriver* serial_drv, struct SerialHardwareDevice* serial_dev) { - struct SerialCfgParam *serial_cfg = (struct SerialCfgParam *)serial_drv->private_data; - - if (RESET != USART_GetITStatus((USART_TypeDef *)serial_cfg->hw_cfg.serial_register_base, USART_IT_RXNE)) - { + struct SerialCfgParam* serial_cfg = (struct SerialCfgParam*)serial_drv->private_data; + + if (RESET != USART_GetITStatus((USART_TypeDef*)serial_cfg->hw_cfg.serial_register_base, USART_IT_RXNE)) { SerialSetIsr(serial_dev, SERIAL_EVENT_RX_IND); - USART_ClearITPendingBit((USART_TypeDef *)serial_cfg->hw_cfg.serial_register_base, USART_IT_RXNE); + USART_ClearITPendingBit((USART_TypeDef*)serial_cfg->hw_cfg.serial_register_base, USART_IT_RXNE); } } -static uint32 SerialInit(struct SerialDriver *serial_drv, struct BusConfigureInfo *configure_info) +static uint32 SerialInit(struct SerialDriver* serial_drv, struct BusConfigureInfo* configure_info) { NULL_PARAM_CHECK(serial_drv); struct SerialCfgParam* serial_cfg = (struct SerialCfgParam*)serial_drv->private_data; // struct UsartHwCfg *serial_hw_cfg = (struct UsartHwCfg *)serial_cfg->hw_cfg.private_data; - struct SerialHardwareDevice *serial_dev = (struct SerialHardwareDevice *)serial_drv->driver.owner_bus->owner_haldev; - struct SerialDevParam *dev_param = (struct SerialDevParam *)serial_dev->haldev.private_data; + struct SerialHardwareDevice* serial_dev = (struct SerialHardwareDevice*)serial_drv->driver.owner_bus->owner_haldev; + struct SerialDevParam* dev_param = (struct SerialDevParam*)serial_dev->haldev.private_data; if (configure_info->private_data) { - struct SerialCfgParam *serial_cfg_new = (struct SerialCfgParam *)configure_info->private_data; + struct SerialCfgParam* serial_cfg_new = (struct SerialCfgParam*)configure_info->private_data; SerialCfgParamCheck(serial_cfg, serial_cfg_new); - + if (serial_cfg_new->data_cfg.dev_recv_callback) { BusDevRecvCallback(&(serial_dev->haldev), serial_cfg_new->data_cfg.dev_recv_callback); } } - // config serial receive sem timeout - dev_param->serial_timeout = serial_cfg->data_cfg.serial_timeout; + // config serial receive sem timeout + dev_param->serial_timeout = serial_cfg->data_cfg.serial_timeout; // init usart type def - GPIO_InitTypeDef GPIO_InitStructure; + GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE); @@ -109,8 +108,7 @@ static uint32 SerialInit(struct SerialDriver *serial_drv, struct BusConfigureInf USART_InitTypeDef USART_InitStructure; USART_InitStructure.USART_BaudRate = serial_cfg->data_cfg.serial_baud_rate; - switch (serial_cfg->data_cfg.serial_data_bits) - { + switch (serial_cfg->data_cfg.serial_data_bits) { case DATA_BITS_8: USART_InitStructure.USART_WordLength = USART_WordLength_8b; break; @@ -122,9 +120,8 @@ static uint32 SerialInit(struct SerialDriver *serial_drv, struct BusConfigureInf USART_InitStructure.USART_WordLength = USART_WordLength_8b; break; } - - switch (serial_cfg->data_cfg.serial_stop_bits) - { + + switch (serial_cfg->data_cfg.serial_stop_bits) { case STOP_BITS_1: USART_InitStructure.USART_StopBits = USART_StopBits_1; break; @@ -135,9 +132,8 @@ static uint32 SerialInit(struct SerialDriver *serial_drv, struct BusConfigureInf USART_InitStructure.USART_StopBits = USART_StopBits_1; break; } - - switch (serial_cfg->data_cfg.serial_parity_mode) - { + + switch (serial_cfg->data_cfg.serial_parity_mode) { case PARITY_NONE: USART_InitStructure.USART_Parity = USART_Parity_No; break; @@ -160,81 +156,77 @@ static uint32 SerialInit(struct SerialDriver *serial_drv, struct BusConfigureInf // usart_hardware_flow_rts_config(serial_cfg->hw_cfg.serial_register_base, USART_RTS_DISABLE); // usart_hardware_flow_cts_config(serial_cfg->hw_cfg.serial_register_base, USART_CTS_DISABLE); - return EOK; } -static uint32 SerialConfigure(struct SerialDriver *serial_drv, int serial_operation_cmd) +static uint32 SerialConfigure(struct SerialDriver* serial_drv, int serial_operation_cmd) { NULL_PARAM_CHECK(serial_drv); - struct SerialCfgParam *serial_cfg = (struct SerialCfgParam *)serial_drv->private_data; + struct SerialCfgParam* serial_cfg = (struct SerialCfgParam*)serial_drv->private_data; - switch (serial_operation_cmd) - { + switch (serial_operation_cmd) { case OPER_CLR_INT: NVIC_DisableIRQ(serial_cfg->hw_cfg.serial_irq_interrupt); - USART_ITConfig((USART_TypeDef *)serial_cfg->hw_cfg.serial_register_base, USART_IT_RXNE, DISABLE); + USART_ITConfig((USART_TypeDef*)serial_cfg->hw_cfg.serial_register_base, USART_IT_RXNE, DISABLE); break; case OPER_SET_INT: NVIC_EnableIRQ(serial_cfg->hw_cfg.serial_irq_interrupt); /* enable USART0 receive interrupt */ - USART_ITConfig((USART_TypeDef *)serial_cfg->hw_cfg.serial_register_base, USART_IT_RXNE, ENABLE); + USART_ITConfig((USART_TypeDef*)serial_cfg->hw_cfg.serial_register_base, USART_IT_RXNE, ENABLE); break; } - + return EOK; } -static uint32 SerialDrvConfigure(void *drv, struct BusConfigureInfo *configure_info) +static uint32 SerialDrvConfigure(void* drv, struct BusConfigureInfo* configure_info) { NULL_PARAM_CHECK(drv); NULL_PARAM_CHECK(configure_info); x_err_t ret = EOK; int serial_operation_cmd; - struct SerialDriver *serial_drv = (struct SerialDriver *)drv; + struct SerialDriver* serial_drv = (struct SerialDriver*)drv; - switch (configure_info->configure_cmd) - { - case OPE_INT: - ret = SerialInit(serial_drv, configure_info); - break; - case OPE_CFG: - serial_operation_cmd = *(int *)configure_info->private_data; - ret = SerialConfigure(serial_drv, serial_operation_cmd); - break; - default: - break; + switch (configure_info->configure_cmd) { + case OPE_INT: + ret = SerialInit(serial_drv, configure_info); + break; + case OPE_CFG: + serial_operation_cmd = *(int*)configure_info->private_data; + ret = SerialConfigure(serial_drv, serial_operation_cmd); + break; + default: + break; } return ret; } -static int SerialPutChar(struct SerialHardwareDevice *serial_dev, char c) +static int SerialPutChar(struct SerialHardwareDevice* serial_dev, char c) { struct SerialCfgParam* serial_cfg = (struct SerialCfgParam*)serial_dev->private_data; - while (USART_GetFlagStatus((USART_TypeDef *)serial_cfg->hw_cfg.serial_register_base, USART_FLAG_TXE) == RESET); - USART_SendData((USART_TypeDef *)serial_cfg->hw_cfg.serial_register_base, (uint8_t) c); + while (USART_GetFlagStatus((USART_TypeDef*)serial_cfg->hw_cfg.serial_register_base, USART_FLAG_TXE) == RESET) + ; + USART_SendData((USART_TypeDef*)serial_cfg->hw_cfg.serial_register_base, (uint8_t)c); return 0; } -static int SerialGetChar(struct SerialHardwareDevice *serial_dev) +static int SerialGetChar(struct SerialHardwareDevice* serial_dev) { int ch = -1; - struct SerialCfgParam *serial_cfg = (struct SerialCfgParam *)serial_dev->private_data; + struct SerialCfgParam* serial_cfg = (struct SerialCfgParam*)serial_dev->private_data; - if (RESET != USART_GetFlagStatus((USART_TypeDef *)serial_cfg->hw_cfg.serial_register_base, USART_FLAG_RXNE)) - { + if (RESET != USART_GetFlagStatus((USART_TypeDef*)serial_cfg->hw_cfg.serial_register_base, USART_FLAG_RXNE)) { ch = USART_ReceiveData((USART_TypeDef*)serial_cfg->hw_cfg.serial_register_base) & 0xff; } return ch; } -static const struct SerialDataCfg data_cfg_init = -{ +static const struct SerialDataCfg data_cfg_init = { .serial_baud_rate = BAUD_RATE_115200, .serial_data_bits = DATA_BITS_8, .serial_stop_bits = STOP_BITS_1, @@ -246,20 +238,18 @@ static const struct SerialDataCfg data_cfg_init = }; /*manage the serial device operations*/ -static const struct SerialDrvDone drv_done = -{ +static const struct SerialDrvDone drv_done = { .init = SerialInit, .configure = SerialConfigure, }; /*manage the serial device hal operations*/ -static struct SerialHwDevDone hwdev_done = -{ +static struct SerialHwDevDone hwdev_done = { .put_char = SerialPutChar, .get_char = SerialGetChar, }; -static int BoardSerialBusInit(struct SerialBus *serial_bus, struct SerialDriver *serial_driver, const char *bus_name, const char *drv_name) +static int BoardSerialBusInit(struct SerialBus* serial_bus, struct SerialDriver* serial_driver, const char* bus_name, const char* drv_name) { x_err_t ret = EOK; @@ -282,13 +272,13 @@ static int BoardSerialBusInit(struct SerialBus *serial_bus, struct SerialDriver if (EOK != ret) { KPrintf("InitHwUart SerialDriverAttachToBus error %d\n", ret); return ERROR; - } + } return ret; } /*Attach the serial device to the serial bus*/ -static int BoardSerialDevBend(struct SerialHardwareDevice *serial_device, void *serial_param, const char *bus_name, const char *dev_name) +static int BoardSerialDevBend(struct SerialHardwareDevice* serial_device, void* serial_param, const char* bus_name, const char* dev_name) { x_err_t ret = EOK; @@ -296,27 +286,27 @@ static int BoardSerialDevBend(struct SerialHardwareDevice *serial_device, void * if (EOK != ret) { KPrintf("InitHwUart SerialDeviceInit device %s error %d\n", dev_name, ret); return ERROR; - } + } ret = SerialDeviceAttachToBus(dev_name, bus_name); if (EOK != ret) { KPrintf("InitHwUart SerialDeviceAttachToBus device %s error %d\n", dev_name, ret); return ERROR; - } + } - return ret; + return ret; } #ifdef BSP_USING_UART1 struct SerialDriver serial_driver_1; struct SerialHardwareDevice serial_device_1; -void USART1_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast"))); +void USART1_IRQHandler(void) __attribute__((interrupt())); void USART1_IRQHandler(void) { GET_INT_SP(); x_base level; - level= DisableLocalInterrupt(); + level = DisableLocalInterrupt(); isrManager.done->incCounter(); EnableLocalInterrupt(level); UartIsr(&serial_driver_1, &serial_device_1); @@ -333,7 +323,7 @@ int InitHwUart(void) static struct SerialBus serial_bus; memset(&serial_bus, 0, sizeof(struct SerialBus)); - + memset(&serial_driver_1, 0, sizeof(struct SerialDriver)); memset(&serial_device_1, 0, sizeof(struct SerialHardwareDevice)); @@ -343,7 +333,7 @@ int InitHwUart(void) static struct SerialDevParam serial_dev_param; memset(&serial_dev_param, 0, sizeof(struct SerialDevParam)); - + serial_driver_1.drv_done = &drv_done; serial_driver_1.configure = &SerialDrvConfigure; serial_device_1.hwdev_done = &hwdev_done; @@ -354,11 +344,11 @@ int InitHwUart(void) serial_cfg.hw_cfg.serial_register_base = (uint32)USART1; serial_cfg.hw_cfg.serial_irq_interrupt = USART1_IRQn; #endif - + serial_driver_1.private_data = (void*)&serial_cfg; serial_dev_param.serial_work_mode = SIGN_OPER_INT_RX; - serial_device_1.haldev.private_data = (void *)&serial_dev_param; + serial_device_1.haldev.private_data = (void*)&serial_dev_param; ret = BoardSerialBusInit(&serial_bus, &serial_driver_1, SERIAL_BUS_NAME_1, SERIAL_DRV_NAME_1); if (EOK != ret) { @@ -366,7 +356,7 @@ int InitHwUart(void) return ERROR; } - ret = BoardSerialDevBend(&serial_device_1, (void *)&serial_cfg, SERIAL_BUS_NAME_1, SERIAL_1_DEVICE_NAME_0); + ret = BoardSerialDevBend(&serial_device_1, (void*)&serial_cfg, SERIAL_BUS_NAME_1, SERIAL_1_DEVICE_NAME_0); if (EOK != ret) { KPrintf("InitHwUart uarths error ret %u\n", ret); return ERROR; @@ -384,14 +374,14 @@ int InitHwUart(void) GPIO_Init(GPIOA, &gpio_init_struct); USART_InitTypeDef usart_init_struct; - usart_init_struct.USART_BaudRate = 115200; - usart_init_struct.USART_HardwareFlowControl = USART_HardwareFlowControl_None; - usart_init_struct.USART_Mode = USART_Mode_Tx|USART_Mode_Rx; - usart_init_struct.USART_WordLength = USART_WordLength_8b; - usart_init_struct.USART_StopBits = USART_StopBits_1; - usart_init_struct.USART_Parity = USART_Parity_No; - USART_Init((USART_TypeDef *)serial_cfg.hw_cfg.serial_register_base, &usart_init_struct); + usart_init_struct.USART_BaudRate = 115200; + usart_init_struct.USART_HardwareFlowControl = USART_HardwareFlowControl_None; + usart_init_struct.USART_Mode = USART_Mode_Tx | USART_Mode_Rx; + usart_init_struct.USART_WordLength = USART_WordLength_8b; + usart_init_struct.USART_StopBits = USART_StopBits_1; + usart_init_struct.USART_Parity = USART_Parity_No; + USART_Init((USART_TypeDef*)serial_cfg.hw_cfg.serial_register_base, &usart_init_struct); USART_Cmd((USART_TypeDef*)serial_cfg.hw_cfg.serial_register_base, ENABLE); - + return ret; } \ No newline at end of file From f8b8ac46cac55b0558ec3cf2db98d24e17d8f798 Mon Sep 17 00:00:00 2001 From: Allenn Date: Wed, 13 Dec 2023 19:30:37 +0800 Subject: [PATCH 2/3] feat: support tcp client --- .../risc-v/ch32v307vct6/User/ch32v30x_it.c | 51 + .../XiZi_IIoT/board/ch32v307vct6/board.c | 71 +- .../XiZi_IIoT/board/ch32v307vct6/config.mk | 2 + .../ch32v307vct6/third_party_driver/Kconfig | 4 + .../ch32v307vct6/third_party_driver/Makefile | 4 + .../Peripheral/inc/ch32v30x_eth.h | 3 + .../third_party_driver/ethernet/Makefile | 4 + .../ethernet/connect_ether.c | 286 ++++ .../third_party_driver/ethernet/eth_driver.c | 1257 +++++++++++++++++ .../third_party_driver/ethernet/libwchnet.a | Bin 0 -> 222966 bytes .../third_party_driver/ethernet/test/Makefile | 4 + .../ethernet/test/wch_tcp_test.c | 72 + .../include/connect_ether.h | 40 + .../third_party_driver/include/eth_driver.h | 181 +++ .../third_party_driver/include/net_config.h | 160 +++ .../third_party_driver/include/wchnet.h | 591 ++++++++ Ubiquitous/XiZi_IIoT/link.mk | 2 +- 17 files changed, 2696 insertions(+), 36 deletions(-) create mode 100755 Ubiquitous/XiZi_IIoT/board/ch32v307vct6/third_party_driver/ethernet/Makefile create mode 100644 Ubiquitous/XiZi_IIoT/board/ch32v307vct6/third_party_driver/ethernet/connect_ether.c create mode 100644 Ubiquitous/XiZi_IIoT/board/ch32v307vct6/third_party_driver/ethernet/eth_driver.c create mode 100644 Ubiquitous/XiZi_IIoT/board/ch32v307vct6/third_party_driver/ethernet/libwchnet.a create mode 100755 Ubiquitous/XiZi_IIoT/board/ch32v307vct6/third_party_driver/ethernet/test/Makefile create mode 100644 Ubiquitous/XiZi_IIoT/board/ch32v307vct6/third_party_driver/ethernet/test/wch_tcp_test.c create mode 100644 Ubiquitous/XiZi_IIoT/board/ch32v307vct6/third_party_driver/include/connect_ether.h create mode 100644 Ubiquitous/XiZi_IIoT/board/ch32v307vct6/third_party_driver/include/eth_driver.h create mode 100644 Ubiquitous/XiZi_IIoT/board/ch32v307vct6/third_party_driver/include/net_config.h create mode 100644 Ubiquitous/XiZi_IIoT/board/ch32v307vct6/third_party_driver/include/wchnet.h diff --git a/Ubiquitous/XiZi_IIoT/arch/risc-v/ch32v307vct6/User/ch32v30x_it.c b/Ubiquitous/XiZi_IIoT/arch/risc-v/ch32v307vct6/User/ch32v30x_it.c index fcc2ab4da..d2f42f67b 100644 --- a/Ubiquitous/XiZi_IIoT/arch/risc-v/ch32v307vct6/User/ch32v30x_it.c +++ b/Ubiquitous/XiZi_IIoT/arch/risc-v/ch32v307vct6/User/ch32v30x_it.c @@ -18,10 +18,16 @@ Modification: *************************************************/ #include "ch32v30x_it.h" #include "board.h" +#include "ch32v30x_exti.h" +#include "ch32v30x_tim.h" +#include "eth_driver.h" #include void NMI_Handler(void) __attribute__((interrupt())); void HardFault_Handler(void) __attribute__((interrupt())); +void ETH_IRQHandler(void) __attribute__((interrupt())); +void TIM2_IRQHandler(void) __attribute__((interrupt())); +void EXTI9_5_IRQHandler(void) __attribute__((interrupt())); /********************************************************************* * @fn NMI_Handler @@ -51,6 +57,51 @@ void HardFault_Handler(void) GET_INT_SP(); isrManager.done->incCounter(); KPrintf("HardFault_Handler.\n"); + + KPrintf("mepc :%08x\r\n", __get_MEPC()); + KPrintf("mcause:%08x\r\n", __get_MCAUSE()); + KPrintf("mtval :%08x\r\n", __get_MTVAL()); + while (1) + ; + isrManager.done->decCounter(); FREE_INT_SP(); } + +/********************************************************************* + * @fn ETH_IRQHandler + * + * @brief This function handles ETH exception. + * + * @return none + */ +void ETH_IRQHandler(void) +{ + WCHNET_ETHIsr(); +} + +/********************************************************************* + * @fn TIM2_IRQHandler + * + * @brief This function handles TIM2 exception. + * + * @return none + */ +void TIM2_IRQHandler(void) +{ + WCHNET_TimeIsr(WCHNETTIMERPERIOD); + TIM_ClearITPendingBit(TIM2, TIM_IT_Update); +} + +/********************************************************************* + * @fn EXTI9_5_IRQHandler + * + * @brief This function handles GPIO exception. + * + * @return none + */ +void EXTI9_5_IRQHandler(void) +{ + ETH_PHYLink(); + EXTI_ClearITPendingBit(EXTI_Line7); /* Clear Flag */ +} \ No newline at end of file diff --git a/Ubiquitous/XiZi_IIoT/board/ch32v307vct6/board.c b/Ubiquitous/XiZi_IIoT/board/ch32v307vct6/board.c index 85ddfc11a..5ff2e9855 100644 --- a/Ubiquitous/XiZi_IIoT/board/ch32v307vct6/board.c +++ b/Ubiquitous/XiZi_IIoT/board/ch32v307vct6/board.c @@ -9,48 +9,48 @@ * 2018-11-12 Ernest Chen modify copyright */ /* -* Copyright (c) 2020 AIIT XUOS Lab -* XiUOS is licensed under Mulan PSL v2. -* You can use this software according to the terms and conditions of the Mulan PSL v2. -* You may obtain a copy of Mulan PSL v2 at: -* http://license.coscl.org.cn/MulanPSL2 -* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, -* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, -* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -* See the Mulan PSL v2 for more details. -*/ + * Copyright (c) 2020 AIIT XUOS Lab + * XiUOS is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ /** -* @file board.c -* @brief support ch32v307 init configure and start-up -* @version 1.0 -* @author AIIT XUOS Lab -* @date 2022-08-08 -*/ -#include -#include -#include -#include -#include "connect_uart.h" -#include "xsconfig.h" + * @file board.c + * @brief support ch32v307 init configure and start-up + * @version 1.0 + * @author AIIT XUOS Lab + * @date 2022-08-08 + */ #include "ch32v30x.h" +#include "connect_ether.h" +#include "connect_uart.h" #include "core_riscv.h" - // #include +#include "xsconfig.h" +#include +#include +#include +#include - // core clock. +// core clock. extern uint32_t SystemCoreClock; static uint32_t _SysTick_Config(uint32_t ticks) { - NVIC_SetPriority(SysTicK_IRQn,0xf0); - NVIC_SetPriority(Software_IRQn,0xf0); + NVIC_SetPriority(SysTicK_IRQn, 0xf0); + NVIC_SetPriority(Software_IRQn, 0xf0); NVIC_EnableIRQ(SysTicK_IRQn); NVIC_EnableIRQ(Software_IRQn); - SysTick->CTLR=0; - SysTick->SR=0; - SysTick->CNT=0; - SysTick->CMP=ticks-1; - SysTick->CTLR=0xF; + SysTick->CTLR = 0; + SysTick->SR = 0; + SysTick->CNT = 0; + SysTick->CMP = ticks - 1; + SysTick->CTLR = 0xF; return 0; } @@ -67,12 +67,13 @@ void InitBoardHardware() InitHwUart(); InstallConsole("uart1", "uart1_drv", "uart1_dev1"); + InitHwEth(); + KPrintf("consle init completed.\n"); KPrintf("board initialization......\n"); // KPrintf("memory address range: [0x%08x - 0x%08x], size: %d\n", (x_ubase) MEMORY_START_ADDRESS, (x_ubase) MEMORY_END_ADDRESS, gd32vf103_SRAM_SIZE); /* initialize memory system */ - - KPrintf("board init done.\n"); - KPrintf("start okernel...\n"); -} + KPrintf("board init done.\n"); + KPrintf("start okernel...\n"); +} diff --git a/Ubiquitous/XiZi_IIoT/board/ch32v307vct6/config.mk b/Ubiquitous/XiZi_IIoT/board/ch32v307vct6/config.mk index 5e4eba29b..630327afc 100755 --- a/Ubiquitous/XiZi_IIoT/board/ch32v307vct6/config.mk +++ b/Ubiquitous/XiZi_IIoT/board/ch32v307vct6/config.mk @@ -15,6 +15,8 @@ export CROSS_COMPILE ?=/opt/riscv-embedded-gcc/bin/riscv-none-embed- export DEFINES := -DHAVE_CCONFIG_H -DHAVE_SIGINFO +export LINK_WCH_NET := $(KERNEL_ROOT)/board/ch32v307vct6/third_party_driver/ethernet/libwchnet.a + export ARCH = risc-v export MCU = GH32V307 diff --git a/Ubiquitous/XiZi_IIoT/board/ch32v307vct6/third_party_driver/Kconfig b/Ubiquitous/XiZi_IIoT/board/ch32v307vct6/third_party_driver/Kconfig index ef7bb1505..64a20aaa9 100755 --- a/Ubiquitous/XiZi_IIoT/board/ch32v307vct6/third_party_driver/Kconfig +++ b/Ubiquitous/XiZi_IIoT/board/ch32v307vct6/third_party_driver/Kconfig @@ -14,5 +14,9 @@ menuconfig BSP_USING_GPIO if BSP_USING_GPIO source "$BSP_DIR/third_party_driver/gpio/Kconfig" endif + +menuconfig BSP_USING_ETH + bool "Using Ethernet" + default y \ No newline at end of file diff --git a/Ubiquitous/XiZi_IIoT/board/ch32v307vct6/third_party_driver/Makefile b/Ubiquitous/XiZi_IIoT/board/ch32v307vct6/third_party_driver/Makefile index bb2d5c7b2..7167d1e93 100644 --- a/Ubiquitous/XiZi_IIoT/board/ch32v307vct6/third_party_driver/Makefile +++ b/Ubiquitous/XiZi_IIoT/board/ch32v307vct6/third_party_driver/Makefile @@ -9,4 +9,8 @@ ifeq ($(CONFIG_BSP_USING_GPIO),y) SRC_DIR += gpio endif +ifeq ($(CONFIG_BSP_USING_ETH),y) + SRC_DIR += ethernet +endif + include $(KERNEL_ROOT)/compiler.mk \ No newline at end of file diff --git a/Ubiquitous/XiZi_IIoT/board/ch32v307vct6/third_party_driver/Peripheral/inc/ch32v30x_eth.h b/Ubiquitous/XiZi_IIoT/board/ch32v307vct6/third_party_driver/Peripheral/inc/ch32v30x_eth.h index 62ae39348..2f1621797 100755 --- a/Ubiquitous/XiZi_IIoT/board/ch32v307vct6/third_party_driver/Peripheral/inc/ch32v30x_eth.h +++ b/Ubiquitous/XiZi_IIoT/board/ch32v307vct6/third_party_driver/Peripheral/inc/ch32v30x_eth.h @@ -319,6 +319,9 @@ typedef struct /* PHY basic register */ #define PHY_BCR 0x0 /*PHY transceiver Basic Control Register */ #define PHY_BSR 0x01 /*PHY transceiver Basic Status Register */ +#define PHY_ANAR 0x04 /* Auto-Negotiation Advertisement Register */ +#define PHY_ANLPAR 0x05 /* Auto-Negotiation Link Partner Base Page Ability Register*/ +#define PHY_ANER 0x06 /* Auto-Negotiation Expansion Register */ #define PHY_BMCR PHY_BCR #define PHY_BMSR PHY_BSR #define PHY_STATUS 0x10 diff --git a/Ubiquitous/XiZi_IIoT/board/ch32v307vct6/third_party_driver/ethernet/Makefile b/Ubiquitous/XiZi_IIoT/board/ch32v307vct6/third_party_driver/ethernet/Makefile new file mode 100755 index 000000000..17b175216 --- /dev/null +++ b/Ubiquitous/XiZi_IIoT/board/ch32v307vct6/third_party_driver/ethernet/Makefile @@ -0,0 +1,4 @@ +SRC_FILES := eth_driver.c connect_ether.c +SRC_DIR := test + +include $(KERNEL_ROOT)/compiler.mk diff --git a/Ubiquitous/XiZi_IIoT/board/ch32v307vct6/third_party_driver/ethernet/connect_ether.c b/Ubiquitous/XiZi_IIoT/board/ch32v307vct6/third_party_driver/ethernet/connect_ether.c new file mode 100644 index 000000000..ae20610cc --- /dev/null +++ b/Ubiquitous/XiZi_IIoT/board/ch32v307vct6/third_party_driver/ethernet/connect_ether.c @@ -0,0 +1,286 @@ +/********************************** (C) COPYRIGHT ******************************* + * File Name : main.c + * Author : WCH + * Version : V1.0.0 + * Date : 2022/05/31 + * Description : Main program body. + ********************************************************************************* + * Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd. + * Attention: This software (modified or not) and binary are used for + * microcontroller manufactured by Nanjing Qinheng Microelectronics. + *******************************************************************************/ +/* + *@Note +TCP Client example, demonstrating that TCP Client connects +to the server and receives data and then sends it back. +For details on the selection of engineering chips, +please refer to the "CH32V30x Evaluation Board Manual" under the CH32V307EVT\EVT\PUB folder. + */ + +#include "connect_ether.h" +#include "ch32v30x_rcc.h" +#include "ch32v30x_tim.h" +#include "core_riscv.h" +#include "eth_driver.h" +#include "string.h" +#include "xs_base.h" + +extern uint32_t SystemCoreClock; +#define KEEPALIVE_ENABLE 1 // Enable keep alive function + +uint8_t MACAddr[6]; // MAC address +uint8_t IPAddr[4] = { 192, 168, 1, 10 }; // IP address +uint8_t GWIPAddr[4] = { 192, 168, 1, 1 }; // Gateway IP address +uint8_t IPMask[4] = { 255, 255, 255, 0 }; // subnet mask + +uint8_t MyBuf[RECE_BUF_LEN]; +uint8_t socket[WCHNET_MAX_SOCKET_NUM]; // Save the currently connected socket +uint8_t SocketRecvBuf[WCHNET_MAX_SOCKET_NUM][RECE_BUF_LEN]; // socket receive buffer + +/********************************************************************* + * @fn mStopIfError + * + * @brief check if error. + * + * @param iError - error constants. + * + * @return none + */ +void mStopIfError(uint8_t iError) +{ + if (iError == WCHNET_ERR_SUCCESS) + return; + KPrintf("Error: %02X\r\n", (uint16_t)iError); +} + +/********************************************************************* + * @fn TIM2_Init + * + * @brief Initializes TIM2. + * + * @return none + */ +void TIM2_Init(void) +{ + TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure = { 0 }; + + RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); + + TIM_TimeBaseStructure.TIM_Period = SystemCoreClock / 1000000; + TIM_TimeBaseStructure.TIM_Prescaler = WCHNETTIMERPERIOD * 1000 - 1; + TIM_TimeBaseStructure.TIM_ClockDivision = 0; + TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; + TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); + TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); + + TIM_Cmd(TIM2, ENABLE); + TIM_ClearITPendingBit(TIM2, TIM_IT_Update); + NVIC_EnableIRQ(TIM2_IRQn); +} + +/********************************************************************* + * @fn WCHNET_CreateTcpSocket + * + * @brief Create TCP Socket + * + * @return none + */ +void WCHNET_CreateTcpSocket(uint8_t* DESIP, uint16_t srcport, uint16_t desport, uint8_t* SocketId) +{ + uint8_t i; + SOCK_INF TmpSocketInf; + + memset((void*)&TmpSocketInf, 0, sizeof(SOCK_INF)); + memcpy((void*)TmpSocketInf.IPAddr, DESIP, 4); + TmpSocketInf.DesPort = desport; + TmpSocketInf.SourPort = srcport++; + TmpSocketInf.ProtoType = PROTO_TYPE_TCP; + TmpSocketInf.RecvBufLen = RECE_BUF_LEN; + i = WCHNET_SocketCreat(SocketId, &TmpSocketInf); + KPrintf("SocketId %d\r\n", *SocketId); + mStopIfError(i); + i = WCHNET_SocketConnect(*SocketId); // make a TCP connection + mStopIfError(i); +} + +/********************************************************************* + * @fn WCHNET_CreateTcpSocketListen + * + * @brief Create TCP Socket for Listening + * + * @return none + */ +void WCHNET_CreateTcpSocketListen(uint16_t srcport, uint8_t* SocketId) +{ + uint8_t i; + SOCK_INF TmpSocketInf; + + memset((void*)&TmpSocketInf, 0, sizeof(SOCK_INF)); + TmpSocketInf.SourPort = srcport; + TmpSocketInf.ProtoType = PROTO_TYPE_TCP; + i = WCHNET_SocketCreat(SocketId, &TmpSocketInf); + KPrintf("SocketIdForListen %d\r\n", *SocketId); + mStopIfError(i); + i = WCHNET_SocketListen(*SocketId); // listen for connections + mStopIfError(i); +} + +/********************************************************************* + * @fn WCHNET_DataLoopback + * + * @brief Data loopback function. + * + * @param id - socket id. + * + * @return none + */ +void WCHNET_DataLoopback(uint8_t id) +{ + u32 len, totallen; + uint8_t *p = MyBuf, TransCnt = 255; + + len = WCHNET_SocketRecvLen(id, NULL); // query length + KPrintf("Receive Len = %d\r\n", len); + totallen = len; + WCHNET_SocketRecv(id, MyBuf, &len); // Read the data of the receive buffer into MyBuf + while (1) { + len = totallen; + WCHNET_SocketSend(id, p, &len); // Send the data + totallen -= len; // Subtract the sent length from the total length + p += len; // offset buffer pointer + if (!--TransCnt) + break; // Timeout exit + if (totallen) + continue; // If the data is not sent, continue to send + break; // After sending, exit + } +} + +/********************************************************************* + * @fn WCHNET_HandleSockInt + * + * @brief Socket Interrupt Handle + * + * @param socketid - socket id. + * intstat - interrupt status + * + * @return none + */ +void WCHNET_HandleSockInt(uint8_t socketid, uint8_t intstat) +{ + uint8_t i; + + if (intstat & SINT_STAT_RECV) // receive data + { + WCHNET_DataLoopback(socketid); // Data loopback + } + if (intstat & SINT_STAT_CONNECT) // connect successfully + { +#if KEEPALIVE_ENABLE + WCHNET_SocketSetKeepLive(socketid, ENABLE); +#endif + WCHNET_ModifyRecvBuf(socketid, (u32)SocketRecvBuf[socketid], RECE_BUF_LEN); + for (i = 0; i < WCHNET_MAX_SOCKET_NUM; i++) { + if (socket[i] == 0xff) { // save connected socket id + socket[i] = socketid; + break; + } + } + KPrintf("TCP Connect Success\r\n"); + KPrintf("socket id: %d\r\n", socket[i]); + } + if (intstat & SINT_STAT_DISCONNECT) // disconnect + { + for (i = 0; i < WCHNET_MAX_SOCKET_NUM; i++) { // delete disconnected socket id + if (socket[i] == socketid) { + socket[i] = 0xff; + break; + } + } + KPrintf("TCP Disconnect\r\n"); + } + if (intstat & SINT_STAT_TIM_OUT) // timeout disconnect + { + for (i = 0; i < WCHNET_MAX_SOCKET_NUM; i++) { // delete disconnected socket id + if (socket[i] == socketid) { + socket[i] = 0xff; + break; + } + } + KPrintf("TCP Timeout\r\n"); + } +} + +/********************************************************************* + * @fn WCHNET_HandleGlobalInt + * + * @brief Global Interrupt Handle + * + * @return none + */ +void WCHNET_HandleGlobalInt(void) +{ + uint8_t intstat; + uint16_t i; + uint8_t socketint; + + intstat = WCHNET_GetGlobalInt(); // get global interrupt flag + if (intstat & GINT_STAT_UNREACH) // Unreachable interrupt + { + KPrintf("GINT_STAT_UNREACH\r\n"); + } + if (intstat & GINT_STAT_IP_CONFLI) // IP conflict + { + KPrintf("GINT_STAT_IP_CONFLI\r\n"); + } + if (intstat & GINT_STAT_PHY_CHANGE) // PHY status change + { + i = WCHNET_GetPHYStatus(); + if (i & PHY_Linked_Status) + KPrintf("PHY Link Success\r\n"); + } + if (intstat & GINT_STAT_SOCKET) { // socket related interrupt + for (i = 0; i < WCHNET_MAX_SOCKET_NUM; i++) { + socketint = WCHNET_GetSocketInt(i); + if (socketint) + WCHNET_HandleSockInt(i, socketint); + } + } +} + +uint8_t InitHwEth() +{ + uint8_t i = 0; + + KPrintf("net version:%x\n", WCHNET_GetVer()); + if (WCHNET_LIB_VER != WCHNET_GetVer()) { + KPrintf("version error.\n"); + } + + WCHNET_GetMacAddr(MACAddr); // get the chip MAC address + KPrintf("mac addr:"); + for (i = 0; i < 6; i++) + KPrintf("%x ", MACAddr[i]); + KPrintf("\n"); + + TIM2_Init(); + + i = ETH_LibInit(IPAddr, GWIPAddr, IPMask, MACAddr); // Ethernet library initialize + mStopIfError(i); + if (i == WCHNET_ERR_SUCCESS) + KPrintf("WCHNET_LibInit Success\r\n"); +#if KEEPALIVE_ENABLE // Configure keep alive parameters + { + struct _KEEP_CFG cfg; + + cfg.KLIdle = 20000; + cfg.KLIntvl = 15000; + cfg.KLCount = 9; + WCHNET_ConfigKeepLive(&cfg); + } +#endif + + memset(socket, 0xff, WCHNET_MAX_SOCKET_NUM); + + return i; +} diff --git a/Ubiquitous/XiZi_IIoT/board/ch32v307vct6/third_party_driver/ethernet/eth_driver.c b/Ubiquitous/XiZi_IIoT/board/ch32v307vct6/third_party_driver/ethernet/eth_driver.c new file mode 100644 index 000000000..2b719e6d6 --- /dev/null +++ b/Ubiquitous/XiZi_IIoT/board/ch32v307vct6/third_party_driver/ethernet/eth_driver.c @@ -0,0 +1,1257 @@ +// /********************************** (C) COPYRIGHT ******************************* +// * File Name : eth_driver.c +// * Author : WCH +// * Version : V1.3.0 +// * Date : 2022/06/02 +// * Description : eth program body. +// ********************************************************************************* +// * Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd. +// * Attention: This software (modified or not) and binary are used for +// * microcontroller manufactured by Nanjing Qinheng Microelectronics. +// *******************************************************************************/ + +#include "eth_driver.h" +#include "ch32v30x_eth.h" +#include "ch32v30x_gpio.h" +#include "ch32v30x_rcc.h" +#include "debug.h" +#include "net_config.h" +#include "stdint.h" +#include "wchnet.h" +#include "xs_base.h" +#include + +__attribute__((__aligned__(4))) ETH_DMADESCTypeDef DMARxDscrTab[ETH_RXBUFNB]; /* MAC receive descriptor, 4-byte aligned*/ +__attribute__((__aligned__(4))) ETH_DMADESCTypeDef DMATxDscrTab[ETH_TXBUFNB]; /* MAC send descriptor, 4-byte aligned */ + +__attribute__((__aligned__(4))) uint8_t MACRxBuf[ETH_RXBUFNB * ETH_RX_BUF_SZE]; /* MAC receive buffer, 4-byte aligned */ +__attribute__((__aligned__(4))) uint8_t MACTxBuf[ETH_TXBUFNB * ETH_TX_BUF_SZE]; /* MAC send buffer, 4-byte aligned */ + +__attribute__((__aligned__(4))) SOCK_INF SocketInf[WCHNET_MAX_SOCKET_NUM]; /* Socket information table, 4-byte alignment */ + +const uint16_t MemNum[8] = { WCHNET_NUM_IPRAW, + WCHNET_NUM_UDP, + WCHNET_NUM_TCP, + WCHNET_NUM_TCP_LISTEN, + WCHNET_NUM_TCP_SEG, + WCHNET_NUM_IP_REASSDATA, + WCHNET_NUM_PBUF, + WCHNET_NUM_POOL_BUF }; +const uint16_t MemSize[8] = { WCHNET_MEM_ALIGN_SIZE(WCHNET_SIZE_IPRAW_PCB), + WCHNET_MEM_ALIGN_SIZE(WCHNET_SIZE_UDP_PCB), + WCHNET_MEM_ALIGN_SIZE(WCHNET_SIZE_TCP_PCB), + WCHNET_MEM_ALIGN_SIZE(WCHNET_SIZE_TCP_PCB_LISTEN), + WCHNET_MEM_ALIGN_SIZE(WCHNET_SIZE_TCP_SEG), + WCHNET_MEM_ALIGN_SIZE(WCHNET_SIZE_IP_REASSDATA), + WCHNET_MEM_ALIGN_SIZE(WCHNET_SIZE_PBUF), + WCHNET_MEM_ALIGN_SIZE(WCHNET_SIZE_PBUF) + WCHNET_MEM_ALIGN_SIZE(WCHNET_SIZE_POOL_BUF) }; + +__attribute__((__aligned__(4))) uint8_t Memp_Memory[WCHNET_MEMP_SIZE]; +__attribute__((__aligned__(4))) uint8_t Mem_Heap_Memory[WCHNET_RAM_HEAP_SIZE]; +__attribute__((__aligned__(4))) uint8_t Mem_ArpTable[WCHNET_RAM_ARP_TABLE_SIZE]; + +uint16_t gPHYAddress; +uint32_t volatile LocalTime; + +ETH_DMADESCTypeDef* pDMARxSet; +ETH_DMADESCTypeDef* pDMATxSet; + +#if (PHY_MODE == USE_10M_BASE) +/* PHY negotiation function */ +uint8_t phyLinkStatus = 0; +uint8_t phyStatus = 0; +uint8_t phyLinkCnt = 0; +uint8_t phySucCnt = 0; +uint8_t phyPN = PHY_PN_SWITCH_AUTO; +uint8_t TRDetectStep = 0; +uint8_t TRDetectCnt = 0; +uint8_t LinkTaskPeriod = 50; +uint32_t RandVal = 0; +volatile uint8_t phyLinkReset; +volatile uint32_t phyLinkTime; +/* PHY receive processing */ +uint8_t RBUFlag = 0; +uint8_t RecStopFlag = 0; +uint8_t ReInitMACFlag = 0; +uint8_t DuplexMode = 0; +uint8_t PhyPolarityDetect = 0; +uint32_t LinkSuccTime = 0; +extern uint8_t MACAddr[6]; +void ReInitMACReg(void); +#define IDLE_DES_NUM 2 // The number of descriptors that need to be free +#endif + +#if ENABLE_POLLING_TO_QUERY_PHY_LINK_STAT +uint16_t LastPhyStat = 0; +uint32_t LastQueryPhyTime = 0; +#endif + +/********************************************************************* + * @fn WCHNET_GetMacAddr + * + * @brief Get the MAC address + * + * @return none. + */ +void WCHNET_GetMacAddr(uint8_t* macaddr) +{ + uint8_t i; + uint8_t* mac_addr = (uint8_t*)(ROM_CFG_USERADR_ID + 5); + KPrintf("WCHNET_GetMacAddr %x. \n", mac_addr); + + for (i = 0; i < 6; i++) { + *macaddr = *mac_addr; + macaddr++; + mac_addr--; + } +} + +/********************************************************************* + * @fn WCHNET_TimeIsr + * + * @brief + * + * @return none. + */ +void WCHNET_TimeIsr(uint16_t timperiod) +{ + LocalTime += timperiod; +} + +/********************************************************************* + * @fn WCHNET_QueryPhySta + * + * @brief Query external PHY status + * + * @return none. + */ +#if ENABLE_POLLING_TO_QUERY_PHY_LINK_STAT +void WCHNET_QueryPhySta(void) +{ + uint16_t phy_stat; + if (QUERY_STAT_FLAG) { /* Query the PHY link status every 1s */ + LastQueryPhyTime = LocalTime / 1000; +#if ((PHY_MODE == USE_MAC_MII) || (PHY_MODE == USE_MAC_RMII)) + phy_stat = ETH_ReadPHYRegister(PHY_ADDRESS, PHY_BSR); + if (phy_stat != LastPhyStat) { + ETH_PHYLink(); + } +#elif (PHY_MODE == USE_MAC_RGMII) + ETH_WritePHYRegister(PHY_ADDRESS, 0x1F, 0x0a43); + /*In some cases the status is not updated in time, + * so read this register twice to get the correct status value.*/ + ETH_ReadPHYRegister(PHY_ADDRESS, 0x1A); + phy_stat = ETH_ReadPHYRegister(PHY_ADDRESS, 0x1A) & 0x04; + if (phy_stat != LastPhyStat) { + ETH_PHYLink(); + } +#endif + } +} +#endif + +#if (PHY_MODE == USE_10M_BASE) +/********************************************************************* + * @fn WCHNET_PhyPNProcess + * + * @brief Phy PN Polarity related processing + * + * @param none. + * + * @return none. + */ +void WCHNET_PhyPNProcess(void) +{ + uint32_t PhyVal; + + LinkSuccTime = LocalTime; + if ((ETH->MMCRGUFCR == 0) && (ETH->MMCRFCECR >= 3)) { + PhyVal = ETH_ReadPHYRegister(gPHYAddress, PHY_MDIX); + if ((PhyVal >> 2) & 0x01) + PhyVal &= ~(3 << 2); // change PHY PN Polarity to normal + else + PhyVal |= 1 << 2; // change PHY PN Polarity to reverse + ETH_WritePHYRegister(gPHYAddress, PHY_MDIX, PhyVal); + ETH->MMCCR |= ETH_MMCCR_CR; // Counters Reset + while (ETH->MMCCR & ETH_MMCCR_CR) + ; // Wait for counters reset to complete + } + if (ETH->MMCRGUFCR != 0) { + PhyPolarityDetect = 0; + /* enable Filter function */ + ETH->MACFFR &= ~(ETH_ReceiveAll_Enable | ETH_PromiscuousMode_Enable); + } +} + +/********************************************************************* + * @fn WCHNET_RecProcess + * + * @brief Receiving related processing + * + * @param none. + * + * @return none. + */ +void WCHNET_RecProcess(void) +{ + if (RBUFlag) { + RBUFlag = 0; + RecStopFlag = 0; + if (PhyPolarityDetect) + WCHNET_PhyPNProcess(); + ReInitMACReg(); + } + + if (RecStopFlag) { + if ((pDMARxSet == (ETH_DMADESCTypeDef*)ETH->DMACHRDR) && (pDMARxSet->Status & ETH_DMARxDesc_OWN)) { + RecStopFlag = 0; + ETH_MACReceptionCmd(ENABLE); + } + } +} + +/********************************************************************* + * @fn WCHNET_LinkProcess + * + * @brief link process. + * + * @param none. + * + * @return none. + */ +void WCHNET_LinkProcess(void) +{ + uint16_t phy_anlpar, phy_bmsr, phy_mdix, RegVal; + phy_anlpar = ETH_ReadPHYRegister(gPHYAddress, PHY_ANLPAR); + phy_bmsr = ETH_ReadPHYRegister(gPHYAddress, PHY_BMSR); + + if ((phy_anlpar & PHY_ANLPAR_SELECTOR_FIELD)) { + if (TRDetectStep == 0) { + TRDetectStep = 1; + TRDetectCnt = 1; + PHY_TR_SWITCH(); + LinkTaskPeriod = RandVal % 100 + 50; + return; + } else if (TRDetectStep == 1) { + TRDetectStep = 2; + TRDetectCnt = 0; + } + if (!(phyLinkStatus & PHY_LINK_WAIT_SUC)) { + if (phyPN == PHY_PN_SWITCH_AUTO) { + PHY_PN_SWITCH(PHY_PN_SWITCH_P); + } else if (phyPN == PHY_PN_SWITCH_P) { + phyLinkStatus = PHY_LINK_WAIT_SUC; + } else { + phyLinkStatus = PHY_LINK_WAIT_SUC; + } + } else { + if ((phySucCnt++ == 5) && ((phy_bmsr & (1 << 5)) == 0)) { + phySucCnt = 0; + if (phyPN == PHY_PN_SWITCH_N) + PHY_PN_SWITCH(PHY_PN_SWITCH_P); + else + PHY_PN_SWITCH(PHY_PN_SWITCH_N); + } + } + phyLinkCnt = 0; + } else { + if (TRDetectStep == 1) { + TRDetectCnt++; + if (TRDetectCnt == 8) { + TRDetectCnt = 0; + TRDetectStep = 0; + ETH_WritePHYRegister(gPHYAddress, PHY_MDIX, PHY_PN_SWITCH_AUTO); + return; + } + PHY_TR_SWITCH(); + return; + } + if (phyLinkStatus == PHY_LINK_WAIT_SUC) { + if (phyLinkCnt++ == 15) { + phyLinkCnt = 0; + phySucCnt = 0; + TRDetectStep = 0; + phyLinkStatus = PHY_LINK_INIT; + PHY_PN_SWITCH(PHY_PN_SWITCH_AUTO); + } + } else { + if (phyPN == PHY_PN_SWITCH_P) { + if (phyLinkCnt++ == 4) { + phyLinkCnt = 0; + PHY_PN_SWITCH(PHY_PN_SWITCH_N); + } + } else if (phyPN == PHY_PN_SWITCH_N) { + if (phyLinkCnt++ == 15) { + phyLinkCnt = 0; + phySucCnt = 0; + TRDetectStep = 0; + phyLinkStatus = PHY_LINK_INIT; + PHY_PN_SWITCH(PHY_PN_SWITCH_AUTO); + } + } else { + if (phyLinkCnt++ == (5000 / PHY_LINK_TASK_PERIOD)) + PHY_LINK_RESET(); + } + } + } +} + +/********************************************************************* + * @fn WCHNET_HandlePhyNegotiation + * + * @brief Handle PHY Negotiation. + * + * @param none. + * + * @return none. + */ +void WCHNET_HandlePhyNegotiation(void) +{ + if (phyLinkReset) /* After the PHY link is disconnected, wait 500ms before turning on the PHY clock*/ + { + if (LocalTime - phyLinkTime >= 500) { + phyLinkReset = 0; + EXTEN->EXTEN_CTR |= EXTEN_ETH_10M_EN; + PHY_LINK_RESET(); + } + } else { + if (!phyStatus) /* Handling PHY Negotiation Exceptions */ + { + ACCELERATE_LINK_PROCESS(); + if (LocalTime - phyLinkTime >= LinkTaskPeriod) { + UPDATE_LINKTASKPERIOD(); + phyLinkTime = LocalTime; + WCHNET_LinkProcess(); + } + if (ReInitMACFlag) + ReInitMACFlag = 0; + } else { /* PHY link complete */ + if (ReInitMACFlag) { + if (LocalTime - phyLinkTime >= 5 * PHY_LINK_TASK_PERIOD) { + uint32_t phy_stat; + ReInitMACFlag = 0; + phy_stat = ETH_ReadPHYRegister(gPHYAddress, PHY_BMSR); + if ((phy_stat & PHY_Linked_Status) == 0) { + WCHNET_PhyStatus(phy_stat); + PHY_LINK_RESET(); + } + } + } + if (PhyPolarityDetect) { + if (LocalTime - LinkSuccTime >= 2 * PHY_LINK_TASK_PERIOD) { + WCHNET_PhyPNProcess(); + } + } + } + } +} +#endif + +/********************************************************************* + * @fn WCHNET_MainTask + * + * @brief library main task function + * + * @param none. + * + * @return none. + */ +void WCHNET_MainTask(void) +{ + WCHNET_NetInput(); /* Ethernet data input */ + WCHNET_PeriodicHandle(); /* Protocol stack time-related task processing */ + +#if (PHY_MODE == USE_10M_BASE) + WCHNET_HandlePhyNegotiation(); + WCHNET_RecProcess(); +#endif + +#if ENABLE_POLLING_TO_QUERY_PHY_LINK_STAT + WCHNET_QueryPhySta(); /* Query external PHY status */ +#endif +} + +#if (PHY_MODE == USE_10M_BASE) +/********************************************************************* + * @fn ETH_LedLinkSet + * + * @brief set eth link led,setbit 0 or 1,the link led turn on or turn off + * + * @return none + */ +void ETH_LedLinkSet(uint8_t mode) +{ + if (mode == LED_OFF) { + GPIO_SetBits(GPIOC, GPIO_Pin_0); + } else { + GPIO_ResetBits(GPIOC, GPIO_Pin_0); + } +} + +/********************************************************************* + * @fn ETH_LedDataSet + * + * @brief set eth data led,setbit 0 or 1,the data led turn on or turn off + * + * @return none + */ +void ETH_LedDataSet(uint8_t mode) +{ + if (mode == LED_OFF) { + GPIO_SetBits(GPIOC, GPIO_Pin_1); + } else { + GPIO_ResetBits(GPIOC, GPIO_Pin_1); + } +} + +/********************************************************************* + * @fn ETH_LedConfiguration + * + * @brief set eth data and link led pin + * + * @param none. + * + * @return none. + */ +void ETH_LedConfiguration(void) +{ + GPIO_InitTypeDef GPIO = { 0 }; + + RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE); + GPIO.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1; + GPIO.GPIO_Mode = GPIO_Mode_Out_PP; + GPIO.GPIO_Speed = GPIO_Speed_50MHz; + GPIO_Init(GPIOC, &GPIO); + ETH_LedDataSet(LED_OFF); + ETH_LedLinkSet(LED_OFF); +} + +/********************************************************************* + * @fn ETH_SetClock + * + * @brief Set ETH Clock(60MHZ). + * + * @param none. + * + * @return none. + */ +void ETH_SetClock(void) +{ + RCC_PLL3Cmd(DISABLE); + RCC_PREDIV2Config(RCC_PREDIV2_Div2); /* HSE = 8M */ + RCC_PLL3Config(RCC_PLL3Mul_15); /* 4M*15 = 60MHz */ + RCC_PLL3Cmd(ENABLE); + while (RESET == RCC_GetFlagStatus(RCC_FLAG_PLL3RDY)) + ; +} + +#elif (PHY_MODE == USE_MAC_MII) +/********************************************************************* + * @fn ETH_MIIPinInit + * + * @brief PHY MII interface GPIO initialization. + * + * @param none. + * + * @return none. + */ +void ETH_MIIPinInit(void) +{ + GPIO_InitTypeDef GPIO_InitStructure; + + RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOC, ENABLE); + + GPIO_Output(GPIOA, GPIO_Pin_2); /* MDIO */ + GPIO_Output(GPIOC, GPIO_Pin_1); /* MDC */ + + GPIO_Input(GPIOC, GPIO_Pin_3); /* TXCLK */ + GPIO_Output(GPIOB, GPIO_Pin_11); /* TXEN */ + GPIO_Output(GPIOB, GPIO_Pin_12); /* TXD0 */ + GPIO_Output(GPIOB, GPIO_Pin_13); /* TXD1 */ + GPIO_Output(GPIOC, GPIO_Pin_2); /* TXD2 */ + GPIO_Output(GPIOB, GPIO_Pin_8); /* TXD3 */ + GPIO_Input(GPIOA, GPIO_Pin_1); /* RXC */ + GPIO_Input(GPIOA, GPIO_Pin_7); /* RXDV */ + GPIO_Input(GPIOC, GPIO_Pin_4); /* RXD0 */ + GPIO_Input(GPIOC, GPIO_Pin_5); /* RXD1 */ + GPIO_Input(GPIOB, GPIO_Pin_0); /* RXD2 */ + GPIO_Input(GPIOB, GPIO_Pin_1); /* RXD3 */ + GPIO_Input(GPIOB, GPIO_Pin_10); /* RXER */ + + GPIO_Output(GPIOA, GPIO_Pin_0); /* CRS */ + GPIO_Output(GPIOA, GPIO_Pin_3); /* COL */ +} + +#elif (PHY_MODE == USE_MAC_RMII) +/********************************************************************* + * @fn ETH_RMIIPinInit + * + * @brief PHY RMII interface GPIO initialization. + * + * @param none. + * + * @return none. + */ +void ETH_RMIIPinInit(void) +{ + GPIO_InitTypeDef GPIO_InitStructure; + + RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOC | RCC_APB2Periph_AFIO, ENABLE); + GPIO_ETH_MediaInterfaceConfig(GPIO_ETH_MediaInterface_RMII); + GPIO_Output(GPIOA, GPIO_Pin_2); /* MDIO */ + GPIO_Output(GPIOC, GPIO_Pin_1); /* MDC */ + + GPIO_Output(GPIOB, GPIO_Pin_11); /* TXEN */ + GPIO_Output(GPIOB, GPIO_Pin_12); /* TXD0 */ + GPIO_Output(GPIOB, GPIO_Pin_13); /* TXD1 */ + + GPIO_Input(GPIOA, GPIO_Pin_1); /* REFCLK */ + GPIO_Input(GPIOA, GPIO_Pin_7); /* CRSDV */ + GPIO_Input(GPIOC, GPIO_Pin_4); /* RXD0 */ + GPIO_Input(GPIOC, GPIO_Pin_5); /* RXD1 */ +} + +#elif (PHY_MODE == USE_MAC_RGMII) +/********************************************************************* + * @fn ETH_RGMIIPinInit + * + * @brief PHY RGMII interface GPIO initialization. + * + * @param none. + * + * @return none. + */ +void ETH_RGMIIPinInit(void) +{ + GPIO_InitTypeDef GPIO_InitStructure; + + RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); + RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOC, ENABLE); + GPIOB->CFGHR &= ~(0xff << 16); + GPIOB->CFGHR |= (0xbb << 16); + GPIOB->CFGLR &= ~(0xff << 4); + + GPIO_Output(GPIOA, GPIO_Pin_2); + GPIO_Output(GPIOA, GPIO_Pin_3); + GPIO_Output(GPIOA, GPIO_Pin_7); + GPIO_Output(GPIOC, GPIO_Pin_4); + GPIO_Output(GPIOC, GPIO_Pin_5); + GPIO_Output(GPIOB, GPIO_Pin_0); + + GPIO_Input(GPIOC, GPIO_Pin_0); + GPIO_Input(GPIOC, GPIO_Pin_1); + GPIO_Input(GPIOC, GPIO_Pin_2); + GPIO_Input(GPIOC, GPIO_Pin_3); + GPIO_Input(GPIOA, GPIO_Pin_0); + GPIO_Input(GPIOA, GPIO_Pin_1); + + GPIO_Input(GPIOB, GPIO_Pin_1); /* 125m in */ + + GPIO_Input(GPIOC, GPIO_Pin_7); /* interrupt pin */ +} +#endif + +#if (((PHY_MODE == USE_MAC_RMII) || (PHY_MODE == USE_MAC_RGMII)) && !ENABLE_POLLING_TO_QUERY_PHY_LINK_STAT) +/********************************************************************* + * @fn EXTI_Line_Init + * + * @brief Configure EXTI Line7. + * + * @param none. + * + * @return none. + */ +void EXTI_Line_Init(void) +{ + GPIO_InitTypeDef GPIO_InitStructure = { 0 }; + EXTI_InitTypeDef EXTI_InitStructure = { 0 }; + NVIC_InitTypeDef NVIC_InitStructure = { 0 }; + + RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOC, ENABLE); + + GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; + GPIO_Init(GPIOC, &GPIO_InitStructure); + + /* GPIOC 7 ----> EXTI_Line7 */ + GPIO_EXTILineConfig(GPIO_PortSourceGPIOC, GPIO_PinSource7); + EXTI_InitStructure.EXTI_Line = EXTI_Line7; + EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; + EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; + EXTI_InitStructure.EXTI_LineCmd = ENABLE; + EXTI_Init(&EXTI_InitStructure); + + NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQn; + NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; + NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; + NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; + NVIC_Init(&NVIC_InitStructure); +} + +/********************************************************************* + * @fn PHY_InterruptInit + * + * @brief Configure PHY interrupt function,Supported chip is:RTL8211FS + * + * @param none. + * + * @return none. + */ +void PHY_InterruptInit(void) +{ + uint16_t RegValue; +#if (PHY_MODE == USE_MAC_RGMII) + ETH_WritePHYRegister(gPHYAddress, 0x1F, 0x0a42); + RegValue = ETH_ReadPHYRegister(gPHYAddress, 0x12); + /* enable Link Status Change Interrupt and + * Auto-Negotiation Completed Interrupt*/ + RegValue |= (1 << 4) | (1 << 3); + ETH_WritePHYRegister(gPHYAddress, 0x12, RegValue); + /* Clear the Interrupt status */ + ETH_WritePHYRegister(gPHYAddress, 0x1F, 0x0a43); + ETH_ReadPHYRegister(gPHYAddress, 0x1D); +#elif (PHY_MODE == USE_MAC_RMII) + ETH_WritePHYRegister(gPHYAddress, 0x1F, 0x07); + /* Configure RMII mode */ + RegValue = ETH_ReadPHYRegister(gPHYAddress, 0x10); + RegValue |= 0x01 << 3; + ETH_WritePHYRegister(gPHYAddress, 0x10, RegValue); + /* Configure interrupt function */ + RegValue = ETH_ReadPHYRegister(gPHYAddress, 0x13); + RegValue |= 0x0f << 11; + ETH_WritePHYRegister(gPHYAddress, 0x13, RegValue); + /* Clear the Interrupt status */ + ETH_WritePHYRegister(gPHYAddress, 0x1F, 0x00); + ETH_ReadPHYRegister(gPHYAddress, 0x1E); +#endif +} +#endif + +/********************************************************************* + * @fn ETH_PHYLink + * + * @brief Configure MAC parameters after the PHY Link is successful. + * + * @param none. + * + * @return none. + */ +void ETH_PHYLink(void) +{ + uint32_t phy_stat; + +#if (PHY_MODE == USE_10M_BASE) + uint16_t phy_anlpar; + phy_anlpar = ETH_ReadPHYRegister(gPHYAddress, PHY_ANLPAR); + phy_stat = ETH_ReadPHYRegister(gPHYAddress, PHY_BSR); + + if ((phy_stat & (PHY_Linked_Status)) && (phy_anlpar == 0)) { /* restart negotiation */ + EXTEN->EXTEN_CTR &= ~EXTEN_ETH_10M_EN; + phyLinkReset = 1; + phyLinkTime = LocalTime; + return; + } + WCHNET_PhyStatus(phy_stat); + + if ((phy_stat & PHY_Linked_Status) && (phy_stat & PHY_AutoNego_Complete)) { + phy_stat = ETH_ReadPHYRegister(gPHYAddress, PHY_STATUS); + if (phy_stat & (1 << 2)) { + ETH->MACCR |= ETH_Mode_FullDuplex; + } else { + if ((phy_anlpar & PHY_ANLPAR_SELECTOR_FIELD) != PHY_ANLPAR_SELECTOR_VALUE) { + ETH->MACCR |= ETH_Mode_FullDuplex; + } else { + ETH->MACCR &= ~ETH_Mode_FullDuplex; + } + } + ETH->MACCR &= ~(ETH_Speed_100M | ETH_Speed_1000M); + phyStatus = PHY_Linked_Status; + + /* disable Filter function */ + ETH->MACFFR |= (ETH_ReceiveAll_Enable | ETH_PromiscuousMode_Enable); + + ETH->MMCCR |= ETH_MMCCR_CR; // Counters Reset + while (ETH->MMCCR & ETH_MMCCR_CR) + ; // Wait for counters reset to complete + PhyPolarityDetect = 1; + LinkSuccTime = LocalTime; + ETH_Start(); + } else { + EXTEN->EXTEN_CTR &= ~EXTEN_ETH_10M_EN; + phyLinkReset = 1; + phyLinkTime = LocalTime; + } + DuplexMode = (ETH->MACCR >> 11) & 0x01; /* Record duplex mode*/ +#elif (PHY_MODE == USE_MAC_MII) + phy_stat = ETH_ReadPHYRegister(PHY_ADDRESS, PHY_BSR); + LastPhyStat = phy_stat; + WCHNET_PhyStatus(phy_stat); + if ((phy_stat & PHY_Linked_Status) && (phy_stat & PHY_AutoNego_Complete)) { + phy_stat = ETH_ReadPHYRegister(PHY_ADDRESS, PHY_BCR); + /* PHY negotiation result */ + if (phy_stat & (1 << 13)) /* 100M */ + { + ETH->MACCR &= ~(ETH_Speed_100M | ETH_Speed_1000M); + ETH->MACCR |= ETH_Speed_100M; + } else /* 10M */ + { + ETH->MACCR &= ~(ETH_Speed_100M | ETH_Speed_1000M); + } + if (phy_stat & (1 << 8)) /* full duplex */ + { + ETH->MACCR |= ETH_Mode_FullDuplex; + } else /* half duplex */ + { + ETH->MACCR &= ~ETH_Mode_FullDuplex; + } + ETH_Start(); + } +#elif (PHY_MODE == USE_MAC_RMII) + ETH_WritePHYRegister(gPHYAddress, 0x1F, 0x00); + phy_stat = ETH_ReadPHYRegister(PHY_ADDRESS, PHY_BSR); +#if ENABLE_POLLING_TO_QUERY_PHY_LINK_STAT + LastPhyStat = phy_stat; +#endif + WCHNET_PhyStatus(phy_stat); + if ((phy_stat & PHY_Linked_Status) && (phy_stat & PHY_AutoNego_Complete)) { + phy_stat = ETH_ReadPHYRegister(PHY_ADDRESS, PHY_BCR); + /* PHY negotiation result */ + if (phy_stat & (1 << 13)) /* 100M */ + { + ETH->MACCR &= ~(ETH_Speed_100M | ETH_Speed_1000M); + ETH->MACCR |= ETH_Speed_100M; + } else /* 10M */ + { + ETH->MACCR &= ~(ETH_Speed_100M | ETH_Speed_1000M); + } + if (phy_stat & (1 << 8)) /* full duplex */ + { + ETH->MACCR |= ETH_Mode_FullDuplex; + } else /* half duplex */ + { + ETH->MACCR &= ~ETH_Mode_FullDuplex; + } + ETH_Start(); + } + phy_stat = ETH_ReadPHYRegister(gPHYAddress, 0x1E); /* Clear the Interrupt status */ +#elif (PHY_MODE == USE_MAC_RGMII) + ETH_WritePHYRegister(gPHYAddress, 0x1F, 0x0a43); + /*In some cases the status is not updated in time, + * so read this register twice to get the correct status value.*/ + ETH_ReadPHYRegister(gPHYAddress, 0x1A); + phy_stat = ETH_ReadPHYRegister(gPHYAddress, 0x1A); +#if ENABLE_POLLING_TO_QUERY_PHY_LINK_STAT + LastPhyStat = phy_stat & 0x04; +#endif + WCHNET_PhyStatus(phy_stat); + + if (phy_stat & 0x04) { + if (phy_stat & 0x08) { + ETH->MACCR |= ETH_Mode_FullDuplex; + } else { + ETH->MACCR &= ~ETH_Mode_FullDuplex; + } + if ((phy_stat & 0x30) == 0x00) { + ETH->MACCR &= ~(ETH_Speed_100M | ETH_Speed_1000M); + } else if ((phy_stat & 0x30) == 0x10) { + ETH->MACCR &= ~(ETH_Speed_100M | ETH_Speed_1000M); + ETH->MACCR |= ETH_Speed_100M; + } else if ((phy_stat & 0x30) == 0x20) { + ETH->MACCR &= ~(ETH_Speed_100M | ETH_Speed_1000M); + ETH->MACCR |= ETH_Speed_1000M; + } + ETH_Start(); + } + phy_stat = ETH_ReadPHYRegister(gPHYAddress, 0x1D); /* Clear the Interrupt status */ +#endif +} + +#if (PHY_MODE == USE_10M_BASE) +/********************************************************************* + * @fn ReInitMACReg + * + * @brief Reinitialize MAC register. + * + * @param none. + * + * @return none. + */ +void ReInitMACReg(void) +{ + ETH_InitTypeDef ETH_InitStructure; + uint16_t timeout = 10000; + uint16_t RegVal; + uint32_t tmpreg = 0; + + /* Wait for sending data to complete */ + while ((ETH->DMASR & (7 << 20)) != ETH_DMA_TransmitProcess_Suspended) + ; + + PHY_TR_REVERSE(); + + /* Software reset */ + ETH_SoftwareReset(); + /* Wait for software reset */ + do { + // Delay_Us(10); + if (!--timeout) + break; + } while (ETH->DMABMR & ETH_DMABMR_SR); + + /* ETHERNET Configuration */ + /* Call ETH_StructInit if you don't like to configure all ETH_InitStructure parameter */ + ETH_StructInit(Ð_InitStructure); + /* Fill ETH_InitStructure parameters */ + /*------------------------ MAC -----------------------------------*/ + ETH_InitStructure.ETH_Mode = ETH_Mode_FullDuplex; + ETH_InitStructure.ETH_Speed = ETH_Speed_10M; +#if HARDWARE_CHECKSUM_CONFIG + ETH_InitStructure.ETH_ChecksumOffload = ETH_ChecksumOffload_Enable; +#endif + ETH_InitStructure.ETH_AutoNegotiation = ETH_AutoNegotiation_Enable; + ETH_InitStructure.ETH_LoopbackMode = ETH_LoopbackMode_Disable; + ETH_InitStructure.ETH_RetryTransmission = ETH_RetryTransmission_Disable; + ETH_InitStructure.ETH_AutomaticPadCRCStrip = ETH_AutomaticPadCRCStrip_Disable; + /* Filter function configuration */ + ETH_InitStructure.ETH_ReceiveAll = ETH_ReceiveAll_Disable; + ETH_InitStructure.ETH_BroadcastFramesReception = ETH_BroadcastFramesReception_Enable; + ETH_InitStructure.ETH_PromiscuousMode = ETH_PromiscuousMode_Disable; + ETH_InitStructure.ETH_MulticastFramesFilter = ETH_MulticastFramesFilter_Perfect; + ETH_InitStructure.ETH_UnicastFramesFilter = ETH_UnicastFramesFilter_Perfect; + /*------------------------ DMA -----------------------------------*/ + /* When we use the Checksum offload feature, we need to enable the Store and Forward mode: + the store and forward guarantee that a whole frame is stored in the FIFO, so the MAC can insert/verify the checksum, + if the checksum is OK the DMA can handle the frame otherwise the frame is dropped */ + ETH_InitStructure.ETH_DropTCPIPChecksumErrorFrame = ETH_DropTCPIPChecksumErrorFrame_Enable; + ETH_InitStructure.ETH_ReceiveStoreForward = ETH_ReceiveStoreForward_Enable; + ETH_InitStructure.ETH_TransmitStoreForward = ETH_TransmitStoreForward_Enable; + ETH_InitStructure.ETH_ForwardErrorFrames = ETH_ForwardErrorFrames_Enable; + ETH_InitStructure.ETH_ForwardUndersizedGoodFrames = ETH_ForwardUndersizedGoodFrames_Enable; + ETH_InitStructure.ETH_SecondFrameOperate = ETH_SecondFrameOperate_Disable; + /* Configure Ethernet */ + /*---------------------- Physical layer configuration -------------------*/ + /* Set the SMI interface clock, set as the main frequency divided by 42 */ + tmpreg = ETH->MACMIIAR; + tmpreg &= MACMIIAR_CR_MASK; + tmpreg |= (uint32_t)ETH_MACMIIAR_CR_Div42; + ETH->MACMIIAR = (uint32_t)tmpreg; + + /*------------------------ MAC register configuration ----------------------- --------------------*/ + tmpreg = ETH->MACCR; + tmpreg &= MACCR_CLEAR_MASK; + tmpreg |= (uint32_t)(ETH_InitStructure.ETH_AutoNegotiation | ETH_InitStructure.ETH_Watchdog | ETH_InitStructure.ETH_Jabber | ETH_InitStructure.ETH_InterFrameGap | ETH_InitStructure.ETH_CarrierSense | ETH_InitStructure.ETH_Speed | ETH_InitStructure.ETH_ReceiveOwn | ETH_InitStructure.ETH_LoopbackMode | ETH_InitStructure.ETH_Mode | ETH_InitStructure.ETH_ChecksumOffload | ETH_InitStructure.ETH_RetryTransmission | ETH_InitStructure.ETH_AutomaticPadCRCStrip | ETH_InitStructure.ETH_BackOffLimit | ETH_InitStructure.ETH_DeferralCheck); + /* Write MAC Control Register */ + ETH->MACCR = (uint32_t)tmpreg; +#if (PHY_MODE == USE_10M_BASE) + ETH->MACCR |= ETH_Internal_Pull_Up_Res_Enable; /*Turn on the internal pull-up resistor*/ +#endif + ETH->MACFFR = (uint32_t)(ETH_InitStructure.ETH_ReceiveAll | ETH_InitStructure.ETH_SourceAddrFilter | ETH_InitStructure.ETH_PassControlFrames | ETH_InitStructure.ETH_BroadcastFramesReception | ETH_InitStructure.ETH_DestinationAddrFilter | ETH_InitStructure.ETH_PromiscuousMode | ETH_InitStructure.ETH_MulticastFramesFilter | ETH_InitStructure.ETH_UnicastFramesFilter); + /*--------------- ETHERNET MACHTHR and MACHTLR Configuration ---------------*/ + /* Write to ETHERNET MACHTHR */ + ETH->MACHTHR = (uint32_t)ETH_InitStructure.ETH_HashTableHigh; + /* Write to ETHERNET MACHTLR */ + ETH->MACHTLR = (uint32_t)ETH_InitStructure.ETH_HashTableLow; + /*----------------------- ETHERNET MACFCR Configuration --------------------*/ + /* Get the ETHERNET MACFCR value */ + tmpreg = ETH->MACFCR; + /* Clear xx bits */ + tmpreg &= MACFCR_CLEAR_MASK; + tmpreg |= (uint32_t)((ETH_InitStructure.ETH_PauseTime << 16) | ETH_InitStructure.ETH_ZeroQuantaPause | ETH_InitStructure.ETH_PauseLowThreshold | ETH_InitStructure.ETH_UnicastPauseFrameDetect | ETH_InitStructure.ETH_ReceiveFlowControl | ETH_InitStructure.ETH_TransmitFlowControl); + ETH->MACFCR = (uint32_t)tmpreg; + + ETH->MACVLANTR = (uint32_t)(ETH_InitStructure.ETH_VLANTagComparison | ETH_InitStructure.ETH_VLANTagIdentifier); + + tmpreg = ETH->DMAOMR; + tmpreg &= DMAOMR_CLEAR_MASK; + tmpreg |= (uint32_t)(ETH_InitStructure.ETH_DropTCPIPChecksumErrorFrame | ETH_InitStructure.ETH_ReceiveStoreForward | ETH_InitStructure.ETH_FlushReceivedFrame | ETH_InitStructure.ETH_TransmitStoreForward | ETH_InitStructure.ETH_TransmitThresholdControl | ETH_InitStructure.ETH_ForwardErrorFrames | ETH_InitStructure.ETH_ForwardUndersizedGoodFrames | ETH_InitStructure.ETH_ReceiveThresholdControl | ETH_InitStructure.ETH_SecondFrameOperate); + ETH->DMAOMR = (uint32_t)tmpreg; + + /* Configure MAC address */ + ETH->MACA0HR = (uint32_t)((MACAddr[5] << 8) | MACAddr[4]); + ETH->MACA0LR = (uint32_t)(MACAddr[0] | (MACAddr[1] << 8) | (MACAddr[2] << 16) | (MACAddr[3] << 24)); + + /* Mask the interrupt that Tx good frame count counter reaches half the maximum value */ + ETH->MMCTIMR = ETH_MMCTIMR_TGFM; + /* Mask the interrupt that Rx good unicast frames counter reaches half the maximum value */ + /* Mask the interrupt that Rx crc error counter reaches half the maximum value */ + ETH->MMCRIMR = ETH_MMCRIMR_RGUFM | ETH_MMCRIMR_RFCEM; + + ETH_DMAITConfig(ETH_DMA_IT_NIS | ETH_DMA_IT_R | ETH_DMA_IT_T | ETH_DMA_IT_AIS | ETH_DMA_IT_RBU | ETH_DMA_IT_PHYLINK, + ENABLE); + + ETH_DMATxDescChainInit(DMATxDscrTab, MACTxBuf, ETH_TXBUFNB); + ETH_DMARxDescChainInit(DMARxDscrTab, MACRxBuf, ETH_RXBUFNB); + pDMARxSet = DMARxDscrTab; + pDMATxSet = DMATxDscrTab; + + ETH->MACCR &= ~ETH_Mode_FullDuplex; // configure working mode based on the link result + if (DuplexMode) { + ETH->MACCR |= ETH_Mode_FullDuplex; + } + + ETH->MACCR &= ~(ETH_Speed_100M | ETH_Speed_1000M); + + ETH_Start(); + + PHY_TR_REVERSE(); + + if (!phyStatus) { + PHY_LINK_RESET(); + } + + ReInitMACFlag = 1; + phyLinkTime = LocalTime; +} +#endif + +/********************************************************************* + * @fn ETH_RegInit + * + * @brief ETH register initialization. + * + * @param ETH_InitStruct:initialization struct. + * PHYAddress:PHY address. + * + * @return Initialization status. + */ +uint32_t ETH_RegInit(ETH_InitTypeDef* ETH_InitStruct, uint16_t PHYAddress) +{ + uint32_t tmpreg = 0; + + /*---------------------- Physical layer configuration -------------------*/ + /* Set the SMI interface clock, set as the main frequency divided by 42 */ + tmpreg = ETH->MACMIIAR; + tmpreg &= MACMIIAR_CR_MASK; + tmpreg |= (uint32_t)ETH_MACMIIAR_CR_Div42; + ETH->MACMIIAR = (uint32_t)tmpreg; + + /*------------------------ MAC register configuration ----------------------- --------------------*/ + tmpreg = ETH->MACCR; + tmpreg &= MACCR_CLEAR_MASK; + tmpreg |= (uint32_t)(ETH_InitStruct->ETH_AutoNegotiation | ETH_InitStruct->ETH_Watchdog | ETH_InitStruct->ETH_Jabber | ETH_InitStruct->ETH_InterFrameGap | ETH_InitStruct->ETH_CarrierSense | ETH_InitStruct->ETH_Speed | ETH_InitStruct->ETH_ReceiveOwn | ETH_InitStruct->ETH_LoopbackMode | ETH_InitStruct->ETH_Mode | ETH_InitStruct->ETH_ChecksumOffload | ETH_InitStruct->ETH_RetryTransmission | ETH_InitStruct->ETH_AutomaticPadCRCStrip | ETH_InitStruct->ETH_BackOffLimit | ETH_InitStruct->ETH_DeferralCheck); + /* Write MAC Control Register */ + ETH->MACCR = (uint32_t)tmpreg; +#if (PHY_MODE == USE_10M_BASE) + ETH->MACCR |= ETH_Internal_Pull_Up_Res_Enable; /*Turn on the internal pull-up resistor*/ +#endif + ETH->MACFFR = (uint32_t)(ETH_InitStruct->ETH_ReceiveAll | ETH_InitStruct->ETH_SourceAddrFilter | ETH_InitStruct->ETH_PassControlFrames | ETH_InitStruct->ETH_BroadcastFramesReception | ETH_InitStruct->ETH_DestinationAddrFilter | ETH_InitStruct->ETH_PromiscuousMode | ETH_InitStruct->ETH_MulticastFramesFilter | ETH_InitStruct->ETH_UnicastFramesFilter); + /*--------------- ETHERNET MACHTHR and MACHTLR Configuration ---------------*/ + /* Write to ETHERNET MACHTHR */ + ETH->MACHTHR = (uint32_t)ETH_InitStruct->ETH_HashTableHigh; + /* Write to ETHERNET MACHTLR */ + ETH->MACHTLR = (uint32_t)ETH_InitStruct->ETH_HashTableLow; + /*----------------------- ETHERNET MACFCR Configuration --------------------*/ + /* Get the ETHERNET MACFCR value */ + tmpreg = ETH->MACFCR; + /* Clear xx bits */ + tmpreg &= MACFCR_CLEAR_MASK; + tmpreg |= (uint32_t)((ETH_InitStruct->ETH_PauseTime << 16) | ETH_InitStruct->ETH_ZeroQuantaPause | ETH_InitStruct->ETH_PauseLowThreshold | ETH_InitStruct->ETH_UnicastPauseFrameDetect | ETH_InitStruct->ETH_ReceiveFlowControl | ETH_InitStruct->ETH_TransmitFlowControl); + ETH->MACFCR = (uint32_t)tmpreg; + + ETH->MACVLANTR = (uint32_t)(ETH_InitStruct->ETH_VLANTagComparison | ETH_InitStruct->ETH_VLANTagIdentifier); + + tmpreg = ETH->DMAOMR; + tmpreg &= DMAOMR_CLEAR_MASK; + tmpreg |= (uint32_t)(ETH_InitStruct->ETH_DropTCPIPChecksumErrorFrame | ETH_InitStruct->ETH_ReceiveStoreForward | ETH_InitStruct->ETH_FlushReceivedFrame | ETH_InitStruct->ETH_TransmitStoreForward | ETH_InitStruct->ETH_TransmitThresholdControl | ETH_InitStruct->ETH_ForwardErrorFrames | ETH_InitStruct->ETH_ForwardUndersizedGoodFrames | ETH_InitStruct->ETH_ReceiveThresholdControl | ETH_InitStruct->ETH_SecondFrameOperate); + ETH->DMAOMR = (uint32_t)tmpreg; + + /* Reset the physical layer */ + ETH_WritePHYRegister(PHYAddress, PHY_BCR, PHY_Reset); +#if (PHY_MODE == USE_10M_BASE) + ETH_WritePHYRegister(PHYAddress, PHY_MDIX, PHY_PN_SWITCH_AUTO); +#endif + return ETH_SUCCESS; +} + +/********************************************************************* + * @fn ETH_Configuration + * + * @brief Ethernet configure. + * + * @return none + */ +void ETH_Configuration(uint8_t* macAddr) +{ + ETH_InitTypeDef ETH_InitStructure; + uint16_t timeout = 10000; + + /* Enable Ethernet MAC clock */ + RCC_AHBPeriphClockCmd(RCC_AHBPeriph_ETH_MAC | RCC_AHBPeriph_ETH_MAC_Tx | RCC_AHBPeriph_ETH_MAC_Rx, ENABLE); + + gPHYAddress = PHY_ADDRESS; +#if (PHY_MODE == USE_10M_BASE) + ETH_SetClock(); + /* Enable internal 10BASE-T PHY*/ + EXTEN->EXTEN_CTR |= EXTEN_ETH_10M_EN; /* Enable 10M Ethernet physical layer */ +#elif (PHY_MODE == USE_MAC_MII) + /* Enable MII GPIO */ + ETH_MIIPinInit(); +#elif (PHY_MODE == USE_MAC_RMII) + /* Enable RMII GPIO */ + ETH_RMIIPinInit(); +#elif (PHY_MODE == USE_MAC_RGMII) + /* Enable 1G MAC*/ + EXTEN->EXTEN_CTR |= EXTEN_ETH_RGMII_SEL; + RCC_ETH1GCLKConfig(RCC_ETH1GCLKSource_PB1_IN); + RCC_ETH1G_125Mcmd(ENABLE); + /* Enable RGMII GPIO */ + ETH_RGMIIPinInit(); +#endif + /* Reset ETHERNET on AHB Bus */ + ETH_DeInit(); + + /* Software reset */ + ETH_SoftwareReset(); + + /* Wait for software reset */ + do { + // Delay_Us(10); + if (!--timeout) + break; + } while (ETH->DMABMR & ETH_DMABMR_SR); + + /* ETHERNET Configuration */ + /* Call ETH_StructInit if you don't like to configure all ETH_InitStructure parameter */ + ETH_StructInit(Ð_InitStructure); + /* Fill ETH_InitStructure parameters */ + /*------------------------ MAC -----------------------------------*/ + ETH_InitStructure.ETH_Mode = ETH_Mode_FullDuplex; +#if (PHY_MODE == USE_10M_BASE) + ETH_InitStructure.ETH_Speed = ETH_Speed_10M; +#elif ((PHY_MODE == USE_MAC_MII) || (PHY_MODE == USE_MAC_RMII)) + ETH_InitStructure.ETH_Speed = ETH_Speed_100M; +#elif (PHY_MODE == USE_MAC_RGMII) + ETH_InitStructure.ETH_Speed = ETH_Speed_1000M; +#endif +#if HARDWARE_CHECKSUM_CONFIG + ETH_InitStructure.ETH_ChecksumOffload = ETH_ChecksumOffload_Enable; +#endif + ETH_InitStructure.ETH_AutoNegotiation = ETH_AutoNegotiation_Enable; + ETH_InitStructure.ETH_LoopbackMode = ETH_LoopbackMode_Disable; + ETH_InitStructure.ETH_RetryTransmission = ETH_RetryTransmission_Disable; + ETH_InitStructure.ETH_AutomaticPadCRCStrip = ETH_AutomaticPadCRCStrip_Disable; + /* Filter function configuration */ + ETH_InitStructure.ETH_ReceiveAll = ETH_ReceiveAll_Disable; + ETH_InitStructure.ETH_PromiscuousMode = ETH_PromiscuousMode_Disable; + ETH_InitStructure.ETH_BroadcastFramesReception = ETH_BroadcastFramesReception_Enable; + ETH_InitStructure.ETH_MulticastFramesFilter = ETH_MulticastFramesFilter_Perfect; + ETH_InitStructure.ETH_UnicastFramesFilter = ETH_UnicastFramesFilter_Perfect; + /*------------------------ DMA -----------------------------------*/ + /* When we use the Checksum offload feature, we need to enable the Store and Forward mode: + the store and forward guarantee that a whole frame is stored in the FIFO, so the MAC can insert/verify the checksum, + if the checksum is OK the DMA can handle the frame otherwise the frame is dropped */ + ETH_InitStructure.ETH_DropTCPIPChecksumErrorFrame = ETH_DropTCPIPChecksumErrorFrame_Enable; + ETH_InitStructure.ETH_ReceiveStoreForward = ETH_ReceiveStoreForward_Enable; + ETH_InitStructure.ETH_TransmitStoreForward = ETH_TransmitStoreForward_Enable; + ETH_InitStructure.ETH_ForwardErrorFrames = ETH_ForwardErrorFrames_Enable; + ETH_InitStructure.ETH_ForwardUndersizedGoodFrames = ETH_ForwardUndersizedGoodFrames_Enable; + ETH_InitStructure.ETH_SecondFrameOperate = ETH_SecondFrameOperate_Disable; + /* Configure Ethernet */ + ETH_RegInit(Ð_InitStructure, gPHYAddress); + + /* Configure MAC address */ + ETH->MACA0HR = (uint32_t)((macAddr[5] << 8) | macAddr[4]); + ETH->MACA0LR = (uint32_t)(macAddr[0] | (macAddr[1] << 8) | (macAddr[2] << 16) | (macAddr[3] << 24)); + + /* Mask the interrupt that Tx good frame count counter reaches half the maximum value */ + ETH->MMCTIMR = ETH_MMCTIMR_TGFM; + /* Mask the interrupt that Rx good unicast frames counter reaches half the maximum value */ + /* Mask the interrupt that Rx crc error counter reaches half the maximum value */ + ETH->MMCRIMR = ETH_MMCRIMR_RGUFM | ETH_MMCRIMR_RFCEM; + +#if (PHY_MODE == USE_10M_BASE) + ETH_DMAITConfig(ETH_DMA_IT_NIS | ETH_DMA_IT_R | ETH_DMA_IT_T | ETH_DMA_IT_AIS | ETH_DMA_IT_RBU | ETH_DMA_IT_PHYLINK, + ENABLE); +#else + ETH_DMAITConfig(ETH_DMA_IT_NIS | ETH_DMA_IT_R | ETH_DMA_IT_T | ETH_DMA_IT_AIS | ETH_DMA_IT_RBU, + ENABLE); +#if (PHY_MODE == USE_MAC_RMII) +#if ENABLE_POLLING_TO_QUERY_PHY_LINK_STAT + uint16_t RegValue; + ETH_WritePHYRegister(gPHYAddress, 0x1F, 0x07); + /* Configure RMII mode */ + RegValue = ETH_ReadPHYRegister(gPHYAddress, 0x10); + RegValue |= 0x01 << 3; + ETH_WritePHYRegister(gPHYAddress, 0x10, RegValue); +#else + /* Configure the PHY interrupt function, the supported chip is: CH182H RMII */ + PHY_InterruptInit(); + /* Configure EXTI Line7. */ + EXTI_Line_Init(); +#endif +#elif (PHY_MODE == USE_MAC_RGMII) + /* Configure the polarity and delay of TXC */ + RGMII_TXC_Delay(0, 4); +#if !ENABLE_POLLING_TO_QUERY_PHY_LINK_STAT + /* Configure the PHY interrupt function, the supported chip is: RTL8211FS */ + PHY_InterruptInit(); + /* Configure EXTI Line7. */ + EXTI_Line_Init(); +#endif +#endif +#endif +} + +/********************************************************************* + * @fn ETH_TxPktChainMode + * + * @brief Ethernet sends data frames in chain mode. + * + * @param len Send data length + * pBuff send buffer pointer + * + * @return Send status. + */ +uint32_t ETH_TxPktChainMode(uint16_t len, uint32_t* pBuff) +{ + /* Check if the descriptor is owned by the ETHERNET DMA (when set) or CPU (when reset) */ + if ((DMATxDescToSet->Status & ETH_DMATxDesc_OWN) != (uint32_t)RESET) { + /* Return ERROR: OWN bit set */ + return ETH_ERROR; + } + /* Setting the Frame Length: bits[12:0] */ + DMATxDescToSet->ControlBufferSize = (len & ETH_DMATxDesc_TBS1); + DMATxDescToSet->Buffer1Addr = (uint32_t)pBuff; + + /* Setting the last segment and first segment bits (in this case a frame is transmitted in one descriptor) */ +#if HARDWARE_CHECKSUM_CONFIG + DMATxDescToSet->Status |= ETH_DMATxDesc_LS | ETH_DMATxDesc_FS | ETH_DMATxDesc_CIC_TCPUDPICMP_Full; +#else + DMATxDescToSet->Status |= ETH_DMATxDesc_LS | ETH_DMATxDesc_FS; +#endif + + /* Set Own bit of the Tx descriptor Status: gives the buffer back to ETHERNET DMA */ + DMATxDescToSet->Status |= ETH_DMATxDesc_OWN; + + /* Clear TBUS ETHERNET DMA flag */ + ETH->DMASR = ETH_DMASR_TBUS; + /* Resume DMA transmission*/ + ETH->DMATPDR = 0; + + /* Update the ETHERNET DMA global Tx descriptor with next Tx descriptor */ + /* Chained Mode */ + /* Selects the next DMA Tx descriptor list for next buffer to send */ + DMATxDescToSet = (ETH_DMADESCTypeDef*)(DMATxDescToSet->Buffer2NextDescAddr); + /* Return SUCCESS */ + return ETH_SUCCESS; +} + +/********************************************************************* + * @fn WCHNET_ETHIsr + * + * @brief Ethernet Interrupt Service Routine + * + * @return none + */ +void WCHNET_ETHIsr(void) +{ + uint32_t int_sta; +#if (PHY_MODE == USE_10M_BASE) + uint8_t i; + ETH_DMADESCTypeDef* TempDesPtr; +#endif + + int_sta = ETH->DMASR; + if (int_sta & ETH_DMA_IT_AIS) { + if (int_sta & ETH_DMA_IT_RBU) { +#if (PHY_MODE == USE_10M_BASE) + RBUFlag = 1; + ETH_MACReceptionCmd(DISABLE); +#endif + ETH_DMAClearITPendingBit(ETH_DMA_IT_RBU); + } + ETH_DMAClearITPendingBit(ETH_DMA_IT_AIS); + } + + if (int_sta & ETH_DMA_IT_NIS) { + if (int_sta & ETH_DMA_IT_R) { +#if (PHY_MODE == USE_10M_BASE) + TempDesPtr = (ETH_DMADESCTypeDef*)(ETH->DMACHRDR); + for (i = 0; i < (IDLE_DES_NUM + 1); i++) { + if ((TempDesPtr->Status & ETH_DMARxDesc_OWN) == RESET) // Descriptor is occupied + { + if ((RecStopFlag == 0) && (RBUFlag == 0)) { + RecStopFlag = 1; + ETH_MACReceptionCmd(DISABLE); + } + break; + } + TempDesPtr = (ETH_DMADESCTypeDef*)(TempDesPtr->Buffer2NextDescAddr); + } +#endif + /*If you don't use the Ethernet library, + * you can do some data processing operations here*/ + ETH_DMAClearITPendingBit(ETH_DMA_IT_R); + } + if (int_sta & ETH_DMA_IT_T) { + ETH_DMAClearITPendingBit(ETH_DMA_IT_T); + } +#if (PHY_MODE == USE_10M_BASE) + if (int_sta & ETH_DMA_IT_PHYLINK) { + ETH_PHYLink(); + ETH_DMAClearITPendingBit(ETH_DMA_IT_PHYLINK); + } +#endif + ETH_DMAClearITPendingBit(ETH_DMA_IT_NIS); + } +} + +/********************************************************************* + * @fn ETH_Init + * + * @brief Ethernet initialization. + * + * @return none + */ +void ETH_Init(uint8_t* macAddr) +{ +#if (PHY_MODE == USE_10M_BASE) + // todo: delay func + // Delay_Ms(100); + ETH_LedConfiguration(); + RandVal = (macAddr[3] ^ macAddr[4] ^ macAddr[5]) * 214017 + 2531017; +#endif + ETH_Configuration(macAddr); + ETH_DMATxDescChainInit(DMATxDscrTab, MACTxBuf, ETH_TXBUFNB); + ETH_DMARxDescChainInit(DMARxDscrTab, MACRxBuf, ETH_RXBUFNB); + pDMARxSet = DMARxDscrTab; + pDMATxSet = DMATxDscrTab; + NVIC_EnableIRQ(ETH_IRQn); + NVIC_SetPriority(ETH_IRQn, 0); +} + +/********************************************************************* + * @fn ETH_LibInit + * + * @brief Ethernet library initialization program + * + * @return command status + */ +uint8_t ETH_LibInit(uint8_t* ip, uint8_t* gwip, uint8_t* mask, uint8_t* macaddr) +{ + uint8_t s; + struct _WCH_CFG cfg; + + memset(&cfg, 0, sizeof(cfg)); + cfg.TxBufSize = ETH_TX_BUF_SZE; + cfg.TCPMss = WCHNET_TCP_MSS; + cfg.HeapSize = WCHNET_MEM_HEAP_SIZE; + cfg.ARPTableNum = WCHNET_NUM_ARP_TABLE; + cfg.MiscConfig0 = WCHNET_MISC_CONFIG0; + cfg.MiscConfig1 = WCHNET_MISC_CONFIG1; +#if (PHY_MODE == USE_10M_BASE) + cfg.led_link = ETH_LedLinkSet; + cfg.led_data = ETH_LedDataSet; +#endif + cfg.net_send = ETH_TxPktChainMode; + cfg.CheckValid = WCHNET_CFG_VALID; + s = WCHNET_ConfigLIB(&cfg); + if (s) { + return (s); + } + s = WCHNET_Init(ip, gwip, mask, macaddr); + ETH_Init(macaddr); + return (s); +} + +// /******************************** endfile @ eth_driver ******************************/ diff --git a/Ubiquitous/XiZi_IIoT/board/ch32v307vct6/third_party_driver/ethernet/libwchnet.a b/Ubiquitous/XiZi_IIoT/board/ch32v307vct6/third_party_driver/ethernet/libwchnet.a new file mode 100644 index 0000000000000000000000000000000000000000..ccab70a976a8c84755fd698ba1e6887114da34f2 GIT binary patch literal 222966 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 A?f?J) literal 0 HcmV?d00001 diff --git a/Ubiquitous/XiZi_IIoT/board/ch32v307vct6/third_party_driver/ethernet/test/Makefile b/Ubiquitous/XiZi_IIoT/board/ch32v307vct6/third_party_driver/ethernet/test/Makefile new file mode 100755 index 000000000..bf55d6f89 --- /dev/null +++ b/Ubiquitous/XiZi_IIoT/board/ch32v307vct6/third_party_driver/ethernet/test/Makefile @@ -0,0 +1,4 @@ +SRC_FILES := wch_tcp_test.c + + +include $(KERNEL_ROOT)/compiler.mk diff --git a/Ubiquitous/XiZi_IIoT/board/ch32v307vct6/third_party_driver/ethernet/test/wch_tcp_test.c b/Ubiquitous/XiZi_IIoT/board/ch32v307vct6/third_party_driver/ethernet/test/wch_tcp_test.c new file mode 100644 index 000000000..a855fc890 --- /dev/null +++ b/Ubiquitous/XiZi_IIoT/board/ch32v307vct6/third_party_driver/ethernet/test/wch_tcp_test.c @@ -0,0 +1,72 @@ +#include "connect_ether.h" +#include "eth_driver.h" +#include "shell.h" +#include "wchnet.h" +#include "xs_base.h" + +uint8_t DESIP[4] = { 192, 168, 1, 100 }; // destination IP address +uint16_t desport = 1000; // destination port +uint16_t srcport = 1000; // source port + +uint8_t SocketId; +uint8_t SocketIdForListen; // Socket for Listening + +/********************************************************************* + * @fn TCP client + * + * + * @return none + */ +int Tcp_Client(void) +{ + uint8_t i; + for (i = 0; i < WCHNET_MAX_SOCKET_NUM; i++) + WCHNET_CreateTcpSocket(DESIP, srcport, desport, &SocketId); // Create TCP Socket + + while (1) { + /*Ethernet library main task function, + * which needs to be called cyclically*/ + WCHNET_MainTask(); + /*Query the Ethernet global interrupt, + * if there is an interrupt, call the global interrupt handler*/ + if (WCHNET_QueryGlobalInt()) { + WCHNET_HandleGlobalInt(); + } + } +} + +int test_tcp_client(int argc, char* argv[]) +{ + KPrintf("TCPClient Test\r\n"); + Tcp_Client(); + return 0; +} + +SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0) | SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN), + test_tcp_client, test_tcp_client, test tcp client); + +int Tcp_Server(void) +{ + WCHNET_CreateTcpSocketListen(srcport, &SocketIdForListen); // Create TCP Socket for Listening + + while (1) { + /*Ethernet library main task function, + * which needs to be called cyclically*/ + WCHNET_MainTask(); + /*Query the Ethernet global interrupt, + * if there is an interrupt, call the global interrupt handler*/ + if (WCHNET_QueryGlobalInt()) { + WCHNET_HandleGlobalInt(); + } + } +} + +int test_tcp_server(int argc, char* argv[]) +{ + KPrintf("TCPServer Test\r\n"); + Tcp_Server(); + return 0; +} + +SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0) | SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN), + test_tcp_server, test_tcp_server, test tcp server); \ No newline at end of file diff --git a/Ubiquitous/XiZi_IIoT/board/ch32v307vct6/third_party_driver/include/connect_ether.h b/Ubiquitous/XiZi_IIoT/board/ch32v307vct6/third_party_driver/include/connect_ether.h new file mode 100644 index 000000000..13ee80495 --- /dev/null +++ b/Ubiquitous/XiZi_IIoT/board/ch32v307vct6/third_party_driver/include/connect_ether.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2020 AIIT XUOS Lab + * XiUOS is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +/** + * @file connect_ethernet.h + * @brief define rvstar uart function + * @version 1.0 + * @author AIIT XUOS Lab + * @date 2022-08-01 + */ + +#ifndef CONNECT_ETH_H +#define CONNECT_ETH_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +uint8_t InitHwEth(); +void WCHNET_CreateTcpSocket(uint8_t* DESIP, uint16_t srcport, uint16_t desport, uint8_t* SocketId); +void WCHNET_CreateTcpSocketListen(uint16_t srcport, uint8_t* SocketId); + +void WCHNET_HandleGlobalInt(void); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/Ubiquitous/XiZi_IIoT/board/ch32v307vct6/third_party_driver/include/eth_driver.h b/Ubiquitous/XiZi_IIoT/board/ch32v307vct6/third_party_driver/include/eth_driver.h new file mode 100644 index 000000000..8da2ecf72 --- /dev/null +++ b/Ubiquitous/XiZi_IIoT/board/ch32v307vct6/third_party_driver/include/eth_driver.h @@ -0,0 +1,181 @@ +/********************************** (C) COPYRIGHT ************* ****************** + * File Name : eth_driver.h + * Author : WCH + * Version : V1.3.0 + * Date : 2022/06/02 + * Description : This file contains the headers of the ETH Driver. + ********************************************************************************* + * Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd. + * Attention: This software (modified or not) and binary are used for + * microcontroller manufactured by Nanjing Qinheng Microelectronics. + *******************************************************************************/ +#ifndef __ETH_DRIVER__ +#define __ETH_DRIVER__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "ch32v30x_eth.h" +#include "debug.h" +#include "wchnet.h" + +/* Internal 10M PHY */ +#define USE_10M_BASE 1 +/* The chips supported by the MII/RMII driver are: CH182/RTL8201F, etc. */ +#define USE_MAC_MII 2 +#define USE_MAC_RMII 3 +#define USE_MAC_RGMII 4 + +#ifndef PHY_MODE +#define PHY_MODE USE_10M_BASE +#endif + +/* 1: interrupt 0: polling in RMII or RGMII mode */ +#define LINK_STAT_ACQUISITION_METHOD 1 + +#define PHY_ADDRESS 1 + +#define ETH_DMARxDesc_FrameLengthShift 16 + +#define ROM_CFG_USERADR_ID 0x1FFFF7E8 + +#define PHY_LINK_TASK_PERIOD 50 + +#define PHY_ANLPAR_SELECTOR_FIELD 0x1F +#define PHY_ANLPAR_SELECTOR_VALUE 0x01 /* 5B'00001 */ + +#define PHY_LINK_INIT 0x00 +#define PHY_LINK_SUC_P (1 << 0) +#define PHY_LINK_SUC_N (1 << 1) +#define PHY_LINK_WAIT_SUC (1 << 7) + +#define PHY_PN_SWITCH_P (0 << 2) +#define PHY_PN_SWITCH_N (1 << 2) +#define PHY_PN_SWITCH_AUTO (2 << 2) + +#ifndef WCHNETTIMERPERIOD +#define WCHNETTIMERPERIOD 10 /* Timer period, in Ms. */ +#endif + +#define GPIO_Output(a, b) \ + GPIO_InitStructure.GPIO_Pin = b; \ + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; \ + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; \ + GPIO_Init(a, &GPIO_InitStructure) + +#define GPIO_Input(a, b) \ + GPIO_InitStructure.GPIO_Pin = b; \ + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; \ + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; \ + GPIO_Init(a, &GPIO_InitStructure) + +#define QUERY_STAT_FLAG ((LastQueryPhyTime == (LocalTime / 1000)) ? 0 : 1) + +#define ENABLE_POLLING_TO_QUERY_PHY_LINK_STAT ((PHY_MODE == USE_MAC_MII) || (((PHY_MODE == USE_MAC_RMII) || (PHY_MODE == USE_MAC_RGMII)) && !LINK_STAT_ACQUISITION_METHOD)) + +#define ACCELERATE_LINK_PROCESS() \ + do { \ + if ((TRDetectStep < 2) && (ETH_ReadPHYRegister(gPHYAddress, PHY_ANLPAR) & PHY_ANLPAR_SELECTOR_FIELD)) \ + LinkTaskPeriod = 0; \ + } while (0) + +#define UPDATE_LINKTASKPERIOD() \ + do { \ + if (TRDetectStep == 1) { \ + RandVal = RandVal * 214017 + 2531017; \ + LinkTaskPeriod = RandVal % 100 + 50; \ + } else { \ + LinkTaskPeriod = 50; \ + } \ + } while (0) + +#define PHY_RESTART_AUTONEGOTIATION() \ + do { \ + RegVal = ETH_ReadPHYRegister(gPHYAddress, PHY_BCR); \ + RegVal &= ~0x01; \ + RegVal |= PHY_Restart_AutoNegotiation; \ + ETH_WritePHYRegister(gPHYAddress, PHY_BCR, RegVal); \ + RegVal = ETH_ReadPHYRegister(gPHYAddress, PHY_BCR); \ + RegVal |= 0x03 | PHY_Restart_AutoNegotiation; \ + ETH_WritePHYRegister(gPHYAddress, PHY_BCR, RegVal); \ + } while (0) + +#define PHY_TR_SWITCH() \ + do { \ + phy_mdix = ETH_ReadPHYRegister(gPHYAddress, PHY_MDIX); \ + if (phy_mdix & 0x01) { \ + phy_mdix &= ~0x03; \ + phy_mdix |= 1 << 1; \ + } else { \ + phy_mdix &= ~0x03; \ + phy_mdix |= 1 << 0; \ + } \ + ETH_WritePHYRegister(gPHYAddress, PHY_MDIX, phy_mdix); \ + PHY_RESTART_AUTONEGOTIATION(); \ + } while (0) + +#define PHY_TR_REVERSE() \ + do { \ + RegVal = ETH_ReadPHYRegister(gPHYAddress, PHY_MDIX); \ + if (RegVal & 0x01) { \ + RegVal &= ~0x03; \ + RegVal |= 1 << 1; \ + } else { \ + RegVal &= ~0x03; \ + RegVal |= 1 << 0; \ + } \ + ETH_WritePHYRegister(gPHYAddress, PHY_MDIX, RegVal); \ + } while (0) + +#define PHY_PN_SWITCH(PNMode) \ + do { \ + if (PNMode == PHY_PN_SWITCH_AUTO) { \ + phyPN = PHY_PN_SWITCH_AUTO; \ + } else { \ + phyPN = (ETH_ReadPHYRegister(gPHYAddress, PHY_MDIX) & (~(0x03 << 2))) | PNMode; \ + } \ + ETH_WritePHYRegister(gPHYAddress, PHY_MDIX, phyPN); \ + phyPN = PNMode; \ + PHY_RESTART_AUTONEGOTIATION(); \ + } while (0) + +#define PHY_NEGOTIATION_PARAM_INIT() \ + do { \ + phyStatus = 0; \ + phySucCnt = 0; \ + phyLinkCnt = 0; \ + TRDetectStep = 0; \ + PhyPolarityDetect = 0; \ + phyLinkStatus = PHY_LINK_INIT; \ + phyPN = PHY_PN_SWITCH_AUTO; \ + ETH_WritePHYRegister(gPHYAddress, PHY_MDIX, phyPN); \ + } while (0) + +#define PHY_LINK_RESET() \ + do { \ + ETH_WritePHYRegister(gPHYAddress, PHY_BCR, PHY_Reset); \ + PHY_NEGOTIATION_PARAM_INIT(); \ + } while (0) + +extern ETH_DMADESCTypeDef* DMATxDescToSet; +extern ETH_DMADESCTypeDef* DMARxDescToGet; +extern SOCK_INF SocketInf[]; + +void ETH_PHYLink(void); +void WCHNET_ETHIsr(void); +void WCHNET_MainTask(void); +void ETH_LedConfiguration(void); +void ETH_Init(uint8_t* macAddr); +void ETH_LedLinkSet(uint8_t mode); +void ETH_LedDataSet(uint8_t mode); +void WCHNET_TimeIsr(uint16_t timperiod); +void ETH_Configuration(uint8_t* macAddr); +uint8_t ETH_LibInit(uint8_t* ip, uint8_t* gwip, uint8_t* mask, uint8_t* macaddr); +void WCHNET_MainTask(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/Ubiquitous/XiZi_IIoT/board/ch32v307vct6/third_party_driver/include/net_config.h b/Ubiquitous/XiZi_IIoT/board/ch32v307vct6/third_party_driver/include/net_config.h new file mode 100644 index 000000000..d9af02575 --- /dev/null +++ b/Ubiquitous/XiZi_IIoT/board/ch32v307vct6/third_party_driver/include/net_config.h @@ -0,0 +1,160 @@ +/********************************** (C) COPYRIGHT ******************************* + * File Name : net_config.h + * Author : WCH + * Version : V1.30 + * Date : 2022/06/02 + * Description : This file contains the configurations of + * Ethernet protocol stack library + ********************************************************************************* + * Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd. + * Attention: This software (modified or not) and binary are used for + * microcontroller manufactured by Nanjing Qinheng Microelectronics. + *******************************************************************************/ +#ifndef __NET_CONFIG_H__ +#define __NET_CONFIG_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/********************************************************************* + * socket configuration, IPRAW + UDP + TCP + TCP_LISTEN = number of sockets + */ +#define WCHNET_NUM_IPRAW 0 /* Number of IPRAW connections */ + +#define WCHNET_NUM_UDP 0 /* The number of UDP connections */ + +#define WCHNET_NUM_TCP 1 /* Number of TCP connections */ + +#define WCHNET_NUM_TCP_LISTEN 0 /* Number of TCP listening */ + +/* The number of sockets, the maximum is 31 */ +#define WCHNET_MAX_SOCKET_NUM (WCHNET_NUM_IPRAW + WCHNET_NUM_UDP + WCHNET_NUM_TCP + WCHNET_NUM_TCP_LISTEN) + +#define WCHNET_TCP_MSS 1460 /* Size of TCP MSS*/ + +#define WCHNET_NUM_POOL_BUF (WCHNET_NUM_TCP * 2 + 2) /* The number of POOL BUFs, the number of receive queues */ + +/********************************************************************* + * MAC queue configuration + */ +#define ETH_TXBUFNB 2 /* The number of descriptors sent by the MAC */ + +#define ETH_RXBUFNB 4 /* Number of MAC received descriptors */ + +#ifndef ETH_MAX_PACKET_SIZE +#define ETH_RX_BUF_SZE 1520 /* MAC receive buffer length, an integer multiple of 4 */ +#define ETH_TX_BUF_SZE 1520 /* MAC send buffer length, an integer multiple of 4 */ +#else +#define ETH_RX_BUF_SZE ETH_MAX_PACKET_SIZE +#define ETH_TX_BUF_SZE ETH_MAX_PACKET_SIZE +#endif + +/********************************************************************* + * Functional configuration + */ +#define WCHNET_PING_ENABLE 1 /* PING is enabled, PING is enabled by default */ + +#define TCP_RETRY_COUNT 20 /* The number of TCP retransmissions, the default value is 20 */ + +#define TCP_RETRY_PERIOD 10 /* TCP retransmission period, the default value is 10, the unit is 50ms */ + +#define SOCKET_SEND_RETRY 0 /* Send failed retry configuration, 1: enable, 0: disable */ + +#define HARDWARE_CHECKSUM_CONFIG 0 /* Hardware checksum checking and insertion configuration, 1: enable, 0: disable */ + +#define FINE_DHCP_PERIOD 8 /* Fine DHCP period, the default value is 8, the unit is 250ms */ + +#define CFG0_TCP_SEND_COPY 1 /* TCP send buffer copy, 1: copy, 0: not copy */ + +#define CFG0_TCP_RECV_COPY 1 /* TCP receive replication optimization, internal debugging use */ + +#define CFG0_TCP_OLD_DELETE 0 /* Delete oldest TCP connection, 1: enable, 0: disable */ + +#define CFG0_IP_REASS_PBUFS 0 /* Number of reassembled IP PBUFs */ + +#define CFG0_TCP_DEALY_ACK_DISABLE 1 /* 1: disable TCP delay ACK 0: enable TCP delay ACK */ + +/********************************************************************* + * Memory related configuration + */ +/* If you want to achieve a higher transmission speed, + * try to increase RECE_BUF_LEN to (WCHNET_TCP_MSS*4) + * and increase WCHNET_NUM_TCP_SEG to (WCHNET_NUM_TCP*4)*/ +#define RECE_BUF_LEN (WCHNET_TCP_MSS * 2) /* socket receive buffer size */ + +#define WCHNET_NUM_PBUF WCHNET_NUM_POOL_BUF /* Number of PBUF structures */ + +#define WCHNET_NUM_TCP_SEG (WCHNET_NUM_TCP * 2) /* The number of TCP segments used to send */ + +#define WCHNET_MEM_HEAP_SIZE (((WCHNET_TCP_MSS + 0x10 + 54 + 8) * WCHNET_NUM_TCP_SEG) + ETH_TX_BUF_SZE + 64 + 2 * 0x18) /* memory heap size */ + +#define WCHNET_NUM_ARP_TABLE 50 /* Number of ARP lists */ + +#define WCHNET_MEM_ALIGNMENT 4 /* 4 byte alignment */ + +#if CFG0_IP_REASS_PBUFS +#define WCHNET_NUM_IP_REASSDATA 2 /* Number of reassembled IP structures */ +/*1: When using the fragmentation function, + * ensure that the size of WCHNET_SIZE_POOL_BUF is large enough to store a single fragmented packet*/ +#define WCHNET_SIZE_POOL_BUF (((1500 + 14 + 4) + 3) & ~3) /* Buffer size for receiving a single packet */ +/*2: When creating a socket that can receive fragmented packets, + * ensure that "RecvBufLen" member of the "struct _SOCK_INF" structure + * (the parameter initialized when calling WCHNET_SocketCreat) is sufficient + * to receive a complete fragmented packet */ +#else +#define WCHNET_NUM_IP_REASSDATA 0 /* Number of reassembled IP structures */ +#define WCHNET_SIZE_POOL_BUF (((WCHNET_TCP_MSS + 40 + 14 + 4) + 3) & ~3) /* Buffer size for receiving a single packet */ +#endif + +/* Check receive buffer */ +#if (WCHNET_NUM_POOL_BUF * WCHNET_SIZE_POOL_BUF < ETH_RX_BUF_SZE) +#error "WCHNET_NUM_POOL_BUF or WCHNET_TCP_MSS Error" +#error "Please Increase WCHNET_NUM_POOL_BUF or WCHNET_TCP_MSS to make sure the receive buffer is sufficient" +#endif +/* Check the configuration of the SOCKET quantity */ +#if (WCHNET_NUM_TCP_LISTEN && !WCHNET_NUM_TCP) +#error "WCHNET_NUM_TCP Error,Please Configure WCHNET_NUM_TCP >= 1" +#endif +/* Check byte alignment must be a multiple of 4 */ +#if ((WCHNET_MEM_ALIGNMENT % 4) || (WCHNET_MEM_ALIGNMENT == 0)) +#error "WCHNET_MEM_ALIGNMENT Error,Please Configure WCHNET_MEM_ALIGNMENT = 4 * N, N >=1" +#endif +/* TCP maximum segment length */ +#if ((WCHNET_TCP_MSS > 1460) || (WCHNET_TCP_MSS < 60)) +#error "WCHNET_TCP_MSS Error,Please Configure WCHNET_TCP_MSS >= 60 && WCHNET_TCP_MSS <= 1460" +#endif +/* Number of ARP cache tables */ +#if ((WCHNET_NUM_ARP_TABLE > 0X7F) || (WCHNET_NUM_ARP_TABLE < 1)) +#error "WCHNET_NUM_ARP_TABLE Error,Please Configure WCHNET_NUM_ARP_TABLE >= 1 && WCHNET_NUM_ARP_TABLE <= 0X7F" +#endif +/* Check POOL BUF configuration */ +#if (WCHNET_NUM_POOL_BUF < 1) +#error "WCHNET_NUM_POOL_BUF Error,Please Configure WCHNET_NUM_POOL_BUF >= 1" +#endif +/* Check PBUF structure configuration */ +#if (WCHNET_NUM_PBUF < 1) +#error "WCHNET_NUM_PBUF Error,Please Configure WCHNET_NUM_PBUF >= 1" +#endif +/* Check IP Assignment Configuration */ +#if (CFG0_IP_REASS_PBUFS && ((WCHNET_NUM_IP_REASSDATA > 10) || (WCHNET_NUM_IP_REASSDATA < 1))) +#error "WCHNET_NUM_IP_REASSDATA Error,Please Configure WCHNET_NUM_IP_REASSDATA < 10 && WCHNET_NUM_IP_REASSDATA >= 1 " +#endif +/* Check the number of reassembled IP PBUFs */ +#if (CFG0_IP_REASS_PBUFS > WCHNET_NUM_POOL_BUF) +#error "WCHNET_NUM_POOL_BUF Error,Please Configure CFG0_IP_REASS_PBUFS < WCHNET_NUM_POOL_BUF" +#endif +/* Check Timer period, in Ms. */ +#if (WCHNETTIMERPERIOD > 50) +#error "WCHNETTIMERPERIOD Error,Please Configure WCHNETTIMERPERIOD < 50" +#endif + +/* Configuration value 0 */ +#define WCHNET_MISC_CONFIG0 (((CFG0_TCP_SEND_COPY) << 0) | ((CFG0_TCP_RECV_COPY) << 1) | ((CFG0_TCP_OLD_DELETE) << 2) | ((CFG0_IP_REASS_PBUFS) << 3) | ((CFG0_TCP_DEALY_ACK_DISABLE) << 8)) +/* Configuration value 1 */ +#define WCHNET_MISC_CONFIG1 (((WCHNET_MAX_SOCKET_NUM) << 0) | ((WCHNET_PING_ENABLE) << 13) | ((TCP_RETRY_COUNT) << 14) | ((TCP_RETRY_PERIOD) << 19) | ((SOCKET_SEND_RETRY) << 25) | ((HARDWARE_CHECKSUM_CONFIG) << 26) | ((FINE_DHCP_PERIOD) << 27)) + +#ifdef __cplusplus +} +#endif +#endif diff --git a/Ubiquitous/XiZi_IIoT/board/ch32v307vct6/third_party_driver/include/wchnet.h b/Ubiquitous/XiZi_IIoT/board/ch32v307vct6/third_party_driver/include/wchnet.h new file mode 100644 index 000000000..4d52a19da --- /dev/null +++ b/Ubiquitous/XiZi_IIoT/board/ch32v307vct6/third_party_driver/include/wchnet.h @@ -0,0 +1,591 @@ +/********************************** (C) COPYRIGHT ******************************* + * File Name : wchnet.h + * Author : WCH + * Version : V1.80 + * Date : 2023/05/12 + * Description : This file contains the headers of + * the Ethernet protocol stack library. + ********************************************************************************* + * Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd. + * Attention: This software (modified or not) and binary are used for + * microcontroller manufactured by Nanjing Qinheng Microelectronics. + *******************************************************************************/ +#ifndef __WCHNET_H__ +#define __WCHNET_H__ + +#include "stdint.h" +#ifndef NET_LIB +#include "net_config.h" +#endif +#ifdef __cplusplus +extern "C" { +#endif + +#define WCHNET_LIB_VER 0x18 // the library version number +#define WCHNET_CFG_VALID 0x12345678 // Configuration value valid flag + +/* LED state @LED_STAT */ +#define LED_ON 0 +#define LED_OFF 1 + +/* PHY state @PHY_STAT */ +#define PHY_LINK_SUCCESS (1 << 2) // PHY connection success +#define PHY_AUTO_SUCCESS (1 << 5) // PHY auto negotiation completed + +/* Library initialization state @CFG_INIT_STAT */ +#define INIT_OK 0x00 +#define INIT_ERR_RX_BUF_SIZE 0x01 +#define INIT_ERR_TCP_MSS 0x02 +#define INIT_ERR_HEAP_SIZE 0x03 +#define INIT_ERR_ARP_TABLE_NEM 0x04 +#define INIT_ERR_MISC_CONFIG0 0x05 +#define INIT_ERR_MISC_CONFIG1 0x06 +#define INIT_ERR_FUNC_SEND 0x09 +#define INIT_ERR_CHECK_VALID 0xFF + +/* Socket protocol type */ +#define PROTO_TYPE_IP_RAW 0 // IP layer raw data +#define PROTO_TYPE_UDP 2 // UDP protocol +#define PROTO_TYPE_TCP 3 // TCP protocol + +/* interrupt status */ +/* The following are the states + * that GLOB_INT will generate */ +#define GINT_STAT_UNREACH (1 << 0) // unreachable interrupt +#define GINT_STAT_IP_CONFLI (1 << 1) // IP conflict interrupt +#define GINT_STAT_PHY_CHANGE (1 << 2) // PHY state change interrupt +#define GINT_STAT_SOCKET (1 << 4) // socket related interrupt + +/* The following are the states + * that Sn_INT will generate*/ +#define SINT_STAT_RECV (1 << 2) // the socket receives data or the receive buffer is not empty +#define SINT_STAT_CONNECT (1 << 3) // connect successfully,generated in TCP mode +#define SINT_STAT_DISCONNECT (1 << 4) // disconnect,generated in TCP mode +#define SINT_STAT_TIM_OUT (1 << 6) // timeout disconnect,generated in TCP mode + +/* Definitions for error constants. @ERR_T */ +#define ERR_T +#define WCHNET_ERR_SUCCESS 0x00 // No error, everything OK +#define WCHNET_ERR_BUSY 0x10 // busy +#define WCHNET_ERR_MEM 0x11 // Out of memory error +#define WCHNET_ERR_BUF 0x12 // Buffer error +#define WCHNET_ERR_TIMEOUT 0x13 // Timeout +#define WCHNET_ERR_RTE 0x14 // Routing problem +#define WCHNET_ERR_ABRT 0x15 // Connection aborted +#define WCHNET_ERR_RST 0x16 // Connection reset +#define WCHNET_ERR_CLSD 0x17 // Connection closed +#define WCHNET_ERR_CONN 0x18 // Not connected +#define WCHNET_ERR_VAL 0x19 // Illegal value +#define WCHNET_ERR_ARG 0x1a // Illegal argument +#define WCHNET_ERR_USE 0x1b // Address in use +#define WCHNET_ERR_IF 0x1c // Low-level netif error +#define WCHNET_ERR_ISCONN 0x1d // Already connected +#define WCHNET_ERR_INPROGRESS 0x1e // Operation in progress +#define WCHNET_ERR_SOCKET_MEM 0X20 // Socket information error +#define WCHNET_ERR_UNSUPPORT_PROTO 0X21 // unsupported protocol type +#define WCHNET_RET_ABORT 0x5F // command process fail +#define WCHNET_ERR_UNKNOW 0xFA // unknow + +/* unreachable condition related codes */ +#define UNREACH_CODE_HOST 0 // host unreachable +#define UNREACH_CODE_NET 1 // network unreachable +#define UNREACH_CODE_PROTOCOL 2 // protocol unreachable +#define UNREACH_CODE_PROT 3 // port unreachable +/*For other values, please refer to the RFC792 document*/ + +/* TCP disconnect related codes */ +#define TCP_CLOSE_NORMAL 0 // normal disconnect,a four-way handshake +#define TCP_CLOSE_RST 1 // reset the connection and close +#define TCP_CLOSE_ABANDON 2 // drop connection, and no termination message is sent + +/* socket state code */ +#define SOCK_STAT_CLOSED 0X00 // socket close +#define SOCK_STAT_OPEN 0X05 // socket open + +/* TCP state code */ +#define TCP_CLOSED 0 // TCP close +#define TCP_LISTEN 1 // TCP listening +#define TCP_SYN_SENT 2 // SYN send, connect request +#define TCP_SYN_RCVD 3 // SYN received, connection request received +#define TCP_ESTABLISHED 4 // TCP connection establishment +#define TCP_FIN_WAIT_1 5 // WAIT_1 state +#define TCP_FIN_WAIT_2 6 // WAIT_2 state +#define TCP_CLOSE_WAIT 7 // wait to close +#define TCP_CLOSING 8 // closing +#define TCP_LAST_ACK 9 // LAST_ACK +#define TCP_TIME_WAIT 10 // 2MSL wait + +/* The following values are fixed and cannot be changed */ +#define WCHNET_MEM_ALIGN_SIZE(size) (((size) + WCHNET_MEM_ALIGNMENT - 1) & ~(WCHNET_MEM_ALIGNMENT - 1)) +#define WCHNET_SIZE_IPRAW_PCB 0x1C // IPRAW PCB size +#define WCHNET_SIZE_UDP_PCB 0x20 // UDP PCB size +#define WCHNET_SIZE_TCP_PCB 0xB4 // TCP PCB size +#define WCHNET_SIZE_TCP_PCB_LISTEN 0x24 // TCP LISTEN PCB size +#define WCHNET_SIZE_IP_REASSDATA 0x20 // IP reassembled Management +#define WCHNET_SIZE_PBUF 0x10 // Packet Buf +#define WCHNET_SIZE_TCP_SEG 0x14 // TCP SEG structure +#define WCHNET_SIZE_MEM 0x08 // sizeof(struct mem) +#define WCHNET_SIZE_ARP_TABLE 0x18 // sizeof ARP table + +#define WCHNET_MEMP_SIZE ((WCHNET_MEM_ALIGNMENT - 1) + (WCHNET_NUM_IPRAW * WCHNET_MEM_ALIGN_SIZE(WCHNET_SIZE_IPRAW_PCB)) + (WCHNET_NUM_UDP * WCHNET_MEM_ALIGN_SIZE(WCHNET_SIZE_UDP_PCB)) + (WCHNET_NUM_TCP * WCHNET_MEM_ALIGN_SIZE(WCHNET_SIZE_TCP_PCB)) + (WCHNET_NUM_TCP_LISTEN * WCHNET_MEM_ALIGN_SIZE(WCHNET_SIZE_TCP_PCB_LISTEN)) + (WCHNET_NUM_TCP_SEG * WCHNET_MEM_ALIGN_SIZE(WCHNET_SIZE_TCP_SEG)) + (WCHNET_NUM_IP_REASSDATA * WCHNET_MEM_ALIGN_SIZE(WCHNET_SIZE_IP_REASSDATA)) + (WCHNET_NUM_PBUF * WCHNET_MEM_ALIGN_SIZE(WCHNET_SIZE_PBUF)) + (WCHNET_NUM_POOL_BUF * (WCHNET_MEM_ALIGN_SIZE(WCHNET_SIZE_PBUF) + WCHNET_MEM_ALIGN_SIZE(WCHNET_SIZE_POOL_BUF)))) + +#define HEAP_MEM_ALIGN_SIZE (WCHNET_MEM_ALIGN_SIZE(WCHNET_SIZE_MEM)) +#define WCHNET_RAM_HEAP_SIZE (WCHNET_MEM_ALIGN_SIZE(WCHNET_MEM_HEAP_SIZE) + HEAP_MEM_ALIGN_SIZE) +#define WCHNET_RAM_ARP_TABLE_SIZE (WCHNET_MEM_ALIGN_SIZE(WCHNET_SIZE_ARP_TABLE) * WCHNET_NUM_ARP_TABLE) + +typedef struct +{ + uint32_t length; + uint32_t buffer; +} ETHFrameType; + +/* LED callback type */ +typedef void (*led_callback)(uint8_t setbit); + +/* net send callback type */ +typedef uint32_t (*eth_tx_set)(uint16_t len, uint32_t* pBuff); + +/* net receive callback type */ +typedef uint32_t (*eth_rx_set)(ETHFrameType* pkt); + +/* DNS callback type */ +typedef void (*dns_callback)(const char* name, uint8_t* ipaddr, void* callback_arg); + +/* DHCP callback type */ +typedef uint8_t (*dhcp_callback)(uint8_t status, void*); + +/* socket receive callback type */ +struct _SOCK_INF; +typedef void (*pSockRecv)(struct _SOCK_INF*, uint32_t, uint16_t, uint8_t*, uint32_t); + +/* Socket information struct */ +typedef struct _SOCK_INF { + uint32_t IntStatus; // interrupt state + uint32_t SockIndex; // Socket index value + uint32_t RecvStartPoint; // Start pointer of the receive buffer + uint32_t RecvBufLen; // Receive buffer length + uint32_t RecvCurPoint; // current pointer to receive buffer + uint32_t RecvReadPoint; // The read pointer of the receive buffer + uint32_t RecvRemLen; // The length of the remaining data in the receive buffer + uint32_t ProtoType; // protocol type + uint32_t SockStatus; // Low byte Socket state, the next low byte is TCP state, only meaningful in TCP mode + uint32_t DesPort; // destination port + uint32_t SourPort; // Source port, protocol type in IPRAW mode + uint8_t IPAddr[4]; // Socket destination IP address + void* Resv1; // Reserved, for internal use, for saving individual PCBs + void* Resv2; // Reserved, used internally, used by TCP Server + pSockRecv AppCallBack; // receive callback function +} SOCK_INF; + +struct _WCH_CFG { + uint32_t TxBufSize; // MAC send buffer size, reserved for use + uint32_t TCPMss; // TCP MSS size + uint32_t HeapSize; // heap memory size + uint32_t ARPTableNum; // Number of ARP lists + uint32_t MiscConfig0; // Miscellaneous Configuration 0 + /* Bit 0 TCP send buffer copy 1: copy, 0: not copy */ + /* Bit 1 TCP receive replication optimization, used for internal debugging */ + /* bit 2 delete oldest TCP connection 1: enable, 0: disable */ + /* Bits 3-7 Number of PBUFs of IP segments */ + /* Bit 8 TCP Delay ACK disable */ + uint32_t MiscConfig1; // Miscellaneous Configuration 1 + /* Bits 0-7 Number of Sockets*/ + /* Bits 8-12 Reserved */ + /* Bit 13 PING enable, 1: On 0: Off */ + /* Bits 14-18 TCP retransmission times */ + /* Bits 19-23 TCP retransmission period, in 50 milliseconds */ + /* bit 25 send failed retry, 1: enable, 0: disable */ + /* bit 26 Select whether to perform IPv4 checksum check on + * the TCP/UDP/ICMP header of the received frame payload by hardware, + * and calculate and insert the checksum of the IP header and payload of the sent frame by hardware.*/ + /* Bits 27-31 period (in 250 milliseconds) of Fine DHCP periodic process */ + led_callback led_link; // PHY Link Status Indicator + led_callback led_data; // Ethernet communication indicator + eth_tx_set net_send; // Ethernet send + eth_rx_set net_recv; // Ethernet receive + uint32_t CheckValid; // Configuration value valid flag, fixed value @WCHNET_CFG_VALID +}; + +struct _NET_SYS { + uint8_t IPAddr[4]; // IP address + uint8_t GWIPAddr[4]; // Gateway IP address + uint8_t MASKAddr[4]; // subnet mask + uint8_t MacAddr[8]; // MAC address + uint8_t UnreachIPAddr[4]; // Unreachable IP address + uint32_t RetranCount; // number of retries,default is 10 times + uint32_t RetranPeriod; // Retry period, unit MS, default 500MS + uint32_t PHYStat; // PHY state code + uint32_t NetStat; // The status of the Ethernet, including whether it is open, etc. + uint32_t MackFilt; // MAC filtering, the default is to receive broadcasts, receive local MAC + uint32_t GlobIntStatus; // global interrupt + uint32_t UnreachCode; // unreachable code + uint32_t UnreachProto; // unreachable protocol + uint32_t UnreachPort; // unreachable port + uint32_t SendFlag; + uint32_t Flags; +}; + +/* KEEP LIVE configuration structure */ +struct _KEEP_CFG { + uint32_t KLIdle; // KEEPLIVE idle time, in ms + uint32_t KLIntvl; // KEEPLIVE period, in ms + uint32_t KLCount; // KEEPLIVE times +}; + +/** + * @brief Library initialization . + * + * @param ip - IP address pointer + * @param gwip - Gateway address pointer + * @param mask - Subnet mask pointer + * @param macaddr - MAC address pointer + * + * @return @ERR_T + */ +uint8_t WCHNET_Init(const uint8_t* ip, const uint8_t* gwip, const uint8_t* mask, const uint8_t* macaddr); + +/** + * @brief get library version + * + * @param None + * + * @return library version + */ +uint8_t WCHNET_GetVer(void); + +/** + * @brief Get MAC address. + * + * @param(in) macaddr - MAC address + * + * @param(out) MAC address + * + * @return None + */ +void WCHNET_GetMacAddr(uint8_t* macaddr); + +/** + * @brief Library parameter configuration. + * + * @param cfg - Configuration parameter @_WCH_CFG + * + * @return Library configuration initialization state @CFG_INIT_STAT + */ +uint8_t WCHNET_ConfigLIB(struct _WCH_CFG* cfg); + +/** + * @brief Handle periodic tasks in the protocol stack + * + * @param None + * + * @return None + */ +void WCHNET_PeriodicHandle(void); + +/** + * @brief Ethernet data input. Always called in the main program, + * or called after the reception interrupt is detected. + * + * @param + * + * @return None + */ +void WCHNET_NetInput(void); + +/** + * @brief Ethernet interrupt service function. Called after + * Ethernet interrupt is generated. + * + * @param None + * + * @return None + */ +void WCHNET_ETHIsr(void); + +/** + * @brief Get PHY status + * + * @param None + * + * @return PHY status @PHY_STAT + */ +uint8_t WCHNET_GetPHYStatus(void); + +/** + * @brief Query global interrupt status. + * + * @param None + * + * @return GLOB_INT + */ +uint8_t WCHNET_QueryGlobalInt(void); + +/** + * @brief Read global interrupt and clear it. + * + * @param None + * + * @return GLOB_INT + */ +uint8_t WCHNET_GetGlobalInt(void); + +/** + * @brief create socket + * + * @param(in) *socketid - socket variable pointer + * @param socinf - Configuration parameters for creating sockets @SOCK_INF + * + * @param(out) *socketid - socket value + * + * @return @ERR_T + */ +uint8_t WCHNET_SocketCreat(uint8_t* socketid, SOCK_INF* socinf); + +/** + * @brief Socket sends data. + * + * @param socketid - socket id value + * @param *buf - the first address of send buffer + * @param(in) *len - pointer to the length of the data expected to be sent + * + * @param(out) *len - pointer to the length of the data sent actually + * + * @return @ERR_T + */ +uint8_t WCHNET_SocketSend(uint8_t socketid, uint8_t* buf, uint32_t* len); + +/** + * @brief Socket receives data. + * + * @param socketid - socket id value + * @param *buf - the first address of receive buffer + * @param(in) *len - pointer to the length of the data expected to be read + * + * @param(out) *buf - the first address of data buffer + * @param(out) *len - pointer to the length of the data read actually + * + * @return @ERR_T + */ +uint8_t WCHNET_SocketRecv(uint8_t socketid, uint8_t* buf, uint32_t* len); + +/** + * @brief Get socket interrupt, and clear socket interrupt. + * + * @param socketid - socket id value + * + * @return Sn_INT + */ +uint8_t WCHNET_GetSocketInt(uint8_t socketid); + +/** + * @brief Get the length of the data received by socket. + * + * @param socketid - socket id value + * @param(in) *bufaddr - the first address of receive buffer + * + * @param(out) *bufaddr - the first address of data buffer + * + * @return the length of the data + */ +uint32_t WCHNET_SocketRecvLen(uint8_t socketid, uint32_t* bufaddr); + +/** + * @brief TCP connect. Used in TCP Client mode. + * + * @param socketid - socket id value + * + * @return @ERR_T + */ +uint8_t WCHNET_SocketConnect(uint8_t socketid); + +/** + * @brief TCP listen. Used in TCP SERVER mode. + * + * @param socketid - socket id value + * + * @return @ERR_T + */ +uint8_t WCHNET_SocketListen(uint8_t socketid); + +/** + * @brief Close socket. + * + * @param socketid - socket id value + * @param mode - the way of disconnection.Used in TCP connection. + * @TCP disconnect related codes + * + * @return @ERR_T + */ +uint8_t WCHNET_SocketClose(uint8_t socketid, uint8_t mode); + +/** + * @brief Modify socket receive buffer. + * + * @param socketid - socket id value + * @param bufaddr - Address of the receive buffer + * @param bufsize - Size of the receive buffer + * + * @return None + */ +void WCHNET_ModifyRecvBuf(uint8_t socketid, uint32_t bufaddr, uint32_t bufsize); + +/** + * @brief UDP send, specify the target IP and target port + * + * @param socketid - socket id value + * @param *buf - Address of the sent data + * @param(in) *slen - Address of the sent length + * @param *sip - destination IP address + * @param port - destination port + * + * @param(out) *slen - actual length sent + * + * @return @ERR_T + */ +uint8_t WCHNET_SocketUdpSendTo(uint8_t socketid, uint8_t* buf, uint32_t* slen, uint8_t* sip, uint16_t port); + +/** + * @brief Convert ASCII address to network address. + * + * @param *cp - ASCII address to be converted, such as "192.168.1.2" + * @param(in) *addr - First address of the memory stored in the converted network address + * @param(out) *addr - Converted network address, such as 0xC0A80102 + * @return 0 - Success. Others - Failure. + */ +uint8_t WCHNET_Aton(const char* cp, uint8_t* addr); + +/** + * @brief Convert network address to ASCII address. + * + * @param *ipaddr - socket id value + * + * @return Converted ASCII address + */ +uint8_t* WCHNET_Ntoa(uint8_t* ipaddr); + +/** + * @brief Set socket TTL. + * + * @param socketid - socket id value + * @param ttl - TTL value + * + * @return @ERR_T + */ +uint8_t WCHNET_SetSocketTTL(uint8_t socketid, uint8_t ttl); + +/** + * @brief Start TCP retry sending immediately. + * + * @param socketid - TTL value + * + * @return None + */ +void WCHNET_RetrySendUnack(uint8_t socketid); + +/** + * @brief Query the packets that are not sent successfully. + * + * @param socketid - TTL value + * @param(in) *addrlist - pointer to the address of the address list + * @param lislen - Length of the list + * + * @param(out) *addrlist - Address list of the data packets that are not sent successfully + * + * @return Number of unsent and unacknowledged segments + */ +uint8_t WCHNET_QueryUnack(uint8_t socketid, uint32_t* addrlist, uint16_t lislen); + +/** + * @brief Start DHCP. + * + * @param dhcp - Application layer callback function + * + * @return @ERR_T + */ +uint8_t WCHNET_DHCPStart(dhcp_callback dhcp); + +/** + * @brief Stop DHCP. + * + * @param None + * + * @return @ERR_T + */ +uint8_t WCHNET_DHCPStop(void); + +/** + * @brief Configure DHCP host name. + * + * @param *name - First address of DHCP host name + * + * @return 0 - Success. Others - Failure. + */ +uint8_t WCHNET_DHCPSetHostname(char* name); + +/** + * @brief Initialize the resolver: set up the UDP pcb and configure the default server + * + * @param *dnsip - the IP address of dns server + * @param port - the port number of dns server + * + * @return None + */ +void WCHNET_InitDNS(uint8_t* dnsip, uint16_t port); + +/** + * @brief Stop DNS. + * + * @param None + * + * @return None + */ +void WCHNET_DNSStop(void); + +/** + * Resolve a hostname (string) into an IP address. + * + * @param hostname - the hostname that is to be queried + * @param addr - pointer to a struct ip_addr where to store the address if it is already + * cached in the dns_table (only valid if ERR_OK is returned!) + * @param found - a callback function to be called on success, failure or timeout (only if + * ERR_INPROGRESS is returned!) + * @param arg - argument to pass to the callback function + * + * @return @ERR_T + * WCHNET_ERR_SUCCESS if hostname is a valid IP address string or the host name is already in the local names table. + * ERR_INPROGRESS enqueue a request to be sent to the DNS server for resolution if no errors are present. + */ +uint8_t WCHNET_HostNameGetIp(const char* hostname, uint8_t* addr, dns_callback found, void* arg); + +/** + * @brief Configure KEEP LIVE parameter. + * + * @param *cfg - KEEPLIVE configuration parameter + * + * @return None + */ +void WCHNET_ConfigKeepLive(struct _KEEP_CFG* cfg); + +/** + * @brief Configure socket KEEP LIVE enable. + * + * @param socketid - socket id value + * @param enable - 1: Enabled. 0: Disabled. + * + * @return @ERR_T + */ +uint8_t WCHNET_SocketSetKeepLive(uint8_t socketid, uint8_t enable); + +/** + * @brief Configure PHY state + * + * @param phy_stat - PHY state + * + * @return None + */ +void WCHNET_PhyStatus(uint32_t phy_stat); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/Ubiquitous/XiZi_IIoT/link.mk b/Ubiquitous/XiZi_IIoT/link.mk index 09e643226..97dcbba8f 100644 --- a/Ubiquitous/XiZi_IIoT/link.mk +++ b/Ubiquitous/XiZi_IIoT/link.mk @@ -3,7 +3,7 @@ OBJS := $(shell cat make.obj) $(TARGET): $(OBJS) @echo ------------------------------------------------ @echo link $(TARGET) - @$(CROSS_COMPILE)g++ -o $@ $($(LINK_FLAGS)) $(OBJS) $(LINK_LWIP) $(LINK_MUSLLIB) $(LINK_MONGOOSE) $(LIBCC) + @$(CROSS_COMPILE)g++ -o $@ $($(LINK_FLAGS)) $(OBJS) $(LINK_LWIP) $(LINK_MUSLLIB) $(LINK_MONGOOSE) $(LINK_WCH_NET) $(LIBCC) @echo ------------------------------------------------ @$(CROSS_COMPILE)objcopy -O binary $@ XiZi-$(BOARD)$(COMPILE_TYPE).bin @$(CROSS_COMPILE)objdump -S $@ > XiZi-$(BOARD)$(COMPILE_TYPE).asm From e0de3d1e4691d639595c87672ba0a6da71efe568 Mon Sep 17 00:00:00 2001 From: Allenn Date: Thu, 14 Dec 2023 11:42:19 +0800 Subject: [PATCH 3/3] feat: keep client connect request after timeout --- .../XiZi_IIoT/board/ch32v307vct6/board.c | 2 + .../ethernet/connect_ether.c | 38 +++++-------------- .../ethernet/test/wch_tcp_test.c | 33 ++-------------- .../include/connect_ether.h | 4 +- 4 files changed, 19 insertions(+), 58 deletions(-) diff --git a/Ubiquitous/XiZi_IIoT/board/ch32v307vct6/board.c b/Ubiquitous/XiZi_IIoT/board/ch32v307vct6/board.c index 5ff2e9855..1f36ec74a 100644 --- a/Ubiquitous/XiZi_IIoT/board/ch32v307vct6/board.c +++ b/Ubiquitous/XiZi_IIoT/board/ch32v307vct6/board.c @@ -67,7 +67,9 @@ void InitBoardHardware() InitHwUart(); InstallConsole("uart1", "uart1_drv", "uart1_dev1"); +#ifdef BSP_USING_ETH InitHwEth(); +#endif KPrintf("consle init completed.\n"); KPrintf("board initialization......\n"); diff --git a/Ubiquitous/XiZi_IIoT/board/ch32v307vct6/third_party_driver/ethernet/connect_ether.c b/Ubiquitous/XiZi_IIoT/board/ch32v307vct6/third_party_driver/ethernet/connect_ether.c index ae20610cc..b4c52ba63 100644 --- a/Ubiquitous/XiZi_IIoT/board/ch32v307vct6/third_party_driver/ethernet/connect_ether.c +++ b/Ubiquitous/XiZi_IIoT/board/ch32v307vct6/third_party_driver/ethernet/connect_ether.c @@ -103,28 +103,6 @@ void WCHNET_CreateTcpSocket(uint8_t* DESIP, uint16_t srcport, uint16_t desport, mStopIfError(i); } -/********************************************************************* - * @fn WCHNET_CreateTcpSocketListen - * - * @brief Create TCP Socket for Listening - * - * @return none - */ -void WCHNET_CreateTcpSocketListen(uint16_t srcport, uint8_t* SocketId) -{ - uint8_t i; - SOCK_INF TmpSocketInf; - - memset((void*)&TmpSocketInf, 0, sizeof(SOCK_INF)); - TmpSocketInf.SourPort = srcport; - TmpSocketInf.ProtoType = PROTO_TYPE_TCP; - i = WCHNET_SocketCreat(SocketId, &TmpSocketInf); - KPrintf("SocketIdForListen %d\r\n", *SocketId); - mStopIfError(i); - i = WCHNET_SocketListen(*SocketId); // listen for connections - mStopIfError(i); -} - /********************************************************************* * @fn WCHNET_DataLoopback * @@ -164,9 +142,9 @@ void WCHNET_DataLoopback(uint8_t id) * @param socketid - socket id. * intstat - interrupt status * - * @return none + * @return 0 or TIME_OUT */ -void WCHNET_HandleSockInt(uint8_t socketid, uint8_t intstat) +int WCHNET_HandleSockInt(uint8_t socketid, uint8_t intstat) { uint8_t i; @@ -208,7 +186,9 @@ void WCHNET_HandleSockInt(uint8_t socketid, uint8_t intstat) } } KPrintf("TCP Timeout\r\n"); + return TIME_OUT; } + return 0; } /********************************************************************* @@ -216,9 +196,9 @@ void WCHNET_HandleSockInt(uint8_t socketid, uint8_t intstat) * * @brief Global Interrupt Handle * - * @return none + * @return 0 or SockInt */ -void WCHNET_HandleGlobalInt(void) +int WCHNET_HandleGlobalInt(void) { uint8_t intstat; uint16_t i; @@ -242,10 +222,12 @@ void WCHNET_HandleGlobalInt(void) if (intstat & GINT_STAT_SOCKET) { // socket related interrupt for (i = 0; i < WCHNET_MAX_SOCKET_NUM; i++) { socketint = WCHNET_GetSocketInt(i); - if (socketint) - WCHNET_HandleSockInt(i, socketint); + if (socketint) { + return WCHNET_HandleSockInt(i, socketint); + } } } + return 0; } uint8_t InitHwEth() diff --git a/Ubiquitous/XiZi_IIoT/board/ch32v307vct6/third_party_driver/ethernet/test/wch_tcp_test.c b/Ubiquitous/XiZi_IIoT/board/ch32v307vct6/third_party_driver/ethernet/test/wch_tcp_test.c index a855fc890..298c1b37e 100644 --- a/Ubiquitous/XiZi_IIoT/board/ch32v307vct6/third_party_driver/ethernet/test/wch_tcp_test.c +++ b/Ubiquitous/XiZi_IIoT/board/ch32v307vct6/third_party_driver/ethernet/test/wch_tcp_test.c @@ -9,7 +9,6 @@ uint16_t desport = 1000; // destination port uint16_t srcport = 1000; // source port uint8_t SocketId; -uint8_t SocketIdForListen; // Socket for Listening /********************************************************************* * @fn TCP client @@ -30,7 +29,9 @@ int Tcp_Client(void) /*Query the Ethernet global interrupt, * if there is an interrupt, call the global interrupt handler*/ if (WCHNET_QueryGlobalInt()) { - WCHNET_HandleGlobalInt(); + if (WCHNET_HandleGlobalInt() == TIME_OUT) { + WCHNET_CreateTcpSocket(DESIP, srcport, desport, &SocketId); + } } } } @@ -43,30 +44,4 @@ int test_tcp_client(int argc, char* argv[]) } SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0) | SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN), - test_tcp_client, test_tcp_client, test tcp client); - -int Tcp_Server(void) -{ - WCHNET_CreateTcpSocketListen(srcport, &SocketIdForListen); // Create TCP Socket for Listening - - while (1) { - /*Ethernet library main task function, - * which needs to be called cyclically*/ - WCHNET_MainTask(); - /*Query the Ethernet global interrupt, - * if there is an interrupt, call the global interrupt handler*/ - if (WCHNET_QueryGlobalInt()) { - WCHNET_HandleGlobalInt(); - } - } -} - -int test_tcp_server(int argc, char* argv[]) -{ - KPrintf("TCPServer Test\r\n"); - Tcp_Server(); - return 0; -} - -SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0) | SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN), - test_tcp_server, test_tcp_server, test tcp server); \ No newline at end of file + test_tcp_client, test_tcp_client, test tcp client); \ No newline at end of file diff --git a/Ubiquitous/XiZi_IIoT/board/ch32v307vct6/third_party_driver/include/connect_ether.h b/Ubiquitous/XiZi_IIoT/board/ch32v307vct6/third_party_driver/include/connect_ether.h index 13ee80495..074822c5d 100644 --- a/Ubiquitous/XiZi_IIoT/board/ch32v307vct6/third_party_driver/include/connect_ether.h +++ b/Ubiquitous/XiZi_IIoT/board/ch32v307vct6/third_party_driver/include/connect_ether.h @@ -31,7 +31,9 @@ uint8_t InitHwEth(); void WCHNET_CreateTcpSocket(uint8_t* DESIP, uint16_t srcport, uint16_t desport, uint8_t* SocketId); void WCHNET_CreateTcpSocketListen(uint16_t srcport, uint8_t* SocketId); -void WCHNET_HandleGlobalInt(void); +int WCHNET_HandleGlobalInt(void); + +#define TIME_OUT -1 #ifdef __cplusplus }