From 68e5436aae8d519eef629dfb9447f266e88f75a7 Mon Sep 17 00:00:00 2001 From: wlyu Date: Mon, 14 Mar 2022 18:13:13 +0800 Subject: [PATCH 1/9] remove default config --- Ubiquitous/XiZi/board/ok1052-c/board.c | 13 +++++++++++-- .../XiZi/board/ok1052-c/third_party_driver/Kconfig | 6 +++--- .../board/ok1052-c/third_party_driver/spi/Makefile | 6 +++++- 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/Ubiquitous/XiZi/board/ok1052-c/board.c b/Ubiquitous/XiZi/board/ok1052-c/board.c index 55786560d..7ff852c2b 100644 --- a/Ubiquitous/XiZi/board/ok1052-c/board.c +++ b/Ubiquitous/XiZi/board/ok1052-c/board.c @@ -17,10 +17,10 @@ File name: board.c Description: support imxrt1052-board init function Others: take SDK_2.6.1_MIMXRT1052xxxxB for references -History: +History: 1. Date: 2022-01-25 Author: AIIT XUOS Lab -Modification: +Modification: 1. support imxrt1052-board MPU、clock、memory init 2. support imxrt1052-board uart、semc、sdio driver init 3. support imxrt1052-board I2C, SPI, ADC, RTC driver init @@ -68,9 +68,18 @@ int MountSDCard(void) #include #include + +#ifdef BSP_USING_ADC #include +#endif + +#ifdef BSP_USING_SPI #include +#endif + +#ifdef BSP_USING_RTC #include +#endif #define NVIC_PRIORITYGROUP_0 0x00000007U /*!< 0 bits for pre-emption priority 4 bits for subpriority */ diff --git a/Ubiquitous/XiZi/board/ok1052-c/third_party_driver/Kconfig b/Ubiquitous/XiZi/board/ok1052-c/third_party_driver/Kconfig index ed032333a..0ec9c3aa4 100644 --- a/Ubiquitous/XiZi/board/ok1052-c/third_party_driver/Kconfig +++ b/Ubiquitous/XiZi/board/ok1052-c/third_party_driver/Kconfig @@ -22,7 +22,7 @@ menuconfig BSP_USING_GPIO menuconfig BSP_USING_I2C bool "Using I2C device" - default y + default n select RESOURCES_I2C if BSP_USING_I2C @@ -31,7 +31,7 @@ menuconfig BSP_USING_I2C menuconfig BSP_USING_ADC bool "Using ADC device" - default y + default n select RESOURCES_ADC if BSP_USING_ADC @@ -40,7 +40,7 @@ menuconfig BSP_USING_ADC menuconfig BSP_USING_SPI bool "Using SPI device" - default y + default n select RESOURCES_SPI if BSP_USING_SPI diff --git a/Ubiquitous/XiZi/board/ok1052-c/third_party_driver/spi/Makefile b/Ubiquitous/XiZi/board/ok1052-c/third_party_driver/spi/Makefile index 7eaf163ae..8765fdab1 100755 --- a/Ubiquitous/XiZi/board/ok1052-c/third_party_driver/spi/Makefile +++ b/Ubiquitous/XiZi/board/ok1052-c/third_party_driver/spi/Makefile @@ -1,3 +1,7 @@ -SRC_FILES := fsl_lpspi.c connect_spi.c connect_flash_spi.c +SRC_FILES := fsl_lpspi.c connect_spi.c + +ifeq ($(CONFIG_RESOURCES_SPI_SFUD),y) + SRC_FILES += connect_flash_spi.c +endif include $(KERNEL_ROOT)/compiler.mk From e5d124231c72798f7f77b842cc8c631b79043914 Mon Sep 17 00:00:00 2001 From: wlyu Date: Wed, 16 Mar 2022 18:34:55 +0800 Subject: [PATCH 2/9] support plc socket and optimize LWIP test demo cases --- .../socket_demo/lwip_tcp_socket_demo.c | 24 +- .../socket_demo/lwip_udp_socket_demo.c | 6 +- .../control_app/opcua_demo/opcua_demo.c | 38 +- .../control/plc/interoperability/Kconfig | 6 +- .../control/plc/interoperability/Makefile | 4 + .../plc/interoperability/opcua/open62541.c | 6 +- .../plc/interoperability/opcua/ua_api.c | 7 +- .../plc/interoperability/opcua/ua_api.h | 3 +- .../plc/interoperability/opcua/ua_client.c | 42 +- .../plc/interoperability/opcua/ua_test.c | 6 +- .../plc/interoperability/socket/Kconfig | 11 + .../plc/interoperability/socket/Makefile | 4 + .../plc/interoperability/socket/br_socket.c | 1867 +++++++++++++++++ .../plc/interoperability/socket/plc_socket.c | 322 +++ .../plc/interoperability/socket/plc_socket.h | 67 + Ubiquitous/XiZi/kernel/thread/msgqueue.c | 1 + .../resources/ethernet/LwIP/api/api_lib.c | 2 +- .../resources/ethernet/LwIP/arch/lwipopts.h | 22 +- .../resources/ethernet/LwIP/arch/sys_arch.c | 14 +- .../ethernet/cmd_lwip/lwip_config_demo.c | 16 +- .../ethernet/cmd_lwip/lwip_dhcp_demo.c | 34 +- .../ethernet/cmd_lwip/lwip_ping_demo.c | 2 +- .../ethernet/cmd_lwip/lwip_tcp_demo.c | 50 +- .../ethernet/cmd_lwip/lwip_udp_demo.c | 32 +- .../XiZi/resources/ethernet/cmd_lwip/ping.c | 10 +- .../resources/ethernet/cmd_lwip/tcpecho_raw.c | 4 +- 26 files changed, 2442 insertions(+), 158 deletions(-) create mode 100755 APP_Framework/Framework/control/plc/interoperability/socket/Kconfig create mode 100755 APP_Framework/Framework/control/plc/interoperability/socket/Makefile create mode 100755 APP_Framework/Framework/control/plc/interoperability/socket/br_socket.c create mode 100755 APP_Framework/Framework/control/plc/interoperability/socket/plc_socket.c create mode 100755 APP_Framework/Framework/control/plc/interoperability/socket/plc_socket.h diff --git a/APP_Framework/Applications/connection_app/socket_demo/lwip_tcp_socket_demo.c b/APP_Framework/Applications/connection_app/socket_demo/lwip_tcp_socket_demo.c index d30248442..a09866fa9 100755 --- a/APP_Framework/Applications/connection_app/socket_demo/lwip_tcp_socket_demo.c +++ b/APP_Framework/Applications/connection_app/socket_demo/lwip_tcp_socket_demo.c @@ -67,7 +67,7 @@ static void TCPSocketRecvTask(void *arg) } lw_print("tcp bind success, start to receive.\n"); - lw_pr_info("\n\nLocal Port:%d\n\n", LWIP_LOCAL_PORT); + lw_notice("\n\nLocal Port:%d\n\n", LWIP_LOCAL_PORT); // setup socket fd as listening mode if (listen(fd, 5) != 0 ) @@ -78,7 +78,7 @@ static void TCPSocketRecvTask(void *arg) // accept client connection clientfd = accept(fd, (struct sockaddr *)&tcp_addr, (socklen_t*)&addr_len); - lw_pr_info("client %s connected\n", inet_ntoa(tcp_addr.sin_addr)); + lw_notice("client %s connected\n", inet_ntoa(tcp_addr.sin_addr)); while(1) { @@ -86,8 +86,8 @@ static void TCPSocketRecvTask(void *arg) recv_len = recvfrom(clientfd, recv_buf, TCP_DEMO_BUF_SIZE, 0, (struct sockaddr *)&tcp_addr, &addr_len); if(recv_len > 0) { - lw_pr_info("Receive from : %s\n", inet_ntoa(tcp_addr.sin_addr)); - lw_pr_info("Receive data : %d - %s\n\n", recv_len, recv_buf); + lw_notice("Receive from : %s\n", inet_ntoa(tcp_addr.sin_addr)); + lw_notice("Receive data : %d - %s\n\n", recv_len, recv_buf); } sendto(clientfd, recv_buf, recv_len, 0, (struct sockaddr*)&tcp_addr, addr_len); } @@ -113,7 +113,7 @@ void TCPSocketRecvTest(int argc, char *argv[]) sscanf(argv[1], "%d.%d.%d.%d", &tcp_socket_ip[0], &tcp_socket_ip[1], &tcp_socket_ip[2], &tcp_socket_ip[3]); } - lwip_config_tcp(lwip_ipaddr, lwip_netmask, lwip_gwaddr); + lwip_config_tcp(lwip_ipaddr, lwip_netmask, tcp_socket_ip); sys_thread_new("TCPSocketRecvTask", TCPSocketRecvTask, NULL, LWIP_TASK_STACK_SIZE, LWIP_DEMO_TASK_PRIO); } @@ -133,7 +133,7 @@ static void TCPSocketSendTask(void *arg) if (fd < 0) { lw_print("Socket error\n"); - goto __exit; + return; } struct sockaddr_in tcp_sock; @@ -145,25 +145,23 @@ static void TCPSocketSendTask(void *arg) if (connect(fd, (struct sockaddr *)&tcp_sock, sizeof(struct sockaddr))) { lw_print("Unable to connect\n"); - goto __exit; + closesocket(fd); + return; } lw_print("tcp connect success, start to send.\n"); - lw_pr_info("\n\nTarget Port:%d\n\n", LWIP_TARGET_PORT); + lw_notice("\n\nTarget Port:%d\n\n", LWIP_TARGET_PORT); while (cnt --) { lw_print("Lwip client is running.\n"); snprintf(send_msg, sizeof(send_msg), "TCP test package times %d\r\n", cnt); sendto(fd, send_msg, strlen(send_msg), 0, (struct sockaddr*)&tcp_sock, sizeof(struct sockaddr)); - lw_pr_info("Send tcp msg: %s ", send_msg); + lw_notice("Send tcp msg: %s ", send_msg); MdelayKTask(1000); } -__exit: - if (fd >= 0) - closesocket(fd); - + closesocket(fd); return; } diff --git a/APP_Framework/Applications/connection_app/socket_demo/lwip_udp_socket_demo.c b/APP_Framework/Applications/connection_app/socket_demo/lwip_udp_socket_demo.c index 4dd23b9e2..24a1a374f 100755 --- a/APP_Framework/Applications/connection_app/socket_demo/lwip_udp_socket_demo.c +++ b/APP_Framework/Applications/connection_app/socket_demo/lwip_udp_socket_demo.c @@ -78,8 +78,8 @@ static void UdpSocketRecvTask(void *arg) { memset(recv_buf, 0, UDP_BUF_SIZE); recv_len = recvfrom(socket_fd, recv_buf, UDP_BUF_SIZE, 0, (struct sockaddr *)&server_addr, &addr_len); - lw_pr_info("Receive from : %s\n", inet_ntoa(server_addr.sin_addr)); - lw_pr_info("Receive data : %s\n\n", recv_buf); + lw_notice("Receive from : %s\n", inet_ntoa(server_addr.sin_addr)); + lw_notice("Receive data : %s\n\n", recv_buf); sendto(socket_fd, recv_buf, recv_len, 0, (struct sockaddr*)&server_addr, addr_len); } @@ -151,7 +151,7 @@ static void UdpSocketSendTask(void *arg) { snprintf(send_str, sizeof(send_str), "UDP test package times %d\r\n", cnt); sendto(socket_fd, send_str, strlen(send_str), 0, (struct sockaddr*)&udp_sock, sizeof(struct sockaddr)); - lw_pr_info("Send UDP msg: %s ", send_str); + lw_notice("Send UDP msg: %s ", send_str); MdelayKTask(1000); } diff --git a/APP_Framework/Applications/control_app/opcua_demo/opcua_demo.c b/APP_Framework/Applications/control_app/opcua_demo/opcua_demo.c index c8a0e8d54..e1edcf29a 100755 --- a/APP_Framework/Applications/control_app/opcua_demo/opcua_demo.c +++ b/APP_Framework/Applications/control_app/opcua_demo/opcua_demo.c @@ -11,7 +11,7 @@ */ /** - * @file ua_demo.c + * @file opcua_demo.c * @brief Demo for OpcUa function * @version 1.0 * @author AIIT XUOS Lab @@ -29,7 +29,6 @@ * Definitions ******************************************************************************/ -#define TCP_LOCAL_PORT 4840 #define UA_URL_SIZE 100 #define UA_STACK_SIZE 4096 #define UA_TASK_PRIO 15 @@ -66,18 +65,17 @@ static void UaConnectTestTask(void* arg) UA_ClientConfig_setDefault(config); snprintf(ua_uri, sizeof(ua_uri), "opc.tcp://%d.%d.%d.%d:4840", test_ua_ip[0], test_ua_ip[1], test_ua_ip[2], test_ua_ip[3]); - ua_pr_info("ua uri: %d %s\n", strlen(ua_uri), ua_uri); + ua_notice("ua uri: %d %s\n", strlen(ua_uri), ua_uri); retval = UA_Client_connect(client,ua_uri); if(retval != UA_STATUSCODE_GOOD) { - ua_pr_info("ua: [%s] connected failed %x\n", __func__, retval); + ua_notice("ua: [%s] connected failed %x\n", __func__, retval); UA_Client_delete(client); return; } - ua_pr_info("ua: [%s] connected ok!\n", __func__); - UA_Client_disconnect(client); + ua_notice("ua: [%s] connected ok!\n", __func__); UA_Client_delete(client); } @@ -92,12 +90,13 @@ SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0) | SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN) | void UaBrowserObjectsTestTask(void* param) { + static int test_cnt = 0; UA_Client* client = UA_Client_new(); - ua_pr_info("ua: [%s] start ...\n", __func__); + ua_notice("ua: [%s] start %d ...\n", __func__, test_cnt++); if(client == NULL) { - ua_print("ua: [%s] tcp client null\n", __func__); + ua_error("ua: [%s] tcp client NULL\n", __func__); return; } @@ -107,18 +106,17 @@ void UaBrowserObjectsTestTask(void* param) if(retval != UA_STATUSCODE_GOOD) { - ua_print("ua: [%s] connect failed %#x\n", __func__, retval); + ua_error("ua: [%s] connect failed %#x\n", __func__, retval); UA_Client_delete(client); return; } - ua_print("ua: [%s] connect ok!\n", __func__); - ua_pr_info("--- start read time ---\n", __func__); + ua_notice("--- start read time ---\n", __func__); ua_read_time(client); - ua_pr_info("--- get server info ---\n", __func__); + ua_notice("--- get server info ---\n", __func__); ua_test_browser_objects(client); + /* Clean up */ - UA_Client_disconnect(client); UA_Client_delete(client); /* Disconnects the client internally */ } @@ -130,7 +128,7 @@ void* UaBrowserObjectsTest(int argc, char* argv[]) { if(sscanf(argv[1], "%d.%d.%d.%d", &test_ua_ip[0], &test_ua_ip[1], &test_ua_ip[2], &test_ua_ip[3]) == EOF) { - lw_pr_info("input wrong ip\n"); + lw_notice("input wrong ip\n"); return NULL; } } @@ -147,7 +145,7 @@ SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0) | SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN) | void UaGetInfoTestTask(void* param) { UA_Client* client = UA_Client_new(); - ua_pr_info("ua: [%s] start ...\n", __func__); + ua_notice("ua: [%s] start ...\n", __func__); if(client == NULL) { @@ -167,7 +165,7 @@ void UaGetInfoTestTask(void* param) } ua_print("ua: [%s] connect ok!\n", __func__); - ua_pr_info("--- interactive server ---\n", __func__); + ua_notice("--- interactive server ---\n", __func__); ua_test_interact_server(client); /* Clean up */ UA_Client_disconnect(client); @@ -182,7 +180,7 @@ void* UaGetInfoTest(int argc, char* argv[]) { if(sscanf(argv[1], "%d.%d.%d.%d", &test_ua_ip[0], &test_ua_ip[1], &test_ua_ip[2], &test_ua_ip[3]) == EOF) { - lw_pr_info("input wrong ip\n"); + lw_notice("input wrong ip\n"); return NULL; } } @@ -199,7 +197,7 @@ SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0) | SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN) | void UaAddNodesTask(void* param) { UA_Client* client = UA_Client_new(); - ua_pr_info("ua: [%s] start ...\n", __func__); + ua_notice("ua: [%s] start ...\n", __func__); if(client == NULL) { @@ -219,7 +217,7 @@ void UaAddNodesTask(void* param) } ua_print("ua: [%s] connect ok!\n", __func__); - ua_pr_info("--- add nodes ---\n", __func__); + ua_notice("--- add nodes ---\n", __func__); ua_add_nodes(client); /* Clean up */ UA_Client_disconnect(client); @@ -234,7 +232,7 @@ void* UaAddNodesTest(int argc, char* argv[]) { if(sscanf(argv[1], "%d.%d.%d.%d", &test_ua_ip[0], &test_ua_ip[1], &test_ua_ip[2], &test_ua_ip[3]) == EOF) { - lw_pr_info("input wrong ip\n"); + lw_notice("input wrong ip\n"); return NULL; } } diff --git a/APP_Framework/Framework/control/plc/interoperability/Kconfig b/APP_Framework/Framework/control/plc/interoperability/Kconfig index 66ae5f182..6a7cb0324 100755 --- a/APP_Framework/Framework/control/plc/interoperability/Kconfig +++ b/APP_Framework/Framework/control/plc/interoperability/Kconfig @@ -2,5 +2,9 @@ menuconfig USING_CONTROL_PLC_OPCUA bool "PLC support OPCUA" default y - depends on RESOURCES_LWIP + depends on RESOURCES_LWIP +menuconfig USING_CONTROL_PLC_SOCKET + bool "PLC support SOCKET" + default y + depends on RESOURCES_LWIP diff --git a/APP_Framework/Framework/control/plc/interoperability/Makefile b/APP_Framework/Framework/control/plc/interoperability/Makefile index 7f542f711..d329b91fb 100755 --- a/APP_Framework/Framework/control/plc/interoperability/Makefile +++ b/APP_Framework/Framework/control/plc/interoperability/Makefile @@ -6,6 +6,10 @@ ifeq ($(CONFIG_USING_CONTROL_PLC_OPCUA), y) SRC_DIR += opcua endif +ifeq ($(CONFIG_USING_CONTROL_PLC_SOCKET), y) + SRC_DIR += socket +endif + endif SRC_FILES += interoperability.c diff --git a/APP_Framework/Framework/control/plc/interoperability/opcua/open62541.c b/APP_Framework/Framework/control/plc/interoperability/opcua/open62541.c index 3da01d2b7..fc525aa8d 100755 --- a/APP_Framework/Framework/control/plc/interoperability/opcua/open62541.c +++ b/APP_Framework/Framework/control/plc/interoperability/opcua/open62541.c @@ -68231,11 +68231,11 @@ UA_Log_Stdout_log(void *context, UA_LogLevel level, UA_LogCategory category, // (int)(tOffset / UA_DATETIME_SEC / 36), logLevelNames[level], logCategoryNames[category]); // vprintf(msg, args); - KPrintf("%s/%s" ANSI_COLOR_RESET "\t", + ua_print("%s/%s" ANSI_COLOR_RESET "\t", logLevelNames[level], logCategoryNames[category]); vsnprintf(str, sizeof(str) - 1, msg, args); - KPrintf(msg, str); - KPrintf("\n"); + ua_print(msg, str); + ua_print("\n"); // printf("\n"); fflush(stdout); diff --git a/APP_Framework/Framework/control/plc/interoperability/opcua/ua_api.c b/APP_Framework/Framework/control/plc/interoperability/opcua/ua_api.c index e2e5fdb3e..4512368be 100755 --- a/APP_Framework/Framework/control/plc/interoperability/opcua/ua_api.c +++ b/APP_Framework/Framework/control/plc/interoperability/opcua/ua_api.c @@ -28,7 +28,7 @@ int ua_open(void *dev) param->client = UA_Client_new(); - ua_pr_info("ua: [%s] start ...\n", __func__); + ua_notice("ua: [%s] start ...\n", __func__); if (param->client == NULL) { @@ -39,11 +39,11 @@ int ua_open(void *dev) UA_ClientConfig *config = UA_Client_getConfig(param->client); UA_ClientConfig_setDefault(config); - ua_pr_info("ua: [%s] %d %s\n", __func__, strlen(param->ua_remote_ip), param->ua_remote_ip); + ua_notice("ua: [%s] %d %s\n", __func__, strlen(param->ua_remote_ip), param->ua_remote_ip); UA_StatusCode retval = UA_Client_connect(param->client, param->ua_remote_ip); if(retval != UA_STATUSCODE_GOOD) { - ua_pr_info("ua: [%s] deleted ret %x!\n", __func__, retval); + ua_notice("ua: [%s] deleted ret %x!\n", __func__, retval); return (int)retval; } return EOK; @@ -52,7 +52,6 @@ int ua_open(void *dev) void ua_close(void *dev) { UaParamType *param = (UaParamType *)dev; - UA_Client_disconnect(param->client); UA_Client_delete(param->client); /* Disconnects the client internally */ } diff --git a/APP_Framework/Framework/control/plc/interoperability/opcua/ua_api.h b/APP_Framework/Framework/control/plc/interoperability/opcua/ua_api.h index 903d9c6aa..7da7a4faa 100755 --- a/APP_Framework/Framework/control/plc/interoperability/opcua/ua_api.h +++ b/APP_Framework/Framework/control/plc/interoperability/opcua/ua_api.h @@ -43,8 +43,9 @@ typedef struct UaParam #define ua_print //KPrintf #define ua_trace() //KPrintf("ua: [%s] line %d checked!\n", __func__, __LINE__) -#define ua_pr_info KPrintf +#define ua_notice KPrintf #define ua_debug //KPrintf +#define ua_error KPrintf extern const char *opc_server_url; extern char test_ua_ip[]; diff --git a/APP_Framework/Framework/control/plc/interoperability/opcua/ua_client.c b/APP_Framework/Framework/control/plc/interoperability/opcua/ua_client.c index 65a4ecb43..749025f75 100755 --- a/APP_Framework/Framework/control/plc/interoperability/opcua/ua_client.c +++ b/APP_Framework/Framework/control/plc/interoperability/opcua/ua_client.c @@ -42,7 +42,7 @@ static UA_StatusCode nodeIter(UA_NodeId childId, UA_Boolean isInverse, UA_NodeId } UA_NodeId* parent = (UA_NodeId*)handle; - ua_pr_info("%d, %d --- %d ---> NodeId %d, %d\n", + ua_notice("%d, %d --- %d ---> NodeId %d, %d\n", parent->namespaceIndex, parent->identifier.numeric, referenceTypeId.identifier.numeric, childId.namespaceIndex, childId.identifier.numeric); @@ -81,38 +81,38 @@ void ua_print_value(UA_Variant* val) if(val->type == &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]) { UA_LocalizedText* ptr = (UA_LocalizedText*)val->data; - ua_pr_info("%.*s (Text)\n", ptr->text.length, ptr->text.data); + ua_notice("%.*s (Text)\n", ptr->text.length, ptr->text.data); } else if(val->type == &UA_TYPES[UA_TYPES_UINT32]) { UA_UInt32* ptr = (UA_UInt32*)val->data; - ua_pr_info("%d (UInt32)\n", *ptr); + ua_notice("%d (UInt32)\n", *ptr); } else if(val->type == &UA_TYPES[UA_TYPES_BOOLEAN]) { UA_Boolean* ptr = (UA_Boolean*)val->data; - ua_pr_info("%i (BOOL)\n", *ptr); + ua_notice("%i (BOOL)\n", *ptr); } else if(val->type == &UA_TYPES[UA_TYPES_INT32]) { UA_Int32* ptr = (UA_Int32*)val->data; - ua_pr_info("%d (Int32)\n", *ptr); + ua_notice("%d (Int32)\n", *ptr); } else if(val->type == &UA_TYPES[UA_TYPES_INT16]) { UA_Int16* ptr = (UA_Int16*)val->data; - ua_pr_info("%d (Int16)\n", *ptr); + ua_notice("%d (Int16)\n", *ptr); } else if(val->type == &UA_TYPES[UA_TYPES_STRING]) { UA_String* ptr = (UA_String*)val->data; - ua_pr_info("%*.s (String)\n", ptr->length, ptr->data); + ua_notice("%*.s (String)\n", ptr->length, ptr->data); } else if(val->type == &UA_TYPES[UA_TYPES_DATETIME]) { UA_DateTime* ptr = (UA_DateTime*)val->data; UA_DateTimeStruct dts = UA_DateTime_toStruct(*ptr); - ua_pr_info("%d-%d-%d %d:%d:%d.%03d (Time)\n", + ua_notice("%d-%d-%d %d:%d:%d.%03d (Time)\n", dts.day, dts.month, dts.year, dts.hour, dts.min, dts.sec, dts.milliSec); } } @@ -144,14 +144,14 @@ void ua_print_nodeid(UA_NodeId *node_id) switch(node_id->identifierType) { case UA_NODEIDTYPE_NUMERIC: - ua_pr_info(" NodeID n%d,%d ", node_id->namespaceIndex, node_id->identifier.numeric); + ua_notice(" NodeID n%d,%d ", node_id->namespaceIndex, node_id->identifier.numeric); break; case UA_NODEIDTYPE_STRING: - ua_pr_info(" NodeID n%d,%.*s ", node_id->namespaceIndex, node_id->identifier.string.length, + ua_notice(" NodeID n%d,%.*s ", node_id->namespaceIndex, node_id->identifier.string.length, node_id->identifier.string.data); break; case UA_NODEIDTYPE_BYTESTRING: - ua_pr_info(" NodeID n%d,%s ", node_id->namespaceIndex, node_id->identifier.byteString.data); + ua_notice(" NodeID n%d,%s ", node_id->namespaceIndex, node_id->identifier.byteString.data); break; default: break; @@ -160,7 +160,7 @@ void ua_print_nodeid(UA_NodeId *node_id) void ua_print_object(UA_BrowseResponse* res) { - ua_pr_info("%-9s %-16s %-16s %-16s\n", "NAMESPACE", "NODEID", "BROWSE NAME", "DISPLAY NAME"); + ua_notice("%-9s %-16s %-16s %-16s\n", "NAMESPACE", "NODEID", "BROWSE NAME", "DISPLAY NAME"); for(size_t i = 0; i < res->resultsSize; ++i) { @@ -170,14 +170,14 @@ void ua_print_object(UA_BrowseResponse* res) if(ref->nodeId.nodeId.identifierType == UA_NODEIDTYPE_NUMERIC) { - ua_pr_info("%-9d %-16d %-16.*s %-16.*s\n", ref->nodeId.nodeId.namespaceIndex, + ua_notice("%-9d %-16d %-16.*s %-16.*s\n", ref->nodeId.nodeId.namespaceIndex, ref->nodeId.nodeId.identifier.numeric, (int)ref->browseName.name.length, ref->browseName.name.data, (int)ref->displayName.text.length, ref->displayName.text.data); } else if(ref->nodeId.nodeId.identifierType == UA_NODEIDTYPE_STRING) { - ua_pr_info("%-9d %-16.*s %-16.*s %-16.*s\n", ref->nodeId.nodeId.namespaceIndex, + ua_notice("%-9d %-16.*s %-16.*s %-16.*s\n", ref->nodeId.nodeId.namespaceIndex, (int)ref->nodeId.nodeId.identifier.string.length, ref->nodeId.nodeId.identifier.string.data, (int)ref->browseName.name.length, ref->browseName.name.data, @@ -188,7 +188,7 @@ void ua_print_object(UA_BrowseResponse* res) } } - ua_pr_info("\n"); + ua_notice("\n"); } UA_StatusCode ua_read_array_value(UA_Client* client, int array_size, UA_ReadValueId* array) @@ -203,7 +203,7 @@ UA_StatusCode ua_read_array_value(UA_Client* client, int array_size, UA_ReadValu || (response.resultsSize != array_size)) { UA_ReadResponse_clear(&response); - ua_pr_info("ua: [%s] read failed 0x%x\n", __func__, + ua_notice("ua: [%s] read failed 0x%x\n", __func__, response.responseHeader.serviceResult); return UA_STATUSCODE_BADUNEXPECTEDERROR; } @@ -215,11 +215,11 @@ UA_StatusCode ua_read_array_value(UA_Client* client, int array_size, UA_ReadValu if((response.results[i].status == UA_STATUSCODE_GOOD) && (response.results[i].hasValue)) { - ua_pr_info("node %s: ", ua_get_nodeid_str(&array[i].nodeId)); + ua_notice("node %s: ", ua_get_nodeid_str(&array[i].nodeId)); ua_print_value(&response.results[i].value); } } - ua_pr_info("\n"); + ua_notice("\n"); free(arr_ret); UA_ReadResponse_clear(&response); @@ -229,7 +229,7 @@ UA_StatusCode ua_read_array_value(UA_Client* client, int array_size, UA_ReadValu void ua_browser_id(UA_Client* client, UA_NodeId id) { /* Browse some objects */ - ua_pr_info("Browsing nodes in objects folder:\n"); + ua_notice("Browsing nodes in objects folder:\n"); UA_BrowseRequest bReq; UA_BrowseRequest_init(&bReq); bReq.requestedMaxReferencesPerNode = 0; @@ -327,7 +327,7 @@ void ua_write_nodeid_value(UA_Client* client, UA_NodeId id, char* value) if(wResp.responseHeader.serviceResult == UA_STATUSCODE_GOOD) { - ua_pr_info("write new value is: %s\n", value); + ua_notice("write new value is: %s\n", value); } UA_WriteRequest_clear(&wReq); @@ -489,7 +489,7 @@ void ua_read_time(UA_Client* client) { UA_DateTime raw_date = *(UA_DateTime*) value.data; UA_DateTimeStruct dts = UA_DateTime_toStruct(raw_date); - ua_pr_info("date is: %d-%d-%d %d:%d:%d.%03d\n", + ua_notice("date is: %d-%d-%d %d:%d:%d.%03d\n", dts.day, dts.month, dts.year, dts.hour, dts.min, dts.sec, dts.milliSec); } diff --git a/APP_Framework/Framework/control/plc/interoperability/opcua/ua_test.c b/APP_Framework/Framework/control/plc/interoperability/opcua/ua_test.c index 329118bfc..d11b86896 100755 --- a/APP_Framework/Framework/control/plc/interoperability/opcua/ua_test.c +++ b/APP_Framework/Framework/control/plc/interoperability/opcua/ua_test.c @@ -53,7 +53,7 @@ void ua_test_browser_objects(UA_Client *client) ua_browser_id(client, UA_TEST_BROWSER_NODEID); ua_browser_id(client, UA_TEST_BROWSER_NODEID1); test_id = UA_TEST_BROWSER_NODEID1; - ua_pr_info("Show values in %s:\n", ua_get_nodeid_str(&test_id)); + ua_notice("Show values in %s:\n", ua_get_nodeid_str(&test_id)); ua_test_read_array(client); return; } @@ -64,11 +64,11 @@ void ua_test_write_attr(UA_Client *client) char val_str[UA_NODE_LEN]; UA_NodeId id = UA_TEST_WRITE_NODEID; - ua_pr_info("--- Test write %s ---\n", ua_get_nodeid_str(&id)); + ua_notice("--- Test write %s ---\n", ua_get_nodeid_str(&id)); ua_read_nodeid_value(client, id, &value); ua_write_nodeid_value(client, id, itoa(value + 1, val_str, 10)); ua_read_nodeid_value(client, id, &value); - ua_pr_info("\n"); + ua_notice("\n"); } int ua_test_interact_server(UA_Client *client) diff --git a/APP_Framework/Framework/control/plc/interoperability/socket/Kconfig b/APP_Framework/Framework/control/plc/interoperability/socket/Kconfig new file mode 100755 index 000000000..d8325aa40 --- /dev/null +++ b/APP_Framework/Framework/control/plc/interoperability/socket/Kconfig @@ -0,0 +1,11 @@ + +menuconfig USING_CONTROL_PLC_OPCUA + bool "PLC support OPCUA" + default y + depends on RESOURCES_LWIP + +menuconfig USING_CONTROL_PLC_SOCKET + bool "PLC support SOCKET" + default y + depends on RESOURCES_LWIP + diff --git a/APP_Framework/Framework/control/plc/interoperability/socket/Makefile b/APP_Framework/Framework/control/plc/interoperability/socket/Makefile new file mode 100755 index 000000000..2c3e00b48 --- /dev/null +++ b/APP_Framework/Framework/control/plc/interoperability/socket/Makefile @@ -0,0 +1,4 @@ +SRC_FILES := plc_socket.c + +include $(KERNEL_ROOT)/compiler.mk + diff --git a/APP_Framework/Framework/control/plc/interoperability/socket/br_socket.c b/APP_Framework/Framework/control/plc/interoperability/socket/br_socket.c new file mode 100755 index 000000000..a655a0a67 --- /dev/null +++ b/APP_Framework/Framework/control/plc/interoperability/socket/br_socket.c @@ -0,0 +1,1867 @@ +#include +#include +#include "plc_socket.h" + +#define xs_kprintf KPrintf +#define xs_device_t uint32_t + + +static unsigned char data_head = 0x5A; +static char device_s14[] = "S14"; +static char device_s15[] = "S15"; +static char device_s16[] = "S16"; +static char device_s17[] = "S17"; +static char device_s18[] = "S18"; +static char data_end[] = "#"; +unsigned char redis_data[1024]; + +// 创建一个信号量,用于接收消息的同步 +static sem_t Ch_Sem = NULL; +static sem_t Rx_Sem = NULL; + +extern xs_device_t ec200t; + +//for SIEMENS TCP read data cmd +const unsigned char handshake_first[] = +{ + 0x3, 0x00, 0x00, 0x16, 0x11, 0xe0, 0x00, 0x00, 0x02, + 0xc8,0x00,0xc1,0x02,0x02,0x01,0xc2,0x02,0x02,0x01,0xc0,0x01,0x0a +}; + +const unsigned char handshake_second[] = +{ + 0x3,0x00,0x00,0x19,0x02,0xf0,0x80,0x32,0x01,0x00,0x00,0x00,0x0d,0x00,0x08,0x00,0x00,0xf0,0x00,0x00,0x01,0x00,0x01,0x00,0xf0 +}; + +const unsigned char siemens_read_data[] = +{ + 0x3,0x00,0x00, 0x1f,0x02,0xf0,0x80,0x32,0x01,0x00,0x00,0x33,0x01,0x00,0x0e,0x00,0x00,0x04,0x01,0x12,0x0a,0x10,0x02,0x00,0xD2,0x00,0x34,0x84,0x00,0x00,0x00 +}; + +//for OML UDP read data cmd +const unsigned char UDP_read_data[] = +{ + 0x80,0x00,0x02,0x00,0x03,0x00,0x00,0x7E,0x00,0x00,0x01,0x01,0x82,0x0F,0xA0,0x00,0x00,0x20 +}; + +//for SIEMENS 1200 read data cmd +const unsigned char handshake_1200_first[] = +{ + 0x03, 0x00, 0x00, 0x16, 0x11, 0xE0, 0x00, 0x00, 0x02, 0xC8, 0x00, 0xC1, 0x02, 0x02, 0x01, 0xC2, 0x02, 0x02, 0x01, 0xC0, 0x01, 0x0A +}; + +const unsigned char handshake_1200_second[] = +{ + 0x03, 0x00, 0x00, 0x19, 0x02, 0xF0, 0x80, 0x32, 0x01, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x08, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0xF0 +}; + +const unsigned char siemens_1200_read_data[] = +{ + 0x03, 0x00, 0x00, 0x1F, 0x02, 0xF0, 0x80, 0x32, 0x01, 0x00, 0x00, 0x33, 0x01, 0x00, 0x0E, 0x00, 0x00, 0x04, 0x01, 0x12, 0x0A, 0x10, 0x02, 0x00, 0xD2, 0x00, 0x34, 0x84, 0x00, 0x00, 0x00 +}; + +#define PUT_ULONG_BE(n,b,i) \ +{ \ + (b)[(i) ] = (uint8_t) ( (n) >> 24 ); \ + (b)[(i) + 1] = (uint8_t) ( (n) >> 16 ); \ + (b)[(i) + 2] = (uint8_t) ( (n) >> 8 ); \ + (b)[(i) + 3] = (uint8_t) ( (n) ); \ +} + +#define GET_ULONG_BE(n,b,i) \ +{ \ + (n) = ( (uint32_t) (b)[(i) ] << 24 ) \ + | ( (uint32_t) (b)[(i) + 1] << 16 ) \ + | ( (uint32_t) (b)[(i) + 2] << 8 ) \ + | ( (uint32_t) (b)[(i) + 3] ); \ +} + +#define XS_SERIAL_RB_BUFSZ 128 + +char rx_buffer_cmp[256 + 1]; +char msg_pool[256]; +char rx_buffer[XS_SERIAL_RB_BUFSZ + 1] = {0}; +#define WIFI_RESET 73 + +#define LEN_PARA_BUF 128 +uint8_t server_addr_wifi[LEN_PARA_BUF] = "192.168.23.181"; //??????? +uint8_t server_port_wifi[LEN_PARA_BUF] = "9999"; //?????? + +uint8_t WIFI_ssid[LEN_PARA_BUF] = "xiaoshanos"; //WIFI? +uint8_t WIFI_pwd[LEN_PARA_BUF] = "12345678"; //WIFI???? + +#if 0 +/* ?????? */ +xs_device_t hfa21; +/* ??????? */ +struct xs_MessageQueue* rx3_mq; +/* ???????? */ + +void hfa21_sta_config(void) +{ + xs_kprintf("hfa21_sta_config start\r\n"); + uint8_t i,step; + uint8_t cmd[LEN_PARA_BUF]; + step = 1; + strcpy(cmd, "+++"); + //send + xs_DeviceWrite(hfa21, 0, cmd, 3); + xs_kprintf("cmd.%d=%s\r\n",step,cmd); + step = 2; + memset(cmd,0,sizeof(cmd)); + xs_MdelayKTask(100); + strcpy(cmd,"a"); + //send + xs_DeviceWrite(hfa21, 0, cmd, 1); + xs_kprintf("cmd.%d=%s\r\n",step,cmd); + step = 3; + memset(cmd,0,sizeof(cmd)); + xs_MdelayKTask(2500); + strcpy(cmd, "AT+FCLR\r\n"); + + //send + for(i=0; i 1000) + { + xs_kprintf("data length too long\n "); + return; + } + + xs_DeviceWrite(hfa21, 0, buf, len); + return ; +} + +void msg_send_once(void) +{ + uint8_t msg[1024] = { 0}; + uint32_t i = 0; + xs_kprintf("ap test, send msg to server : "); + memset(msg,0x37,1024); + xs_memcpy(msg,"arm:dev7,data:",14); + msg[1023] = '\n'; + xs_DeviceWrite(hfa21,0, msg, 1024); + // xs_MdelayKTask(1000); + return ; +} +MSH_CMD_EXPORT(msg_send_once, asd); + +void msg_send_nointerval(void) +{ + uint8_t msg[1024] = { 0}; + uint32_t i = 1; + uint32_t index = 0; + uint8_t seq[10] = { 0}; + uint8_t* prefix = "{board:stm32f407-st-discovery,arch:arm,device:7,seq:"; + xs_kprintf("ap test, send msg to server : "); + + for(;;) + { + index = 0; + memset(msg,0x37,1024); + memset(seq,0,10); + msg[1021] = '}'; + msg[1022] = '\n'; + msg[1023] = 0; + xs_memcpy(msg,prefix,strlen(prefix)); + index = index + strlen(prefix); + PUT_ULONG_BE(i, msg, index) + index = index + 4; + xs_memcpy(msg+index,",data:",6); + xs_DeviceWrite(hfa21,0, msg, 1024); + ++ i; + } + + return ; +} +MSH_CMD_EXPORT(msg_send_nointerval, asd); + +void msg_send_interval(void) +{ + uint8_t msg[1024] = { 0}; + uint32_t i = 1; + uint32_t index = 0; + uint8_t seq[4] = { 0}; + uint8_t* prefix = "{board:stm32f407-st-discovery,arch:arm,device:7,seq:"; + xs_kprintf("ap test, send msg to server : "); + + for(;;) + { + index = 0; + memset(msg,0x37,1024); + memset(seq,0,10); + msg[1021] = '}'; + msg[1022] = '\n'; + msg[1023] = 0; + xs_memcpy(msg,prefix,strlen(prefix)); + index = index + strlen(prefix); + PUT_ULONG_BE(i, msg, index) + index = index + 4; + xs_memcpy(msg+index,",data:",6); + xs_DeviceWrite(hfa21,0, msg, 1024); + xs_MdelayKTask(1000); + ++ i; + } + + return ; +} +MSH_CMD_EXPORT(msg_send_interval, asd); + +struct xs_Ringbuffer* ringbuffer; + +uint8_t stack[256] = {0}; +xs_uint16 data_index = 0 ; +uint8_t start = 0xaa; +uint8_t end = 0xbb; + +uint8_t lora_buffer[8][256] = {0}; + +static void read_ringbuffer_thread_entry(void* parameter) +{ + uint8_t ch = 0; + xs_uint16 index = 0 ; + uint8_t devicenumber; + + while(1) + { + if(1 == xs_GetRingBufferchar(ringbuffer,&ch)) + { + if(data_index < 256) + { + stack[data_index++] = ch; + + if(data_index > 2) + { + if(stack[data_index-1] == start && stack[data_index-2] == start) + { + data_index = 0; + stack[data_index++] == start; + stack[data_index++] == start; + } + else if(stack[data_index-1] == end && stack[data_index-2] == end) + { + //end + devicenumber = stack[3] - 0x30; + + if(devicenumber > 0 && devicenumber < 8) + { + memset(lora_buffer[devicenumber-1],0,256); + memcpy(lora_buffer[devicenumber-1], &stack[2],data_index - 4); + // xs_kprintf("lora data: %s\n",lora_buffer[devicenumber-1]); + } + + data_index = 0; + } + } + } + else + { + data_index = 0; + } + } + else + { + xs_MdelayKTask(20); + } + } +} +#endif + +#if 0 +/***********************************************************************/ +//欧姆龙PLC IP 192.168.250.3 port 9600 + +static xs_err_t oml_uart_input(xs_device_t dev, xs_size_t size) +{ + xs_KSemaphoreAbandon(Ch_Sem); + return XS_EOK; +} + +static void oml_plc_thread(void* parameter) +{ + xs_err_t ret; + uint32_t rx_length, total_rx_length = 0; + uint8_t i; + unsigned int length = 0; + + while(1) + { + ret = xs_KSemaphoreObtain(Ch_Sem, XS_WAITING_FOREVER); + + if(XS_EOK == ret) + { + rx_length = xs_DeviceRead(hfa21, 0, redis_data + 1 + sizeof(device_s14) + total_rx_length, 78); + xs_kprintf("dst data length %d total length %d\n", rx_length, total_rx_length); + + for(i = 0; i < rx_length; ++i) + { + xs_kprintf("0x%x ", redis_data[i + 1 + sizeof(device_s14) + total_rx_length]); + } + + xs_kprintf("\n"); + total_rx_length += rx_length; + + if((78 == total_rx_length) && (0xC0 == redis_data[1 + sizeof(device_s14)]) + && (0x00 == redis_data[1 + sizeof(device_s14) + 1]) + && (0x02 == redis_data[1 + sizeof(device_s14) + 2]) + && (0x00 == redis_data[1 + sizeof(device_s14) + 3])) + { + /******format redis data******/ + memcpy(redis_data, &data_head, 1); + memcpy(redis_data + 1, device_s14, sizeof(device_s14)); + memcpy(redis_data + 1 + sizeof(device_s14) + total_rx_length, data_end, sizeof(data_end)); + length = 1 + sizeof(device_s14) + total_rx_length + sizeof(data_end); + /******end******/ + xs_DeviceWrite(ec200t, 0, redis_data, length); + total_rx_length = 0; + memset(redis_data, 0, sizeof(redis_data)); + xs_KSemaphoreAbandon(Rx_Sem); + } + } + } +} + +int OML_UDP(void) +{ + struct SerialConfigure config3 = XS_SERIAL_CONFIG_DEFAULT; + hfa21 = xs_DeviceFind("uart3"); + + if(!hfa21) + { + xs_kprintf("find dev.hfa21 failed!\r\n"); + return XS_ERROR; + } + + Ch_Sem = xs_KCreateSemaphore("Ch_Sem",0,XS_LINKLIST_FLAG_FIFO); + + if(Ch_Sem == NULL) + { + xs_kprintf("create Ch_sem failed .\n"); + return XS_ERROR; + } + + Rx_Sem = xs_KCreateSemaphore("Rx_Sem",0,XS_LINKLIST_FLAG_FIFO); + + if(Rx_Sem == NULL) + { + xs_kprintf("create Rx_sem failed .\n"); + return XS_ERROR; + } + + //hfa21 + config3.BaudRate = BAUD_RATE_460800; + config3.DataBits = DATA_BITS_8; + config3.StopBits = STOP_BITS_1; + config3.bufsz = XS_SERIAL_RB_BUFSZ; + config3.parity = PARITY_NONE; + xs_DeviceControl(hfa21, XS_DEVICE_CTRL_CONFIG, &config3); + xs_DeviceOpen(hfa21,XS_DEVICE_FLAG_DMA_RX); + xs_DeviceSetRxIndicate(hfa21, oml_uart_input); + xs_kprintf("hfa21 init success!\r\n"); + TaskDescriptorPointer thread_oml_plc = xs_KTaskCreate("oml_hfa21", oml_plc_thread, NULL, 1024, 25); + + if(thread_oml_plc != NULL) + { + xs_StartupKTask(thread_oml_plc); + } + else + { + return XS_ERROR; + } + + xs_MdelayKTask(10000); + xs_kprintf("The UDP send_receive function is running......\n"); + + while(1) + { +// CH438UARTSend(6, UDP_read_data, sizeof(UDP_read_data)); //UDP_read_data + xs_DeviceWrite(hfa21, 0, UDP_read_data, sizeof(UDP_read_data)); + xs_kprintf("hfa21 write cmd\n"); + xs_KSemaphoreObtain(Rx_Sem, XS_WAITING_FOREVER); + xs_MdelayKTask(1000); + } + + return XS_EOK; +} +MSH_CMD_EXPORT(OML_UDP, oml); + +/*********************************************************************************************/ +// IP 192.168.250.50 port 102 tcp + +//工控机的测试代码,数据帧头FF,帧尾FE,数据帧长度不固?static struct xs_MessageQueue *ipc_mq; +xs_device_t ipc_hfa21; + +static xs_err_t ipc_uart_input(xs_device_t dev, xs_size_t size) +{ + struct rx_msg msg; + xs_err_t result; + msg.dev = dev; + msg.size = size; + result = xs_KMsgQueueSend(ipc_mq, &msg, sizeof(msg)); + + if(result == -XS_EFULL) + { + xs_kprintf("ipc_mq message queue full!\r\n"); + } + + xs_KSemaphoreAbandon(Ch_Sem); + return result; +} + +static void ipc_plc_thread(void* parameter) +{ + struct rx_msg msg; + xs_err_t ret; + uint32_t rx_length, total_rx_length = 0; + uint8_t i; + unsigned int length = 0; + + while(1) + { + ret = xs_KSemaphoreObtain(Ch_Sem, XS_WAITING_FOREVER); + + if(XS_EOK == ret) + { + xs_memset(&msg, 0, sizeof(msg)); + ret = xs_KMsgQueueRecv(ipc_mq, &msg, sizeof(msg), XS_WAITING_FOREVER); + + if(XS_EOK == ret) + { + rx_length = xs_DeviceRead(ipc_hfa21, 0, redis_data + 2 + sizeof(device_s15) + total_rx_length, msg.size); + xs_kprintf("dst data length %d total length %d\n", rx_length, total_rx_length); + + for(i = 0; i < rx_length; ++i) + { + xs_kprintf("0x%x ", redis_data[i + 2 + sizeof(device_s15) + total_rx_length]); + } + + xs_kprintf("\n"); + total_rx_length += rx_length; + + if((0x46 == redis_data[2 + sizeof(device_s15)]) && (0x46 == redis_data[2 + sizeof(device_s15) + 1]) && + (0x45 == redis_data[2 + sizeof(device_s15) + total_rx_length - 1]) && (0x46 == redis_data[2 + sizeof(device_s15) + total_rx_length - 2])) + { + /******format redis data******/ + redis_data[0] = data_head; + redis_data[1] = PLC_IPC_FLAG; + memcpy(redis_data + 2, device_s15, sizeof(device_s15)); + length = 2 + sizeof(device_s15) + total_rx_length; + /******end******/ + // xs_kprintf("redis data : \n"); + // for(i = 0; i < length; ++i) + // xs_kprintf("0x%x ", redis_data[i]); + // xs_kprintf("\n"); + xs_DeviceWrite(ec200t, 0, redis_data, length); + total_rx_length = 0; + memset(redis_data, 0, sizeof(redis_data)); + xs_KSemaphoreAbandon(Rx_Sem); + } + } + } + } +} + +int BRL_IPC(void) +{ + struct SerialConfigure config3 = XS_SERIAL_CONFIG_DEFAULT; + ipc_hfa21 = xs_DeviceFind("uart3"); + + if(!ipc_hfa21) + { + xs_kprintf("find ipc_hfa21 failed!\r\n"); + return XS_ERROR; + } + + Ch_Sem = xs_KCreateSemaphore("Ch_Sem",0,XS_LINKLIST_FLAG_FIFO); + + if(Ch_Sem == NULL) + { + xs_kprintf("BRL_IPC create sem failed .\n"); + return XS_ERROR; + } + + Rx_Sem = xs_KCreateSemaphore("Rx_Sem",0,XS_LINKLIST_FLAG_FIFO); + + if(Rx_Sem == NULL) + { + xs_kprintf("BRL_IPC create Rx_sem failed .\n"); + return XS_ERROR; + } + + ipc_mq = xs_KCreateMsgQueue("ipc_mq", + sizeof(struct rx_msg), + sizeof(msg_pool), + XS_LINKLIST_FLAG_FIFO); + + if(ipc_mq == NULL) + { + xs_kprintf("create ipc_mq mutex failed.\n"); + return -1; + } + + //ipc_hfa21 + config3.BaudRate = BAUD_RATE_460800; + config3.DataBits = DATA_BITS_8; + config3.StopBits = STOP_BITS_1; + config3.bufsz = XS_SERIAL_RB_BUFSZ; + config3.parity = PARITY_NONE; + xs_DeviceControl(ipc_hfa21, XS_DEVICE_CTRL_CONFIG, &config3); + xs_DeviceOpen(ipc_hfa21,XS_DEVICE_FLAG_DMA_RX); + xs_DeviceSetRxIndicate(ipc_hfa21, ipc_uart_input); + xs_kprintf("ipc_hfa21 init success!\r\n"); + TaskDescriptorPointer thread_ipc_plc = xs_KTaskCreate("ipc_hfa21", ipc_plc_thread, NULL, 1024, 25); + + if(thread_ipc_plc != NULL) + { + xs_StartupKTask(thread_ipc_plc); + } + else + { + return XS_ERROR; + } + + xs_kprintf("start to receive data...\n"); + + while(1) + { + xs_MdelayKTask(100); + xs_KSemaphoreObtain(Rx_Sem, XS_WAITING_FOREVER); + xs_kprintf("\n"); + } + + return XS_EOK; +} +MSH_CMD_EXPORT(BRL_IPC, IPC server); + +/*********************************************************************************************/ +// IP 192.168.250.150 port 9898 tcp + +//金凤工控机的测试代码,数据帧头FF,帧尾EF +xs_device_t jf_ipc_hfa21; +static sem_t jf_input_sem = NULL; +static sem_t jf_ec200t_sem = NULL; + +static xs_err_t jf_ipc_uart_input(xs_device_t dev, xs_size_t size) +{ + xs_KSemaphoreAbandon(jf_input_sem); + return XS_EOK; +} + +static void jf_ipc_plc_thread(void* parameter) +{ + xs_err_t ret; + uint32_t rx_length, total_rx_length = 0; + uint8_t i; + unsigned int length = 0; + + while(1) + { + ret = xs_KSemaphoreObtain(jf_input_sem, XS_WAITING_FOREVER); + + if(XS_EOK == ret) + { + rx_length = xs_DeviceRead(jf_ipc_hfa21, 0, redis_data + 2 + sizeof(device_s16) + total_rx_length, 512); + xs_kprintf("dst data length %d total length %d\n", rx_length, total_rx_length); + + for(i = 0; i < rx_length; ++i) + { + xs_kprintf("0x%x ", redis_data[i + 2 + sizeof(device_s16) + total_rx_length]); + } + + xs_kprintf("\n"); + total_rx_length += rx_length; + + if(((((unsigned int)redis_data[2 + sizeof(device_s16) + 2] & 0x000000FF) << 8) | ((unsigned int)redis_data[2 + sizeof(device_s16) + 3] & 0x000000FF) == total_rx_length) && + (0xFF == redis_data[2 + sizeof(device_s16)]) && (0x20 == redis_data[2 + sizeof(device_s16) + 1])) + { + /******format redis data******/ + redis_data[0] = data_head; + redis_data[1] = PLC_JF_IPC_FLAG; + memcpy(redis_data + 2, device_s16, sizeof(device_s16)); + length = 2 + sizeof(device_s16) + total_rx_length; + /******end******/ + xs_DeviceWrite(ec200t, 0, redis_data, length); + total_rx_length = 0; + memset(redis_data, 0, sizeof(redis_data)); + xs_KSemaphoreAbandon(jf_ec200t_sem); + } + } + } +} + +int JF_IPC(void) +{ + struct SerialConfigure config3 = XS_SERIAL_CONFIG_DEFAULT; + jf_ipc_hfa21 = xs_DeviceFind("uart3"); + + if(!jf_ipc_hfa21) + { + xs_kprintf("find jf_ipc_hfa21 failed!\r\n"); + return XS_ERROR; + } + + jf_ec200t_sem = xs_KCreateSemaphore("jf_ec200t_sem",0,XS_LINKLIST_FLAG_FIFO); + + if(jf_ec200t_sem == NULL) + { + xs_kprintf("JF_IPC create jf_ec200t_sem failed .\n"); + return XS_ERROR; + } + + jf_input_sem = xs_KCreateSemaphore("jf_input_sem",0,XS_LINKLIST_FLAG_FIFO); + + if(jf_input_sem == NULL) + { + xs_kprintf("JF_IPC create jf_input_sem failed .\n"); + return XS_ERROR; + } + + //jf_ipc_hfa21 + config3.BaudRate = BAUD_RATE_460800; + config3.DataBits = DATA_BITS_8; + config3.StopBits = STOP_BITS_1; + config3.bufsz = XS_SERIAL_RB_BUFSZ; + config3.parity = PARITY_NONE; + xs_DeviceControl(jf_ipc_hfa21, XS_DEVICE_CTRL_CONFIG, &config3); + xs_DeviceOpen(jf_ipc_hfa21,XS_DEVICE_FLAG_DMA_RX); + xs_DeviceSetRxIndicate(jf_ipc_hfa21, jf_ipc_uart_input); + xs_kprintf("jf_ipc_hfa21 init success!\r\n"); + TaskDescriptorPointer thread_jf_ipc_plc = xs_KTaskCreate("jf_ipc_hfa21", jf_ipc_plc_thread, NULL, 1024, 25); + + if(thread_jf_ipc_plc != NULL) + { + xs_StartupKTask(thread_jf_ipc_plc); + } + else + { + return XS_ERROR; + } + + xs_kprintf("start to receive data...\n"); + + while(1) + { + xs_MdelayKTask(100); + xs_KSemaphoreObtain(jf_ec200t_sem, XS_WAITING_FOREVER); + xs_kprintf("JF send data to server done\n"); + } + + return XS_EOK; +} +MSH_CMD_EXPORT(JF_IPC, JF IPC client); + +/*********************************************************************************************/ +//西门子PLC IP 192.168.250.9 port 102 + +static xs_device_t siemens_hfa21; +static sem_t siemens_input_sem = NULL; +static sem_t siemens_ec200t_sem = NULL; + +static xs_err_t siemens_uart_input(xs_device_t dev, xs_size_t size) +{ + xs_KSemaphoreAbandon(siemens_input_sem); + return XS_EOK; +} + +static void siemens_plc_thread(void* parameter) +{ + xs_err_t ret; + uint32_t rx_length, total_rx_length = 0; + uint8_t i; + static char shakehand_cnt = 0; + unsigned int length = 0; + + while(1) + { + ret = xs_KSemaphoreObtain(siemens_input_sem, XS_WAITING_FOREVER); + + if(XS_EOK == ret) + { + //rx_length = xs_DeviceRead(siemens_hfa21, 0, redis_data, 234); + // xs_kprintf("siemens dst data length %d\n", rx_length); + // for(i = 0; i < rx_length; ++i) + // xs_kprintf("0x%x ", redis_data[i]); + // xs_kprintf("\n"); + if(shakehand_cnt < 2) + { + //ignore first two siemens input sem + xs_DeviceRead(siemens_hfa21, 0, redis_data + 2 + sizeof(device_s17), 87); + shakehand_cnt++; + continue; + } + + rx_length = xs_DeviceRead(siemens_hfa21, 0, redis_data + 2 + sizeof(device_s17) + total_rx_length, 87); + xs_kprintf("siemens dst data length %d total length %d\n", rx_length, total_rx_length); + + for(i = 0; i < rx_length; ++i) + { + xs_kprintf("0x%x ", redis_data[i + 2 + sizeof(device_s17) + total_rx_length]); + } + + xs_kprintf("\n"); + total_rx_length += rx_length; + + if((87 == total_rx_length) && (0x03 == redis_data[2 + sizeof(device_s17)]) + && (0x00 == redis_data[2 + sizeof(device_s17) + 1]) + && (0x00 == redis_data[2 + sizeof(device_s17) + 2]) + && (0x57 == redis_data[2 + sizeof(device_s17) + 3])) + { + /******format redis data******/ + redis_data[0] = data_head; + redis_data[1] = PLC_SIEMENS_FLAG; + memcpy(redis_data + 2, device_s17, sizeof(device_s17)); + length = 2 + sizeof(device_s17) + total_rx_length; + /******end******/ + xs_DeviceWrite(ec200t, 0, redis_data, length); + total_rx_length = 0; + memset(redis_data, 0, sizeof(redis_data)); + xs_KSemaphoreAbandon(siemens_ec200t_sem); + } + } + } +} + +int SIEMENS_TCP(void) +{ + xs_err_t result; + struct SerialConfigure config3 = XS_SERIAL_CONFIG_DEFAULT; + siemens_hfa21 = xs_DeviceFind("uart3"); + + if(!siemens_hfa21) + { + xs_kprintf("find siemens_hfa21 failed!\r\n"); + return XS_ERROR; + } + + siemens_input_sem = xs_KCreateSemaphore("siemens_input_sem", 0, XS_LINKLIST_FLAG_FIFO); + + if(siemens_input_sem == NULL) + { + xs_kprintf("siemens_hfa21 create siemens_input_sem failed.\n"); + return XS_ERROR; + } + + siemens_ec200t_sem = xs_KCreateSemaphore("siemens_ec200t_sem", 0, XS_LINKLIST_FLAG_FIFO); + + if(siemens_ec200t_sem == NULL) + { + xs_kprintf("siemens_hfa21 create siemens_ec200t_sem failed.\n"); + return XS_ERROR; + } + + //siemens_hfa21 + config3.BaudRate = BAUD_RATE_460800; + config3.DataBits = DATA_BITS_8; + config3.StopBits = STOP_BITS_1; + config3.bufsz = XS_SERIAL_RB_BUFSZ; + config3.parity = PARITY_NONE; + xs_DeviceControl(siemens_hfa21, XS_DEVICE_CTRL_CONFIG, &config3); + xs_DeviceOpen(siemens_hfa21, XS_DEVICE_FLAG_DMA_RX); + xs_DeviceSetRxIndicate(siemens_hfa21, siemens_uart_input); + TaskDescriptorPointer thread_siemens_plc = xs_KTaskCreate("siemens_hfa21", siemens_plc_thread, NULL, 1024, 25); + + if(thread_siemens_plc != NULL) + { + xs_StartupKTask(thread_siemens_plc); + } + else + { + return XS_ERROR; + } + + //step1 : send handshake_first cmd, waiting for response sem + xs_kprintf("siemens_hfa21 start send handshake_first!\r\n"); + //CH438UARTSend(6, handshake_first, sizeof(handshake_first)); + xs_DeviceWrite(siemens_hfa21, 0, handshake_first, sizeof(handshake_first)); + xs_MdelayKTask(3000); + xs_DeviceWrite(siemens_hfa21, 0, handshake_first, sizeof(handshake_first)); + xs_MdelayKTask(500); + //step2 : send handshake_second cmd, waiting for response sem + xs_kprintf("siemens_hfa21 start send handshake_second!\r\n"); + //CH438UARTSend(6, handshake_second, sizeof(handshake_second)); + xs_DeviceWrite(siemens_hfa21, 0, handshake_second, sizeof(handshake_second)); + xs_kprintf("siemens_hfa21 init success!\r\n"); + xs_MdelayKTask(500); + + while(1) + { + //CH438UARTSend(6, siemens_read_data, sizeof(siemens_read_data)); //read_data + xs_DeviceWrite(siemens_hfa21, 0, siemens_read_data, sizeof(siemens_read_data)); + xs_kprintf("siemens hfa21 write cmd\n"); + xs_KSemaphoreObtain(siemens_ec200t_sem, XS_WAITING_FOREVER); + xs_MdelayKTask(1000); + } + + return XS_EOK; +} +MSH_CMD_EXPORT(SIEMENS_TCP, Siemens TCP PLC); + +/*********************************************************************************************/ +//西门?1200 PLC IP 192.168.250.107 port 102 + +static xs_device_t siemens_1200_hfa21; +static sem_t siemens_1200_input_sem = NULL; +static sem_t siemens_1200_ec200t_sem = NULL; + +static xs_err_t siemens_1200_uart_input(xs_device_t dev, xs_size_t size) +{ + xs_KSemaphoreAbandon(siemens_1200_input_sem); + return XS_EOK; +} + +static void siemens_1200_plc_thread(void* parameter) +{ + xs_err_t ret; + uint32_t rx_length, total_rx_length = 0; + uint8_t i; + static char shakehand_cnt = 0; + unsigned int length = 0; + + while(1) + { + ret = xs_KSemaphoreObtain(siemens_1200_input_sem, XS_WAITING_FOREVER); + + if(XS_EOK == ret) + { + if(shakehand_cnt < 2) + { + //ignore first two siemens input sem + xs_DeviceRead(siemens_1200_hfa21, 0, redis_data + 2 + sizeof(device_s18), 235); + shakehand_cnt++; + continue; + } + + rx_length = xs_DeviceRead(siemens_1200_hfa21, 0, redis_data + 2 + sizeof(device_s18) + total_rx_length, 235); + xs_kprintf("siemens 1200 dst data length %d total length %d\n", rx_length, total_rx_length); + + for(i = 0; i < rx_length; ++i) + { + xs_kprintf("0x%x ", redis_data[i + 2 + sizeof(device_s18) + total_rx_length]); + } + + xs_kprintf("\n"); + total_rx_length += rx_length; + + if((235 == total_rx_length) && (0x03 == redis_data[2 + sizeof(device_s18)]) + && (0x00 == redis_data[2 + sizeof(device_s18) + 1]) + && (0x00 == redis_data[2 + sizeof(device_s18) + 2]) + && (0xEB == redis_data[2 + sizeof(device_s18) + 3])) + { + /******format redis data******/ + redis_data[0] = data_head; + redis_data[1] = PLC_SIEMENS_1200_FLAG; + memcpy(redis_data + 2, device_s18, sizeof(device_s18)); + length = 2 + sizeof(device_s18) + total_rx_length; + /******end******/ + xs_DeviceWrite(ec200t, 0, redis_data, length); + total_rx_length = 0; + memset(redis_data, 0, sizeof(redis_data)); + xs_KSemaphoreAbandon(siemens_1200_ec200t_sem); + } + } + } +} + +int SIEMENS_1200(void) +{ + xs_err_t result; + struct SerialConfigure config3 = XS_SERIAL_CONFIG_DEFAULT; + siemens_1200_hfa21 = xs_DeviceFind("uart3"); + + if(!siemens_1200_hfa21) + { + xs_kprintf("find siemens_1200_hfa21 failed!\r\n"); + return XS_ERROR; + } + + siemens_1200_input_sem = xs_KCreateSemaphore("siemens_1200_input_sem", 0, XS_LINKLIST_FLAG_FIFO); + + if(siemens_1200_input_sem == NULL) + { + xs_kprintf("siemens_1200_hfa21 create siemens_1200_input_sem failed.\n"); + return XS_ERROR; + } + + siemens_1200_ec200t_sem = xs_KCreateSemaphore("siemens_1200_ec200t_sem", 0, XS_LINKLIST_FLAG_FIFO); + + if(siemens_1200_ec200t_sem == NULL) + { + xs_kprintf("siemens_1200_hfa21 create siemens_1200_ec200t_sem failed.\n"); + return XS_ERROR; + } + + //siemens_hfa21 + config3.BaudRate = BAUD_RATE_460800; + config3.DataBits = DATA_BITS_8; + config3.StopBits = STOP_BITS_1; + config3.bufsz = XS_SERIAL_RB_BUFSZ; + config3.parity = PARITY_NONE; + xs_DeviceControl(siemens_1200_hfa21, XS_DEVICE_CTRL_CONFIG, &config3); + xs_DeviceOpen(siemens_1200_hfa21, XS_DEVICE_FLAG_DMA_RX); + xs_DeviceSetRxIndicate(siemens_1200_hfa21, siemens_1200_uart_input); + TaskDescriptorPointer thread_siemens_1200_plc = xs_KTaskCreate("siemens_1200_hfa21", siemens_1200_plc_thread, NULL, 1024, 25); + + if(thread_siemens_1200_plc != NULL) + { + xs_StartupKTask(thread_siemens_1200_plc); + } + else + { + return XS_ERROR; + } + + //step1 : send handshake_1200_first cmd, waiting for response sem + xs_kprintf("siemens_1200_hfa21 start send handshake_1200_first!\r\n"); + //CH438UARTSend(6, handshake_1200_first, sizeof(handshake_1200_first)); + xs_DeviceWrite(siemens_1200_hfa21, 0, handshake_1200_first, sizeof(handshake_1200_first)); + xs_MdelayKTask(3000); + xs_DeviceWrite(siemens_1200_hfa21, 0, handshake_1200_first, sizeof(handshake_1200_first)); + xs_MdelayKTask(500); + //step2 : send handshake_1200_second cmd, waiting for response sem + xs_kprintf("siemens_1200_hfa21 start send handshake_1200_second!\r\n"); + //CH438UARTSend(6, handshake_1200_second, sizeof(handshake_1200_second)); + xs_DeviceWrite(siemens_1200_hfa21, 0, handshake_1200_second, sizeof(handshake_1200_second)); + xs_kprintf("siemens_1200_hfa21 init success!\r\n"); + xs_MdelayKTask(500); + + while(1) + { + //CH438UARTSend(6, siemens_1200_read_data, sizeof(siemens_1200_read_data)); //read_data + xs_DeviceWrite(siemens_1200_hfa21, 0, siemens_1200_read_data, sizeof(siemens_1200_read_data)); + xs_kprintf("siemens 1200 hfa21 write cmd\n"); + xs_KSemaphoreObtain(siemens_1200_ec200t_sem, XS_WAITING_FOREVER); + xs_MdelayKTask(1000); + } + + return XS_EOK; +} +MSH_CMD_EXPORT(SIEMENS_1200, Siemens 1200 PLC); +#endif + +/*********************************************************************************************/ +//贝加莱PLC IP 192.168.250.4 port 12000 +static unsigned char brl_redis_data[2048]; +static xs_device_t brl_plc_hfa21; +static sem_t brl_plc_input_sem = NULL; +static sem_t brl_plc_ec200t_sem = NULL; + +static xs_err_t brl_plc_uart_input(xs_device_t dev, xs_size_t size) +{ + xs_KSemaphoreAbandon(brl_plc_input_sem); + return XS_EOK; +} + +static void brl_plc_thread(void* parameter) +{ + xs_err_t ret; + uint32_t rx_length, total_rx_length = 0; + uint8_t i; + unsigned int length = 0; + + while(1) + { + ret = xs_KSemaphoreObtain(brl_plc_input_sem, XS_WAITING_FOREVER); + + if(XS_EOK == ret) + { + xs_kprintf("before total %d\n", total_rx_length); + rx_length = xs_DeviceRead(brl_plc_hfa21, 0, brl_redis_data + 2 + total_rx_length, 1640); + xs_kprintf("brl dst data length %d total length %d\n", rx_length, total_rx_length); + + for(i = 0; i < rx_length / 10; i ++) + { + xs_kprintf("0x%x ", brl_redis_data[i + 2 + total_rx_length]); + } + + xs_kprintf("\n"); + total_rx_length += rx_length; + + if(total_rx_length > 1640) + { + xs_kprintf("ERROR : rx_buffer is full total_rx_length %d\n", total_rx_length); + total_rx_length = 0; + memset(brl_redis_data, 0, sizeof(brl_redis_data)); + xs_KSemaphoreAbandon(brl_plc_ec200t_sem); + continue; + } + + if((1640 == total_rx_length) && (0x53 == brl_redis_data[2])) + { + /******format redis data******/ + brl_redis_data[0] = data_head; + brl_redis_data[1] = PLC_BRL_FLAG; + length = 2 + total_rx_length; + + /******end******/ + for(i = 0; i < 10; i ++) + { + xs_kprintf("0x%x ", brl_redis_data[i + 1478]); + } + + xs_kprintf("\n"); + xs_DeviceWrite(ec200t, 0, brl_redis_data, length); + total_rx_length = 0; + memset(brl_redis_data, 0, sizeof(brl_redis_data)); + xs_KSemaphoreAbandon(brl_plc_ec200t_sem); + } + } + } +} + +int BRL_PLC(void) +{ + xs_err_t result; + const unsigned char brl_reply_data[] = {0x1, 0x1, 0x1}; + struct SerialConfigure config3 = XS_SERIAL_CONFIG_DEFAULT; + brl_plc_hfa21 = xs_DeviceFind("uart3"); + + if(!brl_plc_hfa21) + { + xs_kprintf("find brl_plc_hfa21 failed!\r\n"); + return XS_ERROR; + } + + brl_plc_input_sem = xs_KCreateSemaphore("brl_plc_input_sem", 0, XS_LINKLIST_FLAG_FIFO); + + if(brl_plc_input_sem == NULL) + { + xs_kprintf("brl_plc_hfa21 create brl_plc_input_sem failed.\n"); + return XS_ERROR; + } + + brl_plc_ec200t_sem = xs_KCreateSemaphore("brl_plc_ec200t_sem", 0, XS_LINKLIST_FLAG_FIFO); + + if(brl_plc_ec200t_sem == NULL) + { + xs_kprintf("brl_plc_hfa21 create brl_plc_ec200t_sem failed.\n"); + return XS_ERROR; + } + + //brl_hfa21 + config3.BaudRate = BAUD_RATE_460800; + config3.DataBits = DATA_BITS_8; + config3.StopBits = STOP_BITS_1; + config3.bufsz = 2048; + config3.parity = PARITY_NONE; + xs_DeviceControl(brl_plc_hfa21, XS_DEVICE_CTRL_CONFIG, &config3); + xs_DeviceOpen(brl_plc_hfa21, XS_DEVICE_FLAG_DMA_RX); + xs_DeviceSetRxIndicate(brl_plc_hfa21, brl_plc_uart_input); + TaskDescriptorPointer thread_brl_plc = xs_KTaskCreate("brl_plc_hfa21", brl_plc_thread, NULL, 1024, 25); + + if(thread_brl_plc != NULL) + { + xs_StartupKTask(thread_brl_plc); + } + else + { + return XS_ERROR; + } + + xs_kprintf("brl_plc_hfa21 init success!\r\n"); + xs_MdelayKTask(500); + + while(1) + { + //CH438UARTSend(6, brl_reply_data, sizeof(brl_reply_data)); + xs_kprintf("brl_plc hfa21 write cmd\n"); //read_data + xs_DeviceWrite(brl_plc_hfa21, 0, brl_reply_data, sizeof(brl_reply_data)); + xs_KSemaphoreObtain(brl_plc_ec200t_sem, XS_WAITING_FOREVER); + } + + return XS_EOK; +} + +MSH_CMD_EXPORT(BRL_PLC, Brl PLC); + diff --git a/APP_Framework/Framework/control/plc/interoperability/socket/plc_socket.c b/APP_Framework/Framework/control/plc/interoperability/socket/plc_socket.c new file mode 100755 index 000000000..e61202f05 --- /dev/null +++ b/APP_Framework/Framework/control/plc/interoperability/socket/plc_socket.c @@ -0,0 +1,322 @@ +/* + * Copyright (c) 2022 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 plc_socket.c + * @brief Demo for PLC socket communication function + * @version 1.0 + * @author AIIT XUOS Lab + * @date 2022.03.16 + */ + +#include "transform.h" +#include "plc_socket.h" +#include "sys_arch.h" +#include "lwip/sockets.h" + +#define PLC_SOCK_CMD_NUM 10 + +// for saving PLC command +int plc_cmd_index = 0; + +//siemens test +PlcBinCmdType TestPlcCmd[PLC_SOCK_CMD_NUM] = +{ +#ifdef SUPPORT_PLC_SIEMENS + // handshake1 repeat 1 + { + 0, 3000, 22, + { + 0x03, 0x00, 0x00, 0x16, 0x11, 0xE0, 0x00, 0x00, + 0x02, 0xC8, 0x00, 0xC1, 0x02, 0x02, 0x01, 0xC2, + 0x02, 0x02, 0x01, 0xC0, 0x01, 0x0A + } + }, + // handshake2 + { + 1, 500, 25, + { + 0x03, 0x00, 0x00, 0x19, 0x02, 0xF0, 0x80, 0x32, + 0x01, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x08, 0x00, + 0x00, 0xF0, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, + 0xF0 + } + }, + // read command + { + 2, 1000, 31, + { + 0x03, 0x00, 0x00, 0x1F, 0x02, 0xF0, 0x80, 0x32, + 0x01, 0x00, 0x00, 0x33, 0x01, 0x00, 0x0E, 0x00, + 0x00, 0x04, 0x01, 0x12, 0x0A, 0x10, 0x02, 0x00, + 0xD2, 0x00, 0x34, 0x84, 0x00, 0x00, 0x00 + } + } + // oml plc +#else// SUPPORT_PLC_OML + { + 0, 1000, 18, + { + 0x80, 0x00, 0x02, 0x00, 0x03, 0x00, 0x00, 0x7E, + 0x00, 0x00, 0x01, 0x01, 0x82, 0x0F, 0xA0, 0x00, + 0x00, 0x20 + } + } +#endif +}; + +//SIEMENS PLC IP: 192.168.250.9 Port: 102 +//S7-200 192.168.250.8 +//S7-1200 192.168.250.6 +//OML 192.168.250.3 port 9600 +PlcSocketParamType plc_socket_demo_data = { + .ip = {192, 168, 250, 3}, + .port = 9600, + .device_type = PLC_DEV_TYPE_OML, + .socket_type = SOCK_STREAM, //SOCK_DGRAM, //udp + .step = 0, + .cmd_num = 3, + .buf_size = PLC_TEST_SIZE, + .buf = NULL, +}; + +#define OML_HEADER_LEN 78 +#define CHECK_OML_HEADER(_s) ((0xC0 == *(_s)) && (0x00 == *(_s + 1)) && (0x02 == *(_s + 2)) && (0x00 == *(_s + 3))) + +/******************************************************************************/ + +static void *PlcSocketStart(void *arg) +{ + int fd = -1; + int recv_len; + int step = 0; + char *recv_buf; + struct sockaddr_in sock_addr; + socklen_t addr_len = sizeof(struct sockaddr_in); + PlcSocketParamType *param = (PlcSocketParamType *)&plc_socket_demo_data; + + plc_print("start %d.%d.%d.%d:%d dev %d sock %d\n", + param->ip[0], + param->ip[1], + param->ip[2], + param->ip[3], + param->port, + param->device_type, + param->socket_type); + + //malloc memory + recv_buf = (char *)malloc(param->buf_size); + if (recv_buf == NULL) + { + plc_error("No memory\n"); + return NULL; + } + + fd = socket(AF_INET, param->socket_type, 0); + if (fd < 0) + { + plc_error("Socket error %d\n", param->socket_type); + free(recv_buf); + return NULL; + } + + plc_print("start %d.%d.%d.%d:%d\n", param->ip[0], param->ip[1], param->ip[2], param->ip[3], param->port); + + sock_addr.sin_family = AF_INET; + sock_addr.sin_port = htons(param->port); + sock_addr.sin_addr.s_addr = PP_HTONL(LWIP_MAKEU32(param->ip[0], param->ip[1], param->ip[2], param->ip[3])); + memset(&(sock_addr.sin_zero), 0, sizeof(sock_addr.sin_zero)); + + if (connect(fd, (struct sockaddr *)&sock_addr, sizeof(struct sockaddr)) < 0) + { + plc_error("Unable to connect\n"); + closesocket(fd); + free(recv_buf); + return NULL; + } + + lw_notice("client %s connected\n", inet_ntoa(sock_addr.sin_addr)); + + while(step < param->cmd_num) + { + sendto(fd, TestPlcCmd[step].cmd, TestPlcCmd[step].cmd_len, 0, (struct sockaddr*)&sock_addr, addr_len); + lw_notice("Send Cmd: %d - ", TestPlcCmd[step].cmd_len); + for(int i = 0; i < TestPlcCmd[step].cmd_len; i++) + { + lw_notice(" %#x", TestPlcCmd[step].cmd[i]); + } + lw_notice("\n"); + MdelayKTask(TestPlcCmd[step].delay_ms); + + memset(recv_buf, 0, param->buf_size); + while(1) + { + recv_len = recvfrom(fd, recv_buf, param->buf_size, 0, (struct sockaddr *)&sock_addr, &addr_len); + if(recv_len > 0) + { + if(param->device_type == PLC_DEV_TYPE_OML) + { + if((recv_len == OML_HEADER_LEN) && (CHECK_OML_HEADER(recv_buf))) + { + lw_notice("This is Oml package!!!\n"); + } + } + lw_notice("Receive from : %s\n", inet_ntoa(sock_addr.sin_addr)); + lw_notice("Receive data : %d -", recv_len); + for(int i = 0; i < recv_len; i++) + { + lw_notice(" %#x", recv_buf[i]); + } + lw_notice("\n"); + break; + } + } + step ++; + } + + closesocket(fd); + free(recv_buf); + return NULL; +} + +void PlcGetParamCmd(char *cmd) +{ + const char s[2] = ","; + char *token; + uint16_t cmd_index = 0; + char bin_cmd[PLC_BIN_CMD_LEN] = {0}; + token = strtok(cmd, s); + while(token != NULL) + { + sscanf(token, "%x", &bin_cmd[cmd_index]); + plc_print("%d - %s %d\n", cmd_index, token, bin_cmd[cmd_index]); + token = strtok(NULL, s); + cmd_index ++; + } + TestPlcCmd[plc_cmd_index].cmd_len = cmd_index; + memcpy(TestPlcCmd[plc_cmd_index].cmd, bin_cmd, cmd_index); + plc_print("get %d cmd len %d\n", plc_cmd_index, TestPlcCmd[plc_cmd_index].cmd_len); + plc_cmd_index ++; + plc_socket_demo_data.cmd_num = plc_cmd_index; +} + +void PlcShowUsage(void) +{ + plc_notice("------------------------------------\n"); + plc_notice("PlcSocket [ip].[ip].[ip].[ip]:[port]\n"); + plc_notice("PlcSocket support other param:\n"); + plc_notice("plc=[] 0: OML 1:SIEMENS\n"); + plc_notice("tcp=[] 0: udp 1:tcp\n"); + plc_notice("ip=[ip.ip.ip.ip]\n"); + plc_notice("port=port\n"); + plc_notice("------------------------------------\n"); +} + +void PlcCheckParam(int argc, char *argv[]) +{ + int i; + PlcSocketParamType *param = &plc_socket_demo_data; + plc_cmd_index = 0; + + for(i = 0; i < argc; i++) + { + char *str = argv[i]; + int is_tcp = 0; + char cmd_str[PLC_BIN_CMD_LEN] = {0}; + + plc_print("check %d %s\n", i, str); + + if(sscanf(str, "ip=%d.%d.%d.%d", + ¶m->ip[0], + ¶m->ip[1], + ¶m->ip[2], + ¶m->ip[3]) == 4) + { + plc_print("find ip %d %d %d %d\n", param->ip[0], param->ip[1], param->ip[2], param->ip[3]); + continue; + } + + if(sscanf(str, "port=%d", ¶m->port) == 1) + { + plc_print("find port %d\n", param->port); + continue; + } + + if(sscanf(str, "tcp=%d", &is_tcp) == 1) + { + plc_print("find tcp %d\n", is_tcp); + param->socket_type = is_tcp ? SOCK_STREAM:SOCK_DGRAM; + continue; + } + + if(sscanf(str, "plc=%d", ¶m->device_type) == 1) + { + plc_print("find device %d\n", param->device_type); + continue; + } + + if(sscanf(str, "cmd=%s", cmd_str) == 1) + { + plc_print("find cmd %s\n", cmd_str); + PlcGetParamCmd(cmd_str); + continue; + } + } + + if(argc >= 2) + { + if(sscanf(argv[1], "%d.%d.%d.%d:%d", + ¶m->ip[0], + ¶m->ip[1], + ¶m->ip[2], + ¶m->ip[3], + ¶m->port) != EOF) + { + return; + } + + if(sscanf(argv[1], "%d.%d.%d.%d", + ¶m->ip[0], + ¶m->ip[1], + ¶m->ip[2], + ¶m->ip[3]) != EOF) + { + return; + } + } + else + { + PlcShowUsage(); + } +} + + +void PlcSocketTask(int argc, char *argv[]) +{ + int result = 0; + pthread_t th_id; + + pthread_attr_t attr; + attr.schedparam.sched_priority = LWIP_DEMO_TASK_PRIO; + attr.stacksize = LWIP_TASK_STACK_SIZE; + PlcSocketParamType *param = &plc_socket_demo_data; + + PlcCheckParam(argc, argv); + + lwip_config_net(lwip_ipaddr, lwip_netmask, param->ip); + PrivTaskCreate(&th_id, &attr, PlcSocketStart, param); +} + + +SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0) | SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN) | SHELL_CMD_PARAM_NUM(3), + PlcSocket, PlcSocketTask, Test PLC Socket); + diff --git a/APP_Framework/Framework/control/plc/interoperability/socket/plc_socket.h b/APP_Framework/Framework/control/plc/interoperability/socket/plc_socket.h new file mode 100755 index 000000000..363877083 --- /dev/null +++ b/APP_Framework/Framework/control/plc/interoperability/socket/plc_socket.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2022 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 plc_socket.h + * @brief Demo for PLC socket communication function + * @version 1.0 + * @author AIIT XUOS Lab + * @date 2022.03.16 + */ + +#ifndef __PLC_SOCKET_H_ +#define __PLC_SOCKET_H_ + +#define PLC_BIN_CMD_LEN 512 + +typedef struct +{ + uint8_t step; + uint16_t delay_ms; + uint8_t cmd_len; + uint8_t cmd[PLC_BIN_CMD_LEN]; +}PlcBinCmdType; + +enum PlcDeviceType { + PLC_DEV_TYPE_OML = 0, + PLC_DEV_TYPE_IPC, + PLC_DEV_TYPE_BRL, + PLC_DEV_TYPE_SIEMENS, + PLC_DEV_TYPE_SIEMENS_1200, + PLC_DEV_TYPE_JF_IPC, + PLC_DEV_TYPE_HG, + /* ...... */ + PLC_DEV_TYPE_END, +}; + +#define PLC_IP_SIZE 16 +#define PLC_DEV_SIZE 32 +#define PLC_TEST_SIZE 65536 + +typedef struct PlcSocketParamStruct{ + char ip[PLC_IP_SIZE]; + uint32_t port; + uint32_t device_type; //PlcDeviceType + uint32_t socket_type; //UDP or TCP + char device[PLC_DEV_SIZE]; + uint32_t step; // communication step + uint32_t cmd_num; // command number + uint32_t buf_size; + uint8_t *buf; +}PlcSocketParamType; + +//debug command +#define plc_print //KPrintf +#define plc_error KPrintf +#define plc_notice KPrintf + +#endif diff --git a/Ubiquitous/XiZi/kernel/thread/msgqueue.c b/Ubiquitous/XiZi/kernel/thread/msgqueue.c index 3c690011a..9480afdb5 100644 --- a/Ubiquitous/XiZi/kernel/thread/msgqueue.c +++ b/Ubiquitous/XiZi/kernel/thread/msgqueue.c @@ -297,6 +297,7 @@ static x_err_t _DeleteMsgQueue(struct MsgQueue *mq) KERNEL_FREE(mq->msg_buf); lock = CriticalAreaLock(); + IdRemoveObj(&k_mq_id_manager, mq->id.id); DoubleLinkListRmNode(&(mq->link)); CriticalAreaUnLock(lock); KERNEL_FREE(mq); diff --git a/Ubiquitous/XiZi/resources/ethernet/LwIP/api/api_lib.c b/Ubiquitous/XiZi/resources/ethernet/LwIP/api/api_lib.c index a2c136f0b..e03b8b745 100644 --- a/Ubiquitous/XiZi/resources/ethernet/LwIP/api/api_lib.c +++ b/Ubiquitous/XiZi/resources/ethernet/LwIP/api/api_lib.c @@ -246,7 +246,7 @@ netconn_delete(struct netconn *conn) } else #endif /* LWIP_NETCONN_FULLDUPLEX */ { -// err = netconn_prepare_delete(conn); + err = netconn_prepare_delete(conn); } if (err == ERR_OK) { netconn_free(conn); diff --git a/Ubiquitous/XiZi/resources/ethernet/LwIP/arch/lwipopts.h b/Ubiquitous/XiZi/resources/ethernet/LwIP/arch/lwipopts.h index ff32bc38b..a8f4b5be8 100644 --- a/Ubiquitous/XiZi/resources/ethernet/LwIP/arch/lwipopts.h +++ b/Ubiquitous/XiZi/resources/ethernet/LwIP/arch/lwipopts.h @@ -74,7 +74,7 @@ a lot of data that needs to be copied, this should be set high. */ #define MEMP_NUM_TCP_PCB_LISTEN 2 /* MEMP_NUM_TCP_SEG: the number of simultaneously queued TCP segments. */ -#define MEMP_NUM_TCP_SEG 120 +#define MEMP_NUM_TCP_SEG 20 /* MEMP_NUM_SYS_TIMEOUT: the number of simulateously active timeouts. */ #define MEMP_NUM_SYS_TIMEOUT 6 @@ -212,26 +212,19 @@ The STM32F4x7 allows computing and verifying the IP, UDP, TCP and ICMP checksums --------------------------------- */ -#define DEFAULT_RAW_RECVMBOX_SIZE 10 -#define DEFAULT_UDP_RECVMBOX_SIZE 10 -#define DEFAULT_TCP_RECVMBOX_SIZE 10 -#define DEFAULT_ACCEPTMBOX_SIZE 10 +#define DEFAULT_RAW_RECVMBOX_SIZE 8 +#define DEFAULT_UDP_RECVMBOX_SIZE 8 +#define DEFAULT_TCP_RECVMBOX_SIZE 8 +#define DEFAULT_ACCEPTMBOX_SIZE 8 #define DEFAULT_THREAD_PRIO 20 #define DEFAULT_THREAD_STACKSIZE 1024 #define TCPIP_THREAD_NAME "tcp" #define TCPIP_THREAD_STACKSIZE 8192 -#define TCPIP_MBOX_SIZE 10 +#define TCPIP_MBOX_SIZE 8 #define TCPIP_THREAD_PRIO 15 -//#define IPERF_SERVER_THREAD_NAME "iperf_server" -//#define IPERF_SERVER_THREAD_STACKSIZE 1024 -//#define IPERF_SERVER_THREAD_PRIO 0 - -//#define BLOCK_TIME 250 -//#define BLOCK_TIME_WAITING_FOR_INPUT ( ( portTickType ) 100 ) - /* ---------------------------------------- ---------- Lwip Debug options ---------- @@ -259,7 +252,8 @@ typedef unsigned int nfds_t; #define lw_print //KPrintf #define lw_trace() //KPrintf("lw: [%s][%d] passed!\n", __func__, __LINE__) #define lw_error() //KPrintf("lw: [%s][%d] failed!\n", __func__, __LINE__) -#define lw_pr_info KPrintf +#define lw_debug KPrintf +#define lw_notice KPrintf #endif /* __LWIPOPTS_H__ */ /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/Ubiquitous/XiZi/resources/ethernet/LwIP/arch/sys_arch.c b/Ubiquitous/XiZi/resources/ethernet/LwIP/arch/sys_arch.c index ad8b13af3..5088c7f37 100644 --- a/Ubiquitous/XiZi/resources/ethernet/LwIP/arch/sys_arch.c +++ b/Ubiquitous/XiZi/resources/ethernet/LwIP/arch/sys_arch.c @@ -513,16 +513,16 @@ void lwip_config_net(char *ip, char *mask, char *gw) if(chk_lwip_bit(LWIP_PRINT_FLAG)) { - lw_pr_info("\r\n************************************************\r\n"); - lw_pr_info(" Network Configuration\r\n"); - lw_pr_info("************************************************\r\n"); - lw_pr_info(" IPv4 Address : %u.%u.%u.%u\r\n", ((u8_t *)&net_ipaddr)[0], ((u8_t *)&net_ipaddr)[1], + lw_notice("\r\n************************************************\r\n"); + lw_notice(" Network Configuration\r\n"); + lw_notice("************************************************\r\n"); + lw_notice(" IPv4 Address : %u.%u.%u.%u\r\n", ((u8_t *)&net_ipaddr)[0], ((u8_t *)&net_ipaddr)[1], ((u8_t *)&net_ipaddr)[2], ((u8_t *)&net_ipaddr)[3]); - lw_pr_info(" IPv4 Subnet mask : %u.%u.%u.%u\r\n", ((u8_t *)&net_netmask)[0], ((u8_t *)&net_netmask)[1], + lw_notice(" IPv4 Subnet mask : %u.%u.%u.%u\r\n", ((u8_t *)&net_netmask)[0], ((u8_t *)&net_netmask)[1], ((u8_t *)&net_netmask)[2], ((u8_t *)&net_netmask)[3]); - lw_pr_info(" IPv4 Gateway : %u.%u.%u.%u\r\n", ((u8_t *)&net_gw)[0], ((u8_t *)&net_gw)[1], + lw_notice(" IPv4 Gateway : %u.%u.%u.%u\r\n", ((u8_t *)&net_gw)[0], ((u8_t *)&net_gw)[1], ((u8_t *)&net_gw)[2], ((u8_t *)&net_gw)[3]); - lw_pr_info("************************************************\r\n"); + lw_notice("************************************************\r\n"); } lwip_config_input(&gnetif); } diff --git a/Ubiquitous/XiZi/resources/ethernet/cmd_lwip/lwip_config_demo.c b/Ubiquitous/XiZi/resources/ethernet/cmd_lwip/lwip_config_demo.c index 87e79fd8e..a9e662c09 100755 --- a/Ubiquitous/XiZi/resources/ethernet/cmd_lwip/lwip_config_demo.c +++ b/Ubiquitous/XiZi/resources/ethernet/cmd_lwip/lwip_config_demo.c @@ -81,18 +81,18 @@ void LwipShowIPTask(int argc, char *argv[]) { char mac_addr[] = configMAC_ADDR; - lw_pr_info("\r\n************************************************\r\n"); - lw_pr_info(" Network Configuration\r\n"); - lw_pr_info("************************************************\r\n"); - lw_pr_info(" IPv4 Address : %u.%u.%u.%u\r\n", ((u8_t *)&lwip_ipaddr)[0], ((u8_t *)&lwip_ipaddr)[1], + lw_notice("\r\n************************************************\r\n"); + lw_notice(" Network Configuration\r\n"); + lw_notice("************************************************\r\n"); + lw_notice(" IPv4 Address : %u.%u.%u.%u\r\n", ((u8_t *)&lwip_ipaddr)[0], ((u8_t *)&lwip_ipaddr)[1], ((u8_t *)&lwip_ipaddr)[2], ((u8_t *)&lwip_ipaddr)[3]); - lw_pr_info(" IPv4 Subnet mask : %u.%u.%u.%u\r\n", ((u8_t *)&lwip_netmask)[0], ((u8_t *)&lwip_netmask)[1], + lw_notice(" IPv4 Subnet mask : %u.%u.%u.%u\r\n", ((u8_t *)&lwip_netmask)[0], ((u8_t *)&lwip_netmask)[1], ((u8_t *)&lwip_netmask)[2], ((u8_t *)&lwip_netmask)[3]); - lw_pr_info(" IPv4 Gateway : %u.%u.%u.%u\r\n", ((u8_t *)&lwip_gwaddr)[0], ((u8_t *)&lwip_gwaddr)[1], + lw_notice(" IPv4 Gateway : %u.%u.%u.%u\r\n", ((u8_t *)&lwip_gwaddr)[0], ((u8_t *)&lwip_gwaddr)[1], ((u8_t *)&lwip_gwaddr)[2], ((u8_t *)&lwip_gwaddr)[3]); - lw_pr_info(" MAC Address : %x:%x:%x:%x:%x:%x\r\n", mac_addr[0], mac_addr[1], mac_addr[2], + lw_notice(" MAC Address : %x:%x:%x:%x:%x:%x\r\n", mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]); - lw_pr_info("************************************************\r\n"); + lw_notice("************************************************\r\n"); } SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0) | SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN) | SHELL_CMD_PARAM_NUM(0), diff --git a/Ubiquitous/XiZi/resources/ethernet/cmd_lwip/lwip_dhcp_demo.c b/Ubiquitous/XiZi/resources/ethernet/cmd_lwip/lwip_dhcp_demo.c index cfeff2499..87443523e 100755 --- a/Ubiquitous/XiZi/resources/ethernet/cmd_lwip/lwip_dhcp_demo.c +++ b/Ubiquitous/XiZi/resources/ethernet/cmd_lwip/lwip_dhcp_demo.c @@ -61,54 +61,54 @@ int LwipPrintDHCP(struct netif *netif) { dhcp_last_state = dhcp->state; - lw_pr_info(" DHCP state : "); + lw_notice(" DHCP state : "); switch (dhcp_last_state) { case DHCP_STATE_OFF: - lw_pr_info("OFF"); + lw_notice("OFF"); break; case DHCP_STATE_REQUESTING: - lw_pr_info("REQUESTING"); + lw_notice("REQUESTING"); break; case DHCP_STATE_INIT: - lw_pr_info("INIT"); + lw_notice("INIT"); break; case DHCP_STATE_REBOOTING: - lw_pr_info("REBOOTING"); + lw_notice("REBOOTING"); break; case DHCP_STATE_REBINDING: - lw_pr_info("REBINDING"); + lw_notice("REBINDING"); break; case DHCP_STATE_RENEWING: - lw_pr_info("RENEWING"); + lw_notice("RENEWING"); break; case DHCP_STATE_SELECTING: - lw_pr_info("SELECTING"); + lw_notice("SELECTING"); break; case DHCP_STATE_INFORMING: - lw_pr_info("INFORMING"); + lw_notice("INFORMING"); break; case DHCP_STATE_CHECKING: - lw_pr_info("CHECKING"); + lw_notice("CHECKING"); break; case DHCP_STATE_BOUND: - lw_pr_info("BOUND"); + lw_notice("BOUND"); break; case DHCP_STATE_BACKING_OFF: - lw_pr_info("BACKING_OFF"); + lw_notice("BACKING_OFF"); break; default: - lw_pr_info("%u", dhcp_last_state); + lw_notice("%u", dhcp_last_state); assert(0); break; } - lw_pr_info("\r\n"); + lw_notice("\r\n"); if (dhcp_last_state == DHCP_STATE_BOUND) { - lw_pr_info("\r\n IPv4 Address : %s\r\n", ipaddr_ntoa(&netif->ip_addr)); - lw_pr_info(" IPv4 Subnet mask : %s\r\n", ipaddr_ntoa(&netif->netmask)); - lw_pr_info(" IPv4 Gateway : %s\r\n\r\n", ipaddr_ntoa(&netif->gw)); + lw_notice("\r\n IPv4 Address : %s\r\n", ipaddr_ntoa(&netif->ip_addr)); + lw_notice(" IPv4 Subnet mask : %s\r\n", ipaddr_ntoa(&netif->netmask)); + lw_notice(" IPv4 Gateway : %s\r\n\r\n", ipaddr_ntoa(&netif->gw)); return 1; } } diff --git a/Ubiquitous/XiZi/resources/ethernet/cmd_lwip/lwip_ping_demo.c b/Ubiquitous/XiZi/resources/ethernet/cmd_lwip/lwip_ping_demo.c index f99e8b380..4eb01d0ba 100755 --- a/Ubiquitous/XiZi/resources/ethernet/cmd_lwip/lwip_ping_demo.c +++ b/Ubiquitous/XiZi/resources/ethernet/cmd_lwip/lwip_ping_demo.c @@ -60,7 +60,7 @@ void LwipPingTest(int argc, char *argv[]) { if(sscanf(argv[1], "%d.%d.%d.%d", &lwip_gwaddr[0], &lwip_gwaddr[1], &lwip_gwaddr[2], &lwip_gwaddr[3]) == EOF) { - lw_pr_info("input wrong ip\n"); + lw_notice("input wrong ip\n"); return; } } diff --git a/Ubiquitous/XiZi/resources/ethernet/cmd_lwip/lwip_tcp_demo.c b/Ubiquitous/XiZi/resources/ethernet/cmd_lwip/lwip_tcp_demo.c index 98bf62cb5..387899abc 100755 --- a/Ubiquitous/XiZi/resources/ethernet/cmd_lwip/lwip_tcp_demo.c +++ b/Ubiquitous/XiZi/resources/ethernet/cmd_lwip/lwip_tcp_demo.c @@ -27,52 +27,61 @@ #define MSG_SIZE 128 +typedef struct LwipTcpSocketStruct +{ + char ip[6]; + uint16_t port; + char *buf; // test buffer +}LwipTcpSocketParamType; + // this is for test in shell, in fact, shell restrict the length of input string, which is less then 128 char tcp_send_msg[MSG_SIZE] = {0}; char tcp_target[] = {192, 168, 250, 252}; +uint16_t tcp_port = LWIP_TARGET_PORT; /******************************************************************************/ static void LwipTcpSendTask(void *arg) { - lw_print("LwipTcpSendTask start.\n"); + lw_notice("LwipTcpSendTask start.\n"); int fd = -1; fd = socket(AF_INET, SOCK_STREAM, 0); if (fd < 0) { - lw_print("Socket error\n"); + lw_notice("Socket error\n"); return; } + LwipTcpSocketParamType *param = (LwipTcpSocketParamType *)arg; + struct sockaddr_in tcp_sock; tcp_sock.sin_family = AF_INET; - tcp_sock.sin_port = htons(LWIP_TARGET_PORT); - tcp_sock.sin_addr.s_addr = PP_HTONL(LWIP_MAKEU32(tcp_target[0], tcp_target[1], tcp_target[2], tcp_target[3])); + tcp_sock.sin_port = htons(param->port); + tcp_sock.sin_addr.s_addr = PP_HTONL(LWIP_MAKEU32(param->ip[0], param->ip[1], param->ip[2], param->ip[3])); memset(&(tcp_sock.sin_zero), 0, sizeof(tcp_sock.sin_zero)); if (connect(fd, (struct sockaddr *)&tcp_sock, sizeof(struct sockaddr))) { - lw_print("Unable to connect\n"); - goto __exit; + lw_notice("Unable to connect\n"); + closesocket(fd); + return; } - lw_print("tcp connect success, start to send.\n"); - lw_print("\n\nTarget Port:%d\n\n", tcp_sock.sin_port); + lw_notice("tcp connect success, start to send.\n"); + lw_notice("\n\nTarget Port:%d\n\n", tcp_sock.sin_port); sendto(fd, tcp_send_msg, strlen(tcp_send_msg), 0, (struct sockaddr*)&tcp_sock, sizeof(struct sockaddr)); - lw_print("Send tcp msg: %s ", tcp_send_msg); - -__exit: - if (fd >= 0) - closesocket(fd); + lw_notice("Send tcp msg: %s ", tcp_send_msg); + closesocket(fd); return; } void LwipTcpSendTest(int argc, char *argv[]) { + LwipTcpSocketParamType param; memset(tcp_send_msg, 0, MSG_SIZE); if(argc >= 2) { @@ -86,11 +95,20 @@ void LwipTcpSendTest(int argc, char *argv[]) if(argc >= 3) { - sscanf(argv[2], "%d.%d.%d.%d", &tcp_target[0], &tcp_target[1], &tcp_target[2], &tcp_target[3]); + if(sscanf(argv[2], "%d.%d.%d.%d:%d", &tcp_target[0], &tcp_target[1], &tcp_target[2], &tcp_target[3], &tcp_port) == EOK) + { + sscanf(argv[2], "%d.%d.%d.%d", &tcp_target[0], &tcp_target[1], &tcp_target[2], &tcp_target[3]); + } } + lw_notice("get ipaddr %d.%d.%d.%d:%d\n", tcp_target[0], tcp_target[1], tcp_target[2], tcp_target[3], tcp_port); + lwip_config_tcp(lwip_ipaddr, lwip_netmask, tcp_target); - lwip_config_tcp(lwip_ipaddr, lwip_netmask, lwip_gwaddr); - sys_thread_new("tcp send", LwipTcpSendTask, NULL, LWIP_TASK_STACK_SIZE, LWIP_DEMO_TASK_PRIO); + memcpy(param.ip, tcp_target, 4); + param.port = tcp_port; + param.buf = malloc(MSG_SIZE); + memcpy(param.buf, tcp_send_msg, MSG_SIZE); + + sys_thread_new("tcp send", LwipTcpSendTask, ¶m, LWIP_TASK_STACK_SIZE, LWIP_DEMO_TASK_PRIO); } SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0) | SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN) | SHELL_CMD_PARAM_NUM(3), diff --git a/Ubiquitous/XiZi/resources/ethernet/cmd_lwip/lwip_udp_demo.c b/Ubiquitous/XiZi/resources/ethernet/cmd_lwip/lwip_udp_demo.c index 9f459031a..97d06b119 100755 --- a/Ubiquitous/XiZi/resources/ethernet/cmd_lwip/lwip_udp_demo.c +++ b/Ubiquitous/XiZi/resources/ethernet/cmd_lwip/lwip_udp_demo.c @@ -30,7 +30,8 @@ #define PBUF_SIZE 27 static struct udp_pcb *udpecho_raw_pcb; -char udp_target[] = {192, 168, 250, 252}; +char udp_demo_ip[] = {192, 168, 250, 252}; +uint16_t udp_demo_port = LWIP_TARGET_PORT; char hello_str[] = {"hello world\r\n"}; char udp_send_msg[] = "\n\nThis one is UDP pkg. Congratulations on you.\n\n"; @@ -52,28 +53,23 @@ static void LwipUDPSendTask(void *arg) struct sockaddr_in udp_sock; udp_sock.sin_family = AF_INET; - udp_sock.sin_port = htons(LWIP_TARGET_PORT); - udp_sock.sin_addr.s_addr = PP_HTONL(LWIP_MAKEU32(udp_target[0],udp_target[1],udp_target[2],udp_target[3])); + udp_sock.sin_port = htons(udp_demo_port); + udp_sock.sin_addr.s_addr = PP_HTONL(LWIP_MAKEU32(udp_demo_ip[0], udp_demo_ip[1], udp_demo_ip[2], udp_demo_ip[3])); memset(&(udp_sock.sin_zero), 0, sizeof(udp_sock.sin_zero)); if (connect(socket_fd, (struct sockaddr *)&udp_sock, sizeof(struct sockaddr))) { lw_print("Unable to connect\n"); - goto __exit; + closesocket(socket_fd); + return; } lw_print("UDP connect success, start to send.\n"); lw_print("\n\nTarget Port:%d\n\n", udp_sock.sin_port); sendto(socket_fd, udp_send_msg, strlen(udp_send_msg), 0, (struct sockaddr*)&udp_sock, sizeof(struct sockaddr)); - lw_pr_info("Send UDP msg: %s ", udp_send_msg); - -__exit: - if (socket_fd >= 0) - { - closesocket(socket_fd); - } - + lw_notice("Send UDP msg: %s ", udp_send_msg); + closesocket(socket_fd); return; } @@ -86,7 +82,7 @@ void *LwipUdpSendTest(int argc, char *argv[]) if(argc == 1) { - lw_print("lw: [%s] gw %d.%d.%d.%d\n", __func__, udp_target[0], udp_target[1], udp_target[2], udp_target[3]); + lw_print("lw: [%s] gw %d.%d.%d.%d\n", __func__, udp_demo_ip[0], udp_demo_ip[1], udp_demo_ip[2], udp_demo_ip[3]); strncpy(udp_send_msg, hello_str, strlen(hello_str)); } else @@ -95,12 +91,12 @@ void *LwipUdpSendTest(int argc, char *argv[]) strncat(udp_send_msg, "\r\n", 2); if(argc == 3) { - sscanf(argv[2], "%d.%d.%d.%d", &udp_target[0], &udp_target[1], &udp_target[2], &udp_target[3]); + sscanf(argv[2], "%d.%d.%d.%d", &udp_demo_ip[0], &udp_demo_ip[1], &udp_demo_ip[2], &udp_demo_ip[3]); } } - lw_print("lw: [%s] gw %d.%d.%d.%d\n", __func__, udp_target[0], udp_target[1], udp_target[2], udp_target[3]); + lw_print("lw: [%s] gw %d.%d.%d.%d\n", __func__, udp_demo_ip[0], udp_demo_ip[1], udp_demo_ip[2], udp_demo_ip[3]); - lwip_config_tcp(lwip_ipaddr, lwip_netmask, lwip_gwaddr); + lwip_config_net(lwip_ipaddr, lwip_netmask, lwip_gwaddr); sys_thread_new("udp socket send", LwipUDPSendTask, NULL, LWIP_TASK_STACK_SIZE, LWIP_DEMO_TASK_PRIO); } @@ -121,11 +117,11 @@ static void LwipUdpRecvTask(void *arg, struct udp_pcb *upcb, struct pbuf *p, return; } udp_len = p->tot_len; - lw_pr_info("Receive data :%dB\r\n", udp_len); + lw_notice("Receive data :%dB\r\n", udp_len); if(udp_len <= 80) { - lw_pr_info("%.*s\r\n", udp_len, (char *)(p->payload)); + lw_notice("%.*s\r\n", udp_len, (char *)(p->payload)); } udp_buf = pbuf_alloc(PBUF_TRANSPORT, PBUF_SIZE, PBUF_RAM); diff --git a/Ubiquitous/XiZi/resources/ethernet/cmd_lwip/ping.c b/Ubiquitous/XiZi/resources/ethernet/cmd_lwip/ping.c index 1a66bd272..15c2f6c1e 100755 --- a/Ubiquitous/XiZi/resources/ethernet/cmd_lwip/ping.c +++ b/Ubiquitous/XiZi/resources/ethernet/cmd_lwip/ping.c @@ -492,7 +492,7 @@ int get_url_ip(char* url) /* convert URL to IP */ if (lwip_getaddrinfo(url, NULL, &hint, &res) != 0) { - lw_pr_info("ping: unknown host %s\n", url); + lw_notice("ping: unknown host %s\n", url); return -1; } memcpy(&h, &res->ai_addr, sizeof(struct sockaddr_in *)); @@ -500,13 +500,13 @@ int get_url_ip(char* url) lwip_freeaddrinfo(res); if (inet_aton(inet_ntoa(ina), &target_addr) == 0) { - lw_pr_info("ping: unknown host %s\n", url); + lw_notice("ping: unknown host %s\n", url); return -2; } /* new a socket */ if ((s = lwip_socket(AF_INET, SOCK_RAW, IP_PROTO_ICMP)) < 0) { - lw_pr_info("ping: create socket failed\n"); + lw_notice("ping: create socket failed\n"); return -3; } @@ -521,12 +521,12 @@ int get_url_ip(char* url) #endif /* LWIP_DEBUG */ if ((recv_len = lwip_ping_recv(s, &ttl)) >= 0) { - lw_pr_info("%d bytes from %s icmp_seq=%d ttl=%d time=%d ms\n", recv_len, inet_ntoa(ina), cnt, + lw_notice("%d bytes from %s icmp_seq=%d ttl=%d time=%d ms\n", recv_len, inet_ntoa(ina), cnt, ttl, sys_now() - ping_time); } else { - lw_pr_info("From %s icmp_seq=%d timeout\n", inet_ntoa(ina), cnt); + lw_notice("From %s icmp_seq=%d timeout\n", inet_ntoa(ina), cnt); } } diff --git a/Ubiquitous/XiZi/resources/ethernet/cmd_lwip/tcpecho_raw.c b/Ubiquitous/XiZi/resources/ethernet/cmd_lwip/tcpecho_raw.c index 026207891..d924f3285 100755 --- a/Ubiquitous/XiZi/resources/ethernet/cmd_lwip/tcpecho_raw.c +++ b/Ubiquitous/XiZi/resources/ethernet/cmd_lwip/tcpecho_raw.c @@ -159,9 +159,9 @@ tcpecho_raw_ack(struct tcp_pcb *tpcb, struct tcpecho_raw_state* es){ if((recv_len != TCP_MSS) || (recv_buf[recv_len - 1] == TCP_EOF_CH)) { if(g_buf_size < MAX_TCP_SHOW_SIZE){ - lw_pr_info("Received: %s\n", g_buf); + lw_notice("Received: %s\n", g_buf); }else{ - lw_pr_info("Received a string of length %d\n", g_buf_size); + lw_notice("Received a string of length %d\n", g_buf_size); } tcpecho_raw_ack_size(tpcb, g_buf_size); From 4843ff9e61bb3971a3bf241e207f1ced98244386 Mon Sep 17 00:00:00 2001 From: wlyu Date: Thu, 17 Mar 2022 15:33:37 +0800 Subject: [PATCH 3/9] support control json file --- .../socket_demo/lwip_tcp_socket_demo.c | 24 +- .../socket_demo/lwip_udp_socket_demo.c | 6 +- .../control_app/opcua_demo/opcua_demo.c | 38 +- .../control/plc/interoperability/Kconfig | 6 +- .../control/plc/interoperability/Makefile | 4 + .../plc/interoperability/opcua/open62541.c | 6 +- .../plc/interoperability/opcua/ua_api.c | 7 +- .../plc/interoperability/opcua/ua_api.h | 3 +- .../plc/interoperability/opcua/ua_client.c | 42 +- .../plc/interoperability/opcua/ua_test.c | 6 +- .../plc/interoperability/socket/Kconfig | 11 + .../plc/interoperability/socket/Makefile | 4 + .../plc/interoperability/socket/br_socket.c | 1867 +++++++++++++++++ .../plc/interoperability/socket/plc_socket.c | 329 +++ .../plc/interoperability/socket/plc_socket.h | 67 + .../Framework/control/plc/shared/plc_device.h | 2 +- .../Framework/control/shared/Makefile | 4 + .../Framework/control/shared/control_file.c | 129 ++ .../Framework/control/shared/control_file.h | 35 + APP_Framework/lib/Kconfig | 4 +- APP_Framework/lib/Makefile | 3 + APP_Framework/lib/cJSON/Makefile | 3 + Ubiquitous/XiZi/board/ok1052-c/board.c | 17 +- .../board/ok1052-c/third_party_driver/Kconfig | 6 +- .../ok1052-c/third_party_driver/spi/Makefile | 6 +- Ubiquitous/XiZi/kernel/thread/msgqueue.c | 25 +- Ubiquitous/XiZi/path_kernel.mk | 1 + .../resources/ethernet/LwIP/api/api_lib.c | 5 +- .../resources/ethernet/LwIP/api/sockets.c | 20 + .../resources/ethernet/LwIP/arch/lwipopts.h | 22 +- .../resources/ethernet/LwIP/arch/sys_arch.c | 14 +- .../resources/ethernet/LwIP/core/tcp_out.c | 6 +- .../ethernet/cmd_lwip/lwip_config_demo.c | 16 +- .../ethernet/cmd_lwip/lwip_dhcp_demo.c | 34 +- .../ethernet/cmd_lwip/lwip_ping_demo.c | 2 +- .../ethernet/cmd_lwip/lwip_tcp_demo.c | 50 +- .../ethernet/cmd_lwip/lwip_udp_demo.c | 32 +- .../XiZi/resources/ethernet/cmd_lwip/ping.c | 10 +- .../resources/ethernet/cmd_lwip/tcpecho_raw.c | 4 +- Ubiquitous/XiZi/resources/include/flash_spi.h | 2 + .../XiZi/tool/shell/letter-shell/shell.c | 299 +-- .../XiZi/tool/shell/letter-shell/shell_port.c | 32 +- 42 files changed, 2857 insertions(+), 346 deletions(-) create mode 100755 APP_Framework/Framework/control/plc/interoperability/socket/Kconfig create mode 100755 APP_Framework/Framework/control/plc/interoperability/socket/Makefile create mode 100755 APP_Framework/Framework/control/plc/interoperability/socket/br_socket.c create mode 100755 APP_Framework/Framework/control/plc/interoperability/socket/plc_socket.c create mode 100755 APP_Framework/Framework/control/plc/interoperability/socket/plc_socket.h create mode 100755 APP_Framework/Framework/control/shared/control_file.c create mode 100755 APP_Framework/Framework/control/shared/control_file.h create mode 100755 APP_Framework/lib/cJSON/Makefile diff --git a/APP_Framework/Applications/connection_app/socket_demo/lwip_tcp_socket_demo.c b/APP_Framework/Applications/connection_app/socket_demo/lwip_tcp_socket_demo.c index d30248442..a09866fa9 100755 --- a/APP_Framework/Applications/connection_app/socket_demo/lwip_tcp_socket_demo.c +++ b/APP_Framework/Applications/connection_app/socket_demo/lwip_tcp_socket_demo.c @@ -67,7 +67,7 @@ static void TCPSocketRecvTask(void *arg) } lw_print("tcp bind success, start to receive.\n"); - lw_pr_info("\n\nLocal Port:%d\n\n", LWIP_LOCAL_PORT); + lw_notice("\n\nLocal Port:%d\n\n", LWIP_LOCAL_PORT); // setup socket fd as listening mode if (listen(fd, 5) != 0 ) @@ -78,7 +78,7 @@ static void TCPSocketRecvTask(void *arg) // accept client connection clientfd = accept(fd, (struct sockaddr *)&tcp_addr, (socklen_t*)&addr_len); - lw_pr_info("client %s connected\n", inet_ntoa(tcp_addr.sin_addr)); + lw_notice("client %s connected\n", inet_ntoa(tcp_addr.sin_addr)); while(1) { @@ -86,8 +86,8 @@ static void TCPSocketRecvTask(void *arg) recv_len = recvfrom(clientfd, recv_buf, TCP_DEMO_BUF_SIZE, 0, (struct sockaddr *)&tcp_addr, &addr_len); if(recv_len > 0) { - lw_pr_info("Receive from : %s\n", inet_ntoa(tcp_addr.sin_addr)); - lw_pr_info("Receive data : %d - %s\n\n", recv_len, recv_buf); + lw_notice("Receive from : %s\n", inet_ntoa(tcp_addr.sin_addr)); + lw_notice("Receive data : %d - %s\n\n", recv_len, recv_buf); } sendto(clientfd, recv_buf, recv_len, 0, (struct sockaddr*)&tcp_addr, addr_len); } @@ -113,7 +113,7 @@ void TCPSocketRecvTest(int argc, char *argv[]) sscanf(argv[1], "%d.%d.%d.%d", &tcp_socket_ip[0], &tcp_socket_ip[1], &tcp_socket_ip[2], &tcp_socket_ip[3]); } - lwip_config_tcp(lwip_ipaddr, lwip_netmask, lwip_gwaddr); + lwip_config_tcp(lwip_ipaddr, lwip_netmask, tcp_socket_ip); sys_thread_new("TCPSocketRecvTask", TCPSocketRecvTask, NULL, LWIP_TASK_STACK_SIZE, LWIP_DEMO_TASK_PRIO); } @@ -133,7 +133,7 @@ static void TCPSocketSendTask(void *arg) if (fd < 0) { lw_print("Socket error\n"); - goto __exit; + return; } struct sockaddr_in tcp_sock; @@ -145,25 +145,23 @@ static void TCPSocketSendTask(void *arg) if (connect(fd, (struct sockaddr *)&tcp_sock, sizeof(struct sockaddr))) { lw_print("Unable to connect\n"); - goto __exit; + closesocket(fd); + return; } lw_print("tcp connect success, start to send.\n"); - lw_pr_info("\n\nTarget Port:%d\n\n", LWIP_TARGET_PORT); + lw_notice("\n\nTarget Port:%d\n\n", LWIP_TARGET_PORT); while (cnt --) { lw_print("Lwip client is running.\n"); snprintf(send_msg, sizeof(send_msg), "TCP test package times %d\r\n", cnt); sendto(fd, send_msg, strlen(send_msg), 0, (struct sockaddr*)&tcp_sock, sizeof(struct sockaddr)); - lw_pr_info("Send tcp msg: %s ", send_msg); + lw_notice("Send tcp msg: %s ", send_msg); MdelayKTask(1000); } -__exit: - if (fd >= 0) - closesocket(fd); - + closesocket(fd); return; } diff --git a/APP_Framework/Applications/connection_app/socket_demo/lwip_udp_socket_demo.c b/APP_Framework/Applications/connection_app/socket_demo/lwip_udp_socket_demo.c index 4dd23b9e2..24a1a374f 100755 --- a/APP_Framework/Applications/connection_app/socket_demo/lwip_udp_socket_demo.c +++ b/APP_Framework/Applications/connection_app/socket_demo/lwip_udp_socket_demo.c @@ -78,8 +78,8 @@ static void UdpSocketRecvTask(void *arg) { memset(recv_buf, 0, UDP_BUF_SIZE); recv_len = recvfrom(socket_fd, recv_buf, UDP_BUF_SIZE, 0, (struct sockaddr *)&server_addr, &addr_len); - lw_pr_info("Receive from : %s\n", inet_ntoa(server_addr.sin_addr)); - lw_pr_info("Receive data : %s\n\n", recv_buf); + lw_notice("Receive from : %s\n", inet_ntoa(server_addr.sin_addr)); + lw_notice("Receive data : %s\n\n", recv_buf); sendto(socket_fd, recv_buf, recv_len, 0, (struct sockaddr*)&server_addr, addr_len); } @@ -151,7 +151,7 @@ static void UdpSocketSendTask(void *arg) { snprintf(send_str, sizeof(send_str), "UDP test package times %d\r\n", cnt); sendto(socket_fd, send_str, strlen(send_str), 0, (struct sockaddr*)&udp_sock, sizeof(struct sockaddr)); - lw_pr_info("Send UDP msg: %s ", send_str); + lw_notice("Send UDP msg: %s ", send_str); MdelayKTask(1000); } diff --git a/APP_Framework/Applications/control_app/opcua_demo/opcua_demo.c b/APP_Framework/Applications/control_app/opcua_demo/opcua_demo.c index c8a0e8d54..e1edcf29a 100755 --- a/APP_Framework/Applications/control_app/opcua_demo/opcua_demo.c +++ b/APP_Framework/Applications/control_app/opcua_demo/opcua_demo.c @@ -11,7 +11,7 @@ */ /** - * @file ua_demo.c + * @file opcua_demo.c * @brief Demo for OpcUa function * @version 1.0 * @author AIIT XUOS Lab @@ -29,7 +29,6 @@ * Definitions ******************************************************************************/ -#define TCP_LOCAL_PORT 4840 #define UA_URL_SIZE 100 #define UA_STACK_SIZE 4096 #define UA_TASK_PRIO 15 @@ -66,18 +65,17 @@ static void UaConnectTestTask(void* arg) UA_ClientConfig_setDefault(config); snprintf(ua_uri, sizeof(ua_uri), "opc.tcp://%d.%d.%d.%d:4840", test_ua_ip[0], test_ua_ip[1], test_ua_ip[2], test_ua_ip[3]); - ua_pr_info("ua uri: %d %s\n", strlen(ua_uri), ua_uri); + ua_notice("ua uri: %d %s\n", strlen(ua_uri), ua_uri); retval = UA_Client_connect(client,ua_uri); if(retval != UA_STATUSCODE_GOOD) { - ua_pr_info("ua: [%s] connected failed %x\n", __func__, retval); + ua_notice("ua: [%s] connected failed %x\n", __func__, retval); UA_Client_delete(client); return; } - ua_pr_info("ua: [%s] connected ok!\n", __func__); - UA_Client_disconnect(client); + ua_notice("ua: [%s] connected ok!\n", __func__); UA_Client_delete(client); } @@ -92,12 +90,13 @@ SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0) | SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN) | void UaBrowserObjectsTestTask(void* param) { + static int test_cnt = 0; UA_Client* client = UA_Client_new(); - ua_pr_info("ua: [%s] start ...\n", __func__); + ua_notice("ua: [%s] start %d ...\n", __func__, test_cnt++); if(client == NULL) { - ua_print("ua: [%s] tcp client null\n", __func__); + ua_error("ua: [%s] tcp client NULL\n", __func__); return; } @@ -107,18 +106,17 @@ void UaBrowserObjectsTestTask(void* param) if(retval != UA_STATUSCODE_GOOD) { - ua_print("ua: [%s] connect failed %#x\n", __func__, retval); + ua_error("ua: [%s] connect failed %#x\n", __func__, retval); UA_Client_delete(client); return; } - ua_print("ua: [%s] connect ok!\n", __func__); - ua_pr_info("--- start read time ---\n", __func__); + ua_notice("--- start read time ---\n", __func__); ua_read_time(client); - ua_pr_info("--- get server info ---\n", __func__); + ua_notice("--- get server info ---\n", __func__); ua_test_browser_objects(client); + /* Clean up */ - UA_Client_disconnect(client); UA_Client_delete(client); /* Disconnects the client internally */ } @@ -130,7 +128,7 @@ void* UaBrowserObjectsTest(int argc, char* argv[]) { if(sscanf(argv[1], "%d.%d.%d.%d", &test_ua_ip[0], &test_ua_ip[1], &test_ua_ip[2], &test_ua_ip[3]) == EOF) { - lw_pr_info("input wrong ip\n"); + lw_notice("input wrong ip\n"); return NULL; } } @@ -147,7 +145,7 @@ SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0) | SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN) | void UaGetInfoTestTask(void* param) { UA_Client* client = UA_Client_new(); - ua_pr_info("ua: [%s] start ...\n", __func__); + ua_notice("ua: [%s] start ...\n", __func__); if(client == NULL) { @@ -167,7 +165,7 @@ void UaGetInfoTestTask(void* param) } ua_print("ua: [%s] connect ok!\n", __func__); - ua_pr_info("--- interactive server ---\n", __func__); + ua_notice("--- interactive server ---\n", __func__); ua_test_interact_server(client); /* Clean up */ UA_Client_disconnect(client); @@ -182,7 +180,7 @@ void* UaGetInfoTest(int argc, char* argv[]) { if(sscanf(argv[1], "%d.%d.%d.%d", &test_ua_ip[0], &test_ua_ip[1], &test_ua_ip[2], &test_ua_ip[3]) == EOF) { - lw_pr_info("input wrong ip\n"); + lw_notice("input wrong ip\n"); return NULL; } } @@ -199,7 +197,7 @@ SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0) | SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN) | void UaAddNodesTask(void* param) { UA_Client* client = UA_Client_new(); - ua_pr_info("ua: [%s] start ...\n", __func__); + ua_notice("ua: [%s] start ...\n", __func__); if(client == NULL) { @@ -219,7 +217,7 @@ void UaAddNodesTask(void* param) } ua_print("ua: [%s] connect ok!\n", __func__); - ua_pr_info("--- add nodes ---\n", __func__); + ua_notice("--- add nodes ---\n", __func__); ua_add_nodes(client); /* Clean up */ UA_Client_disconnect(client); @@ -234,7 +232,7 @@ void* UaAddNodesTest(int argc, char* argv[]) { if(sscanf(argv[1], "%d.%d.%d.%d", &test_ua_ip[0], &test_ua_ip[1], &test_ua_ip[2], &test_ua_ip[3]) == EOF) { - lw_pr_info("input wrong ip\n"); + lw_notice("input wrong ip\n"); return NULL; } } diff --git a/APP_Framework/Framework/control/plc/interoperability/Kconfig b/APP_Framework/Framework/control/plc/interoperability/Kconfig index 66ae5f182..6a7cb0324 100755 --- a/APP_Framework/Framework/control/plc/interoperability/Kconfig +++ b/APP_Framework/Framework/control/plc/interoperability/Kconfig @@ -2,5 +2,9 @@ menuconfig USING_CONTROL_PLC_OPCUA bool "PLC support OPCUA" default y - depends on RESOURCES_LWIP + depends on RESOURCES_LWIP +menuconfig USING_CONTROL_PLC_SOCKET + bool "PLC support SOCKET" + default y + depends on RESOURCES_LWIP diff --git a/APP_Framework/Framework/control/plc/interoperability/Makefile b/APP_Framework/Framework/control/plc/interoperability/Makefile index 7f542f711..d329b91fb 100755 --- a/APP_Framework/Framework/control/plc/interoperability/Makefile +++ b/APP_Framework/Framework/control/plc/interoperability/Makefile @@ -6,6 +6,10 @@ ifeq ($(CONFIG_USING_CONTROL_PLC_OPCUA), y) SRC_DIR += opcua endif +ifeq ($(CONFIG_USING_CONTROL_PLC_SOCKET), y) + SRC_DIR += socket +endif + endif SRC_FILES += interoperability.c diff --git a/APP_Framework/Framework/control/plc/interoperability/opcua/open62541.c b/APP_Framework/Framework/control/plc/interoperability/opcua/open62541.c index 3da01d2b7..fc525aa8d 100755 --- a/APP_Framework/Framework/control/plc/interoperability/opcua/open62541.c +++ b/APP_Framework/Framework/control/plc/interoperability/opcua/open62541.c @@ -68231,11 +68231,11 @@ UA_Log_Stdout_log(void *context, UA_LogLevel level, UA_LogCategory category, // (int)(tOffset / UA_DATETIME_SEC / 36), logLevelNames[level], logCategoryNames[category]); // vprintf(msg, args); - KPrintf("%s/%s" ANSI_COLOR_RESET "\t", + ua_print("%s/%s" ANSI_COLOR_RESET "\t", logLevelNames[level], logCategoryNames[category]); vsnprintf(str, sizeof(str) - 1, msg, args); - KPrintf(msg, str); - KPrintf("\n"); + ua_print(msg, str); + ua_print("\n"); // printf("\n"); fflush(stdout); diff --git a/APP_Framework/Framework/control/plc/interoperability/opcua/ua_api.c b/APP_Framework/Framework/control/plc/interoperability/opcua/ua_api.c index e2e5fdb3e..4512368be 100755 --- a/APP_Framework/Framework/control/plc/interoperability/opcua/ua_api.c +++ b/APP_Framework/Framework/control/plc/interoperability/opcua/ua_api.c @@ -28,7 +28,7 @@ int ua_open(void *dev) param->client = UA_Client_new(); - ua_pr_info("ua: [%s] start ...\n", __func__); + ua_notice("ua: [%s] start ...\n", __func__); if (param->client == NULL) { @@ -39,11 +39,11 @@ int ua_open(void *dev) UA_ClientConfig *config = UA_Client_getConfig(param->client); UA_ClientConfig_setDefault(config); - ua_pr_info("ua: [%s] %d %s\n", __func__, strlen(param->ua_remote_ip), param->ua_remote_ip); + ua_notice("ua: [%s] %d %s\n", __func__, strlen(param->ua_remote_ip), param->ua_remote_ip); UA_StatusCode retval = UA_Client_connect(param->client, param->ua_remote_ip); if(retval != UA_STATUSCODE_GOOD) { - ua_pr_info("ua: [%s] deleted ret %x!\n", __func__, retval); + ua_notice("ua: [%s] deleted ret %x!\n", __func__, retval); return (int)retval; } return EOK; @@ -52,7 +52,6 @@ int ua_open(void *dev) void ua_close(void *dev) { UaParamType *param = (UaParamType *)dev; - UA_Client_disconnect(param->client); UA_Client_delete(param->client); /* Disconnects the client internally */ } diff --git a/APP_Framework/Framework/control/plc/interoperability/opcua/ua_api.h b/APP_Framework/Framework/control/plc/interoperability/opcua/ua_api.h index 903d9c6aa..7da7a4faa 100755 --- a/APP_Framework/Framework/control/plc/interoperability/opcua/ua_api.h +++ b/APP_Framework/Framework/control/plc/interoperability/opcua/ua_api.h @@ -43,8 +43,9 @@ typedef struct UaParam #define ua_print //KPrintf #define ua_trace() //KPrintf("ua: [%s] line %d checked!\n", __func__, __LINE__) -#define ua_pr_info KPrintf +#define ua_notice KPrintf #define ua_debug //KPrintf +#define ua_error KPrintf extern const char *opc_server_url; extern char test_ua_ip[]; diff --git a/APP_Framework/Framework/control/plc/interoperability/opcua/ua_client.c b/APP_Framework/Framework/control/plc/interoperability/opcua/ua_client.c index 65a4ecb43..749025f75 100755 --- a/APP_Framework/Framework/control/plc/interoperability/opcua/ua_client.c +++ b/APP_Framework/Framework/control/plc/interoperability/opcua/ua_client.c @@ -42,7 +42,7 @@ static UA_StatusCode nodeIter(UA_NodeId childId, UA_Boolean isInverse, UA_NodeId } UA_NodeId* parent = (UA_NodeId*)handle; - ua_pr_info("%d, %d --- %d ---> NodeId %d, %d\n", + ua_notice("%d, %d --- %d ---> NodeId %d, %d\n", parent->namespaceIndex, parent->identifier.numeric, referenceTypeId.identifier.numeric, childId.namespaceIndex, childId.identifier.numeric); @@ -81,38 +81,38 @@ void ua_print_value(UA_Variant* val) if(val->type == &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]) { UA_LocalizedText* ptr = (UA_LocalizedText*)val->data; - ua_pr_info("%.*s (Text)\n", ptr->text.length, ptr->text.data); + ua_notice("%.*s (Text)\n", ptr->text.length, ptr->text.data); } else if(val->type == &UA_TYPES[UA_TYPES_UINT32]) { UA_UInt32* ptr = (UA_UInt32*)val->data; - ua_pr_info("%d (UInt32)\n", *ptr); + ua_notice("%d (UInt32)\n", *ptr); } else if(val->type == &UA_TYPES[UA_TYPES_BOOLEAN]) { UA_Boolean* ptr = (UA_Boolean*)val->data; - ua_pr_info("%i (BOOL)\n", *ptr); + ua_notice("%i (BOOL)\n", *ptr); } else if(val->type == &UA_TYPES[UA_TYPES_INT32]) { UA_Int32* ptr = (UA_Int32*)val->data; - ua_pr_info("%d (Int32)\n", *ptr); + ua_notice("%d (Int32)\n", *ptr); } else if(val->type == &UA_TYPES[UA_TYPES_INT16]) { UA_Int16* ptr = (UA_Int16*)val->data; - ua_pr_info("%d (Int16)\n", *ptr); + ua_notice("%d (Int16)\n", *ptr); } else if(val->type == &UA_TYPES[UA_TYPES_STRING]) { UA_String* ptr = (UA_String*)val->data; - ua_pr_info("%*.s (String)\n", ptr->length, ptr->data); + ua_notice("%*.s (String)\n", ptr->length, ptr->data); } else if(val->type == &UA_TYPES[UA_TYPES_DATETIME]) { UA_DateTime* ptr = (UA_DateTime*)val->data; UA_DateTimeStruct dts = UA_DateTime_toStruct(*ptr); - ua_pr_info("%d-%d-%d %d:%d:%d.%03d (Time)\n", + ua_notice("%d-%d-%d %d:%d:%d.%03d (Time)\n", dts.day, dts.month, dts.year, dts.hour, dts.min, dts.sec, dts.milliSec); } } @@ -144,14 +144,14 @@ void ua_print_nodeid(UA_NodeId *node_id) switch(node_id->identifierType) { case UA_NODEIDTYPE_NUMERIC: - ua_pr_info(" NodeID n%d,%d ", node_id->namespaceIndex, node_id->identifier.numeric); + ua_notice(" NodeID n%d,%d ", node_id->namespaceIndex, node_id->identifier.numeric); break; case UA_NODEIDTYPE_STRING: - ua_pr_info(" NodeID n%d,%.*s ", node_id->namespaceIndex, node_id->identifier.string.length, + ua_notice(" NodeID n%d,%.*s ", node_id->namespaceIndex, node_id->identifier.string.length, node_id->identifier.string.data); break; case UA_NODEIDTYPE_BYTESTRING: - ua_pr_info(" NodeID n%d,%s ", node_id->namespaceIndex, node_id->identifier.byteString.data); + ua_notice(" NodeID n%d,%s ", node_id->namespaceIndex, node_id->identifier.byteString.data); break; default: break; @@ -160,7 +160,7 @@ void ua_print_nodeid(UA_NodeId *node_id) void ua_print_object(UA_BrowseResponse* res) { - ua_pr_info("%-9s %-16s %-16s %-16s\n", "NAMESPACE", "NODEID", "BROWSE NAME", "DISPLAY NAME"); + ua_notice("%-9s %-16s %-16s %-16s\n", "NAMESPACE", "NODEID", "BROWSE NAME", "DISPLAY NAME"); for(size_t i = 0; i < res->resultsSize; ++i) { @@ -170,14 +170,14 @@ void ua_print_object(UA_BrowseResponse* res) if(ref->nodeId.nodeId.identifierType == UA_NODEIDTYPE_NUMERIC) { - ua_pr_info("%-9d %-16d %-16.*s %-16.*s\n", ref->nodeId.nodeId.namespaceIndex, + ua_notice("%-9d %-16d %-16.*s %-16.*s\n", ref->nodeId.nodeId.namespaceIndex, ref->nodeId.nodeId.identifier.numeric, (int)ref->browseName.name.length, ref->browseName.name.data, (int)ref->displayName.text.length, ref->displayName.text.data); } else if(ref->nodeId.nodeId.identifierType == UA_NODEIDTYPE_STRING) { - ua_pr_info("%-9d %-16.*s %-16.*s %-16.*s\n", ref->nodeId.nodeId.namespaceIndex, + ua_notice("%-9d %-16.*s %-16.*s %-16.*s\n", ref->nodeId.nodeId.namespaceIndex, (int)ref->nodeId.nodeId.identifier.string.length, ref->nodeId.nodeId.identifier.string.data, (int)ref->browseName.name.length, ref->browseName.name.data, @@ -188,7 +188,7 @@ void ua_print_object(UA_BrowseResponse* res) } } - ua_pr_info("\n"); + ua_notice("\n"); } UA_StatusCode ua_read_array_value(UA_Client* client, int array_size, UA_ReadValueId* array) @@ -203,7 +203,7 @@ UA_StatusCode ua_read_array_value(UA_Client* client, int array_size, UA_ReadValu || (response.resultsSize != array_size)) { UA_ReadResponse_clear(&response); - ua_pr_info("ua: [%s] read failed 0x%x\n", __func__, + ua_notice("ua: [%s] read failed 0x%x\n", __func__, response.responseHeader.serviceResult); return UA_STATUSCODE_BADUNEXPECTEDERROR; } @@ -215,11 +215,11 @@ UA_StatusCode ua_read_array_value(UA_Client* client, int array_size, UA_ReadValu if((response.results[i].status == UA_STATUSCODE_GOOD) && (response.results[i].hasValue)) { - ua_pr_info("node %s: ", ua_get_nodeid_str(&array[i].nodeId)); + ua_notice("node %s: ", ua_get_nodeid_str(&array[i].nodeId)); ua_print_value(&response.results[i].value); } } - ua_pr_info("\n"); + ua_notice("\n"); free(arr_ret); UA_ReadResponse_clear(&response); @@ -229,7 +229,7 @@ UA_StatusCode ua_read_array_value(UA_Client* client, int array_size, UA_ReadValu void ua_browser_id(UA_Client* client, UA_NodeId id) { /* Browse some objects */ - ua_pr_info("Browsing nodes in objects folder:\n"); + ua_notice("Browsing nodes in objects folder:\n"); UA_BrowseRequest bReq; UA_BrowseRequest_init(&bReq); bReq.requestedMaxReferencesPerNode = 0; @@ -327,7 +327,7 @@ void ua_write_nodeid_value(UA_Client* client, UA_NodeId id, char* value) if(wResp.responseHeader.serviceResult == UA_STATUSCODE_GOOD) { - ua_pr_info("write new value is: %s\n", value); + ua_notice("write new value is: %s\n", value); } UA_WriteRequest_clear(&wReq); @@ -489,7 +489,7 @@ void ua_read_time(UA_Client* client) { UA_DateTime raw_date = *(UA_DateTime*) value.data; UA_DateTimeStruct dts = UA_DateTime_toStruct(raw_date); - ua_pr_info("date is: %d-%d-%d %d:%d:%d.%03d\n", + ua_notice("date is: %d-%d-%d %d:%d:%d.%03d\n", dts.day, dts.month, dts.year, dts.hour, dts.min, dts.sec, dts.milliSec); } diff --git a/APP_Framework/Framework/control/plc/interoperability/opcua/ua_test.c b/APP_Framework/Framework/control/plc/interoperability/opcua/ua_test.c index 329118bfc..d11b86896 100755 --- a/APP_Framework/Framework/control/plc/interoperability/opcua/ua_test.c +++ b/APP_Framework/Framework/control/plc/interoperability/opcua/ua_test.c @@ -53,7 +53,7 @@ void ua_test_browser_objects(UA_Client *client) ua_browser_id(client, UA_TEST_BROWSER_NODEID); ua_browser_id(client, UA_TEST_BROWSER_NODEID1); test_id = UA_TEST_BROWSER_NODEID1; - ua_pr_info("Show values in %s:\n", ua_get_nodeid_str(&test_id)); + ua_notice("Show values in %s:\n", ua_get_nodeid_str(&test_id)); ua_test_read_array(client); return; } @@ -64,11 +64,11 @@ void ua_test_write_attr(UA_Client *client) char val_str[UA_NODE_LEN]; UA_NodeId id = UA_TEST_WRITE_NODEID; - ua_pr_info("--- Test write %s ---\n", ua_get_nodeid_str(&id)); + ua_notice("--- Test write %s ---\n", ua_get_nodeid_str(&id)); ua_read_nodeid_value(client, id, &value); ua_write_nodeid_value(client, id, itoa(value + 1, val_str, 10)); ua_read_nodeid_value(client, id, &value); - ua_pr_info("\n"); + ua_notice("\n"); } int ua_test_interact_server(UA_Client *client) diff --git a/APP_Framework/Framework/control/plc/interoperability/socket/Kconfig b/APP_Framework/Framework/control/plc/interoperability/socket/Kconfig new file mode 100755 index 000000000..d8325aa40 --- /dev/null +++ b/APP_Framework/Framework/control/plc/interoperability/socket/Kconfig @@ -0,0 +1,11 @@ + +menuconfig USING_CONTROL_PLC_OPCUA + bool "PLC support OPCUA" + default y + depends on RESOURCES_LWIP + +menuconfig USING_CONTROL_PLC_SOCKET + bool "PLC support SOCKET" + default y + depends on RESOURCES_LWIP + diff --git a/APP_Framework/Framework/control/plc/interoperability/socket/Makefile b/APP_Framework/Framework/control/plc/interoperability/socket/Makefile new file mode 100755 index 000000000..2c3e00b48 --- /dev/null +++ b/APP_Framework/Framework/control/plc/interoperability/socket/Makefile @@ -0,0 +1,4 @@ +SRC_FILES := plc_socket.c + +include $(KERNEL_ROOT)/compiler.mk + diff --git a/APP_Framework/Framework/control/plc/interoperability/socket/br_socket.c b/APP_Framework/Framework/control/plc/interoperability/socket/br_socket.c new file mode 100755 index 000000000..a655a0a67 --- /dev/null +++ b/APP_Framework/Framework/control/plc/interoperability/socket/br_socket.c @@ -0,0 +1,1867 @@ +#include +#include +#include "plc_socket.h" + +#define xs_kprintf KPrintf +#define xs_device_t uint32_t + + +static unsigned char data_head = 0x5A; +static char device_s14[] = "S14"; +static char device_s15[] = "S15"; +static char device_s16[] = "S16"; +static char device_s17[] = "S17"; +static char device_s18[] = "S18"; +static char data_end[] = "#"; +unsigned char redis_data[1024]; + +// 创建一个信号量,用于接收消息的同步 +static sem_t Ch_Sem = NULL; +static sem_t Rx_Sem = NULL; + +extern xs_device_t ec200t; + +//for SIEMENS TCP read data cmd +const unsigned char handshake_first[] = +{ + 0x3, 0x00, 0x00, 0x16, 0x11, 0xe0, 0x00, 0x00, 0x02, + 0xc8,0x00,0xc1,0x02,0x02,0x01,0xc2,0x02,0x02,0x01,0xc0,0x01,0x0a +}; + +const unsigned char handshake_second[] = +{ + 0x3,0x00,0x00,0x19,0x02,0xf0,0x80,0x32,0x01,0x00,0x00,0x00,0x0d,0x00,0x08,0x00,0x00,0xf0,0x00,0x00,0x01,0x00,0x01,0x00,0xf0 +}; + +const unsigned char siemens_read_data[] = +{ + 0x3,0x00,0x00, 0x1f,0x02,0xf0,0x80,0x32,0x01,0x00,0x00,0x33,0x01,0x00,0x0e,0x00,0x00,0x04,0x01,0x12,0x0a,0x10,0x02,0x00,0xD2,0x00,0x34,0x84,0x00,0x00,0x00 +}; + +//for OML UDP read data cmd +const unsigned char UDP_read_data[] = +{ + 0x80,0x00,0x02,0x00,0x03,0x00,0x00,0x7E,0x00,0x00,0x01,0x01,0x82,0x0F,0xA0,0x00,0x00,0x20 +}; + +//for SIEMENS 1200 read data cmd +const unsigned char handshake_1200_first[] = +{ + 0x03, 0x00, 0x00, 0x16, 0x11, 0xE0, 0x00, 0x00, 0x02, 0xC8, 0x00, 0xC1, 0x02, 0x02, 0x01, 0xC2, 0x02, 0x02, 0x01, 0xC0, 0x01, 0x0A +}; + +const unsigned char handshake_1200_second[] = +{ + 0x03, 0x00, 0x00, 0x19, 0x02, 0xF0, 0x80, 0x32, 0x01, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x08, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0xF0 +}; + +const unsigned char siemens_1200_read_data[] = +{ + 0x03, 0x00, 0x00, 0x1F, 0x02, 0xF0, 0x80, 0x32, 0x01, 0x00, 0x00, 0x33, 0x01, 0x00, 0x0E, 0x00, 0x00, 0x04, 0x01, 0x12, 0x0A, 0x10, 0x02, 0x00, 0xD2, 0x00, 0x34, 0x84, 0x00, 0x00, 0x00 +}; + +#define PUT_ULONG_BE(n,b,i) \ +{ \ + (b)[(i) ] = (uint8_t) ( (n) >> 24 ); \ + (b)[(i) + 1] = (uint8_t) ( (n) >> 16 ); \ + (b)[(i) + 2] = (uint8_t) ( (n) >> 8 ); \ + (b)[(i) + 3] = (uint8_t) ( (n) ); \ +} + +#define GET_ULONG_BE(n,b,i) \ +{ \ + (n) = ( (uint32_t) (b)[(i) ] << 24 ) \ + | ( (uint32_t) (b)[(i) + 1] << 16 ) \ + | ( (uint32_t) (b)[(i) + 2] << 8 ) \ + | ( (uint32_t) (b)[(i) + 3] ); \ +} + +#define XS_SERIAL_RB_BUFSZ 128 + +char rx_buffer_cmp[256 + 1]; +char msg_pool[256]; +char rx_buffer[XS_SERIAL_RB_BUFSZ + 1] = {0}; +#define WIFI_RESET 73 + +#define LEN_PARA_BUF 128 +uint8_t server_addr_wifi[LEN_PARA_BUF] = "192.168.23.181"; //??????? +uint8_t server_port_wifi[LEN_PARA_BUF] = "9999"; //?????? + +uint8_t WIFI_ssid[LEN_PARA_BUF] = "xiaoshanos"; //WIFI? +uint8_t WIFI_pwd[LEN_PARA_BUF] = "12345678"; //WIFI???? + +#if 0 +/* ?????? */ +xs_device_t hfa21; +/* ??????? */ +struct xs_MessageQueue* rx3_mq; +/* ???????? */ + +void hfa21_sta_config(void) +{ + xs_kprintf("hfa21_sta_config start\r\n"); + uint8_t i,step; + uint8_t cmd[LEN_PARA_BUF]; + step = 1; + strcpy(cmd, "+++"); + //send + xs_DeviceWrite(hfa21, 0, cmd, 3); + xs_kprintf("cmd.%d=%s\r\n",step,cmd); + step = 2; + memset(cmd,0,sizeof(cmd)); + xs_MdelayKTask(100); + strcpy(cmd,"a"); + //send + xs_DeviceWrite(hfa21, 0, cmd, 1); + xs_kprintf("cmd.%d=%s\r\n",step,cmd); + step = 3; + memset(cmd,0,sizeof(cmd)); + xs_MdelayKTask(2500); + strcpy(cmd, "AT+FCLR\r\n"); + + //send + for(i=0; i 1000) + { + xs_kprintf("data length too long\n "); + return; + } + + xs_DeviceWrite(hfa21, 0, buf, len); + return ; +} + +void msg_send_once(void) +{ + uint8_t msg[1024] = { 0}; + uint32_t i = 0; + xs_kprintf("ap test, send msg to server : "); + memset(msg,0x37,1024); + xs_memcpy(msg,"arm:dev7,data:",14); + msg[1023] = '\n'; + xs_DeviceWrite(hfa21,0, msg, 1024); + // xs_MdelayKTask(1000); + return ; +} +MSH_CMD_EXPORT(msg_send_once, asd); + +void msg_send_nointerval(void) +{ + uint8_t msg[1024] = { 0}; + uint32_t i = 1; + uint32_t index = 0; + uint8_t seq[10] = { 0}; + uint8_t* prefix = "{board:stm32f407-st-discovery,arch:arm,device:7,seq:"; + xs_kprintf("ap test, send msg to server : "); + + for(;;) + { + index = 0; + memset(msg,0x37,1024); + memset(seq,0,10); + msg[1021] = '}'; + msg[1022] = '\n'; + msg[1023] = 0; + xs_memcpy(msg,prefix,strlen(prefix)); + index = index + strlen(prefix); + PUT_ULONG_BE(i, msg, index) + index = index + 4; + xs_memcpy(msg+index,",data:",6); + xs_DeviceWrite(hfa21,0, msg, 1024); + ++ i; + } + + return ; +} +MSH_CMD_EXPORT(msg_send_nointerval, asd); + +void msg_send_interval(void) +{ + uint8_t msg[1024] = { 0}; + uint32_t i = 1; + uint32_t index = 0; + uint8_t seq[4] = { 0}; + uint8_t* prefix = "{board:stm32f407-st-discovery,arch:arm,device:7,seq:"; + xs_kprintf("ap test, send msg to server : "); + + for(;;) + { + index = 0; + memset(msg,0x37,1024); + memset(seq,0,10); + msg[1021] = '}'; + msg[1022] = '\n'; + msg[1023] = 0; + xs_memcpy(msg,prefix,strlen(prefix)); + index = index + strlen(prefix); + PUT_ULONG_BE(i, msg, index) + index = index + 4; + xs_memcpy(msg+index,",data:",6); + xs_DeviceWrite(hfa21,0, msg, 1024); + xs_MdelayKTask(1000); + ++ i; + } + + return ; +} +MSH_CMD_EXPORT(msg_send_interval, asd); + +struct xs_Ringbuffer* ringbuffer; + +uint8_t stack[256] = {0}; +xs_uint16 data_index = 0 ; +uint8_t start = 0xaa; +uint8_t end = 0xbb; + +uint8_t lora_buffer[8][256] = {0}; + +static void read_ringbuffer_thread_entry(void* parameter) +{ + uint8_t ch = 0; + xs_uint16 index = 0 ; + uint8_t devicenumber; + + while(1) + { + if(1 == xs_GetRingBufferchar(ringbuffer,&ch)) + { + if(data_index < 256) + { + stack[data_index++] = ch; + + if(data_index > 2) + { + if(stack[data_index-1] == start && stack[data_index-2] == start) + { + data_index = 0; + stack[data_index++] == start; + stack[data_index++] == start; + } + else if(stack[data_index-1] == end && stack[data_index-2] == end) + { + //end + devicenumber = stack[3] - 0x30; + + if(devicenumber > 0 && devicenumber < 8) + { + memset(lora_buffer[devicenumber-1],0,256); + memcpy(lora_buffer[devicenumber-1], &stack[2],data_index - 4); + // xs_kprintf("lora data: %s\n",lora_buffer[devicenumber-1]); + } + + data_index = 0; + } + } + } + else + { + data_index = 0; + } + } + else + { + xs_MdelayKTask(20); + } + } +} +#endif + +#if 0 +/***********************************************************************/ +//欧姆龙PLC IP 192.168.250.3 port 9600 + +static xs_err_t oml_uart_input(xs_device_t dev, xs_size_t size) +{ + xs_KSemaphoreAbandon(Ch_Sem); + return XS_EOK; +} + +static void oml_plc_thread(void* parameter) +{ + xs_err_t ret; + uint32_t rx_length, total_rx_length = 0; + uint8_t i; + unsigned int length = 0; + + while(1) + { + ret = xs_KSemaphoreObtain(Ch_Sem, XS_WAITING_FOREVER); + + if(XS_EOK == ret) + { + rx_length = xs_DeviceRead(hfa21, 0, redis_data + 1 + sizeof(device_s14) + total_rx_length, 78); + xs_kprintf("dst data length %d total length %d\n", rx_length, total_rx_length); + + for(i = 0; i < rx_length; ++i) + { + xs_kprintf("0x%x ", redis_data[i + 1 + sizeof(device_s14) + total_rx_length]); + } + + xs_kprintf("\n"); + total_rx_length += rx_length; + + if((78 == total_rx_length) && (0xC0 == redis_data[1 + sizeof(device_s14)]) + && (0x00 == redis_data[1 + sizeof(device_s14) + 1]) + && (0x02 == redis_data[1 + sizeof(device_s14) + 2]) + && (0x00 == redis_data[1 + sizeof(device_s14) + 3])) + { + /******format redis data******/ + memcpy(redis_data, &data_head, 1); + memcpy(redis_data + 1, device_s14, sizeof(device_s14)); + memcpy(redis_data + 1 + sizeof(device_s14) + total_rx_length, data_end, sizeof(data_end)); + length = 1 + sizeof(device_s14) + total_rx_length + sizeof(data_end); + /******end******/ + xs_DeviceWrite(ec200t, 0, redis_data, length); + total_rx_length = 0; + memset(redis_data, 0, sizeof(redis_data)); + xs_KSemaphoreAbandon(Rx_Sem); + } + } + } +} + +int OML_UDP(void) +{ + struct SerialConfigure config3 = XS_SERIAL_CONFIG_DEFAULT; + hfa21 = xs_DeviceFind("uart3"); + + if(!hfa21) + { + xs_kprintf("find dev.hfa21 failed!\r\n"); + return XS_ERROR; + } + + Ch_Sem = xs_KCreateSemaphore("Ch_Sem",0,XS_LINKLIST_FLAG_FIFO); + + if(Ch_Sem == NULL) + { + xs_kprintf("create Ch_sem failed .\n"); + return XS_ERROR; + } + + Rx_Sem = xs_KCreateSemaphore("Rx_Sem",0,XS_LINKLIST_FLAG_FIFO); + + if(Rx_Sem == NULL) + { + xs_kprintf("create Rx_sem failed .\n"); + return XS_ERROR; + } + + //hfa21 + config3.BaudRate = BAUD_RATE_460800; + config3.DataBits = DATA_BITS_8; + config3.StopBits = STOP_BITS_1; + config3.bufsz = XS_SERIAL_RB_BUFSZ; + config3.parity = PARITY_NONE; + xs_DeviceControl(hfa21, XS_DEVICE_CTRL_CONFIG, &config3); + xs_DeviceOpen(hfa21,XS_DEVICE_FLAG_DMA_RX); + xs_DeviceSetRxIndicate(hfa21, oml_uart_input); + xs_kprintf("hfa21 init success!\r\n"); + TaskDescriptorPointer thread_oml_plc = xs_KTaskCreate("oml_hfa21", oml_plc_thread, NULL, 1024, 25); + + if(thread_oml_plc != NULL) + { + xs_StartupKTask(thread_oml_plc); + } + else + { + return XS_ERROR; + } + + xs_MdelayKTask(10000); + xs_kprintf("The UDP send_receive function is running......\n"); + + while(1) + { +// CH438UARTSend(6, UDP_read_data, sizeof(UDP_read_data)); //UDP_read_data + xs_DeviceWrite(hfa21, 0, UDP_read_data, sizeof(UDP_read_data)); + xs_kprintf("hfa21 write cmd\n"); + xs_KSemaphoreObtain(Rx_Sem, XS_WAITING_FOREVER); + xs_MdelayKTask(1000); + } + + return XS_EOK; +} +MSH_CMD_EXPORT(OML_UDP, oml); + +/*********************************************************************************************/ +// IP 192.168.250.50 port 102 tcp + +//工控机的测试代码,数据帧头FF,帧尾FE,数据帧长度不固?static struct xs_MessageQueue *ipc_mq; +xs_device_t ipc_hfa21; + +static xs_err_t ipc_uart_input(xs_device_t dev, xs_size_t size) +{ + struct rx_msg msg; + xs_err_t result; + msg.dev = dev; + msg.size = size; + result = xs_KMsgQueueSend(ipc_mq, &msg, sizeof(msg)); + + if(result == -XS_EFULL) + { + xs_kprintf("ipc_mq message queue full!\r\n"); + } + + xs_KSemaphoreAbandon(Ch_Sem); + return result; +} + +static void ipc_plc_thread(void* parameter) +{ + struct rx_msg msg; + xs_err_t ret; + uint32_t rx_length, total_rx_length = 0; + uint8_t i; + unsigned int length = 0; + + while(1) + { + ret = xs_KSemaphoreObtain(Ch_Sem, XS_WAITING_FOREVER); + + if(XS_EOK == ret) + { + xs_memset(&msg, 0, sizeof(msg)); + ret = xs_KMsgQueueRecv(ipc_mq, &msg, sizeof(msg), XS_WAITING_FOREVER); + + if(XS_EOK == ret) + { + rx_length = xs_DeviceRead(ipc_hfa21, 0, redis_data + 2 + sizeof(device_s15) + total_rx_length, msg.size); + xs_kprintf("dst data length %d total length %d\n", rx_length, total_rx_length); + + for(i = 0; i < rx_length; ++i) + { + xs_kprintf("0x%x ", redis_data[i + 2 + sizeof(device_s15) + total_rx_length]); + } + + xs_kprintf("\n"); + total_rx_length += rx_length; + + if((0x46 == redis_data[2 + sizeof(device_s15)]) && (0x46 == redis_data[2 + sizeof(device_s15) + 1]) && + (0x45 == redis_data[2 + sizeof(device_s15) + total_rx_length - 1]) && (0x46 == redis_data[2 + sizeof(device_s15) + total_rx_length - 2])) + { + /******format redis data******/ + redis_data[0] = data_head; + redis_data[1] = PLC_IPC_FLAG; + memcpy(redis_data + 2, device_s15, sizeof(device_s15)); + length = 2 + sizeof(device_s15) + total_rx_length; + /******end******/ + // xs_kprintf("redis data : \n"); + // for(i = 0; i < length; ++i) + // xs_kprintf("0x%x ", redis_data[i]); + // xs_kprintf("\n"); + xs_DeviceWrite(ec200t, 0, redis_data, length); + total_rx_length = 0; + memset(redis_data, 0, sizeof(redis_data)); + xs_KSemaphoreAbandon(Rx_Sem); + } + } + } + } +} + +int BRL_IPC(void) +{ + struct SerialConfigure config3 = XS_SERIAL_CONFIG_DEFAULT; + ipc_hfa21 = xs_DeviceFind("uart3"); + + if(!ipc_hfa21) + { + xs_kprintf("find ipc_hfa21 failed!\r\n"); + return XS_ERROR; + } + + Ch_Sem = xs_KCreateSemaphore("Ch_Sem",0,XS_LINKLIST_FLAG_FIFO); + + if(Ch_Sem == NULL) + { + xs_kprintf("BRL_IPC create sem failed .\n"); + return XS_ERROR; + } + + Rx_Sem = xs_KCreateSemaphore("Rx_Sem",0,XS_LINKLIST_FLAG_FIFO); + + if(Rx_Sem == NULL) + { + xs_kprintf("BRL_IPC create Rx_sem failed .\n"); + return XS_ERROR; + } + + ipc_mq = xs_KCreateMsgQueue("ipc_mq", + sizeof(struct rx_msg), + sizeof(msg_pool), + XS_LINKLIST_FLAG_FIFO); + + if(ipc_mq == NULL) + { + xs_kprintf("create ipc_mq mutex failed.\n"); + return -1; + } + + //ipc_hfa21 + config3.BaudRate = BAUD_RATE_460800; + config3.DataBits = DATA_BITS_8; + config3.StopBits = STOP_BITS_1; + config3.bufsz = XS_SERIAL_RB_BUFSZ; + config3.parity = PARITY_NONE; + xs_DeviceControl(ipc_hfa21, XS_DEVICE_CTRL_CONFIG, &config3); + xs_DeviceOpen(ipc_hfa21,XS_DEVICE_FLAG_DMA_RX); + xs_DeviceSetRxIndicate(ipc_hfa21, ipc_uart_input); + xs_kprintf("ipc_hfa21 init success!\r\n"); + TaskDescriptorPointer thread_ipc_plc = xs_KTaskCreate("ipc_hfa21", ipc_plc_thread, NULL, 1024, 25); + + if(thread_ipc_plc != NULL) + { + xs_StartupKTask(thread_ipc_plc); + } + else + { + return XS_ERROR; + } + + xs_kprintf("start to receive data...\n"); + + while(1) + { + xs_MdelayKTask(100); + xs_KSemaphoreObtain(Rx_Sem, XS_WAITING_FOREVER); + xs_kprintf("\n"); + } + + return XS_EOK; +} +MSH_CMD_EXPORT(BRL_IPC, IPC server); + +/*********************************************************************************************/ +// IP 192.168.250.150 port 9898 tcp + +//金凤工控机的测试代码,数据帧头FF,帧尾EF +xs_device_t jf_ipc_hfa21; +static sem_t jf_input_sem = NULL; +static sem_t jf_ec200t_sem = NULL; + +static xs_err_t jf_ipc_uart_input(xs_device_t dev, xs_size_t size) +{ + xs_KSemaphoreAbandon(jf_input_sem); + return XS_EOK; +} + +static void jf_ipc_plc_thread(void* parameter) +{ + xs_err_t ret; + uint32_t rx_length, total_rx_length = 0; + uint8_t i; + unsigned int length = 0; + + while(1) + { + ret = xs_KSemaphoreObtain(jf_input_sem, XS_WAITING_FOREVER); + + if(XS_EOK == ret) + { + rx_length = xs_DeviceRead(jf_ipc_hfa21, 0, redis_data + 2 + sizeof(device_s16) + total_rx_length, 512); + xs_kprintf("dst data length %d total length %d\n", rx_length, total_rx_length); + + for(i = 0; i < rx_length; ++i) + { + xs_kprintf("0x%x ", redis_data[i + 2 + sizeof(device_s16) + total_rx_length]); + } + + xs_kprintf("\n"); + total_rx_length += rx_length; + + if(((((unsigned int)redis_data[2 + sizeof(device_s16) + 2] & 0x000000FF) << 8) | ((unsigned int)redis_data[2 + sizeof(device_s16) + 3] & 0x000000FF) == total_rx_length) && + (0xFF == redis_data[2 + sizeof(device_s16)]) && (0x20 == redis_data[2 + sizeof(device_s16) + 1])) + { + /******format redis data******/ + redis_data[0] = data_head; + redis_data[1] = PLC_JF_IPC_FLAG; + memcpy(redis_data + 2, device_s16, sizeof(device_s16)); + length = 2 + sizeof(device_s16) + total_rx_length; + /******end******/ + xs_DeviceWrite(ec200t, 0, redis_data, length); + total_rx_length = 0; + memset(redis_data, 0, sizeof(redis_data)); + xs_KSemaphoreAbandon(jf_ec200t_sem); + } + } + } +} + +int JF_IPC(void) +{ + struct SerialConfigure config3 = XS_SERIAL_CONFIG_DEFAULT; + jf_ipc_hfa21 = xs_DeviceFind("uart3"); + + if(!jf_ipc_hfa21) + { + xs_kprintf("find jf_ipc_hfa21 failed!\r\n"); + return XS_ERROR; + } + + jf_ec200t_sem = xs_KCreateSemaphore("jf_ec200t_sem",0,XS_LINKLIST_FLAG_FIFO); + + if(jf_ec200t_sem == NULL) + { + xs_kprintf("JF_IPC create jf_ec200t_sem failed .\n"); + return XS_ERROR; + } + + jf_input_sem = xs_KCreateSemaphore("jf_input_sem",0,XS_LINKLIST_FLAG_FIFO); + + if(jf_input_sem == NULL) + { + xs_kprintf("JF_IPC create jf_input_sem failed .\n"); + return XS_ERROR; + } + + //jf_ipc_hfa21 + config3.BaudRate = BAUD_RATE_460800; + config3.DataBits = DATA_BITS_8; + config3.StopBits = STOP_BITS_1; + config3.bufsz = XS_SERIAL_RB_BUFSZ; + config3.parity = PARITY_NONE; + xs_DeviceControl(jf_ipc_hfa21, XS_DEVICE_CTRL_CONFIG, &config3); + xs_DeviceOpen(jf_ipc_hfa21,XS_DEVICE_FLAG_DMA_RX); + xs_DeviceSetRxIndicate(jf_ipc_hfa21, jf_ipc_uart_input); + xs_kprintf("jf_ipc_hfa21 init success!\r\n"); + TaskDescriptorPointer thread_jf_ipc_plc = xs_KTaskCreate("jf_ipc_hfa21", jf_ipc_plc_thread, NULL, 1024, 25); + + if(thread_jf_ipc_plc != NULL) + { + xs_StartupKTask(thread_jf_ipc_plc); + } + else + { + return XS_ERROR; + } + + xs_kprintf("start to receive data...\n"); + + while(1) + { + xs_MdelayKTask(100); + xs_KSemaphoreObtain(jf_ec200t_sem, XS_WAITING_FOREVER); + xs_kprintf("JF send data to server done\n"); + } + + return XS_EOK; +} +MSH_CMD_EXPORT(JF_IPC, JF IPC client); + +/*********************************************************************************************/ +//西门子PLC IP 192.168.250.9 port 102 + +static xs_device_t siemens_hfa21; +static sem_t siemens_input_sem = NULL; +static sem_t siemens_ec200t_sem = NULL; + +static xs_err_t siemens_uart_input(xs_device_t dev, xs_size_t size) +{ + xs_KSemaphoreAbandon(siemens_input_sem); + return XS_EOK; +} + +static void siemens_plc_thread(void* parameter) +{ + xs_err_t ret; + uint32_t rx_length, total_rx_length = 0; + uint8_t i; + static char shakehand_cnt = 0; + unsigned int length = 0; + + while(1) + { + ret = xs_KSemaphoreObtain(siemens_input_sem, XS_WAITING_FOREVER); + + if(XS_EOK == ret) + { + //rx_length = xs_DeviceRead(siemens_hfa21, 0, redis_data, 234); + // xs_kprintf("siemens dst data length %d\n", rx_length); + // for(i = 0; i < rx_length; ++i) + // xs_kprintf("0x%x ", redis_data[i]); + // xs_kprintf("\n"); + if(shakehand_cnt < 2) + { + //ignore first two siemens input sem + xs_DeviceRead(siemens_hfa21, 0, redis_data + 2 + sizeof(device_s17), 87); + shakehand_cnt++; + continue; + } + + rx_length = xs_DeviceRead(siemens_hfa21, 0, redis_data + 2 + sizeof(device_s17) + total_rx_length, 87); + xs_kprintf("siemens dst data length %d total length %d\n", rx_length, total_rx_length); + + for(i = 0; i < rx_length; ++i) + { + xs_kprintf("0x%x ", redis_data[i + 2 + sizeof(device_s17) + total_rx_length]); + } + + xs_kprintf("\n"); + total_rx_length += rx_length; + + if((87 == total_rx_length) && (0x03 == redis_data[2 + sizeof(device_s17)]) + && (0x00 == redis_data[2 + sizeof(device_s17) + 1]) + && (0x00 == redis_data[2 + sizeof(device_s17) + 2]) + && (0x57 == redis_data[2 + sizeof(device_s17) + 3])) + { + /******format redis data******/ + redis_data[0] = data_head; + redis_data[1] = PLC_SIEMENS_FLAG; + memcpy(redis_data + 2, device_s17, sizeof(device_s17)); + length = 2 + sizeof(device_s17) + total_rx_length; + /******end******/ + xs_DeviceWrite(ec200t, 0, redis_data, length); + total_rx_length = 0; + memset(redis_data, 0, sizeof(redis_data)); + xs_KSemaphoreAbandon(siemens_ec200t_sem); + } + } + } +} + +int SIEMENS_TCP(void) +{ + xs_err_t result; + struct SerialConfigure config3 = XS_SERIAL_CONFIG_DEFAULT; + siemens_hfa21 = xs_DeviceFind("uart3"); + + if(!siemens_hfa21) + { + xs_kprintf("find siemens_hfa21 failed!\r\n"); + return XS_ERROR; + } + + siemens_input_sem = xs_KCreateSemaphore("siemens_input_sem", 0, XS_LINKLIST_FLAG_FIFO); + + if(siemens_input_sem == NULL) + { + xs_kprintf("siemens_hfa21 create siemens_input_sem failed.\n"); + return XS_ERROR; + } + + siemens_ec200t_sem = xs_KCreateSemaphore("siemens_ec200t_sem", 0, XS_LINKLIST_FLAG_FIFO); + + if(siemens_ec200t_sem == NULL) + { + xs_kprintf("siemens_hfa21 create siemens_ec200t_sem failed.\n"); + return XS_ERROR; + } + + //siemens_hfa21 + config3.BaudRate = BAUD_RATE_460800; + config3.DataBits = DATA_BITS_8; + config3.StopBits = STOP_BITS_1; + config3.bufsz = XS_SERIAL_RB_BUFSZ; + config3.parity = PARITY_NONE; + xs_DeviceControl(siemens_hfa21, XS_DEVICE_CTRL_CONFIG, &config3); + xs_DeviceOpen(siemens_hfa21, XS_DEVICE_FLAG_DMA_RX); + xs_DeviceSetRxIndicate(siemens_hfa21, siemens_uart_input); + TaskDescriptorPointer thread_siemens_plc = xs_KTaskCreate("siemens_hfa21", siemens_plc_thread, NULL, 1024, 25); + + if(thread_siemens_plc != NULL) + { + xs_StartupKTask(thread_siemens_plc); + } + else + { + return XS_ERROR; + } + + //step1 : send handshake_first cmd, waiting for response sem + xs_kprintf("siemens_hfa21 start send handshake_first!\r\n"); + //CH438UARTSend(6, handshake_first, sizeof(handshake_first)); + xs_DeviceWrite(siemens_hfa21, 0, handshake_first, sizeof(handshake_first)); + xs_MdelayKTask(3000); + xs_DeviceWrite(siemens_hfa21, 0, handshake_first, sizeof(handshake_first)); + xs_MdelayKTask(500); + //step2 : send handshake_second cmd, waiting for response sem + xs_kprintf("siemens_hfa21 start send handshake_second!\r\n"); + //CH438UARTSend(6, handshake_second, sizeof(handshake_second)); + xs_DeviceWrite(siemens_hfa21, 0, handshake_second, sizeof(handshake_second)); + xs_kprintf("siemens_hfa21 init success!\r\n"); + xs_MdelayKTask(500); + + while(1) + { + //CH438UARTSend(6, siemens_read_data, sizeof(siemens_read_data)); //read_data + xs_DeviceWrite(siemens_hfa21, 0, siemens_read_data, sizeof(siemens_read_data)); + xs_kprintf("siemens hfa21 write cmd\n"); + xs_KSemaphoreObtain(siemens_ec200t_sem, XS_WAITING_FOREVER); + xs_MdelayKTask(1000); + } + + return XS_EOK; +} +MSH_CMD_EXPORT(SIEMENS_TCP, Siemens TCP PLC); + +/*********************************************************************************************/ +//西门?1200 PLC IP 192.168.250.107 port 102 + +static xs_device_t siemens_1200_hfa21; +static sem_t siemens_1200_input_sem = NULL; +static sem_t siemens_1200_ec200t_sem = NULL; + +static xs_err_t siemens_1200_uart_input(xs_device_t dev, xs_size_t size) +{ + xs_KSemaphoreAbandon(siemens_1200_input_sem); + return XS_EOK; +} + +static void siemens_1200_plc_thread(void* parameter) +{ + xs_err_t ret; + uint32_t rx_length, total_rx_length = 0; + uint8_t i; + static char shakehand_cnt = 0; + unsigned int length = 0; + + while(1) + { + ret = xs_KSemaphoreObtain(siemens_1200_input_sem, XS_WAITING_FOREVER); + + if(XS_EOK == ret) + { + if(shakehand_cnt < 2) + { + //ignore first two siemens input sem + xs_DeviceRead(siemens_1200_hfa21, 0, redis_data + 2 + sizeof(device_s18), 235); + shakehand_cnt++; + continue; + } + + rx_length = xs_DeviceRead(siemens_1200_hfa21, 0, redis_data + 2 + sizeof(device_s18) + total_rx_length, 235); + xs_kprintf("siemens 1200 dst data length %d total length %d\n", rx_length, total_rx_length); + + for(i = 0; i < rx_length; ++i) + { + xs_kprintf("0x%x ", redis_data[i + 2 + sizeof(device_s18) + total_rx_length]); + } + + xs_kprintf("\n"); + total_rx_length += rx_length; + + if((235 == total_rx_length) && (0x03 == redis_data[2 + sizeof(device_s18)]) + && (0x00 == redis_data[2 + sizeof(device_s18) + 1]) + && (0x00 == redis_data[2 + sizeof(device_s18) + 2]) + && (0xEB == redis_data[2 + sizeof(device_s18) + 3])) + { + /******format redis data******/ + redis_data[0] = data_head; + redis_data[1] = PLC_SIEMENS_1200_FLAG; + memcpy(redis_data + 2, device_s18, sizeof(device_s18)); + length = 2 + sizeof(device_s18) + total_rx_length; + /******end******/ + xs_DeviceWrite(ec200t, 0, redis_data, length); + total_rx_length = 0; + memset(redis_data, 0, sizeof(redis_data)); + xs_KSemaphoreAbandon(siemens_1200_ec200t_sem); + } + } + } +} + +int SIEMENS_1200(void) +{ + xs_err_t result; + struct SerialConfigure config3 = XS_SERIAL_CONFIG_DEFAULT; + siemens_1200_hfa21 = xs_DeviceFind("uart3"); + + if(!siemens_1200_hfa21) + { + xs_kprintf("find siemens_1200_hfa21 failed!\r\n"); + return XS_ERROR; + } + + siemens_1200_input_sem = xs_KCreateSemaphore("siemens_1200_input_sem", 0, XS_LINKLIST_FLAG_FIFO); + + if(siemens_1200_input_sem == NULL) + { + xs_kprintf("siemens_1200_hfa21 create siemens_1200_input_sem failed.\n"); + return XS_ERROR; + } + + siemens_1200_ec200t_sem = xs_KCreateSemaphore("siemens_1200_ec200t_sem", 0, XS_LINKLIST_FLAG_FIFO); + + if(siemens_1200_ec200t_sem == NULL) + { + xs_kprintf("siemens_1200_hfa21 create siemens_1200_ec200t_sem failed.\n"); + return XS_ERROR; + } + + //siemens_hfa21 + config3.BaudRate = BAUD_RATE_460800; + config3.DataBits = DATA_BITS_8; + config3.StopBits = STOP_BITS_1; + config3.bufsz = XS_SERIAL_RB_BUFSZ; + config3.parity = PARITY_NONE; + xs_DeviceControl(siemens_1200_hfa21, XS_DEVICE_CTRL_CONFIG, &config3); + xs_DeviceOpen(siemens_1200_hfa21, XS_DEVICE_FLAG_DMA_RX); + xs_DeviceSetRxIndicate(siemens_1200_hfa21, siemens_1200_uart_input); + TaskDescriptorPointer thread_siemens_1200_plc = xs_KTaskCreate("siemens_1200_hfa21", siemens_1200_plc_thread, NULL, 1024, 25); + + if(thread_siemens_1200_plc != NULL) + { + xs_StartupKTask(thread_siemens_1200_plc); + } + else + { + return XS_ERROR; + } + + //step1 : send handshake_1200_first cmd, waiting for response sem + xs_kprintf("siemens_1200_hfa21 start send handshake_1200_first!\r\n"); + //CH438UARTSend(6, handshake_1200_first, sizeof(handshake_1200_first)); + xs_DeviceWrite(siemens_1200_hfa21, 0, handshake_1200_first, sizeof(handshake_1200_first)); + xs_MdelayKTask(3000); + xs_DeviceWrite(siemens_1200_hfa21, 0, handshake_1200_first, sizeof(handshake_1200_first)); + xs_MdelayKTask(500); + //step2 : send handshake_1200_second cmd, waiting for response sem + xs_kprintf("siemens_1200_hfa21 start send handshake_1200_second!\r\n"); + //CH438UARTSend(6, handshake_1200_second, sizeof(handshake_1200_second)); + xs_DeviceWrite(siemens_1200_hfa21, 0, handshake_1200_second, sizeof(handshake_1200_second)); + xs_kprintf("siemens_1200_hfa21 init success!\r\n"); + xs_MdelayKTask(500); + + while(1) + { + //CH438UARTSend(6, siemens_1200_read_data, sizeof(siemens_1200_read_data)); //read_data + xs_DeviceWrite(siemens_1200_hfa21, 0, siemens_1200_read_data, sizeof(siemens_1200_read_data)); + xs_kprintf("siemens 1200 hfa21 write cmd\n"); + xs_KSemaphoreObtain(siemens_1200_ec200t_sem, XS_WAITING_FOREVER); + xs_MdelayKTask(1000); + } + + return XS_EOK; +} +MSH_CMD_EXPORT(SIEMENS_1200, Siemens 1200 PLC); +#endif + +/*********************************************************************************************/ +//贝加莱PLC IP 192.168.250.4 port 12000 +static unsigned char brl_redis_data[2048]; +static xs_device_t brl_plc_hfa21; +static sem_t brl_plc_input_sem = NULL; +static sem_t brl_plc_ec200t_sem = NULL; + +static xs_err_t brl_plc_uart_input(xs_device_t dev, xs_size_t size) +{ + xs_KSemaphoreAbandon(brl_plc_input_sem); + return XS_EOK; +} + +static void brl_plc_thread(void* parameter) +{ + xs_err_t ret; + uint32_t rx_length, total_rx_length = 0; + uint8_t i; + unsigned int length = 0; + + while(1) + { + ret = xs_KSemaphoreObtain(brl_plc_input_sem, XS_WAITING_FOREVER); + + if(XS_EOK == ret) + { + xs_kprintf("before total %d\n", total_rx_length); + rx_length = xs_DeviceRead(brl_plc_hfa21, 0, brl_redis_data + 2 + total_rx_length, 1640); + xs_kprintf("brl dst data length %d total length %d\n", rx_length, total_rx_length); + + for(i = 0; i < rx_length / 10; i ++) + { + xs_kprintf("0x%x ", brl_redis_data[i + 2 + total_rx_length]); + } + + xs_kprintf("\n"); + total_rx_length += rx_length; + + if(total_rx_length > 1640) + { + xs_kprintf("ERROR : rx_buffer is full total_rx_length %d\n", total_rx_length); + total_rx_length = 0; + memset(brl_redis_data, 0, sizeof(brl_redis_data)); + xs_KSemaphoreAbandon(brl_plc_ec200t_sem); + continue; + } + + if((1640 == total_rx_length) && (0x53 == brl_redis_data[2])) + { + /******format redis data******/ + brl_redis_data[0] = data_head; + brl_redis_data[1] = PLC_BRL_FLAG; + length = 2 + total_rx_length; + + /******end******/ + for(i = 0; i < 10; i ++) + { + xs_kprintf("0x%x ", brl_redis_data[i + 1478]); + } + + xs_kprintf("\n"); + xs_DeviceWrite(ec200t, 0, brl_redis_data, length); + total_rx_length = 0; + memset(brl_redis_data, 0, sizeof(brl_redis_data)); + xs_KSemaphoreAbandon(brl_plc_ec200t_sem); + } + } + } +} + +int BRL_PLC(void) +{ + xs_err_t result; + const unsigned char brl_reply_data[] = {0x1, 0x1, 0x1}; + struct SerialConfigure config3 = XS_SERIAL_CONFIG_DEFAULT; + brl_plc_hfa21 = xs_DeviceFind("uart3"); + + if(!brl_plc_hfa21) + { + xs_kprintf("find brl_plc_hfa21 failed!\r\n"); + return XS_ERROR; + } + + brl_plc_input_sem = xs_KCreateSemaphore("brl_plc_input_sem", 0, XS_LINKLIST_FLAG_FIFO); + + if(brl_plc_input_sem == NULL) + { + xs_kprintf("brl_plc_hfa21 create brl_plc_input_sem failed.\n"); + return XS_ERROR; + } + + brl_plc_ec200t_sem = xs_KCreateSemaphore("brl_plc_ec200t_sem", 0, XS_LINKLIST_FLAG_FIFO); + + if(brl_plc_ec200t_sem == NULL) + { + xs_kprintf("brl_plc_hfa21 create brl_plc_ec200t_sem failed.\n"); + return XS_ERROR; + } + + //brl_hfa21 + config3.BaudRate = BAUD_RATE_460800; + config3.DataBits = DATA_BITS_8; + config3.StopBits = STOP_BITS_1; + config3.bufsz = 2048; + config3.parity = PARITY_NONE; + xs_DeviceControl(brl_plc_hfa21, XS_DEVICE_CTRL_CONFIG, &config3); + xs_DeviceOpen(brl_plc_hfa21, XS_DEVICE_FLAG_DMA_RX); + xs_DeviceSetRxIndicate(brl_plc_hfa21, brl_plc_uart_input); + TaskDescriptorPointer thread_brl_plc = xs_KTaskCreate("brl_plc_hfa21", brl_plc_thread, NULL, 1024, 25); + + if(thread_brl_plc != NULL) + { + xs_StartupKTask(thread_brl_plc); + } + else + { + return XS_ERROR; + } + + xs_kprintf("brl_plc_hfa21 init success!\r\n"); + xs_MdelayKTask(500); + + while(1) + { + //CH438UARTSend(6, brl_reply_data, sizeof(brl_reply_data)); + xs_kprintf("brl_plc hfa21 write cmd\n"); //read_data + xs_DeviceWrite(brl_plc_hfa21, 0, brl_reply_data, sizeof(brl_reply_data)); + xs_KSemaphoreObtain(brl_plc_ec200t_sem, XS_WAITING_FOREVER); + } + + return XS_EOK; +} + +MSH_CMD_EXPORT(BRL_PLC, Brl PLC); + diff --git a/APP_Framework/Framework/control/plc/interoperability/socket/plc_socket.c b/APP_Framework/Framework/control/plc/interoperability/socket/plc_socket.c new file mode 100755 index 000000000..1b8204c04 --- /dev/null +++ b/APP_Framework/Framework/control/plc/interoperability/socket/plc_socket.c @@ -0,0 +1,329 @@ +/* + * Copyright (c) 2022 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 plc_socket.c + * @brief Demo for PLC socket communication function + * @version 1.0 + * @author AIIT XUOS Lab + * @date 2022.03.16 + */ + +#include "transform.h" +#include "plc_socket.h" +#include "sys_arch.h" +#include "lwip/sockets.h" +#include "control_file.h" + +#define PLC_SOCK_CMD_NUM 10 + +// for saving PLC command +int plc_cmd_index = 0; + +//siemens test +PlcBinCmdType TestPlcCmd[PLC_SOCK_CMD_NUM] = +{ +#ifdef SUPPORT_PLC_SIEMENS + // handshake1 repeat 1 + { + 0, 3000, 22, + { + 0x03, 0x00, 0x00, 0x16, 0x11, 0xE0, 0x00, 0x00, + 0x02, 0xC8, 0x00, 0xC1, 0x02, 0x02, 0x01, 0xC2, + 0x02, 0x02, 0x01, 0xC0, 0x01, 0x0A + } + }, + // handshake2 + { + 1, 500, 25, + { + 0x03, 0x00, 0x00, 0x19, 0x02, 0xF0, 0x80, 0x32, + 0x01, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x08, 0x00, + 0x00, 0xF0, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, + 0xF0 + } + }, + // read command + { + 2, 1000, 31, + { + 0x03, 0x00, 0x00, 0x1F, 0x02, 0xF0, 0x80, 0x32, + 0x01, 0x00, 0x00, 0x33, 0x01, 0x00, 0x0E, 0x00, + 0x00, 0x04, 0x01, 0x12, 0x0A, 0x10, 0x02, 0x00, + 0xD2, 0x00, 0x34, 0x84, 0x00, 0x00, 0x00 + } + } + // oml plc +#else// SUPPORT_PLC_OML + { + 0, 1000, 18, + { + 0x80, 0x00, 0x02, 0x00, 0x03, 0x00, 0x00, 0x7E, + 0x00, 0x00, 0x01, 0x01, 0x82, 0x0F, 0xA0, 0x00, + 0x00, 0x20 + } + } +#endif +}; + +//Test information +//SIEMENS ip: 192.168.250.9 port: 102 +//S7-200 ip: 192.168.250.8 port: 102 +//S7-1200 ip: 192.168.250.6 port: 102 +//OML ip: 192.168.250.3 port: 9600 + +PlcSocketParamType plc_socket_demo_data = { + .ip = {192, 168, 250, 3}, + .port = 9600, + .device_type = PLC_DEV_TYPE_OML, + .socket_type = SOCK_STREAM, //SOCK_DGRAM, //udp + .step = 0, + .cmd_num = 3, + .buf_size = PLC_TEST_SIZE, + .buf = NULL, +}; + +#define OML_HEADER_LEN 78 +#define CHECK_OML_HEADER(_s) ((0xC0 == *(_s)) && (0x00 == *(_s + 1)) && (0x02 == *(_s + 2)) && (0x00 == *(_s + 3))) + +/******************************************************************************/ + +static void *PlcSocketStart(void *arg) +{ + int fd = -1; + int recv_len; + int step = 0; + char *recv_buf; + struct sockaddr_in sock_addr; + socklen_t addr_len = sizeof(struct sockaddr_in); + PlcSocketParamType *param = (PlcSocketParamType *)&plc_socket_demo_data; + + plc_print("start %d.%d.%d.%d:%d dev %d sock %d\n", + param->ip[0], + param->ip[1], + param->ip[2], + param->ip[3], + param->port, + param->device_type, + param->socket_type); + + //malloc memory + recv_buf = (char *)malloc(param->buf_size); + if (recv_buf == NULL) + { + plc_error("No memory\n"); + return NULL; + } + + fd = socket(AF_INET, param->socket_type, 0); + if (fd < 0) + { + plc_error("Socket error %d\n", param->socket_type); + free(recv_buf); + return NULL; + } + + plc_print("start %d.%d.%d.%d:%d\n", param->ip[0], param->ip[1], param->ip[2], param->ip[3], param->port); + + sock_addr.sin_family = AF_INET; + sock_addr.sin_port = htons(param->port); + sock_addr.sin_addr.s_addr = PP_HTONL(LWIP_MAKEU32(param->ip[0], param->ip[1], param->ip[2], param->ip[3])); + memset(&(sock_addr.sin_zero), 0, sizeof(sock_addr.sin_zero)); + + if (connect(fd, (struct sockaddr *)&sock_addr, sizeof(struct sockaddr)) < 0) + { + plc_error("Unable to connect\n"); + closesocket(fd); + free(recv_buf); + return NULL; + } + + lw_notice("client %s connected\n", inet_ntoa(sock_addr.sin_addr)); + + while(step < param->cmd_num) + { + sendto(fd, TestPlcCmd[step].cmd, TestPlcCmd[step].cmd_len, 0, (struct sockaddr*)&sock_addr, addr_len); + lw_notice("Send Cmd: %d - ", TestPlcCmd[step].cmd_len); + for(int i = 0; i < TestPlcCmd[step].cmd_len; i++) + { + lw_notice(" %#x", TestPlcCmd[step].cmd[i]); + } + lw_notice("\n"); + MdelayKTask(TestPlcCmd[step].delay_ms); + + memset(recv_buf, 0, param->buf_size); + while(1) + { + recv_len = recvfrom(fd, recv_buf, param->buf_size, 0, (struct sockaddr *)&sock_addr, &addr_len); + if(recv_len > 0) + { + if(param->device_type == PLC_DEV_TYPE_OML) + { + if((recv_len == OML_HEADER_LEN) && (CHECK_OML_HEADER(recv_buf))) + { + lw_notice("This is Oml package!!!\n"); + } + } + lw_notice("Receive from : %s\n", inet_ntoa(sock_addr.sin_addr)); + lw_notice("Receive data : %d -", recv_len); + for(int i = 0; i < recv_len; i++) + { + lw_notice(" %#x", recv_buf[i]); + } + lw_notice("\n"); + break; + } + } + step ++; + } + + closesocket(fd); + free(recv_buf); + return NULL; +} + +void PlcGetParamCmd(char *cmd) +{ + const char s[2] = ","; + char *token; + uint16_t cmd_index = 0; + char bin_cmd[PLC_BIN_CMD_LEN] = {0}; + token = strtok(cmd, s); + while(token != NULL) + { + sscanf(token, "%x", &bin_cmd[cmd_index]); + plc_print("%d - %s %d\n", cmd_index, token, bin_cmd[cmd_index]); + token = strtok(NULL, s); + cmd_index ++; + } + TestPlcCmd[plc_cmd_index].cmd_len = cmd_index; + memcpy(TestPlcCmd[plc_cmd_index].cmd, bin_cmd, cmd_index); + plc_print("get %d cmd len %d\n", plc_cmd_index, TestPlcCmd[plc_cmd_index].cmd_len); + plc_cmd_index ++; + plc_socket_demo_data.cmd_num = plc_cmd_index; +} + +void PlcShowUsage(void) +{ + plc_notice("------------------------------------\n"); + plc_notice("PlcSocket [ip].[ip].[ip].[ip]:[port]\n"); + plc_notice("PlcSocket support other param:\n"); + plc_notice("plc=[] 0: OML 1:SIEMENS\n"); + plc_notice("tcp=[] 0: udp 1:tcp\n"); + plc_notice("ip=[ip.ip.ip.ip]\n"); + plc_notice("port=port\n"); + plc_notice("------------------------------------\n"); +} + +void PlcCheckParam(int argc, char *argv[]) +{ + int i; + PlcSocketParamType *param = &plc_socket_demo_data; + plc_cmd_index = 0; + + for(i = 0; i < argc; i++) + { + char *str = argv[i]; + int is_tcp = 0; + char cmd_str[PLC_BIN_CMD_LEN] = {0}; + + plc_print("check %d %s\n", i, str); + + if(sscanf(str, "ip=%d.%d.%d.%d", + ¶m->ip[0], + ¶m->ip[1], + ¶m->ip[2], + ¶m->ip[3]) == 4) + { + plc_print("find ip %d %d %d %d\n", param->ip[0], param->ip[1], param->ip[2], param->ip[3]); + continue; + } + + if(sscanf(str, "port=%d", ¶m->port) == 1) + { + plc_print("find port %d\n", param->port); + continue; + } + + if(sscanf(str, "tcp=%d", &is_tcp) == 1) + { + plc_print("find tcp %d\n", is_tcp); + param->socket_type = is_tcp ? SOCK_STREAM:SOCK_DGRAM; + continue; + } + + if(sscanf(str, "plc=%d", ¶m->device_type) == 1) + { + plc_print("find device %d\n", param->device_type); + continue; + } + + if(sscanf(str, "cmd=%s", cmd_str) == 1) + { + plc_print("find cmd %s\n", cmd_str); + PlcGetParamCmd(cmd_str); + continue; + } + } + + if(argc >= 2) + { + if(sscanf(argv[1], "%d.%d.%d.%d:%d", + ¶m->ip[0], + ¶m->ip[1], + ¶m->ip[2], + ¶m->ip[3], + ¶m->port) != EOF) + { + return; + } + + if(sscanf(argv[1], "%d.%d.%d.%d", + ¶m->ip[0], + ¶m->ip[1], + ¶m->ip[2], + ¶m->ip[3]) != EOF) + { + return; + } + } + else + { + PlcShowUsage(); + } +} + +void PlcGetParamFromFile(char *file) +{ + +} + +void PlcSocketTask(int argc, char *argv[]) +{ + int result = 0; + pthread_t th_id; + + pthread_attr_t attr; + attr.schedparam.sched_priority = LWIP_DEMO_TASK_PRIO; + attr.stacksize = LWIP_TASK_STACK_SIZE; + PlcSocketParamType *param = &plc_socket_demo_data; + + PlcCheckParam(argc, argv); + + lwip_config_net(lwip_ipaddr, lwip_netmask, param->ip); + PrivTaskCreate(&th_id, &attr, PlcSocketStart, param); +} + + +SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0) | SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN) | SHELL_CMD_PARAM_NUM(3), + PlcSocket, PlcSocketTask, Test PLC Socket); + diff --git a/APP_Framework/Framework/control/plc/interoperability/socket/plc_socket.h b/APP_Framework/Framework/control/plc/interoperability/socket/plc_socket.h new file mode 100755 index 000000000..363877083 --- /dev/null +++ b/APP_Framework/Framework/control/plc/interoperability/socket/plc_socket.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2022 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 plc_socket.h + * @brief Demo for PLC socket communication function + * @version 1.0 + * @author AIIT XUOS Lab + * @date 2022.03.16 + */ + +#ifndef __PLC_SOCKET_H_ +#define __PLC_SOCKET_H_ + +#define PLC_BIN_CMD_LEN 512 + +typedef struct +{ + uint8_t step; + uint16_t delay_ms; + uint8_t cmd_len; + uint8_t cmd[PLC_BIN_CMD_LEN]; +}PlcBinCmdType; + +enum PlcDeviceType { + PLC_DEV_TYPE_OML = 0, + PLC_DEV_TYPE_IPC, + PLC_DEV_TYPE_BRL, + PLC_DEV_TYPE_SIEMENS, + PLC_DEV_TYPE_SIEMENS_1200, + PLC_DEV_TYPE_JF_IPC, + PLC_DEV_TYPE_HG, + /* ...... */ + PLC_DEV_TYPE_END, +}; + +#define PLC_IP_SIZE 16 +#define PLC_DEV_SIZE 32 +#define PLC_TEST_SIZE 65536 + +typedef struct PlcSocketParamStruct{ + char ip[PLC_IP_SIZE]; + uint32_t port; + uint32_t device_type; //PlcDeviceType + uint32_t socket_type; //UDP or TCP + char device[PLC_DEV_SIZE]; + uint32_t step; // communication step + uint32_t cmd_num; // command number + uint32_t buf_size; + uint8_t *buf; +}PlcSocketParamType; + +//debug command +#define plc_print //KPrintf +#define plc_error KPrintf +#define plc_notice KPrintf + +#endif diff --git a/APP_Framework/Framework/control/plc/shared/plc_device.h b/APP_Framework/Framework/control/plc/shared/plc_device.h index 6744a1474..b8c57d490 100755 --- a/APP_Framework/Framework/control/plc/shared/plc_device.h +++ b/APP_Framework/Framework/control/plc/shared/plc_device.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2021 AIIT XUOS Lab +* Copyright (c) 2022 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: diff --git a/APP_Framework/Framework/control/shared/Makefile b/APP_Framework/Framework/control/shared/Makefile index 4890d7675..633191b89 100755 --- a/APP_Framework/Framework/control/shared/Makefile +++ b/APP_Framework/Framework/control/shared/Makefile @@ -1,4 +1,8 @@ SRC_FILES := control.c +ifeq ($(CONFIG_MOUNT_SDCARD),y) + SRC_FILES += control_file.c +endif + include $(KERNEL_ROOT)/compiler.mk diff --git a/APP_Framework/Framework/control/shared/control_file.c b/APP_Framework/Framework/control/shared/control_file.c new file mode 100755 index 000000000..920f89296 --- /dev/null +++ b/APP_Framework/Framework/control/shared/control_file.c @@ -0,0 +1,129 @@ +/* +* Copyright (c) 2022 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 control_file.c + * @brief control relative file operation + * @version 1.0 + * @author AIIT XUOS Lab + * @date 2022-03-17 + */ + +#include +#include "cJSON.h" +#include "transform.h" +#include "control_file.h" + +#define CTL_FILE_NAME "/plc/control_cmd.txt" + +#define TEST_PLC_CMD_TXT \ + "cmd=03,00,00,16,11,E0,00,00,02,C8,00,C1,02,02,01,C2,02,02,01,C0,01,0A\r\n\r\n" \ + "cmd=03,00,00,19,02,F0,80,32,01,00,00,00,0D,00,08,00,00,F0,00,00,01,00,01,00,F0\r\n\r\n" \ + "cmd=03,00,00,1F,02,F0,80,32,01,00,00,33,01,00,0E,00,00,04,01,12,0A,10,02,00,D2,00,34,84,00,00,00\r\n\r\n" \ + "port=102\r\n" \ + "ip=192.168.250.6\r\n" + + +#define TEST_PLC_JSON_TXT \ +"{ \r\n"\ +" \"S1200\": [ \r\n"\ +" { \"ip\": \"192.168.250.6\"}, \r\n"\ +" { \"port\": 102}, \r\n"\ +" { \"cmd\": [3, 0, 0, 22, 17, 224, 0, 0, 2, 200, 0, 193, 2, 2, 1, 194, 2, 2, 1, 192, 1, 10]}\r\n"\ +" ] \r\n"\ +"}" +//" { \"cmd\": [3, 0, 0, 25, 2, 240, 128, 50, 1, 0, 0, 0, 13, 0, 8, 0, 0, 240, 0, 0, 1, 0, 1, 0, 240]},\r\n" \ +//" { \"cmd\": [3, 0, 0, 31, 2, 240, 128, 50, 1, 0, 0, 51, 1, 0, 14, 0, 0, 4, 1, 18, 10, 16, 2, 0, 210, 0, 52, 132, 0, 0, 0]}\r\n" \ + + +FILE *CtlFileInit(void) +{ + FILE *fd = NULL; + + fd = fopen(CTL_FILE_NAME, "a+"); + if(fd == NULL) + { + ctl_error("open file %s failed\n", CTL_FILE_NAME); + } + + return fd; +} + +void CtlFileClose(FILE *fd) +{ + fclose(fd); +} + +void CtlFileRead(FILE *fd, int size, char *buf) +{ + fread(buf, size, 1, fd); + ctl_print("read file %d: %s\n", size, buf); +} + +void CtlFileWrite(FILE *fd, int size, char *buf) +{ + size_t write_size = 0; + write_size = fwrite(buf, strlen(buf) + 1, 1, fd); + ctl_print("write size %d: %s\n", size, buf); +} + +void CtlFileTest(void) +{ + FILE *fd = CtlFileInit(); + char *file_buf = TEST_PLC_JSON_TXT; + CtlFileWrite(fd, strlen(file_buf), file_buf); + CtlFileRead(fd, CTL_FILE_SIZE, file_buf); + CtlFileClose(fd); +} + +SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0) | SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN) | SHELL_CMD_PARAM_NUM(0), + CtlTestFile, CtlFileTest, Test control file); + +#ifdef LIB_USING_CJSON + +void CtlParseJsonData(char *buf) +{ + cJSON *file_dat = NULL; + cJSON *ip_dat = NULL; + cJSON *port_dat = NULL; + cJSON *cmd_dat = NULL; + + file_dat = cJSON_Parse(buf); + if(file_dat == NULL) + { + ctl_error("ctrl parse failed\n"); + return; + } + + ip_dat = cJSON_GetObjectItem(file_dat, "ip"); + port_dat = cJSON_GetObjectItem(file_dat, "port"); + cmd_dat = cJSON_GetObjectItem(file_dat, "cmd"); + + ctl_print("ip : %s\n", ip_dat->string); + ctl_print("port: %d\n", port_dat->valueint); + ctl_print("cmd : %s\n", cmd_dat->valueint); +} + +void CtlParseFileTest(void) +{ + char file_buf[CTL_FILE_SIZE] = {0}; + FILE *fd = CtlFileInit(); + CtlFileRead(fd, CTL_FILE_SIZE, file_buf); + CtlFileClose(fd); + CtlParseJsonData(file_buf); +} + +SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0) | SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN) | SHELL_CMD_PARAM_NUM(0), + CtlParseFile, CtlParseFileTest, Parse control file); + +#endif + diff --git a/APP_Framework/Framework/control/shared/control_file.h b/APP_Framework/Framework/control/shared/control_file.h new file mode 100755 index 000000000..6c933594c --- /dev/null +++ b/APP_Framework/Framework/control/shared/control_file.h @@ -0,0 +1,35 @@ +/* +* Copyright (c) 2022 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 control_file.h + * @brief control relative API + * @version 1.0 + * @author AIIT XUOS Lab + * @date 2022-03-17 + */ + +#ifndef __CONTROL_FILE_H_ +#define __CONTROL_FILE_H_ + +#define CTL_FILE_SIZE 1024 + +#define ctl_print KPrintf +#define ctl_error KPrintf + +FILE *CtlFileInit(void); +void CtlFileClose(FILE *fd); +void CtlFileRead(FILE *fd, int size, char *buf); +void CtlFileWrite(FILE *fd, int size, char *buf); + +#endif + diff --git a/APP_Framework/lib/Kconfig b/APP_Framework/lib/Kconfig index b3497fca5..8fefd677d 100755 --- a/APP_Framework/lib/Kconfig +++ b/APP_Framework/lib/Kconfig @@ -5,12 +5,12 @@ menu "lib" config APP_SELECT_NEWLIB bool "app select newlib" - + config APP_SELECT_OTHER_LIB bool "app select other lib" endchoice source "$APP_DIR/lib/cJSON/Kconfig" - source "$APP_DIR/lib/queue/Kconfig" + source "$APP_DIR/lib/queue/Kconfig" source "$APP_DIR/lib/lvgl/Kconfig" source "$APP_DIR/lib/embedded_database/Kconfig" endmenu diff --git a/APP_Framework/lib/Makefile b/APP_Framework/lib/Makefile index 3e8e31a84..24a27d0fa 100644 --- a/APP_Framework/lib/Makefile +++ b/APP_Framework/lib/Makefile @@ -10,5 +10,8 @@ ifeq ($(CONFIG_LIB_LV),y) SRC_DIR += lvgl endif +ifeq ($(CONFIG_LIB_USING_CJSON),y) + SRC_DIR += cJSON +endif include $(KERNEL_ROOT)/compiler.mk diff --git a/APP_Framework/lib/cJSON/Makefile b/APP_Framework/lib/cJSON/Makefile new file mode 100755 index 000000000..79f1fc37d --- /dev/null +++ b/APP_Framework/lib/cJSON/Makefile @@ -0,0 +1,3 @@ +SRC_FILES := cJSON.c + +include $(KERNEL_ROOT)/compiler.mk diff --git a/Ubiquitous/XiZi/board/ok1052-c/board.c b/Ubiquitous/XiZi/board/ok1052-c/board.c index 55786560d..020ec347e 100644 --- a/Ubiquitous/XiZi/board/ok1052-c/board.c +++ b/Ubiquitous/XiZi/board/ok1052-c/board.c @@ -17,10 +17,10 @@ File name: board.c Description: support imxrt1052-board init function Others: take SDK_2.6.1_MIMXRT1052xxxxB for references -History: +History: 1. Date: 2022-01-25 Author: AIIT XUOS Lab -Modification: +Modification: 1. support imxrt1052-board MPU、clock、memory init 2. support imxrt1052-board uart、semc、sdio driver init 3. support imxrt1052-board I2C, SPI, ADC, RTC driver init @@ -66,11 +66,24 @@ int MountSDCard(void) #include "fsl_gpio.h" #include "fsl_lpuart.h" +#ifdef BSP_USING_LWIP #include +#endif +#ifdef BSP_USING_LPUART #include +#endif + +#ifdef BSP_USING_ADC #include +#endif + +#ifdef BSP_USING_SPI #include +#endif + +#ifdef BSP_USING_RTC #include +#endif #define NVIC_PRIORITYGROUP_0 0x00000007U /*!< 0 bits for pre-emption priority 4 bits for subpriority */ diff --git a/Ubiquitous/XiZi/board/ok1052-c/third_party_driver/Kconfig b/Ubiquitous/XiZi/board/ok1052-c/third_party_driver/Kconfig index ed032333a..0ec9c3aa4 100644 --- a/Ubiquitous/XiZi/board/ok1052-c/third_party_driver/Kconfig +++ b/Ubiquitous/XiZi/board/ok1052-c/third_party_driver/Kconfig @@ -22,7 +22,7 @@ menuconfig BSP_USING_GPIO menuconfig BSP_USING_I2C bool "Using I2C device" - default y + default n select RESOURCES_I2C if BSP_USING_I2C @@ -31,7 +31,7 @@ menuconfig BSP_USING_I2C menuconfig BSP_USING_ADC bool "Using ADC device" - default y + default n select RESOURCES_ADC if BSP_USING_ADC @@ -40,7 +40,7 @@ menuconfig BSP_USING_ADC menuconfig BSP_USING_SPI bool "Using SPI device" - default y + default n select RESOURCES_SPI if BSP_USING_SPI diff --git a/Ubiquitous/XiZi/board/ok1052-c/third_party_driver/spi/Makefile b/Ubiquitous/XiZi/board/ok1052-c/third_party_driver/spi/Makefile index 7eaf163ae..8765fdab1 100755 --- a/Ubiquitous/XiZi/board/ok1052-c/third_party_driver/spi/Makefile +++ b/Ubiquitous/XiZi/board/ok1052-c/third_party_driver/spi/Makefile @@ -1,3 +1,7 @@ -SRC_FILES := fsl_lpspi.c connect_spi.c connect_flash_spi.c +SRC_FILES := fsl_lpspi.c connect_spi.c + +ifeq ($(CONFIG_RESOURCES_SPI_SFUD),y) + SRC_FILES += connect_flash_spi.c +endif include $(KERNEL_ROOT)/compiler.mk diff --git a/Ubiquitous/XiZi/kernel/thread/msgqueue.c b/Ubiquitous/XiZi/kernel/thread/msgqueue.c index 3c690011a..6a253dd8f 100644 --- a/Ubiquitous/XiZi/kernel/thread/msgqueue.c +++ b/Ubiquitous/XiZi/kernel/thread/msgqueue.c @@ -38,13 +38,13 @@ static struct MsgQueue *GetMsgQueueById(int32 id) if (id < 0 ) return NONE; - + lock = CriticalAreaLock(); idnode = IdGetObj(&k_mq_id_manager, id); if (idnode == NONE){ CriticalAreaUnLock(lock); return NONE; - } + } mq = CONTAINER_OF(idnode, struct MsgQueue, id); CriticalAreaUnLock(lock); return mq; @@ -61,7 +61,7 @@ static x_err_t _InitMsgQueue( struct MsgQueue *mq ,x_size_t msg_size, mq->num_msgs = 0; mq->each_len = ALIGN_MEN_UP(msg_size, MEM_ALIGN_SIZE); mq->index = 0; - + InitDoubleLinkList(&mq->send_pend_list); InitDoubleLinkList(&(mq->recv_pend_list)); @@ -102,9 +102,9 @@ static x_err_t _MsgQueueSend(struct MsgQueue *mq, CriticalAreaUnLock(lock); return -EFULL; } - + while(mq->num_msgs >= mq->max_msgs ) { - + task->exstatus = EOK; if (timeout == 0) { CriticalAreaUnLock(lock); @@ -171,7 +171,7 @@ static x_err_t _MsgQueueUrgentSend(struct MsgQueue *mq, const void *buffer, x_si mq->index --; if (mq->index < 0) mq->index += mq->max_msgs; - + msg = mq->msg_buf + ( ( mq->index + mq->num_msgs ) % mq->max_msgs ) * mq->each_len ; memcpy(msg , buffer, size); mq->num_msgs ++; @@ -297,6 +297,7 @@ static x_err_t _DeleteMsgQueue(struct MsgQueue *mq) KERNEL_FREE(mq->msg_buf); lock = CriticalAreaLock(); + IdRemoveObj(&k_mq_id_manager, mq->id.id); DoubleLinkListRmNode(&(mq->link)); CriticalAreaUnLock(lock); KERNEL_FREE(mq); @@ -381,7 +382,7 @@ x_err_t KMsgQueueReinit(int32 id) * @param buffer message info * @param size the size of buffer * @param timeout time needed waiting - * + * * @return EOK on success;EINVALED on failure * */ @@ -406,7 +407,7 @@ x_err_t KMsgQueueRecv(int32 id, * a dynamic messagequeue will be deleted from the manage list * * @param id the message id - * + * * @return EOK on success;EINVALED on failure * */ @@ -429,7 +430,7 @@ x_err_t KDeleteMsgQueue(int32 id) * @param id the message id * @param buffer message info * @param size the size of buffer - * + * * @return EOK on success;EINVALED on failure * */ @@ -453,7 +454,7 @@ x_err_t KMsgQueueUrgentSend(int32 id, const void *buffer, x_size_t size) * @param id the message id * @param buffer message info * @param size the size of buffer - * + * * @return EOK on success;EINVALED on failure * */ @@ -477,7 +478,7 @@ x_err_t KMsgQueueSend(int32 id, const void *buffer, x_size_t size) * @param buffer message info * @param size the size of buffer * @param timeout waiting time - * + * * @return EOK on success;EINVALED on failure * */ @@ -493,4 +494,4 @@ x_err_t KMsgQueueSendwait(int32 id, const void *buffer, x_size_t size,int32 tim return mq->Done->send(mq,buffer,size,timeout); else return -EINVALED; -} \ No newline at end of file +} diff --git a/Ubiquitous/XiZi/path_kernel.mk b/Ubiquitous/XiZi/path_kernel.mk index 724f31011..705f0e685 100755 --- a/Ubiquitous/XiZi/path_kernel.mk +++ b/Ubiquitous/XiZi/path_kernel.mk @@ -291,6 +291,7 @@ endif ifeq ($(CONFIG_SUPPORT_CONTROL_FRAMEWORK), y) KERNELPATHS += -I$(KERNEL_ROOT)/../../APP_Framework/Framework/control # +KERNELPATHS += -I$(KERNEL_ROOT)/../../APP_Framework/Framework/control/shared # KERNELPATHS += -I$(KERNEL_ROOT)/../../APP_Framework/Framework/control/plc/interoperability/opcua # KERNELPATHS += -I$(KERNEL_ROOT)/../../APP_Framework/Framework/control/plc/shared # KERNELPATHS += -I$(KERNEL_ROOT)/../../APP_Framework/lib/cJSON diff --git a/Ubiquitous/XiZi/resources/ethernet/LwIP/api/api_lib.c b/Ubiquitous/XiZi/resources/ethernet/LwIP/api/api_lib.c index a2c136f0b..88d24dc6e 100644 --- a/Ubiquitous/XiZi/resources/ethernet/LwIP/api/api_lib.c +++ b/Ubiquitous/XiZi/resources/ethernet/LwIP/api/api_lib.c @@ -232,7 +232,7 @@ netconn_prepare_delete(struct netconn *conn) err_t netconn_delete(struct netconn *conn) { - err_t err; + err_t err = ERR_OK; /* No ASSERT here because possible to get a (conn == NULL) if we got an accept error */ if (conn == NULL) { @@ -245,8 +245,9 @@ netconn_delete(struct netconn *conn) err = ERR_OK; } else #endif /* LWIP_NETCONN_FULLDUPLEX */ +//tst by wly { -// err = netconn_prepare_delete(conn); + err = netconn_prepare_delete(conn); } if (err == ERR_OK) { netconn_free(conn); diff --git a/Ubiquitous/XiZi/resources/ethernet/LwIP/api/sockets.c b/Ubiquitous/XiZi/resources/ethernet/LwIP/api/sockets.c index 2660f73e5..1e8031672 100644 --- a/Ubiquitous/XiZi/resources/ethernet/LwIP/api/sockets.c +++ b/Ubiquitous/XiZi/resources/ethernet/LwIP/api/sockets.c @@ -497,6 +497,18 @@ get_socket(int fd) return sock; } +void pr_socket_buf(void) +{ + int i; + lw_debug("socket:\n"); + for (i = 0; i < NUM_SOCKETS; ++i) { +// if (!sockets[i].conn) + lw_debug("%d: conn %p rcv %d send %d wait %d\n", i, sockets[i].conn, sockets[i].rcvevent, sockets[i].sendevent, + sockets[i].select_waiting); + } +} + + /** * Allocate a new socket for a given netconn. * @@ -588,6 +600,7 @@ free_socket_free_elements(int is_tcp, struct netconn *conn, union lwip_sock_last } if (conn != NULL) { /* netconn_prepare_delete() has already been called, here we only free the conn */ + lw_debug("lw: [%s] tcp %d socket %d accept %d recv %d\n", __func__, is_tcp, conn->socket, conn->acceptmbox, conn->recvmbox); netconn_delete(conn); } } @@ -660,6 +673,8 @@ lwip_accept(int s, struct sockaddr *addr, socklen_t *addrlen) newsock = alloc_socket(newconn, 1); if (newsock == -1) { + lw_debug("lw: [%s] recv %d\n", __func__, newconn->recvmbox); + pr_socket_buf(); netconn_delete(newconn); sock_set_errno(sock, ENFILE); done_socket(sock); @@ -696,6 +711,8 @@ lwip_accept(int s, struct sockaddr *addr, socklen_t *addrlen) err = netconn_peer(newconn, &naddr, &port); if (err != ERR_OK) { LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d): netconn_peer failed, err=%d\n", s, err)); + lw_debug("lw: [%s] recv %x\n", __func__, newconn->recvmbox); + netconn_delete(newconn); free_socket(nsock, 1); sock_set_errno(sock, err_to_errno(err)); @@ -1739,6 +1756,8 @@ lwip_socket(int domain, int type, int protocol) i = alloc_socket(conn, 0); if (i == -1) { + lw_debug("lw: [%s] recv %d delete no socket\n", __func__, conn->recvmbox); + pr_socket_buf(); netconn_delete(conn); set_errno(ENFILE); return -1; @@ -1747,6 +1766,7 @@ lwip_socket(int domain, int type, int protocol) done_socket(&sockets[i - LWIP_SOCKET_OFFSET]); LWIP_DEBUGF(SOCKETS_DEBUG, ("%d\n", i)); set_errno(0); + lw_debug("lw: [%s] new socket %d %p\n", __func__, i, conn); return i; } diff --git a/Ubiquitous/XiZi/resources/ethernet/LwIP/arch/lwipopts.h b/Ubiquitous/XiZi/resources/ethernet/LwIP/arch/lwipopts.h index ff32bc38b..a8f4b5be8 100644 --- a/Ubiquitous/XiZi/resources/ethernet/LwIP/arch/lwipopts.h +++ b/Ubiquitous/XiZi/resources/ethernet/LwIP/arch/lwipopts.h @@ -74,7 +74,7 @@ a lot of data that needs to be copied, this should be set high. */ #define MEMP_NUM_TCP_PCB_LISTEN 2 /* MEMP_NUM_TCP_SEG: the number of simultaneously queued TCP segments. */ -#define MEMP_NUM_TCP_SEG 120 +#define MEMP_NUM_TCP_SEG 20 /* MEMP_NUM_SYS_TIMEOUT: the number of simulateously active timeouts. */ #define MEMP_NUM_SYS_TIMEOUT 6 @@ -212,26 +212,19 @@ The STM32F4x7 allows computing and verifying the IP, UDP, TCP and ICMP checksums --------------------------------- */ -#define DEFAULT_RAW_RECVMBOX_SIZE 10 -#define DEFAULT_UDP_RECVMBOX_SIZE 10 -#define DEFAULT_TCP_RECVMBOX_SIZE 10 -#define DEFAULT_ACCEPTMBOX_SIZE 10 +#define DEFAULT_RAW_RECVMBOX_SIZE 8 +#define DEFAULT_UDP_RECVMBOX_SIZE 8 +#define DEFAULT_TCP_RECVMBOX_SIZE 8 +#define DEFAULT_ACCEPTMBOX_SIZE 8 #define DEFAULT_THREAD_PRIO 20 #define DEFAULT_THREAD_STACKSIZE 1024 #define TCPIP_THREAD_NAME "tcp" #define TCPIP_THREAD_STACKSIZE 8192 -#define TCPIP_MBOX_SIZE 10 +#define TCPIP_MBOX_SIZE 8 #define TCPIP_THREAD_PRIO 15 -//#define IPERF_SERVER_THREAD_NAME "iperf_server" -//#define IPERF_SERVER_THREAD_STACKSIZE 1024 -//#define IPERF_SERVER_THREAD_PRIO 0 - -//#define BLOCK_TIME 250 -//#define BLOCK_TIME_WAITING_FOR_INPUT ( ( portTickType ) 100 ) - /* ---------------------------------------- ---------- Lwip Debug options ---------- @@ -259,7 +252,8 @@ typedef unsigned int nfds_t; #define lw_print //KPrintf #define lw_trace() //KPrintf("lw: [%s][%d] passed!\n", __func__, __LINE__) #define lw_error() //KPrintf("lw: [%s][%d] failed!\n", __func__, __LINE__) -#define lw_pr_info KPrintf +#define lw_debug KPrintf +#define lw_notice KPrintf #endif /* __LWIPOPTS_H__ */ /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/Ubiquitous/XiZi/resources/ethernet/LwIP/arch/sys_arch.c b/Ubiquitous/XiZi/resources/ethernet/LwIP/arch/sys_arch.c index ad8b13af3..5088c7f37 100644 --- a/Ubiquitous/XiZi/resources/ethernet/LwIP/arch/sys_arch.c +++ b/Ubiquitous/XiZi/resources/ethernet/LwIP/arch/sys_arch.c @@ -513,16 +513,16 @@ void lwip_config_net(char *ip, char *mask, char *gw) if(chk_lwip_bit(LWIP_PRINT_FLAG)) { - lw_pr_info("\r\n************************************************\r\n"); - lw_pr_info(" Network Configuration\r\n"); - lw_pr_info("************************************************\r\n"); - lw_pr_info(" IPv4 Address : %u.%u.%u.%u\r\n", ((u8_t *)&net_ipaddr)[0], ((u8_t *)&net_ipaddr)[1], + lw_notice("\r\n************************************************\r\n"); + lw_notice(" Network Configuration\r\n"); + lw_notice("************************************************\r\n"); + lw_notice(" IPv4 Address : %u.%u.%u.%u\r\n", ((u8_t *)&net_ipaddr)[0], ((u8_t *)&net_ipaddr)[1], ((u8_t *)&net_ipaddr)[2], ((u8_t *)&net_ipaddr)[3]); - lw_pr_info(" IPv4 Subnet mask : %u.%u.%u.%u\r\n", ((u8_t *)&net_netmask)[0], ((u8_t *)&net_netmask)[1], + lw_notice(" IPv4 Subnet mask : %u.%u.%u.%u\r\n", ((u8_t *)&net_netmask)[0], ((u8_t *)&net_netmask)[1], ((u8_t *)&net_netmask)[2], ((u8_t *)&net_netmask)[3]); - lw_pr_info(" IPv4 Gateway : %u.%u.%u.%u\r\n", ((u8_t *)&net_gw)[0], ((u8_t *)&net_gw)[1], + lw_notice(" IPv4 Gateway : %u.%u.%u.%u\r\n", ((u8_t *)&net_gw)[0], ((u8_t *)&net_gw)[1], ((u8_t *)&net_gw)[2], ((u8_t *)&net_gw)[3]); - lw_pr_info("************************************************\r\n"); + lw_notice("************************************************\r\n"); } lwip_config_input(&gnetif); } diff --git a/Ubiquitous/XiZi/resources/ethernet/LwIP/core/tcp_out.c b/Ubiquitous/XiZi/resources/ethernet/LwIP/core/tcp_out.c index 724df1097..3fb89e245 100644 --- a/Ubiquitous/XiZi/resources/ethernet/LwIP/core/tcp_out.c +++ b/Ubiquitous/XiZi/resources/ethernet/LwIP/core/tcp_out.c @@ -355,7 +355,7 @@ tcp_write_checks(struct tcp_pcb *pcb, u16_t len) * it can send them more efficiently by combining them together). * To prompt the system to send data now, call tcp_output() after * calling tcp_write(). - * + * * This function enqueues the data pointed to by the argument dataptr. The length of * the data is passed as the len parameter. The apiflags can be one or more of: * - TCP_WRITE_FLAG_COPY: indicates whether the new memory should be allocated @@ -1386,13 +1386,17 @@ tcp_output(struct tcp_pcb *pcb) /* In the case of fast retransmit, the packet should not go to the tail * of the unacked queue, but rather somewhere before it. We need to check for * this case. -STJ Jul 27, 2004 */ + lw_debug("check %s seg %p useg %p\n", __func__, seg, useg); if (TCP_SEQ_LT(lwip_ntohl(seg->tcphdr->seqno), lwip_ntohl(useg->tcphdr->seqno))) { /* add segment to before tail of unacked list, keeping the list sorted */ struct tcp_seg **cur_seg = &(pcb->unacked); + lw_debug("check %s ", __func__); while (*cur_seg && TCP_SEQ_LT(lwip_ntohl((*cur_seg)->tcphdr->seqno), lwip_ntohl(seg->tcphdr->seqno))) { + lw_debug("%p -> ", *cur_seg); cur_seg = &((*cur_seg)->next ); } + lw_debug("\n"); seg->next = (*cur_seg); (*cur_seg) = seg; } else { diff --git a/Ubiquitous/XiZi/resources/ethernet/cmd_lwip/lwip_config_demo.c b/Ubiquitous/XiZi/resources/ethernet/cmd_lwip/lwip_config_demo.c index 87e79fd8e..a9e662c09 100755 --- a/Ubiquitous/XiZi/resources/ethernet/cmd_lwip/lwip_config_demo.c +++ b/Ubiquitous/XiZi/resources/ethernet/cmd_lwip/lwip_config_demo.c @@ -81,18 +81,18 @@ void LwipShowIPTask(int argc, char *argv[]) { char mac_addr[] = configMAC_ADDR; - lw_pr_info("\r\n************************************************\r\n"); - lw_pr_info(" Network Configuration\r\n"); - lw_pr_info("************************************************\r\n"); - lw_pr_info(" IPv4 Address : %u.%u.%u.%u\r\n", ((u8_t *)&lwip_ipaddr)[0], ((u8_t *)&lwip_ipaddr)[1], + lw_notice("\r\n************************************************\r\n"); + lw_notice(" Network Configuration\r\n"); + lw_notice("************************************************\r\n"); + lw_notice(" IPv4 Address : %u.%u.%u.%u\r\n", ((u8_t *)&lwip_ipaddr)[0], ((u8_t *)&lwip_ipaddr)[1], ((u8_t *)&lwip_ipaddr)[2], ((u8_t *)&lwip_ipaddr)[3]); - lw_pr_info(" IPv4 Subnet mask : %u.%u.%u.%u\r\n", ((u8_t *)&lwip_netmask)[0], ((u8_t *)&lwip_netmask)[1], + lw_notice(" IPv4 Subnet mask : %u.%u.%u.%u\r\n", ((u8_t *)&lwip_netmask)[0], ((u8_t *)&lwip_netmask)[1], ((u8_t *)&lwip_netmask)[2], ((u8_t *)&lwip_netmask)[3]); - lw_pr_info(" IPv4 Gateway : %u.%u.%u.%u\r\n", ((u8_t *)&lwip_gwaddr)[0], ((u8_t *)&lwip_gwaddr)[1], + lw_notice(" IPv4 Gateway : %u.%u.%u.%u\r\n", ((u8_t *)&lwip_gwaddr)[0], ((u8_t *)&lwip_gwaddr)[1], ((u8_t *)&lwip_gwaddr)[2], ((u8_t *)&lwip_gwaddr)[3]); - lw_pr_info(" MAC Address : %x:%x:%x:%x:%x:%x\r\n", mac_addr[0], mac_addr[1], mac_addr[2], + lw_notice(" MAC Address : %x:%x:%x:%x:%x:%x\r\n", mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]); - lw_pr_info("************************************************\r\n"); + lw_notice("************************************************\r\n"); } SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0) | SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN) | SHELL_CMD_PARAM_NUM(0), diff --git a/Ubiquitous/XiZi/resources/ethernet/cmd_lwip/lwip_dhcp_demo.c b/Ubiquitous/XiZi/resources/ethernet/cmd_lwip/lwip_dhcp_demo.c index cfeff2499..87443523e 100755 --- a/Ubiquitous/XiZi/resources/ethernet/cmd_lwip/lwip_dhcp_demo.c +++ b/Ubiquitous/XiZi/resources/ethernet/cmd_lwip/lwip_dhcp_demo.c @@ -61,54 +61,54 @@ int LwipPrintDHCP(struct netif *netif) { dhcp_last_state = dhcp->state; - lw_pr_info(" DHCP state : "); + lw_notice(" DHCP state : "); switch (dhcp_last_state) { case DHCP_STATE_OFF: - lw_pr_info("OFF"); + lw_notice("OFF"); break; case DHCP_STATE_REQUESTING: - lw_pr_info("REQUESTING"); + lw_notice("REQUESTING"); break; case DHCP_STATE_INIT: - lw_pr_info("INIT"); + lw_notice("INIT"); break; case DHCP_STATE_REBOOTING: - lw_pr_info("REBOOTING"); + lw_notice("REBOOTING"); break; case DHCP_STATE_REBINDING: - lw_pr_info("REBINDING"); + lw_notice("REBINDING"); break; case DHCP_STATE_RENEWING: - lw_pr_info("RENEWING"); + lw_notice("RENEWING"); break; case DHCP_STATE_SELECTING: - lw_pr_info("SELECTING"); + lw_notice("SELECTING"); break; case DHCP_STATE_INFORMING: - lw_pr_info("INFORMING"); + lw_notice("INFORMING"); break; case DHCP_STATE_CHECKING: - lw_pr_info("CHECKING"); + lw_notice("CHECKING"); break; case DHCP_STATE_BOUND: - lw_pr_info("BOUND"); + lw_notice("BOUND"); break; case DHCP_STATE_BACKING_OFF: - lw_pr_info("BACKING_OFF"); + lw_notice("BACKING_OFF"); break; default: - lw_pr_info("%u", dhcp_last_state); + lw_notice("%u", dhcp_last_state); assert(0); break; } - lw_pr_info("\r\n"); + lw_notice("\r\n"); if (dhcp_last_state == DHCP_STATE_BOUND) { - lw_pr_info("\r\n IPv4 Address : %s\r\n", ipaddr_ntoa(&netif->ip_addr)); - lw_pr_info(" IPv4 Subnet mask : %s\r\n", ipaddr_ntoa(&netif->netmask)); - lw_pr_info(" IPv4 Gateway : %s\r\n\r\n", ipaddr_ntoa(&netif->gw)); + lw_notice("\r\n IPv4 Address : %s\r\n", ipaddr_ntoa(&netif->ip_addr)); + lw_notice(" IPv4 Subnet mask : %s\r\n", ipaddr_ntoa(&netif->netmask)); + lw_notice(" IPv4 Gateway : %s\r\n\r\n", ipaddr_ntoa(&netif->gw)); return 1; } } diff --git a/Ubiquitous/XiZi/resources/ethernet/cmd_lwip/lwip_ping_demo.c b/Ubiquitous/XiZi/resources/ethernet/cmd_lwip/lwip_ping_demo.c index f99e8b380..4eb01d0ba 100755 --- a/Ubiquitous/XiZi/resources/ethernet/cmd_lwip/lwip_ping_demo.c +++ b/Ubiquitous/XiZi/resources/ethernet/cmd_lwip/lwip_ping_demo.c @@ -60,7 +60,7 @@ void LwipPingTest(int argc, char *argv[]) { if(sscanf(argv[1], "%d.%d.%d.%d", &lwip_gwaddr[0], &lwip_gwaddr[1], &lwip_gwaddr[2], &lwip_gwaddr[3]) == EOF) { - lw_pr_info("input wrong ip\n"); + lw_notice("input wrong ip\n"); return; } } diff --git a/Ubiquitous/XiZi/resources/ethernet/cmd_lwip/lwip_tcp_demo.c b/Ubiquitous/XiZi/resources/ethernet/cmd_lwip/lwip_tcp_demo.c index 98bf62cb5..387899abc 100755 --- a/Ubiquitous/XiZi/resources/ethernet/cmd_lwip/lwip_tcp_demo.c +++ b/Ubiquitous/XiZi/resources/ethernet/cmd_lwip/lwip_tcp_demo.c @@ -27,52 +27,61 @@ #define MSG_SIZE 128 +typedef struct LwipTcpSocketStruct +{ + char ip[6]; + uint16_t port; + char *buf; // test buffer +}LwipTcpSocketParamType; + // this is for test in shell, in fact, shell restrict the length of input string, which is less then 128 char tcp_send_msg[MSG_SIZE] = {0}; char tcp_target[] = {192, 168, 250, 252}; +uint16_t tcp_port = LWIP_TARGET_PORT; /******************************************************************************/ static void LwipTcpSendTask(void *arg) { - lw_print("LwipTcpSendTask start.\n"); + lw_notice("LwipTcpSendTask start.\n"); int fd = -1; fd = socket(AF_INET, SOCK_STREAM, 0); if (fd < 0) { - lw_print("Socket error\n"); + lw_notice("Socket error\n"); return; } + LwipTcpSocketParamType *param = (LwipTcpSocketParamType *)arg; + struct sockaddr_in tcp_sock; tcp_sock.sin_family = AF_INET; - tcp_sock.sin_port = htons(LWIP_TARGET_PORT); - tcp_sock.sin_addr.s_addr = PP_HTONL(LWIP_MAKEU32(tcp_target[0], tcp_target[1], tcp_target[2], tcp_target[3])); + tcp_sock.sin_port = htons(param->port); + tcp_sock.sin_addr.s_addr = PP_HTONL(LWIP_MAKEU32(param->ip[0], param->ip[1], param->ip[2], param->ip[3])); memset(&(tcp_sock.sin_zero), 0, sizeof(tcp_sock.sin_zero)); if (connect(fd, (struct sockaddr *)&tcp_sock, sizeof(struct sockaddr))) { - lw_print("Unable to connect\n"); - goto __exit; + lw_notice("Unable to connect\n"); + closesocket(fd); + return; } - lw_print("tcp connect success, start to send.\n"); - lw_print("\n\nTarget Port:%d\n\n", tcp_sock.sin_port); + lw_notice("tcp connect success, start to send.\n"); + lw_notice("\n\nTarget Port:%d\n\n", tcp_sock.sin_port); sendto(fd, tcp_send_msg, strlen(tcp_send_msg), 0, (struct sockaddr*)&tcp_sock, sizeof(struct sockaddr)); - lw_print("Send tcp msg: %s ", tcp_send_msg); - -__exit: - if (fd >= 0) - closesocket(fd); + lw_notice("Send tcp msg: %s ", tcp_send_msg); + closesocket(fd); return; } void LwipTcpSendTest(int argc, char *argv[]) { + LwipTcpSocketParamType param; memset(tcp_send_msg, 0, MSG_SIZE); if(argc >= 2) { @@ -86,11 +95,20 @@ void LwipTcpSendTest(int argc, char *argv[]) if(argc >= 3) { - sscanf(argv[2], "%d.%d.%d.%d", &tcp_target[0], &tcp_target[1], &tcp_target[2], &tcp_target[3]); + if(sscanf(argv[2], "%d.%d.%d.%d:%d", &tcp_target[0], &tcp_target[1], &tcp_target[2], &tcp_target[3], &tcp_port) == EOK) + { + sscanf(argv[2], "%d.%d.%d.%d", &tcp_target[0], &tcp_target[1], &tcp_target[2], &tcp_target[3]); + } } + lw_notice("get ipaddr %d.%d.%d.%d:%d\n", tcp_target[0], tcp_target[1], tcp_target[2], tcp_target[3], tcp_port); + lwip_config_tcp(lwip_ipaddr, lwip_netmask, tcp_target); - lwip_config_tcp(lwip_ipaddr, lwip_netmask, lwip_gwaddr); - sys_thread_new("tcp send", LwipTcpSendTask, NULL, LWIP_TASK_STACK_SIZE, LWIP_DEMO_TASK_PRIO); + memcpy(param.ip, tcp_target, 4); + param.port = tcp_port; + param.buf = malloc(MSG_SIZE); + memcpy(param.buf, tcp_send_msg, MSG_SIZE); + + sys_thread_new("tcp send", LwipTcpSendTask, ¶m, LWIP_TASK_STACK_SIZE, LWIP_DEMO_TASK_PRIO); } SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0) | SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN) | SHELL_CMD_PARAM_NUM(3), diff --git a/Ubiquitous/XiZi/resources/ethernet/cmd_lwip/lwip_udp_demo.c b/Ubiquitous/XiZi/resources/ethernet/cmd_lwip/lwip_udp_demo.c index 9f459031a..97d06b119 100755 --- a/Ubiquitous/XiZi/resources/ethernet/cmd_lwip/lwip_udp_demo.c +++ b/Ubiquitous/XiZi/resources/ethernet/cmd_lwip/lwip_udp_demo.c @@ -30,7 +30,8 @@ #define PBUF_SIZE 27 static struct udp_pcb *udpecho_raw_pcb; -char udp_target[] = {192, 168, 250, 252}; +char udp_demo_ip[] = {192, 168, 250, 252}; +uint16_t udp_demo_port = LWIP_TARGET_PORT; char hello_str[] = {"hello world\r\n"}; char udp_send_msg[] = "\n\nThis one is UDP pkg. Congratulations on you.\n\n"; @@ -52,28 +53,23 @@ static void LwipUDPSendTask(void *arg) struct sockaddr_in udp_sock; udp_sock.sin_family = AF_INET; - udp_sock.sin_port = htons(LWIP_TARGET_PORT); - udp_sock.sin_addr.s_addr = PP_HTONL(LWIP_MAKEU32(udp_target[0],udp_target[1],udp_target[2],udp_target[3])); + udp_sock.sin_port = htons(udp_demo_port); + udp_sock.sin_addr.s_addr = PP_HTONL(LWIP_MAKEU32(udp_demo_ip[0], udp_demo_ip[1], udp_demo_ip[2], udp_demo_ip[3])); memset(&(udp_sock.sin_zero), 0, sizeof(udp_sock.sin_zero)); if (connect(socket_fd, (struct sockaddr *)&udp_sock, sizeof(struct sockaddr))) { lw_print("Unable to connect\n"); - goto __exit; + closesocket(socket_fd); + return; } lw_print("UDP connect success, start to send.\n"); lw_print("\n\nTarget Port:%d\n\n", udp_sock.sin_port); sendto(socket_fd, udp_send_msg, strlen(udp_send_msg), 0, (struct sockaddr*)&udp_sock, sizeof(struct sockaddr)); - lw_pr_info("Send UDP msg: %s ", udp_send_msg); - -__exit: - if (socket_fd >= 0) - { - closesocket(socket_fd); - } - + lw_notice("Send UDP msg: %s ", udp_send_msg); + closesocket(socket_fd); return; } @@ -86,7 +82,7 @@ void *LwipUdpSendTest(int argc, char *argv[]) if(argc == 1) { - lw_print("lw: [%s] gw %d.%d.%d.%d\n", __func__, udp_target[0], udp_target[1], udp_target[2], udp_target[3]); + lw_print("lw: [%s] gw %d.%d.%d.%d\n", __func__, udp_demo_ip[0], udp_demo_ip[1], udp_demo_ip[2], udp_demo_ip[3]); strncpy(udp_send_msg, hello_str, strlen(hello_str)); } else @@ -95,12 +91,12 @@ void *LwipUdpSendTest(int argc, char *argv[]) strncat(udp_send_msg, "\r\n", 2); if(argc == 3) { - sscanf(argv[2], "%d.%d.%d.%d", &udp_target[0], &udp_target[1], &udp_target[2], &udp_target[3]); + sscanf(argv[2], "%d.%d.%d.%d", &udp_demo_ip[0], &udp_demo_ip[1], &udp_demo_ip[2], &udp_demo_ip[3]); } } - lw_print("lw: [%s] gw %d.%d.%d.%d\n", __func__, udp_target[0], udp_target[1], udp_target[2], udp_target[3]); + lw_print("lw: [%s] gw %d.%d.%d.%d\n", __func__, udp_demo_ip[0], udp_demo_ip[1], udp_demo_ip[2], udp_demo_ip[3]); - lwip_config_tcp(lwip_ipaddr, lwip_netmask, lwip_gwaddr); + lwip_config_net(lwip_ipaddr, lwip_netmask, lwip_gwaddr); sys_thread_new("udp socket send", LwipUDPSendTask, NULL, LWIP_TASK_STACK_SIZE, LWIP_DEMO_TASK_PRIO); } @@ -121,11 +117,11 @@ static void LwipUdpRecvTask(void *arg, struct udp_pcb *upcb, struct pbuf *p, return; } udp_len = p->tot_len; - lw_pr_info("Receive data :%dB\r\n", udp_len); + lw_notice("Receive data :%dB\r\n", udp_len); if(udp_len <= 80) { - lw_pr_info("%.*s\r\n", udp_len, (char *)(p->payload)); + lw_notice("%.*s\r\n", udp_len, (char *)(p->payload)); } udp_buf = pbuf_alloc(PBUF_TRANSPORT, PBUF_SIZE, PBUF_RAM); diff --git a/Ubiquitous/XiZi/resources/ethernet/cmd_lwip/ping.c b/Ubiquitous/XiZi/resources/ethernet/cmd_lwip/ping.c index 1a66bd272..15c2f6c1e 100755 --- a/Ubiquitous/XiZi/resources/ethernet/cmd_lwip/ping.c +++ b/Ubiquitous/XiZi/resources/ethernet/cmd_lwip/ping.c @@ -492,7 +492,7 @@ int get_url_ip(char* url) /* convert URL to IP */ if (lwip_getaddrinfo(url, NULL, &hint, &res) != 0) { - lw_pr_info("ping: unknown host %s\n", url); + lw_notice("ping: unknown host %s\n", url); return -1; } memcpy(&h, &res->ai_addr, sizeof(struct sockaddr_in *)); @@ -500,13 +500,13 @@ int get_url_ip(char* url) lwip_freeaddrinfo(res); if (inet_aton(inet_ntoa(ina), &target_addr) == 0) { - lw_pr_info("ping: unknown host %s\n", url); + lw_notice("ping: unknown host %s\n", url); return -2; } /* new a socket */ if ((s = lwip_socket(AF_INET, SOCK_RAW, IP_PROTO_ICMP)) < 0) { - lw_pr_info("ping: create socket failed\n"); + lw_notice("ping: create socket failed\n"); return -3; } @@ -521,12 +521,12 @@ int get_url_ip(char* url) #endif /* LWIP_DEBUG */ if ((recv_len = lwip_ping_recv(s, &ttl)) >= 0) { - lw_pr_info("%d bytes from %s icmp_seq=%d ttl=%d time=%d ms\n", recv_len, inet_ntoa(ina), cnt, + lw_notice("%d bytes from %s icmp_seq=%d ttl=%d time=%d ms\n", recv_len, inet_ntoa(ina), cnt, ttl, sys_now() - ping_time); } else { - lw_pr_info("From %s icmp_seq=%d timeout\n", inet_ntoa(ina), cnt); + lw_notice("From %s icmp_seq=%d timeout\n", inet_ntoa(ina), cnt); } } diff --git a/Ubiquitous/XiZi/resources/ethernet/cmd_lwip/tcpecho_raw.c b/Ubiquitous/XiZi/resources/ethernet/cmd_lwip/tcpecho_raw.c index 026207891..d924f3285 100755 --- a/Ubiquitous/XiZi/resources/ethernet/cmd_lwip/tcpecho_raw.c +++ b/Ubiquitous/XiZi/resources/ethernet/cmd_lwip/tcpecho_raw.c @@ -159,9 +159,9 @@ tcpecho_raw_ack(struct tcp_pcb *tpcb, struct tcpecho_raw_state* es){ if((recv_len != TCP_MSS) || (recv_buf[recv_len - 1] == TCP_EOF_CH)) { if(g_buf_size < MAX_TCP_SHOW_SIZE){ - lw_pr_info("Received: %s\n", g_buf); + lw_notice("Received: %s\n", g_buf); }else{ - lw_pr_info("Received a string of length %d\n", g_buf_size); + lw_notice("Received a string of length %d\n", g_buf_size); } tcpecho_raw_ack_size(tpcb, g_buf_size); diff --git a/Ubiquitous/XiZi/resources/include/flash_spi.h b/Ubiquitous/XiZi/resources/include/flash_spi.h index f41d40d25..1f6aa4913 100644 --- a/Ubiquitous/XiZi/resources/include/flash_spi.h +++ b/Ubiquitous/XiZi/resources/include/flash_spi.h @@ -21,7 +21,9 @@ #ifndef FLASH_SPI_H #define FLASH_SPI_H +#ifdef RESOURCES_SPI_SFUD #include +#endif #ifdef __cplusplus extern "C" { diff --git a/Ubiquitous/XiZi/tool/shell/letter-shell/shell.c b/Ubiquitous/XiZi/tool/shell/letter-shell/shell.c index e1224a867..7cab44a65 100644 --- a/Ubiquitous/XiZi/tool/shell/letter-shell/shell.c +++ b/Ubiquitous/XiZi/tool/shell/letter-shell/shell.c @@ -3,12 +3,12 @@ * @author Letter (NevermindZZT@gmail.com) * @version 3.0.0 * @date 2019-12-30 - * + * * @copyright (c) 2020 Letter - * + * */ /*change log: - Change Chinese comment to English comment + Change Chinese comment to English comment */ #include "shell.h" @@ -38,14 +38,14 @@ const ShellCommand shellUserDefault SECTION("shellCommand") = }; - + #if defined(__GNUC__) extern const unsigned long _shell_command_start; extern const unsigned long _shell_command_end; #endif /** - * @brief shell Constant text index + * @brief shell Constant text index */ enum { @@ -87,45 +87,45 @@ static const char *shellText[] = "Build: "__DATE__" "__TIME__"\r\n" "Version: "SHELL_VERSION"\r\n" "Copyright: (c) 2020 Letter\r\n", - [SHELL_TEXT_CMD_TOO_LONG] = + [SHELL_TEXT_CMD_TOO_LONG] = "\r\nWarning: Command is too long\r\n", - [SHELL_TEXT_CMD_LIST] = + [SHELL_TEXT_CMD_LIST] = "\r\nCommand List:\r\n", - [SHELL_TEXT_VAR_LIST] = + [SHELL_TEXT_VAR_LIST] = "\r\nVar List:\r\n", - [SHELL_TEXT_USER_LIST] = + [SHELL_TEXT_USER_LIST] = "\r\nUser List:\r\n", [SHELL_TEXT_KEY_LIST] = "\r\nKey List:\r\n", - [SHELL_TEXT_CMD_NOT_FOUND] = + [SHELL_TEXT_CMD_NOT_FOUND] = "Command not Found\r\n", - [SHELL_TEXT_POINT_CANNOT_MODIFY] = + [SHELL_TEXT_POINT_CANNOT_MODIFY] = "can't set pointer\r\n", - [SHELL_TEXT_VAR_READ_ONLY_CANNOT_MODIFY] = + [SHELL_TEXT_VAR_READ_ONLY_CANNOT_MODIFY] = "can't set read only var\r\n", [SHELL_TEXT_NOT_VAR] = " is not a var\r\n", - [SHELL_TEXT_VAR_NOT_FOUND] = + [SHELL_TEXT_VAR_NOT_FOUND] = "Var not Fount\r\n", [SHELL_TEXT_HELP_HEADER] = "command help of ", - [SHELL_TEXT_PASSWORD_HINT] = + [SHELL_TEXT_PASSWORD_HINT] = "\r\nPlease input password:", - [SHELL_TEXT_PASSWORD_ERROR] = + [SHELL_TEXT_PASSWORD_ERROR] = "\r\npassword error\r\n", - [SHELL_TEXT_CLEAR_CONSOLE] = + [SHELL_TEXT_CLEAR_CONSOLE] = "\033[2J\033[1H", - [SHELL_TEXT_CLEAR_LINE] = + [SHELL_TEXT_CLEAR_LINE] = "\033[2K\r", - [SHELL_TEXT_TYPE_CMD] = + [SHELL_TEXT_TYPE_CMD] = "CMD ", - [SHELL_TEXT_TYPE_VAR] = + [SHELL_TEXT_TYPE_VAR] = "VAR ", - [SHELL_TEXT_TYPE_USER] = + [SHELL_TEXT_TYPE_USER] = "USER", - [SHELL_TEXT_TYPE_KEY] = + [SHELL_TEXT_TYPE_KEY] = "KEY ", - [SHELL_TEXT_TYPE_NONE] = + [SHELL_TEXT_TYPE_NONE] = "NONE", }; @@ -147,8 +147,8 @@ ShellCommand* shellSeekCommand(Shell *shell, unsigned short compareLength); /** - * @brief shell initialization - * + * @brief shell initialization + * * @param shell shell * @param buffer buffer,used to record history * @param size the size of buffer @@ -195,7 +195,7 @@ void shellInit(Shell *shell, char *buffer, unsigned short size) /** * @brief Add shell - * + * * @param shell shell */ static void shellAdd(Shell *shell) @@ -212,9 +212,9 @@ static void shellAdd(Shell *shell) /** - * @brief Get the current active shell - * - * @return Shell* Currently active shell object + * @brief Get the current active shell + * + * @return Shell* Currently active shell object */ Shell* shellGetCurrent(void) { @@ -230,10 +230,10 @@ Shell* shellGetCurrent(void) /** - * @brief Shell write characters - * + * @brief Shell write characters + * * @param shell shell - * @param data Character data + * @param data Character data */ static void shellWriteByte(Shell *shell, const char data) { @@ -242,11 +242,11 @@ static void shellWriteByte(Shell *shell, const char data) /** - * @brief shell Write string - * + * @brief shell Write string + * * @param shell shell - * @param string String data - * + * @param string String data + * * @return unsigned short Number of characters written (include '\n') */ unsigned short shellWriteString(Shell *shell, const char *string) @@ -263,12 +263,12 @@ unsigned short shellWriteString(Shell *shell, const char *string) /** - * @brief Shell write command description string - * - * @param shell shell - * @param string String data - * - * @return unsigned short Number of characters written + * @brief Shell write command description string + * + * @param shell shell + * @param string String data + * + * @return unsigned short Number of characters written */ static unsigned short shellWriteCommandDesc(Shell *shell, const char *string) { @@ -296,10 +296,10 @@ extern char working_dir[]; #endif /** * @brief Shell write new command line - * + * * @param shell shell - * @param newline Bool ,Whether to write a line - * + * @param newline Bool ,Whether to write a line + * */ static void shellWriteCommandLine(Shell *shell, unsigned char newline) { @@ -326,19 +326,19 @@ static void shellWriteCommandLine(Shell *shell, unsigned char newline) /** - * @brief shell Check command permissions - * + * @brief shell Check command permissions + * * @param shell shell * @param command ShellCommand - * - * @return signed char 0 The current user has the command authority - * @return signec char -1 The current user does not have the command authority + * + * @return signed char 0 The current user has the command authority + * @return signec char -1 The current user does not have the command authority */ signed char shellCheckPermission(Shell *shell, ShellCommand *command) { return ((!command->attr.attrs.permission || command->attr.attrs.type == SHELL_TYPE_USER - || (command->attr.attrs.permission + || (command->attr.attrs.permission & shell->info.user->attr.attrs.permission)) && (shell->status.isChecked || command->attr.attrs.enableUnchecked)) @@ -347,12 +347,12 @@ signed char shellCheckPermission(Shell *shell, ShellCommand *command) /** - * @brief int to hexadecimal string - * + * @brief int to hexadecimal string + * * @param value int value * @param buffer hexadecimal result - * - * @return signed char, data length after conversion + * + * @return signed char, data length after conversion */ signed char shellToHex(unsigned int value, char *buffer) { @@ -370,12 +370,12 @@ signed char shellToHex(unsigned int value, char *buffer) /** -* @brief int to decimal string - * +* @brief int to decimal string + * * @param value int value * @param buffer decimal result - * - * @return signed char, data length after conversion + * + * @return signed char, data length after conversion */ signed char shellToDec(int value, char *buffer) { @@ -403,11 +403,11 @@ signed char shellToDec(int value, char *buffer) /** - * @brief shell string copy - * - * @param dest destination string - * @param src Source string - * @return Unsigned short ,String length + * @brief shell string copy + * + * @param dest destination string + * @param src Source string + * @return Unsigned short ,String length */ static unsigned short shellStringCopy(char *dest, char* src) { @@ -423,11 +423,11 @@ static unsigned short shellStringCopy(char *dest, char* src) /** - * @brief Shell string comparison - * - * @param dest destination string - * @param src Source string - * @return unsigned short, Match length + * @brief Shell string comparison + * + * @param dest destination string + * @param src Source string + * @return unsigned short, Match length */ static unsigned short shellStringCompare(char* dest, char *src) { @@ -448,10 +448,10 @@ static unsigned short shellStringCompare(char* dest, char *src) /** - * @brief shell get command name - * + * @brief shell get command name + * * @param command command - * @return const char* ,Command name + * @return const char* ,Command name */ static const char* shellGetCommandName(ShellCommand *command) { @@ -482,7 +482,7 @@ static const char* shellGetCommandName(ShellCommand *command) /** * @brief shell get command description - * + * * @param command command * @return const char* ,Command description */ @@ -508,7 +508,7 @@ static const char* shellGetCommandDesc(ShellCommand *command) /** * @brief shell list items - * + * * @param shell shell ojbect * @param item ShellCommand item(func,user,key,variable...) */ @@ -555,8 +555,8 @@ void shellListItem(Shell *shell, ShellCommand *item) /** - * @brief shell lists all function commands - * + * @brief shell lists all function commands + * * @param shell shell */ void shellListCommand(Shell *shell) @@ -576,7 +576,7 @@ void shellListCommand(Shell *shell) /** * @brief shell list all variable - * + * * @param shell shell */ void shellListVar(Shell *shell) @@ -597,8 +597,8 @@ void shellListVar(Shell *shell) /** * @brief shell list all users - * - * @param shell shell + * + * @param shell shell */ void shellListUser(Shell *shell) { @@ -618,8 +618,8 @@ void shellListUser(Shell *shell) /** * @brief shell list all keys - * - * @param shell shell + * + * @param shell shell */ void shellListKey(Shell *shell) { @@ -638,8 +638,8 @@ void shellListKey(Shell *shell) /** - * @brief shell delete command line data - * + * @brief shell delete command line data + * * @param shell shell * @param length length of delete data */ @@ -654,7 +654,7 @@ void shellDeleteCommandLine(Shell *shell, unsigned char length) /** * @brief shell clear command line data - * + * * @param shell shell */ void shellClearCommandLine(Shell *shell) @@ -668,8 +668,8 @@ void shellClearCommandLine(Shell *shell) /** - * @brief shell Insert a character at the cursor position - * + * @brief shell Insert a character at the cursor position + * * @param shell shell * @param data charactoer */ @@ -696,7 +696,7 @@ void shellInsertByte(Shell *shell, char data) { for (short i = shell->parser.length - shell->parser.cursor; i > 0; i--) { - shell->parser.buffer[shell->parser.cursor + i] = + shell->parser.buffer[shell->parser.cursor + i] = shell->parser.buffer[shell->parser.cursor + i - 1]; } shell->parser.buffer[shell->parser.cursor++] = data; @@ -715,10 +715,10 @@ void shellInsertByte(Shell *shell, char data) /** * @brief shell delete character - * + * * @param shell shell * @param direction delete a character: - * {@code 1} Delete the character before the cursor + * {@code 1} Delete the character before the cursor * {@code -1}Delete the character after the cursor */ void shellDeleteByte(Shell *shell, signed char direction) @@ -741,7 +741,7 @@ void shellDeleteByte(Shell *shell, signed char direction) { for (short i = offset; i < shell->parser.length - shell->parser.cursor; i++) { - shell->parser.buffer[shell->parser.cursor + i - 1] = + shell->parser.buffer[shell->parser.cursor + i - 1] = shell->parser.buffer[shell->parser.cursor + i]; } shell->parser.length--; @@ -765,8 +765,8 @@ void shellDeleteByte(Shell *shell, signed char direction) /** - * @brief shell Parsing parameters - * + * @brief shell Parsing parameters + * * @param shell shell */ static void shellParserParam(Shell *shell) @@ -815,8 +815,8 @@ static void shellParserParam(Shell *shell) /** - * @brief shell Remove the double quotes at the beginning and end of string parameters - * + * @brief shell Remove the double quotes at the beginning and end of string parameters + * * @param shell shell */ static void shellRemoveParamQuotes(Shell *shell) @@ -839,13 +839,13 @@ static void shellRemoveParamQuotes(Shell *shell) /** - * @brief shell seeking command - * + * @brief shell seeking command + * * @param shell shell * @param cmd command - * @param base command table base address - * @param compareLength Match string length - * @return ShellCommand* :find command + * @param base command table base address + * @param compareLength Match string length + * @return ShellCommand* :find command */ ShellCommand* shellSeekCommand(Shell *shell, const char *cmd, @@ -883,8 +883,8 @@ ShellCommand* shellSeekCommand(Shell *shell, /** - * @brief shell Get variable value - * + * @brief shell Get variable value + * * @param shell shell * @param command command * @return int value @@ -921,7 +921,7 @@ int shellGetVarValue(Shell *shell, ShellCommand *command) /** * @brief shell set variable value - * + * * @param shell shell * @param command command * @param value value @@ -976,7 +976,7 @@ int shellSetVarValue(Shell *shell, ShellCommand *command, int value) /** * @brief shell print variable value - * + * * @param shell shell * @param command command * @return int value @@ -985,7 +985,7 @@ static int shellShowVar(Shell *shell, ShellCommand *command) { char buffer[12] = "00000000000"; int value = shellGetVarValue(shell, command); - + shellWriteString(shell, command->data.var.name); shellWriteString(shell, " = "); @@ -1019,7 +1019,7 @@ static int shellShowVar(Shell *shell, ShellCommand *command) /** * @brief shell set variable value - * + * * @param name value name * @param value value * @return int value @@ -1056,10 +1056,10 @@ setVar, shellSetVar, set var); /** * @brief shell run command - * + * * @param shell shell * @param command command - * + * * @return unsigned int command return value */ unsigned int shellRunCommand(Shell *shell, ShellCommand *command) @@ -1104,7 +1104,7 @@ unsigned int shellRunCommand(Shell *shell, ShellCommand *command) /** * @brief shell check password - * + * * @param shell shell */ static void shellCheckPassword(Shell *shell) @@ -1124,20 +1124,20 @@ static void shellCheckPassword(Shell *shell) /** - * @brief shell set user - * + * @brief shell set user + * * @param shell shell * @param user user */ static void shellSetUser(Shell *shell, const ShellCommand *user) { shell->info.user = user; - shell->status.isChecked = + shell->status.isChecked = ((user->data.user.password && strlen(user->data.user.password) != 0) && (shell->parser.paramCount < 2 || strcmp(user->data.user.password, shell->parser.param[1]) != 0)) ? 0 : 1; - + if (shell->status.isChecked) { shellWriteString(shell, shellText[SHELL_TEXT_INFO]); @@ -1147,7 +1147,7 @@ static void shellSetUser(Shell *shell, const ShellCommand *user) /** * @brief shell write return value - * + * * @param shell shell * @param value value */ @@ -1169,14 +1169,14 @@ static void shellWriteReturnValue(Shell *shell, int value) /** * @brief shell add history - * + * * @param shell shell */ static void shellHistoryAdd(Shell *shell) { shell->history.offset = 0; if (shell->history.number > 0 - && strcmp(shell->history.item[(shell->history.record == 0 ? + && strcmp(shell->history.item[(shell->history.record == 0 ? SHELL_HISTORY_MAX_NUMBER : shell->history.record) - 1], shell->parser.buffer) == 0) { @@ -1200,7 +1200,7 @@ static void shellHistoryAdd(Shell *shell) /** * @brief shell history find - * + * * @param shell shell * @param dir directory:{@code <0}UP, {@code >0}Down */ @@ -1208,7 +1208,7 @@ static void shellHistory(Shell *shell, signed char dir) { if (dir > 0) { - if (shell->history.offset-- <= + if (shell->history.offset-- <= -((shell->history.number > shell->history.record) ? shell->history.number : shell->history.record)) { @@ -1244,14 +1244,14 @@ static void shellHistory(Shell *shell, signed char dir) shell->parser.cursor = shell->parser.length; shellWriteString(shell, shell->parser.buffer); } - + } /** * @brief shell normalinput - * - * @param shell shell + * + * @param shell shell * @param data input character */ void shellNormalInput(Shell *shell, char data) @@ -1263,12 +1263,12 @@ void shellNormalInput(Shell *shell, char data) /** * @brief shell run command - * + * * @param shell shell */ void shellExec(Shell *shell) { - + if (shell->parser.length == 0) { return; @@ -1308,8 +1308,8 @@ void shellExec(Shell *shell) /** - * @brief shell Previous history - * + * @brief shell Previous history + * * @param shell shell */ void shellUp(Shell *shell) @@ -1321,7 +1321,7 @@ SHELL_EXPORT_KEY(SHELL_CMD_PERMISSION(0), 0x1B5B4100, shellUp, up); /** * @brief shell Next - * + * * @param shell shell */ void shellDown(Shell *shell) @@ -1333,7 +1333,7 @@ SHELL_EXPORT_KEY(SHELL_CMD_PERMISSION(0), 0x1B5B4200, shellDown, down); /** * @brief shell input right key - * + * * @param shell shell */ void shellRight(Shell *shell) @@ -1349,7 +1349,7 @@ SHELL_EXPORT_KEY(SHELL_CMD_PERMISSION(0)|SHELL_CMD_ENABLE_UNCHECKED, /** * @brief shell input left key - * + * * @param shell shell */ void shellLeft(Shell *shell) @@ -1366,7 +1366,7 @@ SHELL_EXPORT_KEY(SHELL_CMD_PERMISSION(0)|SHELL_CMD_ENABLE_UNCHECKED, /** * @brief shell Tab key function - * + * * @param shell shell */ void shellTab(Shell *shell) @@ -1399,7 +1399,7 @@ void shellTab(Shell *shell) shellWriteString(shell, "\r\n"); } shellListItem(shell, &base[lastMatchIndex]); - length = + length = shellStringCompare((char *)shellGetCommandName(&base[lastMatchIndex]), (char *)shellGetCommandName(&base[i])); maxMatch = (maxMatch > length) ? length : maxMatch; @@ -1418,7 +1418,7 @@ void shellTab(Shell *shell) } if (matchNum != 0) { - shell->parser.length = + shell->parser.length = shellStringCopy(shell->parser.buffer, (char *)shellGetCommandName(&base[lastMatchIndex])); } @@ -1460,8 +1460,8 @@ SHELL_EXPORT_KEY(SHELL_CMD_PERMISSION(0), 0x09000000, shellTab, tab); /** - * @brief shell backspace - * + * @brief shell backspace + * * @param shell shell */ void shellBackspace(Shell *shell) @@ -1476,7 +1476,7 @@ SHELL_EXPORT_KEY(SHELL_CMD_PERMISSION(0)|SHELL_CMD_ENABLE_UNCHECKED, /** * @brief shell delet - * + * * @param shell shell */ void shellDelete(Shell *shell) @@ -1489,7 +1489,7 @@ SHELL_EXPORT_KEY(SHELL_CMD_PERMISSION(0)|SHELL_CMD_ENABLE_UNCHECKED, /** * @brief shell Enter key - * + * * @param shell shell */ void shellEnter(Shell *shell) @@ -1497,24 +1497,24 @@ void shellEnter(Shell *shell) shellExec(shell); shellWriteCommandLine(shell, 1); } -#ifdef SHELL_ENTER_LF +#ifdef SHELL_ENTER_LF SHELL_EXPORT_KEY(SHELL_CMD_PERMISSION(0)|SHELL_CMD_ENABLE_UNCHECKED, 0x0A000000, shellEnter, enter); #endif -#ifdef SHELL_ENTER_CR +#ifdef SHELL_ENTER_CR SHELL_EXPORT_KEY(SHELL_CMD_PERMISSION(0)|SHELL_CMD_ENABLE_UNCHECKED, 0x0D000000, shellEnter, enter); #endif -#ifdef SHELL_ENTER_CRLF +#ifdef SHELL_ENTER_CRLF SHELL_EXPORT_KEY(SHELL_CMD_PERMISSION(0)|SHELL_CMD_ENABLE_UNCHECKED, 0x0D0A0000, shellEnter, enter); #endif /** * @brief shell help - * - * @param argc Number of parameters - * @param argv parameter + * + * @param argc Number of parameters + * @param argv parameter */ void shellHelp(int argc, char *argv[]) { @@ -1542,15 +1542,15 @@ SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN)|SHELL_CMD_DISABLE_RE Help, shellHelp, show command info\r\nhelp [cmd]); /** - * @brief shell Input processing - * + * @brief shell Input processing + * * @param shell shell * @param data input data */ void shellHandler(Shell *shell, char data) { CHECK(data); - + #if SHELL_LOCK_TIMEOUT > 0 if (shell->info.user->data.user.password @@ -1597,7 +1597,7 @@ void shellHandler(Shell *shell, char data) { shell->parser.keyValue |= data << keyByteOffset; data = 0x00; - if (keyByteOffset == 0 + if (keyByteOffset == 0 || (base[i].data.key.value & (0xFF << (keyByteOffset - 8))) == 0x00000000) { @@ -1630,9 +1630,9 @@ void shellHandler(Shell *shell, char data) /** * @brief shell task - * + * * @param param parameter(shell) - * + * */ void shellTask(void *param) { @@ -1652,12 +1652,13 @@ void shellTask(void *param) shellHandler(shell, data[i]); } } + KPrintf(""); } } /** - * @brief Output user list (shell call) + * @brief Output user list (shell call) */ void shellUsers(void) { @@ -1675,7 +1676,7 @@ users, shellUsers, list all user); /** - * @brief Output variable list (shell call) + * @brief Output variable list (shell call) */ void shellVars(void) { @@ -1692,7 +1693,7 @@ vars, shellVars, list all var); #endif /** - * @brief Output key list (shell call) + * @brief Output key list (shell call) */ void shellKeys(void) { @@ -1708,7 +1709,7 @@ SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC)|SHELL_CMD_DISABLE_RE keys, shellKeys, list all key); #endif /** - * @brief Clear the console (shell call) + * @brief Clear the console (shell call) */ void shellClear(void) { diff --git a/Ubiquitous/XiZi/tool/shell/letter-shell/shell_port.c b/Ubiquitous/XiZi/tool/shell/letter-shell/shell_port.c index 661cfb604..5d8d21a65 100644 --- a/Ubiquitous/XiZi/tool/shell/letter-shell/shell_port.c +++ b/Ubiquitous/XiZi/tool/shell/letter-shell/shell_port.c @@ -1,12 +1,12 @@ /** * @file shell_port.c * @author Letter (NevermindZZT@gmail.com) - * @brief + * @brief * @version 0.1 * @date 2019-02-22 - * + * * @copyright (c) 2019 Letter - * + * */ #include "xizi.h" @@ -26,11 +26,11 @@ char *shellBuffer; ShellFs shellFs; char *shellPathBuffer; -HardwareDevType console; +HardwareDevType console; /** - * @brief Shell write - * + * @brief Shell write + * * @param data write data */ void userShellWrite(char data) @@ -40,7 +40,7 @@ void userShellWrite(char data) /** * @brief shell read - * + * * @param data read data * @return char read status */ @@ -62,7 +62,7 @@ signed char userShellRead(char *data) #ifdef SHELL_ENABLE_FILESYSTEM /** * @brief list file - * + * * @param path path * @param buffer result buffer * @param maxLen the maximum buffer size @@ -86,14 +86,14 @@ size_t userShellListDir(char *path, char *buffer, size_t maxLen) #endif /** - * @brief Initialize the shell - * + * @brief Initialize the shell + * */ int userShellInit(void) { shellBuffer = x_malloc(512*sizeof(char)); - - + + #ifdef SHELL_ENABLE_FILESYSTEM shellPathBuffer = x_malloc(512*sizeof(char)); shellPathBuffer[0] = '/'; @@ -102,7 +102,7 @@ int userShellInit(void) shellFs.listdir = userShellListDir; shellFsInit(&shellFs, shellPathBuffer, 512); shellSetPath(&shell, shellPathBuffer); -#endif +#endif shell.write = userShellWrite; shell.read = userShellRead; @@ -114,13 +114,13 @@ int userShellInit(void) serial_dev_param->serial_set_mode = 0; serial_dev_param->serial_stream_mode = SIGN_OPER_STREAM; BusDevOpen(console); - - shellInit(&shell, shellBuffer, 512); + + shellInit(&shell, shellBuffer, 4096); int32 tid; tid = KTaskCreate("letter-shell", shellTask, &shell, SHELL_TASK_STACK_SIZE, SHELL_TASK_PRIORITY); - + StartupKTask(tid); return 0; } From be61178e4410325eb266205749904593f34e3395 Mon Sep 17 00:00:00 2001 From: wlyu Date: Thu, 17 Mar 2022 18:30:26 +0800 Subject: [PATCH 4/9] support json file parse for plc socket demo --- .../plc/interoperability/socket/plc_socket.c | 53 +++++++-- .../plc/interoperability/socket/plc_socket.h | 2 +- .../Framework/control/shared/control_file.c | 102 +++++++++++++----- .../Framework/control/shared/control_file.h | 23 +++- 4 files changed, 142 insertions(+), 38 deletions(-) diff --git a/APP_Framework/Framework/control/plc/interoperability/socket/plc_socket.c b/APP_Framework/Framework/control/plc/interoperability/socket/plc_socket.c index 1b8204c04..a69d5dc9d 100755 --- a/APP_Framework/Framework/control/plc/interoperability/socket/plc_socket.c +++ b/APP_Framework/Framework/control/plc/interoperability/socket/plc_socket.c @@ -224,6 +224,46 @@ void PlcShowUsage(void) plc_notice("------------------------------------\n"); } +void PlcGetParamFromFile(void) +{ + PlcSocketParamType *param = &plc_socket_demo_data; + + //for PLC socket parameter file + char file_buf[CTL_FILE_SIZE] = {0}; + FILE *fd = CtlFileInit(PLC_SOCK_FILE_NAME); + + if(fd == NULL) + return; + + memset(file_buf, 0, CTL_FILE_SIZE); + + CtlFileRead(fd, CTL_FILE_SIZE, file_buf); + CtlFileClose(fd); + CtlParseJsonData(file_buf); + + memcpy(param->ip, ctl_file_param.ip, 4); + param->port = ctl_file_param.port; + param->cmd_num = ctl_file_param.cmd_num; + + for(int i = 0; i < param->cmd_num; i++) + { + TestPlcCmd[i].cmd_len = ctl_file_param.cmd_len[i]; + memcpy(TestPlcCmd[i].cmd, ctl_file_param.cmd[i], TestPlcCmd[i].cmd_len); + } + + plc_print("ip: %d.%d.%d.%d\n", param->ip[0], param->ip[1], param->ip[2], param->ip[3]); + plc_print("port: %d", param->port); + plc_print("cmd number: %d\n", param->cmd_num); + + for(int i = 0; i < param->cmd_num; i++) + { + plc_print("cmd %d len %d: ", i, TestPlcCmd[i].cmd_len); + for(int j = 0; j < TestPlcCmd[i].cmd_len; j++) + plc_print("%x ", TestPlcCmd[i].cmd[j]); + plc_print("\n"); + } +} + void PlcCheckParam(int argc, char *argv[]) { int i; @@ -238,6 +278,13 @@ void PlcCheckParam(int argc, char *argv[]) plc_print("check %d %s\n", i, str); + if(strcmp(str, "file") == 0) + { + plc_notice("get parameter file %s\n", PLC_SOCK_FILE_NAME); + PlcGetParamFromFile(); + return; + } + if(sscanf(str, "ip=%d.%d.%d.%d", ¶m->ip[0], ¶m->ip[1], @@ -302,11 +349,6 @@ void PlcCheckParam(int argc, char *argv[]) } } -void PlcGetParamFromFile(char *file) -{ - -} - void PlcSocketTask(int argc, char *argv[]) { int result = 0; @@ -323,7 +365,6 @@ void PlcSocketTask(int argc, char *argv[]) PrivTaskCreate(&th_id, &attr, PlcSocketStart, param); } - SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0) | SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN) | SHELL_CMD_PARAM_NUM(3), PlcSocket, PlcSocketTask, Test PLC Socket); diff --git a/APP_Framework/Framework/control/plc/interoperability/socket/plc_socket.h b/APP_Framework/Framework/control/plc/interoperability/socket/plc_socket.h index 363877083..fbccd9b64 100755 --- a/APP_Framework/Framework/control/plc/interoperability/socket/plc_socket.h +++ b/APP_Framework/Framework/control/plc/interoperability/socket/plc_socket.h @@ -60,7 +60,7 @@ typedef struct PlcSocketParamStruct{ }PlcSocketParamType; //debug command -#define plc_print //KPrintf +#define plc_print KPrintf #define plc_error KPrintf #define plc_notice KPrintf diff --git a/APP_Framework/Framework/control/shared/control_file.c b/APP_Framework/Framework/control/shared/control_file.c index 920f89296..d906beca8 100755 --- a/APP_Framework/Framework/control/shared/control_file.c +++ b/APP_Framework/Framework/control/shared/control_file.c @@ -23,36 +23,35 @@ #include "transform.h" #include "control_file.h" -#define CTL_FILE_NAME "/plc/control_cmd.txt" - -#define TEST_PLC_CMD_TXT \ - "cmd=03,00,00,16,11,E0,00,00,02,C8,00,C1,02,02,01,C2,02,02,01,C0,01,0A\r\n\r\n" \ - "cmd=03,00,00,19,02,F0,80,32,01,00,00,00,0D,00,08,00,00,F0,00,00,01,00,01,00,F0\r\n\r\n" \ - "cmd=03,00,00,1F,02,F0,80,32,01,00,00,33,01,00,0E,00,00,04,01,12,0A,10,02,00,D2,00,34,84,00,00,00\r\n\r\n" \ - "port=102\r\n" \ - "ip=192.168.250.6\r\n" - +//json file parameter for PLC socket communication as below: +//{ +// "ip": "192.168.250.6", +// "port": 102, +// "cmd": [3, 0, 0, 22, 17, 224, 0, 0, 2, 200, 0, 193, 2, 2, 1, 194, 2, 2, 1, 192, 1, 10], +// "cmd1": [3, 0, 0, 25, 2, 240, 128, 50, 1, 0, 0, 0, 13, 0, 8, 0, 0, 240, 0, 0, 1, 0, 1, 0, 240], \r\n" \ +// "cmd2": [3, 0, 0, 31, 2, 240, 128, 50, 1, 0, 0, 51, 1, 0, 14, 0, 0, 4, 1, 18, 10, 16, 2, 0, 210, 0, 52, 132, 0, 0, 0]\r\n" \ +//}" #define TEST_PLC_JSON_TXT \ "{ \r\n"\ -" \"S1200\": [ \r\n"\ -" { \"ip\": \"192.168.250.6\"}, \r\n"\ -" { \"port\": 102}, \r\n"\ -" { \"cmd\": [3, 0, 0, 22, 17, 224, 0, 0, 2, 200, 0, 193, 2, 2, 1, 194, 2, 2, 1, 192, 1, 10]}\r\n"\ -" ] \r\n"\ +" \"ip\": \"192.168.250.6\", \r\n"\ +" \"port\": 102, \r\n"\ +" \"cmd\": [3, 0, 0, 22, 17, 224, 0, 0, 2, 200, 0, 193, 2, 2, 1, 194, 2, 2, 1, 192, 1, 10], \r\n"\ +" \"cmd1\": [3, 0, 0, 25, 2, 240, 128, 50, 1, 0, 0, 0, 13, 0, 8, 0, 0, 240, 0, 0, 1, 0, 1, 0, 240], \r\n" \ +" \"cmd2\": [3, 0, 0, 31, 2, 240, 128, 50, 1, 0, 0, 51, 1, 0, 14, 0, 0, 4, 1, 18, 10, 16, 2, 0, 210, 0, 52, 132, 0, 0, 0]\r\n" \ "}" -//" { \"cmd\": [3, 0, 0, 25, 2, 240, 128, 50, 1, 0, 0, 0, 13, 0, 8, 0, 0, 240, 0, 0, 1, 0, 1, 0, 240]},\r\n" \ -//" { \"cmd\": [3, 0, 0, 31, 2, 240, 128, 50, 1, 0, 0, 51, 1, 0, 14, 0, 0, 4, 1, 18, 10, 16, 2, 0, 210, 0, 52, 132, 0, 0, 0]}\r\n" \ -FILE *CtlFileInit(void) +CtlPlcSockParamType ctl_file_param; + +FILE *CtlFileInit(char *file) { FILE *fd = NULL; - fd = fopen(CTL_FILE_NAME, "a+"); + fd = fopen(file, "a+"); if(fd == NULL) { - ctl_error("open file %s failed\n", CTL_FILE_NAME); + ctl_error("open file %s failed\n", file); } return fd; @@ -65,8 +64,9 @@ void CtlFileClose(FILE *fd) void CtlFileRead(FILE *fd, int size, char *buf) { + fseek(fd, 0, SEEK_SET); fread(buf, size, 1, fd); - ctl_print("read file %d: %s\n", size, buf); + ctl_print("read file %d: %.100s\n", size, buf); } void CtlFileWrite(FILE *fd, int size, char *buf) @@ -76,26 +76,52 @@ void CtlFileWrite(FILE *fd, int size, char *buf) ctl_print("write size %d: %s\n", size, buf); } -void CtlFileTest(void) +void CtlCreateFileTest(void) { - FILE *fd = CtlFileInit(); + FILE *fd = CtlFileInit(PLC_SOCK_FILE_NAME); + if(fd == NULL) + return; char *file_buf = TEST_PLC_JSON_TXT; CtlFileWrite(fd, strlen(file_buf), file_buf); - CtlFileRead(fd, CTL_FILE_SIZE, file_buf); CtlFileClose(fd); } SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0) | SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN) | SHELL_CMD_PARAM_NUM(0), - CtlTestFile, CtlFileTest, Test control file); + CtlCreateFile, CtlCreateFileTest, Test control file); #ifdef LIB_USING_CJSON +void CtlParseJsonArray(cJSON *dat, int *cmd_len, char *cmd) +{ + int len, i; + if(cJSON_IsArray(dat)) + { + len = cJSON_GetArraySize(dat); + ctl_print("json cmd %d\n", len); + + for(i = 0; i < len; i++) + { + cJSON *cmd_val = cJSON_GetArrayItem(dat, i); + if(NULL == cmd_val) + continue; + ctl_print("0x%x ", cmd_val->valueint); + cmd[i] = cmd_val->valueint; + } + *cmd_len = len; + ctl_print("\n"); + } +} + void CtlParseJsonData(char *buf) { cJSON *file_dat = NULL; cJSON *ip_dat = NULL; cJSON *port_dat = NULL; cJSON *cmd_dat = NULL; + int cmd_num = 0; + int cmd_index = 1; + char cmd_str[10] = {0}; + CtlPlcSockParamType *file_param = &ctl_file_param; file_dat = cJSON_Parse(buf); if(file_dat == NULL) @@ -106,17 +132,35 @@ void CtlParseJsonData(char *buf) ip_dat = cJSON_GetObjectItem(file_dat, "ip"); port_dat = cJSON_GetObjectItem(file_dat, "port"); - cmd_dat = cJSON_GetObjectItem(file_dat, "cmd"); - ctl_print("ip : %s\n", ip_dat->string); - ctl_print("port: %d\n", port_dat->valueint); - ctl_print("cmd : %s\n", cmd_dat->valueint); + ctl_print(" ip : %s\n", ip_dat->valuestring); + sscanf(ip_dat->valuestring, "%d.%d.%d.%d", &file_param->ip[0], + &file_param->ip[1], + &file_param->ip[2], + &file_param->ip[3]); + + ctl_print(" port: %s %d\n", ip_dat->string, port_dat->valueint); + file_param->port = port_dat->valueint; + + strcpy(cmd_str, "cmd"); + while(cmd_dat = cJSON_GetObjectItem(file_dat, cmd_str)) + { + CtlParseJsonArray(cmd_dat, &file_param->cmd_len[cmd_index - 1], file_param->cmd[cmd_index - 1]); + snprintf(cmd_str, sizeof(cmd_str), "cmd%d", cmd_index++); + } + file_param->cmd_num = cmd_index - 1; + + cJSON_Delete(file_dat); } void CtlParseFileTest(void) { + //for PLC socket parameter file char file_buf[CTL_FILE_SIZE] = {0}; - FILE *fd = CtlFileInit(); + FILE *fd = CtlFileInit(PLC_SOCK_FILE_NAME); + if(fd == NULL) + return; + memset(file_buf, 0, CTL_FILE_SIZE); CtlFileRead(fd, CTL_FILE_SIZE, file_buf); CtlFileClose(fd); CtlParseJsonData(file_buf); diff --git a/APP_Framework/Framework/control/shared/control_file.h b/APP_Framework/Framework/control/shared/control_file.h index 6c933594c..dcafee179 100755 --- a/APP_Framework/Framework/control/shared/control_file.h +++ b/APP_Framework/Framework/control/shared/control_file.h @@ -21,15 +21,34 @@ #ifndef __CONTROL_FILE_H_ #define __CONTROL_FILE_H_ -#define CTL_FILE_SIZE 1024 +#define CTL_FILE_SIZE 1000 +#define CTL_CMD_NUM 10 // max command number +#define CTL_CMD_LEN 100 +#define CTL_IP_LEN 32 + +#define PLC_SOCK_FILE_NAME "/plc/socket_param.json" #define ctl_print KPrintf #define ctl_error KPrintf -FILE *CtlFileInit(void); +typedef struct CtlPlcSockParamStruct +{ + char ip[CTL_IP_LEN]; + int port; + int cmd_num; //command number + int cmd_len[CTL_CMD_NUM]; // command length + char cmd[CTL_CMD_NUM][CTL_CMD_LEN]; +}CtlPlcSockParamType; + +extern CtlPlcSockParamType ctl_file_param; + +FILE *CtlFileInit(char *file); void CtlFileClose(FILE *fd); void CtlFileRead(FILE *fd, int size, char *buf); void CtlFileWrite(FILE *fd, int size, char *buf); +#ifdef LIB_USING_CJSON +void CtlParseJsonData(char *buf); +#endif #endif From ec7b594345f3d42d589eb36ef9eed98364d014e4 Mon Sep 17 00:00:00 2001 From: Wang_Weigen Date: Mon, 21 Mar 2022 16:19:57 +0800 Subject: [PATCH 5/9] repair ch438 driver --- .../Applications/sensor_app/SConscript | 2 + APP_Framework/Framework/sensor/ch4/Kconfig | 17 + APP_Framework/Framework/sensor/ch4/SConscript | 14 + .../Framework/sensor/ch4/as830/SConscript | 10 + APP_Framework/Framework/sensor/co2/Kconfig | 17 + .../Framework/sensor/co2/zg09/SConscript | 10 + APP_Framework/Framework/sensor/voice/Kconfig | 17 + .../aiit_board/aiit-riscv64-board/.config | 36 +- .../aiit-riscv64-board/applications/main.c | 3 +- .../aiit-riscv64-board/base-drivers/ch438.c | 327 +++++++++--------- .../aiit_board/aiit-riscv64-board/rtconfig.h | 15 +- 11 files changed, 287 insertions(+), 181 deletions(-) create mode 100644 APP_Framework/Framework/sensor/ch4/SConscript create mode 100644 APP_Framework/Framework/sensor/ch4/as830/SConscript create mode 100644 APP_Framework/Framework/sensor/co2/zg09/SConscript diff --git a/APP_Framework/Applications/sensor_app/SConscript b/APP_Framework/Applications/sensor_app/SConscript index 58ec915b4..dbf2e67c0 100644 --- a/APP_Framework/Applications/sensor_app/SConscript +++ b/APP_Framework/Applications/sensor_app/SConscript @@ -15,6 +15,8 @@ if GetDepend(['APPLICATION_SENSOR_HUMIDITY_HS300X']): SOURCES = ['humidity_hs300x.c'] + SOURCES if GetDepend(['APPLICATION_SENSOR_TEMPERATURE_HS300X']): SOURCES = ['temperature_hs300x.c'] + SOURCES +if GetDepend(['APPLICATION_SENSOR_CH4_AS830']): + SOURCES = ['ch4_as830.c'] + SOURCES path = [cwd] objs = DefineGroup('sensor_app', src = SOURCES, depend = DEPENDS,CPPPATH = path) Return("objs") \ No newline at end of file diff --git a/APP_Framework/Framework/sensor/ch4/Kconfig b/APP_Framework/Framework/sensor/ch4/Kconfig index 85b5099cd..f61a56941 100644 --- a/APP_Framework/Framework/sensor/ch4/Kconfig +++ b/APP_Framework/Framework/sensor/ch4/Kconfig @@ -43,6 +43,23 @@ config SENSOR_AS830 endif if ADD_RTTHREAD_FETURES + config SENSOR_AS830_DRIVER_EXTUART + bool "Using extra uart to support as830" + default y + config SENSOR_DEVICE_AS830_DEV + string "as830 device uart path" + default "/dev/uart2" + depends on !SENSOR_AS830_DRIVER_EXTUART + + if SENSOR_AS830_DRIVER_EXTUART + config SENSOR_DEVICE_AS830_DEV + string "as830 device extra uart path" + default "/dev/extuart_dev1" + + config SENSOR_DEVICE_AS830_DEV_EXT_PORT + int "if AS830 device using extuart, choose port" + default "1" + endif endif endif diff --git a/APP_Framework/Framework/sensor/ch4/SConscript b/APP_Framework/Framework/sensor/ch4/SConscript new file mode 100644 index 000000000..f307e3f70 --- /dev/null +++ b/APP_Framework/Framework/sensor/ch4/SConscript @@ -0,0 +1,14 @@ +import os +Import('RTT_ROOT') +from building import * + +cwd = GetCurrentDir() +objs = [] +list = os.listdir(cwd) + +for d in list: + path = os.path.join(cwd, d) + if os.path.isfile(os.path.join(path, 'SConscript')): + objs = objs + SConscript(os.path.join(path, 'SConscript')) + +Return('objs') diff --git a/APP_Framework/Framework/sensor/ch4/as830/SConscript b/APP_Framework/Framework/sensor/ch4/as830/SConscript new file mode 100644 index 000000000..1e361eddf --- /dev/null +++ b/APP_Framework/Framework/sensor/ch4/as830/SConscript @@ -0,0 +1,10 @@ +from building import * +import os + +cwd = GetCurrentDir() +src = [] +if GetDepend(['SENSOR_AS830']): + src += ['as830.c'] +group = DefineGroup('sensor ch4 as830', src, depend = [], CPPPATH = [cwd]) + +Return('group') \ No newline at end of file diff --git a/APP_Framework/Framework/sensor/co2/Kconfig b/APP_Framework/Framework/sensor/co2/Kconfig index da5581985..e4c4c430f 100644 --- a/APP_Framework/Framework/sensor/co2/Kconfig +++ b/APP_Framework/Framework/sensor/co2/Kconfig @@ -43,7 +43,24 @@ config SENSOR_ZG09 endif if ADD_RTTHREAD_FETURES + config SENSOR_ZG09_DRIVER_EXTUART + bool "Using extra uart to support zg09" + default y + config SENSOR_DEVICE_ZG09_DEV + string "zg09 device uart path" + default "/dev/uart2_dev2" + depends on !SENSOR_ZG09_DRIVER_EXTUART + + if SENSOR_ZG09_DRIVER_EXTUART + config SENSOR_DEVICE_ZG09_DEV + string "zg09 device extra uart path" + default "/dev/extuart_dev4" + + config SENSOR_DEVICE_ZG09_DEV_EXT_PORT + int "if ZG09 device using extuart, choose port" + default "4" + endif endif endif diff --git a/APP_Framework/Framework/sensor/co2/zg09/SConscript b/APP_Framework/Framework/sensor/co2/zg09/SConscript new file mode 100644 index 000000000..084f10122 --- /dev/null +++ b/APP_Framework/Framework/sensor/co2/zg09/SConscript @@ -0,0 +1,10 @@ +from building import * +import os + +cwd = GetCurrentDir() +src = [] +if GetDepend(['SENSOR_ZG09']): + src += ['zg09.c'] +group = DefineGroup('sensor co2 zg09', src, depend = [], CPPPATH = [cwd]) + +Return('group') \ No newline at end of file diff --git a/APP_Framework/Framework/sensor/voice/Kconfig b/APP_Framework/Framework/sensor/voice/Kconfig index bad0d4602..87a8c213f 100644 --- a/APP_Framework/Framework/sensor/voice/Kconfig +++ b/APP_Framework/Framework/sensor/voice/Kconfig @@ -43,6 +43,23 @@ config SENSOR_D124 endif if ADD_RTTHREAD_FETURES + config SENSOR_D124_DRIVER_EXTUART + bool "Using extra uart to support D124" + default y + config SENSOR_DEVICE_D124_DEV + string "D124 device name" + default "/dev/uart2_dev2" + depends on !SENSOR_D124_DRIVER_EXTUART + + if SENSOR_D124_DRIVER_EXTUART + config SENSOR_DEVICE_D124_DEV + string "D124 device extra uart path" + default "/dev/extuart_dev4" + + config SENSOR_DEVICE_D124_DEV_EXT_PORT + int "if D124 device using extuart, choose port" + default "4" + endif endif endif diff --git a/Ubiquitous/RT_Thread/aiit_board/aiit-riscv64-board/.config b/Ubiquitous/RT_Thread/aiit_board/aiit-riscv64-board/.config index 7888f84f1..987bff78e 100644 --- a/Ubiquitous/RT_Thread/aiit_board/aiit-riscv64-board/.config +++ b/Ubiquitous/RT_Thread/aiit_board/aiit-riscv64-board/.config @@ -11,7 +11,7 @@ CONFIG_BOARD_K210_EVB=y # # RT-Thread Kernel # -CONFIG_RT_NAME_MAX=8 +CONFIG_RT_NAME_MAX=24 # CONFIG_RT_USING_BIG_ENDIAN is not set # CONFIG_RT_USING_ARCH_DATA_TYPE is not set CONFIG_RT_USING_SMP=y @@ -292,11 +292,6 @@ CONFIG_BSP_UART1_CTS_PIN=-1 # CONFIG_BSP_USING_I2C1 is not set # CONFIG_BSP_USING_SPI1 is not set # CONFIG_BSP_USING_LCD is not set -# CONFIG_BSP_LCD_BACKLIGHT_ACTIVE_LOW is not set -# CONFIG_BSP_LCD_BACKLIGHT_ACTIVE_HIGH is not set -# CONFIG_BSP_BOARD_KD233 is not set -# CONFIG_BSP_BOARD_K210_OPENMV_TEST is not set -# CONFIG_BSP_BOARD_USER is not set # CONFIG_BSP_USING_SDCARD is not set # CONFIG_BSP_USING_DVP is not set CONFIG_BSP_USING_CH438=y @@ -332,10 +327,28 @@ CONFIG_PKG_KENDRYTE_SDK_VERNUM=0x0055 # Framework # CONFIG_TRANSFORM_LAYER_ATTRIUBUTE=y -CONFIG_ADD_XIZI_FETURES=y +# CONFIG_ADD_XIZI_FETURES is not set # CONFIG_ADD_NUTTX_FETURES is not set -# CONFIG_ADD_RTTHREAD_FETURES is not set -# CONFIG_SUPPORT_SENSOR_FRAMEWORK is not set +CONFIG_ADD_RTTHREAD_FETURES=y +CONFIG_SUPPORT_SENSOR_FRAMEWORK=y +# CONFIG_SENSOR_HCHO is not set +# CONFIG_SENSOR_TVOC is not set +# CONFIG_SENSOR_IAQ is not set +CONFIG_SENSOR_CH4=y +CONFIG_SENSOR_AS830=y +CONFIG_SENSOR_DEVICE_AS830="as830_1" +CONFIG_SENSOR_QUANTITY_AS830_CH4="ch4_1" +CONFIG_SENSOR_AS830_DRIVER_EXTUART=y +CONFIG_SENSOR_DEVICE_AS830_DEV="/dev/extuart_dev1" +CONFIG_SENSOR_DEVICE_AS830_DEV_EXT_PORT=1 +# CONFIG_SENSOR_CO2 is not set +# CONFIG_SENSOR_PM is not set +# CONFIG_SENSOR_VOICE is not set +# CONFIG_SENSOR_TEMPERATURE is not set +# CONFIG_SENSOR_HUMIDITY is not set +# CONFIG_SENSOR_WINDSPEED is not set +# CONFIG_SENSOR_WINDDIRECTION is not set +# CONFIG_SENSOR_ALTITUDE is not set # CONFIG_SUPPORT_CONNECTION_FRAMEWORK is not set # CONFIG_SUPPORT_KNOWING_FRAMEWORK is not set # CONFIG_SUPPORT_CONTROL_FRAMEWORK is not set @@ -385,7 +398,8 @@ CONFIG_APPLICATION_SENSOR=y # CONFIG_APPLICATION_SENSOR_HCHO is not set # CONFIG_APPLICATION_SENSOR_TVOC is not set # CONFIG_APPLICATION_SENSOR_IAQ is not set -# CONFIG_APPLICATION_SENSOR_CH4 is not set +CONFIG_APPLICATION_SENSOR_CH4=y +CONFIG_APPLICATION_SENSOR_CH4_AS830=y # CONFIG_APPLICATION_SENSOR_CO2 is not set # CONFIG_APPLICATION_SENSOR_PM1_0 is not set # CONFIG_APPLICATION_SENSOR_PM2_5 is not set @@ -403,7 +417,7 @@ CONFIG_APPLICATION_SENSOR=y # CONFIG_APP_SELECT_NEWLIB=y # CONFIG_APP_SELECT_OTHER_LIB is not set -CONFIG_LIB_USING_CJSON=y +# CONFIG_LIB_USING_CJSON is not set # CONFIG_LIB_USING_QUEUE is not set # CONFIG_LIB_LV is not set # CONFIG_USING_EMBEDDED_DATABASE is not set diff --git a/Ubiquitous/RT_Thread/aiit_board/aiit-riscv64-board/applications/main.c b/Ubiquitous/RT_Thread/aiit_board/aiit-riscv64-board/applications/main.c index 0ae9f62ea..43f682c6a 100644 --- a/Ubiquitous/RT_Thread/aiit_board/aiit-riscv64-board/applications/main.c +++ b/Ubiquitous/RT_Thread/aiit_board/aiit-riscv64-board/applications/main.c @@ -20,9 +20,10 @@ #include #include - +extern int FrameworkInit(); int main(void) { printf("Hello World\n"); + FrameworkInit(); return 0; } diff --git a/Ubiquitous/RT_Thread/aiit_board/aiit-riscv64-board/base-drivers/ch438.c b/Ubiquitous/RT_Thread/aiit_board/aiit-riscv64-board/base-drivers/ch438.c index 2d75e64a0..7a5212739 100644 --- a/Ubiquitous/RT_Thread/aiit_board/aiit-riscv64-board/base-drivers/ch438.c +++ b/Ubiquitous/RT_Thread/aiit_board/aiit-riscv64-board/base-drivers/ch438.c @@ -5,11 +5,10 @@ #include "board.h" #include "ch438.h" #include "sleep.h" - -static struct rt_semaphore ch438_sem; +#include static rt_uint8_t offsetadd[] = {0x00,0x10,0x20,0x30,0x08,0x18,0x28,0x38,}; /* Offset address of serial port number */ -rt_uint8_t RevLen ,Ch438Buff[8][BUFFSIZE],Ch438BuffPtr[8]; +struct rt_serial_device *extuart_serial_parm[8]; void CH438_INIT(void) { @@ -25,7 +24,7 @@ void CH438_INIT(void) gpiohs_set_pin(FPIOA_CH438_ALE, GPIO_PV_HIGH); } -void CH438_PORT_INIT( rt_uint8_t ext_uart_no,rt_uint32_t BaudRate ) +void CH438_PORT_INIT( rt_uint8_t ext_uart_no,rt_uint32_t BaudRate ) { rt_uint32_t div; rt_uint8_t DLL,DLM,dlab; @@ -164,7 +163,6 @@ rt_uint8_t ReadCH438Data( rt_uint8_t addr ) usleep(1); return dat; - } @@ -216,106 +214,47 @@ static void WriteCH438Data( rt_uint8_t addr, rt_uint8_t dat) return; } - static void WriteCH438Block( rt_uint8_t mAddr, rt_uint8_t mLen, rt_uint8_t *mBuf ) { - while ( mLen -- ) WriteCH438Data( mAddr, *mBuf++ ); - } +static int Ch438Irq(void *parameter) +{ + rt_uint8_t gInterruptStatus; + rt_uint8_t port = 0; -// void CH438UARTSend( rt_uint8_t ext_uart_no,rt_uint8_t *Data, rt_uint8_t Num ) -// { -// rt_uint8_t REG_LSR_ADDR,REG_THR_ADDR; - -// REG_LSR_ADDR = offsetadd[ext_uart_no] | REG_LSR0_ADDR; -// REG_THR_ADDR = offsetadd[ext_uart_no] | REG_THR0_ADDR; - -// while( 1 ) -// { - -// while( ( ReadCH438Data( REG_LSR_ADDR ) & BIT_LSR_TEMT ) == 0 ); - -// if( Num <= 128 ) -// { - -// WriteCH438Block( REG_THR_ADDR, Num, Data ); - -// break; - -// } - -// else -// { - -// WriteCH438Block( REG_THR_ADDR, 128, Data ); - -// Num -= 128; - -// Data += 128; - -// } - -// } -// } - - - -// rt_uint8_t CH438UARTRcv( rt_uint8_t ext_uart_no, rt_uint8_t* buf ) -// { -// rt_uint8_t RcvNum = 0; -// rt_uint8_t dat = 0; -// rt_uint8_t REG_LSR_ADDR,REG_RBR_ADDR; -// rt_uint8_t *p_rev; - -// p_rev = buf; - -// REG_LSR_ADDR = offsetadd[ext_uart_no] | REG_LSR0_ADDR; -// REG_RBR_ADDR = offsetadd[ext_uart_no] | REG_RBR0_ADDR; - -// { - -// while( ( ReadCH438Data( REG_LSR_ADDR ) & BIT_LSR_DATARDY ) == 0 ); - -// while( ( ReadCH438Data( REG_LSR_ADDR ) & BIT_LSR_DATARDY ) == 0x01 ) -// { -// dat = ReadCH438Data( REG_RBR_ADDR ); - -// Ch438Buff[ext_uart_no][Ch438BuffPtr[ext_uart_no]] = dat; - -// Ch438BuffPtr[ext_uart_no] = Ch438BuffPtr[ext_uart_no] + 1; -// if (Ch438BuffPtr[ext_uart_no] == BUFFSIZE) -// Ch438BuffPtr[ext_uart_no] = 0; - -// RcvNum = RcvNum + 1; - -// } -// } -// return( RcvNum ); -// } + gInterruptStatus = ReadCH438Data(REG_SSR_ADDR); + port = log(gInterruptStatus & 0xFF)/log(2); + rt_hw_serial_isr(extuart_serial_parm[port], RT_SERIAL_EVENT_RX_IND); +} static rt_err_t rt_extuart_configure(struct rt_serial_device *serial, struct serial_configure *cfg) { + rt_uint32_t baud_rate = cfg->baud_rate; + rt_uint16_t port = cfg->reserved; - rt_uint32_t baud_rate = cfg->baud_rate; - uint16_t port = cfg->reserved; CH438_PORT_INIT(port, baud_rate); + return RT_EOK; } static rt_err_t extuart_control(struct rt_serial_device *serial, int cmd, void *arg) { + uint16_t ext_uart_no = serial->config.reserved; + switch (cmd) { case RT_DEVICE_CTRL_CLR_INT: + gpiohs_irq_unregister(FPIOA_CH438_INT); break; - case RT_DEVICE_CTRL_SET_INT: - break; + gpiohs_set_drive_mode(FPIOA_CH438_INT, GPIO_DM_INPUT_PULL_UP); + gpiohs_set_pin_edge(FPIOA_CH438_INT,GPIO_PE_FALLING); + gpiohs_irq_register(FPIOA_CH438_INT, 1, Ch438Irq, RT_NULL); + break; } - return (RT_EOK); } @@ -335,22 +274,21 @@ static int drv_extuart_putc(struct rt_serial_device *serial, char c) static int drv_extuart_getc(struct rt_serial_device *serial) { - rt_uint8_t dat = 0; + rt_int8_t dat = -1; rt_uint8_t REG_LSR_ADDR,REG_RBR_ADDR; - uint16_t ext_uart_no = serial->config.reserved;///< get extern uart port REG_LSR_ADDR = offsetadd[ext_uart_no] | REG_LSR0_ADDR; REG_RBR_ADDR = offsetadd[ext_uart_no] | REG_RBR0_ADDR; - - while( ( ReadCH438Data( REG_LSR_ADDR ) & BIT_LSR_DATARDY ) == 0 ); - // while( ( ReadCH438Data( REG_LSR_ADDR ) & BIT_LSR_DATARDY ) == 0x01 ) - // { - dat = ReadCH438Data( REG_RBR_ADDR ); - // } - - return( dat ); + if((ReadCH438Data(REG_LSR_ADDR) & BIT_LSR_DATARDY) == 0x01) + { + dat = ReadCH438Data( REG_RBR_ADDR ); + if(dat >= 0) + return dat; + } else { + return -1; + } } const struct rt_uart_ops extuart_ops = @@ -362,111 +300,168 @@ const struct rt_uart_ops extuart_ops = RT_NULL }; -static int Ch438Irq(void *parameter) -{ - rt_sem_release(&ch438_sem); -} - -int Ch438InitDefault(void) -{ - rt_err_t flag; - - flag = rt_sem_init(&ch438_sem, "sem_438",0,RT_IPC_FLAG_FIFO); - if (flag != RT_EOK) - { - rt_kprintf("ch438.drv create sem failed .\n"); - return -1; - } - - gpiohs_set_drive_mode(FPIOA_CH438_INT, GPIO_DM_INPUT_PULL_UP); - gpiohs_set_pin_edge(FPIOA_CH438_INT,GPIO_PE_FALLING); - gpiohs_irq_register(FPIOA_CH438_INT, 1, Ch438Irq, 0); - - CH438_INIT(); - return 0; -} -INIT_APP_EXPORT(Ch438InitDefault); - int rt_hw_ch438_init(void) { struct rt_serial_device *extserial; struct device_uart *extuart; struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT; + rt_err_t ret; - -// #ifdef BSP_USING_UART1 { static struct rt_serial_device extserial0; - // static struct device_uart extuart0; extserial = &extserial0; - // extuart = &extuart0; - extserial->ops = &extuart_ops; extserial->config = config; extserial->config.baud_rate = 115200; - extserial->config.reserved = 0; ///< extern uart port + extserial->config.reserved = 0; ///< extern uart port - // extuart->hw_base = UART1_BASE_ADDR; - // extuart->irqno = IRQN_UART1_INTERRUPT; + extuart_serial_parm[0] = &extserial0; - // _uart_init(UART_DEVICE_1); - - rt_hw_serial_register(extserial, + ret = rt_hw_serial_register(extserial, "extuart_dev0", RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX, extuart); + if(ret < 0){ + rt_kprintf("extuart_dev0 register failed.\n"); + } } -// #endif + { + static struct rt_serial_device extserial1; -// #ifdef BSP_USING_UART2 - // { - // static struct rt_serial_device serial2; - // static struct device_uart uart2; + extserial = &extserial1; + extserial->ops = &extuart_ops; + extserial->config = config; + extserial->config.baud_rate = 9600; + extserial->config.reserved = 1; ///< extern uart port - // serial = &serial2; - // uart = &uart2; + extuart_serial_parm[1] = &extserial1; - // serial->ops = &_uart_ops; - // serial->config = config; - // serial->config.baud_rate = UART_DEFAULT_BAUDRATE; + ret = rt_hw_serial_register(extserial, + "extuart_dev1", + RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX, + extuart); + if(ret < 0){ + rt_kprintf("extuart_dev1 register failed.\n"); + } + } + { + static struct rt_serial_device extserial2; - // uart->hw_base = UART2_BASE_ADDR; - // uart->irqno = IRQN_UART2_INTERRUPT; + extserial = &extserial2; + extserial->ops = &extuart_ops; + extserial->config = config; + extserial->config.baud_rate = 9600; + extserial->config.reserved = 2; ///< extern uart port - // _uart_init(UART_DEVICE_2); + extuart_serial_parm[2] = &extserial2; - // rt_hw_serial_register(serial, - // "uart2", - // RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX, - // uart); - // } -// #endif + ret = rt_hw_serial_register(extserial, + "extuart_dev2", + RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX, + extuart); + if(ret < 0){ + rt_kprintf("extuart_dev2 register failed.\n"); + } -// #ifdef BSP_USING_UART3 - // { - // static struct rt_serial_device serial3; - // static struct device_uart uart3; + } + { + static struct rt_serial_device extserial3; - // serial = &serial3; - // uart = &uart3; + extserial = &extserial3; + extserial->ops = &extuart_ops; + extserial->config = config; + extserial->config.baud_rate = 9600; + extserial->config.reserved = 3; ///< extern uart port - // serial->ops = &_uart_ops; - // serial->config = config; - // serial->config.baud_rate = UART_DEFAULT_BAUDRATE; + ret = rt_hw_serial_register(extserial, + "extuart_dev3", + RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX, + extuart); + if(ret < 0){ + rt_kprintf("extuart_dev3 register failed.\n"); + } - // uart->hw_base = UART3_BASE_ADDR; - // uart->irqno = IRQN_UART3_INTERRUPT; + extuart_serial_parm[3] = &extserial3; + } + { + static struct rt_serial_device extserial4; - // _uart_init(UART_DEVICE_3); + extserial = &extserial4; + extserial->ops = &extuart_ops; + extserial->config = config; + extserial->config.baud_rate = 9600; + extserial->config.reserved = 4; ///< extern uart port - // rt_hw_serial_register(serial, - // "uart3", - // RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX, - // uart); - // } -// #endif - // Ch438InitDefault(); + ret = rt_hw_serial_register(extserial, + "extuart_dev4", + RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX, + extuart); + if(ret < 0){ + rt_kprintf("extuart_dev4 register failed.\n"); + } + + extuart_serial_parm[4] = &extserial4; + } + { + static struct rt_serial_device extserial5; + + extserial = &extserial5; + extserial->ops = &extuart_ops; + extserial->config = config; + extserial->config.baud_rate = 115200; + extserial->config.reserved = 5; ///< extern uart port + + ret = rt_hw_serial_register(extserial, + "extuart_dev5", + RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX, + extuart); + if(ret < 0){ + rt_kprintf("extuart_dev5 register failed.\n"); + } + + extuart_serial_parm[5] = &extserial5; + } + { + static struct rt_serial_device extserial6; + + extserial = &extserial6; + extserial->ops = &extuart_ops; + extserial->config = config; + extserial->config.baud_rate = 57600; + extserial->config.reserved = 6; ///< extern uart port + + ret = rt_hw_serial_register(extserial, + "extuart_dev6", + RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX, + extuart); + if(ret < 0){ + rt_kprintf("extuart_dev6 register failed.\n"); + } + + extuart_serial_parm[6] = &extserial6; + } + { + static struct rt_serial_device extserial7; + + extserial = &extserial7; + extserial->ops = &extuart_ops; + extserial->config = config; + extserial->config.baud_rate = 9600; + extserial->config.reserved = 7; ///< extern uart port + + ret = rt_hw_serial_register(extserial, + "extuart_dev7", + RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX, + extuart); + if(ret < 0){ + rt_kprintf("extuart_dev7 register failed.\n"); + } + extuart_serial_parm[7] = &extserial7; + + } + + CH438_INIT(); return 0; } INIT_DEVICE_EXPORT(rt_hw_ch438_init); diff --git a/Ubiquitous/RT_Thread/aiit_board/aiit-riscv64-board/rtconfig.h b/Ubiquitous/RT_Thread/aiit_board/aiit-riscv64-board/rtconfig.h index e8a596e33..0f8d716bf 100644 --- a/Ubiquitous/RT_Thread/aiit_board/aiit-riscv64-board/rtconfig.h +++ b/Ubiquitous/RT_Thread/aiit_board/aiit-riscv64-board/rtconfig.h @@ -12,7 +12,7 @@ /* RT-Thread Kernel */ -#define RT_NAME_MAX 8 +#define RT_NAME_MAX 24 #define RT_USING_SMP #define RT_CPUS_NR 2 #define RT_ALIGN_SIZE 8 @@ -206,7 +206,15 @@ /* Framework */ #define TRANSFORM_LAYER_ATTRIUBUTE -#define ADD_XIZI_FETURES +#define ADD_RTTHREAD_FETURES +#define SUPPORT_SENSOR_FRAMEWORK +#define SENSOR_CH4 +#define SENSOR_AS830 +#define SENSOR_DEVICE_AS830 "as830_1" +#define SENSOR_QUANTITY_AS830_CH4 "ch4_1" +#define SENSOR_AS830_DRIVER_EXTUART +#define SENSOR_DEVICE_AS830_DEV "/dev/extuart_dev1" +#define SENSOR_DEVICE_AS830_DEV_EXT_PORT 1 /* Security */ @@ -234,11 +242,12 @@ /* sensor app */ #define APPLICATION_SENSOR +#define APPLICATION_SENSOR_CH4 +#define APPLICATION_SENSOR_CH4_AS830 /* lib */ #define APP_SELECT_NEWLIB -#define LIB_USING_CJSON #define __STACKSIZE__ 4096 #endif From f74d1dafd782e803ef38ba11fc2ce34acee14a88 Mon Sep 17 00:00:00 2001 From: wlyu Date: Mon, 21 Mar 2022 16:47:32 +0800 Subject: [PATCH 6/9] optimize the codes with lwip and opcua --- .../socket_demo/lwip_tcp_socket_demo.c | 73 +- .../socket_demo/lwip_udp_socket_demo.c | 140 +- .../control_app/opcua_demo/README.md | 16 + .../control_app/plc_demo/README.md | 48 + .../control_app/plc_demo/plc_control_demo.c | 4 - .../plc/interoperability/socket/README.md | 18 + .../plc/interoperability/socket/br_socket.c | 1867 ----------------- .../plc/interoperability/socket/plc_socket.c | 188 +- .../plc/interoperability/socket/plc_socket.h | 21 +- .../Framework/control/shared/control_file.c | 62 +- .../Framework/control/shared/control_file.h | 15 +- Ubiquitous/XiZi/board/ok1052-c/board.c | 8 + .../third_party_driver/i2c/connect_i2c.c | 2 +- .../i2c/connect_i2c_eeprom.c | 1 - .../third_party_driver/include/connect_i2c.h | 2 +- .../resources/ethernet/LwIP/api/api_lib.c | 1 - .../resources/ethernet/LwIP/api/sockets.c | 20 - .../resources/ethernet/LwIP/arch/lwipopts.h | 5 +- .../resources/ethernet/LwIP/core/tcp_out.c | 6 +- .../ethernet/cmd_lwip/lwip_config_demo.c | 34 +- .../resources/ethernet/cmd_lwip/lwip_demo.h | 30 + .../ethernet/cmd_lwip/lwip_tcp_demo.c | 65 +- .../ethernet/cmd_lwip/lwip_udp_demo.c | 49 +- Ubiquitous/XiZi/resources/include/flash_spi.h | 2 - .../XiZi/tool/shell/letter-shell/shell_port.c | 32 +- 25 files changed, 452 insertions(+), 2257 deletions(-) create mode 100755 APP_Framework/Applications/control_app/opcua_demo/README.md create mode 100755 APP_Framework/Applications/control_app/plc_demo/README.md create mode 100755 APP_Framework/Framework/control/plc/interoperability/socket/README.md delete mode 100755 APP_Framework/Framework/control/plc/interoperability/socket/br_socket.c diff --git a/APP_Framework/Applications/connection_app/socket_demo/lwip_tcp_socket_demo.c b/APP_Framework/Applications/connection_app/socket_demo/lwip_tcp_socket_demo.c index a09866fa9..18fcc9e8c 100755 --- a/APP_Framework/Applications/connection_app/socket_demo/lwip_tcp_socket_demo.c +++ b/APP_Framework/Applications/connection_app/socket_demo/lwip_tcp_socket_demo.c @@ -1,5 +1,5 @@ /* -* Copyright (c) 2020 AIIT XUOS Lab +* Copyright (c) 2022 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: @@ -11,16 +11,14 @@ */ /** -* @file tcp_echo_socket_demo.c -* @brief One UDP demo based on LwIP +* @file lwip_tcp_socket_demo.c +* @brief TCP socket demo based on LwIP * @version 1.0 * @author AIIT XUOS Lab -* @date 2021-05-29 +* @date 2022-03-21 */ #include -#include -#include "board.h" #include "sys_arch.h" #include #include "lwip/sys.h" @@ -28,6 +26,7 @@ #define TCP_DEMO_BUF_SIZE 65535 char tcp_socket_ip[] = {192, 168, 250, 252}; +u16_t tcp_socket_port = LWIP_TARGET_PORT; /******************************************************************************/ @@ -44,36 +43,41 @@ static void TCPSocketRecvTask(void *arg) recv_buf = (char *)malloc(TCP_DEMO_BUF_SIZE); if (recv_buf == NULL) { - lw_print("No memory\n"); - goto __exit; + lw_error("No memory\n"); + continue; } fd = socket(AF_INET, SOCK_STREAM, 0); if (fd < 0) { - lw_print("Socket error\n"); - goto __exit; + lw_error("Socket error\n"); + free(recv_buf); + continue; } tcp_addr.sin_family = AF_INET; tcp_addr.sin_addr.s_addr = INADDR_ANY; - tcp_addr.sin_port = htons(LWIP_LOCAL_PORT); + tcp_addr.sin_port = htons(tcp_socket_port); memset(&(tcp_addr.sin_zero), 0, sizeof(tcp_addr.sin_zero)); if (bind(fd, (struct sockaddr *)&tcp_addr, sizeof(struct sockaddr)) == -1) { - lw_print("Unable to bind\n"); - goto __exit; + lw_error("Unable to bind\n"); + closesocket(fd); + free(recv_buf); + continue; } lw_print("tcp bind success, start to receive.\n"); - lw_notice("\n\nLocal Port:%d\n\n", LWIP_LOCAL_PORT); + lw_notice("\n\nLocal Port:%d\n\n", tcp_socket_port); // setup socket fd as listening mode if (listen(fd, 5) != 0 ) { - lw_print("Unable to listen\n"); - goto __exit; + lw_error("Unable to listen\n"); + closesocket(fd); + free(recv_buf); + continue; } // accept client connection @@ -91,26 +95,23 @@ static void TCPSocketRecvTask(void *arg) } sendto(clientfd, recv_buf, recv_len, 0, (struct sockaddr*)&tcp_addr, addr_len); } - - __exit: - if (fd >= 0) - closesocket(fd); - - if (recv_buf) - free(recv_buf); } + + closesocket(fd); + free(recv_buf); } void TCPSocketRecvTest(int argc, char *argv[]) { int result = 0; - pthread_t th_id; - pthread_attr_t attr; - if(argc == 2) + if(argc >= 2) { - lw_print("lw: [%s] gw %s\n", __func__, argv[1]); - sscanf(argv[1], "%d.%d.%d.%d", &tcp_socket_ip[0], &tcp_socket_ip[1], &tcp_socket_ip[2], &tcp_socket_ip[3]); + lw_print("lw: [%s] target ip %s\n", __func__, argv[1]); + if(sscanf(argv[1], "%d.%d.%d.%d:%d", &tcp_socket_ip[0], &tcp_socket_ip[1], &tcp_socket_ip[2], &tcp_socket_ip[3], &tcp_socket_port) == EOK) + { + sscanf(argv[1], "%d.%d.%d.%d", &tcp_socket_ip[0], &tcp_socket_ip[1], &tcp_socket_ip[2], &tcp_socket_ip[3]); + } } lwip_config_tcp(lwip_ipaddr, lwip_netmask, tcp_socket_ip); @@ -138,7 +139,7 @@ static void TCPSocketSendTask(void *arg) struct sockaddr_in tcp_sock; tcp_sock.sin_family = AF_INET; - tcp_sock.sin_port = htons(LWIP_TARGET_PORT); + tcp_sock.sin_port = htons(tcp_socket_port); tcp_sock.sin_addr.s_addr = PP_HTONL(LWIP_MAKEU32(tcp_socket_ip[0], tcp_socket_ip[1], tcp_socket_ip[2], tcp_socket_ip[3])); memset(&(tcp_sock.sin_zero), 0, sizeof(tcp_sock.sin_zero)); @@ -149,8 +150,7 @@ static void TCPSocketSendTask(void *arg) return; } - lw_print("tcp connect success, start to send.\n"); - lw_notice("\n\nTarget Port:%d\n\n", LWIP_TARGET_PORT); + lw_notice("\n\nTarget Port:%d\n\n", tcp_socket_port); while (cnt --) { @@ -168,14 +168,17 @@ static void TCPSocketSendTask(void *arg) void TCPSocketSendTest(int argc, char *argv[]) { - if(argc == 2) + if(argc >= 2) { - lw_print("lw: [%s] gw %s\n", __func__, argv[1]); - sscanf(argv[1], "%d.%d.%d.%d", &tcp_socket_ip[0], &tcp_socket_ip[1], &tcp_socket_ip[2], &tcp_socket_ip[3]); + lw_print("lw: [%s] target ip %s\n", __func__, argv[1]); + if(sscanf(argv[1], "%d.%d.%d.%d:%d", &tcp_socket_ip[0], &tcp_socket_ip[1], &tcp_socket_ip[2], &tcp_socket_ip[3], &tcp_socket_port) == EOK) + { + sscanf(argv[1], "%d.%d.%d.%d", &tcp_socket_ip[0], &tcp_socket_ip[1], &tcp_socket_ip[2], &tcp_socket_ip[3]); + } } lwip_config_tcp(lwip_ipaddr, lwip_netmask, tcp_socket_ip); - sys_thread_new("tcp socket", TCPSocketSendTask, NULL, LWIP_TASK_STACK_SIZE, LWIP_DEMO_TASK_PRIO); + sys_thread_new("TCP Socket Send", TCPSocketSendTask, NULL, LWIP_TASK_STACK_SIZE, LWIP_DEMO_TASK_PRIO); } SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0) | SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN) | SHELL_CMD_PARAM_NUM(0), diff --git a/APP_Framework/Applications/connection_app/socket_demo/lwip_udp_socket_demo.c b/APP_Framework/Applications/connection_app/socket_demo/lwip_udp_socket_demo.c index 24a1a374f..fcc5be0cd 100755 --- a/APP_Framework/Applications/connection_app/socket_demo/lwip_udp_socket_demo.c +++ b/APP_Framework/Applications/connection_app/socket_demo/lwip_udp_socket_demo.c @@ -1,5 +1,5 @@ /* -* Copyright (c) 2020 AIIT XUOS Lab +* Copyright (c) 2022 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: @@ -12,33 +12,25 @@ /** * @file lwip_udp_socket_demo.c -* @brief One UDP demo based on LwIP +* @brief UDP demo based on LwIP * @version 1.0 * @author AIIT XUOS Lab -* @date 2021-05-29 +* @date 2022-03-21 */ #include -#include -#include "board.h" #include "sys_arch.h" -#include "lwip/udp.h" -#include "lwip/opt.h" -#include -#include "lwip/sys.h" +#include "lwip/sockets.h" #define UDP_BUF_SIZE 65536 -extern char udp_target[]; -static struct udp_pcb *udpecho_raw_pcb; char udp_socket_ip[] = {192, 168, 250, 252}; +u16_t udp_socket_port = LWIP_LOCAL_PORT; -/******************************************************************************/ +/*****************************************************************************/ static void UdpSocketRecvTask(void *arg) { - lw_print("UdpSocketRecvTask start.\n"); - - int socket_fd = -1; + int fd = -1; char *recv_buf; struct sockaddr_in udp_addr, server_addr; int recv_len; @@ -49,134 +41,124 @@ static void UdpSocketRecvTask(void *arg) recv_buf = (char *)malloc(UDP_BUF_SIZE); if(recv_buf == NULL) { - lw_print("No memory\n"); - goto __exit; + lw_error("No memory\n"); + continue; } - socket_fd = socket(AF_INET, SOCK_DGRAM, 0); - if(socket_fd < 0) + fd = socket(AF_INET, SOCK_DGRAM, 0); + if(fd < 0) { - lw_print("Socket error\n"); - goto __exit; + lw_error("Socket error\n"); + free(recv_buf); + continue; } udp_addr.sin_family = AF_INET; udp_addr.sin_addr.s_addr = INADDR_ANY; - udp_addr.sin_port = htons(LWIP_LOCAL_PORT); + udp_addr.sin_port = htons(udp_socket_port); memset(&(udp_addr.sin_zero), 0, sizeof(udp_addr.sin_zero)); - if(bind(socket_fd, (struct sockaddr *)&udp_addr, sizeof(struct sockaddr)) == -1) + if(bind(fd, (struct sockaddr *)&udp_addr, sizeof(struct sockaddr)) == -1) { - lw_print("Unable to bind\n"); - goto __exit; + lw_error("Unable to bind\n"); + closesocket(fd); + free(recv_buf); + continue; } - lw_print("UDP bind sucess, start to receive.\n"); - lw_print("\n\nLocal Port:%d\n\n", LWIP_LOCAL_PORT); + lw_notice("UDP bind sucess, start to receive.\n"); + lw_notice("\n\nLocal Port:%d\n\n", udp_socket_port); while(1) { memset(recv_buf, 0, UDP_BUF_SIZE); - recv_len = recvfrom(socket_fd, recv_buf, UDP_BUF_SIZE, 0, (struct sockaddr *)&server_addr, &addr_len); - lw_notice("Receive from : %s\n", inet_ntoa(server_addr.sin_addr)); - lw_notice("Receive data : %s\n\n", recv_buf); - sendto(socket_fd, recv_buf, recv_len, 0, (struct sockaddr*)&server_addr, addr_len); + recv_len = recvfrom(fd, recv_buf, UDP_BUF_SIZE, 0, (struct sockaddr *)&server_addr, &addr_len); + if(recv_len > 0) + { + lw_notice("Receive from : %s\n", inet_ntoa(server_addr.sin_addr)); + lw_notice("Receive data : %s\n\n", recv_buf); + } + sendto(fd, recv_buf, recv_len, 0, (struct sockaddr*)&server_addr, addr_len); } - __exit: - if(socket_fd >= 0) - { - closesocket(socket_fd); - } - - if(recv_buf) - { - free(recv_buf); - } + closesocket(fd); + free(recv_buf); } } -void UdpSocketRecvTask(int argc, char *argv[]) +void UdpSocketRecvTest(int argc, char *argv[]) { - int result = 0; - pthread_t th_id; - pthread_attr_t attr; - - if(argc == 2) + if(argc >= 2) { - lw_print("lw: [%s] gw %s\n", __func__, argv[1]); - sscanf(argv[1], "%d.%d.%d.%d", &udp_socket_ip[0], &udp_socket_ip[1], &udp_socket_ip[2], &udp_socket_ip[3]); + lw_notice("lw: [%s] target ip %s\n", __func__, argv[1]); + if(sscanf(argv[1], "%d.%d.%d.%d:%d", &udp_socket_ip[0], &udp_socket_ip[1], &udp_socket_ip[2], &udp_socket_ip[3], &udp_socket_port) == EOK) + { + sscanf(argv[1], "%d.%d.%d.%d", &udp_socket_ip[0], &udp_socket_ip[1], &udp_socket_ip[2], &udp_socket_ip[3]); + } } - lwip_config_tcp(lwip_ipaddr, lwip_netmask, lwip_gwaddr); + lwip_config_tcp(lwip_ipaddr, lwip_netmask, udp_socket_ip); sys_thread_new("UdpSocketRecvTask", UdpSocketRecvTask, NULL, LWIP_TASK_STACK_SIZE, LWIP_DEMO_TASK_PRIO); } SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0) | SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN) | SHELL_CMD_PARAM_NUM(3), - UDPSocketRecv, UdpSocketRecvTask, UDP recv echo); + UDPSocketRecv, UdpSocketRecvTest, UDP Receive DEMO); static void UdpSocketSendTask(void *arg) { int cnt = LWIP_DEMO_TIMES; char send_str[128]; + int fd = -1; - lw_print("UdpSocketSendTask start.\n"); - - int socket_fd = -1; memset(send_str, 0, sizeof(send_str)); - socket_fd = socket(AF_INET, SOCK_DGRAM, 0); - if(socket_fd < 0) + fd = socket(AF_INET, SOCK_DGRAM, 0); + if(fd < 0) { - lw_print("Socket error\n"); - goto __exit; + lw_error("Socket error\n"); + return; } struct sockaddr_in udp_sock; udp_sock.sin_family = AF_INET; - udp_sock.sin_port = htons(LWIP_TARGET_PORT); - udp_sock.sin_addr.s_addr = PP_HTONL(LWIP_MAKEU32(udp_target[0], udp_target[1], udp_target[2], udp_target[3])); + udp_sock.sin_port = htons(udp_socket_port); + udp_sock.sin_addr.s_addr = PP_HTONL(LWIP_MAKEU32(udp_socket_ip[0], udp_socket_ip[1], udp_socket_ip[2], udp_socket_ip[3])); memset(&(udp_sock.sin_zero), 0, sizeof(udp_sock.sin_zero)); - if(connect(socket_fd, (struct sockaddr *)&udp_sock, sizeof(struct sockaddr))) + if(connect(fd, (struct sockaddr *)&udp_sock, sizeof(struct sockaddr))) { - lw_print("Unable to connect\n"); - goto __exit; + lw_error("Unable to connect\n"); + closesocket(fd); + return; } lw_print("UDP connect success, start to send.\n"); - lw_print("\n\nTarget Port:%d\n\n", udp_sock.sin_port); + lw_notice("\n\nTarget Port:%d\n\n", udp_sock.sin_port); while (cnt --) { snprintf(send_str, sizeof(send_str), "UDP test package times %d\r\n", cnt); - sendto(socket_fd, send_str, strlen(send_str), 0, (struct sockaddr*)&udp_sock, sizeof(struct sockaddr)); + sendto(fd, send_str, strlen(send_str), 0, (struct sockaddr*)&udp_sock, sizeof(struct sockaddr)); lw_notice("Send UDP msg: %s ", send_str); MdelayKTask(1000); } -__exit: - if(socket_fd >= 0) - { - closesocket(socket_fd); - } - + closesocket(fd); return; } void UdpSocketSendTest(int argc, char *argv[]) { - int result = 0; - pthread_t th_id; - pthread_attr_t attr; - - if(argc == 2) + if(argc >= 2) { - lw_print("lw: [%s] gw %s\n", __func__, argv[1]); - sscanf(argv[1], "%d.%d.%d.%d", &udp_socket_ip[0], &udp_socket_ip[1], &udp_socket_ip[2], &udp_socket_ip[3]); + lw_notice("lw: [%s] target ip %s\n", __func__, argv[1]); + if(sscanf(argv[1], "%d.%d.%d.%d:%d", &udp_socket_ip[0], &udp_socket_ip[1], &udp_socket_ip[2], &udp_socket_ip[3], &udp_socket_port) == EOK) + { + sscanf(argv[1], "%d.%d.%d.%d", &udp_socket_ip[0], &udp_socket_ip[1], &udp_socket_ip[2], &udp_socket_ip[3]); + } } - lwip_config_tcp(lwip_ipaddr, lwip_netmask, lwip_gwaddr); + lwip_config_tcp(lwip_ipaddr, lwip_netmask, udp_socket_ip); sys_thread_new("UdpSocketSendTask", UdpSocketSendTask, NULL, LWIP_TASK_STACK_SIZE, LWIP_DEMO_TASK_PRIO); } diff --git a/APP_Framework/Applications/control_app/opcua_demo/README.md b/APP_Framework/Applications/control_app/opcua_demo/README.md new file mode 100755 index 000000000..9b809af14 --- /dev/null +++ b/APP_Framework/Applications/control_app/opcua_demo/README.md @@ -0,0 +1,16 @@ +# OPCUA DEMO README + +## 文件说明 + +用于OPCUA 相关测试命令演示,需要开启LWIP和OPCUA协议. + +### 命令行 + +UaConnect [IP] + +用于测试与OPCUA服务器连接,连接成功应显示OK + +UaObject [IP] + +用于显示对应的OPCUA设备的节点信息 + diff --git a/APP_Framework/Applications/control_app/plc_demo/README.md b/APP_Framework/Applications/control_app/plc_demo/README.md new file mode 100755 index 000000000..1d361b998 --- /dev/null +++ b/APP_Framework/Applications/control_app/plc_demo/README.md @@ -0,0 +1,48 @@ +# PLC DEMO README + +## 文件说明 + +用于PLC设备相关测试命令演示,目前支持OPCUA协议对PLC进行远程控制,该命令基于LWIP和OPCUA,需要开启相关开关。 + +多个PLC设备可以组成一个channel,用于一条相关业务线控制。 + +### 命令行 + +ShowChannel + +显示注册到channel上的PLC设备,范例如下: + + ch_type ch_name drv_name dev_name cnt +----------------------------------------------------------------- + PLC_Channel PLC OPCUA PLC Demo 4 1 + PLC Demo 3 2 + PLC Demo 2 3 + PLC Demo 1 4 + PLC Demo 0 5 + +ShowPLC + +用于显示PLC,范例如下: + + device vendor model product id +----------------------------------------------------------------- + PLC Demo 4 B&R X20 X20 CP1381 5 + PLC Demo 3 B&R X20 X20 CP1586 4 + PLC Demo 2 SIEMSNS S7-200 CPU SR60 3 + PLC Demo 1 SIEMENS S7-1200 CPU 1215C 2 + PLC Demo 0 SIEMENS S7-1500 CPU 1512SP-1PN 1 + + PlcRead [NodeID] + + 用于读取PLC节点信息 + + - [NodeID]: 如n4,1, 其中4代表namespace,1代表节点号 + + + PlcWrite + + 用于写入PLC节点数值 + + - [NodeID]: 如n4,1, 其中4代表namespace,1代表节点号 + + - [value]: 为写入数值,目前支持bool类型,和int类型。bool型应为0b(代表false), 1b(代表true) diff --git a/APP_Framework/Applications/control_app/plc_demo/plc_control_demo.c b/APP_Framework/Applications/control_app/plc_demo/plc_control_demo.c index d8f480d59..64cb7d913 100755 --- a/APP_Framework/Applications/control_app/plc_demo/plc_control_demo.c +++ b/APP_Framework/Applications/control_app/plc_demo/plc_control_demo.c @@ -101,8 +101,6 @@ void PlcReadUATask(void* arg) if(EOK != ret) { plc_print("plc: [%s] open failed %#x\n", __func__, ret); -// free(plc_demo_dev.priv_data); -// plc_demo_dev.priv_data = NULL; return; } @@ -163,8 +161,6 @@ void PlcWriteUATask(void* arg) if(EOK != ret) { plc_print("plc: [%s] open failed %#x\n", __func__, ret); -// free(plc_demo_dev.priv_data); -// plc_demo_dev.priv_data = NULL; return; } diff --git a/APP_Framework/Framework/control/plc/interoperability/socket/README.md b/APP_Framework/Framework/control/plc/interoperability/socket/README.md new file mode 100755 index 000000000..798e81e3f --- /dev/null +++ b/APP_Framework/Framework/control/plc/interoperability/socket/README.md @@ -0,0 +1,18 @@ +# PLC SOCKET README + +## 文件说明 + +用于测试PLC socket通信. 通过建立与制定IP的PLC设备的socket连接, 发送命令给PLC设备, 实现相关功能. 实现该功能需要开启LWIP, 同时需要扩大shell的栈大小和内存空间。 + +### 命令行 + +PLCSocket ip=[PLC IP] port=[PLC port] tcp=[1: TCP; 0: UDP] cmd=[相关命令] file=[制定配置文件] + +配置文件支持json格式, 默认文件名为socket_param.json, 放置于plc目录下, 文件内容如下: + +{ + "ip": "192.168.250.6", + "port": 102, + "tcp": 1, + "cmd": [x, x, x] +} \ No newline at end of file diff --git a/APP_Framework/Framework/control/plc/interoperability/socket/br_socket.c b/APP_Framework/Framework/control/plc/interoperability/socket/br_socket.c deleted file mode 100755 index a655a0a67..000000000 --- a/APP_Framework/Framework/control/plc/interoperability/socket/br_socket.c +++ /dev/null @@ -1,1867 +0,0 @@ -#include -#include -#include "plc_socket.h" - -#define xs_kprintf KPrintf -#define xs_device_t uint32_t - - -static unsigned char data_head = 0x5A; -static char device_s14[] = "S14"; -static char device_s15[] = "S15"; -static char device_s16[] = "S16"; -static char device_s17[] = "S17"; -static char device_s18[] = "S18"; -static char data_end[] = "#"; -unsigned char redis_data[1024]; - -// 创建一个信号量,用于接收消息的同步 -static sem_t Ch_Sem = NULL; -static sem_t Rx_Sem = NULL; - -extern xs_device_t ec200t; - -//for SIEMENS TCP read data cmd -const unsigned char handshake_first[] = -{ - 0x3, 0x00, 0x00, 0x16, 0x11, 0xe0, 0x00, 0x00, 0x02, - 0xc8,0x00,0xc1,0x02,0x02,0x01,0xc2,0x02,0x02,0x01,0xc0,0x01,0x0a -}; - -const unsigned char handshake_second[] = -{ - 0x3,0x00,0x00,0x19,0x02,0xf0,0x80,0x32,0x01,0x00,0x00,0x00,0x0d,0x00,0x08,0x00,0x00,0xf0,0x00,0x00,0x01,0x00,0x01,0x00,0xf0 -}; - -const unsigned char siemens_read_data[] = -{ - 0x3,0x00,0x00, 0x1f,0x02,0xf0,0x80,0x32,0x01,0x00,0x00,0x33,0x01,0x00,0x0e,0x00,0x00,0x04,0x01,0x12,0x0a,0x10,0x02,0x00,0xD2,0x00,0x34,0x84,0x00,0x00,0x00 -}; - -//for OML UDP read data cmd -const unsigned char UDP_read_data[] = -{ - 0x80,0x00,0x02,0x00,0x03,0x00,0x00,0x7E,0x00,0x00,0x01,0x01,0x82,0x0F,0xA0,0x00,0x00,0x20 -}; - -//for SIEMENS 1200 read data cmd -const unsigned char handshake_1200_first[] = -{ - 0x03, 0x00, 0x00, 0x16, 0x11, 0xE0, 0x00, 0x00, 0x02, 0xC8, 0x00, 0xC1, 0x02, 0x02, 0x01, 0xC2, 0x02, 0x02, 0x01, 0xC0, 0x01, 0x0A -}; - -const unsigned char handshake_1200_second[] = -{ - 0x03, 0x00, 0x00, 0x19, 0x02, 0xF0, 0x80, 0x32, 0x01, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x08, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0xF0 -}; - -const unsigned char siemens_1200_read_data[] = -{ - 0x03, 0x00, 0x00, 0x1F, 0x02, 0xF0, 0x80, 0x32, 0x01, 0x00, 0x00, 0x33, 0x01, 0x00, 0x0E, 0x00, 0x00, 0x04, 0x01, 0x12, 0x0A, 0x10, 0x02, 0x00, 0xD2, 0x00, 0x34, 0x84, 0x00, 0x00, 0x00 -}; - -#define PUT_ULONG_BE(n,b,i) \ -{ \ - (b)[(i) ] = (uint8_t) ( (n) >> 24 ); \ - (b)[(i) + 1] = (uint8_t) ( (n) >> 16 ); \ - (b)[(i) + 2] = (uint8_t) ( (n) >> 8 ); \ - (b)[(i) + 3] = (uint8_t) ( (n) ); \ -} - -#define GET_ULONG_BE(n,b,i) \ -{ \ - (n) = ( (uint32_t) (b)[(i) ] << 24 ) \ - | ( (uint32_t) (b)[(i) + 1] << 16 ) \ - | ( (uint32_t) (b)[(i) + 2] << 8 ) \ - | ( (uint32_t) (b)[(i) + 3] ); \ -} - -#define XS_SERIAL_RB_BUFSZ 128 - -char rx_buffer_cmp[256 + 1]; -char msg_pool[256]; -char rx_buffer[XS_SERIAL_RB_BUFSZ + 1] = {0}; -#define WIFI_RESET 73 - -#define LEN_PARA_BUF 128 -uint8_t server_addr_wifi[LEN_PARA_BUF] = "192.168.23.181"; //??????? -uint8_t server_port_wifi[LEN_PARA_BUF] = "9999"; //?????? - -uint8_t WIFI_ssid[LEN_PARA_BUF] = "xiaoshanos"; //WIFI? -uint8_t WIFI_pwd[LEN_PARA_BUF] = "12345678"; //WIFI???? - -#if 0 -/* ?????? */ -xs_device_t hfa21; -/* ??????? */ -struct xs_MessageQueue* rx3_mq; -/* ???????? */ - -void hfa21_sta_config(void) -{ - xs_kprintf("hfa21_sta_config start\r\n"); - uint8_t i,step; - uint8_t cmd[LEN_PARA_BUF]; - step = 1; - strcpy(cmd, "+++"); - //send - xs_DeviceWrite(hfa21, 0, cmd, 3); - xs_kprintf("cmd.%d=%s\r\n",step,cmd); - step = 2; - memset(cmd,0,sizeof(cmd)); - xs_MdelayKTask(100); - strcpy(cmd,"a"); - //send - xs_DeviceWrite(hfa21, 0, cmd, 1); - xs_kprintf("cmd.%d=%s\r\n",step,cmd); - step = 3; - memset(cmd,0,sizeof(cmd)); - xs_MdelayKTask(2500); - strcpy(cmd, "AT+FCLR\r\n"); - - //send - for(i=0; i 1000) - { - xs_kprintf("data length too long\n "); - return; - } - - xs_DeviceWrite(hfa21, 0, buf, len); - return ; -} - -void msg_send_once(void) -{ - uint8_t msg[1024] = { 0}; - uint32_t i = 0; - xs_kprintf("ap test, send msg to server : "); - memset(msg,0x37,1024); - xs_memcpy(msg,"arm:dev7,data:",14); - msg[1023] = '\n'; - xs_DeviceWrite(hfa21,0, msg, 1024); - // xs_MdelayKTask(1000); - return ; -} -MSH_CMD_EXPORT(msg_send_once, asd); - -void msg_send_nointerval(void) -{ - uint8_t msg[1024] = { 0}; - uint32_t i = 1; - uint32_t index = 0; - uint8_t seq[10] = { 0}; - uint8_t* prefix = "{board:stm32f407-st-discovery,arch:arm,device:7,seq:"; - xs_kprintf("ap test, send msg to server : "); - - for(;;) - { - index = 0; - memset(msg,0x37,1024); - memset(seq,0,10); - msg[1021] = '}'; - msg[1022] = '\n'; - msg[1023] = 0; - xs_memcpy(msg,prefix,strlen(prefix)); - index = index + strlen(prefix); - PUT_ULONG_BE(i, msg, index) - index = index + 4; - xs_memcpy(msg+index,",data:",6); - xs_DeviceWrite(hfa21,0, msg, 1024); - ++ i; - } - - return ; -} -MSH_CMD_EXPORT(msg_send_nointerval, asd); - -void msg_send_interval(void) -{ - uint8_t msg[1024] = { 0}; - uint32_t i = 1; - uint32_t index = 0; - uint8_t seq[4] = { 0}; - uint8_t* prefix = "{board:stm32f407-st-discovery,arch:arm,device:7,seq:"; - xs_kprintf("ap test, send msg to server : "); - - for(;;) - { - index = 0; - memset(msg,0x37,1024); - memset(seq,0,10); - msg[1021] = '}'; - msg[1022] = '\n'; - msg[1023] = 0; - xs_memcpy(msg,prefix,strlen(prefix)); - index = index + strlen(prefix); - PUT_ULONG_BE(i, msg, index) - index = index + 4; - xs_memcpy(msg+index,",data:",6); - xs_DeviceWrite(hfa21,0, msg, 1024); - xs_MdelayKTask(1000); - ++ i; - } - - return ; -} -MSH_CMD_EXPORT(msg_send_interval, asd); - -struct xs_Ringbuffer* ringbuffer; - -uint8_t stack[256] = {0}; -xs_uint16 data_index = 0 ; -uint8_t start = 0xaa; -uint8_t end = 0xbb; - -uint8_t lora_buffer[8][256] = {0}; - -static void read_ringbuffer_thread_entry(void* parameter) -{ - uint8_t ch = 0; - xs_uint16 index = 0 ; - uint8_t devicenumber; - - while(1) - { - if(1 == xs_GetRingBufferchar(ringbuffer,&ch)) - { - if(data_index < 256) - { - stack[data_index++] = ch; - - if(data_index > 2) - { - if(stack[data_index-1] == start && stack[data_index-2] == start) - { - data_index = 0; - stack[data_index++] == start; - stack[data_index++] == start; - } - else if(stack[data_index-1] == end && stack[data_index-2] == end) - { - //end - devicenumber = stack[3] - 0x30; - - if(devicenumber > 0 && devicenumber < 8) - { - memset(lora_buffer[devicenumber-1],0,256); - memcpy(lora_buffer[devicenumber-1], &stack[2],data_index - 4); - // xs_kprintf("lora data: %s\n",lora_buffer[devicenumber-1]); - } - - data_index = 0; - } - } - } - else - { - data_index = 0; - } - } - else - { - xs_MdelayKTask(20); - } - } -} -#endif - -#if 0 -/***********************************************************************/ -//欧姆龙PLC IP 192.168.250.3 port 9600 - -static xs_err_t oml_uart_input(xs_device_t dev, xs_size_t size) -{ - xs_KSemaphoreAbandon(Ch_Sem); - return XS_EOK; -} - -static void oml_plc_thread(void* parameter) -{ - xs_err_t ret; - uint32_t rx_length, total_rx_length = 0; - uint8_t i; - unsigned int length = 0; - - while(1) - { - ret = xs_KSemaphoreObtain(Ch_Sem, XS_WAITING_FOREVER); - - if(XS_EOK == ret) - { - rx_length = xs_DeviceRead(hfa21, 0, redis_data + 1 + sizeof(device_s14) + total_rx_length, 78); - xs_kprintf("dst data length %d total length %d\n", rx_length, total_rx_length); - - for(i = 0; i < rx_length; ++i) - { - xs_kprintf("0x%x ", redis_data[i + 1 + sizeof(device_s14) + total_rx_length]); - } - - xs_kprintf("\n"); - total_rx_length += rx_length; - - if((78 == total_rx_length) && (0xC0 == redis_data[1 + sizeof(device_s14)]) - && (0x00 == redis_data[1 + sizeof(device_s14) + 1]) - && (0x02 == redis_data[1 + sizeof(device_s14) + 2]) - && (0x00 == redis_data[1 + sizeof(device_s14) + 3])) - { - /******format redis data******/ - memcpy(redis_data, &data_head, 1); - memcpy(redis_data + 1, device_s14, sizeof(device_s14)); - memcpy(redis_data + 1 + sizeof(device_s14) + total_rx_length, data_end, sizeof(data_end)); - length = 1 + sizeof(device_s14) + total_rx_length + sizeof(data_end); - /******end******/ - xs_DeviceWrite(ec200t, 0, redis_data, length); - total_rx_length = 0; - memset(redis_data, 0, sizeof(redis_data)); - xs_KSemaphoreAbandon(Rx_Sem); - } - } - } -} - -int OML_UDP(void) -{ - struct SerialConfigure config3 = XS_SERIAL_CONFIG_DEFAULT; - hfa21 = xs_DeviceFind("uart3"); - - if(!hfa21) - { - xs_kprintf("find dev.hfa21 failed!\r\n"); - return XS_ERROR; - } - - Ch_Sem = xs_KCreateSemaphore("Ch_Sem",0,XS_LINKLIST_FLAG_FIFO); - - if(Ch_Sem == NULL) - { - xs_kprintf("create Ch_sem failed .\n"); - return XS_ERROR; - } - - Rx_Sem = xs_KCreateSemaphore("Rx_Sem",0,XS_LINKLIST_FLAG_FIFO); - - if(Rx_Sem == NULL) - { - xs_kprintf("create Rx_sem failed .\n"); - return XS_ERROR; - } - - //hfa21 - config3.BaudRate = BAUD_RATE_460800; - config3.DataBits = DATA_BITS_8; - config3.StopBits = STOP_BITS_1; - config3.bufsz = XS_SERIAL_RB_BUFSZ; - config3.parity = PARITY_NONE; - xs_DeviceControl(hfa21, XS_DEVICE_CTRL_CONFIG, &config3); - xs_DeviceOpen(hfa21,XS_DEVICE_FLAG_DMA_RX); - xs_DeviceSetRxIndicate(hfa21, oml_uart_input); - xs_kprintf("hfa21 init success!\r\n"); - TaskDescriptorPointer thread_oml_plc = xs_KTaskCreate("oml_hfa21", oml_plc_thread, NULL, 1024, 25); - - if(thread_oml_plc != NULL) - { - xs_StartupKTask(thread_oml_plc); - } - else - { - return XS_ERROR; - } - - xs_MdelayKTask(10000); - xs_kprintf("The UDP send_receive function is running......\n"); - - while(1) - { -// CH438UARTSend(6, UDP_read_data, sizeof(UDP_read_data)); //UDP_read_data - xs_DeviceWrite(hfa21, 0, UDP_read_data, sizeof(UDP_read_data)); - xs_kprintf("hfa21 write cmd\n"); - xs_KSemaphoreObtain(Rx_Sem, XS_WAITING_FOREVER); - xs_MdelayKTask(1000); - } - - return XS_EOK; -} -MSH_CMD_EXPORT(OML_UDP, oml); - -/*********************************************************************************************/ -// IP 192.168.250.50 port 102 tcp - -//工控机的测试代码,数据帧头FF,帧尾FE,数据帧长度不固?static struct xs_MessageQueue *ipc_mq; -xs_device_t ipc_hfa21; - -static xs_err_t ipc_uart_input(xs_device_t dev, xs_size_t size) -{ - struct rx_msg msg; - xs_err_t result; - msg.dev = dev; - msg.size = size; - result = xs_KMsgQueueSend(ipc_mq, &msg, sizeof(msg)); - - if(result == -XS_EFULL) - { - xs_kprintf("ipc_mq message queue full!\r\n"); - } - - xs_KSemaphoreAbandon(Ch_Sem); - return result; -} - -static void ipc_plc_thread(void* parameter) -{ - struct rx_msg msg; - xs_err_t ret; - uint32_t rx_length, total_rx_length = 0; - uint8_t i; - unsigned int length = 0; - - while(1) - { - ret = xs_KSemaphoreObtain(Ch_Sem, XS_WAITING_FOREVER); - - if(XS_EOK == ret) - { - xs_memset(&msg, 0, sizeof(msg)); - ret = xs_KMsgQueueRecv(ipc_mq, &msg, sizeof(msg), XS_WAITING_FOREVER); - - if(XS_EOK == ret) - { - rx_length = xs_DeviceRead(ipc_hfa21, 0, redis_data + 2 + sizeof(device_s15) + total_rx_length, msg.size); - xs_kprintf("dst data length %d total length %d\n", rx_length, total_rx_length); - - for(i = 0; i < rx_length; ++i) - { - xs_kprintf("0x%x ", redis_data[i + 2 + sizeof(device_s15) + total_rx_length]); - } - - xs_kprintf("\n"); - total_rx_length += rx_length; - - if((0x46 == redis_data[2 + sizeof(device_s15)]) && (0x46 == redis_data[2 + sizeof(device_s15) + 1]) && - (0x45 == redis_data[2 + sizeof(device_s15) + total_rx_length - 1]) && (0x46 == redis_data[2 + sizeof(device_s15) + total_rx_length - 2])) - { - /******format redis data******/ - redis_data[0] = data_head; - redis_data[1] = PLC_IPC_FLAG; - memcpy(redis_data + 2, device_s15, sizeof(device_s15)); - length = 2 + sizeof(device_s15) + total_rx_length; - /******end******/ - // xs_kprintf("redis data : \n"); - // for(i = 0; i < length; ++i) - // xs_kprintf("0x%x ", redis_data[i]); - // xs_kprintf("\n"); - xs_DeviceWrite(ec200t, 0, redis_data, length); - total_rx_length = 0; - memset(redis_data, 0, sizeof(redis_data)); - xs_KSemaphoreAbandon(Rx_Sem); - } - } - } - } -} - -int BRL_IPC(void) -{ - struct SerialConfigure config3 = XS_SERIAL_CONFIG_DEFAULT; - ipc_hfa21 = xs_DeviceFind("uart3"); - - if(!ipc_hfa21) - { - xs_kprintf("find ipc_hfa21 failed!\r\n"); - return XS_ERROR; - } - - Ch_Sem = xs_KCreateSemaphore("Ch_Sem",0,XS_LINKLIST_FLAG_FIFO); - - if(Ch_Sem == NULL) - { - xs_kprintf("BRL_IPC create sem failed .\n"); - return XS_ERROR; - } - - Rx_Sem = xs_KCreateSemaphore("Rx_Sem",0,XS_LINKLIST_FLAG_FIFO); - - if(Rx_Sem == NULL) - { - xs_kprintf("BRL_IPC create Rx_sem failed .\n"); - return XS_ERROR; - } - - ipc_mq = xs_KCreateMsgQueue("ipc_mq", - sizeof(struct rx_msg), - sizeof(msg_pool), - XS_LINKLIST_FLAG_FIFO); - - if(ipc_mq == NULL) - { - xs_kprintf("create ipc_mq mutex failed.\n"); - return -1; - } - - //ipc_hfa21 - config3.BaudRate = BAUD_RATE_460800; - config3.DataBits = DATA_BITS_8; - config3.StopBits = STOP_BITS_1; - config3.bufsz = XS_SERIAL_RB_BUFSZ; - config3.parity = PARITY_NONE; - xs_DeviceControl(ipc_hfa21, XS_DEVICE_CTRL_CONFIG, &config3); - xs_DeviceOpen(ipc_hfa21,XS_DEVICE_FLAG_DMA_RX); - xs_DeviceSetRxIndicate(ipc_hfa21, ipc_uart_input); - xs_kprintf("ipc_hfa21 init success!\r\n"); - TaskDescriptorPointer thread_ipc_plc = xs_KTaskCreate("ipc_hfa21", ipc_plc_thread, NULL, 1024, 25); - - if(thread_ipc_plc != NULL) - { - xs_StartupKTask(thread_ipc_plc); - } - else - { - return XS_ERROR; - } - - xs_kprintf("start to receive data...\n"); - - while(1) - { - xs_MdelayKTask(100); - xs_KSemaphoreObtain(Rx_Sem, XS_WAITING_FOREVER); - xs_kprintf("\n"); - } - - return XS_EOK; -} -MSH_CMD_EXPORT(BRL_IPC, IPC server); - -/*********************************************************************************************/ -// IP 192.168.250.150 port 9898 tcp - -//金凤工控机的测试代码,数据帧头FF,帧尾EF -xs_device_t jf_ipc_hfa21; -static sem_t jf_input_sem = NULL; -static sem_t jf_ec200t_sem = NULL; - -static xs_err_t jf_ipc_uart_input(xs_device_t dev, xs_size_t size) -{ - xs_KSemaphoreAbandon(jf_input_sem); - return XS_EOK; -} - -static void jf_ipc_plc_thread(void* parameter) -{ - xs_err_t ret; - uint32_t rx_length, total_rx_length = 0; - uint8_t i; - unsigned int length = 0; - - while(1) - { - ret = xs_KSemaphoreObtain(jf_input_sem, XS_WAITING_FOREVER); - - if(XS_EOK == ret) - { - rx_length = xs_DeviceRead(jf_ipc_hfa21, 0, redis_data + 2 + sizeof(device_s16) + total_rx_length, 512); - xs_kprintf("dst data length %d total length %d\n", rx_length, total_rx_length); - - for(i = 0; i < rx_length; ++i) - { - xs_kprintf("0x%x ", redis_data[i + 2 + sizeof(device_s16) + total_rx_length]); - } - - xs_kprintf("\n"); - total_rx_length += rx_length; - - if(((((unsigned int)redis_data[2 + sizeof(device_s16) + 2] & 0x000000FF) << 8) | ((unsigned int)redis_data[2 + sizeof(device_s16) + 3] & 0x000000FF) == total_rx_length) && - (0xFF == redis_data[2 + sizeof(device_s16)]) && (0x20 == redis_data[2 + sizeof(device_s16) + 1])) - { - /******format redis data******/ - redis_data[0] = data_head; - redis_data[1] = PLC_JF_IPC_FLAG; - memcpy(redis_data + 2, device_s16, sizeof(device_s16)); - length = 2 + sizeof(device_s16) + total_rx_length; - /******end******/ - xs_DeviceWrite(ec200t, 0, redis_data, length); - total_rx_length = 0; - memset(redis_data, 0, sizeof(redis_data)); - xs_KSemaphoreAbandon(jf_ec200t_sem); - } - } - } -} - -int JF_IPC(void) -{ - struct SerialConfigure config3 = XS_SERIAL_CONFIG_DEFAULT; - jf_ipc_hfa21 = xs_DeviceFind("uart3"); - - if(!jf_ipc_hfa21) - { - xs_kprintf("find jf_ipc_hfa21 failed!\r\n"); - return XS_ERROR; - } - - jf_ec200t_sem = xs_KCreateSemaphore("jf_ec200t_sem",0,XS_LINKLIST_FLAG_FIFO); - - if(jf_ec200t_sem == NULL) - { - xs_kprintf("JF_IPC create jf_ec200t_sem failed .\n"); - return XS_ERROR; - } - - jf_input_sem = xs_KCreateSemaphore("jf_input_sem",0,XS_LINKLIST_FLAG_FIFO); - - if(jf_input_sem == NULL) - { - xs_kprintf("JF_IPC create jf_input_sem failed .\n"); - return XS_ERROR; - } - - //jf_ipc_hfa21 - config3.BaudRate = BAUD_RATE_460800; - config3.DataBits = DATA_BITS_8; - config3.StopBits = STOP_BITS_1; - config3.bufsz = XS_SERIAL_RB_BUFSZ; - config3.parity = PARITY_NONE; - xs_DeviceControl(jf_ipc_hfa21, XS_DEVICE_CTRL_CONFIG, &config3); - xs_DeviceOpen(jf_ipc_hfa21,XS_DEVICE_FLAG_DMA_RX); - xs_DeviceSetRxIndicate(jf_ipc_hfa21, jf_ipc_uart_input); - xs_kprintf("jf_ipc_hfa21 init success!\r\n"); - TaskDescriptorPointer thread_jf_ipc_plc = xs_KTaskCreate("jf_ipc_hfa21", jf_ipc_plc_thread, NULL, 1024, 25); - - if(thread_jf_ipc_plc != NULL) - { - xs_StartupKTask(thread_jf_ipc_plc); - } - else - { - return XS_ERROR; - } - - xs_kprintf("start to receive data...\n"); - - while(1) - { - xs_MdelayKTask(100); - xs_KSemaphoreObtain(jf_ec200t_sem, XS_WAITING_FOREVER); - xs_kprintf("JF send data to server done\n"); - } - - return XS_EOK; -} -MSH_CMD_EXPORT(JF_IPC, JF IPC client); - -/*********************************************************************************************/ -//西门子PLC IP 192.168.250.9 port 102 - -static xs_device_t siemens_hfa21; -static sem_t siemens_input_sem = NULL; -static sem_t siemens_ec200t_sem = NULL; - -static xs_err_t siemens_uart_input(xs_device_t dev, xs_size_t size) -{ - xs_KSemaphoreAbandon(siemens_input_sem); - return XS_EOK; -} - -static void siemens_plc_thread(void* parameter) -{ - xs_err_t ret; - uint32_t rx_length, total_rx_length = 0; - uint8_t i; - static char shakehand_cnt = 0; - unsigned int length = 0; - - while(1) - { - ret = xs_KSemaphoreObtain(siemens_input_sem, XS_WAITING_FOREVER); - - if(XS_EOK == ret) - { - //rx_length = xs_DeviceRead(siemens_hfa21, 0, redis_data, 234); - // xs_kprintf("siemens dst data length %d\n", rx_length); - // for(i = 0; i < rx_length; ++i) - // xs_kprintf("0x%x ", redis_data[i]); - // xs_kprintf("\n"); - if(shakehand_cnt < 2) - { - //ignore first two siemens input sem - xs_DeviceRead(siemens_hfa21, 0, redis_data + 2 + sizeof(device_s17), 87); - shakehand_cnt++; - continue; - } - - rx_length = xs_DeviceRead(siemens_hfa21, 0, redis_data + 2 + sizeof(device_s17) + total_rx_length, 87); - xs_kprintf("siemens dst data length %d total length %d\n", rx_length, total_rx_length); - - for(i = 0; i < rx_length; ++i) - { - xs_kprintf("0x%x ", redis_data[i + 2 + sizeof(device_s17) + total_rx_length]); - } - - xs_kprintf("\n"); - total_rx_length += rx_length; - - if((87 == total_rx_length) && (0x03 == redis_data[2 + sizeof(device_s17)]) - && (0x00 == redis_data[2 + sizeof(device_s17) + 1]) - && (0x00 == redis_data[2 + sizeof(device_s17) + 2]) - && (0x57 == redis_data[2 + sizeof(device_s17) + 3])) - { - /******format redis data******/ - redis_data[0] = data_head; - redis_data[1] = PLC_SIEMENS_FLAG; - memcpy(redis_data + 2, device_s17, sizeof(device_s17)); - length = 2 + sizeof(device_s17) + total_rx_length; - /******end******/ - xs_DeviceWrite(ec200t, 0, redis_data, length); - total_rx_length = 0; - memset(redis_data, 0, sizeof(redis_data)); - xs_KSemaphoreAbandon(siemens_ec200t_sem); - } - } - } -} - -int SIEMENS_TCP(void) -{ - xs_err_t result; - struct SerialConfigure config3 = XS_SERIAL_CONFIG_DEFAULT; - siemens_hfa21 = xs_DeviceFind("uart3"); - - if(!siemens_hfa21) - { - xs_kprintf("find siemens_hfa21 failed!\r\n"); - return XS_ERROR; - } - - siemens_input_sem = xs_KCreateSemaphore("siemens_input_sem", 0, XS_LINKLIST_FLAG_FIFO); - - if(siemens_input_sem == NULL) - { - xs_kprintf("siemens_hfa21 create siemens_input_sem failed.\n"); - return XS_ERROR; - } - - siemens_ec200t_sem = xs_KCreateSemaphore("siemens_ec200t_sem", 0, XS_LINKLIST_FLAG_FIFO); - - if(siemens_ec200t_sem == NULL) - { - xs_kprintf("siemens_hfa21 create siemens_ec200t_sem failed.\n"); - return XS_ERROR; - } - - //siemens_hfa21 - config3.BaudRate = BAUD_RATE_460800; - config3.DataBits = DATA_BITS_8; - config3.StopBits = STOP_BITS_1; - config3.bufsz = XS_SERIAL_RB_BUFSZ; - config3.parity = PARITY_NONE; - xs_DeviceControl(siemens_hfa21, XS_DEVICE_CTRL_CONFIG, &config3); - xs_DeviceOpen(siemens_hfa21, XS_DEVICE_FLAG_DMA_RX); - xs_DeviceSetRxIndicate(siemens_hfa21, siemens_uart_input); - TaskDescriptorPointer thread_siemens_plc = xs_KTaskCreate("siemens_hfa21", siemens_plc_thread, NULL, 1024, 25); - - if(thread_siemens_plc != NULL) - { - xs_StartupKTask(thread_siemens_plc); - } - else - { - return XS_ERROR; - } - - //step1 : send handshake_first cmd, waiting for response sem - xs_kprintf("siemens_hfa21 start send handshake_first!\r\n"); - //CH438UARTSend(6, handshake_first, sizeof(handshake_first)); - xs_DeviceWrite(siemens_hfa21, 0, handshake_first, sizeof(handshake_first)); - xs_MdelayKTask(3000); - xs_DeviceWrite(siemens_hfa21, 0, handshake_first, sizeof(handshake_first)); - xs_MdelayKTask(500); - //step2 : send handshake_second cmd, waiting for response sem - xs_kprintf("siemens_hfa21 start send handshake_second!\r\n"); - //CH438UARTSend(6, handshake_second, sizeof(handshake_second)); - xs_DeviceWrite(siemens_hfa21, 0, handshake_second, sizeof(handshake_second)); - xs_kprintf("siemens_hfa21 init success!\r\n"); - xs_MdelayKTask(500); - - while(1) - { - //CH438UARTSend(6, siemens_read_data, sizeof(siemens_read_data)); //read_data - xs_DeviceWrite(siemens_hfa21, 0, siemens_read_data, sizeof(siemens_read_data)); - xs_kprintf("siemens hfa21 write cmd\n"); - xs_KSemaphoreObtain(siemens_ec200t_sem, XS_WAITING_FOREVER); - xs_MdelayKTask(1000); - } - - return XS_EOK; -} -MSH_CMD_EXPORT(SIEMENS_TCP, Siemens TCP PLC); - -/*********************************************************************************************/ -//西门?1200 PLC IP 192.168.250.107 port 102 - -static xs_device_t siemens_1200_hfa21; -static sem_t siemens_1200_input_sem = NULL; -static sem_t siemens_1200_ec200t_sem = NULL; - -static xs_err_t siemens_1200_uart_input(xs_device_t dev, xs_size_t size) -{ - xs_KSemaphoreAbandon(siemens_1200_input_sem); - return XS_EOK; -} - -static void siemens_1200_plc_thread(void* parameter) -{ - xs_err_t ret; - uint32_t rx_length, total_rx_length = 0; - uint8_t i; - static char shakehand_cnt = 0; - unsigned int length = 0; - - while(1) - { - ret = xs_KSemaphoreObtain(siemens_1200_input_sem, XS_WAITING_FOREVER); - - if(XS_EOK == ret) - { - if(shakehand_cnt < 2) - { - //ignore first two siemens input sem - xs_DeviceRead(siemens_1200_hfa21, 0, redis_data + 2 + sizeof(device_s18), 235); - shakehand_cnt++; - continue; - } - - rx_length = xs_DeviceRead(siemens_1200_hfa21, 0, redis_data + 2 + sizeof(device_s18) + total_rx_length, 235); - xs_kprintf("siemens 1200 dst data length %d total length %d\n", rx_length, total_rx_length); - - for(i = 0; i < rx_length; ++i) - { - xs_kprintf("0x%x ", redis_data[i + 2 + sizeof(device_s18) + total_rx_length]); - } - - xs_kprintf("\n"); - total_rx_length += rx_length; - - if((235 == total_rx_length) && (0x03 == redis_data[2 + sizeof(device_s18)]) - && (0x00 == redis_data[2 + sizeof(device_s18) + 1]) - && (0x00 == redis_data[2 + sizeof(device_s18) + 2]) - && (0xEB == redis_data[2 + sizeof(device_s18) + 3])) - { - /******format redis data******/ - redis_data[0] = data_head; - redis_data[1] = PLC_SIEMENS_1200_FLAG; - memcpy(redis_data + 2, device_s18, sizeof(device_s18)); - length = 2 + sizeof(device_s18) + total_rx_length; - /******end******/ - xs_DeviceWrite(ec200t, 0, redis_data, length); - total_rx_length = 0; - memset(redis_data, 0, sizeof(redis_data)); - xs_KSemaphoreAbandon(siemens_1200_ec200t_sem); - } - } - } -} - -int SIEMENS_1200(void) -{ - xs_err_t result; - struct SerialConfigure config3 = XS_SERIAL_CONFIG_DEFAULT; - siemens_1200_hfa21 = xs_DeviceFind("uart3"); - - if(!siemens_1200_hfa21) - { - xs_kprintf("find siemens_1200_hfa21 failed!\r\n"); - return XS_ERROR; - } - - siemens_1200_input_sem = xs_KCreateSemaphore("siemens_1200_input_sem", 0, XS_LINKLIST_FLAG_FIFO); - - if(siemens_1200_input_sem == NULL) - { - xs_kprintf("siemens_1200_hfa21 create siemens_1200_input_sem failed.\n"); - return XS_ERROR; - } - - siemens_1200_ec200t_sem = xs_KCreateSemaphore("siemens_1200_ec200t_sem", 0, XS_LINKLIST_FLAG_FIFO); - - if(siemens_1200_ec200t_sem == NULL) - { - xs_kprintf("siemens_1200_hfa21 create siemens_1200_ec200t_sem failed.\n"); - return XS_ERROR; - } - - //siemens_hfa21 - config3.BaudRate = BAUD_RATE_460800; - config3.DataBits = DATA_BITS_8; - config3.StopBits = STOP_BITS_1; - config3.bufsz = XS_SERIAL_RB_BUFSZ; - config3.parity = PARITY_NONE; - xs_DeviceControl(siemens_1200_hfa21, XS_DEVICE_CTRL_CONFIG, &config3); - xs_DeviceOpen(siemens_1200_hfa21, XS_DEVICE_FLAG_DMA_RX); - xs_DeviceSetRxIndicate(siemens_1200_hfa21, siemens_1200_uart_input); - TaskDescriptorPointer thread_siemens_1200_plc = xs_KTaskCreate("siemens_1200_hfa21", siemens_1200_plc_thread, NULL, 1024, 25); - - if(thread_siemens_1200_plc != NULL) - { - xs_StartupKTask(thread_siemens_1200_plc); - } - else - { - return XS_ERROR; - } - - //step1 : send handshake_1200_first cmd, waiting for response sem - xs_kprintf("siemens_1200_hfa21 start send handshake_1200_first!\r\n"); - //CH438UARTSend(6, handshake_1200_first, sizeof(handshake_1200_first)); - xs_DeviceWrite(siemens_1200_hfa21, 0, handshake_1200_first, sizeof(handshake_1200_first)); - xs_MdelayKTask(3000); - xs_DeviceWrite(siemens_1200_hfa21, 0, handshake_1200_first, sizeof(handshake_1200_first)); - xs_MdelayKTask(500); - //step2 : send handshake_1200_second cmd, waiting for response sem - xs_kprintf("siemens_1200_hfa21 start send handshake_1200_second!\r\n"); - //CH438UARTSend(6, handshake_1200_second, sizeof(handshake_1200_second)); - xs_DeviceWrite(siemens_1200_hfa21, 0, handshake_1200_second, sizeof(handshake_1200_second)); - xs_kprintf("siemens_1200_hfa21 init success!\r\n"); - xs_MdelayKTask(500); - - while(1) - { - //CH438UARTSend(6, siemens_1200_read_data, sizeof(siemens_1200_read_data)); //read_data - xs_DeviceWrite(siemens_1200_hfa21, 0, siemens_1200_read_data, sizeof(siemens_1200_read_data)); - xs_kprintf("siemens 1200 hfa21 write cmd\n"); - xs_KSemaphoreObtain(siemens_1200_ec200t_sem, XS_WAITING_FOREVER); - xs_MdelayKTask(1000); - } - - return XS_EOK; -} -MSH_CMD_EXPORT(SIEMENS_1200, Siemens 1200 PLC); -#endif - -/*********************************************************************************************/ -//贝加莱PLC IP 192.168.250.4 port 12000 -static unsigned char brl_redis_data[2048]; -static xs_device_t brl_plc_hfa21; -static sem_t brl_plc_input_sem = NULL; -static sem_t brl_plc_ec200t_sem = NULL; - -static xs_err_t brl_plc_uart_input(xs_device_t dev, xs_size_t size) -{ - xs_KSemaphoreAbandon(brl_plc_input_sem); - return XS_EOK; -} - -static void brl_plc_thread(void* parameter) -{ - xs_err_t ret; - uint32_t rx_length, total_rx_length = 0; - uint8_t i; - unsigned int length = 0; - - while(1) - { - ret = xs_KSemaphoreObtain(brl_plc_input_sem, XS_WAITING_FOREVER); - - if(XS_EOK == ret) - { - xs_kprintf("before total %d\n", total_rx_length); - rx_length = xs_DeviceRead(brl_plc_hfa21, 0, brl_redis_data + 2 + total_rx_length, 1640); - xs_kprintf("brl dst data length %d total length %d\n", rx_length, total_rx_length); - - for(i = 0; i < rx_length / 10; i ++) - { - xs_kprintf("0x%x ", brl_redis_data[i + 2 + total_rx_length]); - } - - xs_kprintf("\n"); - total_rx_length += rx_length; - - if(total_rx_length > 1640) - { - xs_kprintf("ERROR : rx_buffer is full total_rx_length %d\n", total_rx_length); - total_rx_length = 0; - memset(brl_redis_data, 0, sizeof(brl_redis_data)); - xs_KSemaphoreAbandon(brl_plc_ec200t_sem); - continue; - } - - if((1640 == total_rx_length) && (0x53 == brl_redis_data[2])) - { - /******format redis data******/ - brl_redis_data[0] = data_head; - brl_redis_data[1] = PLC_BRL_FLAG; - length = 2 + total_rx_length; - - /******end******/ - for(i = 0; i < 10; i ++) - { - xs_kprintf("0x%x ", brl_redis_data[i + 1478]); - } - - xs_kprintf("\n"); - xs_DeviceWrite(ec200t, 0, brl_redis_data, length); - total_rx_length = 0; - memset(brl_redis_data, 0, sizeof(brl_redis_data)); - xs_KSemaphoreAbandon(brl_plc_ec200t_sem); - } - } - } -} - -int BRL_PLC(void) -{ - xs_err_t result; - const unsigned char brl_reply_data[] = {0x1, 0x1, 0x1}; - struct SerialConfigure config3 = XS_SERIAL_CONFIG_DEFAULT; - brl_plc_hfa21 = xs_DeviceFind("uart3"); - - if(!brl_plc_hfa21) - { - xs_kprintf("find brl_plc_hfa21 failed!\r\n"); - return XS_ERROR; - } - - brl_plc_input_sem = xs_KCreateSemaphore("brl_plc_input_sem", 0, XS_LINKLIST_FLAG_FIFO); - - if(brl_plc_input_sem == NULL) - { - xs_kprintf("brl_plc_hfa21 create brl_plc_input_sem failed.\n"); - return XS_ERROR; - } - - brl_plc_ec200t_sem = xs_KCreateSemaphore("brl_plc_ec200t_sem", 0, XS_LINKLIST_FLAG_FIFO); - - if(brl_plc_ec200t_sem == NULL) - { - xs_kprintf("brl_plc_hfa21 create brl_plc_ec200t_sem failed.\n"); - return XS_ERROR; - } - - //brl_hfa21 - config3.BaudRate = BAUD_RATE_460800; - config3.DataBits = DATA_BITS_8; - config3.StopBits = STOP_BITS_1; - config3.bufsz = 2048; - config3.parity = PARITY_NONE; - xs_DeviceControl(brl_plc_hfa21, XS_DEVICE_CTRL_CONFIG, &config3); - xs_DeviceOpen(brl_plc_hfa21, XS_DEVICE_FLAG_DMA_RX); - xs_DeviceSetRxIndicate(brl_plc_hfa21, brl_plc_uart_input); - TaskDescriptorPointer thread_brl_plc = xs_KTaskCreate("brl_plc_hfa21", brl_plc_thread, NULL, 1024, 25); - - if(thread_brl_plc != NULL) - { - xs_StartupKTask(thread_brl_plc); - } - else - { - return XS_ERROR; - } - - xs_kprintf("brl_plc_hfa21 init success!\r\n"); - xs_MdelayKTask(500); - - while(1) - { - //CH438UARTSend(6, brl_reply_data, sizeof(brl_reply_data)); - xs_kprintf("brl_plc hfa21 write cmd\n"); //read_data - xs_DeviceWrite(brl_plc_hfa21, 0, brl_reply_data, sizeof(brl_reply_data)); - xs_KSemaphoreObtain(brl_plc_ec200t_sem, XS_WAITING_FOREVER); - } - - return XS_EOK; -} - -MSH_CMD_EXPORT(BRL_PLC, Brl PLC); - diff --git a/APP_Framework/Framework/control/plc/interoperability/socket/plc_socket.c b/APP_Framework/Framework/control/plc/interoperability/socket/plc_socket.c index a69d5dc9d..74eba30c9 100755 --- a/APP_Framework/Framework/control/plc/interoperability/socket/plc_socket.c +++ b/APP_Framework/Framework/control/plc/interoperability/socket/plc_socket.c @@ -24,56 +24,18 @@ #include "lwip/sockets.h" #include "control_file.h" -#define PLC_SOCK_CMD_NUM 10 +// max support plc socket test commands number +#define PLC_SOCK_CMD_NUM CTL_CMD_NUM +#define PLC_SOCK_TIMEOUT 50000 -// for saving PLC command +// for saving PLC command index int plc_cmd_index = 0; +// only for test +#define SUPPORT_PLC_SIEMENS + //siemens test -PlcBinCmdType TestPlcCmd[PLC_SOCK_CMD_NUM] = -{ -#ifdef SUPPORT_PLC_SIEMENS - // handshake1 repeat 1 - { - 0, 3000, 22, - { - 0x03, 0x00, 0x00, 0x16, 0x11, 0xE0, 0x00, 0x00, - 0x02, 0xC8, 0x00, 0xC1, 0x02, 0x02, 0x01, 0xC2, - 0x02, 0x02, 0x01, 0xC0, 0x01, 0x0A - } - }, - // handshake2 - { - 1, 500, 25, - { - 0x03, 0x00, 0x00, 0x19, 0x02, 0xF0, 0x80, 0x32, - 0x01, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x08, 0x00, - 0x00, 0xF0, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, - 0xF0 - } - }, - // read command - { - 2, 1000, 31, - { - 0x03, 0x00, 0x00, 0x1F, 0x02, 0xF0, 0x80, 0x32, - 0x01, 0x00, 0x00, 0x33, 0x01, 0x00, 0x0E, 0x00, - 0x00, 0x04, 0x01, 0x12, 0x0A, 0x10, 0x02, 0x00, - 0xD2, 0x00, 0x34, 0x84, 0x00, 0x00, 0x00 - } - } - // oml plc -#else// SUPPORT_PLC_OML - { - 0, 1000, 18, - { - 0x80, 0x00, 0x02, 0x00, 0x03, 0x00, 0x00, 0x7E, - 0x00, 0x00, 0x01, 0x01, 0x82, 0x0F, 0xA0, 0x00, - 0x00, 0x20 - } - } -#endif -}; +PlcBinCmdType TestPlcCmd[PLC_SOCK_CMD_NUM] = {0}; //Test information //SIEMENS ip: 192.168.250.9 port: 102 @@ -82,14 +44,21 @@ PlcBinCmdType TestPlcCmd[PLC_SOCK_CMD_NUM] = //OML ip: 192.168.250.3 port: 9600 PlcSocketParamType plc_socket_demo_data = { +#ifdef SUPPORT_PLC_SIEMENS + .ip = {192, 168, 250, 6}, + .port = 102, + .device_type = PLC_DEV_TYPE_SIEMENS, + .socket_type = SOCK_STREAM, + .cmd_num = 3, +#else .ip = {192, 168, 250, 3}, .port = 9600, .device_type = PLC_DEV_TYPE_OML, - .socket_type = SOCK_STREAM, //SOCK_DGRAM, //udp - .step = 0, - .cmd_num = 3, - .buf_size = PLC_TEST_SIZE, - .buf = NULL, + .socket_type = SOCK_DGRAM, + .cmd_num = 1, +#endif + .recv_len = PLC_RECV_BUF_LEN, + .recv_buf = NULL, }; #define OML_HEADER_LEN 78 @@ -97,12 +66,20 @@ PlcSocketParamType plc_socket_demo_data = { /******************************************************************************/ +static void plc_print_array(char *title, int size, uint8_t *cmd) +{ + lw_notice("%s : %d - ", title, size); + for(int i = 0; i < size; i++) + { + lw_notice(" %#x", cmd[i]); + } + lw_notice("\n"); +} + static void *PlcSocketStart(void *arg) { int fd = -1; - int recv_len; - int step = 0; - char *recv_buf; + int timeout, recv_len; struct sockaddr_in sock_addr; socklen_t addr_len = sizeof(struct sockaddr_in); PlcSocketParamType *param = (PlcSocketParamType *)&plc_socket_demo_data; @@ -116,9 +93,11 @@ static void *PlcSocketStart(void *arg) param->device_type, param->socket_type); + param->recv_len = PLC_RECV_BUF_LEN; + //malloc memory - recv_buf = (char *)malloc(param->buf_size); - if (recv_buf == NULL) + param->recv_buf = (char *)malloc(param->recv_len); + if (param->recv_buf == NULL) { plc_error("No memory\n"); return NULL; @@ -128,7 +107,7 @@ static void *PlcSocketStart(void *arg) if (fd < 0) { plc_error("Socket error %d\n", param->socket_type); - free(recv_buf); + free(param->recv_buf); return NULL; } @@ -143,51 +122,42 @@ static void *PlcSocketStart(void *arg) { plc_error("Unable to connect\n"); closesocket(fd); - free(recv_buf); + free(param->recv_buf); return NULL; } lw_notice("client %s connected\n", inet_ntoa(sock_addr.sin_addr)); - while(step < param->cmd_num) + for(int i = 0; i < param->cmd_num; i ++) { - sendto(fd, TestPlcCmd[step].cmd, TestPlcCmd[step].cmd_len, 0, (struct sockaddr*)&sock_addr, addr_len); - lw_notice("Send Cmd: %d - ", TestPlcCmd[step].cmd_len); - for(int i = 0; i < TestPlcCmd[step].cmd_len; i++) - { - lw_notice(" %#x", TestPlcCmd[step].cmd[i]); - } - lw_notice("\n"); - MdelayKTask(TestPlcCmd[step].delay_ms); + PlcBinCmdType *cmd = &TestPlcCmd[i]; + sendto(fd, cmd->cmd, cmd->cmd_len, 0, (struct sockaddr*)&sock_addr, addr_len); + plc_print_array("Send cmd", cmd->cmd_len, cmd->cmd); - memset(recv_buf, 0, param->buf_size); - while(1) + MdelayKTask(cmd->delay_ms); + timeout = PLC_SOCK_TIMEOUT; + memset(param->recv_buf, 0, param->recv_len); + while(timeout --) { - recv_len = recvfrom(fd, recv_buf, param->buf_size, 0, (struct sockaddr *)&sock_addr, &addr_len); + recv_len = recvfrom(fd, param->recv_buf, param->recv_len, 0, (struct sockaddr *)&sock_addr, &addr_len); if(recv_len > 0) { if(param->device_type == PLC_DEV_TYPE_OML) { - if((recv_len == OML_HEADER_LEN) && (CHECK_OML_HEADER(recv_buf))) + if((recv_len == OML_HEADER_LEN) && (CHECK_OML_HEADER(param->recv_buf))) { lw_notice("This is Oml package!!!\n"); } } lw_notice("Receive from : %s\n", inet_ntoa(sock_addr.sin_addr)); - lw_notice("Receive data : %d -", recv_len); - for(int i = 0; i < recv_len; i++) - { - lw_notice(" %#x", recv_buf[i]); - } - lw_notice("\n"); + plc_print_array("Receive data", recv_len, param->recv_buf); break; } } - step ++; } closesocket(fd); - free(recv_buf); + free(param->recv_buf); return NULL; } @@ -221,29 +191,46 @@ void PlcShowUsage(void) plc_notice("tcp=[] 0: udp 1:tcp\n"); plc_notice("ip=[ip.ip.ip.ip]\n"); plc_notice("port=port\n"); + plc_notice("file: use %s\n", PLC_SOCK_FILE_NAME); plc_notice("------------------------------------\n"); } -void PlcGetParamFromFile(void) +#if defined(MOUNT_SDCARD) && defined(LIB_USING_CJSON) +void PlcGetParamFromFile(char *file_name) { PlcSocketParamType *param = &plc_socket_demo_data; - //for PLC socket parameter file - char file_buf[CTL_FILE_SIZE] = {0}; - FILE *fd = CtlFileInit(PLC_SOCK_FILE_NAME); - - if(fd == NULL) + char *file_buf = malloc(CTL_FILE_LEN); + if(file_buf == NULL) + { + plc_error("No enough buffer %d\n", CTL_FILE_LEN); return; + } + memset(file_buf, 0, CTL_FILE_LEN); - memset(file_buf, 0, CTL_FILE_SIZE); - - CtlFileRead(fd, CTL_FILE_SIZE, file_buf); - CtlFileClose(fd); + if(CtlFileReadWithFilename(file_name, CTL_FILE_LEN, file_buf) != EOK) + { + plc_error("Can't open file %s\n", file_name); + //try again default file + if(strcmp(file_name, PLC_SOCK_FILE_NAME) != 0) + { + if(CtlFileReadWithFilename(PLC_SOCK_FILE_NAME, CTL_FILE_LEN, file_buf) != EOK) + { + plc_error("Can't open file %s\n", file_name); + return; + } + } + else + { + return; + } + } CtlParseJsonData(file_buf); memcpy(param->ip, ctl_file_param.ip, 4); param->port = ctl_file_param.port; param->cmd_num = ctl_file_param.cmd_num; + param->socket_type = ctl_file_param.tcp ? SOCK_STREAM : SOCK_DGRAM; for(int i = 0; i < param->cmd_num; i++) { @@ -253,17 +240,18 @@ void PlcGetParamFromFile(void) plc_print("ip: %d.%d.%d.%d\n", param->ip[0], param->ip[1], param->ip[2], param->ip[3]); plc_print("port: %d", param->port); + plc_print("tcp: %d", param->socket_type); plc_print("cmd number: %d\n", param->cmd_num); for(int i = 0; i < param->cmd_num; i++) { - plc_print("cmd %d len %d: ", i, TestPlcCmd[i].cmd_len); - for(int j = 0; j < TestPlcCmd[i].cmd_len; j++) - plc_print("%x ", TestPlcCmd[i].cmd[j]); - plc_print("\n"); + plc_print_array("cmd", TestPlcCmd[i].cmd_len, TestPlcCmd[i].cmd); } + free(file_buf); } +#endif + void PlcCheckParam(int argc, char *argv[]) { int i; @@ -278,13 +266,19 @@ void PlcCheckParam(int argc, char *argv[]) plc_print("check %d %s\n", i, str); - if(strcmp(str, "file") == 0) +#if defined(MOUNT_SDCARD) && defined(LIB_USING_CJSON) + if(strncmp(str, "file", 4) == 0) { - plc_notice("get parameter file %s\n", PLC_SOCK_FILE_NAME); - PlcGetParamFromFile(); + char file_name[CTL_FILE_NAME_LEN] = {0}; + if(sscanf(str, "file=%s", file_name) == EOF) + { + strcpy(file_name, PLC_SOCK_FILE_NAME); + } + plc_notice("get %s parameter file %s\n", str, file_name); + PlcGetParamFromFile(file_name); return; } - +#endif if(sscanf(str, "ip=%d.%d.%d.%d", ¶m->ip[0], ¶m->ip[1], diff --git a/APP_Framework/Framework/control/plc/interoperability/socket/plc_socket.h b/APP_Framework/Framework/control/plc/interoperability/socket/plc_socket.h index 4801c63e5..6b4ec9b60 100755 --- a/APP_Framework/Framework/control/plc/interoperability/socket/plc_socket.h +++ b/APP_Framework/Framework/control/plc/interoperability/socket/plc_socket.h @@ -23,9 +23,9 @@ #define PLC_BIN_CMD_LEN 512 +// for plc socket test bin commands typedef struct { - uint8_t step; uint16_t delay_ms; uint8_t cmd_len; uint8_t cmd[PLC_BIN_CMD_LEN]; @@ -43,28 +43,23 @@ enum PlcDeviceType { PLC_DEV_TYPE_END, }; -#define PLC_IP_SIZE 16 -#define PLC_DEV_SIZE 32 -#define PLC_TEST_SIZE 65536 +#define PLC_IP_LEN 16 +#define PLC_DEV_NAME_LEN 32 +#define PLC_RECV_BUF_LEN CTL_FILE_LEN typedef struct PlcSocketParamStruct{ - char ip[PLC_IP_SIZE]; + char ip[PLC_IP_LEN]; uint32_t port; uint32_t device_type; //PlcDeviceType uint32_t socket_type; //UDP or TCP - char device[PLC_DEV_SIZE]; - uint32_t step; // communication step + char device[PLC_DEV_NAME_LEN]; uint32_t cmd_num; // command number - uint32_t buf_size; - uint8_t *buf; + uint32_t recv_len; // receive length + uint8_t *recv_buf; // receive buffer }PlcSocketParamType; //debug command -<<<<<<< HEAD -#define plc_print KPrintf -======= #define plc_print //KPrintf ->>>>>>> e5d124231c72798f7f77b842cc8c631b79043914 #define plc_error KPrintf #define plc_notice KPrintf diff --git a/APP_Framework/Framework/control/shared/control_file.c b/APP_Framework/Framework/control/shared/control_file.c index d906beca8..3b383c7f3 100755 --- a/APP_Framework/Framework/control/shared/control_file.c +++ b/APP_Framework/Framework/control/shared/control_file.c @@ -36,6 +36,7 @@ "{ \r\n"\ " \"ip\": \"192.168.250.6\", \r\n"\ " \"port\": 102, \r\n"\ +" \"tcp\": 1, \r\n"\ " \"cmd\": [3, 0, 0, 22, 17, 224, 0, 0, 2, 200, 0, 193, 2, 2, 1, 194, 2, 2, 1, 192, 1, 10], \r\n"\ " \"cmd1\": [3, 0, 0, 25, 2, 240, 128, 50, 1, 0, 0, 0, 13, 0, 8, 0, 0, 240, 0, 0, 1, 0, 1, 0, 240], \r\n" \ " \"cmd2\": [3, 0, 0, 31, 2, 240, 128, 50, 1, 0, 0, 51, 1, 0, 14, 0, 0, 4, 1, 18, 10, 16, 2, 0, 210, 0, 52, 132, 0, 0, 0]\r\n" \ @@ -48,12 +49,21 @@ FILE *CtlFileInit(char *file) { FILE *fd = NULL; +#ifdef MOUNT_SDCARD + // SD card mount flag 1: OK + if(sd_mount_flag == 0) + { + ctl_error("SD card mount failed\n"); + return NULL; + } + fd = fopen(file, "a+"); if(fd == NULL) { ctl_error("open file %s failed\n", file); } +#endif return fd; } @@ -76,6 +86,22 @@ void CtlFileWrite(FILE *fd, int size, char *buf) ctl_print("write size %d: %s\n", size, buf); } +int CtlFileReadWithFilename(char *file, int size, char *buf) +{ + FILE *fd; + fd = fopen(file, "r"); + if(fd == NULL) + { + ctl_error("open file %s failed\n", file); + return EEMPTY; + } + + fseek(fd, 0, SEEK_SET); + fread(buf, size, 1, fd); + ctl_print("read file %d: %.100s\n", size, buf); + return EOK; +} + void CtlCreateFileTest(void) { FILE *fd = CtlFileInit(PLC_SOCK_FILE_NAME); @@ -117,10 +143,9 @@ void CtlParseJsonData(char *buf) cJSON *file_dat = NULL; cJSON *ip_dat = NULL; cJSON *port_dat = NULL; + cJSON *tcp_dat = NULL; cJSON *cmd_dat = NULL; - int cmd_num = 0; - int cmd_index = 1; - char cmd_str[10] = {0}; + char cmd_title[10] = {"cmd"}; CtlPlcSockParamType *file_param = &ctl_file_param; file_dat = cJSON_Parse(buf); @@ -132,6 +157,7 @@ void CtlParseJsonData(char *buf) ip_dat = cJSON_GetObjectItem(file_dat, "ip"); port_dat = cJSON_GetObjectItem(file_dat, "port"); + tcp_dat = cJSON_GetObjectItem(file_dat, "tcp"); ctl_print(" ip : %s\n", ip_dat->valuestring); sscanf(ip_dat->valuestring, "%d.%d.%d.%d", &file_param->ip[0], @@ -141,14 +167,17 @@ void CtlParseJsonData(char *buf) ctl_print(" port: %s %d\n", ip_dat->string, port_dat->valueint); file_param->port = port_dat->valueint; + file_param->tcp = tcp_dat->valueint; + file_param->cmd_num = 0; - strcpy(cmd_str, "cmd"); - while(cmd_dat = cJSON_GetObjectItem(file_dat, cmd_str)) + for(int i = 0; i < CTL_CMD_NUM; i++) { - CtlParseJsonArray(cmd_dat, &file_param->cmd_len[cmd_index - 1], file_param->cmd[cmd_index - 1]); - snprintf(cmd_str, sizeof(cmd_str), "cmd%d", cmd_index++); + cmd_dat = cJSON_GetObjectItem(file_dat, cmd_title); + if(!cmd_dat) + break; + CtlParseJsonArray(cmd_dat, &file_param->cmd_len[i], file_param->cmd[i]); + snprintf(cmd_title, sizeof(cmd_title), "cmd%d", ++file_param->cmd_num); } - file_param->cmd_num = cmd_index - 1; cJSON_Delete(file_dat); } @@ -156,14 +185,25 @@ void CtlParseJsonData(char *buf) void CtlParseFileTest(void) { //for PLC socket parameter file - char file_buf[CTL_FILE_SIZE] = {0}; FILE *fd = CtlFileInit(PLC_SOCK_FILE_NAME); if(fd == NULL) + { + ctl_error("ctl get file %s failed\n", PLC_SOCK_FILE_NAME); return; - memset(file_buf, 0, CTL_FILE_SIZE); - CtlFileRead(fd, CTL_FILE_SIZE, file_buf); + } + + char *file_buf = malloc(CTL_FILE_LEN); + + if(file_buf == NULL) + { + ctl_error("ctl malloc failed\n"); + return; + } + memset(file_buf, 0, CTL_FILE_LEN); + CtlFileRead(fd, CTL_FILE_LEN, file_buf); CtlFileClose(fd); CtlParseJsonData(file_buf); + free(file_buf); } SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0) | SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN) | SHELL_CMD_PARAM_NUM(0), diff --git a/APP_Framework/Framework/control/shared/control_file.h b/APP_Framework/Framework/control/shared/control_file.h index dcafee179..9f8cda11e 100755 --- a/APP_Framework/Framework/control/shared/control_file.h +++ b/APP_Framework/Framework/control/shared/control_file.h @@ -21,31 +21,36 @@ #ifndef __CONTROL_FILE_H_ #define __CONTROL_FILE_H_ -#define CTL_FILE_SIZE 1000 -#define CTL_CMD_NUM 10 // max command number -#define CTL_CMD_LEN 100 -#define CTL_IP_LEN 32 +#define CTL_FILE_LEN 1000 // control file size +#define CTL_CMD_NUM 10 // support command number +#define CTL_CMD_LEN 100 // control command length +#define CTL_IP_LEN 32 // IP address length +#define CTL_FILE_NAME_LEN 100 // file name length #define PLC_SOCK_FILE_NAME "/plc/socket_param.json" -#define ctl_print KPrintf +#define ctl_print //KPrintf #define ctl_error KPrintf +// for running plc socket typedef struct CtlPlcSockParamStruct { char ip[CTL_IP_LEN]; int port; + int tcp; // 1: TCP 0: UDP int cmd_num; //command number int cmd_len[CTL_CMD_NUM]; // command length char cmd[CTL_CMD_NUM][CTL_CMD_LEN]; }CtlPlcSockParamType; extern CtlPlcSockParamType ctl_file_param; +extern int sd_mount_flag; FILE *CtlFileInit(char *file); void CtlFileClose(FILE *fd); void CtlFileRead(FILE *fd, int size, char *buf); void CtlFileWrite(FILE *fd, int size, char *buf); +int CtlFileReadWithFilename(char *file_name, int size, char *buf); #ifdef LIB_USING_CJSON void CtlParseJsonData(char *buf); diff --git a/Ubiquitous/XiZi/board/ok1052-c/board.c b/Ubiquitous/XiZi/board/ok1052-c/board.c index 82a9a10ea..284857639 100644 --- a/Ubiquitous/XiZi/board/ok1052-c/board.c +++ b/Ubiquitous/XiZi/board/ok1052-c/board.c @@ -84,6 +84,10 @@ int MountSDCard(void) #include #endif +#ifdef BSP_USING_I2C +#include +#endif + #ifdef BSP_USING_SPI #include #endif @@ -688,6 +692,10 @@ void InitBoardHardware() Imrt1052HwAdcInit(); #endif +#ifdef BSP_USING_I2C + Imrt1052HwI2cInit(); +#endif + #ifdef BSP_USING_SPI Imrt1052HwSpiInit(); #endif diff --git a/Ubiquitous/XiZi/board/ok1052-c/third_party_driver/i2c/connect_i2c.c b/Ubiquitous/XiZi/board/ok1052-c/third_party_driver/i2c/connect_i2c.c index 4b9eec117..1b2b827ae 100755 --- a/Ubiquitous/XiZi/board/ok1052-c/third_party_driver/i2c/connect_i2c.c +++ b/Ubiquitous/XiZi/board/ok1052-c/third_party_driver/i2c/connect_i2c.c @@ -143,7 +143,7 @@ static int BoardI2cDevBend(void) } /*BOARD I2C INIT*/ -int Stm32HwI2cInit(void) +int Imrt1052HwI2cInit(void) { static int init_flag = 0; x_err_t ret = EOK; diff --git a/Ubiquitous/XiZi/board/ok1052-c/third_party_driver/i2c/connect_i2c_eeprom.c b/Ubiquitous/XiZi/board/ok1052-c/third_party_driver/i2c/connect_i2c_eeprom.c index 26754cd25..792a19273 100755 --- a/Ubiquitous/XiZi/board/ok1052-c/third_party_driver/i2c/connect_i2c_eeprom.c +++ b/Ubiquitous/XiZi/board/ok1052-c/third_party_driver/i2c/connect_i2c_eeprom.c @@ -65,7 +65,6 @@ void I2cEEpromTestWrite(void) int I2cEEpromTest(void) { - Stm32HwI2cInit(); BOARD_InitI2C1Pins(); I2cHardwareInit(); I2cEEpromTestWrite(); diff --git a/Ubiquitous/XiZi/board/ok1052-c/third_party_driver/include/connect_i2c.h b/Ubiquitous/XiZi/board/ok1052-c/third_party_driver/include/connect_i2c.h index 619e801cc..a06675be0 100755 --- a/Ubiquitous/XiZi/board/ok1052-c/third_party_driver/include/connect_i2c.h +++ b/Ubiquitous/XiZi/board/ok1052-c/third_party_driver/include/connect_i2c.h @@ -37,7 +37,7 @@ typedef struct Stm32I2c #define i2c_print KPrintf -int Stm32HwI2cInit(void); +int Imrt1052HwI2cInit(void); #ifdef __cplusplus } diff --git a/Ubiquitous/XiZi/resources/ethernet/LwIP/api/api_lib.c b/Ubiquitous/XiZi/resources/ethernet/LwIP/api/api_lib.c index 88d24dc6e..5d7f8863c 100644 --- a/Ubiquitous/XiZi/resources/ethernet/LwIP/api/api_lib.c +++ b/Ubiquitous/XiZi/resources/ethernet/LwIP/api/api_lib.c @@ -245,7 +245,6 @@ netconn_delete(struct netconn *conn) err = ERR_OK; } else #endif /* LWIP_NETCONN_FULLDUPLEX */ -//tst by wly { err = netconn_prepare_delete(conn); } diff --git a/Ubiquitous/XiZi/resources/ethernet/LwIP/api/sockets.c b/Ubiquitous/XiZi/resources/ethernet/LwIP/api/sockets.c index 1e8031672..2660f73e5 100644 --- a/Ubiquitous/XiZi/resources/ethernet/LwIP/api/sockets.c +++ b/Ubiquitous/XiZi/resources/ethernet/LwIP/api/sockets.c @@ -497,18 +497,6 @@ get_socket(int fd) return sock; } -void pr_socket_buf(void) -{ - int i; - lw_debug("socket:\n"); - for (i = 0; i < NUM_SOCKETS; ++i) { -// if (!sockets[i].conn) - lw_debug("%d: conn %p rcv %d send %d wait %d\n", i, sockets[i].conn, sockets[i].rcvevent, sockets[i].sendevent, - sockets[i].select_waiting); - } -} - - /** * Allocate a new socket for a given netconn. * @@ -600,7 +588,6 @@ free_socket_free_elements(int is_tcp, struct netconn *conn, union lwip_sock_last } if (conn != NULL) { /* netconn_prepare_delete() has already been called, here we only free the conn */ - lw_debug("lw: [%s] tcp %d socket %d accept %d recv %d\n", __func__, is_tcp, conn->socket, conn->acceptmbox, conn->recvmbox); netconn_delete(conn); } } @@ -673,8 +660,6 @@ lwip_accept(int s, struct sockaddr *addr, socklen_t *addrlen) newsock = alloc_socket(newconn, 1); if (newsock == -1) { - lw_debug("lw: [%s] recv %d\n", __func__, newconn->recvmbox); - pr_socket_buf(); netconn_delete(newconn); sock_set_errno(sock, ENFILE); done_socket(sock); @@ -711,8 +696,6 @@ lwip_accept(int s, struct sockaddr *addr, socklen_t *addrlen) err = netconn_peer(newconn, &naddr, &port); if (err != ERR_OK) { LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d): netconn_peer failed, err=%d\n", s, err)); - lw_debug("lw: [%s] recv %x\n", __func__, newconn->recvmbox); - netconn_delete(newconn); free_socket(nsock, 1); sock_set_errno(sock, err_to_errno(err)); @@ -1756,8 +1739,6 @@ lwip_socket(int domain, int type, int protocol) i = alloc_socket(conn, 0); if (i == -1) { - lw_debug("lw: [%s] recv %d delete no socket\n", __func__, conn->recvmbox); - pr_socket_buf(); netconn_delete(conn); set_errno(ENFILE); return -1; @@ -1766,7 +1747,6 @@ lwip_socket(int domain, int type, int protocol) done_socket(&sockets[i - LWIP_SOCKET_OFFSET]); LWIP_DEBUGF(SOCKETS_DEBUG, ("%d\n", i)); set_errno(0); - lw_debug("lw: [%s] new socket %d %p\n", __func__, i, conn); return i; } diff --git a/Ubiquitous/XiZi/resources/ethernet/LwIP/arch/lwipopts.h b/Ubiquitous/XiZi/resources/ethernet/LwIP/arch/lwipopts.h index a8f4b5be8..cdff39538 100644 --- a/Ubiquitous/XiZi/resources/ethernet/LwIP/arch/lwipopts.h +++ b/Ubiquitous/XiZi/resources/ethernet/LwIP/arch/lwipopts.h @@ -250,10 +250,9 @@ typedef unsigned int nfds_t; #define MEMP_MEM_MALLOC 1 #define lw_print //KPrintf -#define lw_trace() //KPrintf("lw: [%s][%d] passed!\n", __func__, __LINE__) -#define lw_error() //KPrintf("lw: [%s][%d] failed!\n", __func__, __LINE__) -#define lw_debug KPrintf +#define lw_error KPrintf #define lw_notice KPrintf + #endif /* __LWIPOPTS_H__ */ /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/Ubiquitous/XiZi/resources/ethernet/LwIP/core/tcp_out.c b/Ubiquitous/XiZi/resources/ethernet/LwIP/core/tcp_out.c index 3fb89e245..724df1097 100644 --- a/Ubiquitous/XiZi/resources/ethernet/LwIP/core/tcp_out.c +++ b/Ubiquitous/XiZi/resources/ethernet/LwIP/core/tcp_out.c @@ -355,7 +355,7 @@ tcp_write_checks(struct tcp_pcb *pcb, u16_t len) * it can send them more efficiently by combining them together). * To prompt the system to send data now, call tcp_output() after * calling tcp_write(). - * + * * This function enqueues the data pointed to by the argument dataptr. The length of * the data is passed as the len parameter. The apiflags can be one or more of: * - TCP_WRITE_FLAG_COPY: indicates whether the new memory should be allocated @@ -1386,17 +1386,13 @@ tcp_output(struct tcp_pcb *pcb) /* In the case of fast retransmit, the packet should not go to the tail * of the unacked queue, but rather somewhere before it. We need to check for * this case. -STJ Jul 27, 2004 */ - lw_debug("check %s seg %p useg %p\n", __func__, seg, useg); if (TCP_SEQ_LT(lwip_ntohl(seg->tcphdr->seqno), lwip_ntohl(useg->tcphdr->seqno))) { /* add segment to before tail of unacked list, keeping the list sorted */ struct tcp_seg **cur_seg = &(pcb->unacked); - lw_debug("check %s ", __func__); while (*cur_seg && TCP_SEQ_LT(lwip_ntohl((*cur_seg)->tcphdr->seqno), lwip_ntohl(seg->tcphdr->seqno))) { - lw_debug("%p -> ", *cur_seg); cur_seg = &((*cur_seg)->next ); } - lw_debug("\n"); seg->next = (*cur_seg); (*cur_seg) = seg; } else { diff --git a/Ubiquitous/XiZi/resources/ethernet/cmd_lwip/lwip_config_demo.c b/Ubiquitous/XiZi/resources/ethernet/cmd_lwip/lwip_config_demo.c index a9e662c09..7e94dc272 100755 --- a/Ubiquitous/XiZi/resources/ethernet/cmd_lwip/lwip_config_demo.c +++ b/Ubiquitous/XiZi/resources/ethernet/cmd_lwip/lwip_config_demo.c @@ -18,40 +18,18 @@ * @date 2021.12.15 */ -#include "lwip/opt.h" - -#if LWIP_IPV4 && LWIP_RAW - -#include "ping.h" - -#include "lwip/timeouts.h" -#include "lwip/init.h" -#include "netif/ethernet.h" - #include "board.h" -#include "pin_mux.h" -#include "clock_config.h" - -#include -#include -#include "connect_ethernet.h" +#include "sys_arch.h" /******************************************************************************/ -static void *LwipSetIPTask(void *param) +static void LwipSetIPTask(void *param) { lwip_config_net(lwip_ipaddr, lwip_netmask, lwip_gwaddr); } void LwipSetIPTest(int argc, char *argv[]) { - int result = 0; - pthread_t th_id; - pthread_attr_t attr; - - attr.schedparam.sched_priority = LWIP_DEMO_TASK_PRIO; - attr.stacksize = LWIP_TASK_STACK_SIZE; - if(argc >= 4) { lw_print("lw: [%s] ip %s mask %s gw %s\n", __func__, argv[1], argv[2], argv[3]); @@ -65,12 +43,7 @@ void LwipSetIPTest(int argc, char *argv[]) sscanf(argv[1], "%d.%d.%d.%d", &lwip_ipaddr[0], &lwip_ipaddr[1], &lwip_ipaddr[2], &lwip_ipaddr[3]); } - result = pthread_create(&th_id, &attr, LwipSetIPTask, NULL); - if (0 == result) { - lw_print("lw: [%s] thread %d successfully!\n", __func__, th_id); - } else { - lw_print("lw: [%s] failed! error code is %d\n", __func__, result); - } + sys_thread_new("SET ip address", LwipSetIPTask, NULL, LWIP_TASK_STACK_SIZE, LWIP_DEMO_TASK_PRIO); } SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0) | SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN) | SHELL_CMD_PARAM_NUM(3), @@ -98,4 +71,3 @@ void LwipShowIPTask(int argc, char *argv[]) SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0) | SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN) | SHELL_CMD_PARAM_NUM(0), showip, LwipShowIPTask, GetIp [IP] [Netmask] [Gateway]); -#endif diff --git a/Ubiquitous/XiZi/resources/ethernet/cmd_lwip/lwip_demo.h b/Ubiquitous/XiZi/resources/ethernet/cmd_lwip/lwip_demo.h index 99e273aa1..68a78b4d7 100755 --- a/Ubiquitous/XiZi/resources/ethernet/cmd_lwip/lwip_demo.h +++ b/Ubiquitous/XiZi/resources/ethernet/cmd_lwip/lwip_demo.h @@ -1,7 +1,37 @@ +/* +* Copyright (c) 2022 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 lwip_demo.h +* @brief lwip demo relative struct and definition +* @version 1.0 +* @author AIIT XUOS Lab +* @date 2022-03-21 +*/ + #ifndef __LWIP_DEMO_H__ #define __LWIP_DEMO_H__ +typedef struct LwipTcpSocketStruct +{ + char ip[6]; + uint16_t port; + char *buf; +}LwipTcpSocketParamType; + +#define LWIP_TEST_MSG_SIZE 128 + #define LWIP_TEST_STACK_SIZE 4096 #define LWIP_TEST_TASK_PRIO 20 #endif /* __LWIP_DEMO_H__ */ + diff --git a/Ubiquitous/XiZi/resources/ethernet/cmd_lwip/lwip_tcp_demo.c b/Ubiquitous/XiZi/resources/ethernet/cmd_lwip/lwip_tcp_demo.c index 387899abc..f57f80b4f 100755 --- a/Ubiquitous/XiZi/resources/ethernet/cmd_lwip/lwip_tcp_demo.c +++ b/Ubiquitous/XiZi/resources/ethernet/cmd_lwip/lwip_tcp_demo.c @@ -1,5 +1,5 @@ /* -* Copyright (c) 2020 AIIT XUOS Lab +* Copyright (c) 2022 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: @@ -11,45 +11,32 @@ */ /** -* @file tcp_echo_socket_demo.c -* @brief One UDP demo based on LwIP +* @file lwip_tcp_demo.c +* @brief TCP demo based on LwIP * @version 1.0 * @author AIIT XUOS Lab -* @date 2021-05-29 +* @date 2022-03-21 */ -#include -#include + #include "board.h" +#include "lwip_demo.h" #include "sys_arch.h" -#include -#include "lwip/sys.h" +#include "lwip/sockets.h" #include "tcpecho_raw.h" -#define MSG_SIZE 128 - -typedef struct LwipTcpSocketStruct -{ - char ip[6]; - uint16_t port; - char *buf; // test buffer -}LwipTcpSocketParamType; - -// this is for test in shell, in fact, shell restrict the length of input string, which is less then 128 -char tcp_send_msg[MSG_SIZE] = {0}; -char tcp_target[] = {192, 168, 250, 252}; -uint16_t tcp_port = LWIP_TARGET_PORT; +char tcp_demo_msg[LWIP_TEST_MSG_SIZE] = {0}; +char tcp_demo_ip[] = {192, 168, 250, 252}; +u16_t tcp_demo_port = LWIP_TARGET_PORT; /******************************************************************************/ static void LwipTcpSendTask(void *arg) { - lw_notice("LwipTcpSendTask start.\n"); - int fd = -1; fd = socket(AF_INET, SOCK_STREAM, 0); if (fd < 0) { - lw_notice("Socket error\n"); + lw_error("Socket error\n"); return; } @@ -63,7 +50,7 @@ static void LwipTcpSendTask(void *arg) if (connect(fd, (struct sockaddr *)&tcp_sock, sizeof(struct sockaddr))) { - lw_notice("Unable to connect\n"); + lw_error("Unable to connect\n"); closesocket(fd); return; } @@ -71,9 +58,9 @@ static void LwipTcpSendTask(void *arg) lw_notice("tcp connect success, start to send.\n"); lw_notice("\n\nTarget Port:%d\n\n", tcp_sock.sin_port); - sendto(fd, tcp_send_msg, strlen(tcp_send_msg), 0, (struct sockaddr*)&tcp_sock, sizeof(struct sockaddr)); + sendto(fd, tcp_demo_msg, strlen(tcp_demo_msg), 0, (struct sockaddr*)&tcp_sock, sizeof(struct sockaddr)); - lw_notice("Send tcp msg: %s ", tcp_send_msg); + lw_notice("Send tcp msg: %s ", tcp_demo_msg); closesocket(fd); return; @@ -82,31 +69,31 @@ static void LwipTcpSendTask(void *arg) void LwipTcpSendTest(int argc, char *argv[]) { LwipTcpSocketParamType param; - memset(tcp_send_msg, 0, MSG_SIZE); + memset(tcp_demo_msg, 0, LWIP_TEST_MSG_SIZE); if(argc >= 2) { - strncpy(tcp_send_msg, argv[1], strlen(argv[1])); + strncpy(tcp_demo_msg, argv[1], strlen(argv[1])); } else { - strncpy(tcp_send_msg, "hello world", strlen("hello world")); + strncpy(tcp_demo_msg, "hello world", strlen("hello world")); } - strcat(tcp_send_msg, "\r\n"); + strcat(tcp_demo_msg, "\r\n"); if(argc >= 3) { - if(sscanf(argv[2], "%d.%d.%d.%d:%d", &tcp_target[0], &tcp_target[1], &tcp_target[2], &tcp_target[3], &tcp_port) == EOK) + if(sscanf(argv[2], "%d.%d.%d.%d:%d", &tcp_demo_ip[0], &tcp_demo_ip[1], &tcp_demo_ip[2], &tcp_demo_ip[3], &tcp_demo_port) == EOK) { - sscanf(argv[2], "%d.%d.%d.%d", &tcp_target[0], &tcp_target[1], &tcp_target[2], &tcp_target[3]); + sscanf(argv[2], "%d.%d.%d.%d", &tcp_demo_ip[0], &tcp_demo_ip[1], &tcp_demo_ip[2], &tcp_demo_ip[3]); } } - lw_notice("get ipaddr %d.%d.%d.%d:%d\n", tcp_target[0], tcp_target[1], tcp_target[2], tcp_target[3], tcp_port); - lwip_config_tcp(lwip_ipaddr, lwip_netmask, tcp_target); + lw_notice("get ipaddr %d.%d.%d.%d:%d\n", tcp_demo_ip[0], tcp_demo_ip[1], tcp_demo_ip[2], tcp_demo_ip[3], tcp_demo_port); + lwip_config_tcp(lwip_ipaddr, lwip_netmask, tcp_demo_ip); - memcpy(param.ip, tcp_target, 4); - param.port = tcp_port; - param.buf = malloc(MSG_SIZE); - memcpy(param.buf, tcp_send_msg, MSG_SIZE); + memcpy(param.ip, tcp_demo_ip, 4); + param.port = tcp_demo_port; + param.buf = malloc(LWIP_TEST_MSG_SIZE); + memcpy(param.buf, tcp_demo_msg, LWIP_TEST_MSG_SIZE); sys_thread_new("tcp send", LwipTcpSendTask, ¶m, LWIP_TASK_STACK_SIZE, LWIP_DEMO_TASK_PRIO); } diff --git a/Ubiquitous/XiZi/resources/ethernet/cmd_lwip/lwip_udp_demo.c b/Ubiquitous/XiZi/resources/ethernet/cmd_lwip/lwip_udp_demo.c index 97d06b119..bb3f48be1 100755 --- a/Ubiquitous/XiZi/resources/ethernet/cmd_lwip/lwip_udp_demo.c +++ b/Ubiquitous/XiZi/resources/ethernet/cmd_lwip/lwip_udp_demo.c @@ -1,5 +1,5 @@ /* -* Copyright (c) 2021 AIIT XUOS Lab +* Copyright (c) 2022 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: @@ -15,25 +15,23 @@ * @brief One UDP demo based on LwIP * @version 1.0 * @author AIIT XUOS Lab -* @date 2021-05-29 +* @date 2022-03-21 */ -#include -#include #include "board.h" #include "sys_arch.h" #include "lwip/udp.h" -#include -#include "lwip/sys.h" +#include "lwip/sockets.h" + -#define UDP_TASK_STACK_SIZE 4096 -#define UDP_TASK_PRIO 15 #define PBUF_SIZE 27 static struct udp_pcb *udpecho_raw_pcb; + char udp_demo_ip[] = {192, 168, 250, 252}; -uint16_t udp_demo_port = LWIP_TARGET_PORT; +u16_t udp_demo_port = LWIP_TARGET_PORT; + char hello_str[] = {"hello world\r\n"}; -char udp_send_msg[] = "\n\nThis one is UDP pkg. Congratulations on you.\n\n"; +char udp_demo_msg[] = "\nThis one is UDP package!!!\n"; /******************************************************************************/ @@ -47,7 +45,7 @@ static void LwipUDPSendTask(void *arg) socket_fd = socket(AF_INET, SOCK_DGRAM, 0); if (socket_fd < 0) { - lw_print("Socket error\n"); + lw_error("Socket error\n"); return; } @@ -59,45 +57,43 @@ static void LwipUDPSendTask(void *arg) if (connect(socket_fd, (struct sockaddr *)&udp_sock, sizeof(struct sockaddr))) { - lw_print("Unable to connect\n"); + lw_error("Unable to connect\n"); closesocket(socket_fd); return; } - lw_print("UDP connect success, start to send.\n"); - lw_print("\n\nTarget Port:%d\n\n", udp_sock.sin_port); + lw_notice("UDP connect success, start to send.\n"); + lw_notice("\n\nTarget Port:%d\n\n", udp_sock.sin_port); - sendto(socket_fd, udp_send_msg, strlen(udp_send_msg), 0, (struct sockaddr*)&udp_sock, sizeof(struct sockaddr)); - lw_notice("Send UDP msg: %s ", udp_send_msg); + sendto(socket_fd, udp_demo_msg, strlen(udp_demo_msg), 0, (struct sockaddr*)&udp_sock, sizeof(struct sockaddr)); + lw_notice("Send UDP msg: %s ", udp_demo_msg); closesocket(socket_fd); return; } void *LwipUdpSendTest(int argc, char *argv[]) { - int result = 0; - sys_thread_t th_id; - - memset(udp_send_msg, 0, sizeof(udp_send_msg)); + memset(udp_demo_msg, 0, sizeof(udp_demo_msg)); if(argc == 1) { lw_print("lw: [%s] gw %d.%d.%d.%d\n", __func__, udp_demo_ip[0], udp_demo_ip[1], udp_demo_ip[2], udp_demo_ip[3]); - strncpy(udp_send_msg, hello_str, strlen(hello_str)); + strncpy(udp_demo_msg, hello_str, strlen(hello_str)); } else { - strncpy(udp_send_msg, argv[1], strlen(argv[1])); - strncat(udp_send_msg, "\r\n", 2); + strncpy(udp_demo_msg, argv[1], strlen(argv[1])); + strncat(udp_demo_msg, "\r\n", 2); if(argc == 3) { sscanf(argv[2], "%d.%d.%d.%d", &udp_demo_ip[0], &udp_demo_ip[1], &udp_demo_ip[2], &udp_demo_ip[3]); } } + lw_print("lw: [%s] gw %d.%d.%d.%d\n", __func__, udp_demo_ip[0], udp_demo_ip[1], udp_demo_ip[2], udp_demo_ip[3]); - lwip_config_net(lwip_ipaddr, lwip_netmask, lwip_gwaddr); - sys_thread_new("udp socket send", LwipUDPSendTask, NULL, LWIP_TASK_STACK_SIZE, LWIP_DEMO_TASK_PRIO); + lwip_config_net(lwip_ipaddr, lwip_netmask, udp_demo_ip); + sys_thread_new("udp send", LwipUDPSendTask, NULL, LWIP_TASK_STACK_SIZE, LWIP_DEMO_TASK_PRIO); } SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0) | SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN) | SHELL_CMD_PARAM_NUM(3), @@ -116,6 +112,7 @@ static void LwipUdpRecvTask(void *arg, struct udp_pcb *upcb, struct pbuf *p, { return; } + udp_len = p->tot_len; lw_notice("Receive data :%dB\r\n", udp_len); @@ -158,5 +155,5 @@ void LwipUdpRecvTest(void) } SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0) | SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN) | SHELL_CMD_PARAM_NUM(0), - UDPRecv, LwipUdpRecvTest, UDP server echo); + UDPRecv, LwipUdpRecvTest, UDP Receive echo); diff --git a/Ubiquitous/XiZi/resources/include/flash_spi.h b/Ubiquitous/XiZi/resources/include/flash_spi.h index 1f6aa4913..f41d40d25 100644 --- a/Ubiquitous/XiZi/resources/include/flash_spi.h +++ b/Ubiquitous/XiZi/resources/include/flash_spi.h @@ -21,9 +21,7 @@ #ifndef FLASH_SPI_H #define FLASH_SPI_H -#ifdef RESOURCES_SPI_SFUD #include -#endif #ifdef __cplusplus extern "C" { diff --git a/Ubiquitous/XiZi/tool/shell/letter-shell/shell_port.c b/Ubiquitous/XiZi/tool/shell/letter-shell/shell_port.c index 5d8d21a65..661cfb604 100644 --- a/Ubiquitous/XiZi/tool/shell/letter-shell/shell_port.c +++ b/Ubiquitous/XiZi/tool/shell/letter-shell/shell_port.c @@ -1,12 +1,12 @@ /** * @file shell_port.c * @author Letter (NevermindZZT@gmail.com) - * @brief + * @brief * @version 0.1 * @date 2019-02-22 - * + * * @copyright (c) 2019 Letter - * + * */ #include "xizi.h" @@ -26,11 +26,11 @@ char *shellBuffer; ShellFs shellFs; char *shellPathBuffer; -HardwareDevType console; +HardwareDevType console; /** - * @brief Shell write - * + * @brief Shell write + * * @param data write data */ void userShellWrite(char data) @@ -40,7 +40,7 @@ void userShellWrite(char data) /** * @brief shell read - * + * * @param data read data * @return char read status */ @@ -62,7 +62,7 @@ signed char userShellRead(char *data) #ifdef SHELL_ENABLE_FILESYSTEM /** * @brief list file - * + * * @param path path * @param buffer result buffer * @param maxLen the maximum buffer size @@ -86,14 +86,14 @@ size_t userShellListDir(char *path, char *buffer, size_t maxLen) #endif /** - * @brief Initialize the shell - * + * @brief Initialize the shell + * */ int userShellInit(void) { shellBuffer = x_malloc(512*sizeof(char)); - - + + #ifdef SHELL_ENABLE_FILESYSTEM shellPathBuffer = x_malloc(512*sizeof(char)); shellPathBuffer[0] = '/'; @@ -102,7 +102,7 @@ int userShellInit(void) shellFs.listdir = userShellListDir; shellFsInit(&shellFs, shellPathBuffer, 512); shellSetPath(&shell, shellPathBuffer); -#endif +#endif shell.write = userShellWrite; shell.read = userShellRead; @@ -114,13 +114,13 @@ int userShellInit(void) serial_dev_param->serial_set_mode = 0; serial_dev_param->serial_stream_mode = SIGN_OPER_STREAM; BusDevOpen(console); - - shellInit(&shell, shellBuffer, 4096); + + shellInit(&shell, shellBuffer, 512); int32 tid; tid = KTaskCreate("letter-shell", shellTask, &shell, SHELL_TASK_STACK_SIZE, SHELL_TASK_PRIORITY); - + StartupKTask(tid); return 0; } From be221d902ba3827e869eef2dd785981dfef51c8b Mon Sep 17 00:00:00 2001 From: Wang_Weigen Date: Tue, 22 Mar 2022 14:00:34 +0800 Subject: [PATCH 7/9] repair the send peoblem for ch438 --- .../Applications/sensor_app/SConscript | 2 + APP_Framework/Framework/sensor/ch4/Kconfig | 4 +- APP_Framework/Framework/sensor/hcho/Kconfig | 17 ++++++++ .../Framework/sensor/hcho/SConscript | 14 +++++++ .../sensor/hcho/tb600b_wq_hcho1os/SConscript | 10 +++++ .../aiit_board/aiit-riscv64-board/.config | 15 +++++-- .../aiit-riscv64-board/base-drivers/ch438.c | 39 +++++++++++++------ .../aiit_board/aiit-riscv64-board/rtconfig.h | 13 ++++++- 8 files changed, 95 insertions(+), 19 deletions(-) create mode 100644 APP_Framework/Framework/sensor/hcho/SConscript create mode 100644 APP_Framework/Framework/sensor/hcho/tb600b_wq_hcho1os/SConscript diff --git a/APP_Framework/Applications/sensor_app/SConscript b/APP_Framework/Applications/sensor_app/SConscript index dbf2e67c0..d526a7b38 100644 --- a/APP_Framework/Applications/sensor_app/SConscript +++ b/APP_Framework/Applications/sensor_app/SConscript @@ -17,6 +17,8 @@ if GetDepend(['APPLICATION_SENSOR_TEMPERATURE_HS300X']): SOURCES = ['temperature_hs300x.c'] + SOURCES if GetDepend(['APPLICATION_SENSOR_CH4_AS830']): SOURCES = ['ch4_as830.c'] + SOURCES +if GetDepend(['APPLICATION_SENSOR_HCHO']): + SOURCES = ['hcho_tb600b_wq_hcho1os.c'] + SOURCES path = [cwd] objs = DefineGroup('sensor_app', src = SOURCES, depend = DEPENDS,CPPPATH = path) Return("objs") \ No newline at end of file diff --git a/APP_Framework/Framework/sensor/ch4/Kconfig b/APP_Framework/Framework/sensor/ch4/Kconfig index f61a56941..63011de1d 100644 --- a/APP_Framework/Framework/sensor/ch4/Kconfig +++ b/APP_Framework/Framework/sensor/ch4/Kconfig @@ -55,11 +55,11 @@ config SENSOR_AS830 if SENSOR_AS830_DRIVER_EXTUART config SENSOR_DEVICE_AS830_DEV string "as830 device extra uart path" - default "/dev/extuart_dev1" + default "/dev/extuart_dev4" config SENSOR_DEVICE_AS830_DEV_EXT_PORT int "if AS830 device using extuart, choose port" - default "1" + default "4" endif endif endif diff --git a/APP_Framework/Framework/sensor/hcho/Kconfig b/APP_Framework/Framework/sensor/hcho/Kconfig index 07bd8168e..cd87003b6 100644 --- a/APP_Framework/Framework/sensor/hcho/Kconfig +++ b/APP_Framework/Framework/sensor/hcho/Kconfig @@ -43,6 +43,23 @@ config SENSOR_TB600B_WQ_HCHO1OS endif if ADD_RTTHREAD_FETURES + config SENSOR_TB600B_WQ_HCHO1OS_DRIVER_EXTUART + bool "Using extra uart to support tb600b wq_hcho1os" + default y + config SENSOR_DEVICE_TB600B_WQ_HCHO1OS_DEV + string "tb600b wq_hcho1os device uart path" + default "/dev/uart2" + depends on !SENSOR_TB600B_WQ_HCHO1OS_DRIVER_EXTUART + + if SENSOR_TB600B_WQ_HCHO1OS_DRIVER_EXTUART + config SENSOR_DEVICE_TB600B_WQ_HCHO1OS_DEV + string "tb600b wq_hcho1os device extra uart path" + default "/dev/extuart_dev1" + + config SENSOR_DEVICE_TB600B_WQ_HCHO1OS_DEV_EXT_PORT + int "if TB600B_WQ_HCHO1OS device using extuart, choose port" + default "1" + endif endif endif diff --git a/APP_Framework/Framework/sensor/hcho/SConscript b/APP_Framework/Framework/sensor/hcho/SConscript new file mode 100644 index 000000000..f307e3f70 --- /dev/null +++ b/APP_Framework/Framework/sensor/hcho/SConscript @@ -0,0 +1,14 @@ +import os +Import('RTT_ROOT') +from building import * + +cwd = GetCurrentDir() +objs = [] +list = os.listdir(cwd) + +for d in list: + path = os.path.join(cwd, d) + if os.path.isfile(os.path.join(path, 'SConscript')): + objs = objs + SConscript(os.path.join(path, 'SConscript')) + +Return('objs') diff --git a/APP_Framework/Framework/sensor/hcho/tb600b_wq_hcho1os/SConscript b/APP_Framework/Framework/sensor/hcho/tb600b_wq_hcho1os/SConscript new file mode 100644 index 000000000..e8f068b84 --- /dev/null +++ b/APP_Framework/Framework/sensor/hcho/tb600b_wq_hcho1os/SConscript @@ -0,0 +1,10 @@ +from building import * +import os + +cwd = GetCurrentDir() +src = [] +if GetDepend(['SENSOR_TB600B_WQ_HCHO1OS']): + src += ['tb600b_wq_hcho1os.c'] +group = DefineGroup('sensor hcho', src, depend = [], CPPPATH = [cwd]) + +Return('group') \ No newline at end of file diff --git a/Ubiquitous/RT_Thread/aiit_board/aiit-riscv64-board/.config b/Ubiquitous/RT_Thread/aiit_board/aiit-riscv64-board/.config index 987bff78e..a35ce48a2 100644 --- a/Ubiquitous/RT_Thread/aiit_board/aiit-riscv64-board/.config +++ b/Ubiquitous/RT_Thread/aiit_board/aiit-riscv64-board/.config @@ -331,7 +331,13 @@ CONFIG_TRANSFORM_LAYER_ATTRIUBUTE=y # CONFIG_ADD_NUTTX_FETURES is not set CONFIG_ADD_RTTHREAD_FETURES=y CONFIG_SUPPORT_SENSOR_FRAMEWORK=y -# CONFIG_SENSOR_HCHO is not set +CONFIG_SENSOR_HCHO=y +CONFIG_SENSOR_TB600B_WQ_HCHO1OS=y +CONFIG_SENSOR_DEVICE_TB600B_WQ_HCHO1OS="tb600b_wq_hcho1os_1" +CONFIG_SENSOR_QUANTITY_TB600B_HCHO="hcho_1" +CONFIG_SENSOR_TB600B_WQ_HCHO1OS_DRIVER_EXTUART=y +CONFIG_SENSOR_DEVICE_TB600B_WQ_HCHO1OS_DEV="/dev/extuart_dev1" +CONFIG_SENSOR_DEVICE_TB600B_WQ_HCHO1OS_DEV_EXT_PORT=1 # CONFIG_SENSOR_TVOC is not set # CONFIG_SENSOR_IAQ is not set CONFIG_SENSOR_CH4=y @@ -339,8 +345,8 @@ CONFIG_SENSOR_AS830=y CONFIG_SENSOR_DEVICE_AS830="as830_1" CONFIG_SENSOR_QUANTITY_AS830_CH4="ch4_1" CONFIG_SENSOR_AS830_DRIVER_EXTUART=y -CONFIG_SENSOR_DEVICE_AS830_DEV="/dev/extuart_dev1" -CONFIG_SENSOR_DEVICE_AS830_DEV_EXT_PORT=1 +CONFIG_SENSOR_DEVICE_AS830_DEV="/dev/extuart_dev4" +CONFIG_SENSOR_DEVICE_AS830_DEV_EXT_PORT=4 # CONFIG_SENSOR_CO2 is not set # CONFIG_SENSOR_PM is not set # CONFIG_SENSOR_VOICE is not set @@ -395,7 +401,8 @@ CONFIG_MAIN_KTASK_STACK_SIZE=1024 # sensor app # CONFIG_APPLICATION_SENSOR=y -# CONFIG_APPLICATION_SENSOR_HCHO is not set +CONFIG_APPLICATION_SENSOR_HCHO=y +CONFIG_APPLICATION_SENSOR_HCHO_TB600B_WQ_HCHO1OS=y # CONFIG_APPLICATION_SENSOR_TVOC is not set # CONFIG_APPLICATION_SENSOR_IAQ is not set CONFIG_APPLICATION_SENSOR_CH4=y diff --git a/Ubiquitous/RT_Thread/aiit_board/aiit-riscv64-board/base-drivers/ch438.c b/Ubiquitous/RT_Thread/aiit_board/aiit-riscv64-board/base-drivers/ch438.c index 7a5212739..f561deb5f 100644 --- a/Ubiquitous/RT_Thread/aiit_board/aiit-riscv64-board/base-drivers/ch438.c +++ b/Ubiquitous/RT_Thread/aiit_board/aiit-riscv64-board/base-drivers/ch438.c @@ -116,7 +116,7 @@ void set_485_output(rt_uint8_t ch_no) rt_uint8_t ReadCH438Data( rt_uint8_t addr ) { - rt_uint8_t dat; + rt_uint8_t dat = 0; gpiohs_set_pin(FPIOA_CH438_NWR,GPIO_PV_HIGH); gpiohs_set_pin(FPIOA_CH438_NRD,GPIO_PV_HIGH); @@ -224,9 +224,11 @@ static int Ch438Irq(void *parameter) { rt_uint8_t gInterruptStatus; rt_uint8_t port = 0; - + struct rt_serial_device *serial = (struct rt_serial_device *)parameter; + /* multi irq may happen*/ gInterruptStatus = ReadCH438Data(REG_SSR_ADDR); port = log(gInterruptStatus & 0xFF)/log(2); + rt_hw_serial_isr(extuart_serial_parm[port], RT_SERIAL_EVENT_RX_IND); } @@ -242,17 +244,27 @@ static rt_err_t rt_extuart_configure(struct rt_serial_device *serial, struct ser static rt_err_t extuart_control(struct rt_serial_device *serial, int cmd, void *arg) { - uint16_t ext_uart_no = serial->config.reserved; + rt_uint16_t ext_uart_no = serial->config.reserved; + static rt_uint16_t register_flag = 0; switch (cmd) { case RT_DEVICE_CTRL_CLR_INT: - gpiohs_irq_unregister(FPIOA_CH438_INT); + if(1 == register_flag) + { + gpiohs_irq_unregister(FPIOA_CH438_INT); + register_flag = 0; + } break; case RT_DEVICE_CTRL_SET_INT: - gpiohs_set_drive_mode(FPIOA_CH438_INT, GPIO_DM_INPUT_PULL_UP); - gpiohs_set_pin_edge(FPIOA_CH438_INT,GPIO_PE_FALLING); - gpiohs_irq_register(FPIOA_CH438_INT, 1, Ch438Irq, RT_NULL); + if(0 == register_flag) + { + gpiohs_set_drive_mode(FPIOA_CH438_INT, GPIO_DM_INPUT_PULL_UP); + gpiohs_set_pin_edge(FPIOA_CH438_INT,GPIO_PE_FALLING); + gpiohs_irq_register(FPIOA_CH438_INT, 1, Ch438Irq, (void*)serial); + register_flag = 1; + } + break; } return (RT_EOK); @@ -261,20 +273,25 @@ static rt_err_t extuart_control(struct rt_serial_device *serial, int cmd, void * static int drv_extuart_putc(struct rt_serial_device *serial, char c) { uint16_t ext_uart_no = serial->config.reserved; - rt_uint8_t REG_LSR_ADDR,REG_THR_ADDR; REG_LSR_ADDR = offsetadd[ext_uart_no] | REG_LSR0_ADDR; REG_THR_ADDR = offsetadd[ext_uart_no] | REG_THR0_ADDR; - while( ( ReadCH438Data( REG_LSR_ADDR ) & BIT_LSR_TEMT ) == 0 ); - WriteCH438Block( REG_THR_ADDR, 1, &c ); + if((ReadCH438Data( REG_LSR_ADDR ) & BIT_LSR_TEMT) != 0) + { + WriteCH438Block( REG_THR_ADDR, 1, &c ); + return 1; + } else { + return 0; + } + } static int drv_extuart_getc(struct rt_serial_device *serial) { - rt_int8_t dat = -1; + rt_uint8_t dat = 0; rt_uint8_t REG_LSR_ADDR,REG_RBR_ADDR; uint16_t ext_uart_no = serial->config.reserved;///< get extern uart port diff --git a/Ubiquitous/RT_Thread/aiit_board/aiit-riscv64-board/rtconfig.h b/Ubiquitous/RT_Thread/aiit_board/aiit-riscv64-board/rtconfig.h index 0f8d716bf..714ab891e 100644 --- a/Ubiquitous/RT_Thread/aiit_board/aiit-riscv64-board/rtconfig.h +++ b/Ubiquitous/RT_Thread/aiit_board/aiit-riscv64-board/rtconfig.h @@ -208,13 +208,20 @@ #define TRANSFORM_LAYER_ATTRIUBUTE #define ADD_RTTHREAD_FETURES #define SUPPORT_SENSOR_FRAMEWORK +#define SENSOR_HCHO +#define SENSOR_TB600B_WQ_HCHO1OS +#define SENSOR_DEVICE_TB600B_WQ_HCHO1OS "tb600b_wq_hcho1os_1" +#define SENSOR_QUANTITY_TB600B_HCHO "hcho_1" +#define SENSOR_TB600B_WQ_HCHO1OS_DRIVER_EXTUART +#define SENSOR_DEVICE_TB600B_WQ_HCHO1OS_DEV "/dev/extuart_dev1" +#define SENSOR_DEVICE_TB600B_WQ_HCHO1OS_DEV_EXT_PORT 1 #define SENSOR_CH4 #define SENSOR_AS830 #define SENSOR_DEVICE_AS830 "as830_1" #define SENSOR_QUANTITY_AS830_CH4 "ch4_1" #define SENSOR_AS830_DRIVER_EXTUART -#define SENSOR_DEVICE_AS830_DEV "/dev/extuart_dev1" -#define SENSOR_DEVICE_AS830_DEV_EXT_PORT 1 +#define SENSOR_DEVICE_AS830_DEV "/dev/extuart_dev4" +#define SENSOR_DEVICE_AS830_DEV_EXT_PORT 4 /* Security */ @@ -242,6 +249,8 @@ /* sensor app */ #define APPLICATION_SENSOR +#define APPLICATION_SENSOR_HCHO +#define APPLICATION_SENSOR_HCHO_TB600B_WQ_HCHO1OS #define APPLICATION_SENSOR_CH4 #define APPLICATION_SENSOR_CH4_AS830 From c9455ea4429f1135b434cfdd0b0b83d27ae39b29 Mon Sep 17 00:00:00 2001 From: wlyu Date: Tue, 22 Mar 2022 14:21:49 +0800 Subject: [PATCH 8/9] fixed LWIP bug of exception --- Ubiquitous/XiZi/resources/ethernet/LwIP/core/tcp_out.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Ubiquitous/XiZi/resources/ethernet/LwIP/core/tcp_out.c b/Ubiquitous/XiZi/resources/ethernet/LwIP/core/tcp_out.c index 724df1097..456d28917 100644 --- a/Ubiquitous/XiZi/resources/ethernet/LwIP/core/tcp_out.c +++ b/Ubiquitous/XiZi/resources/ethernet/LwIP/core/tcp_out.c @@ -1386,6 +1386,9 @@ tcp_output(struct tcp_pcb *pcb) /* In the case of fast retransmit, the packet should not go to the tail * of the unacked queue, but rather somewhere before it. We need to check for * this case. -STJ Jul 27, 2004 */ + /*when useg is NULL, cause exception*/ + if(useg == NULL) + break; if (TCP_SEQ_LT(lwip_ntohl(seg->tcphdr->seqno), lwip_ntohl(useg->tcphdr->seqno))) { /* add segment to before tail of unacked list, keeping the list sorted */ struct tcp_seg **cur_seg = &(pcb->unacked); From 363a2215335ffcbd6813ae55c2c6be97a1168f62 Mon Sep 17 00:00:00 2001 From: Liu_Weichao Date: Tue, 22 Mar 2022 17:17:35 +0800 Subject: [PATCH 9/9] =?UTF-8?q?1=E3=80=81feat=20add=20xip=20dir=20and=20Fl?= =?UTF-8?q?ash=20Bootable=20Image=20for=20ok1052-c=20and=20xidatong=20boar?= =?UTF-8?q?d=20link.lds=EF=BC=9B2=E3=80=81feat=20add=20ch438=20driver=20fo?= =?UTF-8?q?r=20xidatong=20board=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Ubiquitous/XiZi/board/ok1052-c/Makefile | 2 +- Ubiquitous/XiZi/board/ok1052-c/README.md | 5 +- .../board/ok1052-c/img/NXPBootUtility_2.png | Bin 61493 -> 51375 bytes .../XiZi/board/ok1052-c/img/flashconfig.png | Bin 0 -> 72092 bytes Ubiquitous/XiZi/board/ok1052-c/link-usb.lds | 14 + Ubiquitous/XiZi/board/ok1052-c/link.lds | 14 + .../third_party_driver/gpio/connect_gpio.c | 67 +- Ubiquitous/XiZi/board/ok1052-c/xip/Makefile | 3 + .../board/ok1052-c/xip/fsl_flexspi_nor_boot.c | 1149 +++++++++++++++ .../board/ok1052-c/xip/fsl_flexspi_nor_boot.h | 123 ++ .../ok1052-c/xip/fsl_flexspi_nor_flash.c | 88 ++ .../ok1052-c/xip/fsl_flexspi_nor_flash.h | 303 ++++ Ubiquitous/XiZi/board/xidatong/Makefile | 2 +- Ubiquitous/XiZi/board/xidatong/README.md | 11 +- Ubiquitous/XiZi/board/xidatong/board.c | 8 +- .../board/xidatong/img/NXPBootUtility_1.png | Bin 61992 -> 64966 bytes .../board/xidatong/img/NXPBootUtility_2.png | Bin 61493 -> 52062 bytes .../XiZi/board/xidatong/img/flashconfig.png | Bin 0 -> 72092 bytes .../XiZi/board/xidatong/img/menuconfig.png | Bin 32575 -> 35422 bytes .../XiZi/board/xidatong/img/menuconfig1.png | Bin 24671 -> 18959 bytes .../XiZi/board/xidatong/img/terminal.png | Bin 18686 -> 39730 bytes Ubiquitous/XiZi/board/xidatong/link-usb.lds | 14 + Ubiquitous/XiZi/board/xidatong/link.lds | 14 + .../xidatong/third_party_driver/ch438/Kconfig | 39 + .../third_party_driver/ch438/Makefile | 4 + .../third_party_driver/ch438/connect_ch438.c | 1296 +++++++++++++++++ .../third_party_driver/common/pin_mux.c | 247 +--- .../third_party_driver/gpio/connect_gpio.c | 63 +- .../include/connect_ch438.h | 277 ++++ Ubiquitous/XiZi/board/xidatong/xip/Makefile | 3 + .../board/xidatong/xip/fsl_flexspi_nor_boot.c | 1219 ++++++++++++++++ .../board/xidatong/xip/fsl_flexspi_nor_boot.h | 123 ++ .../xidatong/xip/fsl_flexspi_nor_flash.c | 88 ++ .../xidatong/xip/fsl_flexspi_nor_flash.h | 303 ++++ Ubiquitous/XiZi/path_kernel.mk | 6 +- 35 files changed, 5177 insertions(+), 308 deletions(-) create mode 100644 Ubiquitous/XiZi/board/ok1052-c/img/flashconfig.png create mode 100755 Ubiquitous/XiZi/board/ok1052-c/xip/Makefile create mode 100644 Ubiquitous/XiZi/board/ok1052-c/xip/fsl_flexspi_nor_boot.c create mode 100644 Ubiquitous/XiZi/board/ok1052-c/xip/fsl_flexspi_nor_boot.h create mode 100644 Ubiquitous/XiZi/board/ok1052-c/xip/fsl_flexspi_nor_flash.c create mode 100644 Ubiquitous/XiZi/board/ok1052-c/xip/fsl_flexspi_nor_flash.h create mode 100644 Ubiquitous/XiZi/board/xidatong/img/flashconfig.png create mode 100644 Ubiquitous/XiZi/board/xidatong/third_party_driver/ch438/Kconfig create mode 100644 Ubiquitous/XiZi/board/xidatong/third_party_driver/ch438/Makefile create mode 100644 Ubiquitous/XiZi/board/xidatong/third_party_driver/ch438/connect_ch438.c create mode 100755 Ubiquitous/XiZi/board/xidatong/third_party_driver/include/connect_ch438.h create mode 100755 Ubiquitous/XiZi/board/xidatong/xip/Makefile create mode 100644 Ubiquitous/XiZi/board/xidatong/xip/fsl_flexspi_nor_boot.c create mode 100644 Ubiquitous/XiZi/board/xidatong/xip/fsl_flexspi_nor_boot.h create mode 100644 Ubiquitous/XiZi/board/xidatong/xip/fsl_flexspi_nor_flash.c create mode 100644 Ubiquitous/XiZi/board/xidatong/xip/fsl_flexspi_nor_flash.h diff --git a/Ubiquitous/XiZi/board/ok1052-c/Makefile b/Ubiquitous/XiZi/board/ok1052-c/Makefile index 837118429..c50f207a4 100644 --- a/Ubiquitous/XiZi/board/ok1052-c/Makefile +++ b/Ubiquitous/XiZi/board/ok1052-c/Makefile @@ -1,4 +1,4 @@ -SRC_DIR := third_party_driver +SRC_DIR := third_party_driver xip SRC_FILES := board.c diff --git a/Ubiquitous/XiZi/board/ok1052-c/README.md b/Ubiquitous/XiZi/board/ok1052-c/README.md index 81477339f..4e489f05c 100644 --- a/Ubiquitous/XiZi/board/ok1052-c/README.md +++ b/Ubiquitous/XiZi/board/ok1052-c/README.md @@ -161,7 +161,10 @@ make BOARD=ok1052-c 2、ok1052-c开发板支持micro usb口烧写程序,打开NXP MCU Boot Utility后,选择好芯片类型为i.MXRT105x,开发板上电,使用usb线将开发板和PC连接,拨码开关设置为1 on 2 on 3 off 4 off,按下复位键K1后,若连接成功,可见Vendor ID和Product ID均有数字显示,点击reconnect,等待NXP MCU Boot Utility中红色显示变成蓝色显示,则表示已正确识别并连接到了开发板。如下图所示: ![NXPBootUtility_1](./img/NXPBootUtility_1.png) -3、选择编译生成的XiZi_ok1052-c.elf文件路径,并选择.out(elf) from GCC ARM烧写选项,最后点击ALL-In-One Action即可烧写程序,若烧写无误,则下列绿色进度条会执行到底。如下图所示: +3、同时需要匹配ok1052-c开发板所使用的Flash型号,点击Boot Device Configuration,在Use Typical Device中选择Winbond_W25QxxxJV,然后点击ok。如下图所示: +![flashconfig](./img/flashconfig.png) + +4、选择编译生成的XiZi_ok1052-c.elf或bin文件路径,按照图示步骤,将文件烧写至Flash中(link.lds中已构造Flash Bootable image,如有修改Flash相关配置需求,可修改/xip目录内相关文件,无需NXPBootUtility再次构造),若烧写无误,则下列绿色进度条会执行到底。如下图所示: ![NXPBootUtility_2](./img/NXPBootUtility_2.png) ### 3.2 运行结果 diff --git a/Ubiquitous/XiZi/board/ok1052-c/img/NXPBootUtility_2.png b/Ubiquitous/XiZi/board/ok1052-c/img/NXPBootUtility_2.png index 671bcb0c2e8d6b50ae43d39de568f65dc9b1adc0..a607ce8e5e2f283b052afacd1b7f317d6d49b680 100644 GIT binary patch literal 51375 zcmb5V2UJtr*Ds15tca+9sDL0IMWjSgKtYI6k%-blml8TsBVgzu3JMAWp?6SvkAU<* zRHR5KB7_zoQUeJPA%v2Uqw@$xfPMK1>7ln_b3GU$ZjIH_|b_2 z2JXB~28oVl4?uMwRiKR-=9>GBch||UM~|lOJGks*Xp`%r|KMzEo8m9I8*3{ zb2neUmx_FOoH^5ziA3n)OM>cDjo~ok5pQUJ=j>kyH+5sU=Va+t{c2VP5?PZ`R@yYV z;wd7oYcLc{A$W%Hl@>9wefo^IfPMh$E#}EuTGsGJeN#;n=`X3lN0S^a{MMvvyGvBl zj`(WDsz@4JgEXO#TK8yjw{FX5>z$n52#Z>=Y6HtiD|#flx}EpczggFDJoo8J4Ye!g zUD`tHmJx9_R)wDBXRy`OuRi3L)aF^V8c6iPTfyvgW)t5N4Yx4%?v@7T$c9dV9D8}6 zq^2ZBw%VlWNL1676%6ee5@iJ^G4Gg7a`tVRsOgH%`{!@E<$LLRbZvIFRI|Lc=rIYI z=yZv{>OC8M0PvhhA3d{rL%%-8i%*rpQ+WYc#X%t!u5?!{bMa97Zr~WvD~M&?%xM%b z*=lH$&sy>fC}Pf28u}-bsWrJR?4ZFXLD(SnT}+Nq4=ENc*Gx?GI8F%x20O!X4{-?n z;pAm?n2tp@u?9Zbu_5EenV>OSacS13(xeOZo@uo^8f;&)f>@ZchH@XEMQ3v}wR_6Y zuYuG^Zrx%V(A&02&--BO3DRowPJ;ykt4h+6$X|sQQH@ow^vR)?8bkoq z#}{9(MpL1!1u}O3tc+<$m4ObR=s4nD7;9bHKobJ-ICN<�j$`(e=R<(B?* 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 z zj_3+jqElf~P>I;3iCUh4C3}HrqFABBmMng2W-ik%LN2UB%VKlb(YjPF4DD#a8v}>_ zz%HakSt{%@#}~CiGQk>I=}GqW7{)B!Q9lK3r%GS3Qe*~e7@>G_333LS|MW!4QAZD# zc^57ARbDxP81Y(9zPr66RdS`LlUt45zIxS;3&>!JlcLtP<+)z%i$w-$F7r)VudqaY zN7=p-d*r2!7`gFMdEvfwM{5%sRW~E@BlH#7E^5=j(&T&YF~KFXjf|cH1|Ps%Qpcqpfg@Zi8X&AikV1HKlKf~=K#%JB6w?d0?TGFz*~eqjoZQc)OA?JfkGaQgUx9ALkpqj{1si*_O8q8|n`o1ys@6=*XAAzfBNGc|25)*C3>-Q(wdDw_hB7uZ zwI$~~A|BMrsgJ+8#jkjxpfuAZRSEN}yLbr_R4myY>b6d5(|;_zAt*Em!;Z{Q!UkmM z*1o_k{FB!4F<05#In41f$6g|@!FRl)HkFnCV~n_?J?i3R;S0eoKOdLnLME3UpbV4M zG%AAO66vl>2q@(BJmP*N;^%_qCuw-K5fXN0Nw=%|dqxo5`~?cu&0yMnMK`W&`a%i) zkKL5zu=93(J&WFUW%H%mTJo&;i5FR4ei~vz7&pV$k_(THH#*~|W$VW7h~J?ss{o(X zfFyBq$v+NKpd~%eBZ$vYW?6rGL`5JRLClfs*77q`@y=PwDQ|vr@DBW@eNuQa?6->4 zD+g9(hRRNzM`F+lr(Qy>XGGNi(u4f(;uW=lmwVs#)KxYvx-uM;F26{k zN=Vhmh5euSU{9la6S_~FMaEFXuu5-VHMzzSh>710Z02$HbQM>UU4_qhM%+(csbNb< zHBBTq6bfV$r!XzQ8X8NTVR3R>Q^LC0lJw;?;UB4wDI&0>p-9VsA2^Q>6RBZW6*)Ik z)}R=06k&`~b_)%BnA(!z`4fx9b9+kO_QuAITKWj++E!MDhb0I>#69AdZ zX7O}Ap(MD8c(l?jj_ej1LP#>_Uz;VC)Ddc1eJM*wN{QNcY(e5id@olNsbI4kZ{9YG zDzx$rnj|NT{wlSaDmfp~W}Q4M8clwKLL$ZL&@DDuh43^DPQ(=s2fI>?-|}0?D^}_q z@`@9z8{L?2b9qOF5{KtPc%X4Je+J@=7Fpf2JL$%XOZ!&ocXZYJrg$L- zNBB{sWHzzEIj35vqYP03qua>rV=4C&nHG2ly&mC&caF0@%nPXdE*G7A_-Q?hCPZ4| z%12an?%h?Du_zb6`Gx>B&4t z_E*#7F{866qPGzyrU?T0OlGS%tGU#Z#ha!^bZ1+fODlz&+{Es*4JRWX*tpbPc}SLb z)c>k_wKJBs4WR+;`|u zgljK_N^<#*56PacpR3j@c-tjPYwxx%bYR&2q2Rm)aOME#k33Rqmq8*SpHs z5f-=vbYXvqvvCGlBsz-hzv6MaKi9M)c*v^C6Te}}*)b7Ewv;`R+fxN-U6D)6Pjm$& z<*D*n76RQwN1bv^X9!!E9wH&pGqGBnB)XY9McMRP-yqsOZG94ESphkxJ(2@|r?*r+ z7i!5%Kk$GiS@-#@sTq+G{`_`{3TNH^cD_7~fQFk-%0TBd(^eC3d<*NW6;~$e0tdWn z@$b!7Q9>sa?kQB=923WlCVs17du9~uy7*PGy_|hZ+r(bW)42zmv3WJp^F7``Yw6=|RObO8C`CTBzBEUfT`tVDUzA6|Xi+hXNpk|sUautkX zWhk_feh^Gm{dueOs=)o2MtR0u24J@5(!bufUdQ#veu#f_JT^jB<-nL9WfgR|Y{sAx_ZJ zimpk8DnK=lxc=g>rTm!@3VreX`$Cm)>4V+^>?!(yiDJi(vh5cKhit~_{hy-}2!5&? z(!9$3^h5uG5N+}AR%w!=KLi8})5PiKRmMNETwt1lBv( zsh_yI;5}P$V+9u*o~km7oNahrgf29#nNc<{aNO&T%+A0H)~)6gWh|*OT{Zmwoa(=W zLi7Hkuaq^Fo`w&jlnnzAg&C?*oh_Bk7(Nt|sa#**$Gf6qd7c#3GL?y&*-Y0PwPJ@HPc6waA5o@NGoHsFwnkJOige(e5F)Z+VzlnG?r zA&1iV{-XC*;lp-in$$Gu@+$r*@6}?e6)PuP03Pm~xP{@c&E^;Mc&_&CoPVULg0zxW zD$`$QU(*x{^yG6SMIS_hZGIoQguMK%UJ>P@#z?H7h&f^MRHuBi!)S9+YfZo9iq^Xc z)Ghgu%E?D^an2pR!&Zc1$)+PIA~?T9v3+FPg*D}&5w}VVlauClIi+RUIR1pO(pWwLEH2b4jNp$ z4~Q>oXyhH4VN>dSc(YwnG-e1beBK*p?%iDKQ&H(@jt$)_((Sv0srdmdAOxn=P_SU# zKNBi&j#-4KY`@D)>3o9Jc$!Yy69>Mk1n5Of^%0pZ_M|YEnjPK>tu6WDPdFw9<@NF% zp#C;hTNk2@26s zRHYHL631SRCEqW$m`ll38&SZa?I0YohPW$fJ|3ASaV~{iL>iSUPmkOayI6h@iSseg z9MspRT*poE#|u>lHSAkn18?6W7@KCt*bN&}nOs&4-h^~^!{a2j?K^2Q2mZuilmnmbiAATQ zV|4nb*EnCW9daD(oEhn@j$CRepO)qI#anW6g2ed7LwhA%QfP6<@#uY^-5WnsAN5fb zLZ@snCUN6PLbWXqYr2MV{%6>?#)4*_K4~^rn8f!TOjhS@Fgj#mEmTIW3NT|u^J6`noEdCRfqJ?G(CKQ+!Sh*(1*8&%#><*a&kTYY^V~1@a7y+y*g~r0 z)#oCCbpJX^@Hz&GO{iQ?YdIv5h|-dmv)K$uM|_SSgyQ9hn;|1+We2D?+AD0HI-KJT zpWG;+%H_2#jdjr2;$#zg<`Qt7vRswKaTJ&Bkb2ClKvqWIOn#49nBs!~uN#hoM>}+9 zMoib#yQ5cpB|YG%gW7(wO-0xdi-4ONVV;)B5wcOFkRpW$tz_}f_S6H^vk{W@ib;Jw zRtiI#c`^6w1B$CQ^#(-KU2#^pw}dfK2i_e_F2`uw7O=F&Jb(g6vZ#5aOhYnWQ|~(O zdzSAPUK^S&Rda*}lR%?tymkId<3(&>6-!z&cdEV7mY`lk_lG2kcEcVuCgg$GoCw;ynv}i!7U37|se{>1f6GMtpi7u!-S2z6RURyNR{1;z=1j z8H525uD&APpJgCJR3Zd=$d7J#Cw_gbyhb31%(itnQ1AO6wE>j#!^uZL?>C`i;WFYb zRky?aau0H(9OW|Y;(E8bvhulNvs86_aUNFRKRFzxz}2C)D?*Jo$LSjL{#LI7QS00A zfb->v@t)E6w>cT2U{n_tWIs9M)K8?0VnZ}WS{1u^cdo3ProaPj_K4_=B)82ESIJ%H zfH+RIRat+hHNI``qYS+v*9GRPBt=#Z$P*XrZ&$Hw8O zB1nIoDM0yqF1mH^*5+1H=C(2X?M>`Cqk%O-IfuN?JqX@-UKqNt;VZLScgA#*8#yjS^1!+eh{b^i=Z<4EBHz~*S2 zsO=M@gGuM;-swMn&u;7Nf0IW4%SEYE0*{967XRAgJDL$CUhIzyLqfT(PYexmB!BULxmXmGlpA|vQ^G&uihkql zOCrIGc6#>ipUk7F!!k;c=#j7f?J4<$gyJ_rm7s*5QIrr_Xg)r{0ptp*C@TiOfzFad`IXxJZ@s2%x!Ym}@lW>Hm5;aVV?{0$ z3kq2eWF4cOyACT#H8voU-b8#zP5kbuZOP4$3g{fdH*<(9r9nqn$LqGLve2X1sKLjVhXk`1Cq%7- zia*9pIO|};<-&XPmBsRPXGtK$3Ncukai~VAN1fDJlr6_Ql{>04fXs|8WJ_^FUfTL_MeKGyk8Nl=k`s$5B3gxJu$W&nKx0rc(I)v3&NkZ>PUD(%w> z>B;`F#TefFMN9nRD~kQ_BSE$>c>c4cD#OZ zm=w={^O|4oM;yL>TU`hm$4j614_LK-4ngP8?w^w>*WQ^{o=Y?6#pdWi49wVH=tKWz z>wquU1Z8LXg%mfgMeh%Iz?G7+T)ZEez2PwX=xzUvI4zT&7rNXz?qFX&AG2youOYh% zTQ~idTMnKBQm>3yo2Fl6ChywgyQkz<>x$avPO`<@9o8|HJkKk-pa8zX`SZ*b zA+s(1xRS~Y^fflJKL3 z5}h?KbmB2Nf$-)rWh04fa{;qv^BuQ3qy^L&HawVN6}PPskXFC2)b!sf4!DgT-43gu z58wE?*mnAPvTW*{B>FrEXD?AWfAM3-E1iAppYVJ!gdPQ@5U%f$)Ncp1>^~QV668)8 z2h%n(A4VhZ50VVKx_!(w5CUwC(8I&N8_Wr3XoisTRgGbgw|CfD`WgmwXv5j*4ta5Z zjx^sP{8|n@+Z{P64Qm|2%O{Bc`$iW||IcdI0V98@`=6?ybz4wH4CsIJL4LgB7}#IB zx&7%~=!~5^w6@P8$HAEB8emFz0oZb6fR{uU6O$|;K6v&R_!e}d$YjqBu4)GwV<1y{ z&w#5w0kJYl4D)V*tJHSAOJg)#F^vFEe7*OK2A!h;97w@w|1c{wp3%4ZnFcsk07a4* zIej_~fk~~F-Y*}75t!_0;4ILTU^{TYM8hLom7(|Ew=~KvpAy{$797`Tp^uT?bGWIr zA)qS;Gp~I2C>=}D`8OSY)vUJ&z)R6b~_1<(PCzEaKv&+$FA+O>OBno2}Ov?LkbtG-dr&? zh>f%8Z&i`K`4wpl7@ru$T*vPmNde+31Y7~rzs>;8%fNdedwh#cvA1CtEC0ik?|o|~ zHx5TO$W!hIZMXyY*?*t=9>A}<&Zxa*|9nXMOv*3qkdfs;{Z@I^P zUZ%}jmiB)*lZ^6d(evI`@P5EyLsKvf(n1qG4?iSUz^-@;s}Ps@#MiW)Bt4&ZM&N`u z?#x!(>4Z5;Zk^}9AM`KT(+5@ndY}Gh+C19_ShW8mFguz8S}JgW1v|_~|2N`M-&Rvo z93WO09W>d02ULjCNu$8e|6igez}Z6M9;3l@qyRFQRue_X+}MIDcrc&` z871b}4~&tW3%cB5d-^yX3&_MINH;IwYd-)X1^5qOW>gsqnu&>(v38l5Wd6A*{E#+b zYjgSh_L8EDTh#b6v}~rF#F$sx|3*ptRyJc5y#gBFep%WTT>z`|dpNY49Pi%37*{C3 zx%cgqdKd(iDtOPwuB|#O+WD8CZ;5JDn*Po`X5FoP&if|#PZU;Y?-4jnAOG(Cv_=X{ zD#ksGACAEB2k3t}qF{ZJ2K^JfMwiJxc6yh1zZW!f_DqoR5tQ-KZ4t(Ig+}Bhy1fUN zuhGPCwGj^Gd8IGqE<}d0yvvoX7TlAIJ3yW@y00A;Q7J!oqd! z>gC%kEC;|WEc^S|LBN%tv#ei$Kl{9H8|bp+bR3@p-W+g*-h#5Qxx#p zMYPrH_??>D?En*D>%tkq`9LV1<*%qEw+|idwyZu+tvakj_B;2mMS=J{h4#NZ5%d^$ ztXpzeB%QD8VhYZh=ivLS5{7BfBj#WRhAV*lAYd?Oo1vXZzJwpRllf@yJEOWXBcW1$ z>$&`6_;xCMcZV$Bk3yDNA>#?uHpiTXVCK#VQljOuB*jQ#>lkVOZzz0DJa(;sciu41CUf`Ejg)wQ46Z4%mBFWD8D*{X`It?(WNQv;z3A|A4oJ-qtY5zdz7*S{SD7 zbTR`uIsUoAjxZJC|L@51|L>9W2-r>wb4P`eP(U`1gEKzBy(uGJMnfEX_uPGfSyXa` ziV`2c$1laWcWxDd+>jDGAD*jKPgauJ-68R7#9YXd_GVt>hwT5Xh`qm`fb*pcF@H0* z3lhCC8-w(>t7_G>ELt68r!N0kVSzfbM~t8`ZJ6*rSeax+mZYf}8vMOreiwK5TtVCS z>vP2fH`$o^cNHgSqrh}z8hU^}?ar(U7?UL~B`}F@9`e5Z_z_bGs(`jKSk+74&c)Qz zsZEe+G`T~=OFIFDZiq)KrjOz-&KQcnm;b^3xbt{vrLAm>h1w7$Tr6HO(8Jrk zCCC335SP>|NO@m+TiR(*{S5tcwM!g7!Y?zU1nRL_$*iLfltDa+UK{bFz3`ngluCJx zr7H4=TqKj!KZu7@KX{QkA)Dzc*ohDBG#z>D#JJ8!Udv%zvtXShW8|mpBeP-SS_hY| zhgoa)?~ZZ!uKAqy?Wh0bde^+(+!MMjdw}-QE?|9oZOO&QH2O{%Tr_^6ccxhN3)&fW zVEzF)UuKxlI^fN-lna|e&abVJGs`feQ-3Rw)5WC>=!yJrc3A}bhAMcBnq%&TonZQH zXpAUZZu(O`%U_zRnv9FVlYOZ-ZeS-mM?NFRKCa-)peyr~s?N$yLE>m9?jqm8h~#D* z`1yp=R<{Rcvo0R;fc2`pLot8V4A-(?v>|a#%_#E8h(003~WC<%4fM! z@dy+g%ZCOvysq9>rKAyky^OHB+SixAK5vMFzCFuqpxqa^+*F%USnvhuR*Fe#ey8lBF>_t6m?(2QNKlTcOyIT5Sm4Wg}Uy zT8c#Yd))pg#ZJ+p_5M=Lqc(1TmqPfP0g=vl6Pbj8Fib!WCj49(z0kE#Q4Rb9*7m`wkQtMH=kxiVQA) zjxwkE9N1TR-uwWqXk1%?eR#Ew{@`AswgKXa2tQT#zVt9Mzuh76xu*}25FWHs`by^h zf7kZ<9`tzskJ!F+i0y(`mazywalJ5xnCme)&P8PJ+XZ5_!Rt&G!AJ6Fm!Z+B1l`4HcAXQ8|>?6`R$>wmq$GT8&@D zc1Ek(!451Kdq288^?Xy{k;+j!%w@hrcC*1G%}^lSc#N(huOW8R7pI;gAr_G2`G!en$<9!fK|r_Y{(C zY!nqSe7_slytZa0FGe&nC>v(93=*uGXHnh%aVw{Lazyw`iiCLoawaRA@$TN3HMK;q z#GU#?ywRBH@;4*2(&Tx{H=~_kT4v`?Ds7>9`=++n1YhD-5G|~FYweRMCr2U)YjDW0 z&g#LP=%2E!GZ8GMw^5({zUS~|)tL6=v!K``(ejB=#5}$W%yU#)A>ew6l z7@o1cFlrxTareCHLZjM9R6N(Mk<|oBnK_YGK#Ddeihg0XEfB{UTanCC*MsfGDqj2k z32~u)0iXs)?X{01PH%>IS|z=|oR9mKEG+O(Bzu!O20EzQ+5*w-S?&Y<6QS-L!tVcP z23_-ej3XTX4umFD&QNXnpO^ocgXj8b@sdGA#>dFlg+R=kNQg16dtOVa!=fb=~A2dm-&3EPW~|H!Ay&E+gM zSu1BW9NMi>?8d1XWq6H(5WzlaS)AM+8 z=1b%UH`2{DjzpI8N$0EEV}97mkEMmz;GD*$3yG$1?8!y=F9~=@$+76{qSt$h z&@*pA#IuuJu3UCLP(N4I(OR&ReRcboC~r%l@8XS?3qdaiTgbR()|3ZULI+=&_6^#f zUpKDgKJ0~KuYW=-^MfgaSA+L$-_FRkkabesG~jvS2qkr=7-ZtS-WUjZ&XF54e>p;F zNA1{9q!xL|;PcsyR7%+5%bk?0CQN#&^V`hg-2S|`GgK7(4^__i@uw*2EyAZwyix?2 zqY`mz=EJsjMsXkVXdrS=HJqeYBd^KXs%{Ajnk$1v*5AhPM}AxW%69$h1z;go;3N4j z4vjxT@%-F=4zE?7E5s$9Tar81fA@RDP5&|k=BuaUkyBf3o3>-_&gN_>3s>24Q?pQM zCF!ydCll>fRjz*G&3yPHn)S*hg#*t~@~Cb)Yy%(hJ_ljAZSifxX+tYwb!n4U^C&Y3 zwdZ4_499TyrxhdeMEDt6B0~J&q#j}ZA{}>&q67CWo;h}PTC=J!GyRtr%E3qEL@6)V zo7jCi?Kap5FV%g|*>C=&oTmNy;9m4`hTHy5)KV+@4jt@ZGK;p$5PRuQ7bAS>RWBLB z7_L}cGZ&kQ9fHWyQTGulg^_<9-OqR~NbEt1#iM|G`t9QimgUabb&&HDYwJ?dKa?U} zwuJdn#Rrp)eV7&FU%e!UnXZ}1&M0P)wS*fNb4))+A36SY3L(*+PGf`mA&3{q+=^;n zs?;v-tokb(7Oo>FP1n!fH{fBR{FEoO6>nVb-+D|_QMzBYs z`k|}Iq{r=PHTQxnnSNydccQ<9!8~o5kjgO)daxw)m-lf_4G+rs$cEwy{bkoxzt9EJ zH=&^%F5ihn+98(T-Hd|GS-$%?bIhbG_`ztZDaazDeLTkh&?)coic{Te(NKuJ@$;gr zhtbtl6Otu&3Ofeq#?z+GR8KqKtkhZjw>t+a!sve+1957+HJfQKC{x}0y)55IUGdl8 zJE9%T?vaL}r}yG}JECc#mz+r%npmyxM@K~=#AXq%i&^(vD&mTEDcOa+P-`NI5b9{U z)pTv0lg%C0d_&&W3tD1o9;0+uv*I`(LPv{>BdCk?wRZ8Z%@IsAu}Npg-Ic))h|b1w z9K$RtApPF9mn}6RG#ep)hf6^(jsdRxJsT3=cRs9s`+jBI?#?T8@DG@Gq z`MN%Z)Pd2Nw>&|2`Tzax($xKot(&?c8c=Y(Y z8xud9kI%+I^V-5>UaEqJ#UW597E@4zG=zF>7of4Q{5rr~3nr09j{C;Z!ml&e!x*M+ z`$y=>@I$SRfz!17RW83X?&t^B;L8ikTi#{l0KIdaHgg-Sa@Xu(Bi@4+dy{+zC8FyV zMk|?%t)cML>P2n+Wwzckv>Y-$RNe!#F?nww;$^jF!W3j$hVm0k&%qnwJExkiHm>wS z$D}Krt@;Ccp+#eO_hTSv99u~|eQX`RH2cUTCR==^dDzvz)}dEJ zAx?q$oL#q${ck6nbYdNcv15CpAW@GCox1frwwQ9U-rL5cMoCewK+2a*pL*o5Mfy+I z?>9LZ_2A*B|DZ?_UG><2F^mt%2=c%IcWr@AK3q*x{I+)bBm3(c|KLup6ZEi6X_5?I zx7?jd_fs^?O&}I(ocSm8ym+G~ec*!p>kWJpa5?QRcA^BmP-3Mg8PxU3d-Ao~ewOeA z!`CWT_2*Ou$l}=jOR?%)98s2wjzL``r&*aA5m1b-h(lPL12zz;Ki(amFJ7GA+}xCj z5dYMC8+pwU2(Doa`HS<0oTVom4V%&G%>x3mgqPFXHzFduFiLOOp5|ep`xJo;Xs z@`ljE-!!ugm~|~l&3Z|Xbn~+H7k89mOFIu0PqsZ<%sk(lvKZ!U7k-2D&pxX~E0|8( z2xn*jdXh>?fNzzM5(rhYe^_axF(P#?{|;o7Dmz%ZV1$Ve=)J{M9dCDMs3PgvYNd&c zad>&>D9YWe+)Rzpl0G8aQPpIDT$>`5BekT*?cFgs0W-NfUuo?S28A@p7>BHnvyHm% zbQg@0cZM)t60|E@HpKuOZ1?&~j9fe9SLWC2z`79ouV5q^1(bnE@IX-S%(-?UNQI>)y_H z+GgUm39>gAT_J~9zyxmbku)&dkt6SO1A+ru!Xy?GR^NqrCd7FR{R{)}t507g6$_tp zqi)dfNX9U0^f-ewO^}#HlSb4^iOe<8mZGFvf2+c&RcHmbe*x}dSaY4E8V9;S=h6k% zC|(%{IGG4yTxK&Jhl{sBDLcC$bkLPEaZ*$e{dTemEhWoUdj z`&MO|maz#fCFP)arzn8SVhI=Hcy@dQ=sA^IXXXw_w3J#SmiO3zDN~@a|^*Ej8g@-;CmmYwc4`&GP zcRccA>Zbm83sCPBG{c4YO4(jvv=aF|~ZM`IO)Ee*_@FAXNV7x5-qt)GTFwaJOM?GJZ|i#xNA7?q(~ukv86KkHz=fPxnjrCvI= zSLD6Hyxhh&Jbrc#a5v8xhKgXDs$Zk1ZFfALh|2Ryq~9@Xz3T?6SaHW2Rn6TP4KqY1 z(d6ZKGyocDYXLHmuu3F9CNrC*TOS0wF;l9a5b47jtHJ_#(3Q5Buxl$$lxc3uKa3{# z*2Pztgm}&+&5KN;5;!Q{oheNc#LQ?Es35EPShI8eg!pF+%WazsGpv$w0p~v)n^kks zgeMCr%wx=xcw_hj1-8|BboMIQ|O1C>{mTMpPG^y=giTX4Sw_!PlUB;s$T_d96_u(%exz#fvst0-3~ z>%)#;FH-D~%u2TMrbOjc&}B@z5WfI?kyN#U@u2LC@O{S5kn$ZYY+xg(ZL-~DD6Jg6 zJ%{jZuVo7k^Cub1?LTGnv|snbTeyg0#_|_Q+&IPc%N| zd~!p8^?}4z40fhPa~bRWJ66L_NtB(f@p%W)tI6=XVXQzjMW*Ef_t${pc=yQR4y#MU zY45bLzCaAT46u70XE8@- z(%#F&!X>R*+|?X3NdM;oGa5gm$IQzzi%|oU%I3mGwA|@ODJGDW>av-NadICg$M2uH zmuArOf{Y8f(()Zq_KE3Sp=9b$gK`PxjR#H||N0#-&Jn ziPT#ZyhMPh+vPq-6Mh70(F77h*_Udni#>Vx7k8A|8|HkmP|bA(i6e1g&z`v21Wh+6 z#ssjHRh;0#xWTBI@KVF>qf46#;OCoIgqr}69j5*X4x7)_W2Y{%iOo%?1zuMHhts(<)w3L_cT47vC!3)Kd>bSsI{lvY?$Lv z&W?RV72rhMj9g&{=9ZuiJ7ECiSe7WS)N~)EUA6wiZ{JevUo0_z z=xm)sz@iqkPNUAvPtyuN<()jR&{E=@m++Wbf`EULaM!!~%U}T?QF2K&F>W& z&=!K)N`d?a>_9$}GxVjMGxuW&dOe}#HaPViKyH%$HpV|-!smvU!&$8E2TJ4RF76@;iYdM}t~XZ9355jSGR&Pu)okhy5R z@kCKU6j#uc)7a)21+Z#1kao6HelnQJ{&73hZlZ=o@%J~X-~-6%KuXR+)h{htOF8sJ zGwn>mX1RuEfo^&Lz|g01BRe(d-XzCPdf0qJEZ}-JHH^*={xZUB{xlci43$S>wr1SH zH;9*8B5q+Nvs3AsrFU!nuZcfJ7MnMHf*NO+xRhBJU%dwz zE`|=CJw%IrJLql}!>HP2XcZg_uXQ^Zvd?jG94c)!J8W~{I4WNwYjr>o{3URJbVyM! zbS!K?V=7-_iG?^;kD0nLN1ox9GR8m2nb#|Wju$g5O?v3Ijs@uyPq)MR(zyE-)Io^Y* z;h^!v4R0@b836(X8ygG&+1{8m$F88FDptt&01QQilt9rMn*zLu>e;a^Dv6{lc#-lg zGU{AE7f0=9strXnm%1NG#RL=wys(DFj%*oI3QZtWIh9m+c_QA{+K@d{h#yd-^tGl- zPc(F`VSSFzalb!oit!))-ug4qL+Xn`6ZeF^(a5xU{FU)`R$-w@t2uWpbTx6+~id2%JqAZTH*z1&}+u=W^HSyUq z-{)V>#~c}N-4vjoYdN=nE_&0&`}9*X!R_-0F5q~$DQ5Q|6M>an-VYpMQHcK46mON8 zOmg!~T^6rxa{DKq+oJp{w!+@;Yn8w)IvS3=R`JT<{RK7hq8osDTa+Y?j&Lq7o&Oro zc`%zW_A5~U`y(r*wPfkN&pI*`YV5haHd2Z4&-B7N6o8h=#)YDT-Cx0NCDTYkaRfH7`Zil4^LrC-z%l;IJm-gOenQP zdQKn;wNXn%h2p83hoAc#P&~)@!-qPlQJ4c#0w4Xhu@aW|t@g70Y=Ryy~w9Y?}|LN)k$r3#{=(jx9wnknTpVG^^3$#_hvkA^ z_?X{{h_ykzCTM?!{-)sj*868?d@S^oo;#>+R-53Hp$G zE5{{dr@UsF!nr#dWl)b^A~N00w+cvUC~9Cvuc+nH7>ahUyx=X=qcsuJT{Jmb?*`2cTGoev4^_DgMKP* zEMd*w_Tkv~VO^GHhIc>V)(QX6VizHUehNjUPylVriaV&s}NzCNXK=k_@P4!f)%tuiN&$Ccy8}R z>Y_x9dC2WY50$~m2IqX5KJn~&g|3Xam(UXh-0Qhp4e~p`=M9!( z>>4q(dZjBVq>m7~402~G^07N(T#*9lyP@<_w=E3*VAYtnu)L8T(sZ*PeTBW@v2 zLc*FRtuwqOQ4lq~A>p>8oMIF(S2tRKnSM(`RnswdFb+@H?KbP%t2d2C5>fN0sk=#i zKX5|Nes!`ZmAR+_V0BSWh+lG+%#Tg}Qj2RQs;n@Wt)r+(VoS>$#W<)`nTe+@Z@?!K z0{Un0nK>Dv{GFu?DH8EBLyQ6tK-VZzYV;ghLL&8XJMJQ5)t2N80kB2}Whb$ydOA;J zb?nt&*t6UVJdwXqpS#!0s zBvh+lS?B0_6FVUcp<;7)#BsS{w^> z7h#-6fP!axG2F8F*-`8F9Z3*SPz zfPyRd*YNOwm$XF8)?EGv-D=uOwP)utIVFTMl#HTEjRMttC*&lXIH`j%5QFDtbRc7j zx^mb79_$^RQ$dC2!4;)uR)DAZd>fh(HCUT#uWTdf<;F{2v=x&#q<$M3k6e{B{shyv3;L0Yv2lFC$8~DK4ErZ zFi`=NTud;KQd-^}(7~VFb+zLY!$8%|Df%|_kt?iby#{x`12C?DCfQX8WPWU$dvqz6 zZa^B6-@=oqsh5(tLa~FGHu|_Ix(O%~Gj5qF_c>|@MB;bOAGN6X_kMU5CPO)51%1+BMfB;0fHOs?E zBzk>rOzQR=67uA=hSyrb$T_k$r!VDPr>8rLsWEDfq06rc+W5>x6Sr?r$`hFfk&~TO zrzWQkqe)YwB1iZ|nVVxMKrIk7DCoXHUbNP35FY&-5U zu*%47&fYWIIlQ{Ehp{%GcUC?AdE7M_R)D~HbYGyiXmvWrrkhH%i#3IKbTa`p1O31~ zO{(21go>xlzbueywxrk>(8^o~f({(bnhSjShe~680%o~1^jgbb5j6x#7cJ@j- zU0otIyX`y2N4)}GL+QfE?=e#nT5mt}$@FTmIo>Jnjuv7oQZ4+knK2y=R1|ci|KXpL zl2l@$9_zEl$i9&I&d(Pg#Daz&Ui5;g@#YsMGh_hL&QNany|$R$x2r+WeR|k`iUuMN zu)_Ro(st8OSB|S=#>k2KXG&lj3{i)1MHL5 z6NAl&(z*f}E~`WT(CfN-E--Ae?~^(`9XlKQ=klt-Tps z)C+1bL;7CcG3oI&e%?xxa?XnDj)l@Q*W?^|%7g;*M4!W~dF+GiXE*6jin}-fTsd3QmtJdJ- zmptpmE@_&#LIQlh{9?=diHlxS>i6s(V|>#W^`pk^JCW8{rE%;_ZR|B`*j2HoXa6dx zNB7o%^wWBtUQCjpu-|+IVoiQ$EfS*snk)NOZwvFY-_k$cxmW&db@9a} z$C=^8F(LcuHqmzC+;%@QS-;GlD8Yhqxz{B$nQ-*iu zGCo%@O4~w#mLbtY(I>tu@Y|*7DTL{UA5=WE!z{O?er@9Yh~jOl0Q{RxnYIQi)+T-g zO$f_ERJVLuGo1Eset(T#4$oAo&GO*Z(M>yccc;akSsqn1UH4~U3o z(7ao6?~{Li;sI3RQOxm9( z*pmv!T=lG1z8YMyg7HsnWQF8ZPw-9$D1aM-g!mBCK~JHIik~&+B9_Fwqz%@chI7K6 zX}hvUXxwLukO;AMeNlXZGgR2g8=HKAf8AEN1=lPw^i}xqaN*Yy$B#hG7~fRP$>B$C z&HEgZ+*$U%>CevcYz!j;c8!aJIq~QD_-6>804wSj=s5EGk>e1af)?K+N#4OwMb>^! zj;AWMI45KA#inPz0$(IFgsZ^5nKGPF>kiPJm^XHnQfe-sqpb8JZocmb0ka=SF@M(N zE#}0_DKO-eyuQ7a^F`88g|tcK*;X#nwE!bewZ5Kvj?qorgNb+V3U{6;%mD=OmxBsD zgOFK<-xHlrk5x%ga!^fAd&b~08usF@T#ad~@L0Y$m>9ZJEWRa>20%;kNy`I9CQw^A(3;JsDc+xdQ2-5j{N#+5AFK6j=% zCxgC?RxL8Ke6LzgzU%m5<+$zw$KQTc7)W)iJ0!fMJNcCb?7qKa8<{r}Vsp51si1Zy zxV8+f{g;tvFT|ayWL-_$Y(c!+h+lY=F~jvziAbp(ng0H|!qw8S(>0`} zZ*_S4t7&1cYf#XFGhyJ96@EPt>qcg7(@L@vi{n7sKyoyUyLyF}U9MQa81eL}L;VFY zt2K z>vl!*XMG-v1GK}~WX?yKE$0fjzl>&BrqxJxEx0_&9Z9ni4IC==w@<`MYTa-dnRXI= zjxt?8!T#el-Ja6@hH3e{>Qy;KufO`{>L^7STuwlYRYz011}qEy+FLw8DC)S2|GlSN z-uSDbnp-0;k_x|}@@OD^aaZ?V9FM26%gsjHp-$Os^fk-P&+xKCSRbMPp&fC??YOV| zn{w=58AN`l&F6@<|j z+!k&5<6i}zS-y4Tdg_gizt$sgDhSc{ylIRsyFx#_Kio6%Dznwo8jdeEF^MWF(PyXi zRU{lOsBZMM#lIUv5+?lb`hQNFbc33hYhkOM%C=Q5rgv(GIH_c`&_A#(S7EI2h^G4} zLd(v0KsbSe(A?@1SiQ02MvG9fAG(4~&Xj)#N9XiRsvCJ4wG?a3>a}W<)zLo|V!Ycf zFK}<(GOxvRig<1L=3A|PnQU7z=daOR?(ThSNrHG6}d>&Hlp@{Pq z@sTjZ-29uJwzmI6*@rG+PjXwCj^;dln67d#Cv8I8_nLqFzVTy&WL}+_P);^u0w78J z=1Y=+xrD}P@79fCMC6C`-sgy)eM6YsW1H?S0qu`OW(T=Agu&6Qx{%rR>Z>5u8P~_u zQ!05!ms5yR5d<_r64=o6AqLDCCN7k#f^T)ZmgSCB{q^f=Mx{m zmxLCERYwQqvR8cjy_Fvk-3flMKLn!3%@Kh7Gg*3{Qx%nnUH@uW7&&!6XZ7T8Wa6qO ztm*33=mtS8dyr?8EOLvgt~Hh}BmawArgDLn-4h_*yP}@Gba=aGz^6Mg!*$HE*mX=Q zyh?NdE*C=;Yig94yc9HW4Aj86o9GmHf%N(91gfWLEcXt!mxYd_jI}#rrtdsH{>{mp zqIU{$A={`VefyKFDkyIL7;3<8iE#V__kM~ z3K7&admH68OgdhKX=8jVG%Y??m$2{XhsbK;1B^E{ntbuAR9`3gxEZ`CBD(6&Mr$Y3 zzoOD#Dm}BS-Qa&JaC)XG5BYq`^T|gZg&QLrB!@# z(_oDJ@(Bztl#55@QcjOx%C)EuA*%m1@k3|!y-2&2`A}oK&y3C+sZ?{$eWxj(rGRm` zqO)_pUj8TNYMoX8ojaE7`x%X!6Nr%K%trr)&0zW?;`IF6!$Tbv7Gh5Xp=G&vc80zf zD*R4%`l(r40{WQAmzGS7bouBe*{h#UNCzs4T2wj%|@3;v3r-5RTDoQaT8$5saO2ci`UE;M* z(o1(1^0(7_qDPAZHAEr?O9N*pvQu=*@TwS_cVI%^Qmy)f?RUAEJ0k;3PDL9#b1%Kd z_$Q$3CZj6=SxO)jxvNtw>KN=TK8x~RVuu5nbVhTpMl~_|$@{N~_M@^JRSTmtTfUFX zR0(rq)fWHJJ%Imzob9MVq9u5t4^n$huWx{7_NDRR-omJlFSuN}>7L)wr`zG_XSW;M{aW54}9lb@}h zG~({k*p6X4stuWkpVR50=?t5;S`dVd4?L|3EF4A9zGPPm@9K(f2_EQ2bIvvF^|;sl z>+TBO3{ir}Jl?4-zqR50N_*z=R2|L)OpD3J7++bEN_oABknIT^^2KK$qRE=>_i{6r zG-tOWxSpe8c@M-2x1-*Ae@INx26?v>0n3IE*%kE& zuH8I&xBr$a$23Rmp?gB~Qhi}|oNtyZwCP>ftkXH`6Dg;fCc|1zXr zN1}LNlUNpO6L-CWsvd!7x01E1$>aXmv{V1jEl1Yin?O7oXbia-INUKF+!K#1YGV5HF}f{a<(*c}Iv};ertV-q*i# zCUXH*{k=ZoT*Wt#$s?QjJ1Nrn?;hBf&y0s(R~tTBfE#!bL&i=^x;WngSg}|u*xtw* zr4UsI*l{iYJ~5S(R4?fWffvtI1v`%aasD8Eq6K&uB~z5&BcRB;?f<&n>pe7G4Apdj zu0{Y&xJRO(#%-URRF41z{ADU<$UcYLi>136p91h481Q2yd>JSkbToen-ow;3JSMe& zVQSC?@T0llOA`{TXSW|jb<;Z>QO)I#Krc=BsL|NNcXkvp=mkWKF?z+UeD zv`P+VL5x=)D zlBgBF;~LjZ*18Br7B`5@Bv;tW>uUZi^V#%cU)uI8BW20jo0=Mz}c-LEfKGJMl8ZV*<4zD+mqUsIvh)^ zHqgAakHu&If(VFZQm@btc#=o*PmDX$>2ex4M4pjvV0YWjzCsc9Cv)3AzP;cmCsubr zI4i|TFXoaLZs2Wa*Tu(d=*D29%x&I5$fV()gXY8S3nd>F<9Bo9gIfvuIJok``ujY# zKbW}-A0qvacNd6w+F+CK=XAQ=@4+eDjL`pRBNjF!7v>WtEE#71t!`2RSx#`T9<}iN zd73orlAAqsE$+`X;m;A!eF~^7NkE6f?++@l2uUQL6XHJ_BjFujU2mCa|3;Ku=1t#} z^3QP4XQHcNNT~Mg>U7qN5rz9vhwhmb^SXs7ft3z!#P^uuHC$mqk+&|-4OcK?mGgB}KUA0$;& zu8kZNw0I~74}6OsulNzG!F;aZ&QVV|+*{AFU&n)Vmb5Q`+tcWxifnLA6eW@3&l zfV59KS8=VeZi@7KUek9=IJ4~?;3mRB3nImb&TB#{as?<&zZ(VjQ#@ji0xNqi%-?%2 z6UrOQQJjP$5_sZR_5HNslNNG)>mwiX1CRaGFI_>YXNJEF9C+~{(Y=+##%^;Hpf7%$ z7vUF#`=XZ`&pXdpJT`{qaHC&b?v;ds|TH`CuXwkUgkd>)8(5br`8{Wn4?#2vFWbMHQ&SOxDv}>;1Oo^2H%d=^= z4IKOBbg{Y)vYO3Wzr!^dut2h(4rbc8e}y+zm}+~Ju@!>F(C=RvnEsjdy2goJg5acj zZ9`erM5~fD=1D+z@$y&pn&JC{-*3T$_!}kI>vs)bX_EUwd&@0No8#bo7+ZU?(CpNKW-Jb4uWhcrQO}fAxi}&axrcKqTfB z*sUupPz$R&LyfPfTA8u`Qn@BbmlZcy@lN(GTJJAOKRe7pOH8Nynr=`}vA2!SLb`2L zlzuLL_Ig807qX*$oWn&-Tui8sD|As~v$jO-%#ri*t5_UEgc8JRfANpKcMUwT^1A}~ zx*m>WUc*7{4(QsBlYzO0M|`(uJ1(T8w}g;)4mkHEFBYf9@)z0Iq9c*#@6o~CPUkO- zIHq{KN_>^H5KtOfr@BI1fQ1102k0PY$1mU^LW^E!*c~mz`=EQ@0H{g?-7fcCgmi&6v5=5?+jtrY>;2dKSa#=Lwl+kpGC0Eu z??Nrq(4!s3D~Xi~|ImS`aF+t1?ic7EG2 zwaM4^gqlni?Xw5!EU6$Yz;VS-@rBcK+C|qm{+Vdt0LFPE=XPmohZ)Y$*=h4u_p+fz zHE&sj1vVMn3zN1S6am;w2tVRnK|6xbTH|p1iwMrVR;R56AUMV|%hkup9P@)5%c5oHal=~@KJ zG5pn;$XrrAssXI8tYNcj!4yXc&sM)`RbIlYX9#1G<=TpZLzvR^;v%V?Twf;^>Wtphm| zL%9jRQinQTd@a=L29T2hsmEiVZh+L0(gAUcqk22t^TR6A;>*+H9-UQl_D+eZI0H1> zEe`S>Iki*X*$NUWH~gOSNas6Pf^y5GU!|qEh%dLmIe-9C-SD=Xt9U4T=o@JEt^!ZN zwv@O%&Qe^0`=W0Y$CmPH&FGAy_e6{xh??}1hfn*;(GLjc^o)BsYWGP9m|Y>_Mnd|+ z{u-q(p|6hJ6&l*GCl}v`AWyZM7=c(?WnpkG<+mFX-7fefj_A!8Owt zpCCubhdCA;m)wAdx`h;z3l@08a^#3y8%_k&1UO+0Do#8TP|2eXHN4jpEYb`?JVJ!HP z5dY6>|I7ZrG_p}!W4X|c4V#=CDlR7{BP8T*32=t~**i5Buagaal7O0!8b#d?2z>&M zoy9f!4(k(HA(pGJ)xgny;kI%h{US8v@B!?}OW$%Zchze?UG)$L{2NUC=9u5iXBO-; zAdNb|r&=tMD*ZHR{{s{RIpeG7@x~i!+1~Zj&*7Vzn>CfHIeb`Xp5ko_X4aFZm)`!R zBYy#rryhUVEX$nnl^=A*>wuSLX*i%eE1omXSFGislWTPfT!=sZf5@~UTW*CNf7Qax{sVL)#=kq`lQpAP$MgO(p?nchg6q$FOTw&OvbI)%F@9Q4<^cFKM zsU`zj_z|(^eo3H)Zzkz&>#N4|;JJB?qOG5D8ctsX54?Zkk5Jade7 zbh5L-{;u8ZW}41-qrNJl(lcZ0wlpVI@Q>=8?hsWebuRY0zK~knFQL6t3m0ruuG=+C zL<8<3a$?qpi^Eu()#m9(e!ks61*CT0EPs(xr1p?lL>~?ebI{}8QWNEG3{yNO zhfVH&Sc4Pd+g(F>us1xaFo_&f)t*EcJ>@U@Vobo<<>LzHllK!NM-jES@0?g=k`9r@ zr4ed^j&jph@3FJ0pR+dC$Al@Vc}v?76myV4^q)NWvG1?D=F z?cIl0%GYPk2M$#&JS=v9ldvZ^r_1{ZDco((2}!KOC$N7`f-zgdvhMeN8gNcTL0e_8 z(vF7YNiQ5ga`sM&2ng|O=VzKt0gKtnlX}m+#9_ z6v3MmF6EP|U^%|j3vxfu!Gu`%d-nNH9e}6p%Xs~_(-s0hr*%`+vz^TY%@QSI5Sg-9 z(c$v)iTIXz&CGDq!F1|+X?VxW_IG>1UiIi=bmYO^&gag+d=1XkQ#{dDNRR)++k3}T z{r~Ud$CeSwo}oc@iHM^ktE4pS?2dg%R>nb&R7NO~EtQ77j(t!WIcD}QGLJ*{I1azZ zp;xc^yx*VC_rG8NxpkiFvF_`-u6yn+%6ikylUl6;D(z%$hHp<%F5PuCvFxuKXK2CW zyO#l~C+G z_XNq#n90s@Ib?Z;*znZMDOa9>v=QZM~@;M%BC}$5N4)=ki|HlT6a_AnGqIQRZi%Tr=GS z!@RjwRV#S1=Os2t;U^J$)aG*sg_mx;5Zt(kF>DLa{wgiBE4-1z5AmW^u;^>8*UuhG zq(V@`M&`?@)z=hK7yn^2my!;pAl&$@|^HE?}|i)r-$-pom@ zp4vjn`P%0D%-%n4v-lB?oHI7C^>tMlgCMz3s&RN@=jrNh&E(ibE=(&qaacTW%~#_Y zEdS;pMVhUQh_OKDquDN-vz*66>^&(bRf)NQMXgCcf3M+>qfCkO@laFYuO8!VIvZQA z2{}`7>iS$_=5fm#ewltr0^`e8ekqM{5zE$UuBN6VQn}alS*H$cEhk;vKR1Hq#o4x} zPLglxEiZEzTq;*Ec_}Tz?!nVE5mK9e+3sEpV)}DUS|e+h%OwN+d>}xzinHFo$NO47 zuEd7@N2Hpe&3R`tEoBvxT4B6Kp+qe5^Q$?>9mB`|QW{by)e;sLYrCY&eAw4^r%nW$ zo)Mvp9(qKXn&EQOSg|cGqSM_Z{cKynlgG>Dv1jyCt~hR0UNVY)O|Lk7l73KlZ!l-I zm@{)zQ>bsG#r8bU4f+A4RyWh2YK7JCWoO30zDJ$4*O2C`sbt?;f+hN`8|ZB$RwkexggAWD5e zzsA2|pz{TZH+1tH@$kTFyVKxHnn)01hAi(*z`#_o9k#K?5$ckhiyoW z6UC`CeD-!#uAQo#+&2c?hf4LyvNg#E+XLGJ{EJ@jeZ#p=WF9GFSHeLEch~P7n0Bd#Gcl-id`Ha8m`FPpD9}&1>heMCGkhYz+$_K>BTMg!RKvs= ze3M~tKL)Wn%G^7ts$9hoTxfxYR;W&s4liaU!bIs6uA;b^^|pa-j}MwBj=qZ=6hqxH zFBxIt3+pq9D?7VQ=SN?VMQH;%=_FqY?U#t^DXVBeR+(aS4;4+TbGRu*Z#f`#hD ztkZEiATe+9r&-uk9bK{W4SsB|N^-{oUnQF&9 zULL4F?P_!_skbOgr7a#L5&%maXfSPZ@yxltTB$|lo@uzXAK8!`;)B$6^DNkt z6cv95p-8*Rtjc`wAB6|3jNlfW*OO>Sut*pg3UDv~iGm;&m;a8gNI^I#sQeS0K< zPUb=B6aT+l?k#;lS{Cs~{0Wgh{&y6%7pVvS@}(dU>%B$rzXY%U$Hn|Kgb7VIP`hCIuaGg@bfJG+N&g&mb3 zEqwDh6PGHsUzhGrZF~J0HWc|w!Qy3V^_`K&K?Z(fZ!_Hw3^6B^4=~W-uDX8OYQ%K+ z9%&mR%CCQky8*)66_03(>xn%OTo6cQs4B}I1K{q(afqYvQ|l(ho{%Hm5M>6skLD7} z;K(~TdrD*;6zDjkJW)cG>~Bcf9B|z)fT=QASM&f`lve z0i$z7?Px=cM;0UO1DTJB?l%CQbz`QkvjO<@X&R7ypj`z^`uroUC!#n*o}il)E9kTu z0AQUw!Vua5p4HAoLeI_uQcECnH1Ca`d^t*RI3Qs6X;4;|6oC}ppc{>CQ(gYd&_yq& z1;AR(xiyjo=y3GlrYfXD>$`^;F1kvs%J*E_hSyo*LS`a;^9HeB>F06$uJ4<~aID@; zo{^oa>8HwvwFO+wyKU-G0&kmaDRJTh+1;YvKgXLCWcJeRn2 zE$3=UnOD?vGlRraBHUQlDx`o?{b?OTXh^6@b@A{5=ada;rTZK<0y(2Hz6 z3xKfP6B)GBb@pDXiLQcY1~ z*Vi8rn$9oQ&et+MVGM03qZwUinZI70#6vUkD9VTa#+a+oa6(7a_Xw)toS1AbL4Ko$ zX10gR1i$y1zc}(-lx5}cb-};pR+c0T#zp00WXwC1dd|CPP(yFBw?ewE_9v|VK3sQQLv)?Tqtr$+!}T{SRBuCt)hV6Hw5*9pAoMajL@ns z672YTm6PGG_0u3HG8UMto-vb$M5WuCg7?T_Kp&GDn#>`#Tc2z3mwiEX-7`C$6%=q2 z)?D)1mhdubS972ZTG*Yoh_JinXV%~^fqn+4n_B4J{@k0scW#>|Foc$SOaI6U0lBZQ zacD%6(Hnby}x)29Q{rx4~;{p?R>oDiwXoHx{fpf*? z#nC*18pKirA_XL8sHoLB0D@8aL{8~^tZ}^QkGcLr5sKnzS%^<>7^YXl$-MgJR=th0WNZTY0HQ3tU_0n(AP+7 z^P70~2^5m=hwTSMuj=9}{<0wd^**n!{^L85=o1SjUFL5PT&!Ehe87p577?cqls@?R zo(Pumv~2>%Opdv&2l0Tskc8ziuA9-18xW-QRG_P?46Y*?_ z%;|J}d3lL+Bi=K@!agUcj>7&rU5&n^y}KBs!^gRHU?>byw&s8TWIKZ>`OMiy_A^D# z#OC8<1*j*ovB`v&ZJwcH{)U5sPge@6dWh{sE$|AO>+%P?A_6gJM0t33!`W<1fE$pI zDn(^wjg)-iVO`yt+HQVoH{~2atsZiU=Q8SiOVGQo?h0^d(f~){_n5&ptTvYmbi?S- zKzL(gZ`Ii+O78(X0${Dy!-Vd|ejA`^%xC#Vv0-IIzFPT&u1ErCqm{mKYWJ`9c<>pn zT?AuUxzLH`e^pIg#wAQ>mX$Uh=p~50N_gDS*V|$h&#FVhV(%F(15mu?_feMo9ucy^ za!~y?GVJ)k3}a}?m^J^K3g00F(Er`IYyLO=>rQ3+V_3M&6dwmmFiU=hu$?MoEBcWd*HMP zI_NJ`YR$uXNV`d@<62ZA)dO#7ZkiM!BGwBdvg!dbqKwd!PJFIn1fR7i=D(GqV0XZQ zXHb-FRgCss%Mtlo0XYB0uCin^m`b?+B(ei%-_H~=m<7dd1#K-VM)+S6NQchG)2<>~ zZaW!dKWS?t6uld@9gQcbjK~@31M_(RH}{z#G(%G?5a?Svk9Zv)*xlWKK+ z2fbr1G>2|SKN&D5KHB2`fjH>$4>f>xwOyPsbSarx2Ktj}l+bQ*`}SvQb?Hy(HV&+l zL2XQzL*zakofVr@^HIBd*{ETF8MxjhR8%R)Q9=g%vQkfqPyTGC_$*@AjOvuuX@;1p z92`LT@ma)@y=ybMT?g}gS?_uG0r4w{z;pFSv|I+5-UO}Mb~Qh~RW1NOk~{mN@EG^{ zg`(Dz!Xql`H48d{Zr*vR5(c=XO@0J>3}PZO%y82z41aQqHBPidPw~0S)fc0Vqw0j0 zHiox9ptFx|g`#bAr=brP67v}7YE{xLyT3FFEbFc=!Bsw`A2H0`$rOZ}rW%;9-?7l` z3h3{JUopoohn}yZLnC#aIaw8%rACU=1{X?lUgYt;d}rQL+PLLv+8sO6gC;GkC?Na6 zikCp=Tc2Ap;y0+s%%#=gtH>R^uos&F{srm00Z~F8!(RsIY9=iIb?e0_PR$VEcEhbR zJw$6UxpN7{^n=BWD63WrU9CVqs;=3TG3>)gc@g1{(jqynRZ z6Ip)BCNDwCfs<9${yOFHh;Dya>B7x~+#88TGFSFJG*D1e{d-mf8AHc7ym1C=2uE=u zH*P1tYc#Fz0sdoJpZKzjh=s``E|x^m3Q+F&um0odEZF^wS8#^Ve1{J2eLwQz0h4o?^vb%k^^WTLUr;q5W_qKaPEWuwM@BGRO_G8&M&h9 zCw4|wq^nqD&pYSXV|sLhi98qhHK!CEzpRTWjh=_OhLUsBjaJswc#=*!w2@~|q zQNgJjI2_6``t_gQwT#dvPtZg;c{5wUg;Dh1ql2sYHbS#a!NxhHiKK?~v3@TGWYb9y z5)cwyP5K^Ru z0PlPt2?F61{-YoVvGDfw-tk8(!ea>jmM_hae1FKDw7vt8da-P;w6->g_g` z5!j?U3e9t9AlbL4+NLl|n0cZU@`HCuMD!m;-DK(q&1v6`#d}=AbKG~Wi@xC1BN7*) zZh^5FO+JN{gcW?-iW%%h4y(Xpj8Y08YO*6D>NsoCrjLPF{uQ=bt2ICNU3e%;Ce6F< zDp0v}u6Y^Z^hX#D^lqe4=}$OWH4nB?V=qvRj0E9Z z-yb#6E^OL2K30#95Cvh{&tYk@v~tUue13NZo{!!>e-G5V;6F{O=wTC7_4h9^1v$Qo z)bqJe0V@l?f=VCtckpRbvpB>B(qZqXt?lGC3Ks7g=}&17Ug|3V1+Wm!6y+iE;jf(bKr#=>19{)7hL3oM)34_uv!Z(da-r6nPCbDxIsI9m2_~ z?Rc2_f&XVqmOP)(Z|>dOv##W9oKhkb&ZeU$<7lv9r(o^)!S(>}qqI51g51#<{u0Ni z#1mml-_6h24>IA&{0d>;SksZ~7-*Eu5Th;2w-4X9)GY zlB@{NpqNwKOEXGq43M7$fMXtpP@C&T3Z5&R?-%a9&m6kdV0xo}em}!A36rI_%CXQb z5|$5#4~*ztALg7=Z11Dm2d#t`QQZt@gMeaOf{~|FheyC=?_J&x2>XV}=#?qC@}^b9 z35{9%@+FUWbma?plO(FBu*owrVm~i={86*oyEZFY{^A#+oUHiE6CoWT)n%a}r5*JM zhK}P5SAUO`Ly|RBnad2J&+54OR90OQ981v!_<=V4`48SonBShrMVZ@nmdDkt=eq<{ zG-a0$V^Rz%k**j61GDB+@HqH8&tEtpvd*JE3{-CE5L7x}fN;T5M$kyTAX?tp`XJ#B z$FIZSE4wbjCo4*Yol|Mnj)j}G{0g4Wkeb*No$zz9aqCr%>I&!W>^9R)nF&S5i80J= z%EFUK|4!>zxQdP3;lrr(c)fadopsHNS3MTyjMqP`AaXN@J`h839ukhRMpGY%iS(d` z2+${_bKkC^ytZyLCF7KiJ%8%dp#90gnBJSYqNd}^YUPzVpgO67E8ltRMIS~@H`zCL zln+(WUwRk7v03r?r0}c8HXJveW+5STDT9Gp{d8uyL>A$t`5Jso$rPG17&E!Js=gDD z>ij$+E?breILMt@ibAQqF}ZQscV+ae&~P0Sv&#nOb^>-4S|dBW7pFkc*Zg?ydnY4A-YJXGi8k+@u7fANT^1m8% zFn*H1G*Ngp5^HF`b=v9SFZ+cc(_1%uNgJD~N#PFR*`3I6Q26kS1`SW+Y4)SVN?fM8 zC^Y~C>vEf7a1Qa0uSqSSmhYPCRmI|)mJ2W`Pw}El%il0b-A81(k4PXhqdW(i+$R#> z5lu1|)?uZUeIe=?d+LiOVypH_|E<*N4z_(PpUDOx4HLPwtGjpn83#?U!1)PZP+;V zo>W`}XPgZswfeap#`9A~U%eM?!_a7=Eeskqwj^YlA!d5Gq`Qb$d&;Z0BZhkL*uG-| zj`nV-zDUr9am|Maa=tzqtgCLNG#@*5Ag_JmQ^6`1i>ryCqYDcs6NA*cZ=UY!E_%lvqK?_%mifD!wQ547cjHTd9$&X@(2hOZvVt`(-(1k%|x z(1F#j%GieDKQF=6uWS6Y-@{9S^!*DKaCU9*-1o?j>>OAEw-koEV{)fDw{4^KMhczE zpW8~%`zY*)hjyy_D_nWjR<88t^y3U`87}Fymxsdyz00$en0sJuFiUQCF4nJ}ytk$D z7(zb?sP=kxlW*t1)vb@@qTh@4Ei}nLTE8|3jjH^~!j)|9*NU6<&uV`wlfGAk180iO z^ZW;9Ya8yk!p5d&yXrfI51c<~fA!-_UDz#|otc29sv{=0@7}<)b;5~LoTj;L1sM`j zHu0V-1%B-AW=Sqc^b#eg+Bpu)z9wNp|M^CD({IRxtLt!&lq-eph-t-i^!C}Su@zsf z-MOlUVvm1K^-##H1k%Z~N`3sl&C7>whR{=I&Fj-1U4FP0C@*dzFOrTH(w7&hoMGl+ z24kC{G(3ggQZ?`okI79`p1e$}=bz6z5l5KPU1)gB$r>atI?#N8jC86tu)8LRNRBTg zaz}4VZ3+!P+2wsZ1J01Q^2HqE%Y)Dc_ld`2o?}Y|S+td=tAVDbse8$b z1v#&l6QS}FzPJ0QJlr+HV^^&o*(aehJHey*yU1SP*w+r-1a$t2({d_awm3afB`;umyd_24$r; z#&h*B68lDN%i>LLn2XooAdSbr76woK;2WxY!(cN=C`xka$(${jy9909Qd5n7II2dZ zvA5Xn977Zb9wS(bMIAwq!rUvwxGg&YF5s`+?5dmiC)LDSr+-_Xzmfb5s{{C7+e ziKE6&>}+j)^9x;W(E8u%cK}=*K12u% zNDKNvc-IEW#Ih-AfF!CjdyP-W;bc}zA*_dA>;g9OU_5w-y&eH0ffh$A{7AVc6> zftL%c&EKzOl?46%?ZU~D5g#!ph~wx2X$62s)Pl#)fK&+Yq02(DKyw=c3E2Y!kLn%0 z*JGo=z(lRC0?-kV)8wSYgN?AF|MuCMfq#=Ff8Csub*46L`r#g`pK1TLcMDmJYLC|x z{6DX-0AJ<~(3%I!`X6Ok9JVm}H(z8@`x|KO#l{f2VxhZo0Yrs;6Dp@2sO%wbRQs=1 z5Tp|bXZa!n=X1QU?_T{g@S}RtB40s7#RS-f%)e!9G)6=ea0esj%BrfAO9IY5#Via# zhjc&{|AMoekXRpYjJCk{`3f)d^+(zfm4aHs&=u9+Dd#_+;Yb6ZK)W|gr%#Kp-`@;E zdYu$0B>xhG1N^*okKLGSHr_x$*{J9YyY&$xl|#rW?YzEG?8=*&iUsHKuAP)~ckiK% zdjLY<;gFM>ex8B|Cu`yP|NBXWvLb6&-xu8=ct>%(jv&i4H!{BQt&9OdHa97a2|Eht z7Pl?2g zTqOQHt-tfcZT`#rjxUpYqzYhhiC-k`KTM`959{0E?tbOjP@J>Ok_DHv$d=&8H0CAq z1$jcI&$+tXsbc?wpXV6qUaos#%v9j!QnPC_+Pz+*ZoZ$3q-}wJ$YMU2z$^U1z(Z{(bFEJQo*C_x!eXcJl^av5(_uyO!LVy4 zUS!mDcN$wXSg8}<9Ejqf=)b$-Pb0&4_riBqCQsjWWN?LS?hQ`bfm@BsL_lZK>W4bf z|68inm$qP`@%_{WnbznLMMh!Vq3riz*POgV=b)oAY%KdhXpX`#5n)%=jPwtx}|SK!mdwds)AI96!%qmOhq_YP)k8BMH8 zi)49v7+VN^#_ABzN%q$fZWlINk$nLK)r2j*+|JC@Y`F04#31~1M_kSVW_|557G!6B z&2Q{!-VYpCLKV{Dh~goo;RzkgZ-L_%ga3qP{`1&jx6HGp{InV4AE3jyyi`-%)LAGX z>BVH=F@U;gWV_iIXfGLzn4p9YED_gY;2%~dcg`SgETbi(kVaC6*RS>>W>b&vTnU}9 zNR{)`{O)Cv^~&LGU8BA7@g);BZqt`V5ON%=+IK+PA3nLziW)aogtc{!zi1}d?A80J^zs)&l%8QUBE zoHK7*aeIShz@u6@57IB+qY@)T7-T^j|ID9YC19sLtTx$F=#dq=Q z@8{@jptol>t!3fnAN@m$_z%}&M9{g$n#}Ly*RezM-_D9`wYxVP5=uWL=AM{;X6#~} zI%fTT&iYPPF`xY6dI2wqE#q(>V-tS7?wo)7ykjd9e}aeAUKX4*CTI3li2=kNu#&Va zI4FY;^*w-J!DpVoO8!+CoCP$WVNu+@Dia5nEzqxqAB}NriRiX$8B^A)o9IQ|nmj{O zRxlhkvC6+_Saf5hqZiRva?;LO!ezy}8&7XP9hXymS;YifZ%CY**47|+jpjz&36T6{ z{XU*A96tXu(&EQL*du}3pOJ{$lnc)cL7ul7*~k8GSp%Gv>Fn>QVJ7#LebX2i#%r2s z93DQb*@56k}pfWfSK_c!UW)`B%4A-n?F{ zSm+PwVtPVK2mV@*iHyP9Z=Pq^>-zcsL~%%A7)CfcI@35a{&Q5rX9FdfH1zzLlYBxg z3h72#(kc%a#i^{F(WZ<*Z0a8w$_(g`^T?2zw$mVI!UvFr-Fk`!ZmQ zVT^@}vHdB(Pdg@=_|HhYMz3v6fgDlW3!kIyg~j^dc$w)KGS94mGYDB#L{dl zbOb){Al!O&jI{Ho)~w{TKGX3^Wff4Prh zeW3ITeRCRTtQ_D6flyumWC&>=^Ixb z3=bv{dDagWUWT~<)CrgDqGAlJqt9QnnmuOl<2ey(3e-P?wU>eWH85nc|KcA=&S-do zg=c+6bU&NVa9wj;MEtRxH_wzA@Aou_+%#FtqEo35d~xwRz==VRW^!=Rf5@KaVyUB7 zCOnt;y;1kvYEWX71`Qm3xxkc$?0+sMh_wE;^)Y;8#nIzF!PY#d6&y};a`Wffofa7w zwf}B=R)pd+535cg|4E8h_-jmO=wCOyI{AAw@4LueKq;51KkXwzuMtQ)-w04TjS%@~}cI3Q4u>e@K*pi`S|eULJ6?L2WIk0+fP#u{}4d z%DwjOpq9oXGjoOQ(!v+^K4ic0_bV~+Z$J-$2&=b&t_{!(mhSg0Hqw_FI%VH6(8&nS z22j0*YNs!l(}6ecWB;8?`14X-u#CdDga0UklH_d=x4;TW+|P$XAP%6)dHKnIU~Iw2 zLEe#woeIAo%Kw6Nvn?QyG@uSz^Wzsp0v!WDO1kExcxLGnk86b%%w&PEju1*q+Lu6xHo?+~O!_(r*(U zPD}w%iX?Ffi2-gk=_e$}QbJk1o$iniE*pZfo8AI?{R_dL(X0@AVJ5*7w|Px4>> z^Umj9f`)^q0x1mROnKF`67{9?92<8yj9}a~w^+WEStg&Z&>&4LloDJhLPAi80NRR0 z6ClPg_3sLaX;Hio6H<`Ra$|_)o(FNqXS>=VL^g{%B?6M#46w34f_beC;4`|MOKVAoarrJr-AEF5a$Ozv`m%D zCl@w-F6zFR%;Dk{x?0=E{QrEnbm{YA455yrxt$?tcI#y>iU#7#GdJ-hu<>FqBu^y-`L zukv|NPMZH^gu8w)hL%)*Ri65R8!{4e5uZ~P=$)9u4SktPa?d^I_~RHQ%E}ka<^wWg zOI=oKwTo;P9Fw0vR*GOj|ChUm(~s#gk#7}Lp%q1!mL~joLh(JvbqZE%r~e+@iwt!2 zoP*Zd<8)-C1$f9o9yFzz_c)O{p0eCj!s*TOj7zDqu4+l4U(R>SiO>{`^O-QoI;9;1 z>)^5Pq@(s^?B4~G;TR8l*^KIu4Qq|3^tTB)br#G*;|+Ug)c~3?i>dy;Rx2YessGHL zRTi9f(QS4NUyL=-Sl`_CS6(@5tvF6+8B6`(X2`^O+U$&t?R-fGb`w6eMxB`-U*_sp zF0HeR0XZC$>e4umPps#XhwZ1kRU*`8Ehv-~bADsolJc5c_HfRs6ZQUzWTmz{z!;i# zt1<=DgWq2XtvuNxj66vwj9#tf{QL7k6ZiNC8PHybpMs&bsGjYk6C+)h`J7a+elJj2 zu{Dolx>mJ;5i0N^yP&#WAqw#H{+aGPTxGfwTVP2QO2}EBZitu_ zv>pBEWL5T+Af=h=1bh@mN?JqhuCw7(bIzVcV1k*Wtjw$#5W1}60L8RzXKA%J{vM&k z2ko;1K>x#8cOnM5oMAF#L6rTNRC(Fex>MeaEBNN>^827L#W0?b zIWEz?F=lO8`*QDf?rqs@>}WG~dz<5>xF`~hCv=pRdI0x4=e(`M2PP8p^%D(!} z`Lm$6$~gjON9>8B>`YHLjx5o8GWeF zFE-Lh`2~yqkI@1NH!xcFtYJIyIJs=G=?cGtCB0U3e@?yxAbqJ>HSye>(y^22i%PtG zX1dYkt^;#HtIk!sOD;{0V&*0RYL3GjlOr)b8u@L@OWN6D#{5o4zKaelaD(IM*U{~G z^xiEpH}Z^!e{VfGk;$|5Ku4e(*_}wEs%cuCT(+2oD=rxP^Bq86A!m{9md01k?zOfr zEBmJtqlVhAV7^v;9W6a7QGSi>U_fM|83O6znv115;#$rCarXs!jUrozhouXl9H?Zp z2O9#XbWTT*aDwypG~V3CwBzpReYM`5F9Y7Dor(_MYMn-~Ar{Vgil3i(dOw9v8LLW@x zm$b&W0^A3YQ7KH)1rtegldM&=@5^PV@G?@(!JfOOei=$w*n+ z+1t}Q@;O*c2RrIjmeVK2%xz;|E{Q?55+3oXRsH_AtHfskqtMZ_{IZ;^gBRAS8|l76 zCPV)o%~K+iiuF-=`i2AQ=gqtAO$;m}_&g(iF$la%*1)Z+o^7 z&B7U7z*q1S4TyT<`F_8n9@y{4KFSDhY(7r zGajYv#pQMMhw>QKGz3< zFv}0vG+Kd%zQN8yzDj%D*e?SL8Q263H6@ggekmqh;F`eeVZV%rv)XyQtC_DO%kG2D zp|jvw9V|NQNrOjJ2a1C3n_gH`QGF-*x6@FUx6vRprU}*f5=dHRA8&$|GLiRK$^4}g z$C_d6a}PBF5ymlLu}gAeK0-#Ra@y`D*D915Leq$8qb;N_^7zNT_Nw!!Kuu{m zZS9dJTqw(e@=-MN?b!y!7^7>m%`ntV6zI<6pNh4$4u)sObH#t$>(E4MKJZ^}2B=oo zEV;$`%i3WWghtYGq_b>u$h#%&pslh+i>W&NtmhLx-Y#3++xF1vNCl7v|2;Agh}NFE z&p)1=jqu>v8B+HHec-hMM~w~=D7(MI_J_<$j&v~(cP4OtS$kyUSbqfr`*6t>!Mu4U zNTwCZ!`xFa5eG%4VDw8FZdOQz>VMa(;bA4vzw9P)-1fmx{{g*p(j}Qyk`p}(AaE3r zIdUL5#&^BwK%V2i;>{m-0FFK`?h+_GS($$7xlgJ9)*b!4qe@x_kgS7KWRRLWcXX%# zRx`NIoHmkdEhb-ztbwsApf___|Y4@ zAMdRjWF)8e@F9|C|HlnzUmmdU1pd183u*`_9gdq`S~F|enr~l~$MG5XAJV^Yf!}q$ zy}u)V6ZlAf25S74_#ypyUKkAcexN}5GmXVk_h{Ec=!)RFu*#C~Jg#~NH>}QK8nY{f zeG0YBHwRQU((fR4;GqZH*N43-rj!@F=igsNe()U}9GQe}bwrl8A?MvTcY$=l(_Q(1 zWqEjn_akCZC8@Rs{*j)jl{Td`r7%w=b?<(~`iL(!M)#?Ix1Pbo#H=FFxO`IfY^q5; z*X_h`zV!*y7k;hDO6aMzgRL=GGx%1DYPYf*^Mtnpo*}bkjzhGh?|9Y4Vtfw1&9Kft zYhlDG*_Wn2f$m~O!-pqa1hePr4@l3&)SLbx*XK0(c5Ez6%6)1fc|o%BY>UqzI@54{ zvV;o)sgDHoJ`3WK)NX-q(~XqtlEvCh7dLgaIa$32@{brw-ek&J{#MCuk1dxwtkgi5 zNM7)JOF+92(Az9bo93}q`lC{y*>2zEekL6a)lO)K@o*aLY?4A_L}-s*%ViLpEq?<~ zzRQtRGSylw&7ctmkzCz#vlH@V# zACcZo8E4dX*S2*fOzlllrLW-40Myvf7i(_|ak9Id+j5j&ifO&ws2_|-E%@|S3`GlT zI^26^H9@qR6VC2Siz1e=pj27A<14_ROx9kfe(*Zk%S^(Q88uc(FG&|GA$-Bkc#5;n zeKcXs-T|e0*yS{7zGcCd<}{4-`yo8spt7@g_q}Pr*4uzt{^b`9G1_KG(77%cd)Eru zwd5XkOC0yDvVD?)uFCv&eK1^CtK4ptCs0EU2qe_|m)r*&{DNf>WUYemzXG zk$i?;vZ>`^sh^XsmJvg!bHbV^>7n5)pjcV+^n69`531;;M;G3uP^(*9`s@*EaZjeW z+fJ%?{~?LRGT#>oR(Rr57dK?ZXD!3IXW=zn`K$|?AUmiC0`eE6rtRssSc=U2zHb(5 zo-KlPu>B~RFsNOm>Q`*SmB~Rvx9f_{me}#nfuPbtYg;VJM=$kN!LhG#rT9uego+y6 z!Mg<+X+QW@-9!f`(R)p7Vfn#EMJMR^WpGo<<;K^K_@6d6iX%8K5rw2iy}s_=EmJ(W zE;GB@i)Uk#hc8~$+BpC_ms zK|F<4=*zK7G(o>=TE?~Z9*&b3JcAxpWGABx2#3^^f@6R|+}kT?`%5$Q>D^T?30cb0 zVy?rdI3F#-+=eu+VfbZTTGQGI6O)N9fGFlwInL+3GfOz8UpjmSCDi6}nvuJwEEHr% z+x9htLXgfXP?Tt00PXjPjx!Zf z)$e*SSHrerSJzI7_*H*IG?927YM}ea!+T0aqAO?Gn#|+S__6Y0ULl51N}rwO)PM8?)WI%@i3VXw$!`hNJj?*n&q4HjQwG9l8EFAgDd%EitJ#NUxn^ z5j42(p0Dl6+^*jl*~Cruo-b3R|YYbBvMg=*1kh3A_DgX zEAe%ro+EB`u)Ry;@?g!&9?$lMdx_n->B>7w79$#?_%B#jYXM<5c@dz{9l`*gI8fx_oh`W00*II~Y@R z=N$WSz4(Sy{T!0ab+BBH4#@IJ+kf1X6y?$KzR`{%+bu&hx$!nqQm{Xta(T4Kc-CdX z&ttsV*O|ZfqDlmH~@ssQ8kV}Af}Nv=-Za^*%VXDiMpba8zYG* zUsu_0%4H7I0l|$$n&Tu1dI5x-eXao;cj`3VG15$G0*a&KM zO_dK*rEc8L!4jZ(WZ_oITOMLN9FK@ibW-B9Gh^R!Vq&-u3R4S}BZGw4B=)Kxt2sh_ z-PeiE@xGS6=N79Bo7k~B)(10{hp#M_dGoj%Cykz&Jb;rUz4T{-BB$dF)C8wzKTM=6 z5pC-o1nka1FI}voIX@4;pznj6fMx+2Da|m*3*2F+X9%-h2kHkH4)oAMY#9f-h0J3>nP} zzB|N&rVAbI_OAZDFHTIGYmXu~)s;ngcBBvQYzS^jfOis$xIa)2M~uQGa*^In`%*JN zL(;_&Xf z*cYF;d43GucyeZG3nLz2T;|Q&S-`i7a59tZ$#3ML_-i6b;gKF%;XDnb@A$3VkPEvt zLPhah6~QTjmJ=wQzmz!X0p$FJ82FK3%CV@o_%ztH6@`@dEZkD0(gvkFWUmGPs!TipGuF(O69BQ_FAPLgEY|aoQNPt8)kq$Y zZ7G$l_Lx{w7!vsFDSA~vq`0R*`lqNqCkI3O)MLuFl3`Dtqag9TeTPV)bzy%1Krnj; zI0yAq6f53AuxNgU(8W)yOh*_&5Z6(}5PF&h;Jprb<=j#uf}-DrJp~!gw6?>0__}a` zf8OdT534PZx)`aAzia;zs8JqtIet`HJ#~=P0*z^_Z2j|8Q zjzM;Y4tUAdHKP!mtm!ug3kCnLYZw6qKPp#Xxlv$&_O0NL;~@b~R+h9{Tc9_dnvAw! z^Wg`pcgaygt-Z&Cc|z52rt z9pu3IMAS?wgSxlcd41kH5jR8cM9=X5(e{;bQLbORpaM#xNJ^`ql7gUgh)F31J%ls^qS6c@f(Qr( zp&$&QAX3sjG)OlC4Baqv&kO_S9?-pY|M&Zz^Zw2`eBc8!JkK5LUiXS?T`S<(52_ZH z(^Zd!{b%aBTGGySxR(|jFoNvNbvk10o4XgEg?aYtr>&;TrlG~}bQO*j=~k`-{&EMs z(ez^F$-yVl#qka3;+O^D8gxz92o_0Y61Xm$H}ld$lvG*3>tUiihWh1)O8W0jflvBe zKPe@B3~{sYiL19c+6@YF1vC{?Y&Gc36ihEpkXC@Md7-b)QTe3~?0O8wUN-$qLcmf6 zpR&Jhg}ySm83NFT9)%AfhA2ag&prxQU74<~n^;Zv&|8)m_!B>_Ik^~6?oLbYQz?Fg zJi+KKujx3J#m-R=;PoWM`LgoT3t9%pBA#Yf=ATUC{lUtiSz@3F{fJl{mrya|6;vRU2zq;RIuw%)ypjtB!K1W|DZ6+0lTad zWoan$ZPZ*VhlxJd)z{^_FnPdEKM(u;U3pU9oMhZ(-|Nzk!WGxQ`bw|`T))IH<@;G8 zO#d^c?_`WK>5GYoM4wdM)(`$hfDyA0zlj<=6QMVjUb@+@6Z83IOlbQo$W6yrevj_WeL>S}v4*;!vj%0Egf~p>n+T@ge!-RjZKr&!dMqw!Ml+{a6Q@C5H7H z-;341zbFX{Kn5mPY-F(3GRzv;w9~XtQ3gbM$A*$^1QIzK5#=z`e|++^9NE}0X&R}3 z^SznLmoO}mS)`C@A;dEjbBu|;@L0RCOHE2QYed@~k{qq8SzQ9yOs+~xRmg?MwN1&}o~93h@~{0+29`O(PLY+v7}Uh) zv?7=o4oLKVoiZlf&o46p@|abOuT3kri88>ruS~vE{5WVd+xx=5QFkNoBdc_Ors^-4 z)*p+LidME^c+m(2KIT1AMNdal4lR_28YH6Jf1X6w!Iurq}n|_mN2||JYCv z5Xw`aYYOroRuK8DFp3JW^b9(Z=kv2dgPV<#yQ7LToyt7 zyHi>qpr9RMJCUdyI`ruA=jwfKhkqVC5RU4m3g~^~Pf1}X-^JeTzshK-t!?^f!p|pz z7m6G@cjp5G25T)e{{qTWdv(9H;pYT#{~t+ub!A%N3O71OAY^A1K88aSeB^Ks_k6pq zBP-rO)LGJx@c>yp=zgB)T>61UZ?7m> z|HAo(I)v5uv2pwFBcFP<_9j(YBbl{jbk63yR?T}FJwrh_d%}Egj5HpFF1ZPti{4AT zidk(mg}h__$ksFOe_lpH_}<|0Ate!80J1is;$DStcLy$lOy?*R*Iag$!RPgPp`(xD zi4;~Pho^)V1V}HamW2n?{gUJ{v~s6Q!HxXq!OHTqte;>$-u(`|M=u9$#=AeK)*=H6 zK+7cXlnBxG`)Z%^-hEWfBY$BjluecKXi2T&OuVHB#a@WLy5AS51jqLk=B-+H+JvE!D z*!l3622`7hYAplF0hR(p?<)YR{JL&Am&B&2**ObQuDt=!prh6KfzNAqFAYI5pKy!v zyFI8)dZ;yLyOww_*KeacYBbZW0#lGoCGs{mL8S(#Vle#g@Zm9 zJ~`cQ!c7r=N!{-n&FOwp+Cx769x4k1k=(D56ih+Cj&Q2=@2Bt*g3s(B{rEAcGd*aT ztt@p1%tmz4o+oG%g#N^WFs4+ z>9hcF27m3YtUq6p_P3~K9-W(JN`_hkW43ol6+AjKR9;zpzaz@e&_zm=73{w0caigS zTC%$CRZ<%0;iKNa?hot+;DY|CSEKrKx#5?XTm8GPDFrIf4emu?yem&Z#AMDM53+l) zd+kS?a2^vi2u}anU1|Rxs($^=x*r)Ts7h3}%3f6aDH!xjqs6OSHz2%S*n6%bXF9Fy z27b--E`_Aw<6kossbgrDkx>b$J#mF1o3mtZ3m90Sw7R6rN~mGj?@BvkEdv2y_ZOpn zDmC%-zi#BWCd?fs3vNs_Kn1)V?=9k}e5|sf^OF{xP>B=JZS>b|LqgQ~0u7M}66mNp zIDan)c$`ehzZD^oVdP^x+AmYtl}UG9=~saL0k~yTlQXudsbe6(_e0B{x$luug{!gv+{h|Mh6EI<0vU1Iv$V<6aX2c;>-Q37*XPWy%|2EDK+##Y4Lc;~D{TlU5l&@KMl>KGT2VM7+I>vCXL~&&<=L zZ#4XEss7j}~lSmEnGN)=Fp zZHc}-7s)|$h0wl=4ESmxu8udlLPS!`H2mpkoA1$%%bUs5hdg4+X3?3;nM%$PJY1Q2 zxc*ihP)Gym0Qf7(eiuhzzXS-KWIkc;Quo2l>? zNr)e^y`N-63Nj|!4j(ogd%@N0&!EI5%zjHLE=}#B8wpkR`PDwg8d=-u3gVjwjjRBF zYo(RjAZy3@4Lnsh=(`pHwbpjWp|m7E8=-*P?*+|VK*I;5WS|(OAqdRPKoz~%ShPWs zLUrd;p5RJepx;c{^+#V?-vXWn!2V0ZQXKwK;SN8J@!>Z{qjV{s_)Vr8Y{^wT>?oz5 zBwch_w_p(7^kQ2`xKR|^k3e(}LJ%aW*1eMaoo1bQy=fYl=WCR1KAsCJu`VudlxD7p zjv)l0fI`ax#N0>y%_YEjQgx6-Wt-j8`r(S(Ri?Y)y>D9@QGK?{~l=!^}q;bGl!xJ!6h$ttqe5F{ho@_{v+UX4wjwP( zw2|HW3F%o6vrkws-C4T+M6czFo|ALh$J?jczrTfKuVq_!{2)tx!g1nrVufuE#+5wTQ4* z#ZMeO0qD6^50|hk?hES68)AqiL4Xr|S1-m#)ME%J>GPh!H0f#_t`-Gl@L1{eY!`_j z+&j(nhm)wHhx1BQw0l8!1;T2z^j7{^#ShkWT6;38W6V7T_V&Jbx`}|UL;q^ps;WyC ze>EmEwa8wlb2>e%C+kMX5r`QQ0IKQ<;T=&2pUMj@omvj5B}{{e2q0&}o}06-E+5_Z zv?EqP{1UaAWl91k1NECAUmxs1MING_FB_ODP1s}I8A%vmF3g{ffE5-5-$$HNPpX4% zPZk72dN8}$xv80}V|hoSlR!aBQTpycerm4G7o7OmMH3RALubHkNZEYKOv55oTDxbK z@a@jYy+rA^-Qm|GvR7d-KhCoP`s@dog4Df%q|}i$P7nbiOnkAoGU?UW#a;Gfy869$ zLLCd+tt=4pkJ2}h&NVI%T(mb$$ED$R0{Pu5R_9Y9E~&b_OP6zdq#Y5mHsJ}+kgGF= z#e7Xp1+4pzuBdG})PlxPw?Owwrbxij{vt8Yq3FzoR?x*EZ!-N^#WZHmp(tXY_o^x; z{<6AIou!VwK}o8dlfYkMz+`Mgmo#g7+n!lpT+RA6W8|vwrODhc+AiFH3ZN`4xW!tx zM{IKTnSlP;mcY9E*r$4chW?U(^`$UoP#nnjk#Mg$_Qm#|F^(9NT56o<_&}fyCaS8q zt#@hRMK&rdHj{vi)P2heW+Q&cu zTt(;E@cX2oACBV9zloMVk?lYGBxowhB?h8pL^cd9hk-PYM3V1=mEV9cojAqca1HsR zsyD!RqI?N}4DiO^02vp*{GPC19`oA)efQj^iz$Ti*79p=81+Y5P*Sa++g-a*8JnA1 zU!ETA6f`nchzfVIhi7-!DE}8gdjFk`c`b+8wgt6XrEdU?<;H!_*0MJKe13Z`*LITW z`%pV6=WZBt07dIVoW|v)Lr!-v(K&34ji*G<1!vp?b$}H66hZ(6LP!i}+n{2ZRv%(Q zNx?8;;ADkG(3?_A&jX)7fz8Y%9a#oF_c9kf*t3LU3YrJLFZ-$7_KfHYcgUAIW3tGa zN@GGn1h6x0FTmmWt}V=0v?9vuH)s7&ZrV)ai0CyPEa*|2T0D4;`RTAB?2lshi-7t_ z_(;oBMm>!t9olE|x+l7xznHg0>_(Y-Le zR~8>Z9J#{oS2BbNEYEjduOw?lsGe*i!E~hWmN+k#U5Z^0=Iigjh@($S=|w1e(3Yo2 zmqEt7#OyLm;P=ov#>KvEDql0otoL+Z)L!@N&kq!*Z=WZ>0EQNAEHyu#V#;wH#lXRs zrT==^1?AMS!uHZ33b)v(JIy9GzJ=2GY;nR6`3v%X0Mqa5A(El)sFXfdImRhTS-Dzw z=(I*-=HW7BeU8hwicR~ZXVx|ttGz9UuF?BPVyK`F8|NU4)*8>&6Ix}p&5C_vg=(=? zc-`R#6oZKx>&Y0Y)P@Zkh=pAeX4|U2?txoT^hhtDZ0i`?Nng60ih|k7d*(Ud4}Xw6 zaI5z_VQEDZXtu?@2o3-g^d#f7eTzIUKUJZpL}ZtvO|uwzxZ>>CZr7@A|Jw1ox(Zicc>0#bOwQ#pv=lWK+2I6X z$7ZLYhXsuW$&8#+VP%cK^$#QXBu*%3zJelf+Y{8I9?<6h06}ktY|=_YJ#`$-+82HN z6}>&*X+{?hZOxn$q|ZATkEx;hv+_jM&`^!5ATvbq2rskwDm}Z&kqmYYO*-{lCqFlh z;gXMHzr{>O;{MpO*r*OhC$#6Wx^<^|A(!o?k#3LEKuHK;o0JGkgF5pc?$XcjC^ko% z#9muCPoC_RBP|0`N{AxeI-4XbM>#5Rq}bx2z_j;hd|G;W{?X5jlMY1vT@SjGzAkVZ zHaFZFNj`M1-Ws%NZS;^Cb;LQKVRv;SLLtV*3w_jqMWeg9`IoNs>viR$j;<{%8BEup z!T!noutU}LhPoau-7mojT&S(GEsLRb}q(Kz_8xPZh zJ7)Af~aPE`v+Ho|A8bXg}p#ExuRw4-dGgj$yp`;SviWJPleMA|`H^4d6Q|*zXDY z5!~i9fUoFIrr{1CzG`u6+q?QWusikbeU0?A9-&JBFLxlMh3chpY3D-@r-yn@f#);c zWECwQ{Ta|b@WM4YbZ0WJ+C!G`heq_D$9XkpFS~7~eiYPl`D5&Jq{`LQI|bh@fm~jpxjIN% zg9klJrI#=KzFr#&0Q{=hiHy~#tvK)V#*kDwQ*DhdxI?8-&EwU%HCA&S%4#y%Wn!l) zDv5$9J49hPujM;l(`S;u#~Pvpec~O=CMb<;?ht%okpF~Wy~v6v*tf$bjrjPWAVf2XmnpDQBH0fq64oe-O!|lZGH8MQqV4g))9>6Z{gmaZmNQHlupROCap8x! zJ0Do$K5C?9fR5jOFGZwGG|L)hysx5&6b%nziU(WJDV|3=VbILO?!cr==}AaX2H;?r zhec$vGw`rLP%lKQm;&%}4n?GNqdXiiN^+>Y#aJH`7sl`lk|H})feStOPJwItUmk2{ z^VXwwT(GjYvPJhKfvsfEbF~$8qU~}U#u;pZ4hD^|DhYQVx3LrO8ER_kf%ov8M_V82 zq-$Xt4?S}Uu)(pRjdZ85FLaRzVZH}R;{I=9<%G%CIpbd{(3QwlCa6;TSdd=(A+X4B z$?KS>!#S)b^KlocD7NJvBXNHP_I|G6EQ{`u5Nk!MT81iuEVD1G?|pO|d1$D?pHhZR zDSKiEV3z~+?|BDrL$G(ZB9SGv_;t^+A$;3Y$TVVaRdbP9?pd1Db_M)d1Z?T%R@ah8 zk=wip2O5D#l?|3+yE7c&yX#47;){jT>;$OM6r3*d-NC4Td}UwY(`KR=1nt>W{Ck*> zcS{K=25iuyQ#-5=M?S8-mT)Xf`5^B4GrsjKM4;dNz9I;ffrDqH;eL{|<{fr~Ol~F} zfwkJgBFkjqGAl9>W%aObiPCy}8HZ!f0={wUqkB!+l?6d;?S;WA_D$if?K-)k(s8yW zK3ydXSAvvS(-K!z{>(Y}pK2sj4OTgWT;A(bbv<}mKa&7tz{bl~_n`BQ#aJL>GkTG=Y^`np>4`@cGnR~yEM~*;HhmWrI{|p?w?JGZx1Lak z=&xiUGGISh_!SjfFp@F=D0E$KvbVmGDA=vw*ztB-|FYLD#))0M6cN)htZI*$j3_SK zX)NPMdbNRO@V`e06OSST*MEhjt8x!gXLFzw`~H6a=eW)!_2T?8{K8@t$L@DrK!#_o zr6fOqq#av&ytgYS+oiw3)qf2L;{W!K^Y(4&N!KpH3Aw_-7dMUuO@Fkt%FX6aM6( zzC#%93j}*+t2sj7GLjO#ikI88-Gc9SdOj%w0;QjRGH#IJf4`iB2bQk9wrd)eS&3&X z{Sm#FF>J;{iGDIvR@7a(OJ(OIp4P&M(s@&i$!X2@EI-h4?-^lhp7{9up5Z2K z5I*%Q;W~rk_*~V$Rjjg)Bgpg{+L@9lW<)IeE|%>??2RFISci;RD3t7nuMdrR>`r?Q z>3iZgCi|Tr08clq28t@&Y%|6PjXIiG^~?vnb&wSwFER`4v*lJ20gun?GUZo0d!)Yo z0Gr%?my75ad#shyP5^p9K$1%Gqj4J{!TJjhf7~f;+XlshSww zu!Zftk|nKK!VfJK)=5u^U?4X{8sS&IvL63pBvRr+DC%%*1?4H!S@-sstKu{E z{T={^=y;@|2g@}AMZc*>NNcwLMr;^U$;{mec_QRoK`}5cP+|6>EmKxSh zhi!G6YB)a~``GH9h-)&JH3{mhxDwYx}+wfNxle@u@NH zLF@+w0s#%nr)=}q$l2#%FBL>QexIJYTYu*O4Ze-0uuk8xLyX>Us(2T!JJ`BA-x_W` z3a@CewGtaBdnp&)93D1C>R80YQMha_O`vU(C(~Z*iRrhF3+&$O5r4Yxik@huv7- zpKer@(APevM4{bGF|;vkl(s`UFuo1!dq7qg4;?do+QK!{r`ndCCx0zV5hZzGcC%TgL#v@ z`A}MilDul-^3%5Euf92~myZ?=_8MAP=kpbRfN`ldIxhQPj{=MR;3bg9SyPQi^dYXN zmOQEx%yd*(f6`0*jT-8*)c8~|Rc53iHp4~Z;_o&v&OR+zx_;Mbx6C8T@;qhDuoPBp zYwlrLzsw${RS1c|Wl_EqzZ5P+^{OU;{;d%m(=l$S#t&mHxn)l7=(P|>CesvYxu@}& zgB?wm+d{mX>OObMHEu+QfVh0#u>!^R#$^?HDA?k{Z8{>pD=!xs>IfHBfYO-9+3$w=_+R(WpI0NgSA@(*O>*{W~uqgCp~d)rkJ#di1}JBJKU zJi0NLI|ua>s5X6Up9Z0p=5Jis;Tc}fRo=nL2*@I0+6_^MkFs+w!M)s@jsh2-w(MSf z{*))z)gk+poFtiMP@w|9fb?&(-EU_7UyRIcsT2vrcETM&DU95h!Dd!OA+OBr&BgZN z%$#Xzg{}(0=NSE};^Nh;a z87|E8>dyrL?6&5l?7o2XNOM~@F^DW1>A1+*?_7@58;`*bs<%QAF73*Ug+Ogip=m{r z#GM0K+kag}V7g!Q89Np+A3?Bt$z8_fH|s`6-R())%Rszuun^EFukdqrdrv4bvzd@$iH#5rWWM-`4 zONcSF!3bg%0txf<7^!J}O5@hGH5u~c2b>Py&Vj$)|-sA=!Ge+lJU5jy=T9MfdJn&+H)SQWIbA>a*v2O|QwqrtnvBH7)Q zkYZ8umV$?6*zU!|DBA`OB<&EU#c4{E-vHN-EQ%=GnOwxQdLnR{95|7Ee@hPYUhbSV zkC^_?lm1((6Rg-HQj#U$gHoM45R(0j>`!Q8DJ5tk03eot!{GlL#PYvN-tMQgK_u}1 z62bXDe7v#h0)E+!h_Y7ir~di_K5DG0t#G#dTa)sCZV&LrLTUH`Vo=QqjvIKnhw=Qf zC&E{>U*`7{#rg{enwjWsIP0V8bsuyrAb?lr{>ab*Z5z4j{pIAdL+joEMMfBgE%Egc13d5#YX?do8tDL?b<2tSE|1h)cH-a#&zf1Sx6IV8uW;Av{W&v$5tyEo$_OPYh^Ay2eD zb#zZ3JWHrQv<;rN33D|mzSAdA2}lCL#X|V`AV{#b2uoWA;@oWCCKJ76V=WRcp4UNB z0($)Q>y!*4Cnr{d8}w$ZHF9#K&i)tcG3TeSvFPW1XRy` z*;;z6&xUZCRlkMhdtPmxYXg7VJ?Es=jIgkDIt}_{!2dC$Cj$V7DPC5u&u)}ZRtMdNA=e^i7#~v!9PWoFfSFx{}gq? z-~ZagalnHM7+d~rIf=35|G7Z7%{0m-4K<#D3zYC2!y*8ZBOJ=K*b}jYjZJc(4A}4I z*rPRFbFYwq>^7v(njR0$OwT2HS}7^iqkJHazs^>gwZ?!W3QClHx|`ED^+xO?eRE`FK z2sDG?=g>MtS)rE$^{G66SoqBPima2**qKE!qSNwlb0&H~G=rTPXY(tCAn0=A+L?LX=Y$}_*52o5;>=um|3i^1QY0Q0KSBXL94E_ zi13U`>U1JQnRORG1;F8h0_W228pjeL)_IeVxAAt6P79PB65*R4At&llBD|c5{=5F4 z4~u=J&1eSJI$ULFR?WIdqmIAT;6)*v{y=2R0kJbz5C=Z z5>tuu>GmOVMbqpawGAf#_u{bIXSbq{`NcD9pjE5Sp$D>TeF z^bOY>F}KQ$aeOq{u#gp;tw~mP>lI4?}Q_jes4KRt0cX5Z%_p!am*}< zm{^-#lk`!{TiNF?&3i2iLTlot8eL0QD)6S}GYF^j;Py?eI?%-D($B(X(2|PT-S4m; ze*xQkt<;g4X17U&W(i2-@Ujn9yT9TD$BA&XvK!=d0VGX7@P2-=FfYT#XEwRsI4la-{QndSH={xe@>CRuR6tAokjqFL8wQ}pTe^AnLd+Vpck*2SW^DRmlAizvtak*kRWF2Nz z@AkyKb-n<;33(WlQ5IVOXWiOt*{JP888lh(_uH|XUkbV~=5Pz%B~!Z175~MiYtIV) z?6ozh2!_?gemxyVO!UA=yT&0kQ0IiMDBt;s#JfX=t5L59N)#C+rnaXH1oL|WZJh&l z&b+^m@nd~tjS9^BmIWoP>A>3*Yv(i3v*DTCLDOc(lnS+8GuvzTTmr@FTi1eQb$_bH zc*?J*VjSM~+&It&^Q4J+NuSV|2)i~HOvl89#fAj+ zbXsNSfTlBkPg*nPaWZnOR>pDA?a8$aT=MrWnQk2?KNmZsDeD|bPS`Ay`Nwf`P+1qu zGXBxjK__(hN_zXGE;dF$x2~yeW*nA7;1z@3k}}fhvSnv?ShFf2CKr%^_qeuGssFHZ zrK*7GA#xW!BtrSM^WgUVMWo7E3q3z%mlvv0pV!Ds6h<3sN&SB5e0TZ#G(!xtP=z$! zbRlAk!U3JuqXuhU5EAzlq2VHTYm~@?*iUph?OAO1qvN9+zA|;hTJ(pvIHCasf2nP@ z?L){qf^SnHS11D0Vr2;n$&|dnN)N>^^dn_{%0hGOQU9gu%%DkkL?N8~_Mm2(gZT&O z4ehdKs*OYa4XGDfvQQ_+uWhQ@j)?78+))o)m8!e+%@Yc14Nv>~J}Y&sid`-fn_>0m zlsM_BnSV$t8u@I^GKFz)`Nhn&hwCZH?z$+4CTLU7LrUZDdGk*UlwJWNL+qz&&=|9= ztLGw&i%mc#D?t|iK|d3@)+uP0Jd2ny+4@WALgFR-W|fp6oS(X)wNN-jY~syblXPv zFw>j5~EOC<#U=w)oM=`#3#!zvra4}_@k`*lI(SB z3ld#WC@kT|2d%Etndq-zsFFVPTd3Iwt!^}bUzqCDYb|mKm#EL0R}jC^=Iv=;)wH_v zIwNy|bRsdifh_w__?&rHM9E@2yqVM&vWY%s2MUQU*g55-e>2cahYm;JPJ`k@W7;v? zuRA_K`0HvJzl558$SR|P&MC^da;bz&1Nxb7Vvx;U?d5!7B6s1BlNhheOepIyE0{J{ zFCuv1=v?HI_;Mpc?*Y>F=2Qpn9FGK3WPjQTJ9CSsqspm^H6gM zN&UK&T&T}A@8msf-WZfEnRiKPaf#xv0$-!}V&|raIxa7NYw&N|r_VZxlDrpI?bV)m zTQPOqQlDTs-JwY}G>nI!jmWe|rS@-tM9KgzOX`w1j*1YzD=1Xb-xp%D0WgzpPA~?m zQVIY1e_k9<5?FAeNE$B+M0zmFPP=;o>*6A@00>U=j9N z-vIxv)-)JPfLKkdnA7{ zxCF5Rhk*JO{`C|;u%p4xz6;jNm08yQ>30WD>hz{WWcFVki3K3yBQUd+nk-HfK5;n< z66#NZTTOUC`7^pR;(R~puwtglqt88k_~5wDSdf$kURta8eHCyOnzg~e2|u06Idq^s z?q=($pf)1DrdP`F&%;AEMDpV9G=}Pft+r_S%OQ(zW~GmJC#@zDJ?HmseH!pd<%42N zS{9Iv+kaGIpan2G1!UYy#3}lTe_S$L|w(T^{?U+@MTdak73KaHf-nu^++uifBl1==A5_lXbl- zK|bc%x9S}UeT!UkhzD1n@+Tw+lm0$h(6a7}iw5&yF5l5u6~Dw0Sz(b-9TfDzr4*yl ziyu&itsa2O`p2$-ncli*A1`Rk`P<@3k@||CjHL9tYfeqBsX*!d)5}DqS4#6?p&zLm zW{#@7G^HfZh*#rC0Cs${_Nl$2TrX%9kE-fMN${^Mpn#sqBlHJkpoG)kFW@eNX zhOEJ#GugXSS@Mtw?@rYKMrG*KMHFIq1s+~E4)4TbvSg$mgq%@4Iq#zDOnm7XOp^o z6ni|J3iZ0NVV)MwXan&%+Sc~zOr6~R9;rl$gL3m&uA1Fz2jk@uduXH)bYtFb|4rc6 zQ;;n72NL@K@8i=rR>Sk02k&dUNI-&638u3hyf4nw7ie`BJv}MH#KCtOLC~5@tl&YR zX2FwR-d9uMsZ1_&Fhks1y2UeJ$1j5P^q1jJSe_ z{a^|U&-cGzT*W1RW^9tFy_YheCQvxHJ{I!)+*P4^X{QdhlvU@OXAid21kgbW9Q^51!L#4LC#blsM4onQr+-;tM-< z*$WAr5_AlWFv3lhrWigGWPSoYPboKz>oGfI1-4A z(eDEBq=qnxy0W|&DHjzI+;J4Hk4%8Hf|JzOJ;+SS!iQVGO@rAAe4JTzw(3eA{f8iB^nqB}QJXWp8w!t^RUWoO znYteODV{p=up*B-QTZ+;H@|_0-PFfhAs#u*@&+wFwtz%g)<+Rp$z+wxXyggNHc?p% zOnypXOZf^5(-t^WI5u%wkRX`giXxnc1G#dcve5|jwg{$qP#4NTx*0 zX2a=rqW6rrA7Q$0^{aXVTB&aWH`^3k=wn~Llib{FEMmmxoJM`ap|S>whQn==@&t;8 zS7hmCHz%H2IMTz~F(x-}3;pNoR{XCE9%BzvXlI%abdT+0c*J1JKXec>&`TNK|P1N?q>oSRVStix2b|rMj z+phn|3qo)8Fg;_w3VL`G%G4+ceYd}A_9(j(H0i$d@r9*|%v=O9&OFdj`1jA&_w1Hz zpXfbYl%m|?qd0o;2Y(?_!tn{7n2Gq*vygWrk7(E}rII9!ITP5KFJD;h^ld3)C1o>Q>r#mx{j$t{v61BH_=d62+byRc63 z@?7BEBTs(~>CI-G*BFo9vv)csXWlzVQ|2m^)EKf6*LYMgRdX*UZ=YJsE9Kv+*yEh8 z4Rm8x57=$qR$*;4L0!mrg7i>nAd+ z^1~;&N3E)lWOexY9tzk(M&KoK@}msPpfR>NM7ME8U+i$>YAEUZ!`E9k3c5&F76cDdmgIB=2K;SltzO5xVGRKmhJv)0cF?a^%sIAxAde zt=fAqxHK~1E^Dn~yv)xjSU_%;nov zr;IP6qDrse>a`$wDLjaijc1K5U7*J=KbB0EdZTi7&8}^eojTGjebx14y{U&g1PTf7 zJ}EKVWJldOeyt22h{jb<a<=ahE9aKz z(TBrlM>no&cS&vbF+(+7hgg&`eR)Wx+P67_3g}G{t7-MTFeCZRBI&#h*QBA8=kq0^ z8TRY2!0|1IFB>s#aEap89HHlz6RmGA&cH-nsfmbsZeQ6;^`!%nhQ(Z$HNSOpitz&X zLY2c2`oRgPbLj=t=@Tio#^LHrjj!hHxfv!WuRa^2l2yS5NECkJr+M@q5&kqxq60BF zo3}0*SZ~Vz9p~EkR3*n~o<*`@lm9lOB0bxCKT_=ijgR*th^y|wrdxa^_fO(|;+lH% zq*zy>+G*>{iJN^ycW`T=R#CJ2a#9U-4?mJ=l=J}=vSP!*YaPL zRlUb3*9r5Y7vNdFYr9a~r_e7?lkHkpTs`L}FoJ|*eM;k$REqdYBZ%64=>38kYkg@F z9HaY*z2#Dgb?!~8!n8>}dz+rcU2KTN%{R8UdAnv+wN6R&`Hh;TseVg{d0W4E{HoMi z;2`uC&gxs*Btj~2PRdQN>nqim04bWBZ_I=ljvI?X4a&_ z@%hcm7{SJ&yo^DGn(5saSvz_tb!J%}efViyt}REBoe8#Y9_Q(~Fu3hHpPg#sIlt1@ zgN}BUBk!>H*qTPRKTQlOwab(#MfhzMqW0EVI@dymC15gZLyTa9m7vu9#>=^f0=C!I zHb2~?tf<;;jrtt(RpO&=jYl1_@%`qgWDMx_rQSsSzWJH)!>C6|D67frn`d469$oa$ ziZ4~2zGe40R=lq{zvc-3JJIENFz*y9T)kD#Zk7ymJ@DmMQ3+kn&!2L#OLh@UQHKID zu`Mwam#lo811tIu1q2sBI&hbZ@A%=KJhoEt$Jsc`+-DHNiP(OXkTQMXC_OwXDzWM! zCjhr{Q*%cI*KNQfW?uFzbgttz^+J5CRP8)cay5Z_!tw62VH4# zo=Itvw}R86u{~_HdGso4R^Bv;C}O2VSX(Lt83`HREfwl#z>V$}VtehUI4V!&$=Qj=#PSuY z$kPR)C#YtJV$U$FG`7j$g}2mZVh-KN*tp1l!qsr$pF`48m0zj3fm=q)D7=+H>|J6+ zRjG@nf3zCS{O5Z)V+*J(IXp_e4wcvZ`)A~QM4!Hh`{y$qB?Mx=(-S1Z+iOhxGmP)w z>=t0p5R8XI<=TX;^9FVR5z*~CQ9gO#{|P1ln6vpmU$ork^&I|Co_K_xpxxx<0r0@< z%hzTFUCJZ$`TwK8%O2NQUTsm3Czcle=cAhFkUn0AoDKgutPectWW>r8OBtw$q=*GH zZg-=`Wf*~whDzAsBFZcOxwOL9^=KrH@(txhB_%C%4&n5ILUQbZv4r0+#dSqbI1<;O zrYvz;H&)+mn%*AXsEf|C{Due#ml6Dz@l=H!4)A#Etmjqt+&vqGy%Vn01}S=N?%y^( z>EIN;SVNXEeH>5gpyxU|l#iypSOU#cSD(ga$(`LqeR^?La|4z${~sG_7+k=f&Z_Y! zaOHNJkhBnshF;0*&g%CRE=*5Syo|5&4+!;r96Z5mN!!-b@;*s2aSmtE=c7q542}?D zc3J_1LsovuUPey>Fk{iJC7rm{QMcS^*V>qcoZsl=y7yzNvFscNLn03LO`-ZK>w90@ zt?ZzAX(<{qRsHL?a(H1@aTdcvh<*fr%7sI|gDXPy2owAp_K19$S$F~B)#F6hW^h?& z9CxOSQ-UB@1~Oqb!-r37zzXIo{wWvxb2+6k?_Xn_|6SF`hm)Zzx5jm+5Ml!tt?gyn8dqPWFg`8w?0EC1xdHycu4<((yS>tUrlI-gyq;AGZ36xYvo53(FAWM$5;lGOi!gxsJSxzOwTLbeuFC+bq%ZIKy@Ec#w%+GAE6z*WJavmI;%&C7OCOz%7pOYjf=xGEj zYVZ*D@s6coTXeCOA$+~+Ku`0^`EHBxEa`es;TCxxnI^(s+MJGI3{2H zWh~EL{f(mGOE++jzYr@w8!_iFA*gD-kuAwz>f3l})VKeb`}2S95&9^fV+nNANU`v& z$FK+LwEDwmxP`4(avXiW5fAy*xzUJ$X zxF%-toWB;FKCq^0v&Cre6#65Kwq3^b?GeV8un>c_a{)OtOOmlS7-RGe?Ps=U2CPZx z@nh=+cBgQCYwEOdbzE;S7V`G540-0XTt$ZR*Om>E&JFY7MKXt{PG9}_&F)oJUF>fv zY;s5Rx&OtWuTWOX9tMr|)7ISt3u#o5tWWPhsFYJpIx0#bcrCr8d@1E^w8e{HuLF8oyl?Pj;`opi|ypAz-zxO^J7S6j+6?TMv^7(g4 zKCGII?7O>;76wB|-L5_XMW@N``_r>gj-1*hj$F5%#I#f?DQSxQ-G z%qkj79AF#PZye#$W42Qo+FU<7qz_TLthwM5oq*ImChxa}$Uygo_sZridB4IkoO7f7 z^R7>ftG;)cj?~)aIrrYN>qTk{dw72%+?v^Y83lVjp&oHi;Gecc*ncE(O`Ae=!XBw| z*^I3AXSFb=)5Oa8dmrH!pW&|Rc1Sqa)#sz#jQ9ANzWP$*BA91$N$`k1mritJ%L$>i zkXO4s{h8sEt=cfJ?g&3+SmJTz3H;vVdFs0{g^ra;w|(W^|UXJWUlfJ6U| zm(THY;Oet>J^ccPQ6*C&w_fx2J#I0!nJfx5-wo3Q78{}Zs4a!t#tzPu_g>u4>6mu$t{drT z2}fBZSD|!~S0R(b>x`hkXB9#{Ycpcu;}q=(eX!t^djEX`yJ4g%ag7#XzDILMjMA&T zgkph;Ex#M;t@OXP8q$8;@lRdj8~2Tmu37Q(^eTW?226HM6F2hFu~=-Q-DCBg`On!V|(w+)*nKRx!okjQiG zLk$n@UaYg^=B)B-UIWUO)jU}T5684K#zUHw8qDos%#25Kfvw|p%w2z?f0Rs=>3;j^ z>y3TZyIjj6!*s0pORtDOema@4{qZE$Kbf=3`K;4&J%~QZ?NP2g% zc=t2s+3jzIb5GowCqC!To`;V@?$7@;>sOt|ZK2;!7?1W%{AC5SodK8adCXnyYGQ|b z?_UTP2VRBj)cDI^`$NQ)ruLnD%=@6tW;VXHpaDYgXq?~6d37^>KXLm0_TSU}SL4;b z9FSVc%3q8enNhrU(g7bnb7_Q~`*68uX?<(s@nvE*XG$eL083*Ah8^kfItUmc-=zX3 zsCZ6Vs>`9Fw;tSH2L{u7Lr^3!*m>(LtE_wu9FLTn5%s|!9GqMlyp_=SM%oL0-2eY; z{@>FwjI0~-Kt2Uq#Guy+ZfPUQ@w2UGGu;T!1_Fq^C=|M0&5aQ5iU|%L?wMF7iu*1ja_Wx%B{rhCD zbsN}m3!S?U?qwDE|DpcB+awjwPky2S3@d`TWut%%VW2vd=UNLG0+#Z~`~z-OotHEF zmK=wM2TuUvBbx~d4o9GYMeBfFuWm(%-1XV)A}ag0t<{kP>)n%oJi=xHL&Kz{;4Ue{ zYgb?`{|emUW0(S@Vq_sHKnUo})yfM%!5;uj3!c<_}y2MlBF% z{44?5EWdGTlq@Jf_fHGL9{V2+yN#bkS2y5mJl~E6cj^DXL#^T zhN)}+zpv{-rg%;=|0odf=kNP^bue#FCnUI3DvMdc@#gvI85cOFCOtVT3ih4K$=TXq zZv*v)gTrEyO8z=OV diff --git a/Ubiquitous/XiZi/board/ok1052-c/img/flashconfig.png b/Ubiquitous/XiZi/board/ok1052-c/img/flashconfig.png new file mode 100644 index 0000000000000000000000000000000000000000..9db4bd31233b8b25f7ca59aebba0e16e6e86d5c4 GIT binary patch literal 72092 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

t&8Yl!nSFkEnlP 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 DLICUf literal 0 HcmV?d00001 diff --git a/Ubiquitous/XiZi/board/xidatong/img/menuconfig.png b/Ubiquitous/XiZi/board/xidatong/img/menuconfig.png index ede182cfccce6d870a24e11cefaba7f4a256960b..8da38688a297daf5f0c5c82c9cf4967767b7de65 100644 GIT binary patch literal 35422 zcmeFZdt8$D|3BU!QOiQQc30AGFtX!Js12r_KcvulCBG6~6)mF;N z6itIIGgAcS0SM*LYKaH*S}Kr;P*Wj6AVnZS;J)mzPoMAa`}_Uw`*GiY-H+e(c=+%V zFRttTew|;>*Xwot_|>7XC5v1ZIXO8k*|+!0NGGQSQBF>8J$m;a;3w(O==R4#xWmb*iLiL?*gN3g3%}hPk92ZcdT;*c4MFa$R41n&4g0>_aikz+ zawOu`seO2lJbg{}M{liZ@9Lw(Km5gW&xKExb*y=N$I44*%eEf&|L*rs3XZ+)^X~dj ze*WUa&3BJ|`CHA!^YEe3`}j?rcLtN$ zV6EnvXRtalaOa#ZD!T4a^fjaOUfyqc5O zb9cpG5e^m~XVyEOK$FCw>0!N^KpMa|csTC0J09^J#&*1U0Zfj@dg>NGzrpEi90JxV;;M(&ecQtlDW~f`-EmmyL-2)1Y(3S> z=?C_UGjcw5jwjCy5iU9Dp>t$>cA$Liy3c!>a#!Vvjf{r&JslN(I>Z%TY&PNPW!;67 zz3hTRh8G(p{h0RnL*iFzs7*ay^p@7fpV8#LUAO!B)(_d;QGz zg`sPHm(HnnfB4ga{iAeG)EFbNolt1;iu21>${A$>mouR6`WoTZz?#sG4#GwFddHh=HF7(pm~kLW

    %8vO2ON3 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 B1u6gl literal 32575 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%0D{77$O@P;q}<{Tsh7QXShel>`;lm_A`> zX7g1xWP5Z)T~9_-!UrG6<{Cfj&(r>lGu;@Rw{FnoEDzL*7~RlXF}vd+O9xWAR4)6l zA*Xwb(K{4#KeXb>DwClHq(*PhmP5#svVK*&iGJW?==@6nyZLI*%Fu*0v`pL z-M;H^`HW+pQ*ZXIoU!xn-(-t(xjWz15~`fa|302w0oMC8x8k1n)f=-nsD0P+nWc%; z$p*wVV#|^cDkYb@ADRnD`|E!lz6jERR~?Qe;hgJ0syz_JYy(ITxd)<3T6JscNV>Rb zZY4>ZBZg!?q1qYx!f&hJIDlUsTw*xdUu_9V%D)I(i7LaLHtAnqHJxE_2fHESq~3Pl znB!tU&dO*fLDL2ctC!IDN{^w;h=F|YG{2=Xbh>xPN0cUR!mT01jM0=Jup(pB-m)~% zR+c%{?pY`QQ7I%4_zqtX9L7%$w@K+(p+mD6h=9;HL6WO2DI*C1G7!a87&RVB2gju= z5EHg39qpT$!|ILVK;cF0ctzd}T2}X}l_VJ9B+S9OijUQQ7wQ7tH5;1Fc>T0)W!XIX zbWo(S_cqn|*ZwNXP>lLzCRyPVSp?`yNRbh2Mfepq9bx?Ibb#QPw!eMPwz~b_>g>BJ z((jV3swNw9TM*jm-O0~}_7#$`j|FtFhGF!G2+`F?H{9p@ny9qzl_{oc+=N(Z2jyCx z$*(>0E&(gf;$X=(Oz-g^!6GJBF>z<|1pL&Sw`7N0QQ)E2Rd=|&Qw-BnT2d0*xgv{L z9PkHoNrU|f2>|jDFy!WR1a##iMY#@Z+(4dnA5X!a?Lw~&`aHNZ=Gs3z`@<(}0UBZ+u5i{?#hJHrwF!4gKi-xNwI{(*<<{dF38S2Wm@QkAydVzHv2=kToXCZb zhMGB-3zT=}Bu^|;KiFp;S=s*hsgBwNt5Q`TOj4jYU=5RKc`-&)tbW0V=3vm!qzE8p zJ3;$$v);htg^?5*lUD6RgoIUIzd|}#|ED$^y0z0JLBNXNc5zd^2@@`zG3Y`(vc1Lk z=HOJYx|$$_3D?%qfNHTlZnyyf^z^hTUA=>6s^Q5TGCY|u6SqA^r5gn{ACbXOywtnV z#Ar{N9rcmCG$X1}idgZMjM9=P7wp*&eVdxr9DJsgnyPx_}}iWPuFXeQub100>#Xk#Mvi8UEflb9~Zi8UP&++P;KspBA}Tkrto(4 zT(_fR9QCW|D)ls#Q>kwEg$9|?BYViRCYl4E!oXQ2=?tU#2z)UXQR{!d;Pu>S9-OX( zoH~Osby;wNxGoBAD7Y{zM*T3~a?v;SwFcb4(wyT^7FwsV8q&V5l?t^(j(TWXNDu*E z%>$3ELaj_&=U3Ml;xtv&-Rw5CCgE|vzt`g18K_5uJ+m>A7@NhWBTq{ulb;}}%Oc*s`WxRMCsEp7-MbfU8AVuG|aQILcHXnf=s_z2i zm9Z@(z~~n^cxjU=>3dK?EY-yBPsxdnW*dTzhcHh(EQxf2+I(=g-uU^-`1AQ{ zu0!;oA91=*puq2|&)&Ww1|9q+Za!ZZ5Zrk_Cp9G^uu$EEm}Dkmj)hdX1)F%mY=v_G zmNNxA3?PD21{1Wpu+qh#Segv1@e8M>QC*4lb8DWpGi$e@@3Q|PKP4=gj*+}n0uqO3 zlpVAaP-(N1+*)4O7b~LqVBs{jZRXe&CaZ#GeKwrkq+p|-zjVGlOa|#eG-P{}@R;lb zZi{=@HhE}V1n}GJdxs}g1!6;p_pV_HGtqj9u+7v>CcPM=hNu!kkh>D;nL*y`a9Ow$ z71WQf2#|Em{czuIdnEs}D_xD0gZGUeW;a2y=nml!$(sb6-Ra6a7F_uFq*coF2xmjw zK(h-rgw{s`JcE)_Pm5ykZl*{38F+)N;B&WR)=Y>5~yz88CitqDhI3=tK^ zm=hOpIkftA9eqm+Kjt&lcOhqjK?$a@qDAM(ob6`sRpPE8!6eG$B>p*dSk2Z>hjoTm zqr-!U88=`|pEI~o>h*T6KZ<$eYEOTvtB5QykciOv{ZJH_U@-pm%BvJQ!74s?QaGKR zg^b$f5_+EcRTHVvb$%&a)6}OWuj^>&r+7$V;Ws&z^mj|L=`t!Mp z7ydj&L%pg;yLCKQg2{J{jd1k`x?$iUXa*=d1y^=6lC9!IVk9=5K3F`*pLxUI1&qpa zK0kAB=ea9S+Cg+OOC${Xoxzz?31NGqxyNXES9&xbp=hwmA%DS8WnNrdHg`JL|&-+G5zU7uX%Ky;`5YaI8O*c1qR3T8JE+aI~HZ+_6-tdXe{8+ zITs`eW3oAb^zJ?s$y_69@luBJ$4KC}fDxVkwgg8lo6Sc3l(*|ptV)u?O)X|4Xs?d;t)zc>`Zo{Ig$o8^ z@tJFFTS0cze8*&RHq=h!`w*i@ck)shFQa!G>5iK&~;RHulrYcvR`J_ZWh zSbM)F=rJUa3@k$_VQ8^nnnhRE6R-cpQKLf&zBfwvV4*8B@vg6Ti0Oi#H>eS{n}iE! z^toN&yuV&mEYR6^AK2ejoIE;lC+2Nh2P&tc`57L!Ws+EvQrhqNw0W!f&oNv4T4IO} z$=nqDn zosGn8c1Cp(q{%K2*{M@8s-RlM(@Qi@4TXfJ&d27_n4`w6D^KGXS%Dq#cYU8>pEzwO z^=@;r%+C6DLfP-%Zo^{HJ^RT^r7h>!&yq(&7AN=@gpC`o_BhCVh*?=w)tUa*c|zVt z;PkJEhW?s280(!FJe^i3CYJ4@NU~0w%$Ysh`7t>$@q@Ws{;ic)tGoTXE6?aR@-zAz z)yMchqfo&_)pxwCGVZa{Hw(NbUky?S6?>yW+cd3!w0Ja{0^j6+?Ow>$X%f9*Sn#t$ zocl`~39^l|^Q9L`m4<0F`#TgHU=8yNKTXEQG3T6&>TWu`3F~M%8TpEFs^w+n4iEC) zy_b4J-i(}-TAz(wRe6^XwRiOR+LF1(+Jmq!cC%XYBb2g`P_>=Bne;xZC0BjGX< zE+gSG68`r>LOlVil*J)aMpkI7K0cSR{igS|?0xt055p1*zFSdcL#@Qv95@xa%H;3| z2ma&q7X0&TYo*HUXh4%O1pBT#&a9B2I}tpJsp{^kGi zAUm<4ug&=H_*VzZKeR4G-|c>s?L;DXLF;nRmU;D_F>8bDSzLA)?{_`S|MQzn z+{%_<5+ z88je~HAsKI)5%?U#}4_+8MceiE)Tjv+3{~fxQ0SoA6B+1G-S6_hiTb(WyAe5*3|+0%@lMog4=K?qbW163V7>PnW;3mu66etjEFJyhMpLd}!+n@NmI zT9H|zmtF;z(r4MJg<=zT#{5eqe7?1bW_D4OhQS3fe=4EcUmDGE!T5X3ewrRniax8) z)1Ncp9OW*!r))eVw$Aftz^Wz9-BGZX;|vGu@#*25$gv{zj}i8s*o7Cvc_iGMd@+yy zQ2hcypjt9kF^4E%akcg(c=0H0&-R!=LtgNKB3%j0-N8ITCs6CD;b`4!1c8v`siHPp zXctagd7{@y^=BeTOx#&f$RTlwOv7Explf4MgprkvlKDEXNzOdc6doD}UtE-?3UvbE zP`wMc6!o+&thwN+2B-y67q36nDusw#ftY+vJFUY`R##cn6$m(@fyTj>!8rRz@#R3R zEMV4*cE}v25p5XMVu%!|3g8P~VX0Nx(9IWP{767R5RI2TMCbYw^(>T!UL$M9&I1jX z1HratR&ANg=($ArjIig8Jg#&U<31i7kv3-O+et=ZtH)-A6$64*7)JUS37lD-o zFgcUw-zvWrxkM$^%Uuwp=2)0=wNa+)+e^P=isi}r{mFXPwD?&aO6-sVtm>R(!SDWgv>3ZkALzaJaXbt{04P%EhiDUPP)ke6e+7YNDv! z#2e-y{s~KC$s2*MDYBdb%jp z&z9|+W<{lR^QY@i-hah2+a?;q+0S_@SCt`q@|{ffQ&%@}8;DfGR{I?iql&?oN)HyjY#o@c>N0ML5X!~RWYg`_ zh~SXTP&y|HD+u3l+_$5IdgOU5XE>ST*$v;(a6{~OCEIS&Hfur>HqJ37Sp3G}zbJG{ zwH{~vgii<<6#c!qmfW?4AFV)62lgrLWMS2G^kmhlvQgyB&jjWxip`2xt27%U6w~a| zm?u}%*1=KfPxcR}|_ik3cu#PzmRR;80Km44z$?-BNcNKRPux425}TmqW1NHBov?#1 z7W+sYDe830q4N0>QRD3-cKSY>&bMi`{8Ovln4GcrBs(Ee=r<78Lwh>_Fe!EJts;k8 zgd4D%K4u}L{*`o-8hTqgwy@#meu>B6?CC8CYklT(PKy*9KU^k9`u4icIJo;>a{9*E zx5sH_6YG_1q*YO*4t$o|GeFt>E{?RqmCocl^k4*iv^J`%ztBIK1r4sfvJ8G5k|%GJ z^mYJ$-ZKJ$t|PDXpFWaPY5KnT*L2_r8uDRtDVU3*2RYATGia~(B4XFaY^}ZNbSSz@ zq@bEus|CIFuKt_z?^DaHyB_UpgTWj+QJeF(HE$8F&vXW08<>FjlB*1{P+aGX$McIW ztngYr88R+~AK4q#@U75d(fqweG3w;z!clEP75)JsN2bdT!;FxkeS3?I?2=2p#-`-k z@&X^0dPT<{Zj}EdtP`k8Iq59dB#$WNstO2QUlz*uxVu^-l%Da@>#Xyl1DC%PwQ*JD zO$Y69)sICox%Lz02!#rsU++Fysq)hi>+}&kuTFA)1UnIxTjh^B*)vGzZ3?aigDXDZ z48or&+Fot zPU_Rin80&q;hjD^ndUJuhrIa6hR;c>RCcW9Pzx>fIO2R%iQRN_;x?5mu~mN8^vqWu zxLJ34uXZ9@{317dF7McIwZ}r#IBF87$|UxUSu~mS@Ub=5zSb78&?ElELOFR=w9_{c zbyX8b5a(7lSw+MNC{d_6gB29E*+SSO&GW1{X1maCsYwK5)ah(9& z$vyJ#-$A(;MhE{-KmVUO7(}v+UV&PsOBQ4}bhiG(i$jN*pxDRn3=2Ap1eHyf=o@tO zpa1K{H@B(5Wg849GkX2^u8f9Fe@Z8vqgl4KPV1qUvl_M;gL)m>-TP-Y*#E^|+3NSp q3h%0*j)1n(!aw4MB9@gzXQcwK?@avum+jDZ-kT7Nk5%Kre_Ds%Aw diff --git a/Ubiquitous/XiZi/board/xidatong/img/menuconfig1.png b/Ubiquitous/XiZi/board/xidatong/img/menuconfig1.png index 8cb7e6f407297453c0856fcc5ccc83ec32cb2590..cb8291f00203f4040c6369c1e053dd34692039cf 100644 GIT binary patch literal 18959 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 literal 18686 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 C#GfJn diff --git a/Ubiquitous/XiZi/board/xidatong/link-usb.lds b/Ubiquitous/XiZi/board/xidatong/link-usb.lds index 570c10d29..fc005fd55 100755 --- a/Ubiquitous/XiZi/board/xidatong/link-usb.lds +++ b/Ubiquitous/XiZi/board/xidatong/link-usb.lds @@ -54,6 +54,9 @@ STACK_SIZE = DEFINED(__stack_size__) ? __stack_size__ : 0x1000; /* Specify the memory areas */ MEMORY { + m_boot_data (RX) : ORIGIN = 0x60000000, LENGTH = 0x00001000 + m_image_vertor_table (RX) : ORIGIN = 0x60001000, LENGTH = 0x00001000 + m_interrupts (RX) : ORIGIN = 0x60002000, LENGTH = 0x00000400 m_text (RX) : ORIGIN = 0x60002400, LENGTH = 0x03FFDC00 m_data (RW) : ORIGIN = 0x20000000, LENGTH = 0x00020000 @@ -63,6 +66,17 @@ MEMORY /* Define output sections */ SECTIONS { + .boot_data : + { + KEEP(*(.boot_hdr.conf)) + } > m_boot_data + + .image_vertor_table : + { + KEEP(*(.boot_hdr.ivt)) + KEEP(*(.boot_hdr.boot_data)) + KEEP(*(.boot_hdr.dcd_data)) + } > m_image_vertor_table /* The startup code goes first into internal RAM */ .interrupts : diff --git a/Ubiquitous/XiZi/board/xidatong/link.lds b/Ubiquitous/XiZi/board/xidatong/link.lds index b4176b174..38d111b23 100644 --- a/Ubiquitous/XiZi/board/xidatong/link.lds +++ b/Ubiquitous/XiZi/board/xidatong/link.lds @@ -54,6 +54,9 @@ STACK_SIZE = DEFINED(__stack_size__) ? __stack_size__ : 0x1000; /* Specify the memory areas */ MEMORY { + m_boot_data (RX) : ORIGIN = 0x60000000, LENGTH = 0x00001000 + m_image_vertor_table (RX) : ORIGIN = 0x60001000, LENGTH = 0x00001000 + m_interrupts (RX) : ORIGIN = 0x60002000, LENGTH = 0x00000400 m_text (RX) : ORIGIN = 0x60002400, LENGTH = 0x03FFDC00 m_data (RW) : ORIGIN = 0x20000000, LENGTH = 0x00020000 @@ -63,7 +66,18 @@ MEMORY /* Define output sections */ SECTIONS { + .boot_data : + { + KEEP(*(.boot_hdr.conf)) + } > m_boot_data + .image_vertor_table : + { + KEEP(*(.boot_hdr.ivt)) + KEEP(*(.boot_hdr.boot_data)) + KEEP(*(.boot_hdr.dcd_data)) + } > m_image_vertor_table + /* The startup code goes first into internal RAM */ .interrupts : { diff --git a/Ubiquitous/XiZi/board/xidatong/third_party_driver/ch438/Kconfig b/Ubiquitous/XiZi/board/xidatong/third_party_driver/ch438/Kconfig new file mode 100644 index 000000000..389b67525 --- /dev/null +++ b/Ubiquitous/XiZi/board/xidatong/third_party_driver/ch438/Kconfig @@ -0,0 +1,39 @@ +config CH438_BUS_NAME + string + default "extuart" + +config CH438_DRIVER_NAME + string + default "extuart_drv" + +config CH438_DEVICE_NAME_0 + string + default "extuart_dev0" + +config CH438_DEVICE_NAME_1 + string + default "extuart_dev1" + +config CH438_DEVICE_NAME_2 + string + default "extuart_dev2" + +config CH438_DEVICE_NAME_3 + string + default "extuart_dev3" + +config CH438_DEVICE_NAME_4 + string + default "extuart_dev4" + +config CH438_DEVICE_NAME_5 + string + default "extuart_dev5" + +config CH438_DEVICE_NAME_6 + string + default "extuart_dev6" + +config CH438_DEVICE_NAME_7 + string + default "extuart_dev7" diff --git a/Ubiquitous/XiZi/board/xidatong/third_party_driver/ch438/Makefile b/Ubiquitous/XiZi/board/xidatong/third_party_driver/ch438/Makefile new file mode 100644 index 000000000..47968574c --- /dev/null +++ b/Ubiquitous/XiZi/board/xidatong/third_party_driver/ch438/Makefile @@ -0,0 +1,4 @@ +SRC_FILES := connect_ch438.c + + +include $(KERNEL_ROOT)/compiler.mk diff --git a/Ubiquitous/XiZi/board/xidatong/third_party_driver/ch438/connect_ch438.c b/Ubiquitous/XiZi/board/xidatong/third_party_driver/ch438/connect_ch438.c new file mode 100644 index 000000000..abead660c --- /dev/null +++ b/Ubiquitous/XiZi/board/xidatong/third_party_driver/ch438/connect_ch438.c @@ -0,0 +1,1296 @@ +/* +* 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_ch438.c +* @brief support to register ch438 pointer and function +* @version 2.0 +* @author AIIT XUOS Lab +* @date 2022-03-17 +*/ + +#include + +static const uint8 offset_addr[] = {0x00,0x10,0x20,0x30,0x08,0x18,0x28,0x38,}; /* uart offset address*/ +static const uint8 interrupt_num[8] = {0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80,}; /* SSR register data*/ +static BusType ch438_pin; +static int ch438_sem = NONE; + +static void Ch438Irq(void *parameter) +{ + KSemaphoreAbandon(ch438_sem); +} + +/** + * The time delay function. + * + * @param microseconds. + */ +static void ImxrtUdelay(uint32 us) +{ + uint32 ticks; + uint32 told, tnow, tcnt = 0; + uint32 reload = SysTick->LOAD; + + ticks = us * reload / (1000000 / TICK_PER_SECOND); + told = SysTick->VAL; + while (1) { + tnow = SysTick->VAL; + if (tnow != told) { + if (tnow < told) { + tcnt += told - tnow; + } else { + tcnt += reload - tnow + told; + } + + told = tnow; + if (tcnt >= ticks) { + break; + } + } + } +} + +void CH438SetOutput(void) +{ + struct PinParam pin_cfg; + int ret = 0; + + struct BusConfigureInfo configure_info; + configure_info.configure_cmd = OPE_CFG; + configure_info.private_data = (void *)&pin_cfg; + + pin_cfg.cmd = GPIO_CONFIG_MODE; + pin_cfg.pin = CH438_D0_PIN; + pin_cfg.mode = GPIO_CFG_OUTPUT; + + ret = BusDrvConfigure(ch438_pin->owner_driver, &configure_info); + if (ret != EOK) { + KPrintf("config CH438_D0_PIN pin %d failed!\n", CH438_D0_PIN); + return ; + } + + pin_cfg.pin = CH438_D1_PIN; + ret = BusDrvConfigure(ch438_pin->owner_driver, &configure_info); + if (ret != EOK) { + KPrintf("config CH438_D1_PIN pin %d failed!\n", CH438_D1_PIN); + return ; + } + + pin_cfg.pin = CH438_D2_PIN; + ret = BusDrvConfigure(ch438_pin->owner_driver, &configure_info); + if (ret != EOK) { + KPrintf("config CH438_D2_PIN pin %d failed!\n", CH438_D2_PIN); + return ; + } + + pin_cfg.pin = CH438_D3_PIN; + ret = BusDrvConfigure(ch438_pin->owner_driver, &configure_info); + if (ret != EOK) { + KPrintf("config CH438_D3_PIN pin %d failed!\n", CH438_D3_PIN); + return ; + } + + pin_cfg.pin = CH438_D4_PIN; + ret = BusDrvConfigure(ch438_pin->owner_driver, &configure_info); + if (ret != EOK) { + KPrintf("config CH438_D4_PIN pin %d failed!\n", CH438_D4_PIN); + return ; + } + + pin_cfg.pin = CH438_D5_PIN; + ret = BusDrvConfigure(ch438_pin->owner_driver, &configure_info); + if (ret != EOK) { + KPrintf("config CH438_D5_PIN pin %d failed!\n", CH438_D5_PIN); + return ; + } + + pin_cfg.pin = CH438_D6_PIN; + ret = BusDrvConfigure(ch438_pin->owner_driver, &configure_info); + if (ret != EOK) { + KPrintf("config CH438_D6_PIN pin %d failed!\n", CH438_D6_PIN); + return ; + } + + pin_cfg.pin = CH438_D7_PIN; + ret = BusDrvConfigure(ch438_pin->owner_driver, &configure_info); + if (ret != EOK) { + KPrintf("config CH438_D7_PIN pin %d failed!\n", CH438_D7_PIN); + return ; + } +} + +void CH438SetInput(void) +{ + struct PinParam pin_cfg; + int ret = 0; + + struct BusConfigureInfo configure_info; + configure_info.configure_cmd = OPE_CFG; + configure_info.private_data = (void *)&pin_cfg; + + pin_cfg.cmd = GPIO_CONFIG_MODE; + pin_cfg.pin = CH438_D0_PIN; + pin_cfg.mode = GPIO_CFG_INPUT_PULLUP; + ret = BusDrvConfigure(ch438_pin->owner_driver, &configure_info); + if (ret != EOK) { + KPrintf("config CH438_D0_PIN pin %d INPUT_PULLUP failed!\n", CH438_D0_PIN); + return ; + } + + pin_cfg.pin = CH438_D1_PIN; + ret = BusDrvConfigure(ch438_pin->owner_driver, &configure_info); + if (ret != EOK) { + KPrintf("config CH438_D1_PIN pin %d INPUT_PULLUP failed!\n", CH438_D1_PIN); + return ; + } + + pin_cfg.pin = CH438_D2_PIN; + ret = BusDrvConfigure(ch438_pin->owner_driver, &configure_info); + if (ret != EOK) { + KPrintf("config CH438_D2_PIN pin %d INPUT_PULLUP failed!\n", CH438_D2_PIN); + return ; + } + + pin_cfg.pin = CH438_D3_PIN; + ret = BusDrvConfigure(ch438_pin->owner_driver, &configure_info); + if (ret != EOK) { + KPrintf("config CH438_D3_PIN pin %d INPUT_PULLUP failed!\n", CH438_D3_PIN); + return ; + } + + pin_cfg.pin = CH438_D4_PIN; + ret = BusDrvConfigure(ch438_pin->owner_driver, &configure_info); + if (ret != EOK) { + KPrintf("config CH438_D4_PIN pin %d INPUT_PULLUP failed!\n", CH438_D4_PIN); + return ; + } + + pin_cfg.pin = CH438_D5_PIN; + ret = BusDrvConfigure(ch438_pin->owner_driver, &configure_info); + if (ret != EOK) { + KPrintf("config CH438_D5_PIN pin %d INPUT_PULLUP failed!\n", CH438_D5_PIN); + return ; + } + + pin_cfg.pin = CH438_D6_PIN; + ret = BusDrvConfigure(ch438_pin->owner_driver, &configure_info); + if (ret != EOK) { + KPrintf("config CH438_D6_PIN pin %d INPUT_PULLUP failed!\n", CH438_D6_PIN); + return ; + } + + pin_cfg.pin = CH438_D7_PIN; + ret = BusDrvConfigure(ch438_pin->owner_driver, &configure_info); + if (ret != EOK) { + KPrintf("config CH438_D7_PIN pin %d INPUT_PULLUP failed!\n", CH438_D7_PIN); + return ; + } +} + +/********************************************************************************************************* +** Function name: ReadCH438Data +** Function: read data from CH438 +** input: address +** +** output: data +** +** date: 2011.8.26 +**------------------------------------------------------------------------------------------------------- +** modify: +** date: +**------------------------------------------------------------------------------------------------------- +********************************************************************************************************/ +uint8 ReadCH438Data( uint8 addr ) +{ +//read data api + +//ALE WR RD is high level when not busy, when reading data, put address on the serial port and put ALE low level for a few clock + +//switch input pin, put RD low level, delay a few clock, read the data from the serial port + +//put RD and ALE high level + uint8 dat; + struct PinStat pin_stat; + struct BusBlockWriteParam write_param; + struct BusBlockReadParam read_param; + write_param.buffer = (void *)&pin_stat; + read_param.buffer = (void *)&pin_stat; + + pin_stat.val = GPIO_HIGH; + + pin_stat.pin = CH438_NWR_PIN; + BusDevWriteData(ch438_pin->owner_haldev, &write_param); + + pin_stat.pin = CH438_NRD_PIN; + BusDevWriteData(ch438_pin->owner_haldev, &write_param); + + pin_stat.pin = CH438_ALE_PIN; + BusDevWriteData(ch438_pin->owner_haldev, &write_param); + + CH438SetOutput(); + ImxrtUdelay(1); + + if (addr &0x80) { + pin_stat.val = GPIO_HIGH; + pin_stat.pin = CH438_D7_PIN; + BusDevWriteData(ch438_pin->owner_haldev, &write_param); + } else { + pin_stat.val = GPIO_LOW; + pin_stat.pin = CH438_D7_PIN; + BusDevWriteData(ch438_pin->owner_haldev, &write_param); + } + + if (addr &0x40) { + pin_stat.val = GPIO_HIGH; + pin_stat.pin = CH438_D6_PIN; + BusDevWriteData(ch438_pin->owner_haldev, &write_param); + } else { + pin_stat.val = GPIO_LOW; + pin_stat.pin = CH438_D6_PIN; + BusDevWriteData(ch438_pin->owner_haldev, &write_param); + } + + if (addr &0x20) { + pin_stat.val = GPIO_HIGH; + pin_stat.pin = CH438_D5_PIN; + BusDevWriteData(ch438_pin->owner_haldev, &write_param); + } else { + pin_stat.val = GPIO_LOW; + pin_stat.pin = CH438_D5_PIN; + BusDevWriteData(ch438_pin->owner_haldev, &write_param); + } + + if (addr &0x10) { + pin_stat.val = GPIO_HIGH; + pin_stat.pin = CH438_D4_PIN; + BusDevWriteData(ch438_pin->owner_haldev, &write_param); + } else { + pin_stat.val = GPIO_LOW; + pin_stat.pin = CH438_D4_PIN; + BusDevWriteData(ch438_pin->owner_haldev, &write_param); + } + + if (addr &0x08) { + pin_stat.val = GPIO_HIGH; + pin_stat.pin = CH438_D3_PIN; + BusDevWriteData(ch438_pin->owner_haldev, &write_param); + } else { + pin_stat.val = GPIO_LOW; + pin_stat.pin = CH438_D3_PIN; + BusDevWriteData(ch438_pin->owner_haldev, &write_param); + } + + if (addr &0x04) { + pin_stat.val = GPIO_HIGH; + pin_stat.pin = CH438_D2_PIN; + BusDevWriteData(ch438_pin->owner_haldev, &write_param); + } else { + pin_stat.val = GPIO_LOW; + pin_stat.pin = CH438_D2_PIN; + BusDevWriteData(ch438_pin->owner_haldev, &write_param); + } + + if (addr &0x02) { + pin_stat.val = GPIO_HIGH; + pin_stat.pin = CH438_D1_PIN; + BusDevWriteData(ch438_pin->owner_haldev, &write_param); + } else { + pin_stat.val = GPIO_LOW; + pin_stat.pin = CH438_D1_PIN; + BusDevWriteData(ch438_pin->owner_haldev, &write_param); + } + + if (addr &0x01) { + pin_stat.val = GPIO_HIGH; + pin_stat.pin = CH438_D0_PIN; + BusDevWriteData(ch438_pin->owner_haldev, &write_param); + } else { + pin_stat.val = GPIO_LOW; + pin_stat.pin = CH438_D0_PIN; + BusDevWriteData(ch438_pin->owner_haldev, &write_param); + } + + ImxrtUdelay(1); + + pin_stat.val = GPIO_LOW; + pin_stat.pin = CH438_ALE_PIN; + BusDevWriteData(ch438_pin->owner_haldev, &write_param); + + ImxrtUdelay(1); + + CH438SetInput(); + ImxrtUdelay(1); + + pin_stat.val = GPIO_LOW; + pin_stat.pin = CH438_NRD_PIN; + BusDevWriteData(ch438_pin->owner_haldev, &write_param); + + ImxrtUdelay(1); + + dat = 0; + + pin_stat.pin = CH438_D7_PIN; + if(BusDevReadData(ch438_pin->owner_haldev, &read_param)) + dat |= 0x80; + + pin_stat.pin = CH438_D6_PIN; + if (BusDevReadData(ch438_pin->owner_haldev, &read_param)) + dat |= 0x40; + + pin_stat.pin = CH438_D5_PIN; + if (BusDevReadData(ch438_pin->owner_haldev, &read_param)) + dat |= 0x20; + + pin_stat.pin = CH438_D4_PIN; + if (BusDevReadData(ch438_pin->owner_haldev, &read_param)) + dat |= 0x10; + + pin_stat.pin = CH438_D3_PIN; + if (BusDevReadData(ch438_pin->owner_haldev, &read_param)) + dat |= 0x08; + + pin_stat.pin = CH438_D2_PIN; + if (BusDevReadData(ch438_pin->owner_haldev, &read_param)) + dat |= 0x04; + + pin_stat.pin = CH438_D1_PIN; + if (BusDevReadData(ch438_pin->owner_haldev, &read_param)) + dat |= 0x02; + + pin_stat.pin = CH438_D0_PIN; + if (BusDevReadData(ch438_pin->owner_haldev, &read_param)) + dat |= 0x01; + + pin_stat.val = GPIO_HIGH; + pin_stat.pin = CH438_NRD_PIN; + BusDevWriteData(ch438_pin->owner_haldev, &write_param); + + pin_stat.val = GPIO_HIGH; + pin_stat.pin = CH438_ALE_PIN; + BusDevWriteData(ch438_pin->owner_haldev, &write_param); + + ImxrtUdelay(1); + + return dat; +} + +void WriteCH438Data(uint8 addr, uint8 dat) +{ + struct PinStat pin_stat; + struct BusBlockWriteParam write_param; + write_param.buffer = (void *)&pin_stat; + + pin_stat.val = GPIO_HIGH; + pin_stat.pin = CH438_ALE_PIN; + BusDevWriteData(ch438_pin->owner_haldev, &write_param); + + pin_stat.val = GPIO_HIGH; + pin_stat.pin = CH438_NRD_PIN; + BusDevWriteData(ch438_pin->owner_haldev, &write_param); + + pin_stat.val = GPIO_HIGH; + pin_stat.pin = CH438_NWR_PIN; + BusDevWriteData(ch438_pin->owner_haldev, &write_param); + + CH438SetOutput(); + ImxrtUdelay(1); + + if (addr &0x80) { + pin_stat.val = GPIO_HIGH; + pin_stat.pin = CH438_D7_PIN; + BusDevWriteData(ch438_pin->owner_haldev, &write_param); + } else { + pin_stat.val = GPIO_LOW; + pin_stat.pin = CH438_D7_PIN; + BusDevWriteData(ch438_pin->owner_haldev, &write_param); + } + + if (addr &0x40) { + pin_stat.val = GPIO_HIGH; + pin_stat.pin = CH438_D6_PIN; + BusDevWriteData(ch438_pin->owner_haldev, &write_param); + } else { + pin_stat.val = GPIO_LOW; + pin_stat.pin = CH438_D6_PIN; + BusDevWriteData(ch438_pin->owner_haldev, &write_param); + } + + if (addr &0x20) { + pin_stat.val = GPIO_HIGH; + pin_stat.pin = CH438_D5_PIN; + BusDevWriteData(ch438_pin->owner_haldev, &write_param); + } else { + pin_stat.val = GPIO_LOW; + pin_stat.pin = CH438_D5_PIN; + BusDevWriteData(ch438_pin->owner_haldev, &write_param); + } + + if (addr &0x10) { + pin_stat.val = GPIO_HIGH; + pin_stat.pin = CH438_D4_PIN; + BusDevWriteData(ch438_pin->owner_haldev, &write_param); + } else { + pin_stat.val = GPIO_LOW; + pin_stat.pin = CH438_D4_PIN; + BusDevWriteData(ch438_pin->owner_haldev, &write_param); + } + + if (addr &0x08) { + pin_stat.val = GPIO_HIGH; + pin_stat.pin = CH438_D3_PIN; + BusDevWriteData(ch438_pin->owner_haldev, &write_param); + } else { + pin_stat.val = GPIO_LOW; + pin_stat.pin = CH438_D3_PIN; + BusDevWriteData(ch438_pin->owner_haldev, &write_param); + } + + if (addr &0x04) { + pin_stat.val = GPIO_HIGH; + pin_stat.pin = CH438_D2_PIN; + BusDevWriteData(ch438_pin->owner_haldev, &write_param); + } else { + pin_stat.val = GPIO_LOW; + pin_stat.pin = CH438_D2_PIN; + BusDevWriteData(ch438_pin->owner_haldev, &write_param); + } + + if (addr &0x02) { + pin_stat.val = GPIO_HIGH; + pin_stat.pin = CH438_D1_PIN; + BusDevWriteData(ch438_pin->owner_haldev, &write_param); + } else { + pin_stat.val = GPIO_LOW; + pin_stat.pin = CH438_D1_PIN; + BusDevWriteData(ch438_pin->owner_haldev, &write_param); + } + + if (addr &0x01) { + pin_stat.val = GPIO_HIGH; + pin_stat.pin = CH438_D0_PIN; + BusDevWriteData(ch438_pin->owner_haldev, &write_param); + } else { + pin_stat.val = GPIO_LOW; + pin_stat.pin = CH438_D0_PIN; + BusDevWriteData(ch438_pin->owner_haldev, &write_param); + } + + ImxrtUdelay(1); + + pin_stat.val = GPIO_LOW; + pin_stat.pin = CH438_ALE_PIN; + BusDevWriteData(ch438_pin->owner_haldev, &write_param); + + ImxrtUdelay(1); + + if (dat &0x80) { + pin_stat.val = GPIO_HIGH; + pin_stat.pin = CH438_D7_PIN; + BusDevWriteData(ch438_pin->owner_haldev, &write_param); + } else { + pin_stat.val = GPIO_LOW; + pin_stat.pin = CH438_D7_PIN; + BusDevWriteData(ch438_pin->owner_haldev, &write_param); + } + + if (dat &0x40) { + pin_stat.val = GPIO_HIGH; + pin_stat.pin = CH438_D6_PIN; + BusDevWriteData(ch438_pin->owner_haldev, &write_param); + } else { + pin_stat.val = GPIO_LOW; + pin_stat.pin = CH438_D6_PIN; + BusDevWriteData(ch438_pin->owner_haldev, &write_param); + } + + if (dat &0x20) { + pin_stat.val = GPIO_HIGH; + pin_stat.pin = CH438_D5_PIN; + BusDevWriteData(ch438_pin->owner_haldev, &write_param); + } else { + pin_stat.val = GPIO_LOW; + pin_stat.pin = CH438_D5_PIN; + BusDevWriteData(ch438_pin->owner_haldev, &write_param); + } + + if (dat &0x10) { + pin_stat.val = GPIO_HIGH; + pin_stat.pin = CH438_D4_PIN; + BusDevWriteData(ch438_pin->owner_haldev, &write_param); + } else { + pin_stat.val = GPIO_LOW; + pin_stat.pin = CH438_D4_PIN; + BusDevWriteData(ch438_pin->owner_haldev, &write_param); + } + + if (dat &0x08) { + pin_stat.val = GPIO_HIGH; + pin_stat.pin = CH438_D3_PIN; + BusDevWriteData(ch438_pin->owner_haldev, &write_param); + } else { + pin_stat.val = GPIO_LOW; + pin_stat.pin = CH438_D3_PIN; + BusDevWriteData(ch438_pin->owner_haldev, &write_param); + } + + if (dat &0x04) { + pin_stat.val = GPIO_HIGH; + pin_stat.pin = CH438_D2_PIN; + BusDevWriteData(ch438_pin->owner_haldev, &write_param); + } else { + pin_stat.val = GPIO_LOW; + pin_stat.pin = CH438_D2_PIN; + BusDevWriteData(ch438_pin->owner_haldev, &write_param); + } + + if (dat &0x02) { + pin_stat.val = GPIO_HIGH; + pin_stat.pin = CH438_D1_PIN; + BusDevWriteData(ch438_pin->owner_haldev, &write_param); + } else { + pin_stat.val = GPIO_LOW; + pin_stat.pin = CH438_D1_PIN; + BusDevWriteData(ch438_pin->owner_haldev, &write_param); + } + + if (dat &0x01) { + pin_stat.val = GPIO_HIGH; + pin_stat.pin = CH438_D0_PIN; + BusDevWriteData(ch438_pin->owner_haldev, &write_param); + } else { + pin_stat.val = GPIO_LOW; + pin_stat.pin = CH438_D0_PIN; + BusDevWriteData(ch438_pin->owner_haldev, &write_param); + } + + ImxrtUdelay(1); + + pin_stat.val = GPIO_LOW; + pin_stat.pin = CH438_NWR_PIN; + BusDevWriteData(ch438_pin->owner_haldev, &write_param); + + ImxrtUdelay(1); + + pin_stat.val = GPIO_HIGH; + pin_stat.pin = CH438_NWR_PIN; + BusDevWriteData(ch438_pin->owner_haldev, &write_param); + + pin_stat.val = GPIO_HIGH; + pin_stat.pin = CH438_ALE_PIN; + BusDevWriteData(ch438_pin->owner_haldev, &write_param); + + ImxrtUdelay(1); + + CH438SetInput(); + + return; +} + +/********************************************************************************************************* +** Function name: WriteCH438Block +** Function: write data to CH438 +** input: CH438 register address, data block length, data cache address +** +** output: +** +** date: 2011.8.26 +**------------------------------------------------------------------------------------------------------- +** modify: +** date: +**------------------------------------------------------------------------------------------------------- +********************************************************************************************************/ +void WriteCH438Block(uint8 maddr, uint8 mlen, uint8 *mbuf) +{ + while (mlen--) { + WriteCH438Data(maddr, *mbuf++); + } +} + +/********************************************************************************************************* +** Function name: Ch438UartSend +** Function: active FIFO mode, CH438 send multibyte data by uart 0, max length is 128 bytes a single time +** input: send data cache address, send data length +** +** output: +** +** date: 2011.8.26. +**------------------------------------------------------------------------------------------------------- +** modify: +** date: +**------------------------------------------------------------------------------------------------------- +********************************************************************************************************/ +void Ch438UartSend( uint8 ext_uart_no,uint8 *data, uint8 Num ) +{ + uint8 REG_LSR_ADDR,REG_THR_ADDR; + + REG_LSR_ADDR = offset_addr[ext_uart_no] | REG_LSR0_ADDR; + REG_THR_ADDR = offset_addr[ext_uart_no] | REG_THR0_ADDR; + + while (1) { + while((ReadCH438Data(REG_LSR_ADDR) & BIT_LSR_TEMT) == 0); /* wait for sending data done, THR and TSR is NULL */ + + if (Num <= 128) { + WriteCH438Block(REG_THR_ADDR, Num, data); + break; + } else { + WriteCH438Block(REG_THR_ADDR, 128, data); + Num -= 128; + data += 128; + } + } +} + +/********************************************************************************************************* +** Function name: Ch438UartRcv +** Function: forbidden FIFO mode, CH438 receive multibyte data from uart 0 +** input: recv data address +** +** output: recv data length +** +** date: 2011.8.26 +**------------------------------------------------------------------------------------------------------- +** modify: +** date: +**------------------------------------------------------------------------------------------------------- +********************************************************************************************************/ +uint8 Ch438UartRcv(uint8 ext_uart_no, uint8 *buf, x_size_t size) +{ + uint8 rcv_num = 0; + uint8 dat = 0; + uint8 REG_LSR_ADDR,REG_RBR_ADDR; + uint8 *read_buffer; + x_size_t buffer_index = 0; + + read_buffer = buf; + + REG_LSR_ADDR = offset_addr[ext_uart_no] | REG_LSR0_ADDR; + REG_RBR_ADDR = offset_addr[ext_uart_no] | REG_RBR0_ADDR; + + while ((ReadCH438Data(REG_LSR_ADDR) & BIT_LSR_DATARDY) == 0); /* wait for data is ready */ + + while (((ReadCH438Data(REG_LSR_ADDR) & BIT_LSR_DATARDY) == 0x01) && (size != 0)) { + dat = ReadCH438Data(REG_RBR_ADDR); + + *read_buffer = dat; + read_buffer++; + buffer_index++; + + if (CH438_BUFFSIZE == buffer_index) { + buffer_index = 0; + read_buffer = buf; + } + + rcv_num = rcv_num + 1; + --size; + } + + return rcv_num; +} + +static void Timeout438Proc(void *parameter) +{ + uint8_t rbr,lsr; + + while( ( ReadCH438Data( REG_LSR0_ADDR ) & BIT_LSR_DATARDY ) == 0x01 ) { + rbr = ReadCH438Data( REG_RBR0_ADDR ); + KPrintf("0.RBR=%02x\r\n",rbr); + } + + while( ( ReadCH438Data( REG_LSR1_ADDR ) & BIT_LSR_DATARDY ) == 0x01 ) { + rbr = ReadCH438Data( REG_RBR1_ADDR ); + KPrintf("1.RBR=%02x\r\n",rbr); + } + + while( ( ReadCH438Data( REG_LSR2_ADDR ) & BIT_LSR_DATARDY ) == 0x01 ) { + rbr = ReadCH438Data( REG_RBR2_ADDR ); + KPrintf("2.RBR=%02x\r\n",rbr); + } + + while( ( ReadCH438Data( REG_LSR3_ADDR ) & BIT_LSR_DATARDY ) == 0x01 ) { + rbr = ReadCH438Data( REG_RBR3_ADDR ); + KPrintf("3.RBR=%02x\r\n",rbr); + } + + while( ( ReadCH438Data( REG_LSR4_ADDR ) & BIT_LSR_DATARDY ) == 0x01 ) { + rbr = ReadCH438Data( REG_RBR4_ADDR ); + KPrintf("4.RBR=%02x\r\n",rbr); + } + + while( ( ReadCH438Data( REG_LSR5_ADDR ) & BIT_LSR_DATARDY ) == 0x01 ) { + rbr = ReadCH438Data( REG_RBR5_ADDR ); + KPrintf("5.RBR=%02x\r\n",rbr); + } + + while( ( ReadCH438Data( REG_LSR6_ADDR ) & BIT_LSR_DATARDY ) == 0x01 ) { + rbr = ReadCH438Data( REG_RBR6_ADDR ); + KPrintf("6.RBR=%02x\r\n",rbr); + } + + while( ( ReadCH438Data( REG_LSR7_ADDR ) & BIT_LSR_DATARDY ) == 0x01 ) { + rbr = ReadCH438Data( REG_RBR7_ADDR ); + KPrintf("7.RBR=%02x\r\n",rbr); + } +} + +void Ch438PortInit( uint8 ext_uart_no,uint32 BaudRate ) +{ + uint32 div; + uint8 DLL,DLM,dlab; + uint8 REG_LCR_ADDR; + uint8 REG_DLL_ADDR; + uint8 REG_DLM_ADDR; + uint8 REG_IER_ADDR; + uint8 REG_MCR_ADDR; + uint8 REG_FCR_ADDR; + uint8 REG_RBR_ADDR; + uint8 REG_THR_ADDR; + uint8 REG_IIR_ADDR; + + REG_LCR_ADDR = offset_addr[ext_uart_no] | REG_LCR0_ADDR; + REG_DLL_ADDR = offset_addr[ext_uart_no] | REG_DLL0_ADDR; + REG_DLM_ADDR = offset_addr[ext_uart_no] | REG_DLM0_ADDR; + REG_IER_ADDR = offset_addr[ext_uart_no] | REG_IER0_ADDR; + REG_MCR_ADDR = offset_addr[ext_uart_no] | REG_MCR0_ADDR; + REG_FCR_ADDR = offset_addr[ext_uart_no] | REG_FCR0_ADDR; + REG_RBR_ADDR = offset_addr[ext_uart_no] | REG_RBR0_ADDR; + REG_THR_ADDR = offset_addr[ext_uart_no] | REG_THR0_ADDR; + REG_IIR_ADDR = offset_addr[ext_uart_no] | REG_IIR0_ADDR; + + WriteCH438Data(REG_IER_ADDR, BIT_IER_RESET); /* reset the uart */ + MdelayKTask(50); + + dlab = ReadCH438Data(REG_IER_ADDR); + dlab &= 0xDF; + WriteCH438Data(REG_IER_ADDR, dlab); + + dlab = ReadCH438Data(REG_LCR_ADDR); + dlab |= 0x80; //set LCR register DLAB bit 1 + WriteCH438Data(REG_LCR_ADDR, dlab); + + div = (Fpclk >> 4) / BaudRate; + DLM = div >> 8; + DLL = div & 0xff; + WriteCH438Data(REG_DLL_ADDR, DLL); /* set bps */ + WriteCH438Data(REG_DLM_ADDR, DLM); + WriteCH438Data(REG_FCR_ADDR, BIT_FCR_RECVTG1 | BIT_FCR_RECVTG0 | BIT_FCR_FIFOEN); /* set FIFO mode, 112 bytes */ + + WriteCH438Data(REG_LCR_ADDR, BIT_LCR_WORDSZ1 | BIT_LCR_WORDSZ0); /* 8 bit word size, 1 bit stop bit, no crc */ + + WriteCH438Data(REG_IER_ADDR, BIT_IER_IERECV); /* enable interrupt */ + + WriteCH438Data(REG_MCR_ADDR, BIT_MCR_OUT2);// | BIT_MCR_RTS | BIT_MCR_DTR ); /* allow interrupt output, DTR and RTS is 1 */ + + WriteCH438Data(REG_FCR_ADDR, ReadCH438Data(REG_FCR_ADDR)| BIT_FCR_TFIFORST); /* release the data in FIFO */ +} + +void CH438PortInitParityCheck(uint8 ext_uart_no, uint32 BaudRate) +{ + uint32 div; + uint8 DLL,DLM,dlab; + uint8 REG_LCR_ADDR; + uint8 REG_DLL_ADDR; + uint8 REG_DLM_ADDR; + uint8 REG_IER_ADDR; + uint8 REG_MCR_ADDR; + uint8 REG_FCR_ADDR; + uint8 REG_RBR_ADDR; + uint8 REG_THR_ADDR; + uint8 REG_IIR_ADDR; + + REG_LCR_ADDR = offset_addr[ext_uart_no] | REG_LCR0_ADDR; + REG_DLL_ADDR = offset_addr[ext_uart_no] | REG_DLL0_ADDR; + REG_DLM_ADDR = offset_addr[ext_uart_no] | REG_DLM0_ADDR; + REG_IER_ADDR = offset_addr[ext_uart_no] | REG_IER0_ADDR; + REG_MCR_ADDR = offset_addr[ext_uart_no] | REG_MCR0_ADDR; + REG_FCR_ADDR = offset_addr[ext_uart_no] | REG_FCR0_ADDR; + REG_RBR_ADDR = offset_addr[ext_uart_no] | REG_RBR0_ADDR; + REG_THR_ADDR = offset_addr[ext_uart_no] | REG_THR0_ADDR; + REG_IIR_ADDR = offset_addr[ext_uart_no] | REG_IIR0_ADDR; + + WriteCH438Data(REG_IER_ADDR, BIT_IER_RESET); /* reset the uart */ + MdelayKTask(50); + + dlab = ReadCH438Data(REG_IER_ADDR); + dlab &= 0xDF; + WriteCH438Data(REG_IER_ADDR, dlab); + + dlab = ReadCH438Data(REG_LCR_ADDR); + dlab |= 0x80; //set LCR register DLAB bit 1 + WriteCH438Data(REG_LCR_ADDR, dlab); + + div = (Fpclk >> 4) / BaudRate; + DLM = div >> 8; + DLL = div & 0xff; + WriteCH438Data(REG_DLL_ADDR, DLL); /* set bps */ + WriteCH438Data(REG_DLM_ADDR, DLM); + WriteCH438Data(REG_FCR_ADDR, BIT_FCR_RECVTG1 | BIT_FCR_FIFOEN); /* set FIFO mode, 64 bytes */ + + WriteCH438Data(REG_LCR_ADDR, BIT_LCR_WORDSZ1 | BIT_LCR_WORDSZ0 | BIT_LCR_PAREN | BIT_LCR_PARMODE0); /* 8 bit work size, 1 bit stop bit, even parity check */ + + WriteCH438Data(REG_IER_ADDR, BIT_IER_IERECV); /* enable interrupt */ + + WriteCH438Data(REG_MCR_ADDR, BIT_MCR_OUT2);// | BIT_MCR_RTS | BIT_MCR_DTR ); /* allow interrupt output, DTR and RTS is 1 */ + + WriteCH438Data(REG_FCR_ADDR, ReadCH438Data(REG_FCR_ADDR)| BIT_FCR_TFIFORST); /* release the data in FIFO */ +} + +static uint32 ImxrtCh438Configure(struct SerialCfgParam *ext_serial_cfg) +{ + NULL_PARAM_CHECK(ext_serial_cfg); + + switch (ext_serial_cfg->data_cfg.port_configure) + { + case PORT_CFG_INIT: + Ch438PortInit(ext_serial_cfg->data_cfg.ext_uart_no, ext_serial_cfg->data_cfg.serial_baud_rate); + break; + case PORT_CFG_PARITY_CHECK: + CH438PortInitParityCheck(ext_serial_cfg->data_cfg.ext_uart_no, ext_serial_cfg->data_cfg.serial_baud_rate); + break; + default: + KPrintf("ImxrtCh438Configure do not support configure %d\n", ext_serial_cfg->data_cfg.port_configure); + return ERROR; + break; + } + + return EOK; +} + +static uint32 ImxrtCh438Init(struct SerialDriver *serial_drv, struct SerialCfgParam *ext_serial_cfg) +{ + NULL_PARAM_CHECK(serial_drv); + + struct PinParam pin_cfg; + struct PinStat pin_stat; + int ret = 0; + + struct BusConfigureInfo configure_info; + configure_info.configure_cmd = OPE_CFG; + configure_info.private_data = (void *)&pin_cfg; + + struct BusBlockWriteParam write_param; + write_param.buffer = (void *)&pin_stat; + + ch438_pin = PinBusInitGet(); + + CH438SetOutput(); + + /* config NWR pin as output*/ + pin_cfg.cmd = GPIO_CONFIG_MODE; + pin_cfg.pin = CH438_NWR_PIN; + pin_cfg.mode = GPIO_CFG_OUTPUT; + + ret = BusDrvConfigure(ch438_pin->owner_driver, &configure_info); + if (ret != EOK) { + KPrintf("config NWR pin %d failed!\n", CH438_NWR_PIN); + return ERROR; + } + + /* config NRD pin as output*/ + KPrintf("####TEST CH438_NRD_PIN %u start####\n", CH438_NRD_PIN); + pin_cfg.pin = CH438_NRD_PIN; + ret = BusDrvConfigure(ch438_pin->owner_driver, &configure_info); + if (ret != EOK) { + KPrintf("config NRD pin %d failed!\n", CH438_NRD_PIN); + return ERROR; + } + KPrintf("####TEST CH438_NRD_PIN %u done####\n", CH438_NRD_PIN); + + /* config ALE pin as output*/ + pin_cfg.pin = CH438_ALE_PIN; + ret = BusDrvConfigure(ch438_pin->owner_driver, &configure_info); + if (ret != EOK) { + KPrintf("config ALE pin %d failed!\n", CH438_ALE_PIN); + return ERROR; + } + + /* set NWR pin as high*/ + pin_stat.pin = CH438_NWR_PIN; + pin_stat.val = GPIO_HIGH; + BusDevWriteData(ch438_pin->owner_haldev, &write_param); + + /* set NRD pin as high*/ + pin_stat.pin = CH438_NRD_PIN; + pin_stat.val = GPIO_HIGH; + BusDevWriteData(ch438_pin->owner_haldev, &write_param); + + /* set ALE pin as high*/ + pin_stat.pin = CH438_ALE_PIN; + pin_stat.val = GPIO_HIGH; + BusDevWriteData(ch438_pin->owner_haldev, &write_param); + + return ImxrtCh438Configure(ext_serial_cfg); +} + +static uint32 Ch438DrvConfigure(void *drv, struct BusConfigureInfo *configure_info) +{ + NULL_PARAM_CHECK(drv); + NULL_PARAM_CHECK(configure_info); + + x_err_t ret = EOK; + + struct SerialDriver *serial_drv = (struct SerialDriver *)drv; + struct SerialCfgParam *ext_serial_cfg = (struct SerialCfgParam *)configure_info->private_data; + + switch (configure_info->configure_cmd) + { + case OPE_INT: + ret = ImxrtCh438Init(serial_drv, ext_serial_cfg); + break; + default: + break; + } + + return ret; +} + +static uint32 ImxrtCh438WriteData(void *dev, struct BusBlockWriteParam *write_param) +{ + NULL_PARAM_CHECK(dev); + NULL_PARAM_CHECK(write_param); + + struct SerialHardwareDevice *serial_dev = (struct SerialHardwareDevice *)dev; + struct SerialDevParam *dev_param = (struct SerialDevParam *)serial_dev->haldev.private_data; + + Ch438UartSend(dev_param->ext_uart_no, (uint8 *)write_param->buffer, write_param->size); + + return EOK; +} + +static uint32 ImxrtCh438ReadData(void *dev, struct BusBlockReadParam *read_param) +{ + NULL_PARAM_CHECK(dev); + NULL_PARAM_CHECK(read_param); + + x_err_t result; + uint8 rcv_num = 0; + uint8 gInterruptStatus; + uint8 InterruptStatus; + static uint8 dat; + uint8 REG_LCR_ADDR; + uint8 REG_DLL_ADDR; + uint8 REG_DLM_ADDR; + uint8 REG_IER_ADDR; + uint8 REG_MCR_ADDR; + uint8 REG_FCR_ADDR; + uint8 REG_RBR_ADDR; + uint8 REG_THR_ADDR; + uint8 REG_IIR_ADDR; + uint8 REG_LSR_ADDR; + uint8 REG_MSR_ADDR; + + struct SerialHardwareDevice *serial_dev = (struct SerialHardwareDevice *)dev; + struct SerialDevParam *dev_param = (struct SerialDevParam *)serial_dev->haldev.private_data; + + result = KSemaphoreObtain(ch438_sem, WAITING_FOREVER); + if (EOK == result) { + gInterruptStatus = ReadCH438Data(REG_SSR_ADDR); + if (!gInterruptStatus) { + dat = ReadCH438Data(REG_LCR0_ADDR); + dat = ReadCH438Data(REG_IER0_ADDR); + dat = ReadCH438Data(REG_MCR0_ADDR); + dat = ReadCH438Data(REG_LSR0_ADDR); + dat = ReadCH438Data(REG_MSR0_ADDR); + dat = ReadCH438Data(REG_RBR0_ADDR); + dat = ReadCH438Data(REG_THR0_ADDR); + dat = ReadCH438Data(REG_IIR0_ADDR); + dat = dat ; + } else { + if (gInterruptStatus & interrupt_num[dev_param->ext_uart_no]) { /* check which uart port triggers interrupt*/ + REG_LCR_ADDR = offset_addr[dev_param->ext_uart_no] | REG_LCR0_ADDR; + REG_DLL_ADDR = offset_addr[dev_param->ext_uart_no] | REG_DLL0_ADDR; + REG_DLM_ADDR = offset_addr[dev_param->ext_uart_no] | REG_DLM0_ADDR; + REG_IER_ADDR = offset_addr[dev_param->ext_uart_no] | REG_IER0_ADDR; + REG_MCR_ADDR = offset_addr[dev_param->ext_uart_no] | REG_MCR0_ADDR; + REG_FCR_ADDR = offset_addr[dev_param->ext_uart_no] | REG_FCR0_ADDR; + REG_RBR_ADDR = offset_addr[dev_param->ext_uart_no] | REG_RBR0_ADDR; + REG_THR_ADDR = offset_addr[dev_param->ext_uart_no] | REG_THR0_ADDR; + REG_IIR_ADDR = offset_addr[dev_param->ext_uart_no] | REG_IIR0_ADDR; + REG_LSR_ADDR = offset_addr[dev_param->ext_uart_no] | REG_LSR0_ADDR; + REG_MSR_ADDR = offset_addr[dev_param->ext_uart_no] | REG_MSR0_ADDR; + + InterruptStatus = ReadCH438Data( REG_IIR_ADDR ) & 0x0f; /* read the status of the uart port*/ + + switch( InterruptStatus ) + { + case INT_NOINT: /* NO INTERRUPT */ + break; + case INT_THR_EMPTY: /* THR EMPTY INTERRUPT*/ + break; + case INT_RCV_OVERTIME: /* RECV OVERTIME INTERRUPT*/ + case INT_RCV_SUCCESS: /* RECV INTERRUPT SUCCESSFULLY*/ + rcv_num = Ch438UartRcv(dev_param->ext_uart_no, (uint8 *)read_param->buffer, read_param->size); + read_param->read_length = rcv_num; + break; + case INT_RCV_LINES: /* RECV LINES INTERRUPT */ + ReadCH438Data( REG_LSR_ADDR ); + break; + case INT_MODEM_CHANGE: /* MODEM CHANGE INTERRUPT */ + ReadCH438Data( REG_MSR_ADDR ); + break; + default: + break; + } + } + } + } + return rcv_num; +} + +static const struct SerialDevDone dev_done = +{ + NONE, + NONE, + ImxrtCh438WriteData, + ImxrtCh438ReadData, +}; + +static void Ch438InitDefault(struct SerialDriver *serial_drv) +{ + struct PinParam PinCfg; + BusType ch438_pin; + + struct BusConfigureInfo configure_info; + + int ret = 0; + configure_info.configure_cmd = OPE_CFG; + configure_info.private_data = (void *)&PinCfg; + + ch438_sem = KSemaphoreCreate(0); + if (ch438_sem < 0) { + KPrintf("Ch438InitDefault create sem failed .\n"); + return; + } + + ch438_pin = PinBusInitGet(); + + PinCfg.cmd = GPIO_CONFIG_MODE; + PinCfg.pin = CH438_INT_PIN; + PinCfg.mode = GPIO_CFG_INPUT_PULLUP; + ret = BusDrvConfigure(ch438_pin->owner_driver, &configure_info); + if (ret != EOK) { + KPrintf("config BSP_CH438_INT_PIN pin %d failed!\n", CH438_INT_PIN); + return; + } + + PinCfg.cmd = GPIO_IRQ_REGISTER; + PinCfg.pin = CH438_INT_PIN; + PinCfg.irq_set.irq_mode = GPIO_IRQ_EDGE_FALLING; + PinCfg.irq_set.hdr = Ch438Irq; + PinCfg.irq_set.args = NONE; + ret = BusDrvConfigure(ch438_pin->owner_driver, &configure_info); + if (ret != EOK) { + KPrintf("config BSP_CH438_INT_PIN %d failed!\n", CH438_INT_PIN); + return; + } + + PinCfg.cmd = GPIO_IRQ_ENABLE; + PinCfg.pin = CH438_INT_PIN; + ret = BusDrvConfigure(ch438_pin->owner_driver, &configure_info); + if (ret != EOK) { + KPrintf("config BSP_CH438_INT_PIN %d failed!\n", CH438_INT_PIN); + return; + } + + struct SerialCfgParam serial_cfg; + memset(&serial_cfg, 0, sizeof(struct SerialCfgParam)); + configure_info.configure_cmd = OPE_INT; + configure_info.private_data = (void *)&serial_cfg; + + serial_cfg.data_cfg.port_configure = PORT_CFG_INIT; + + serial_cfg.data_cfg.ext_uart_no = 0; + serial_cfg.data_cfg.serial_baud_rate = BAUD_RATE_115200; + BusDrvConfigure(&serial_drv->driver, &configure_info); + + serial_cfg.data_cfg.ext_uart_no = 1; + serial_cfg.data_cfg.serial_baud_rate = BAUD_RATE_9600; + BusDrvConfigure(&serial_drv->driver, &configure_info); + + serial_cfg.data_cfg.ext_uart_no = 2; + serial_cfg.data_cfg.serial_baud_rate = BAUD_RATE_9600; + BusDrvConfigure(&serial_drv->driver, &configure_info); + + serial_cfg.data_cfg.ext_uart_no = 3; + serial_cfg.data_cfg.serial_baud_rate = BAUD_RATE_9600; + BusDrvConfigure(&serial_drv->driver, &configure_info); + + serial_cfg.data_cfg.ext_uart_no = 4; + serial_cfg.data_cfg.serial_baud_rate = BAUD_RATE_9600; + BusDrvConfigure(&serial_drv->driver, &configure_info); + + serial_cfg.data_cfg.ext_uart_no = 5; + serial_cfg.data_cfg.serial_baud_rate = BAUD_RATE_9600; + BusDrvConfigure(&serial_drv->driver, &configure_info); + + serial_cfg.data_cfg.ext_uart_no = 6; + serial_cfg.data_cfg.serial_baud_rate = BAUD_RATE_9600; + BusDrvConfigure(&serial_drv->driver, &configure_info); + + serial_cfg.data_cfg.ext_uart_no = 7; + serial_cfg.data_cfg.serial_baud_rate = BAUD_RATE_9600; + BusDrvConfigure(&serial_drv->driver, &configure_info); +} + +static uint32 ImxrtCh438DevRegister(struct SerialHardwareDevice *serial_dev, char *dev_name) +{ + x_err_t ret = EOK; + serial_dev->dev_done = &dev_done; + serial_dev->ext_serial_mode = RET_TRUE; + ret = SerialDeviceRegister(serial_dev, NONE, dev_name); + if (ret != EOK) { + KPrintf("ImxrtHwCh438Init Serial device %s register error %d\n", dev_name, ret); + return ERROR; + } + + ret = SerialDeviceAttachToBus(dev_name, CH438_BUS_NAME); + if (ret != EOK) { + KPrintf("ImxrtHwCh438Init Serial device %s register error %d\n", dev_name, ret); + return ERROR; + } + + return ret; +} + +int Imxrt1052HwCh438Init(void) +{ + static struct SerialBus serial_bus; + static struct SerialDriver serial_drv; + + x_err_t ret = EOK; + + ret = SerialBusInit(&serial_bus, CH438_BUS_NAME); + if (ret != EOK) { + KPrintf("ImxrtHwCh438Init Serial bus init error %d\n", ret); + return ERROR; + } + + serial_drv.configure = Ch438DrvConfigure; + ret = SerialDriverInit(&serial_drv, CH438_DRIVER_NAME); + if (ret != EOK) { + KPrintf("ImxrtHwCh438Init Serial driver init error %d\n", ret); + return ERROR; + } + + ret = SerialDriverAttachToBus(CH438_DRIVER_NAME, CH438_BUS_NAME); + if (ret != EOK) { + KPrintf("ImxrtHwCh438Init Serial driver attach error %d\n", ret); + return ERROR; + } + + static struct SerialHardwareDevice serial_dev_0; + static struct SerialDevParam dev_param_0; + dev_param_0.ext_uart_no = 0; + serial_dev_0.haldev.private_data = (void *)&dev_param_0; + ret = ImxrtCh438DevRegister(&serial_dev_0, CH438_DEVICE_NAME_0); + if (ret != EOK) { + KPrintf("ImxrtHwCh438Init ImxrtCh438DevRegister error %d\n", ret); + return ERROR; + } + + static struct SerialHardwareDevice serial_dev_1; + static struct SerialDevParam dev_param_1; + dev_param_1.ext_uart_no = 1; + serial_dev_1.haldev.private_data = (void *)&dev_param_1; + ret = ImxrtCh438DevRegister(&serial_dev_1, CH438_DEVICE_NAME_1); + if (ret != EOK) { + KPrintf("ImxrtHwCh438Init ImxrtCh438DevRegister error %d\n", ret); + return ERROR; + } + + static struct SerialHardwareDevice serial_dev_2; + static struct SerialDevParam dev_param_2; + dev_param_2.ext_uart_no = 2; + serial_dev_2.haldev.private_data = (void *)&dev_param_2; + ret = ImxrtCh438DevRegister(&serial_dev_2, CH438_DEVICE_NAME_2); + if (ret != EOK) { + KPrintf("ImxrtHwCh438Init ImxrtCh438DevRegister error %d\n", ret); + return ERROR; + } + + static struct SerialHardwareDevice serial_dev_3; + static struct SerialDevParam dev_param_3; + dev_param_3.ext_uart_no = 3; + serial_dev_3.haldev.private_data = (void *)&dev_param_3; + ret = ImxrtCh438DevRegister(&serial_dev_3, CH438_DEVICE_NAME_3); + if (ret != EOK) { + KPrintf("ImxrtHwCh438Init ImxrtCh438DevRegister error %d\n", ret); + return ERROR; + } + + static struct SerialHardwareDevice serial_dev_4; + static struct SerialDevParam dev_param_4; + dev_param_4.ext_uart_no = 4; + serial_dev_4.haldev.private_data = (void *)&dev_param_4; + ret = ImxrtCh438DevRegister(&serial_dev_4, CH438_DEVICE_NAME_4); + if (ret != EOK) { + KPrintf("ImxrtHwCh438Init ImxrtCh438DevRegister error %d\n", ret); + return ERROR; + } + + static struct SerialHardwareDevice serial_dev_5; + static struct SerialDevParam dev_param_5; + dev_param_5.ext_uart_no = 5; + serial_dev_5.haldev.private_data = (void *)&dev_param_5; + ret = ImxrtCh438DevRegister(&serial_dev_5, CH438_DEVICE_NAME_5); + if (ret != EOK) { + KPrintf("ImxrtHwCh438Init ImxrtCh438DevRegister error %d\n", ret); + return ERROR; + } + + static struct SerialHardwareDevice serial_dev_6; + static struct SerialDevParam dev_param_6; + dev_param_6.ext_uart_no = 6; + serial_dev_6.haldev.private_data = (void *)&dev_param_6; + ret = ImxrtCh438DevRegister(&serial_dev_6, CH438_DEVICE_NAME_6); + if (ret != EOK) { + KPrintf("ImxrtHwCh438Init ImxrtCh438DevRegister error %d\n", ret); + return ERROR; + } + + static struct SerialHardwareDevice serial_dev_7; + static struct SerialDevParam dev_param_7; + dev_param_7.ext_uart_no = 7; + serial_dev_7.haldev.private_data = (void *)&dev_param_7; + ret = ImxrtCh438DevRegister(&serial_dev_7, CH438_DEVICE_NAME_7); + if (ret != EOK) { + KPrintf("ImxrtHwCh438Init ImxrtCh438DevRegister error %d\n", ret); + return ERROR; + } + + Ch438InitDefault(&serial_drv); + + return ret; +} + +void CH438RegTest(unsigned char num)//for test +{ + uint8 dat; + + KPrintf("current test serilnum: %02x \r\n",offset_addr[num]); + KPrintf("IER: %02x\r\n",ReadCH438Data(offset_addr[num] | REG_IER0_ADDR));//?IER + KPrintf("IIR: %02x\r\n",ReadCH438Data(offset_addr[num] | REG_IIR0_ADDR));//?IIR + KPrintf("LCR: %02x\r\n",ReadCH438Data(offset_addr[num] | REG_LCR0_ADDR));//?LCR + KPrintf("MCR: %02x\r\n",ReadCH438Data(offset_addr[num] | REG_MCR0_ADDR));//?MCR + KPrintf("LSR: %02x\r\n",ReadCH438Data(offset_addr[num] | REG_LSR0_ADDR));//?LSR + KPrintf("MSR: %02x\r\n",ReadCH438Data(offset_addr[num] | REG_MSR0_ADDR));//?MSR + KPrintf("FCR: %02x\r\n",ReadCH438Data(offset_addr[num] | REG_FCR0_ADDR));//?FCR + KPrintf("SSR: %02x\r\n",ReadCH438Data( offset_addr[num] | REG_SSR_ADDR ));//?SSR + + KPrintf("SCR0: %02x\r\n",(unsigned short)ReadCH438Data(offset_addr[num] | REG_SCR0_ADDR));//?SCR + dat = 0x55; + WriteCH438Data(offset_addr[num] | REG_SCR0_ADDR, dat); + KPrintf("SCR55: %02x\r\n",(unsigned short)ReadCH438Data(offset_addr[num] | REG_SCR0_ADDR));//?SCR + dat = 0xAA; + WriteCH438Data(offset_addr[num] | REG_SCR0_ADDR, dat); + KPrintf("SCRAA: %02x\r\n",(unsigned short)ReadCH438Data(offset_addr[num] | REG_SCR0_ADDR));//?SCR +} diff --git a/Ubiquitous/XiZi/board/xidatong/third_party_driver/common/pin_mux.c b/Ubiquitous/XiZi/board/xidatong/third_party_driver/common/pin_mux.c index 27013148d..63eed88b2 100755 --- a/Ubiquitous/XiZi/board/xidatong/third_party_driver/common/pin_mux.c +++ b/Ubiquitous/XiZi/board/xidatong/third_party_driver/common/pin_mux.c @@ -8,9 +8,9 @@ /** * @file pin_mux.c * @brief support imxrt1052-board pin configure -* @version 1.0 +* @version 2.0 * @author AIIT XUOS Lab -* @date 2021-05-29 +* @date 2022-03-22 */ /*********************************************************************************************************************** @@ -749,121 +749,9 @@ BOARD_InitPins: * Description : Configures pin routing and optionally pin electrical features. * * END ****************************************************************************************************************/ - -void BOARD_InitSPIPins ( void ) -{ - IOMUXC_SetPinMux ( - IOMUXC_GPIO_AD_B1_15_LPSPI3_SCK, /* GPIO_AD_B0_00 is configured as LPSPI3_SCK */ - 0U ); /* Software Input On Field: Input Path is determined by functionality */ - IOMUXC_SetPinMux ( - IOMUXC_GPIO_AD_B1_14_LPSPI3_SDO, /* GPIO_AD_B0_01 is configured as LPSPI3_SDO */ - 0U ); /* Software Input On Field: Input Path is determined by functionality */ - IOMUXC_SetPinMux ( - IOMUXC_GPIO_AD_B1_13_LPSPI3_SDI, /* GPIO_AD_B0_02 is configured as LPSPI3_SDI */ - 0U ); /* Software Input On Field: Input Path is determined by functionality */ - IOMUXC_SetPinMux ( - IOMUXC_GPIO_AD_B1_12_LPSPI3_PCS0, /* GPIO_AD_B0_03 is configured as LPSPI3_PCS0 */ - 0U ); /* Software Input On Field: Input Path is determined by functionality */ - IOMUXC_SetPinMux ( - IOMUXC_GPIO_SD_B0_00_LPSPI1_SCK, /* GPIO_SD_B0_00 is configured as LPSPI1_SCK */ - 0U ); /* Software Input On Field: Input Path is determined by functionality */ - IOMUXC_SetPinMux ( - IOMUXC_GPIO_SD_B0_01_LPSPI1_PCS0, /* GPIO_SD_B0_01 is configured as LPSPI1_PCS0 */ - 0U ); /* Software Input On Field: Input Path is determined by functionality */ - IOMUXC_SetPinMux ( - IOMUXC_GPIO_SD_B0_02_LPSPI1_SDO, /* GPIO_SD_B0_02 is configured as LPSPI1_SDO */ - 0U ); /* Software Input On Field: Input Path is determined by functionality */ - IOMUXC_SetPinMux ( - IOMUXC_GPIO_SD_B0_03_LPSPI1_SDI, /* GPIO_SD_B0_03 is configured as LPSPI1_SDI */ - 0U ); /* Software Input On Field: Input Path is determined by functionality */ - - IOMUXC_SetPinConfig ( - IOMUXC_GPIO_SD_B0_00_LPSPI1_SCK, /* GPIO_AD_B0_00 PAD functional properties : */ - 0x10B0u ); /* Slew Rate Field: Slow Slew Rate - Drive Strength Field: R0/6 - Speed Field: medium(100MHz) - Open Drain Enable Field: Open Drain Disabled - Pull / Keep Enable Field: Pull/Keeper Enabled - Pull / Keep Select Field: Keeper - Pull Up / Down Config. Field: 100K Ohm Pull Down - Hyst. Enable Field: Hysteresis Disabled */ - IOMUXC_SetPinConfig ( - IOMUXC_GPIO_AD_B1_14_LPSPI3_SDO, /* GPIO_AD_B0_01 PAD functional properties : */ - 0x10B0u ); /* Slew Rate Field: Slow Slew Rate - Drive Strength Field: R0/6 - Speed Field: medium(100MHz) - Open Drain Enable Field: Open Drain Disabled - Pull / Keep Enable Field: Pull/Keeper Enabled - Pull / Keep Select Field: Keeper - Pull Up / Down Config. Field: 100K Ohm Pull Down - Hyst. Enable Field: Hysteresis Disabled */ - IOMUXC_SetPinConfig ( - IOMUXC_GPIO_AD_B1_13_LPSPI3_SDI, /* GPIO_AD_B0_02 PAD functional properties : */ - 0x10B0u ); /* Slew Rate Field: Slow Slew Rate - Drive Strength Field: R0/6 - Speed Field: medium(100MHz) - Open Drain Enable Field: Open Drain Disabled - Pull / Keep Enable Field: Pull/Keeper Enabled - Pull / Keep Select Field: Keeper - Pull Up / Down Config. Field: 100K Ohm Pull Down - Hyst. Enable Field: Hysteresis Disabled */ - IOMUXC_SetPinConfig ( - IOMUXC_GPIO_AD_B1_12_LPSPI3_PCS0, /* GPIO_AD_B0_03 PAD functional properties : */ - 0x10B0u ); /* Slew Rate Field: Slow Slew Rate - Drive Strength Field: R0/6 - Speed Field: medium(100MHz) - Open Drain Enable Field: Open Drain Disabled - Pull / Keep Enable Field: Pull/Keeper Enabled - Pull / Keep Select Field: Keeper - Pull Up / Down Config. Field: 100K Ohm Pull Down - Hyst. Enable Field: Hysteresis Disabled */ - IOMUXC_SetPinConfig ( - IOMUXC_GPIO_SD_B0_00_LPSPI1_SCK, /* GPIO_SD_B0_00 PAD functional properties : */ - 0x10B0u ); /* Slew Rate Field: Slow Slew Rate - Drive Strength Field: R0/6 - Speed Field: medium(100MHz) - Open Drain Enable Field: Open Drain Disabled - Pull / Keep Enable Field: Pull/Keeper Enabled - Pull / Keep Select Field: Keeper - Pull Up / Down Config. Field: 100K Ohm Pull Down - Hyst. Enable Field: Hysteresis Disabled */ - IOMUXC_SetPinConfig ( - IOMUXC_GPIO_SD_B0_01_LPSPI1_PCS0, /* GPIO_SD_B0_01 PAD functional properties : */ - 0x10B0u ); /* Slew Rate Field: Slow Slew Rate - Drive Strength Field: R0/6 - Speed Field: medium(100MHz) - Open Drain Enable Field: Open Drain Disabled - Pull / Keep Enable Field: Pull/Keeper Enabled - Pull / Keep Select Field: Keeper - Pull Up / Down Config. Field: 100K Ohm Pull Down - Hyst. Enable Field: Hysteresis Disabled */ - IOMUXC_SetPinConfig ( - IOMUXC_GPIO_SD_B0_02_LPSPI1_SDO, /* GPIO_SD_B0_02 PAD functional properties : */ - 0x10B0u ); /* Slew Rate Field: Slow Slew Rate - Drive Strength Field: R0/6 - Speed Field: medium(100MHz) - Open Drain Enable Field: Open Drain Disabled - Pull / Keep Enable Field: Pull/Keeper Enabled - Pull / Keep Select Field: Keeper - Pull Up / Down Config. Field: 100K Ohm Pull Down - Hyst. Enable Field: Hysteresis Disabled */ - IOMUXC_SetPinConfig ( - IOMUXC_GPIO_SD_B0_03_LPSPI1_SDI, /* GPIO_SD_B0_03 PAD functional properties : */ - 0x10B0u ); /* Slew Rate Field: Slow Slew Rate - Drive Strength Field: R0/6 - Speed Field: medium(100MHz) - Open Drain Enable Field: Open Drain Disabled - Pull / Keep Enable Field: Pull/Keeper Enabled - Pull / Keep Select Field: Keeper - Pull Up / Down Config. Field: 100K Ohm Pull Down - Hyst. Enable Field: Hysteresis Disabled */ -} void BOARD_InitPins(void) { CLOCK_EnableClock(kCLOCK_Iomuxc); /* iomuxc clock (iomuxc_clk_enable): 0x03u */ - -// IOMUXC_SetPinMux( -// IOMUXC_GPIO_AD_B0_09_GPIO1_IO09, /* GPIO_AD_B0_09 is configured as GPIO1_IO09 */ -// 0U); /* Software Input On Field: Input Path is determined by functionality */ + /* Software Input On Field: Input Path is determined by functionality */ SemcPinmuxConfig(); IOMUXC_SetPinMux( @@ -914,16 +802,6 @@ void BOARD_InitPins(void) { IOMUXC_SetPinMux( IOMUXC_GPIO_EMC_41_ENET_MDIO, /* GPIO_EMC_41 is configured as ENET_MDIO */ 0U); /* Software Input On Field: Input Path is determined by functionality */ -// IOMUXC_SetPinConfig( -// IOMUXC_GPIO_AD_B0_09_GPIO1_IO09, /* GPIO_AD_B0_09 PAD functional properties : */ -// 0xB0A9u); /* Slew Rate Field: Fast Slew Rate -// Drive Strength Field: R0/5 -// Speed Field: medium(100MHz) -// Open Drain Enable Field: Open Drain Disabled -// Pull / Keep Enable Field: Pull/Keeper Enabled -// Pull / Keep Select Field: Pull -// Pull Up / Down Config. Field: 100K Ohm Pull Up -// Hyst. Enable Field: Hysteresis Disabled */ IOMUXC_SetPinConfig( IOMUXC_GPIO_AD_B0_03_GPIO1_IO03, /* GPIO_AD_B0_09 PAD functional properties : */ @@ -1073,37 +951,6 @@ void BOARD_InitPins(void) { Hyst. Enable Field: Hysteresis Disabled */ } -void BOARD_InitI2C1Pins ( void ) -{ - CLOCK_EnableClock ( kCLOCK_Iomuxc ); /* iomuxc clock (iomuxc_clk_enable): 0x03u */ - IOMUXC_SetPinMux ( - IOMUXC_GPIO_AD_B1_00_LPI2C1_SCL, /* GPIO_AD_B1_00 is configured as LPI2C1_SCL */ - 1U ); /* Software Input On Field: Force input path of pad GPIO_AD_B1_00 */ - IOMUXC_SetPinMux ( - IOMUXC_GPIO_AD_B1_01_LPI2C1_SDA, /* GPIO_AD_B1_01 is configured as LPI2C1_SDA */ - 1U ); /* Software Input On Field: Force input path of pad GPIO_AD_B1_01 */ - IOMUXC_SetPinConfig ( - IOMUXC_GPIO_AD_B1_00_LPI2C1_SCL, /* GPIO_AD_B1_00 PAD functional properties : */ - 0xD8B0u ); /* Slew Rate Field: Slow Slew Rate - Drive Strength Field: R0/6 - Speed Field: medium(100MHz) - Open Drain Enable Field: Open Drain Enabled - Pull / Keep Enable Field: Pull/Keeper Enabled - Pull / Keep Select Field: Keeper - Pull Up / Down Config. Field: 22K Ohm Pull Up - Hyst. Enable Field: Hysteresis Disabled */ - IOMUXC_SetPinConfig ( - IOMUXC_GPIO_AD_B1_01_LPI2C1_SDA, /* GPIO_AD_B1_01 PAD functional properties : */ - 0xD8B0u ); /* Slew Rate Field: Slow Slew Rate - Drive Strength Field: R0/6 - Speed Field: medium(100MHz) - Open Drain Enable Field: Open Drain Enabled - Pull / Keep Enable Field: Pull/Keeper Enabled - Pull / Keep Select Field: Keeper - Pull Up / Down Config. Field: 22K Ohm Pull Up - Hyst. Enable Field: Hysteresis Disabled */ -} - void BOARD_InitUartPins(void) { #ifdef BSP_USING_LPUART1 @@ -1150,94 +997,6 @@ void BOARD_InitUartPins(void) #endif } -void BOARD_InitCh438Pins(void) -{ - IOMUXC_SetPinMux( - IOMUXC_GPIO_AD_B1_09_GPIO1_IO25, /* GPIO1_IO25 is configured as CH438_DATA0 */ - 0U); - IOMUXC_SetPinMux( - IOMUXC_GPIO_AD_B1_08_GPIO1_IO24, /* GPIO1_IO24 is configured as CH438_DATA1 */ - 0U); - IOMUXC_SetPinMux( - IOMUXC_GPIO_AD_B1_04_GPIO1_IO20, /* GPIO1_IO20 is configured as CH438_DATA2 */ - 0U); - IOMUXC_SetPinMux( - IOMUXC_GPIO_AD_B1_05_GPIO1_IO21, /* GPIO1_IO21 is configured as CH438_DATA3 */ - 0U); - IOMUXC_SetPinMux( - IOMUXC_GPIO_AD_B1_15_GPIO1_IO31, /* GPIO1_IO31 is configured as CH438_DATA4 */ - 0U); - IOMUXC_SetPinMux( - IOMUXC_GPIO_AD_B1_12_GPIO1_IO28, /* GPIO1_IO28 is configured as CH438_DATA5 */ - 0U); - IOMUXC_SetPinMux( - IOMUXC_GPIO_AD_B1_14_GPIO1_IO30, /* GPIO1_IO30 is configured as CH438_DATA6 */ - 0U); - IOMUXC_SetPinMux( - IOMUXC_GPIO_AD_B1_13_GPIO1_IO29, /* GPIO1_IO29 is configured as CH438_DATA7 */ - 0U); - IOMUXC_SetPinMux( - IOMUXC_GPIO_SD_B1_04_GPIO3_IO04, /* GPIO3_IO04 is configured as CH438_nWR */ - 0U); - IOMUXC_SetPinMux( - IOMUXC_GPIO_SD_B1_05_GPIO3_IO05, /* GPIO3_IO05 is configured as CH438_nRD */ - 0U); - IOMUXC_SetPinMux( - IOMUXC_GPIO_SD_B1_02_GPIO3_IO02, /* GPIO3_IO02 is configured as CH438_ALE */ - 0U); - IOMUXC_SetPinMux( - IOMUXC_GPIO_SD_B1_03_GPIO3_IO03, /* GPIO3_IO03 is configured as CH438_INT */ - 0U); - IOMUXC_SetPinMux( - IOMUXC_GPIO_AD_B0_02_GPIO1_IO02, /* GPIO3_IO02 is configured as CH438_485_A_DIR */ - 0U); - IOMUXC_SetPinMux( - IOMUXC_GPIO_AD_B0_00_GPIO1_IO00, /* GPIO1_IO00 is configured as CH438_485_B_DIR */ - 0U); - - IOMUXC_SetPinConfig( - IOMUXC_GPIO_AD_B1_09_GPIO1_IO25, /* GPIO1_IO25 is configured as CH438_DATA0 */ - 0x10B0u); - IOMUXC_SetPinConfig( - IOMUXC_GPIO_AD_B1_08_GPIO1_IO24, /* GPIO1_IO24 is configured as CH438_DATA1 */ - 0x10B0u); - IOMUXC_SetPinConfig( - IOMUXC_GPIO_AD_B1_04_GPIO1_IO20, /* GPIO1_IO20 is configured as CH438_DATA2 */ - 0x10B0u); - IOMUXC_SetPinConfig( - IOMUXC_GPIO_AD_B1_05_GPIO1_IO21, /* GPIO1_IO21 is configured as CH438_DATA3 */ - 0x10B0u); - IOMUXC_SetPinConfig( - IOMUXC_GPIO_AD_B1_15_GPIO1_IO31, /* GPIO1_IO31 is configured as CH438_DATA4 */ - 0x10B0u); - IOMUXC_SetPinConfig( - IOMUXC_GPIO_AD_B1_12_GPIO1_IO28, /* GPIO1_IO28 is configured as CH438_DATA5 */ - 0x10B0u); - IOMUXC_SetPinConfig( - IOMUXC_GPIO_AD_B1_14_GPIO1_IO30, /* GPIO1_IO30 is configured as CH438_DATA6 */ - 0x10B0u); - IOMUXC_SetPinConfig( - IOMUXC_GPIO_AD_B1_13_GPIO1_IO29, /* GPIO1_IO29 is configured as CH438_DATA7 */ - 0x10B0u); - IOMUXC_SetPinConfig( - IOMUXC_GPIO_SD_B1_04_GPIO3_IO04, /* GPIO3_IO04 is configured as CH438_nWR */ - 0x10B0u); - IOMUXC_SetPinConfig( - IOMUXC_GPIO_SD_B1_05_GPIO3_IO05, /* GPIO3_IO05 is configured as CH438_nRD */ - 0x10B0u); - IOMUXC_SetPinConfig( - IOMUXC_GPIO_SD_B1_02_GPIO3_IO02, /* GPIO3_IO02 is configured as CH438_ALE */ - 0x10B0u); - IOMUXC_SetPinConfig( - IOMUXC_GPIO_SD_B1_03_GPIO3_IO03, /* GPIO3_IO03 is configured as CH438_INT */ - 0x10B0u); - IOMUXC_SetPinConfig( - IOMUXC_GPIO_AD_B0_02_GPIO1_IO02, /* GPIO3_IO02 is configured as CH438_485_A_DIR */ - 0x10B0u); - IOMUXC_SetPinConfig( - IOMUXC_GPIO_AD_B0_00_GPIO1_IO00, /* GPIO1_IO00 is configured as CH438_485_B_DIR */ - 0x10B0u); -} /*********************************************************************************************************************** * EOF **********************************************************************************************************************/ diff --git a/Ubiquitous/XiZi/board/xidatong/third_party_driver/gpio/connect_gpio.c b/Ubiquitous/XiZi/board/xidatong/third_party_driver/gpio/connect_gpio.c index a61d7d035..4e74fb5cb 100755 --- a/Ubiquitous/XiZi/board/xidatong/third_party_driver/gpio/connect_gpio.c +++ b/Ubiquitous/XiZi/board/xidatong/third_party_driver/gpio/connect_gpio.c @@ -215,6 +215,20 @@ struct PinIrqHdr pin_irq_hdr_tab[] = {-1, 0, NONE, NONE}, }; +#define MUX_BASE 0x401f8014 +#define CONFIG_BASE 0x401f8204 + +#define GPIO5_MUX_BASE 0x400A8000 +#define GPIO5_CONFIG_BASE 0x400A8018 + +const uint8_t reg_offset[] = +{ + 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99,100,101,102,103,104,105, + 112,113,114,115,116,117,118,119,120,121,122,123,106,107,108,109,110,111, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, -1, -1, -1, -1, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, +}; + static int GetPin(struct PinIndex *pin_index, uint8_t pin) { pin_index->index = pin >> 5;//0:GPIO1 1:GPIO2 2:GPIO3 3:GPIO4 4:GPIO5 @@ -229,39 +243,52 @@ static int GetPin(struct PinIndex *pin_index, uint8_t pin) return 0; } -static int32 GpioConfigMode(int mode, struct PinIndex *index) +static int32 GpioConfigMode(int mode, struct PinIndex *pin_index, int32 pin) { gpio_pin_config_t gpio_config; - NULL_PARAM_CHECK(index); + uint32_t config_value = 0; + NULL_PARAM_CHECK(pin_index); gpio_config.outputLogic = 0; + gpio_config.interruptMode = kGPIO_NoIntmode; switch (mode) { case GPIO_CFG_OUTPUT: gpio_config.direction = kGPIO_DigitalOutput; - gpio_config.interruptMode = kGPIO_NoIntmode; + config_value = 0x0030U; /* Drive Strength R0/6 */ break; case GPIO_CFG_INPUT: gpio_config.direction = kGPIO_DigitalInput; - gpio_config.interruptMode = kGPIO_NoIntmode; + config_value = 0x0830U; /* Open Drain Enable */ break; case GPIO_CFG_INPUT_PULLUP: gpio_config.direction = kGPIO_DigitalInput; - gpio_config.interruptMode = kGPIO_NoIntmode; + config_value = 0xB030U; /* 100K Ohm Pull Up */ break; case GPIO_CFG_INPUT_PULLDOWN: gpio_config.direction = kGPIO_DigitalInput; - gpio_config.interruptMode = kGPIO_NoIntmode; + config_value = 0x3030U; /* 100K Ohm Pull Down */ break; case GPIO_CFG_OUTPUT_OD: gpio_config.direction = kGPIO_DigitalOutput; - gpio_config.interruptMode = kGPIO_NoIntmode; + config_value = 0x0830U; /* Open Drain Enable */ break; default: break; } - GPIO_PinInit(index->gpio, index->pin, &gpio_config); + + if (pin_mask[pin_index->index].gpio != GPIO5) { + CLOCK_EnableClock(kCLOCK_Iomuxc); + IOMUXC_SetPinMux(MUX_BASE + reg_offset[pin] * 4, 0x5U, 0, 0, CONFIG_BASE + reg_offset[pin] * 4, 1); + IOMUXC_SetPinConfig(MUX_BASE + reg_offset[pin] * 4, 0x5U, 0, 0, CONFIG_BASE + reg_offset[pin] * 4, config_value); + } else { + CLOCK_EnableClock(kCLOCK_IomuxcSnvs); + IOMUXC_SetPinMux(GPIO5_MUX_BASE + pin_index->pin * 4, 0x5U, 0, 0, GPIO5_CONFIG_BASE + pin_index->pin * 4, 1); + IOMUXC_SetPinConfig(GPIO5_MUX_BASE + pin_index->pin * 4, 0x5U, 0, 0, GPIO5_CONFIG_BASE + pin_index->pin * 4, config_value); + } + + GPIO_PinInit(pin_index->gpio, pin_index->pin, &gpio_config); return EOK; } @@ -385,6 +412,7 @@ static uint32 Imxrt1052PinConfigure(struct PinParam *param) struct PinIndex pin_index; + KPrintf("Imxrt1052PinConfigure\n"); if (GetPin(&pin_index, param->pin) < 0) { return ERROR; } @@ -392,7 +420,8 @@ static uint32 Imxrt1052PinConfigure(struct PinParam *param) switch(param->cmd) { case GPIO_CONFIG_MODE: - GpioConfigMode(param->mode, &pin_index); + KPrintf("GpioConfigMode %u\n", param->pin); + GpioConfigMode(param->mode, &pin_index, param->pin); break; case GPIO_IRQ_REGISTER: ret = GpioIrqRegister(param->pin, param->irq_set.irq_mode, param->irq_set.hdr, param->irq_set.args); @@ -705,21 +734,6 @@ void GpioLedTest(void) KPrintf("initialize %s failed!\n", PIN_BUS_NAME); return; } - - IOMUXC_SetPinMux( - IOMUXC_GPIO_AD_B0_09_GPIO1_IO09, /* GPIO_AD_B0_09 is configured as GPIO1_IO09 */ - 0U); /* Software Input On Field: Input Path is determined by functionality */ - /* Software Input On Field: Input Path is determined by functionality */ - IOMUXC_SetPinConfig( - IOMUXC_GPIO_AD_B0_09_GPIO1_IO09, /* GPIO_AD_B0_09 PAD functional properties : */ - 0x10B0u); /* Slew Rate Field: Slow Slew Rate - Drive Strength Field: R0/6 - Speed Field: medium(100MHz) - Open Drain Enable Field: Open Drain Disabled - Pull / Keep Enable Field: Pull/Keeper Enabled - Pull / Keep Select Field: Keeper - Pull Up / Down Config. Field: 100K Ohm Pull Down - Hyst. Enable Field: Hysteresis Disabled */ struct PinParam led_gpio_param; struct PinStat led_gpio_stat; @@ -762,4 +776,3 @@ void GpioLedTest(void) SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN), GpioLedTest, GpioLedTest, GpioLedTest GPIO1 IO09 LED); #endif - diff --git a/Ubiquitous/XiZi/board/xidatong/third_party_driver/include/connect_ch438.h b/Ubiquitous/XiZi/board/xidatong/third_party_driver/include/connect_ch438.h new file mode 100755 index 000000000..2a4f2da60 --- /dev/null +++ b/Ubiquitous/XiZi/board/xidatong/third_party_driver/include/connect_ch438.h @@ -0,0 +1,277 @@ +/* +* 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_ch438.h +* @brief define imxrt1052-board ch438 function and struct +* @version 2.0 +* @author AIIT XUOS Lab +* @date 2022-03-15 +*/ + +#ifndef CONNECT_CH438_H +#define CONNECT_CH438_H + +#include +#include +#include + +#define CH438_BUFFSIZE 255 + +/******************************************************************************************/ + +/* chip definition */ +/* CH438serial port0 register address */ + +#define REG_RBR0_ADDR 0x00 /* serial port0receive buffer register address */ +#define REG_THR0_ADDR 0x00 /* serial port0send hold register address */ +#define REG_IER0_ADDR 0x01 /* serial port0interrupt enable register address */ +#define REG_IIR0_ADDR 0x02 /* serial port0interrupt identifies register address */ +#define REG_FCR0_ADDR 0x02 /* serial port0FIFO controls register address */ +#define REG_LCR0_ADDR 0x03 /* serial port0circuit control register address */ +#define REG_MCR0_ADDR 0x04 /* serial port0MODEM controls register address */ +#define REG_LSR0_ADDR 0x05 /* serial port0line status register address */ +#define REG_MSR0_ADDR 0x06 /* serial port0address of MODEM status register */ +#define REG_SCR0_ADDR 0x07 /* serial port0the user can define the register address */ +#define REG_DLL0_ADDR 0x00 /* Baud rate divisor latch low 8-bit byte address */ +#define REG_DLM0_ADDR 0x01 /* Baud rate divisor latch high 8-bit byte address */ + +/* CH438serial port1 register address */ + +#define REG_RBR1_ADDR 0x10 /* serial port1receive buffer register address */ +#define REG_THR1_ADDR 0x10 /* serial port1send hold register address */ +#define REG_IER1_ADDR 0x11 /* serial port1interrupt enable register address */ +#define REG_IIR1_ADDR 0x12 /* serial port1interrupt identifies register address */ +#define REG_FCR1_ADDR 0x12 /* serial port1FIFO controls register address */ +#define REG_LCR1_ADDR 0x13 /* serial port1circuit control register address */ +#define REG_MCR1_ADDR 0x14 /* serial port1MODEM controls register address */ +#define REG_LSR1_ADDR 0x15 /* serial port1line status register address */ +#define REG_MSR1_ADDR 0x16 /* serial port1address of MODEM status register */ +#define REG_SCR1_ADDR 0x17 /* serial port1the user can define the register address */ +#define REG_DLL1_ADDR 0x10 /* Baud rate divisor latch low 8-bit byte address */ +#define REG_DLM1_ADDR 0x11 /* Baud rate divisor latch high 8-bit byte address */ + + +/* CH438serial port2 register address */ + +#define REG_RBR2_ADDR 0x20 /* serial port2receive buffer register address */ +#define REG_THR2_ADDR 0x20 /* serial port2send hold register address */ +#define REG_IER2_ADDR 0x21 /* serial port2interrupt enable register address */ +#define REG_IIR2_ADDR 0x22 /* serial port2interrupt identifies register address */ +#define REG_FCR2_ADDR 0x22 /* serial port2FIFO controls register address */ +#define REG_LCR2_ADDR 0x23 /* serial port2circuit control register address */ +#define REG_MCR2_ADDR 0x24 /* serial port2MODEM controls register address */ +#define REG_LSR2_ADDR 0x25 /* serial port2line status register address */ +#define REG_MSR2_ADDR 0x26 /* serial port2address of MODEM status register */ +#define REG_SCR2_ADDR 0x27 /* serial port2the user can define the register address */ +#define REG_DLL2_ADDR 0x20 /* Baud rate divisor latch low 8-bit byte address */ +#define REG_DLM2_ADDR 0x21 /* Baud rate divisor latch high 8-bit byte address */ + + + +/* CH438serial port3 register address */ + +#define REG_RBR3_ADDR 0x30 /* serial port3receive buffer register address */ +#define REG_THR3_ADDR 0x30 /* serial port3send hold register address */ +#define REG_IER3_ADDR 0x31 /* serial port3interrupt enable register address */ +#define REG_IIR3_ADDR 0x32 /* serial port3interrupt identifies register address */ +#define REG_FCR3_ADDR 0x32 /* serial port3FIFO controls register address */ +#define REG_LCR3_ADDR 0x33 /* serial port3circuit control register address */ +#define REG_MCR3_ADDR 0x34 /* serial port3MODEM controls register address */ +#define REG_LSR3_ADDR 0x35 /* serial port3line status register address */ +#define REG_MSR3_ADDR 0x36 /* serial port3address of MODEM status register */ +#define REG_SCR3_ADDR 0x37 /* serial port3the user can define the register address */ +#define REG_DLL3_ADDR 0x30 /* Baud rate divisor latch low 8-bit byte address */ +#define REG_DLM3_ADDR 0x31 /* Baud rate divisor latch high 8-bit byte address */ + + +/* CH438serial port4 register address */ + +#define REG_RBR4_ADDR 0x08 /* serial port4receive buffer register address */ +#define REG_THR4_ADDR 0x08 /* serial port4send hold register address */ +#define REG_IER4_ADDR 0x09 /* serial port4interrupt enable register address */ +#define REG_IIR4_ADDR 0x0A /* serial port4interrupt identifies register address */ +#define REG_FCR4_ADDR 0x0A /* serial port4FIFO controls register address */ +#define REG_LCR4_ADDR 0x0B /* serial port4circuit control register address */ +#define REG_MCR4_ADDR 0x0C /* serial port4MODEM controls register address */ +#define REG_LSR4_ADDR 0x0D /* serial port4line status register address */ +#define REG_MSR4_ADDR 0x0E /* serial port4address of MODEM status register */ +#define REG_SCR4_ADDR 0x0F /* serial port4the user can define the register address */ +#define REG_DLL4_ADDR 0x08 /* Baud rate divisor latch low 8-bit byte address */ +#define REG_DLM4_ADDR 0x09 /* Baud rate divisor latch high 8-bit byte address */ + + + +/* CH438serial port5 register address */ + +#define REG_RBR5_ADDR 0x18 /* serial port5receive buffer register address */ +#define REG_THR5_ADDR 0x18 /* serial port5send hold register address */ +#define REG_IER5_ADDR 0x19 /* serial port5interrupt enable register address */ +#define REG_IIR5_ADDR 0x1A /* serial port5interrupt identifies register address */ +#define REG_FCR5_ADDR 0x1A /* serial port5FIFO controls register address */ +#define REG_LCR5_ADDR 0x1B /* serial port5circuit control register address */ +#define REG_MCR5_ADDR 0x1C /* serial port5MODEM controls register address */ +#define REG_LSR5_ADDR 0x1D /* serial port5line status register address */ +#define REG_MSR5_ADDR 0x1E /* serial port5address of MODEM status register */ +#define REG_SCR5_ADDR 0x1F /* serial port5the user can define the register address */ +#define REG_DLL5_ADDR 0x18 /* Baud rate divisor latch low 8-bit byte address */ +#define REG_DLM5_ADDR 0x19 /* Baud rate divisor latch high 8-bit byte address */ + + +/* CH438serial port6 register address */ + +#define REG_RBR6_ADDR 0x28 /* serial port6receive buffer register address */ +#define REG_THR6_ADDR 0x28 /* serial port6send hold register address */ +#define REG_IER6_ADDR 0x29 /* serial port6interrupt enable register address */ +#define REG_IIR6_ADDR 0x2A /* serial port6interrupt identifies register address */ +#define REG_FCR6_ADDR 0x2A /* serial port6FIFO controls register address */ +#define REG_LCR6_ADDR 0x2B /* serial port6circuit control register address */ +#define REG_MCR6_ADDR 0x2C /* serial port6MODEM controls register address */ +#define REG_LSR6_ADDR 0x2D /* serial port6line status register address */ +#define REG_MSR6_ADDR 0x2E /* serial port6address of MODEM status register */ +#define REG_SCR6_ADDR 0x2F /* serial port6the user can define the register address */ +#define REG_DLL6_ADDR 0x28 /* Baud rate divisor latch low 8-bit byte address */ +#define REG_DLM6_ADDR 0x29 /* Baud rate divisor latch high 8-bit byte address */ + + +/* CH438serial port7 register address */ + +#define REG_RBR7_ADDR 0x38 /* serial port7receive buffer register address */ +#define REG_THR7_ADDR 0x38 /* serial port7send hold register address */ +#define REG_IER7_ADDR 0x39 /* serial port7interrupt enable register address */ +#define REG_IIR7_ADDR 0x3A /* serial port7interrupt identifies register address */ +#define REG_FCR7_ADDR 0x3A /* serial port7FIFO controls register address */ +#define REG_LCR7_ADDR 0x3B /* serial port7circuit control register address */ +#define REG_MCR7_ADDR 0x3C /* serial port7MODEM controls register address */ +#define REG_LSR7_ADDR 0x3D /* serial port7line status register address */ +#define REG_MSR7_ADDR 0x3E /* serial port7address of MODEM status register */ +#define REG_SCR7_ADDR 0x3F /* serial port7the user can define the register address */ +#define REG_DLL7_ADDR 0x38 /* Baud rate divisor latch low 8-bit byte address */ +#define REG_DLM7_ADDR 0x39 /* Baud rate divisor latch high 8-bit byte address */ + + +#define REG_SSR_ADDR 0x4F /* pecial status register address */ + + +/* IER register bit */ + +#define BIT_IER_RESET 0x80 /* The bit is 1 soft reset serial port */ +#define BIT_IER_LOWPOWER 0x40 /* The bit is 1 close serial port internal reference clock */ +#define BIT_IER_SLP 0x20 /* serial port0 is SLP, 1 close clock vibrator */ +#define BIT_IER1_CK2X 0x20 /* serial port1 is CK2X, 1 force the external clock signal after 2 times as internal reference clock */ +#define BIT_IER_IEMODEM 0x08 /* The bit is 1 allows MODEM input status to interrupt */ +#define BIT_IER_IELINES 0x04 /* The bit is 1 allow receiving line status to be interrupted */ +#define BIT_IER_IETHRE 0x02 /* The bit is 1 allows the send hold register to break in mid-air */ +#define BIT_IER_IERECV 0x01 /* The bit is 1 allows receiving data interrupts */ + +/* IIR register bit */ + +#define BIT_IIR_FIFOENS1 0x80 +#define BIT_IIR_FIFOENS0 0x40 /* The two is 1 said use FIFO */ + +/* Interrupt type: 0001 has no interrupt, 0110 receiving line status is interrupted, 0100 receiving data can be interrupted, +1100 received data timeout interrupt, 0010THR register air interrupt, 0000MODEM input change interrupt */ +#define BIT_IIR_IID3 0x08 +#define BIT_IIR_IID2 0x04 +#define BIT_IIR_IID1 0x02 +#define BIT_IIR_NOINT 0x01 + +/* FCR register bit */ + +/* Trigger point: 00 corresponds to 1 byte, 01 corresponds to 16 bytes, 10 corresponds to 64 bytes, 11 corresponds to 112 bytes */ +#define BIT_FCR_RECVTG1 0x80 /* Set the trigger point for FIFO interruption and automatic hardware flow control */ +#define BIT_FCR_RECVTG0 0x40 /* Set the trigger point for FIFO interruption and automatic hardware flow control */ + +#define BIT_FCR_TFIFORST 0x04 /* The bit is 1 empty the data sent in FIFO */ +#define BIT_FCR_RFIFORST 0x02 /* The bit is 1 empty the data sent in FIFO */ +#define BIT_FCR_FIFOEN 0x01 /* The bit is 1 use FIFO, 0 disable FIFO */ + +/* LCR register bit */ + +#define BIT_LCR_DLAB 0x80 /* To access DLL, DLM, 0 to access RBR/THR/IER */ +#define BIT_LCR_BREAKEN 0x40 /* 1 forces a BREAK line interval*/ + +/* Set the check format: when PAREN is 1, 00 odd check, 01 even check, 10 MARK (set 1), 11 blank (SPACE, clear 0) */ +#define BIT_LCR_PARMODE1 0x20 /* Sets the parity bit format */ +#define BIT_LCR_PARMODE0 0x10 /* Sets the parity bit format */ + +#define BIT_LCR_PAREN 0x08 /* A value of 1 allows you to generate and receive parity bits when sending */ +#define BIT_LCR_STOPBIT 0x04 /* If is 1, then two stop bits, is 0, a stop bit */ + +/* Set word length: 00 for 5 data bits, 01 for 6 data bits, 10 for 7 data bits and 11 for 8 data bits */ +#define BIT_LCR_WORDSZ1 0x02 /* Set the word length length */ +#define BIT_LCR_WORDSZ0 0x01 + +/* MCR register bit */ + +#define BIT_MCR_AFE 0x20 /* For 1 allows automatic flow control of CTS and RTS hardware */ +#define BIT_MCR_LOOP 0x10 /* Is the test mode of 1 enabling internal loop */ +#define BIT_MCR_OUT2 0x08 /* 1 Allows an interrupt request for the serial port output */ +#define BIT_MCR_OUT1 0x04 /* The MODEM control bit defined for the user */ +#define BIT_MCR_RTS 0x02 /* The bit is 1 RTS pin output effective */ +#define BIT_MCR_DTR 0x01 /* The bit is 1 DTR pin output effective */ + +/* LSR register bit */ + +#define BIT_LSR_RFIFOERR 0x80 /* 1 said There is at least one error in receiving FIFO */ +#define BIT_LSR_TEMT 0x40 /* 1 said THR and TSR are empty */ +#define BIT_LSR_THRE 0x20 /* 1 said THR is empty*/ +#define BIT_LSR_BREAKINT 0x10 /* The bit is 1 said the BREAK line interval was detected*/ +#define BIT_LSR_FRAMEERR 0x08 /* The bit is 1 said error reading data frame */ +#define BIT_LSR_PARERR 0x04 /* The bit is 1 said parity error */ +#define BIT_LSR_OVERR 0x02 /* 1 said receive FIFO buffer overflow */ +#define BIT_LSR_DATARDY 0x01 /* The bit is 1 said receive data received in FIFO */ + +/* MSR register bit */ + +#define BIT_MSR_DCD 0x80 /* The bit is 1 said DCD pin effective */ +#define BIT_MSR_RI 0x40 /* The bit is 1 said RI pin effective */ +#define BIT_MSR_DSR 0x20 /* The bit is 1 said DSR pin effective */ +#define BIT_MSR_CTS 0x10 /* The bit is 1 said CTS pin effective */ +#define BIT_MSR_DDCD 0x08 /* The bit is 1 said DCD pin The input state has changed */ +#define BIT_MSR_TERI 0x04 /* The bit is 1 said RI pin The input state has changed */ +#define BIT_MSR_DDSR 0x02 /* The bit is 1 said DSR pin The input state has changed */ +#define BIT_MSR_DCTS 0x01 /* The bit is 1 said CTS pin The input state has changed */ + +/* Interrupt status code */ + +#define INT_NOINT 0x01 /* There is no interruption */ +#define INT_THR_EMPTY 0x02 /* THR empty interruption */ +#define INT_RCV_OVERTIME 0x0C /* Receive timeout interrupt */ +#define INT_RCV_SUCCESS 0x04 /* Interrupts are available to receive data */ +#define INT_RCV_LINES 0x06 /* Receiving line status interrupted */ +#define INT_MODEM_CHANGE 0x00 /* MODEM input changes interrupt */ + +#define CH438_IIR_FIFOS_ENABLED 0xC0 /* use FIFO */ + +#define Fpclk 1843200 /* Define the internal clock frequency */ + +#define CH438_D0_PIN IMXRT_GET_PIN(1, 25) +#define CH438_D1_PIN IMXRT_GET_PIN(1, 24) +#define CH438_D2_PIN IMXRT_GET_PIN(1, 20) +#define CH438_D3_PIN IMXRT_GET_PIN(1, 21) +#define CH438_D4_PIN IMXRT_GET_PIN(1, 31) +#define CH438_D5_PIN IMXRT_GET_PIN(1, 28) +#define CH438_D6_PIN IMXRT_GET_PIN(1, 30) +#define CH438_D7_PIN IMXRT_GET_PIN(1, 29) +#define CH438_NWR_PIN IMXRT_GET_PIN(3, 4) +#define CH438_NRD_PIN IMXRT_GET_PIN(3, 5) +#define CH438_ALE_PIN IMXRT_GET_PIN(3, 2) +#define CH438_INT_PIN IMXRT_GET_PIN(3, 3) +// #define DIR_485CH1_PIN +// #define DIR_485CH2_PIN + +void CH438RegTest(unsigned char num); + +int Imxrt1052HwCh438Init(void); + +#endif diff --git a/Ubiquitous/XiZi/board/xidatong/xip/Makefile b/Ubiquitous/XiZi/board/xidatong/xip/Makefile new file mode 100755 index 000000000..e27f9550a --- /dev/null +++ b/Ubiquitous/XiZi/board/xidatong/xip/Makefile @@ -0,0 +1,3 @@ +SRC_FILES := fsl_flexspi_nor_boot.c fsl_flexspi_nor_flash.c + +include $(KERNEL_ROOT)/compiler.mk diff --git a/Ubiquitous/XiZi/board/xidatong/xip/fsl_flexspi_nor_boot.c b/Ubiquitous/XiZi/board/xidatong/xip/fsl_flexspi_nor_boot.c new file mode 100644 index 000000000..052813ed6 --- /dev/null +++ b/Ubiquitous/XiZi/board/xidatong/xip/fsl_flexspi_nor_boot.c @@ -0,0 +1,1219 @@ +/* + * Copyright 2017 NXP + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * o Redistributions of source code must retain the above copyright notice, this list + * of conditions and the following disclaimer. + * + * o Redistributions in binary form must reproduce the above copyright notice, this + * list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * o Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** +* @file fsl_flexspi_nor_boot.c +* @brief support to register flexspi image vector table +* @version 2.0 +* @author AIIT XUOS Lab +* @date 2022-03-22 +*/ + +#include "fsl_flexspi_nor_boot.h" + +#if defined(__CC_ARM) || defined(__GNUC__) + __attribute__((section(".boot_hdr.ivt"))) +#elif defined(__ICCARM__) +#pragma location=".boot_hdr.ivt" +#endif + +const ivt image_vector_table = { + IVT_HEADER, /* IVT Header */ + 0x60002000, /* Image Entry Function */ + IVT_RSVD, /* Reserved = 0 */ + (uint32_t)DCD_ADDRESS, /* Address where DCD information is stored */ + (uint32_t)BOOT_DATA_ADDRESS, /* Address where BOOT Data Structure is stored */ + (uint32_t)&image_vector_table, /* Pointer to IVT Self (absolute address */ + (uint32_t)CSF_ADDRESS, /* Address where CSF file is stored */ + IVT_RSVD /* Reserved = 0 */ +}; + +#if defined(__CC_ARM) || defined(__GNUC__) + __attribute__((section(".boot_hdr.boot_data"))) +#elif defined(__ICCARM__) +#pragma location=".boot_hdr.boot_data" +#endif + +const BOOT_DATA_T boot_data = { + FLASH_BASE, /* boot start location */ + (FLASH_END-FLASH_BASE), /* size */ + PLUGIN_FLAG, /* Plugin flag*/ + 0xFFFFFFFF /* empty - extra data word */ +}; + +#if defined(__CC_ARM) || defined(__GNUC__) + __attribute__((section(".boot_hdr.dcd_data"))) +#elif defined(__ICCARM__) +#pragma location=".boot_hdr.dcd_data" +#endif +//const uint8_t dcd_sdram[1044] = { +///*0000*/ 0xD2, 0x04, 0x14, 0x41, 0xCC, 0x02, 0xF4, 0x04, 0x40, 0x0F, 0xC0, 0x68, 0xFF, 0xFF, 0xFF, 0xFF, +///*0010*/ 0x40, 0x0F, 0xC0, 0x6C, 0xFF, 0xFF, 0xFF, 0xFF, 0x40, 0x0F, 0xC0, 0x70, 0xFF, 0xFF, 0xFF, 0xFF, +///*0020*/ 0x40, 0x0F, 0xC0, 0x74, 0xFF, 0xFF, 0xFF, 0xFF, 0x40, 0x0F, 0xC0, 0x78, 0xFF, 0xFF, 0xFF, 0xFF, +///*0030*/ 0x40, 0x0F, 0xC0, 0x7C, 0xFF, 0xFF, 0xFF, 0xFF, 0x40, 0x0F, 0xC0, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, +///*0040*/ 0x40, 0x0D, 0x80, 0x30, 0x00, 0x00, 0x20, 0x01, 0x40, 0x0D, 0x81, 0x00, 0x00, 0x1D, 0x00, 0x00, +///*0050*/ 0x40, 0x0F, 0xC0, 0x14, 0x00, 0x09, 0x83, 0x40, 0x40, 0x1F, 0x80, 0x14, 0x00, 0x00, 0x00, 0x00, +///*0060*/ 0x40, 0x1F, 0x80, 0x18, 0x00, 0x00, 0x00, 0x00, 0x40, 0x1F, 0x80, 0x1C, 0x00, 0x00, 0x00, 0x00, +///*0070*/ 0x40, 0x1F, 0x80, 0x20, 0x00, 0x00, 0x00, 0x00, 0x40, 0x1F, 0x80, 0x24, 0x00, 0x00, 0x00, 0x00, +///*0080*/ 0x40, 0x1F, 0x80, 0x28, 0x00, 0x00, 0x00, 0x00, 0x40, 0x1F, 0x80, 0x2C, 0x00, 0x00, 0x00, 0x00, +///*0090*/ 0x40, 0x1F, 0x80, 0x30, 0x00, 0x00, 0x00, 0x00, 0x40, 0x1F, 0x80, 0x34, 0x00, 0x00, 0x00, 0x00, +///*00a0*/ 0x40, 0x1F, 0x80, 0x38, 0x00, 0x00, 0x00, 0x00, 0x40, 0x1F, 0x80, 0x3C, 0x00, 0x00, 0x00, 0x00, +///*00b0*/ 0x40, 0x1F, 0x80, 0x40, 0x00, 0x00, 0x00, 0x00, 0x40, 0x1F, 0x80, 0x44, 0x00, 0x00, 0x00, 0x00, +///*00c0*/ 0x40, 0x1F, 0x80, 0x48, 0x00, 0x00, 0x00, 0x00, 0x40, 0x1F, 0x80, 0x4C, 0x00, 0x00, 0x00, 0x00, +///*00d0*/ 0x40, 0x1F, 0x80, 0x50, 0x00, 0x00, 0x00, 0x00, 0x40, 0x1F, 0x80, 0x54, 0x00, 0x00, 0x00, 0x00, +///*00e0*/ 0x40, 0x1F, 0x80, 0x58, 0x00, 0x00, 0x00, 0x00, 0x40, 0x1F, 0x80, 0x5C, 0x00, 0x00, 0x00, 0x00, +///*00f0*/ 0x40, 0x1F, 0x80, 0x60, 0x00, 0x00, 0x00, 0x00, 0x40, 0x1F, 0x80, 0x64, 0x00, 0x00, 0x00, 0x00, +///*0100*/ 0x40, 0x1F, 0x80, 0x68, 0x00, 0x00, 0x00, 0x00, 0x40, 0x1F, 0x80, 0x6C, 0x00, 0x00, 0x00, 0x00, +///*0110*/ 0x40, 0x1F, 0x80, 0x70, 0x00, 0x00, 0x00, 0x00, 0x40, 0x1F, 0x80, 0x74, 0x00, 0x00, 0x00, 0x00, +///*0120*/ 0x40, 0x1F, 0x80, 0x78, 0x00, 0x00, 0x00, 0x00, 0x40, 0x1F, 0x80, 0x7C, 0x00, 0x00, 0x00, 0x00, +///*0130*/ 0x40, 0x1F, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x40, 0x1F, 0x80, 0x84, 0x00, 0x00, 0x00, 0x00, +///*0140*/ 0x40, 0x1F, 0x80, 0x88, 0x00, 0x00, 0x00, 0x00, 0x40, 0x1F, 0x80, 0x8C, 0x00, 0x00, 0x00, 0x00, +///*0150*/ 0x40, 0x1F, 0x80, 0x90, 0x00, 0x00, 0x00, 0x00, 0x40, 0x1F, 0x80, 0x94, 0x00, 0x00, 0x00, 0x00, +///*0160*/ 0x40, 0x1F, 0x80, 0x98, 0x00, 0x00, 0x00, 0x00, 0x40, 0x1F, 0x80, 0x9C, 0x00, 0x00, 0x00, 0x00, +///*0170*/ 0x40, 0x1F, 0x80, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x40, 0x1F, 0x80, 0xA4, 0x00, 0x00, 0x00, 0x00, +///*0180*/ 0x40, 0x1F, 0x80, 0xA8, 0x00, 0x00, 0x00, 0x00, 0x40, 0x1F, 0x80, 0xAC, 0x00, 0x00, 0x00, 0x00, +///*0190*/ 0x40, 0x1F, 0x80, 0xB0, 0x00, 0x00, 0x00, 0x10, 0x40, 0x1F, 0x80, 0xB4, 0x00, 0x00, 0x00, 0x00, +///*01a0*/ 0x40, 0x1F, 0x80, 0xB8, 0x00, 0x00, 0x00, 0x00, 0x40, 0x1F, 0x82, 0x04, 0x00, 0x00, 0x00, 0xF1, +///*01b0*/ 0x40, 0x1F, 0x82, 0x08, 0x00, 0x00, 0x00, 0xF1, 0x40, 0x1F, 0x82, 0x0C, 0x00, 0x00, 0x00, 0xF1, +///*01c0*/ 0x40, 0x1F, 0x82, 0x10, 0x00, 0x00, 0x00, 0xF1, 0x40, 0x1F, 0x82, 0x14, 0x00, 0x00, 0x00, 0xF1, +///*01d0*/ 0x40, 0x1F, 0x82, 0x18, 0x00, 0x00, 0x00, 0xF1, 0x40, 0x1F, 0x82, 0x1C, 0x00, 0x00, 0x00, 0xF1, +///*01e0*/ 0x40, 0x1F, 0x82, 0x20, 0x00, 0x00, 0x00, 0xF1, 0x40, 0x1F, 0x82, 0x24, 0x00, 0x00, 0x00, 0xF1, +///*01f0*/ 0x40, 0x1F, 0x82, 0x28, 0x00, 0x00, 0x00, 0xF1, 0x40, 0x1F, 0x82, 0x2C, 0x00, 0x00, 0x00, 0xF1, +///*0200*/ 0x40, 0x1F, 0x82, 0x30, 0x00, 0x00, 0x00, 0xF1, 0x40, 0x1F, 0x82, 0x34, 0x00, 0x00, 0x00, 0xF1, +///*0210*/ 0x40, 0x1F, 0x82, 0x38, 0x00, 0x00, 0x00, 0xF1, 0x40, 0x1F, 0x82, 0x3C, 0x00, 0x00, 0x00, 0xF1, +///*0220*/ 0x40, 0x1F, 0x82, 0x40, 0x00, 0x00, 0x00, 0xF1, 0x40, 0x1F, 0x82, 0x44, 0x00, 0x00, 0x00, 0xF1, +///*0230*/ 0x40, 0x1F, 0x82, 0x48, 0x00, 0x00, 0x00, 0xF1, 0x40, 0x1F, 0x82, 0x4C, 0x00, 0x00, 0x00, 0xF1, +///*0240*/ 0x40, 0x1F, 0x82, 0x50, 0x00, 0x00, 0x00, 0xF1, 0x40, 0x1F, 0x82, 0x54, 0x00, 0x00, 0x00, 0xF1, +///*0250*/ 0x40, 0x1F, 0x82, 0x58, 0x00, 0x00, 0x00, 0xF1, 0x40, 0x1F, 0x82, 0x5C, 0x00, 0x00, 0x00, 0xF1, +///*0260*/ 0x40, 0x1F, 0x82, 0x60, 0x00, 0x00, 0x00, 0xF1, 0x40, 0x1F, 0x82, 0x64, 0x00, 0x00, 0x00, 0xF1, +///*0270*/ 0x40, 0x1F, 0x82, 0x68, 0x00, 0x00, 0x00, 0xF1, 0x40, 0x1F, 0x82, 0x6C, 0x00, 0x00, 0x00, 0xF1, +///*0280*/ 0x40, 0x1F, 0x82, 0x70, 0x00, 0x00, 0x00, 0xF1, 0x40, 0x1F, 0x82, 0x74, 0x00, 0x00, 0x00, 0xF1, +///*0290*/ 0x40, 0x1F, 0x82, 0x78, 0x00, 0x00, 0x00, 0xF1, 0x40, 0x1F, 0x82, 0x7C, 0x00, 0x00, 0x00, 0xF1, +///*02a0*/ 0x40, 0x1F, 0x82, 0x80, 0x00, 0x00, 0x00, 0xF1, 0x40, 0x1F, 0x82, 0x84, 0x00, 0x00, 0x00, 0xF1, +///*02b0*/ 0x40, 0x1F, 0x82, 0x88, 0x00, 0x00, 0x00, 0xF1, 0x40, 0x1F, 0x82, 0x8C, 0x00, 0x00, 0x00, 0xF1, +///*02c0*/ 0x40, 0x1F, 0x82, 0x90, 0x00, 0x00, 0x00, 0xF1, 0x40, 0x1F, 0x82, 0x94, 0x00, 0x00, 0x00, 0xF1, +///*02d0*/ 0x40, 0x1F, 0x82, 0x98, 0x00, 0x00, 0x00, 0xF1, 0x40, 0x1F, 0x82, 0x9C, 0x00, 0x00, 0x00, 0xF1, +///*02e0*/ 0x40, 0x1F, 0x82, 0xA0, 0x00, 0x00, 0x00, 0xF1, 0x40, 0x1F, 0x82, 0xA4, 0x00, 0x00, 0x00, 0xF1, +///*02f0*/ 0x40, 0x1F, 0x82, 0xA8, 0x00, 0x00, 0x00, 0xF1, 0xCC, 0x00, 0x0C, 0x14, 0x40, 0x2F, 0x00, 0x00, +///*0300*/ 0x00, 0x00, 0x00, 0x02, 0xCC, 0x00, 0x9C, 0x04, 0x40, 0x2F, 0x00, 0x00, 0x10, 0x00, 0x00, 0x04, +///*0310*/ 0x40, 0x2F, 0x00, 0x08, 0x00, 0x03, 0x05, 0x24, 0x40, 0x2F, 0x00, 0x0C, 0x06, 0x03, 0x05, 0x24, +///*0320*/ 0x40, 0x2F, 0x00, 0x10, 0x80, 0x00, 0x00, 0x21, 0x40, 0x2F, 0x00, 0x14, 0x90, 0x00, 0x00, 0x21, +///*0330*/ 0x40, 0x2F, 0x00, 0x04, 0x00, 0x00, 0x00, 0x08, 0x40, 0x2F, 0x00, 0x40, 0x00, 0x00, 0x0B, 0x27, +///*0340*/ 0x40, 0x2F, 0x00, 0x44, 0x00, 0x10, 0x01, 0x00, 0x40, 0x2F, 0x00, 0x48, 0x00, 0x02, 0x02, 0x01, +///*0350*/ 0x40, 0x2F, 0x00, 0x4C, 0x08, 0x19, 0x3D, 0x0E, 0x40, 0x2F, 0x00, 0x74, 0x00, 0x65, 0x29, 0x22, +///*0360*/ 0x40, 0x2F, 0x00, 0x78, 0x00, 0x01, 0x09, 0x20, 0x40, 0x2F, 0x00, 0x7C, 0x50, 0x21, 0x0A, 0x08, +///*0370*/ 0x40, 0x2F, 0x00, 0x80, 0x00, 0x00, 0x00, 0x21, 0x40, 0x2F, 0x00, 0x84, 0x00, 0x88, 0x88, 0x88, +///*0380*/ 0x40, 0x2F, 0x00, 0x94, 0x00, 0x00, 0x00, 0x02, 0x40, 0x2F, 0x00, 0x98, 0x00, 0x00, 0x00, 0x00, +///*0390*/ 0x40, 0x2F, 0x00, 0x90, 0x80, 0x00, 0x00, 0x00, 0x40, 0x2F, 0x00, 0x9C, 0xA5, 0x5A, 0x00, 0x0F, +///*03a0*/ 0xCF, 0x00, 0x0C, 0x1C, 0x40, 0x2F, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x01, 0xCC, 0x00, 0x14, 0x04, +///*03b0*/ 0x40, 0x2F, 0x00, 0x90, 0x80, 0x00, 0x00, 0x00, 0x40, 0x2F, 0x00, 0x9C, 0xA5, 0x5A, 0x00, 0x0C, +///*03c0*/ 0xCF, 0x00, 0x0C, 0x1C, 0x40, 0x2F, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x01, 0xCC, 0x00, 0x14, 0x04, +///*03d0*/ 0x40, 0x2F, 0x00, 0x90, 0x80, 0x00, 0x00, 0x00, 0x40, 0x2F, 0x00, 0x9C, 0xA5, 0x5A, 0x00, 0x0C, +///*03e0*/ 0xCF, 0x00, 0x0C, 0x1C, 0x40, 0x2F, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x01, 0xCC, 0x00, 0x1C, 0x04, +///*03f0*/ 0x40, 0x2F, 0x00, 0xA0, 0x00, 0x00, 0x00, 0x22, 0x40, 0x2F, 0x00, 0x90, 0x80, 0x00, 0x00, 0x00, +///*0400*/ 0x40, 0x2F, 0x00, 0x9C, 0xA5, 0x5A, 0x00, 0x0A, 0xCF, 0x00, 0x0C, 0x1C, 0x40, 0x2F, 0x00, 0x3C, +///*0410*/ 0x00, 0x00, 0x00, 0x01, +//}; + + + +const uint8_t dcd_sdram[1072] = { + /*0000*/ 0xD2, + 0x04, + 0x30, + 0x41, + 0xCC, + 0x03, + 0xAC, + 0x04, + 0x40, + 0x0F, + 0xC0, + 0x68, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + /*0010*/ 0x40, + 0x0F, + 0xC0, + 0x6C, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0x40, + 0x0F, + 0xC0, + 0x70, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + /*0020*/ 0x40, + 0x0F, + 0xC0, + 0x74, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0x40, + 0x0F, + 0xC0, + 0x78, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + /*0030*/ 0x40, + 0x0F, + 0xC0, + 0x7C, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0x40, + 0x0F, + 0xC0, + 0x80, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + /*0040*/ 0x40, + 0x0D, + 0x80, + 0x30, + 0x00, + 0x00, + 0x20, + 0x01, + 0x40, + 0x0D, + 0x81, + 0x00, + 0x00, + 0x1D, + 0x00, + 0x00, + /*0050*/ 0x40, + 0x0F, + 0xC0, + 0x14, + 0x00, + 0x01, + 0x0D, + 0x40, + 0x40, + 0x1F, + 0x80, + 0x14, + 0x00, + 0x00, + 0x00, + 0x00, + /*0060*/ 0x40, + 0x1F, + 0x80, + 0x18, + 0x00, + 0x00, + 0x00, + 0x00, + 0x40, + 0x1F, + 0x80, + 0x1C, + 0x00, + 0x00, + 0x00, + 0x00, + /*0070*/ 0x40, + 0x1F, + 0x80, + 0x20, + 0x00, + 0x00, + 0x00, + 0x00, + 0x40, + 0x1F, + 0x80, + 0x24, + 0x00, + 0x00, + 0x00, + 0x00, + /*0080*/ 0x40, + 0x1F, + 0x80, + 0x28, + 0x00, + 0x00, + 0x00, + 0x00, + 0x40, + 0x1F, + 0x80, + 0x2C, + 0x00, + 0x00, + 0x00, + 0x00, + /*0090*/ 0x40, + 0x1F, + 0x80, + 0x30, + 0x00, + 0x00, + 0x00, + 0x00, + 0x40, + 0x1F, + 0x80, + 0x34, + 0x00, + 0x00, + 0x00, + 0x00, + /*00a0*/ 0x40, + 0x1F, + 0x80, + 0x38, + 0x00, + 0x00, + 0x00, + 0x00, + 0x40, + 0x1F, + 0x80, + 0x3C, + 0x00, + 0x00, + 0x00, + 0x00, + /*00b0*/ 0x40, + 0x1F, + 0x80, + 0x40, + 0x00, + 0x00, + 0x00, + 0x00, + 0x40, + 0x1F, + 0x80, + 0x44, + 0x00, + 0x00, + 0x00, + 0x00, + /*00c0*/ 0x40, + 0x1F, + 0x80, + 0x48, + 0x00, + 0x00, + 0x00, + 0x00, + 0x40, + 0x1F, + 0x80, + 0x4C, + 0x00, + 0x00, + 0x00, + 0x00, + /*00d0*/ 0x40, + 0x1F, + 0x80, + 0x50, + 0x00, + 0x00, + 0x00, + 0x00, + 0x40, + 0x1F, + 0x80, + 0x54, + 0x00, + 0x00, + 0x00, + 0x00, + /*00e0*/ 0x40, + 0x1F, + 0x80, + 0x58, + 0x00, + 0x00, + 0x00, + 0x00, + 0x40, + 0x1F, + 0x80, + 0x5C, + 0x00, + 0x00, + 0x00, + 0x00, + /*00f0*/ 0x40, + 0x1F, + 0x80, + 0x60, + 0x00, + 0x00, + 0x00, + 0x00, + 0x40, + 0x1F, + 0x80, + 0x64, + 0x00, + 0x00, + 0x00, + 0x00, + /*0100*/ 0x40, + 0x1F, + 0x80, + 0x68, + 0x00, + 0x00, + 0x00, + 0x00, + 0x40, + 0x1F, + 0x80, + 0x6C, + 0x00, + 0x00, + 0x00, + 0x00, + /*0110*/ 0x40, + 0x1F, + 0x80, + 0x70, + 0x00, + 0x00, + 0x00, + 0x00, + 0x40, + 0x1F, + 0x80, + 0x74, + 0x00, + 0x00, + 0x00, + 0x00, + /*0120*/ 0x40, + 0x1F, + 0x80, + 0x78, + 0x00, + 0x00, + 0x00, + 0x00, + 0x40, + 0x1F, + 0x80, + 0x7C, + 0x00, + 0x00, + 0x00, + 0x00, + /*0130*/ 0x40, + 0x1F, + 0x80, + 0x80, + 0x00, + 0x00, + 0x00, + 0x00, + 0x40, + 0x1F, + 0x80, + 0x84, + 0x00, + 0x00, + 0x00, + 0x00, + /*0140*/ 0x40, + 0x1F, + 0x80, + 0x88, + 0x00, + 0x00, + 0x00, + 0x00, + 0x40, + 0x1F, + 0x80, + 0x8C, + 0x00, + 0x00, + 0x00, + 0x00, + /*0150*/ 0x40, + 0x1F, + 0x80, + 0x90, + 0x00, + 0x00, + 0x00, + 0x00, + 0x40, + 0x1F, + 0x80, + 0x94, + 0x00, + 0x00, + 0x00, + 0x00, + /*0160*/ 0x40, + 0x1F, + 0x80, + 0x98, + 0x00, + 0x00, + 0x00, + 0x00, + 0x40, + 0x1F, + 0x80, + 0x9C, + 0x00, + 0x00, + 0x00, + 0x00, + /*0170*/ 0x40, + 0x1F, + 0x80, + 0xA0, + 0x00, + 0x00, + 0x00, + 0x00, + 0x40, + 0x1F, + 0x80, + 0xA4, + 0x00, + 0x00, + 0x00, + 0x00, + /*0180*/ 0x40, + 0x1F, + 0x80, + 0xA8, + 0x00, + 0x00, + 0x00, + 0x00, + 0x40, + 0x1F, + 0x80, + 0xAC, + 0x00, + 0x00, + 0x00, + 0x00, + /*0190*/ 0x40, + 0x1F, + 0x80, + 0xB0, + 0x00, + 0x00, + 0x00, + 0x10, + 0x40, + 0x1F, + 0x80, + 0xB4, + 0x00, + 0x00, + 0x00, + 0x00, + /*01a0*/ 0x40, + 0x1F, + 0x80, + 0xB8, + 0x00, + 0x00, + 0x00, + 0x00, + 0x40, + 0x1F, + 0x82, + 0x04, + 0x00, + 0x01, + 0x10, + 0xF9, + /*01b0*/ 0x40, + 0x1F, + 0x82, + 0x08, + 0x00, + 0x01, + 0x10, + 0xF9, + 0x40, + 0x1F, + 0x82, + 0x0C, + 0x00, + 0x01, + 0x10, + 0xF9, + /*01c0*/ 0x40, + 0x1F, + 0x82, + 0x10, + 0x00, + 0x01, + 0x10, + 0xF9, + 0x40, + 0x1F, + 0x82, + 0x14, + 0x00, + 0x01, + 0x10, + 0xF9, + /*01d0*/ 0x40, + 0x1F, + 0x82, + 0x18, + 0x00, + 0x01, + 0x10, + 0xF9, + 0x40, + 0x1F, + 0x82, + 0x1C, + 0x00, + 0x01, + 0x10, + 0xF9, + /*01e0*/ 0x40, + 0x1F, + 0x82, + 0x20, + 0x00, + 0x01, + 0x10, + 0xF9, + 0x40, + 0x1F, + 0x82, + 0x24, + 0x00, + 0x01, + 0x10, + 0xF9, + /*01f0*/ 0x40, + 0x1F, + 0x82, + 0x28, + 0x00, + 0x01, + 0x10, + 0xF9, + 0x40, + 0x1F, + 0x82, + 0x2C, + 0x00, + 0x01, + 0x10, + 0xF9, + /*0200*/ 0x40, + 0x1F, + 0x82, + 0x30, + 0x00, + 0x01, + 0x10, + 0xF9, + 0x40, + 0x1F, + 0x82, + 0x34, + 0x00, + 0x01, + 0x10, + 0xF9, + /*0210*/ 0x40, + 0x1F, + 0x82, + 0x38, + 0x00, + 0x01, + 0x10, + 0xF9, + 0x40, + 0x1F, + 0x82, + 0x3C, + 0x00, + 0x01, + 0x10, + 0xF9, + /*0220*/ 0x40, + 0x1F, + 0x82, + 0x40, + 0x00, + 0x01, + 0x10, + 0xF9, + 0x40, + 0x1F, + 0x82, + 0x44, + 0x00, + 0x01, + 0x10, + 0xF9, + /*0230*/ 0x40, + 0x1F, + 0x82, + 0x48, + 0x00, + 0x01, + 0x10, + 0xF9, + 0x40, + 0x1F, + 0x82, + 0x4C, + 0x00, + 0x01, + 0x10, + 0xF9, + /*0240*/ 0x40, + 0x1F, + 0x82, + 0x50, + 0x00, + 0x01, + 0x10, + 0xF9, + 0x40, + 0x1F, + 0x82, + 0x54, + 0x00, + 0x01, + 0x10, + 0xF9, + /*0250*/ 0x40, + 0x1F, + 0x82, + 0x58, + 0x00, + 0x01, + 0x10, + 0xF9, + 0x40, + 0x1F, + 0x82, + 0x5C, + 0x00, + 0x01, + 0x10, + 0xF9, + /*0260*/ 0x40, + 0x1F, + 0x82, + 0x60, + 0x00, + 0x01, + 0x10, + 0xF9, + 0x40, + 0x1F, + 0x82, + 0x64, + 0x00, + 0x01, + 0x10, + 0xF9, + /*0270*/ 0x40, + 0x1F, + 0x82, + 0x68, + 0x00, + 0x01, + 0x10, + 0xF9, + 0x40, + 0x1F, + 0x82, + 0x6C, + 0x00, + 0x01, + 0x10, + 0xF9, + /*0280*/ 0x40, + 0x1F, + 0x82, + 0x70, + 0x00, + 0x01, + 0x10, + 0xF9, + 0x40, + 0x1F, + 0x82, + 0x74, + 0x00, + 0x01, + 0x10, + 0xF9, + /*0290*/ 0x40, + 0x1F, + 0x82, + 0x78, + 0x00, + 0x01, + 0x10, + 0xF9, + 0x40, + 0x1F, + 0x82, + 0x7C, + 0x00, + 0x01, + 0x10, + 0xF9, + /*02a0*/ 0x40, + 0x1F, + 0x82, + 0x80, + 0x00, + 0x01, + 0x10, + 0xF9, + 0x40, + 0x1F, + 0x82, + 0x84, + 0x00, + 0x01, + 0x10, + 0xF9, + /*02b0*/ 0x40, + 0x1F, + 0x82, + 0x88, + 0x00, + 0x01, + 0x10, + 0xF9, + 0x40, + 0x1F, + 0x82, + 0x8C, + 0x00, + 0x01, + 0x10, + 0xF9, + /*02c0*/ 0x40, + 0x1F, + 0x82, + 0x90, + 0x00, + 0x01, + 0x10, + 0xF9, + 0x40, + 0x1F, + 0x82, + 0x94, + 0x00, + 0x01, + 0x10, + 0xF9, + /*02d0*/ 0x40, + 0x1F, + 0x82, + 0x98, + 0x00, + 0x01, + 0x10, + 0xF9, + 0x40, + 0x1F, + 0x82, + 0x9C, + 0x00, + 0x01, + 0x10, + 0xF9, + /*02e0*/ 0x40, + 0x1F, + 0x82, + 0xA0, + 0x00, + 0x01, + 0x10, + 0xF9, + 0x40, + 0x1F, + 0x82, + 0xA4, + 0x00, + 0x01, + 0x10, + 0xF9, + /*02f0*/ 0x40, + 0x1F, + 0x82, + 0xA8, + 0x00, + 0x01, + 0x10, + 0xF9, + 0x40, + 0x2F, + 0x00, + 0x00, + 0x10, + 0x00, + 0x00, + 0x04, + /*0300*/ 0x40, + 0x2F, + 0x00, + 0x08, + 0x00, + 0x03, + 0x05, + 0x24, + 0x40, + 0x2F, + 0x00, + 0x0C, + 0x06, + 0x03, + 0x05, + 0x24, + /*0310*/ 0x40, + 0x2F, + 0x00, + 0x10, + 0x80, + 0x00, + 0x00, + 0x1B, + 0x40, + 0x2F, + 0x00, + 0x14, + 0x82, + 0x00, + 0x00, + 0x1B, + /*0320*/ 0x40, + 0x2F, + 0x00, + 0x18, + 0x84, + 0x00, + 0x00, + 0x1B, + 0x40, + 0x2F, + 0x00, + 0x1C, + 0x86, + 0x00, + 0x00, + 0x1B, + /*0330*/ 0x40, + 0x2F, + 0x00, + 0x20, + 0x90, + 0x00, + 0x00, + 0x21, + 0x40, + 0x2F, + 0x00, + 0x24, + 0xA0, + 0x00, + 0x00, + 0x19, + /*0340*/ 0x40, + 0x2F, + 0x00, + 0x28, + 0xA8, + 0x00, + 0x00, + 0x17, + 0x40, + 0x2F, + 0x00, + 0x2C, + 0xA9, + 0x00, + 0x00, + 0x1B, + /*0350*/ 0x40, + 0x2F, + 0x00, + 0x30, + 0x00, + 0x00, + 0x00, + 0x21, + 0x40, + 0x2F, + 0x00, + 0x04, + 0x00, + 0x00, + 0x79, + 0xA8, + /*0360*/ 0x40, + 0x2F, + 0x00, + 0x40, + 0x00, + 0x00, + 0x0F, + 0x31, + 0x40, + 0x2F, + 0x00, + 0x44, + 0x00, + 0x65, + 0x29, + 0x22, + /*0370*/ 0x40, + 0x2F, + 0x00, + 0x48, + 0x00, + 0x01, + 0x09, + 0x20, + 0x40, + 0x2F, + 0x00, + 0x4C, + 0x50, + 0x21, + 0x0A, + 0x08, + /*0380*/ 0x40, + 0x2F, + 0x00, + 0x80, + 0x00, + 0x00, + 0x00, + 0x21, + 0x40, + 0x2F, + 0x00, + 0x84, + 0x00, + 0x88, + 0x88, + 0x88, + /*0390*/ 0x40, + 0x2F, + 0x00, + 0x94, + 0x00, + 0x00, + 0x00, + 0x02, + 0x40, + 0x2F, + 0x00, + 0x98, + 0x00, + 0x00, + 0x00, + 0x00, + /*03a0*/ 0x40, + 0x2F, + 0x00, + 0x90, + 0x80, + 0x00, + 0x00, + 0x00, + 0x40, + 0x2F, + 0x00, + 0x9C, + 0xA5, + 0x5A, + 0x00, + 0x0F, + /*03b0*/ 0xCF, + 0x00, + 0x0C, + 0x1C, + 0x40, + 0x2F, + 0x00, + 0x3C, + 0x00, + 0x00, + 0x00, + 0x01, + 0xCC, + 0x00, + 0x14, + 0x04, + /*03c0*/ 0x40, + 0x2F, + 0x00, + 0x90, + 0x80, + 0x00, + 0x00, + 0x00, + 0x40, + 0x2F, + 0x00, + 0x9C, + 0xA5, + 0x5A, + 0x00, + 0x0C, + /*03d0*/ 0xCF, + 0x00, + 0x0C, + 0x1C, + 0x40, + 0x2F, + 0x00, + 0x3C, + 0x00, + 0x00, + 0x00, + 0x01, + 0xCC, + 0x00, + 0x14, + 0x04, + /*03e0*/ 0x40, + 0x2F, + 0x00, + 0x90, + 0x80, + 0x00, + 0x00, + 0x00, + 0x40, + 0x2F, + 0x00, + 0x9C, + 0xA5, + 0x5A, + 0x00, + 0x0C, + /*03f0*/ 0xCF, + 0x00, + 0x0C, + 0x1C, + 0x40, + 0x2F, + 0x00, + 0x3C, + 0x00, + 0x00, + 0x00, + 0x01, + 0xCC, + 0x00, + 0x1C, + 0x04, + /*0400*/ 0x40, + 0x2F, + 0x00, + 0xA0, + 0x00, + 0x00, + 0x00, + 0x33, + 0x40, + 0x2F, + 0x00, + 0x90, + 0x80, + 0x00, + 0x00, + 0x00, + /*0410*/ 0x40, + 0x2F, + 0x00, + 0x9C, + 0xA5, + 0x5A, + 0x00, + 0x0A, + 0xCF, + 0x00, + 0x0C, + 0x1C, + 0x40, + 0x2F, + 0x00, + 0x3C, + /*0420*/ 0x00, + 0x00, + 0x00, + 0x01, + 0xCC, + 0x00, + 0x0C, + 0x04, + 0x40, + 0x2F, + 0x00, + 0x4C, + 0x50, + 0x07, + 0x0A, + 0x09, +}; diff --git a/Ubiquitous/XiZi/board/xidatong/xip/fsl_flexspi_nor_boot.h b/Ubiquitous/XiZi/board/xidatong/xip/fsl_flexspi_nor_boot.h new file mode 100644 index 000000000..b6677b274 --- /dev/null +++ b/Ubiquitous/XiZi/board/xidatong/xip/fsl_flexspi_nor_boot.h @@ -0,0 +1,123 @@ +/* + * Copyright 2017 NXP + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * o Redistributions of source code must retain the above copyright notice, this list + * of conditions and the following disclaimer. + * + * o Redistributions in binary form must reproduce the above copyright notice, this + * list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * o Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** +* @file fsl_flexspi_nor_boot.h +* @brief support to register flexspi image vector table +* @version 2.0 +* @author AIIT XUOS Lab +* @date 2022-03-22 +*/ + +#ifndef _QUADSPI_BOOT_H_ +#define _QUADSPI_BOOT_H_ + +#include + +/************************************* + * IVT Data + *************************************/ +typedef struct _ivt_ { + /** @ref hdr with tag #HAB_TAG_IVT, length and HAB version fields + * (see @ref data) + */ + uint32_t hdr; + /** Absolute address of the first instruction to execute from the + * image + */ + uint32_t entry; + /** Reserved in this version of HAB: should be NULL. */ + uint32_t reserved1; + /** Absolute address of the image DCD: may be NULL. */ + uint32_t dcd; + /** Absolute address of the Boot Data: may be NULL, but not interpreted + * any further by HAB + */ + uint32_t boot_data; + /** Absolute address of the IVT.*/ + uint32_t self; + /** Absolute address of the image CSF.*/ + uint32_t csf; + /** Reserved in this version of HAB: should be zero. */ + uint32_t reserved2; +} ivt; + +#define IVT_MAJOR_VERSION 0x4 +#define IVT_MAJOR_VERSION_SHIFT 0x4 +#define IVT_MAJOR_VERSION_MASK 0xF +#define IVT_MINOR_VERSION 0x1 +#define IVT_MINOR_VERSION_SHIFT 0x0 +#define IVT_MINOR_VERSION_MASK 0xF + +#define IVT_VERSION(major, minor) \ + ((((major) & IVT_MAJOR_VERSION_MASK) << IVT_MAJOR_VERSION_SHIFT) | \ + (((minor) & IVT_MINOR_VERSION_MASK) << IVT_MINOR_VERSION_SHIFT)) + +#define IVT_TAG_HEADER (0xD1) /**< Image Vector Table */ +#define IVT_SIZE 0x2000 +#define IVT_PAR IVT_VERSION(IVT_MAJOR_VERSION, IVT_MINOR_VERSION) + +#define IVT_HEADER (IVT_TAG_HEADER | (IVT_SIZE << 8) | (IVT_PAR << 24)) +#define IVT_RSVD (uint32_t)(0x00000000) + + +/************************************* + * Boot Data + *************************************/ +typedef struct _boot_data_ { + uint32_t start; /* boot start location */ + uint32_t size; /* size */ + uint32_t plugin; /* plugin flag - 1 if downloaded application is plugin */ + uint32_t placeholder; /* placehoder to make even 0x10 size */ +}BOOT_DATA_T; + + +/************************************* + * DCD Data + *************************************/ +#define DCD_TAG_HEADER (0xD2) +#define DCD_TAG_HEADER_SHIFT (24) +#define DCD_VERSION (0x40) +#define DCD_ARRAY_SIZE 1 + +#define FLASH_BASE 0x60000000 +#define FLASH_END 0x7F7FFFFF +#define SCLK 1 + +#define DCD_ADDRESS dcd_sdram +#define BOOT_DATA_ADDRESS &boot_data +#define CSF_ADDRESS 0 +#define PLUGIN_FLAG (uint32_t)0 + +/* External Variables */ +//extern const uint8_t dcd_sdram[1044]; +extern const uint8_t dcd_sdram[1072]; +extern const BOOT_DATA_T boot_data; + +#endif diff --git a/Ubiquitous/XiZi/board/xidatong/xip/fsl_flexspi_nor_flash.c b/Ubiquitous/XiZi/board/xidatong/xip/fsl_flexspi_nor_flash.c new file mode 100644 index 000000000..c5cc52dbc --- /dev/null +++ b/Ubiquitous/XiZi/board/xidatong/xip/fsl_flexspi_nor_flash.c @@ -0,0 +1,88 @@ +/* + * Copyright 2017 NXP + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * o Redistributions of source code must retain the above copyright notice, this list + * of conditions and the following disclaimer. + * + * o Redistributions in binary form must reproduce the above copyright notice, this + * list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * o Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** +* @file fsl_flexspi_nor_flash.c +* @brief support to define flexspi flash config +* @version 2.0 +* @author AIIT XUOS Lab +* @date 2022-03-22 +*/ + +#include "fsl_flexspi_nor_flash.h" + +/******************************************************************************* + * Code + ******************************************************************************/ +#if defined(__CC_ARM) || defined(__GNUC__) + __attribute__((section(".boot_hdr.conf"))) +#elif defined(__ICCARM__) +#pragma location=".boot_hdr.conf" +#endif + +const flexspi_nor_config_t Qspiflash_config = +{ + .memConfig = + { + .tag = FLEXSPI_CFG_BLK_TAG, + .version = FLEXSPI_CFG_BLK_VERSION, + .readSampleClkSrc = kFlexSPIReadSampleClk_LoopbackInternally, + .csHoldTime = 3u, + .csSetupTime = 3u, + .deviceModeCfgEnable = true, + .deviceModeType = 1,//Quad Enable command + .deviceModeSeq.seqNum = 1, + .deviceModeSeq.seqId = 4, + .deviceModeArg = 0x000200,//Set QE + .deviceType = kFlexSpiDeviceType_SerialNOR, + .sflashPadType = kSerialFlash_4Pads, + .serialClkFreq = kFlexSpiSerialClk_60MHz,//80MHz for Winbond, 100MHz for GD, 133MHz for ISSI + .sflashA1Size = 16u * 1024u * 1024u,//4MBytes + .dataValidTime = {16u, 16u}, + .lookupTable = + { +// //Fast Read Sequence +// [0] = FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, 0x0B, RADDR_SDR, FLEXSPI_1PAD, 0x18), +// [1] = FLEXSPI_LUT_SEQ(DUMMY_SDR, FLEXSPI_1PAD, 0x08, READ_SDR, FLEXSPI_1PAD, 0x08), +// [2] = FLEXSPI_LUT_SEQ(JMP_ON_CS, 0, 0, 0, 0, 0), + //Quad Input/output read sequence + [0] = FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, 0xEB, RADDR_SDR, FLEXSPI_4PAD, 0x18), + [1] = FLEXSPI_LUT_SEQ(DUMMY_SDR, FLEXSPI_4PAD, 0x06, READ_SDR, FLEXSPI_4PAD, 0x04), + [2] = FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), + //Read Status + [1*4] = FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, 0x05, READ_SDR, FLEXSPI_1PAD, 0x04), + //Write Enable + [3*4] = FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, 0x06, STOP, 0, 0), + //Write status + [4*4] = FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, 0x01, WRITE_SDR, FLEXSPI_1PAD, 0x2), + }, + }, + .pageSize = 256u, + .sectorSize = 4u * 1024u, +}; diff --git a/Ubiquitous/XiZi/board/xidatong/xip/fsl_flexspi_nor_flash.h b/Ubiquitous/XiZi/board/xidatong/xip/fsl_flexspi_nor_flash.h new file mode 100644 index 000000000..73d4547b7 --- /dev/null +++ b/Ubiquitous/XiZi/board/xidatong/xip/fsl_flexspi_nor_flash.h @@ -0,0 +1,303 @@ +/* + * Copyright (c) 2016, Freescale Semiconductor, Inc. + * Copyright 2016-2017 NXP + * + * Redistribution and use in source and binary forms, with or without + * modification, + * are permitted provided that the following conditions are met: + * + * o Redistributions of source code must retain the above copyright notice, this + * list + * of conditions and the following disclaimer. + * + * o Redistributions in binary form must reproduce the above copyright notice, + * this + * list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * o Neither the name of Freescale Semiconductor, Inc. nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** +* @file fsl_flexspi_nor_flash.h +* @brief support to define flexspi flash config +* @version 2.0 +* @author AIIT XUOS Lab +* @date 2022-03-22 +*/ + +#ifndef __FLEXSPI_NOR_FLASH_H__ +#define __FLEXSPI_NOR_FLASH_H__ + +#include +#include +#include "fsl_common.h" + +/* FLEXSPI memory config block related defintions */ +#define FLEXSPI_CFG_BLK_TAG (0x42464346UL) // ascii "FCFB" Big Endian +#define FLEXSPI_CFG_BLK_VERSION (0x56010400UL) // V1.4.0 +#define FLEXSPI_CFG_BLK_SIZE (512) + +/* FLEXSPI Feature related definitions */ +#define FLEXSPI_FEATURE_HAS_PARALLEL_MODE 1 + +/* Lookup table related defintions */ +#define CMD_INDEX_READ 0 +#define CMD_INDEX_READSTATUS 1 +#define CMD_INDEX_WRITEENABLE 2 +#define CMD_INDEX_WRITE 4 + +#define CMD_LUT_SEQ_IDX_READ 0 +#define CMD_LUT_SEQ_IDX_READSTATUS 1 +#define CMD_LUT_SEQ_IDX_WRITEENABLE 3 +#define CMD_LUT_SEQ_IDX_WRITE 9 + +#define CMD_SDR 0x01 +#define CMD_DDR 0x21 +#define RADDR_SDR 0x02 +#define RADDR_DDR 0x22 +#define CADDR_SDR 0x03 +#define CADDR_DDR 0x23 +#define MODE1_SDR 0x04 +#define MODE1_DDR 0x24 +#define MODE2_SDR 0x05 +#define MODE2_DDR 0x25 +#define MODE4_SDR 0x06 +#define MODE4_DDR 0x26 +#define MODE8_SDR 0x07 +#define MODE8_DDR 0x27 +#define WRITE_SDR 0x08 +#define WRITE_DDR 0x28 +#define READ_SDR 0x09 +#define READ_DDR 0x29 +#define LEARN_SDR 0x0A +#define LEARN_DDR 0x2A +#define DATSZ_SDR 0x0B +#define DATSZ_DDR 0x2B +#define DUMMY_SDR 0x0C +#define DUMMY_DDR 0x2C +#define DUMMY_RWDS_SDR 0x0D +#define DUMMY_RWDS_DDR 0x2D +#define JMP_ON_CS 0x1F +#define STOP 0 + +#define FLEXSPI_1PAD 0 +#define FLEXSPI_2PAD 1 +#define FLEXSPI_4PAD 2 +#define FLEXSPI_8PAD 3 + +#define FLEXSPI_LUT_SEQ(cmd0, pad0, op0, cmd1, pad1, op1) \ + (FLEXSPI_LUT_OPERAND0(op0) | FLEXSPI_LUT_NUM_PADS0(pad0) | FLEXSPI_LUT_OPCODE0(cmd0) | FLEXSPI_LUT_OPERAND1(op1) | \ + FLEXSPI_LUT_NUM_PADS1(pad1) | FLEXSPI_LUT_OPCODE1(cmd1)) + +//!@brief Definitions for FlexSPI Serial Clock Frequency +typedef enum _FlexSpiSerialClockFreq +{ + kFlexSpiSerialClk_30MHz = 1, + kFlexSpiSerialClk_50MHz = 2, + kFlexSpiSerialClk_60MHz = 3, + kFlexSpiSerialClk_75MHz = 4, + kFlexSpiSerialClk_80MHz = 5, + kFlexSpiSerialClk_100MHz = 6, + kFlexSpiSerialClk_133MHz = 7, + kFlexSpiSerialClk_166MHz = 8, + kFlexSpiSerialClk_200MHz = 9, +} flexspi_serial_clk_freq_t; + +//!@brief FlexSPI clock configuration type +enum +{ + kFlexSpiClk_SDR, //!< Clock configure for SDR mode + kFlexSpiClk_DDR, //!< Clock configurat for DDR mode +}; + +//!@brief FlexSPI Read Sample Clock Source definition +typedef enum _FlashReadSampleClkSource +{ + kFlexSPIReadSampleClk_LoopbackInternally = 0, + kFlexSPIReadSampleClk_LoopbackFromDqsPad = 1, + kFlexSPIReadSampleClk_LoopbackFromSckPad = 2, + kFlexSPIReadSampleClk_ExternalInputFromDqsPad = 3, +} flexspi_read_sample_clk_t; + + +//!@brief Misc feature bit definitions +enum +{ + kFlexSpiMiscOffset_DiffClkEnable = 0, //!< Bit for Differential clock enable + kFlexSpiMiscOffset_Ck2Enable = 1, //!< Bit for CK2 enable + kFlexSpiMiscOffset_ParallelEnable = 2, //!< Bit for Parallel mode enable + kFlexSpiMiscOffset_WordAddressableEnable = 3, //!< Bit for Word Addressable enable + kFlexSpiMiscOffset_SafeConfigFreqEnable = 4, //!< Bit for Safe Configuration Frequency enable + kFlexSpiMiscOffset_PadSettingOverrideEnable = 5, //!< Bit for Pad setting override enable + kFlexSpiMiscOffset_DdrModeEnable = 6, //!< Bit for DDR clock confiuration indication. +}; + +//!@brief Flash Type Definition +enum +{ + kFlexSpiDeviceType_SerialNOR = 1, //!< Flash devices are Serial NOR + kFlexSpiDeviceType_SerialNAND = 2, //!< Flash devices are Serial NAND + kFlexSpiDeviceType_SerialRAM = 3, //!< Flash devices are Serial RAM/HyperFLASH + kFlexSpiDeviceType_MCP_NOR_NAND = 0x12, //!< Flash device is MCP device, A1 is Serial NOR, A2 is Serial NAND + kFlexSpiDeviceType_MCP_NOR_RAM = 0x13, //!< Flash deivce is MCP device, A1 is Serial NOR, A2 is Serial RAMs +}; + +//!@brief Flash Pad Definitions +enum +{ + kSerialFlash_1Pad = 1, + kSerialFlash_2Pads = 2, + kSerialFlash_4Pads = 4, + kSerialFlash_8Pads = 8, +}; + +//!@brief FlexSPI LUT Sequence structure +typedef struct _lut_sequence +{ + uint8_t seqNum; //!< Sequence Number, valid number: 1-16 + uint8_t seqId; //!< Sequence Index, valid number: 0-15 + uint16_t reserved; +} flexspi_lut_seq_t; + +//!@brief Flash Configuration Command Type +enum +{ + kDeviceConfigCmdType_Generic, //!< Generic command, for example: configure dummy cycles, drive strength, etc + kDeviceConfigCmdType_QuadEnable, //!< Quad Enable command + kDeviceConfigCmdType_Spi2Xpi, //!< Switch from SPI to DPI/QPI/OPI mode + kDeviceConfigCmdType_Xpi2Spi, //!< Switch from DPI/QPI/OPI to SPI mode + kDeviceConfigCmdType_Spi2NoCmd, //!< Switch to 0-4-4/0-8-8 mode + kDeviceConfigCmdType_Reset, //!< Reset device command +}; + +//!@brief FlexSPI Memory Configuration Block +typedef struct _FlexSPIConfig +{ + uint32_t tag; //!< [0x000-0x003] Tag, fixed value 0x42464346UL + uint32_t version; //!< [0x004-0x007] Version,[31:24] -'V', [23:16] - Major, [15:8] - Minor, [7:0] - bugfix + uint32_t reserved0; //!< [0x008-0x00b] Reserved for future use + uint8_t readSampleClkSrc; //!< [0x00c-0x00c] Read Sample Clock Source, valid value: 0/1/3 + uint8_t csHoldTime; //!< [0x00d-0x00d] CS hold time, default value: 3 + uint8_t csSetupTime; //!< [0x00e-0x00e] CS setup time, default value: 3 + uint8_t columnAddressWidth; //!< [0x00f-0x00f] Column Address with, for HyperBus protocol, it is fixed to 3, For + //! Serial NAND, need to refer to datasheet + uint8_t deviceModeCfgEnable; //!< [0x010-0x010] Device Mode Configure enable flag, 1 - Enable, 0 - Disable + uint8_t deviceModeType; //!< [0x011-0x011] Specify the configuration command type:Quad Enable, DPI/QPI/OPI switch, + //! Generic configuration, etc. + uint16_t waitTimeCfgCommands; //!< [0x012-0x013] Wait time for all configuration commands, unit: 100us, Used for + //! DPI/QPI/OPI switch or reset command + flexspi_lut_seq_t deviceModeSeq; //!< [0x014-0x017] Device mode sequence info, [7:0] - LUT sequence id, [15:8] - LUt + //! sequence number, [31:16] Reserved + uint32_t deviceModeArg; //!< [0x018-0x01b] Argument/Parameter for device configuration + uint8_t configCmdEnable; //!< [0x01c-0x01c] Configure command Enable Flag, 1 - Enable, 0 - Disable + uint8_t configModeType[3]; //!< [0x01d-0x01f] Configure Mode Type, similar as deviceModeTpe + flexspi_lut_seq_t + configCmdSeqs[3]; //!< [0x020-0x02b] Sequence info for Device Configuration command, similar as deviceModeSeq + uint32_t reserved1; //!< [0x02c-0x02f] Reserved for future use + uint32_t configCmdArgs[3]; //!< [0x030-0x03b] Arguments/Parameters for device Configuration commands + uint32_t reserved2; //!< [0x03c-0x03f] Reserved for future use + uint32_t controllerMiscOption; //!< [0x040-0x043] Controller Misc Options, see Misc feature bit definitions for more + //! details + uint8_t deviceType; //!< [0x044-0x044] Device Type: See Flash Type Definition for more details + uint8_t sflashPadType; //!< [0x045-0x045] Serial Flash Pad Type: 1 - Single, 2 - Dual, 4 - Quad, 8 - Octal + uint8_t serialClkFreq; //!< [0x046-0x046] Serial Flash Frequencey, device specific definitions, See System Boot + //! Chapter for more details + uint8_t lutCustomSeqEnable; //!< [0x047-0x047] LUT customization Enable, it is required if the program/erase cannot + //! be done using 1 LUT sequence, currently, only applicable to HyperFLASH + uint32_t reserved3[2]; //!< [0x048-0x04f] Reserved for future use + uint32_t sflashA1Size; //!< [0x050-0x053] Size of Flash connected to A1 + uint32_t sflashA2Size; //!< [0x054-0x057] Size of Flash connected to A2 + uint32_t sflashB1Size; //!< [0x058-0x05b] Size of Flash connected to B1 + uint32_t sflashB2Size; //!< [0x05c-0x05f] Size of Flash connected to B2 + uint32_t csPadSettingOverride; //!< [0x060-0x063] CS pad setting override value + uint32_t sclkPadSettingOverride; //!< [0x064-0x067] SCK pad setting override value + uint32_t dataPadSettingOverride; //!< [0x068-0x06b] data pad setting override value + uint32_t dqsPadSettingOverride; //!< [0x06c-0x06f] DQS pad setting override value + uint32_t timeoutInMs; //!< [0x070-0x073] Timeout threshold for read status command + uint32_t commandInterval; //!< [0x074-0x077] CS deselect interval between two commands + uint16_t dataValidTime[2]; //!< [0x078-0x07b] CLK edge to data valid time for PORT A and PORT B, in terms of 0.1ns + uint16_t busyOffset; //!< [0x07c-0x07d] Busy offset, valid value: 0-31 + uint16_t busyBitPolarity; //!< [0x07e-0x07f] Busy flag polarity, 0 - busy flag is 1 when flash device is busy, 1 - + //! busy flag is 0 when flash device is busy + uint32_t lookupTable[64]; //!< [0x080-0x17f] Lookup table holds Flash command sequences + flexspi_lut_seq_t lutCustomSeq[12]; //!< [0x180-0x1af] Customizable LUT Sequences + uint32_t reserved4[4]; //!< [0x1b0-0x1bf] Reserved for future use +} flexspi_mem_config_t; + +/* */ +#define NOR_CMD_INDEX_READ CMD_INDEX_READ //!< 0 +#define NOR_CMD_INDEX_READSTATUS CMD_INDEX_READSTATUS //!< 1 +#define NOR_CMD_INDEX_WRITEENABLE CMD_INDEX_WRITEENABLE //!< 2 +#define NOR_CMD_INDEX_ERASESECTOR 3 //!< 3 +#define NOR_CMD_INDEX_PAGEPROGRAM CMD_INDEX_WRITE //!< 4 +#define NOR_CMD_INDEX_CHIPERASE 5 //!< 5 +#define NOR_CMD_INDEX_DUMMY 6 //!< 6 +#define NOR_CMD_INDEX_ERASEBLOCK 7 //!< 7 + +#define NOR_CMD_LUT_SEQ_IDX_READ CMD_LUT_SEQ_IDX_READ //!< 0 READ LUT sequence id in lookupTable stored in config block +#define NOR_CMD_LUT_SEQ_IDX_READSTATUS \ + CMD_LUT_SEQ_IDX_READSTATUS //!< 1 Read Status LUT sequence id in lookupTable stored in config block +#define NOR_CMD_LUT_SEQ_IDX_READSTATUS_XPI \ + 2 //!< 2 Read status DPI/QPI/OPI sequence id in lookupTable stored in config block +#define NOR_CMD_LUT_SEQ_IDX_WRITEENABLE \ + CMD_LUT_SEQ_IDX_WRITEENABLE //!< 3 Write Enable sequence id in lookupTable stored in config block +#define NOR_CMD_LUT_SEQ_IDX_WRITEENABLE_XPI \ + 4 //!< 4 Write Enable DPI/QPI/OPI sequence id in lookupTable stored in config block +#define NOR_CMD_LUT_SEQ_IDX_ERASESECTOR 5 //!< 5 Erase Sector sequence id in lookupTable stored in config block +#define NOR_CMD_LUT_SEQ_IDX_ERASEBLOCK 8 //!< 8 Erase Block sequence id in lookupTable stored in config block +#define NOR_CMD_LUT_SEQ_IDX_PAGEPROGRAM \ + CMD_LUT_SEQ_IDX_WRITE //!< 9 Program sequence id in lookupTable stored in config block +#define NOR_CMD_LUT_SEQ_IDX_CHIPERASE 11 //!< 11 Chip Erase sequence in lookupTable id stored in config block +#define NOR_CMD_LUT_SEQ_IDX_READ_SFDP 13 //!< 13 Read SFDP sequence in lookupTable id stored in config block +#define NOR_CMD_LUT_SEQ_IDX_RESTORE_NOCMD \ + 14 //!< 14 Restore 0-4-4/0-8-8 mode sequence id in lookupTable stored in config block +#define NOR_CMD_LUT_SEQ_IDX_EXIT_NOCMD \ + 15 //!< 15 Exit 0-4-4/0-8-8 mode sequence id in lookupTable stored in config blobk + + +/* + * Serial NOR configuration block + */ +typedef struct _flexspi_nor_config +{ + flexspi_mem_config_t memConfig; //!< Common memory configuration info via FlexSPI + uint32_t pageSize; //!< Page size of Serial NOR + uint32_t sectorSize; //!< Sector size of Serial NOR + uint8_t ipcmdSerialClkFreq; //!< Clock frequency for IP command + uint8_t isUniformBlockSize; //!< Sector/Block size is the same + uint8_t reserved0[2]; //!< Reserved for future use + uint8_t serialNorType; //!< Serial NOR Flash type: 0/1/2/3 + uint8_t needExitNoCmdMode; //!< Need to exit NoCmd mode before other IP command + uint8_t halfClkForNonReadCmd; //!< Half the Serial Clock for non-read command: true/false + uint8_t needRestoreNoCmdMode; //!< Need to Restore NoCmd mode after IP commmand execution + uint32_t blockSize; //!< Block size + uint32_t reserve2[11]; //!< Reserved for future use +} flexspi_nor_config_t; + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +} +#endif + +#endif // __FLEXSPI_NOR_FLASH_H__ diff --git a/Ubiquitous/XiZi/path_kernel.mk b/Ubiquitous/XiZi/path_kernel.mk index bd7c40980..b1b906b18 100755 --- a/Ubiquitous/XiZi/path_kernel.mk +++ b/Ubiquitous/XiZi/path_kernel.mk @@ -18,7 +18,8 @@ KERNELPATHS :=-I$(BSP_ROOT) \ -I$(BSP_ROOT)/third_party_driver/CMSIS/Include \ -I$(KERNEL_ROOT)/include \ -I$(KERNEL_ROOT)/resources/include \ - -I$(BSP_ROOT)/include # + -I$(BSP_ROOT)/include \ + -I$(BSP_ROOT)/xip # endif ifeq ($(BSP_ROOT),$(KERNEL_ROOT)/board/kd233) @@ -225,7 +226,8 @@ KERNELPATHS :=-I$(BSP_ROOT) \ -I$(KERNEL_ROOT)/resources/ethernet/LwIP/include/lwip/prot \ -I$(KERNEL_ROOT)/resources/ethernet/LwIP/arch \ -I$(KERNEL_ROOT)/resources/include \ - -I$(BSP_ROOT)/include # + -I$(BSP_ROOT)/include \ + -I$(BSP_ROOT)/xip # endif ifeq ($(BSP_ROOT),$(KERNEL_ROOT)/board/stm32f103-nano)

t&8Yl!nSFkEnlP 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 DLICUf literal 0 HcmV?d00001 diff --git a/Ubiquitous/XiZi/board/ok1052-c/link-usb.lds b/Ubiquitous/XiZi/board/ok1052-c/link-usb.lds index 41649a29f..1a3f12813 100755 --- a/Ubiquitous/XiZi/board/ok1052-c/link-usb.lds +++ b/Ubiquitous/XiZi/board/ok1052-c/link-usb.lds @@ -54,6 +54,9 @@ STACK_SIZE = DEFINED(__stack_size__) ? __stack_size__ : 0x1000; /* Specify the memory areas */ MEMORY { + m_boot_data (RX) : ORIGIN = 0x60000000, LENGTH = 0x00001000 + m_image_vertor_table (RX) : ORIGIN = 0x60001000, LENGTH = 0x00001000 + m_interrupts (RX) : ORIGIN = 0x60002000, LENGTH = 0x00000400 m_text (RX) : ORIGIN = 0x60002400, LENGTH = 0x03FFDC00 m_data (RW) : ORIGIN = 0x20000000, LENGTH = 0x00020000 @@ -63,6 +66,17 @@ MEMORY /* Define output sections */ SECTIONS { + .boot_data : + { + KEEP(*(.boot_hdr.conf)) + } > m_boot_data + + .image_vertor_table : + { + KEEP(*(.boot_hdr.ivt)) + KEEP(*(.boot_hdr.boot_data)) + KEEP(*(.boot_hdr.dcd_data)) + } > m_image_vertor_table /* The startup code goes first into internal RAM */ .interrupts : diff --git a/Ubiquitous/XiZi/board/ok1052-c/link.lds b/Ubiquitous/XiZi/board/ok1052-c/link.lds index 0d3ab2a58..553fadb86 100644 --- a/Ubiquitous/XiZi/board/ok1052-c/link.lds +++ b/Ubiquitous/XiZi/board/ok1052-c/link.lds @@ -54,6 +54,9 @@ STACK_SIZE = DEFINED(__stack_size__) ? __stack_size__ : 0x1000; /* Specify the memory areas */ MEMORY { + m_boot_data (RX) : ORIGIN = 0x60000000, LENGTH = 0x00001000 + m_image_vertor_table (RX) : ORIGIN = 0x60001000, LENGTH = 0x00001000 + m_interrupts (RX) : ORIGIN = 0x60002000, LENGTH = 0x00000400 m_text (RX) : ORIGIN = 0x60002400, LENGTH = 0x03FFDC00 m_data (RW) : ORIGIN = 0x20000000, LENGTH = 0x00020000 @@ -63,6 +66,17 @@ MEMORY /* Define output sections */ SECTIONS { + .boot_data : + { + KEEP(*(.boot_hdr.conf)) + } > m_boot_data + + .image_vertor_table : + { + KEEP(*(.boot_hdr.ivt)) + KEEP(*(.boot_hdr.boot_data)) + KEEP(*(.boot_hdr.dcd_data)) + } > m_image_vertor_table /* The startup code goes first into internal RAM */ .interrupts : diff --git a/Ubiquitous/XiZi/board/ok1052-c/third_party_driver/gpio/connect_gpio.c b/Ubiquitous/XiZi/board/ok1052-c/third_party_driver/gpio/connect_gpio.c index a61d7d035..3032b282e 100755 --- a/Ubiquitous/XiZi/board/ok1052-c/third_party_driver/gpio/connect_gpio.c +++ b/Ubiquitous/XiZi/board/ok1052-c/third_party_driver/gpio/connect_gpio.c @@ -13,7 +13,7 @@ * @brief support gpio function using bus driver framework * @version 2.0 * @author AIIT XUOS Lab -* @date 2022-03-16 +* @date 2022-03-22 */ /************************************************* @@ -22,7 +22,7 @@ Description: support gpio configure and register to bus framework Others: take RT-Thread v4.0.2/bsp/imxrt/libraries/drivers/drv_gpio.c for references https://github.com/RT-Thread/rt-thread/tree/v4.0.2 History: -1. Date: 2022-03-16 +1. Date: 2022-03-22 Author: AIIT XUOS Lab Modification: add bus driver framework support for gpio *************************************************/ @@ -215,6 +215,20 @@ struct PinIrqHdr pin_irq_hdr_tab[] = {-1, 0, NONE, NONE}, }; +#define MUX_BASE 0x401f8014 +#define CONFIG_BASE 0x401f8204 + +#define GPIO5_MUX_BASE 0x400A8000 +#define GPIO5_CONFIG_BASE 0x400A8018 + +const uint8_t reg_offset[] = +{ + 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99,100,101,102,103,104,105, + 112,113,114,115,116,117,118,119,120,121,122,123,106,107,108,109,110,111, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, -1, -1, -1, -1, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, +}; + static int GetPin(struct PinIndex *pin_index, uint8_t pin) { pin_index->index = pin >> 5;//0:GPIO1 1:GPIO2 2:GPIO3 3:GPIO4 4:GPIO5 @@ -229,39 +243,52 @@ static int GetPin(struct PinIndex *pin_index, uint8_t pin) return 0; } -static int32 GpioConfigMode(int mode, struct PinIndex *index) +static int32 GpioConfigMode(int mode, struct PinIndex *pin_index, int32 pin) { gpio_pin_config_t gpio_config; - NULL_PARAM_CHECK(index); + uint32_t config_value = 0; + NULL_PARAM_CHECK(pin_index); gpio_config.outputLogic = 0; + gpio_config.interruptMode = kGPIO_NoIntmode; switch (mode) { case GPIO_CFG_OUTPUT: gpio_config.direction = kGPIO_DigitalOutput; - gpio_config.interruptMode = kGPIO_NoIntmode; + config_value = 0x0030U; /* Drive Strength R0/6 */ break; case GPIO_CFG_INPUT: gpio_config.direction = kGPIO_DigitalInput; - gpio_config.interruptMode = kGPIO_NoIntmode; + config_value = 0x0830U; /* Open Drain Enable */ break; case GPIO_CFG_INPUT_PULLUP: gpio_config.direction = kGPIO_DigitalInput; - gpio_config.interruptMode = kGPIO_NoIntmode; + config_value = 0xB030U; /* 100K Ohm Pull Up */ break; case GPIO_CFG_INPUT_PULLDOWN: gpio_config.direction = kGPIO_DigitalInput; - gpio_config.interruptMode = kGPIO_NoIntmode; + config_value = 0x3030U; /* 100K Ohm Pull Down */ break; case GPIO_CFG_OUTPUT_OD: gpio_config.direction = kGPIO_DigitalOutput; - gpio_config.interruptMode = kGPIO_NoIntmode; + config_value = 0x0830U; /* Open Drain Enable */ break; default: break; } - GPIO_PinInit(index->gpio, index->pin, &gpio_config); + + if (pin_mask[pin_index->index].gpio != GPIO5) { + CLOCK_EnableClock(kCLOCK_Iomuxc); + IOMUXC_SetPinMux(MUX_BASE + reg_offset[pin] * 4, 0x5U, 0, 0, CONFIG_BASE + reg_offset[pin] * 4, 1); + IOMUXC_SetPinConfig(MUX_BASE + reg_offset[pin] * 4, 0x5U, 0, 0, CONFIG_BASE + reg_offset[pin] * 4, config_value); + } else { + CLOCK_EnableClock(kCLOCK_IomuxcSnvs); + IOMUXC_SetPinMux(GPIO5_MUX_BASE + pin_index->pin * 4, 0x5U, 0, 0, GPIO5_CONFIG_BASE + pin_index->pin * 4, 1); + IOMUXC_SetPinConfig(GPIO5_MUX_BASE + pin_index->pin * 4, 0x5U, 0, 0, GPIO5_CONFIG_BASE + pin_index->pin * 4, config_value); + } + + GPIO_PinInit(pin_index->gpio, pin_index->pin, &gpio_config); return EOK; } @@ -385,6 +412,7 @@ static uint32 Imxrt1052PinConfigure(struct PinParam *param) struct PinIndex pin_index; + KPrintf("Imxrt1052PinConfigure\n"); if (GetPin(&pin_index, param->pin) < 0) { return ERROR; } @@ -392,7 +420,8 @@ static uint32 Imxrt1052PinConfigure(struct PinParam *param) switch(param->cmd) { case GPIO_CONFIG_MODE: - GpioConfigMode(param->mode, &pin_index); + KPrintf("GpioConfigMode %u\n", param->pin); + GpioConfigMode(param->mode, &pin_index, param->pin); break; case GPIO_IRQ_REGISTER: ret = GpioIrqRegister(param->pin, param->irq_set.irq_mode, param->irq_set.hdr, param->irq_set.args); @@ -705,21 +734,6 @@ void GpioLedTest(void) KPrintf("initialize %s failed!\n", PIN_BUS_NAME); return; } - - IOMUXC_SetPinMux( - IOMUXC_GPIO_AD_B0_09_GPIO1_IO09, /* GPIO_AD_B0_09 is configured as GPIO1_IO09 */ - 0U); /* Software Input On Field: Input Path is determined by functionality */ - /* Software Input On Field: Input Path is determined by functionality */ - IOMUXC_SetPinConfig( - IOMUXC_GPIO_AD_B0_09_GPIO1_IO09, /* GPIO_AD_B0_09 PAD functional properties : */ - 0x10B0u); /* Slew Rate Field: Slow Slew Rate - Drive Strength Field: R0/6 - Speed Field: medium(100MHz) - Open Drain Enable Field: Open Drain Disabled - Pull / Keep Enable Field: Pull/Keeper Enabled - Pull / Keep Select Field: Keeper - Pull Up / Down Config. Field: 100K Ohm Pull Down - Hyst. Enable Field: Hysteresis Disabled */ struct PinParam led_gpio_param; struct PinStat led_gpio_stat; @@ -762,4 +776,3 @@ void GpioLedTest(void) SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN), GpioLedTest, GpioLedTest, GpioLedTest GPIO1 IO09 LED); #endif - diff --git a/Ubiquitous/XiZi/board/ok1052-c/xip/Makefile b/Ubiquitous/XiZi/board/ok1052-c/xip/Makefile new file mode 100755 index 000000000..e27f9550a --- /dev/null +++ b/Ubiquitous/XiZi/board/ok1052-c/xip/Makefile @@ -0,0 +1,3 @@ +SRC_FILES := fsl_flexspi_nor_boot.c fsl_flexspi_nor_flash.c + +include $(KERNEL_ROOT)/compiler.mk diff --git a/Ubiquitous/XiZi/board/ok1052-c/xip/fsl_flexspi_nor_boot.c b/Ubiquitous/XiZi/board/ok1052-c/xip/fsl_flexspi_nor_boot.c new file mode 100644 index 000000000..503d56845 --- /dev/null +++ b/Ubiquitous/XiZi/board/ok1052-c/xip/fsl_flexspi_nor_boot.c @@ -0,0 +1,1149 @@ +/* + * Copyright 2017 NXP + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * o Redistributions of source code must retain the above copyright notice, this list + * of conditions and the following disclaimer. + * + * o Redistributions in binary form must reproduce the above copyright notice, this + * list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * o Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** +* @file fsl_flexspi_nor_boot.c +* @brief support to register flexspi image vector table +* @version 2.0 +* @author AIIT XUOS Lab +* @date 2022-03-22 +*/ + +#include "fsl_flexspi_nor_boot.h" + +#if defined(__CC_ARM) || defined(__GNUC__) + __attribute__((section(".boot_hdr.ivt"))) +#elif defined(__ICCARM__) +#pragma location=".boot_hdr.ivt" +#endif + +const ivt image_vector_table = { + IVT_HEADER, /* IVT Header */ + 0x60002000, /* Image Entry Function */ + IVT_RSVD, /* Reserved = 0 */ + (uint32_t)DCD_ADDRESS, /* Address where DCD information is stored */ + (uint32_t)BOOT_DATA_ADDRESS, /* Address where BOOT Data Structure is stored */ + (uint32_t)&image_vector_table, /* Pointer to IVT Self (absolute address */ + (uint32_t)CSF_ADDRESS, /* Address where CSF file is stored */ + IVT_RSVD /* Reserved = 0 */ +}; + +#if defined(__CC_ARM) || defined(__GNUC__) + __attribute__((section(".boot_hdr.boot_data"))) +#elif defined(__ICCARM__) +#pragma location=".boot_hdr.boot_data" +#endif + +const BOOT_DATA_T boot_data = { + FLASH_BASE, /* boot start location */ + (FLASH_END-FLASH_BASE), /* size */ + PLUGIN_FLAG, /* Plugin flag*/ + 0xFFFFFFFF /* empty - extra data word */ +}; + +#if defined(__CC_ARM) || defined(__GNUC__) + __attribute__((section(".boot_hdr.dcd_data"))) +#elif defined(__ICCARM__) +#pragma location=".boot_hdr.dcd_data" +#endif + +const uint8_t dcd_sdram[1072] = { + /*0000*/ 0xD2, + 0x04, + 0x30, + 0x41, + 0xCC, + 0x03, + 0xAC, + 0x04, + 0x40, + 0x0F, + 0xC0, + 0x68, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + /*0010*/ 0x40, + 0x0F, + 0xC0, + 0x6C, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0x40, + 0x0F, + 0xC0, + 0x70, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + /*0020*/ 0x40, + 0x0F, + 0xC0, + 0x74, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0x40, + 0x0F, + 0xC0, + 0x78, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + /*0030*/ 0x40, + 0x0F, + 0xC0, + 0x7C, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0x40, + 0x0F, + 0xC0, + 0x80, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + /*0040*/ 0x40, + 0x0D, + 0x80, + 0x30, + 0x00, + 0x00, + 0x20, + 0x01, + 0x40, + 0x0D, + 0x81, + 0x00, + 0x00, + 0x1D, + 0x00, + 0x00, + /*0050*/ 0x40, + 0x0F, + 0xC0, + 0x14, + 0x00, + 0x01, + 0x0D, + 0x40, + 0x40, + 0x1F, + 0x80, + 0x14, + 0x00, + 0x00, + 0x00, + 0x00, + /*0060*/ 0x40, + 0x1F, + 0x80, + 0x18, + 0x00, + 0x00, + 0x00, + 0x00, + 0x40, + 0x1F, + 0x80, + 0x1C, + 0x00, + 0x00, + 0x00, + 0x00, + /*0070*/ 0x40, + 0x1F, + 0x80, + 0x20, + 0x00, + 0x00, + 0x00, + 0x00, + 0x40, + 0x1F, + 0x80, + 0x24, + 0x00, + 0x00, + 0x00, + 0x00, + /*0080*/ 0x40, + 0x1F, + 0x80, + 0x28, + 0x00, + 0x00, + 0x00, + 0x00, + 0x40, + 0x1F, + 0x80, + 0x2C, + 0x00, + 0x00, + 0x00, + 0x00, + /*0090*/ 0x40, + 0x1F, + 0x80, + 0x30, + 0x00, + 0x00, + 0x00, + 0x00, + 0x40, + 0x1F, + 0x80, + 0x34, + 0x00, + 0x00, + 0x00, + 0x00, + /*00a0*/ 0x40, + 0x1F, + 0x80, + 0x38, + 0x00, + 0x00, + 0x00, + 0x00, + 0x40, + 0x1F, + 0x80, + 0x3C, + 0x00, + 0x00, + 0x00, + 0x00, + /*00b0*/ 0x40, + 0x1F, + 0x80, + 0x40, + 0x00, + 0x00, + 0x00, + 0x00, + 0x40, + 0x1F, + 0x80, + 0x44, + 0x00, + 0x00, + 0x00, + 0x00, + /*00c0*/ 0x40, + 0x1F, + 0x80, + 0x48, + 0x00, + 0x00, + 0x00, + 0x00, + 0x40, + 0x1F, + 0x80, + 0x4C, + 0x00, + 0x00, + 0x00, + 0x00, + /*00d0*/ 0x40, + 0x1F, + 0x80, + 0x50, + 0x00, + 0x00, + 0x00, + 0x00, + 0x40, + 0x1F, + 0x80, + 0x54, + 0x00, + 0x00, + 0x00, + 0x00, + /*00e0*/ 0x40, + 0x1F, + 0x80, + 0x58, + 0x00, + 0x00, + 0x00, + 0x00, + 0x40, + 0x1F, + 0x80, + 0x5C, + 0x00, + 0x00, + 0x00, + 0x00, + /*00f0*/ 0x40, + 0x1F, + 0x80, + 0x60, + 0x00, + 0x00, + 0x00, + 0x00, + 0x40, + 0x1F, + 0x80, + 0x64, + 0x00, + 0x00, + 0x00, + 0x00, + /*0100*/ 0x40, + 0x1F, + 0x80, + 0x68, + 0x00, + 0x00, + 0x00, + 0x00, + 0x40, + 0x1F, + 0x80, + 0x6C, + 0x00, + 0x00, + 0x00, + 0x00, + /*0110*/ 0x40, + 0x1F, + 0x80, + 0x70, + 0x00, + 0x00, + 0x00, + 0x00, + 0x40, + 0x1F, + 0x80, + 0x74, + 0x00, + 0x00, + 0x00, + 0x00, + /*0120*/ 0x40, + 0x1F, + 0x80, + 0x78, + 0x00, + 0x00, + 0x00, + 0x00, + 0x40, + 0x1F, + 0x80, + 0x7C, + 0x00, + 0x00, + 0x00, + 0x00, + /*0130*/ 0x40, + 0x1F, + 0x80, + 0x80, + 0x00, + 0x00, + 0x00, + 0x00, + 0x40, + 0x1F, + 0x80, + 0x84, + 0x00, + 0x00, + 0x00, + 0x00, + /*0140*/ 0x40, + 0x1F, + 0x80, + 0x88, + 0x00, + 0x00, + 0x00, + 0x00, + 0x40, + 0x1F, + 0x80, + 0x8C, + 0x00, + 0x00, + 0x00, + 0x00, + /*0150*/ 0x40, + 0x1F, + 0x80, + 0x90, + 0x00, + 0x00, + 0x00, + 0x00, + 0x40, + 0x1F, + 0x80, + 0x94, + 0x00, + 0x00, + 0x00, + 0x00, + /*0160*/ 0x40, + 0x1F, + 0x80, + 0x98, + 0x00, + 0x00, + 0x00, + 0x00, + 0x40, + 0x1F, + 0x80, + 0x9C, + 0x00, + 0x00, + 0x00, + 0x00, + /*0170*/ 0x40, + 0x1F, + 0x80, + 0xA0, + 0x00, + 0x00, + 0x00, + 0x00, + 0x40, + 0x1F, + 0x80, + 0xA4, + 0x00, + 0x00, + 0x00, + 0x00, + /*0180*/ 0x40, + 0x1F, + 0x80, + 0xA8, + 0x00, + 0x00, + 0x00, + 0x00, + 0x40, + 0x1F, + 0x80, + 0xAC, + 0x00, + 0x00, + 0x00, + 0x00, + /*0190*/ 0x40, + 0x1F, + 0x80, + 0xB0, + 0x00, + 0x00, + 0x00, + 0x10, + 0x40, + 0x1F, + 0x80, + 0xB4, + 0x00, + 0x00, + 0x00, + 0x00, + /*01a0*/ 0x40, + 0x1F, + 0x80, + 0xB8, + 0x00, + 0x00, + 0x00, + 0x00, + 0x40, + 0x1F, + 0x82, + 0x04, + 0x00, + 0x01, + 0x10, + 0xF9, + /*01b0*/ 0x40, + 0x1F, + 0x82, + 0x08, + 0x00, + 0x01, + 0x10, + 0xF9, + 0x40, + 0x1F, + 0x82, + 0x0C, + 0x00, + 0x01, + 0x10, + 0xF9, + /*01c0*/ 0x40, + 0x1F, + 0x82, + 0x10, + 0x00, + 0x01, + 0x10, + 0xF9, + 0x40, + 0x1F, + 0x82, + 0x14, + 0x00, + 0x01, + 0x10, + 0xF9, + /*01d0*/ 0x40, + 0x1F, + 0x82, + 0x18, + 0x00, + 0x01, + 0x10, + 0xF9, + 0x40, + 0x1F, + 0x82, + 0x1C, + 0x00, + 0x01, + 0x10, + 0xF9, + /*01e0*/ 0x40, + 0x1F, + 0x82, + 0x20, + 0x00, + 0x01, + 0x10, + 0xF9, + 0x40, + 0x1F, + 0x82, + 0x24, + 0x00, + 0x01, + 0x10, + 0xF9, + /*01f0*/ 0x40, + 0x1F, + 0x82, + 0x28, + 0x00, + 0x01, + 0x10, + 0xF9, + 0x40, + 0x1F, + 0x82, + 0x2C, + 0x00, + 0x01, + 0x10, + 0xF9, + /*0200*/ 0x40, + 0x1F, + 0x82, + 0x30, + 0x00, + 0x01, + 0x10, + 0xF9, + 0x40, + 0x1F, + 0x82, + 0x34, + 0x00, + 0x01, + 0x10, + 0xF9, + /*0210*/ 0x40, + 0x1F, + 0x82, + 0x38, + 0x00, + 0x01, + 0x10, + 0xF9, + 0x40, + 0x1F, + 0x82, + 0x3C, + 0x00, + 0x01, + 0x10, + 0xF9, + /*0220*/ 0x40, + 0x1F, + 0x82, + 0x40, + 0x00, + 0x01, + 0x10, + 0xF9, + 0x40, + 0x1F, + 0x82, + 0x44, + 0x00, + 0x01, + 0x10, + 0xF9, + /*0230*/ 0x40, + 0x1F, + 0x82, + 0x48, + 0x00, + 0x01, + 0x10, + 0xF9, + 0x40, + 0x1F, + 0x82, + 0x4C, + 0x00, + 0x01, + 0x10, + 0xF9, + /*0240*/ 0x40, + 0x1F, + 0x82, + 0x50, + 0x00, + 0x01, + 0x10, + 0xF9, + 0x40, + 0x1F, + 0x82, + 0x54, + 0x00, + 0x01, + 0x10, + 0xF9, + /*0250*/ 0x40, + 0x1F, + 0x82, + 0x58, + 0x00, + 0x01, + 0x10, + 0xF9, + 0x40, + 0x1F, + 0x82, + 0x5C, + 0x00, + 0x01, + 0x10, + 0xF9, + /*0260*/ 0x40, + 0x1F, + 0x82, + 0x60, + 0x00, + 0x01, + 0x10, + 0xF9, + 0x40, + 0x1F, + 0x82, + 0x64, + 0x00, + 0x01, + 0x10, + 0xF9, + /*0270*/ 0x40, + 0x1F, + 0x82, + 0x68, + 0x00, + 0x01, + 0x10, + 0xF9, + 0x40, + 0x1F, + 0x82, + 0x6C, + 0x00, + 0x01, + 0x10, + 0xF9, + /*0280*/ 0x40, + 0x1F, + 0x82, + 0x70, + 0x00, + 0x01, + 0x10, + 0xF9, + 0x40, + 0x1F, + 0x82, + 0x74, + 0x00, + 0x01, + 0x10, + 0xF9, + /*0290*/ 0x40, + 0x1F, + 0x82, + 0x78, + 0x00, + 0x01, + 0x10, + 0xF9, + 0x40, + 0x1F, + 0x82, + 0x7C, + 0x00, + 0x01, + 0x10, + 0xF9, + /*02a0*/ 0x40, + 0x1F, + 0x82, + 0x80, + 0x00, + 0x01, + 0x10, + 0xF9, + 0x40, + 0x1F, + 0x82, + 0x84, + 0x00, + 0x01, + 0x10, + 0xF9, + /*02b0*/ 0x40, + 0x1F, + 0x82, + 0x88, + 0x00, + 0x01, + 0x10, + 0xF9, + 0x40, + 0x1F, + 0x82, + 0x8C, + 0x00, + 0x01, + 0x10, + 0xF9, + /*02c0*/ 0x40, + 0x1F, + 0x82, + 0x90, + 0x00, + 0x01, + 0x10, + 0xF9, + 0x40, + 0x1F, + 0x82, + 0x94, + 0x00, + 0x01, + 0x10, + 0xF9, + /*02d0*/ 0x40, + 0x1F, + 0x82, + 0x98, + 0x00, + 0x01, + 0x10, + 0xF9, + 0x40, + 0x1F, + 0x82, + 0x9C, + 0x00, + 0x01, + 0x10, + 0xF9, + /*02e0*/ 0x40, + 0x1F, + 0x82, + 0xA0, + 0x00, + 0x01, + 0x10, + 0xF9, + 0x40, + 0x1F, + 0x82, + 0xA4, + 0x00, + 0x01, + 0x10, + 0xF9, + /*02f0*/ 0x40, + 0x1F, + 0x82, + 0xA8, + 0x00, + 0x01, + 0x10, + 0xF9, + 0x40, + 0x2F, + 0x00, + 0x00, + 0x10, + 0x00, + 0x00, + 0x04, + /*0300*/ 0x40, + 0x2F, + 0x00, + 0x08, + 0x00, + 0x03, + 0x05, + 0x24, + 0x40, + 0x2F, + 0x00, + 0x0C, + 0x06, + 0x03, + 0x05, + 0x24, + /*0310*/ 0x40, + 0x2F, + 0x00, + 0x10, + 0x80, + 0x00, + 0x00, + 0x1B, + 0x40, + 0x2F, + 0x00, + 0x14, + 0x82, + 0x00, + 0x00, + 0x1B, + /*0320*/ 0x40, + 0x2F, + 0x00, + 0x18, + 0x84, + 0x00, + 0x00, + 0x1B, + 0x40, + 0x2F, + 0x00, + 0x1C, + 0x86, + 0x00, + 0x00, + 0x1B, + /*0330*/ 0x40, + 0x2F, + 0x00, + 0x20, + 0x90, + 0x00, + 0x00, + 0x21, + 0x40, + 0x2F, + 0x00, + 0x24, + 0xA0, + 0x00, + 0x00, + 0x19, + /*0340*/ 0x40, + 0x2F, + 0x00, + 0x28, + 0xA8, + 0x00, + 0x00, + 0x17, + 0x40, + 0x2F, + 0x00, + 0x2C, + 0xA9, + 0x00, + 0x00, + 0x1B, + /*0350*/ 0x40, + 0x2F, + 0x00, + 0x30, + 0x00, + 0x00, + 0x00, + 0x21, + 0x40, + 0x2F, + 0x00, + 0x04, + 0x00, + 0x00, + 0x79, + 0xA8, + /*0360*/ 0x40, + 0x2F, + 0x00, + 0x40, + 0x00, + 0x00, + 0x0F, + 0x31, + 0x40, + 0x2F, + 0x00, + 0x44, + 0x00, + 0x65, + 0x29, + 0x22, + /*0370*/ 0x40, + 0x2F, + 0x00, + 0x48, + 0x00, + 0x01, + 0x09, + 0x20, + 0x40, + 0x2F, + 0x00, + 0x4C, + 0x50, + 0x21, + 0x0A, + 0x08, + /*0380*/ 0x40, + 0x2F, + 0x00, + 0x80, + 0x00, + 0x00, + 0x00, + 0x21, + 0x40, + 0x2F, + 0x00, + 0x84, + 0x00, + 0x88, + 0x88, + 0x88, + /*0390*/ 0x40, + 0x2F, + 0x00, + 0x94, + 0x00, + 0x00, + 0x00, + 0x02, + 0x40, + 0x2F, + 0x00, + 0x98, + 0x00, + 0x00, + 0x00, + 0x00, + /*03a0*/ 0x40, + 0x2F, + 0x00, + 0x90, + 0x80, + 0x00, + 0x00, + 0x00, + 0x40, + 0x2F, + 0x00, + 0x9C, + 0xA5, + 0x5A, + 0x00, + 0x0F, + /*03b0*/ 0xCF, + 0x00, + 0x0C, + 0x1C, + 0x40, + 0x2F, + 0x00, + 0x3C, + 0x00, + 0x00, + 0x00, + 0x01, + 0xCC, + 0x00, + 0x14, + 0x04, + /*03c0*/ 0x40, + 0x2F, + 0x00, + 0x90, + 0x80, + 0x00, + 0x00, + 0x00, + 0x40, + 0x2F, + 0x00, + 0x9C, + 0xA5, + 0x5A, + 0x00, + 0x0C, + /*03d0*/ 0xCF, + 0x00, + 0x0C, + 0x1C, + 0x40, + 0x2F, + 0x00, + 0x3C, + 0x00, + 0x00, + 0x00, + 0x01, + 0xCC, + 0x00, + 0x14, + 0x04, + /*03e0*/ 0x40, + 0x2F, + 0x00, + 0x90, + 0x80, + 0x00, + 0x00, + 0x00, + 0x40, + 0x2F, + 0x00, + 0x9C, + 0xA5, + 0x5A, + 0x00, + 0x0C, + /*03f0*/ 0xCF, + 0x00, + 0x0C, + 0x1C, + 0x40, + 0x2F, + 0x00, + 0x3C, + 0x00, + 0x00, + 0x00, + 0x01, + 0xCC, + 0x00, + 0x1C, + 0x04, + /*0400*/ 0x40, + 0x2F, + 0x00, + 0xA0, + 0x00, + 0x00, + 0x00, + 0x33, + 0x40, + 0x2F, + 0x00, + 0x90, + 0x80, + 0x00, + 0x00, + 0x00, + /*0410*/ 0x40, + 0x2F, + 0x00, + 0x9C, + 0xA5, + 0x5A, + 0x00, + 0x0A, + 0xCF, + 0x00, + 0x0C, + 0x1C, + 0x40, + 0x2F, + 0x00, + 0x3C, + /*0420*/ 0x00, + 0x00, + 0x00, + 0x01, + 0xCC, + 0x00, + 0x0C, + 0x04, + 0x40, + 0x2F, + 0x00, + 0x4C, + 0x50, + 0x07, + 0x0A, + 0x09, +}; diff --git a/Ubiquitous/XiZi/board/ok1052-c/xip/fsl_flexspi_nor_boot.h b/Ubiquitous/XiZi/board/ok1052-c/xip/fsl_flexspi_nor_boot.h new file mode 100644 index 000000000..b6677b274 --- /dev/null +++ b/Ubiquitous/XiZi/board/ok1052-c/xip/fsl_flexspi_nor_boot.h @@ -0,0 +1,123 @@ +/* + * Copyright 2017 NXP + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * o Redistributions of source code must retain the above copyright notice, this list + * of conditions and the following disclaimer. + * + * o Redistributions in binary form must reproduce the above copyright notice, this + * list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * o Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** +* @file fsl_flexspi_nor_boot.h +* @brief support to register flexspi image vector table +* @version 2.0 +* @author AIIT XUOS Lab +* @date 2022-03-22 +*/ + +#ifndef _QUADSPI_BOOT_H_ +#define _QUADSPI_BOOT_H_ + +#include + +/************************************* + * IVT Data + *************************************/ +typedef struct _ivt_ { + /** @ref hdr with tag #HAB_TAG_IVT, length and HAB version fields + * (see @ref data) + */ + uint32_t hdr; + /** Absolute address of the first instruction to execute from the + * image + */ + uint32_t entry; + /** Reserved in this version of HAB: should be NULL. */ + uint32_t reserved1; + /** Absolute address of the image DCD: may be NULL. */ + uint32_t dcd; + /** Absolute address of the Boot Data: may be NULL, but not interpreted + * any further by HAB + */ + uint32_t boot_data; + /** Absolute address of the IVT.*/ + uint32_t self; + /** Absolute address of the image CSF.*/ + uint32_t csf; + /** Reserved in this version of HAB: should be zero. */ + uint32_t reserved2; +} ivt; + +#define IVT_MAJOR_VERSION 0x4 +#define IVT_MAJOR_VERSION_SHIFT 0x4 +#define IVT_MAJOR_VERSION_MASK 0xF +#define IVT_MINOR_VERSION 0x1 +#define IVT_MINOR_VERSION_SHIFT 0x0 +#define IVT_MINOR_VERSION_MASK 0xF + +#define IVT_VERSION(major, minor) \ + ((((major) & IVT_MAJOR_VERSION_MASK) << IVT_MAJOR_VERSION_SHIFT) | \ + (((minor) & IVT_MINOR_VERSION_MASK) << IVT_MINOR_VERSION_SHIFT)) + +#define IVT_TAG_HEADER (0xD1) /**< Image Vector Table */ +#define IVT_SIZE 0x2000 +#define IVT_PAR IVT_VERSION(IVT_MAJOR_VERSION, IVT_MINOR_VERSION) + +#define IVT_HEADER (IVT_TAG_HEADER | (IVT_SIZE << 8) | (IVT_PAR << 24)) +#define IVT_RSVD (uint32_t)(0x00000000) + + +/************************************* + * Boot Data + *************************************/ +typedef struct _boot_data_ { + uint32_t start; /* boot start location */ + uint32_t size; /* size */ + uint32_t plugin; /* plugin flag - 1 if downloaded application is plugin */ + uint32_t placeholder; /* placehoder to make even 0x10 size */ +}BOOT_DATA_T; + + +/************************************* + * DCD Data + *************************************/ +#define DCD_TAG_HEADER (0xD2) +#define DCD_TAG_HEADER_SHIFT (24) +#define DCD_VERSION (0x40) +#define DCD_ARRAY_SIZE 1 + +#define FLASH_BASE 0x60000000 +#define FLASH_END 0x7F7FFFFF +#define SCLK 1 + +#define DCD_ADDRESS dcd_sdram +#define BOOT_DATA_ADDRESS &boot_data +#define CSF_ADDRESS 0 +#define PLUGIN_FLAG (uint32_t)0 + +/* External Variables */ +//extern const uint8_t dcd_sdram[1044]; +extern const uint8_t dcd_sdram[1072]; +extern const BOOT_DATA_T boot_data; + +#endif diff --git a/Ubiquitous/XiZi/board/ok1052-c/xip/fsl_flexspi_nor_flash.c b/Ubiquitous/XiZi/board/ok1052-c/xip/fsl_flexspi_nor_flash.c new file mode 100644 index 000000000..c5cc52dbc --- /dev/null +++ b/Ubiquitous/XiZi/board/ok1052-c/xip/fsl_flexspi_nor_flash.c @@ -0,0 +1,88 @@ +/* + * Copyright 2017 NXP + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * o Redistributions of source code must retain the above copyright notice, this list + * of conditions and the following disclaimer. + * + * o Redistributions in binary form must reproduce the above copyright notice, this + * list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * o Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** +* @file fsl_flexspi_nor_flash.c +* @brief support to define flexspi flash config +* @version 2.0 +* @author AIIT XUOS Lab +* @date 2022-03-22 +*/ + +#include "fsl_flexspi_nor_flash.h" + +/******************************************************************************* + * Code + ******************************************************************************/ +#if defined(__CC_ARM) || defined(__GNUC__) + __attribute__((section(".boot_hdr.conf"))) +#elif defined(__ICCARM__) +#pragma location=".boot_hdr.conf" +#endif + +const flexspi_nor_config_t Qspiflash_config = +{ + .memConfig = + { + .tag = FLEXSPI_CFG_BLK_TAG, + .version = FLEXSPI_CFG_BLK_VERSION, + .readSampleClkSrc = kFlexSPIReadSampleClk_LoopbackInternally, + .csHoldTime = 3u, + .csSetupTime = 3u, + .deviceModeCfgEnable = true, + .deviceModeType = 1,//Quad Enable command + .deviceModeSeq.seqNum = 1, + .deviceModeSeq.seqId = 4, + .deviceModeArg = 0x000200,//Set QE + .deviceType = kFlexSpiDeviceType_SerialNOR, + .sflashPadType = kSerialFlash_4Pads, + .serialClkFreq = kFlexSpiSerialClk_60MHz,//80MHz for Winbond, 100MHz for GD, 133MHz for ISSI + .sflashA1Size = 16u * 1024u * 1024u,//4MBytes + .dataValidTime = {16u, 16u}, + .lookupTable = + { +// //Fast Read Sequence +// [0] = FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, 0x0B, RADDR_SDR, FLEXSPI_1PAD, 0x18), +// [1] = FLEXSPI_LUT_SEQ(DUMMY_SDR, FLEXSPI_1PAD, 0x08, READ_SDR, FLEXSPI_1PAD, 0x08), +// [2] = FLEXSPI_LUT_SEQ(JMP_ON_CS, 0, 0, 0, 0, 0), + //Quad Input/output read sequence + [0] = FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, 0xEB, RADDR_SDR, FLEXSPI_4PAD, 0x18), + [1] = FLEXSPI_LUT_SEQ(DUMMY_SDR, FLEXSPI_4PAD, 0x06, READ_SDR, FLEXSPI_4PAD, 0x04), + [2] = FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), + //Read Status + [1*4] = FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, 0x05, READ_SDR, FLEXSPI_1PAD, 0x04), + //Write Enable + [3*4] = FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, 0x06, STOP, 0, 0), + //Write status + [4*4] = FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, 0x01, WRITE_SDR, FLEXSPI_1PAD, 0x2), + }, + }, + .pageSize = 256u, + .sectorSize = 4u * 1024u, +}; diff --git a/Ubiquitous/XiZi/board/ok1052-c/xip/fsl_flexspi_nor_flash.h b/Ubiquitous/XiZi/board/ok1052-c/xip/fsl_flexspi_nor_flash.h new file mode 100644 index 000000000..73d4547b7 --- /dev/null +++ b/Ubiquitous/XiZi/board/ok1052-c/xip/fsl_flexspi_nor_flash.h @@ -0,0 +1,303 @@ +/* + * Copyright (c) 2016, Freescale Semiconductor, Inc. + * Copyright 2016-2017 NXP + * + * Redistribution and use in source and binary forms, with or without + * modification, + * are permitted provided that the following conditions are met: + * + * o Redistributions of source code must retain the above copyright notice, this + * list + * of conditions and the following disclaimer. + * + * o Redistributions in binary form must reproduce the above copyright notice, + * this + * list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * o Neither the name of Freescale Semiconductor, Inc. nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** +* @file fsl_flexspi_nor_flash.h +* @brief support to define flexspi flash config +* @version 2.0 +* @author AIIT XUOS Lab +* @date 2022-03-22 +*/ + +#ifndef __FLEXSPI_NOR_FLASH_H__ +#define __FLEXSPI_NOR_FLASH_H__ + +#include +#include +#include "fsl_common.h" + +/* FLEXSPI memory config block related defintions */ +#define FLEXSPI_CFG_BLK_TAG (0x42464346UL) // ascii "FCFB" Big Endian +#define FLEXSPI_CFG_BLK_VERSION (0x56010400UL) // V1.4.0 +#define FLEXSPI_CFG_BLK_SIZE (512) + +/* FLEXSPI Feature related definitions */ +#define FLEXSPI_FEATURE_HAS_PARALLEL_MODE 1 + +/* Lookup table related defintions */ +#define CMD_INDEX_READ 0 +#define CMD_INDEX_READSTATUS 1 +#define CMD_INDEX_WRITEENABLE 2 +#define CMD_INDEX_WRITE 4 + +#define CMD_LUT_SEQ_IDX_READ 0 +#define CMD_LUT_SEQ_IDX_READSTATUS 1 +#define CMD_LUT_SEQ_IDX_WRITEENABLE 3 +#define CMD_LUT_SEQ_IDX_WRITE 9 + +#define CMD_SDR 0x01 +#define CMD_DDR 0x21 +#define RADDR_SDR 0x02 +#define RADDR_DDR 0x22 +#define CADDR_SDR 0x03 +#define CADDR_DDR 0x23 +#define MODE1_SDR 0x04 +#define MODE1_DDR 0x24 +#define MODE2_SDR 0x05 +#define MODE2_DDR 0x25 +#define MODE4_SDR 0x06 +#define MODE4_DDR 0x26 +#define MODE8_SDR 0x07 +#define MODE8_DDR 0x27 +#define WRITE_SDR 0x08 +#define WRITE_DDR 0x28 +#define READ_SDR 0x09 +#define READ_DDR 0x29 +#define LEARN_SDR 0x0A +#define LEARN_DDR 0x2A +#define DATSZ_SDR 0x0B +#define DATSZ_DDR 0x2B +#define DUMMY_SDR 0x0C +#define DUMMY_DDR 0x2C +#define DUMMY_RWDS_SDR 0x0D +#define DUMMY_RWDS_DDR 0x2D +#define JMP_ON_CS 0x1F +#define STOP 0 + +#define FLEXSPI_1PAD 0 +#define FLEXSPI_2PAD 1 +#define FLEXSPI_4PAD 2 +#define FLEXSPI_8PAD 3 + +#define FLEXSPI_LUT_SEQ(cmd0, pad0, op0, cmd1, pad1, op1) \ + (FLEXSPI_LUT_OPERAND0(op0) | FLEXSPI_LUT_NUM_PADS0(pad0) | FLEXSPI_LUT_OPCODE0(cmd0) | FLEXSPI_LUT_OPERAND1(op1) | \ + FLEXSPI_LUT_NUM_PADS1(pad1) | FLEXSPI_LUT_OPCODE1(cmd1)) + +//!@brief Definitions for FlexSPI Serial Clock Frequency +typedef enum _FlexSpiSerialClockFreq +{ + kFlexSpiSerialClk_30MHz = 1, + kFlexSpiSerialClk_50MHz = 2, + kFlexSpiSerialClk_60MHz = 3, + kFlexSpiSerialClk_75MHz = 4, + kFlexSpiSerialClk_80MHz = 5, + kFlexSpiSerialClk_100MHz = 6, + kFlexSpiSerialClk_133MHz = 7, + kFlexSpiSerialClk_166MHz = 8, + kFlexSpiSerialClk_200MHz = 9, +} flexspi_serial_clk_freq_t; + +//!@brief FlexSPI clock configuration type +enum +{ + kFlexSpiClk_SDR, //!< Clock configure for SDR mode + kFlexSpiClk_DDR, //!< Clock configurat for DDR mode +}; + +//!@brief FlexSPI Read Sample Clock Source definition +typedef enum _FlashReadSampleClkSource +{ + kFlexSPIReadSampleClk_LoopbackInternally = 0, + kFlexSPIReadSampleClk_LoopbackFromDqsPad = 1, + kFlexSPIReadSampleClk_LoopbackFromSckPad = 2, + kFlexSPIReadSampleClk_ExternalInputFromDqsPad = 3, +} flexspi_read_sample_clk_t; + + +//!@brief Misc feature bit definitions +enum +{ + kFlexSpiMiscOffset_DiffClkEnable = 0, //!< Bit for Differential clock enable + kFlexSpiMiscOffset_Ck2Enable = 1, //!< Bit for CK2 enable + kFlexSpiMiscOffset_ParallelEnable = 2, //!< Bit for Parallel mode enable + kFlexSpiMiscOffset_WordAddressableEnable = 3, //!< Bit for Word Addressable enable + kFlexSpiMiscOffset_SafeConfigFreqEnable = 4, //!< Bit for Safe Configuration Frequency enable + kFlexSpiMiscOffset_PadSettingOverrideEnable = 5, //!< Bit for Pad setting override enable + kFlexSpiMiscOffset_DdrModeEnable = 6, //!< Bit for DDR clock confiuration indication. +}; + +//!@brief Flash Type Definition +enum +{ + kFlexSpiDeviceType_SerialNOR = 1, //!< Flash devices are Serial NOR + kFlexSpiDeviceType_SerialNAND = 2, //!< Flash devices are Serial NAND + kFlexSpiDeviceType_SerialRAM = 3, //!< Flash devices are Serial RAM/HyperFLASH + kFlexSpiDeviceType_MCP_NOR_NAND = 0x12, //!< Flash device is MCP device, A1 is Serial NOR, A2 is Serial NAND + kFlexSpiDeviceType_MCP_NOR_RAM = 0x13, //!< Flash deivce is MCP device, A1 is Serial NOR, A2 is Serial RAMs +}; + +//!@brief Flash Pad Definitions +enum +{ + kSerialFlash_1Pad = 1, + kSerialFlash_2Pads = 2, + kSerialFlash_4Pads = 4, + kSerialFlash_8Pads = 8, +}; + +//!@brief FlexSPI LUT Sequence structure +typedef struct _lut_sequence +{ + uint8_t seqNum; //!< Sequence Number, valid number: 1-16 + uint8_t seqId; //!< Sequence Index, valid number: 0-15 + uint16_t reserved; +} flexspi_lut_seq_t; + +//!@brief Flash Configuration Command Type +enum +{ + kDeviceConfigCmdType_Generic, //!< Generic command, for example: configure dummy cycles, drive strength, etc + kDeviceConfigCmdType_QuadEnable, //!< Quad Enable command + kDeviceConfigCmdType_Spi2Xpi, //!< Switch from SPI to DPI/QPI/OPI mode + kDeviceConfigCmdType_Xpi2Spi, //!< Switch from DPI/QPI/OPI to SPI mode + kDeviceConfigCmdType_Spi2NoCmd, //!< Switch to 0-4-4/0-8-8 mode + kDeviceConfigCmdType_Reset, //!< Reset device command +}; + +//!@brief FlexSPI Memory Configuration Block +typedef struct _FlexSPIConfig +{ + uint32_t tag; //!< [0x000-0x003] Tag, fixed value 0x42464346UL + uint32_t version; //!< [0x004-0x007] Version,[31:24] -'V', [23:16] - Major, [15:8] - Minor, [7:0] - bugfix + uint32_t reserved0; //!< [0x008-0x00b] Reserved for future use + uint8_t readSampleClkSrc; //!< [0x00c-0x00c] Read Sample Clock Source, valid value: 0/1/3 + uint8_t csHoldTime; //!< [0x00d-0x00d] CS hold time, default value: 3 + uint8_t csSetupTime; //!< [0x00e-0x00e] CS setup time, default value: 3 + uint8_t columnAddressWidth; //!< [0x00f-0x00f] Column Address with, for HyperBus protocol, it is fixed to 3, For + //! Serial NAND, need to refer to datasheet + uint8_t deviceModeCfgEnable; //!< [0x010-0x010] Device Mode Configure enable flag, 1 - Enable, 0 - Disable + uint8_t deviceModeType; //!< [0x011-0x011] Specify the configuration command type:Quad Enable, DPI/QPI/OPI switch, + //! Generic configuration, etc. + uint16_t waitTimeCfgCommands; //!< [0x012-0x013] Wait time for all configuration commands, unit: 100us, Used for + //! DPI/QPI/OPI switch or reset command + flexspi_lut_seq_t deviceModeSeq; //!< [0x014-0x017] Device mode sequence info, [7:0] - LUT sequence id, [15:8] - LUt + //! sequence number, [31:16] Reserved + uint32_t deviceModeArg; //!< [0x018-0x01b] Argument/Parameter for device configuration + uint8_t configCmdEnable; //!< [0x01c-0x01c] Configure command Enable Flag, 1 - Enable, 0 - Disable + uint8_t configModeType[3]; //!< [0x01d-0x01f] Configure Mode Type, similar as deviceModeTpe + flexspi_lut_seq_t + configCmdSeqs[3]; //!< [0x020-0x02b] Sequence info for Device Configuration command, similar as deviceModeSeq + uint32_t reserved1; //!< [0x02c-0x02f] Reserved for future use + uint32_t configCmdArgs[3]; //!< [0x030-0x03b] Arguments/Parameters for device Configuration commands + uint32_t reserved2; //!< [0x03c-0x03f] Reserved for future use + uint32_t controllerMiscOption; //!< [0x040-0x043] Controller Misc Options, see Misc feature bit definitions for more + //! details + uint8_t deviceType; //!< [0x044-0x044] Device Type: See Flash Type Definition for more details + uint8_t sflashPadType; //!< [0x045-0x045] Serial Flash Pad Type: 1 - Single, 2 - Dual, 4 - Quad, 8 - Octal + uint8_t serialClkFreq; //!< [0x046-0x046] Serial Flash Frequencey, device specific definitions, See System Boot + //! Chapter for more details + uint8_t lutCustomSeqEnable; //!< [0x047-0x047] LUT customization Enable, it is required if the program/erase cannot + //! be done using 1 LUT sequence, currently, only applicable to HyperFLASH + uint32_t reserved3[2]; //!< [0x048-0x04f] Reserved for future use + uint32_t sflashA1Size; //!< [0x050-0x053] Size of Flash connected to A1 + uint32_t sflashA2Size; //!< [0x054-0x057] Size of Flash connected to A2 + uint32_t sflashB1Size; //!< [0x058-0x05b] Size of Flash connected to B1 + uint32_t sflashB2Size; //!< [0x05c-0x05f] Size of Flash connected to B2 + uint32_t csPadSettingOverride; //!< [0x060-0x063] CS pad setting override value + uint32_t sclkPadSettingOverride; //!< [0x064-0x067] SCK pad setting override value + uint32_t dataPadSettingOverride; //!< [0x068-0x06b] data pad setting override value + uint32_t dqsPadSettingOverride; //!< [0x06c-0x06f] DQS pad setting override value + uint32_t timeoutInMs; //!< [0x070-0x073] Timeout threshold for read status command + uint32_t commandInterval; //!< [0x074-0x077] CS deselect interval between two commands + uint16_t dataValidTime[2]; //!< [0x078-0x07b] CLK edge to data valid time for PORT A and PORT B, in terms of 0.1ns + uint16_t busyOffset; //!< [0x07c-0x07d] Busy offset, valid value: 0-31 + uint16_t busyBitPolarity; //!< [0x07e-0x07f] Busy flag polarity, 0 - busy flag is 1 when flash device is busy, 1 - + //! busy flag is 0 when flash device is busy + uint32_t lookupTable[64]; //!< [0x080-0x17f] Lookup table holds Flash command sequences + flexspi_lut_seq_t lutCustomSeq[12]; //!< [0x180-0x1af] Customizable LUT Sequences + uint32_t reserved4[4]; //!< [0x1b0-0x1bf] Reserved for future use +} flexspi_mem_config_t; + +/* */ +#define NOR_CMD_INDEX_READ CMD_INDEX_READ //!< 0 +#define NOR_CMD_INDEX_READSTATUS CMD_INDEX_READSTATUS //!< 1 +#define NOR_CMD_INDEX_WRITEENABLE CMD_INDEX_WRITEENABLE //!< 2 +#define NOR_CMD_INDEX_ERASESECTOR 3 //!< 3 +#define NOR_CMD_INDEX_PAGEPROGRAM CMD_INDEX_WRITE //!< 4 +#define NOR_CMD_INDEX_CHIPERASE 5 //!< 5 +#define NOR_CMD_INDEX_DUMMY 6 //!< 6 +#define NOR_CMD_INDEX_ERASEBLOCK 7 //!< 7 + +#define NOR_CMD_LUT_SEQ_IDX_READ CMD_LUT_SEQ_IDX_READ //!< 0 READ LUT sequence id in lookupTable stored in config block +#define NOR_CMD_LUT_SEQ_IDX_READSTATUS \ + CMD_LUT_SEQ_IDX_READSTATUS //!< 1 Read Status LUT sequence id in lookupTable stored in config block +#define NOR_CMD_LUT_SEQ_IDX_READSTATUS_XPI \ + 2 //!< 2 Read status DPI/QPI/OPI sequence id in lookupTable stored in config block +#define NOR_CMD_LUT_SEQ_IDX_WRITEENABLE \ + CMD_LUT_SEQ_IDX_WRITEENABLE //!< 3 Write Enable sequence id in lookupTable stored in config block +#define NOR_CMD_LUT_SEQ_IDX_WRITEENABLE_XPI \ + 4 //!< 4 Write Enable DPI/QPI/OPI sequence id in lookupTable stored in config block +#define NOR_CMD_LUT_SEQ_IDX_ERASESECTOR 5 //!< 5 Erase Sector sequence id in lookupTable stored in config block +#define NOR_CMD_LUT_SEQ_IDX_ERASEBLOCK 8 //!< 8 Erase Block sequence id in lookupTable stored in config block +#define NOR_CMD_LUT_SEQ_IDX_PAGEPROGRAM \ + CMD_LUT_SEQ_IDX_WRITE //!< 9 Program sequence id in lookupTable stored in config block +#define NOR_CMD_LUT_SEQ_IDX_CHIPERASE 11 //!< 11 Chip Erase sequence in lookupTable id stored in config block +#define NOR_CMD_LUT_SEQ_IDX_READ_SFDP 13 //!< 13 Read SFDP sequence in lookupTable id stored in config block +#define NOR_CMD_LUT_SEQ_IDX_RESTORE_NOCMD \ + 14 //!< 14 Restore 0-4-4/0-8-8 mode sequence id in lookupTable stored in config block +#define NOR_CMD_LUT_SEQ_IDX_EXIT_NOCMD \ + 15 //!< 15 Exit 0-4-4/0-8-8 mode sequence id in lookupTable stored in config blobk + + +/* + * Serial NOR configuration block + */ +typedef struct _flexspi_nor_config +{ + flexspi_mem_config_t memConfig; //!< Common memory configuration info via FlexSPI + uint32_t pageSize; //!< Page size of Serial NOR + uint32_t sectorSize; //!< Sector size of Serial NOR + uint8_t ipcmdSerialClkFreq; //!< Clock frequency for IP command + uint8_t isUniformBlockSize; //!< Sector/Block size is the same + uint8_t reserved0[2]; //!< Reserved for future use + uint8_t serialNorType; //!< Serial NOR Flash type: 0/1/2/3 + uint8_t needExitNoCmdMode; //!< Need to exit NoCmd mode before other IP command + uint8_t halfClkForNonReadCmd; //!< Half the Serial Clock for non-read command: true/false + uint8_t needRestoreNoCmdMode; //!< Need to Restore NoCmd mode after IP commmand execution + uint32_t blockSize; //!< Block size + uint32_t reserve2[11]; //!< Reserved for future use +} flexspi_nor_config_t; + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +} +#endif + +#endif // __FLEXSPI_NOR_FLASH_H__ diff --git a/Ubiquitous/XiZi/board/xidatong/Makefile b/Ubiquitous/XiZi/board/xidatong/Makefile index 837118429..c50f207a4 100644 --- a/Ubiquitous/XiZi/board/xidatong/Makefile +++ b/Ubiquitous/XiZi/board/xidatong/Makefile @@ -1,4 +1,4 @@ -SRC_DIR := third_party_driver +SRC_DIR := third_party_driver xip SRC_FILES := board.c diff --git a/Ubiquitous/XiZi/board/xidatong/README.md b/Ubiquitous/XiZi/board/xidatong/README.md index 3b8e2817b..5eddfd9a1 100644 --- a/Ubiquitous/XiZi/board/xidatong/README.md +++ b/Ubiquitous/XiZi/board/xidatong/README.md @@ -156,8 +156,17 @@ make BOARD=xidatong ## 3. 烧写及运行 ### 3.1 烧写 +1、烧写工具:NXP MCU Boot Utility,可参考[https://github.com/JayHeng/NXP-MCUBootUtility](https://github.com/JayHeng/NXP-MCUBootUtility) +2、xidatong开发板支持UART串口烧写程序,打开NXP MCU Boot Utility后,选择好芯片类型为i.MXRT105x,开发板上电,使用串口转USB线将开发板和PC连接,拨码开关设置为1 on 2 on 3 off 4 off,重新上电,选择对应的COM口和波特率(需关闭串口终端连接,确保该COM口空闲,否则会导致Utility工具连接失败),连接成功后,点击reconnect,等待NXP MCU Boot Utility中红色显示变成蓝色显示,则表示已正确识别并连接到了开发板。如下图所示: +![NXPBootUtility_1](./img/NXPBootUtility_1.png) +3、同时需要匹配xidatong开发板所使用的Flash型号,点击Boot Device Configuration,在Use Typical Device中选择Winbond_W25QxxxJV,然后点击ok。如下图所示: +![flashconfig](./img/flashconfig.png) + +4、选择编译生成的XiZi_xidatong.elf或bin文件路径,按照图示步骤,将文件烧写至Flash中(link.lds中已构造Flash Bootable image,如有修改Flash相关配置需求,可修改/xip目录内相关文件,无需NXPBootUtility再次构造),若烧写无误,则下列绿色进度条会执行到底。如下图所示: +![NXPBootUtility_2](./img/NXPBootUtility_2.png) ### 3.2 运行结果 - +按照3.1烧写步骤执行后,将拨码开关设置为1 off 2 off 3 off 4 off,重新上电后,重新打开该COM口串口终端,若程序正常,则串口终端上会显示启动信息打印输出。如下图所示: +![terminal](./img/terminal.png) diff --git a/Ubiquitous/XiZi/board/xidatong/board.c b/Ubiquitous/XiZi/board/xidatong/board.c index 6844ef96f..1d8af975a 100644 --- a/Ubiquitous/XiZi/board/xidatong/board.c +++ b/Ubiquitous/XiZi/board/xidatong/board.c @@ -300,22 +300,18 @@ void InitBoardHardware() BOARD_InitUartPins(); #endif -#ifdef BSP_USING_CH438 - BOARD_InitCh438Pins(); -#endif - InitBoardMemory((void *)HEAP_BEGIN, (void *)HEAP_END); #ifdef BSP_USING_LPUART Imxrt1052HwUartInit(); #endif + InstallConsole(KERNEL_CONSOLE_BUS_NAME, KERNEL_CONSOLE_DRV_NAME, KERNEL_CONSOLE_DEVICE_NAME); + #ifdef BSP_USING_CH438 Imxrt1052HwCh438Init(); #endif - InstallConsole(KERNEL_CONSOLE_BUS_NAME, KERNEL_CONSOLE_DRV_NAME, KERNEL_CONSOLE_DEVICE_NAME); - #ifdef BSP_USING_SDIO Imxrt1052HwSdioInit(); #endif diff --git a/Ubiquitous/XiZi/board/xidatong/img/NXPBootUtility_1.png b/Ubiquitous/XiZi/board/xidatong/img/NXPBootUtility_1.png index 734f0382593097fec9418dfbba22fcc8c252018a..939f60f23024d825b0d0b78c78bbf13864fd5723 100644 GIT binary patch literal 64966 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 literal 61992 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 diff --git a/Ubiquitous/XiZi/board/xidatong/img/NXPBootUtility_2.png b/Ubiquitous/XiZi/board/xidatong/img/NXPBootUtility_2.png index 671bcb0c2e8d6b50ae43d39de568f65dc9b1adc0..df8610a7a328484c0acce958bbc9f259f3933ee8 100644 GIT binary patch literal 52062 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 diff --git a/Ubiquitous/XiZi/board/xidatong/img/flashconfig.png b/Ubiquitous/XiZi/board/xidatong/img/flashconfig.png new file mode 100644 index 0000000000000000000000000000000000000000..9db4bd31233b8b25f7ca59aebba0e16e6e86d5c4 GIT binary patch literal 72092 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