The following content was compiled again on stm32 platform around September 2020, but it was not released. The code involved in this article may have changed. Please refer to the official code warehouse
Prepare FreeRTOS+LwIP
The previous compilation of single files said that open62541 is built on the system, so before transplantation, you need to prepare a well running STM32 FressRTO+Lwip code. If not, you can use GitHub STM32F4-FreeRTOS-LwIP Download the code. See readme.md It is said in. The IDE for compiling stm32 code. According to my personal preference, I use keil 5.
When FreeRTOS+LwIP is OK, you can start the next step.
Compilation conditions
Add macro definition
- Add architecture macro - UA in FreeRTOS+LWIP project_ ARCHITECTURE_ FREERTOSLWIP
- Add LwIP macro
// In ` lwipopts H ` add the following to the document #define LWIP_COMPAT_SOCKETS 0 // Don't do name define-transformation in networking function names. #define LWIP_SOCKET 1 // Enable Socket API (normally already set) #define LWIP_DNS 1 // enable the lwip_getaddrinfo function, struct addrinfo and more. #define SO_REUSE 1 // Allows to set the socket as reusable #define LWIP_TIMEVAL_PRIVATE 0 // This is optional. Set this flag if you get a compilation error about redefinition of struct timeval
- Add FreeRTOS macro
// In ` freertosconfig H ` add the following to the document #define configCHECK_FOR_STACK_OVERFLOW 1 #define configUSE_MALLOC_FAILED_HOOK 1
notes
Comment out sockets H lines 315 ~ 319, 324, etc. of the file
Before comments:
After comments:
This is because this structure is used in open62541, but the original LwIP is useless, so it needs to be released. Of course, you can also directly release LwIP_ TIMEVAL_ The value of private is changed to 1, but I don't know why I didn't do that at that time @ (black line)
Add source file
The preparatory work is completed. Now put [zero] compile separate open62541 source files and header files supporting STM32 platform based on open62541 project Open62541.0 generated in C and open62541 H add to project engineering
Adding source files and header files should not require me to say more @ (you know)
Modify FreeRTOS source code
Because UA was checked during previous compilation_ ARCH_ FREERTOS_ USE_ OWN_ Memory is to use FreeRTOS's own memory management function
However, among the memory management functions provided by FreeRTOS, there are only pvPortMalloc and vPortFree. There are no pvPortCalloc and pvPortRealloc functions, so we need to implement these two functions ourselves. It is impossible. Foreign netizens have implemented them. Then I used them and found no problems, so I posted them directly below
void *pvPortCalloc( size_t nmemb, size_t size ) { void *pvReturn; vTaskSuspendAll(); { pvReturn = pvPortMalloc( nmemb*size ); if(pvReturn) memset(pvReturn,0,nmemb*size); } xTaskResumeAll(); return pvReturn; } void *pvPortRealloc( void *pv, size_t xWantedSize ) { uint8_t *puc = ( uint8_t * ) pv; BlockLink_t *pxLink; int datasize; void *pvReturn = NULL; if (xWantedSize == 0) { vPortFree(pv); return NULL; } if (pv == NULL) return pvPortMalloc(xWantedSize); /* The memory being freed will have an BlockLink_t structure immediately before it. */ puc -= xHeapStructSize; pxLink = ( BlockLink_t * ) puc; datasize = (pxLink->xBlockSize & ~xBlockAllocatedBit) - xHeapStructSize; if (datasize >= xWantedSize) // have enough memory don't need realloc return pv; pvReturn = pvPortMalloc(xWantedSize); if (pvReturn == NULL) // malloc fail, return NULL, don't free pv. return NULL; memcpy(pvReturn, pv, xWantedSize); vPortFree(pv);// realloc success, copy and free pv. return pvReturn; }
Then in portable Add the following code to H
void *pvPortCalloc( size_t nmemb, size_t size ); void *pvPortRealloc( void *pv, size_t xWantedSize );
Modify open62541
- In open62541 Redefine int as ssize in H_ t.
- Statement OPEN62541_FEERTOS_USE_OWN_MEM, enable FreeRTOS's own memory management function.
- In open62541 C, AF will be involved_ The contents of inet6 are masked by macro definitions
- If sockets If the H file does not contain the following, add
#if !defined(sa_family_t) && !defined(SA_FAMILY_T_DEFINED) typedef u8_t sa_family_t; #endif struct sockaddr_storage { u8_t s2_len; sa_family_t ss_family; char s2_data1[2]; u32_t s2_data2[3]; #if LWIP_IPV6 u32_t s2_data3[3]; #endif /* LWIP_IPV6 */ };
OPCUA server
Write the following code to establish the server
void opcua_task(void *pvParameter) { //The default 64KB of memory for sending and receicing buffer caused problems to many users. With the code below, they are reduced to ~16KB UA_UInt32 sendBufferSize = 16000; //64 KB was too much for my platform UA_UInt32 recvBufferSize = 16000; //64 KB was too much for my platform UA_UInt16 portNumber = 4840; UA_Server* mUaServer = UA_Server_new(); UA_ServerConfig *uaServerConfig = UA_Server_getConfig(mUaServer); UA_ServerConfig_setMinimalCustomBuffer(uaServerConfig, portNumber, 0, sendBufferSize, recvBufferSize); //VERY IMPORTANT: Set the hostname with your IP before starting the server UA_ServerConfig_setCustomHostname(uaServerConfig, UA_STRING("192.168.0.25")); //The rest is the same as the example UA_Boolean running = true; // // add a variable node to the adresspace // UA_VariableAttributes attr = UA_VariableAttributes_default; // UA_Int32 myInteger = 42; // UA_Variant_setScalarCopy(&attr.value, &myInteger, &UA_TYPES[UA_TYPES_INT32]); // attr.description = UA_LOCALIZEDTEXT_ALLOC("en-US","the answer"); // attr.displayName = UA_LOCALIZEDTEXT_ALLOC("en-US","the answer"); // UA_NodeId myIntegerNodeId = UA_NODEID_STRING_ALLOC(1, "the.answer"); // UA_QualifiedName myIntegerName = UA_QUALIFIEDNAME_ALLOC(1, "the answer"); // UA_NodeId parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER); // UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES); // UA_Server_addVariableNode(mUaServer, myIntegerNodeId, parentNodeId, // parentReferenceNodeId, myIntegerName, // UA_NODEID_NULL, attr, NULL, NULL); // /* allocations on the heap need to be freed */ // UA_VariableAttributes_clear(&attr); // UA_NodeId_clear(&myIntegerNodeId); // UA_QualifiedName_clear(&myIntegerName); UA_StatusCode retval = UA_Server_run(mUaServer, &running); UA_Server_delete(mUaServer); }
compile
If the above steps are OK, click this button at this timeAfter that, the compilation will be successful without errors
connect
Use UaExport to connect to the OPCUA server. After the connection is successful, it is shown in the figure
So far, porting open62541 to STM32 has been completed @ (too happy)