This is the multi-page printable view of this section. Click here to print...

Return to the regular view of this page

As of 2025-09-10

TWENETmcu - Microcontroller Library

Library supporting the basic operation of the microcontroller
Contains source code for basic microcontroller operations.

TWENETmcu - Microcontroller Library

Contains source code for basic microcontroller operations. Much of the code is copied from the JN5189 SDK and partially adapted for TWENET. It also includes the startup functions main() and WarmMain() that run TWENET.

  • board - Hardware initialization (clock, GPIO pins, etc.)
  • component - Libraries providing higher-level functionality (from SDK_X.Y.Z/components, middleware, etc.)
  • framework - Higher-level libraries (from SDK_X.Y.Z/middleware/wireless/framework)
  • device, startup - HAL layer for JN5189
  • drivers - FSL libraries and others for peripheral operations
  • libs - Library files (libXXX.a)
  • main - main() and debug output functions like _putchar()
  • printf - printf() library (does not use NewLib)
  • uMac - Contains MMAC.h
  • utilities - assert() and debug functions like PRINTF()

Technical Documentation

This section describes implementation details of TWENET and considerations for developing application programs.

1 - About Microcontrollers

About microcontrollers
Notes on the microcontroller of TWELITE GOLD.

About Microcontrollers

The microcontroller used in TWELITE GOLD is an ARM CortexM4 core with IEEE802.15.4. TWELITE BLUE/RED use an OpenRISC core, which is a completely different microcontroller, and there are several points to note.

  • While we have described content that requires special attention in terms of specifications, deeper information may be needed depending on the system and hardware requirements you are creating. For more details, please refer to the NXP JN5189 datasheet.

About the descriptions in this document

  • This document mainly describes the differences from the usage with TWELITE BLUE/RED, but please refer to the JN5189 datasheet as the primary source.
  • When expressing pin numbers, PIO indicates the original number of the microcontroller, while DIO/DO indicates the pin names used in TWELITE BLUE/RED. ADC1 and ADC2 are used in the same way depending on the context.
  • To facilitate the porting of applications, we provide the “AHI subset” library. This library is intended for porting some of the applications we have implemented. Therefore, the behavior may differ from the API on TWELITE BLUE/RED.

Microcontroller Differences

  • TWELITE BLUE/RED are big-endian, but TWELITE GOLD is little-endian.
  • The ARM CortexM4 is an architecture with flexible clock configuration. As a general rule, the microcontroller’s operating clocks are 12MHz/32MHz/48MHz, and IO (such as PWM) operates at 32MHz. The main clocks for TWELITE BLUE/RED are 16MHz/32MHz, and the IO is 16MHz.
  • There are restrictions on using GPIO interrupts per pin. You can use GINT (Group Interrupts) to generate an interrupt when each pin changes.
    • This is implemented in the AHI subset. Since it is intended for porting our application code, it is not a comprehensive implementation, and some specifications and behaviors may differ.
    • In the GINT implementation, interrupts are always generated on both edges.
      • For example, if you have a pin that you want to set to a falling edge and its initial state is LOW. If the pin state transitions from LOW -> HIGH -> LOW, an interrupt will be generated for both LOW -> HIGH and HIGH -> LOW.
      • While the microcontroller is running, the AHI subset suppresses the former, but when used for waking from sleep, software cannot suppress it, and it will wake up at the timing of the LOW -> HIGH transition.
      • Conversely, both edges can be detected, which was not possible with TWELITE BLUE/RED, and this is also reflected in the AHI subset (vAHI_DioInterruptEdge(), vAHI_DioWakeEdge()).
    • There is a PINT function as an interrupt function for each pin. The maximum number of pins that can be used with PINT is fixed.
      • This function is not used in the AHI subset and there are no special procedures for its combined use.

Microcontroller Functions

  • It has a built-in RTC. While TWELITE BLUE/RED could improve timer accuracy by connecting an external oscillator, TWELITE GOLD has a built-in 32kHz oscillator and an RTC function.
  • A circuit for floating-point arithmetic is not included. Calculations are performed by a software library, as before. However, the Cortex-M4 has a DSP function that uses fixed-point arithmetic, so using this is also an option.

Module Differences

  • TWELITE RED/BLUE had a total of 24 pins, including 20 DIO pins, 2 dedicated SPI pins, and 2 dedicated analog pins. In contrast, TWELITE GOLD has 22 pins: 16 dedicated digital pins and 6 shared digital-analog pins. This means that two pins are shared on TWELITE GOLD.
    • PIO0 is shared with DO0 (SPICLK) and DIO11.
    • PIO14 is shared with ADC2 (Vref) and DIO8.
      • In the AHI subset, you must set DIO8 to a digital port before using it.
  • While the DIO pins on TWELITE BLUE/RED were in a high-impedance, pull-up state at reset, the state of each pin on TWELITE GOLD is different. In the AHI subset, DIO0..19 and DO0,1 are set as input and pull-up at initialization, while ADC1,2 are set as analog inputs.

Pins that require careful handling

For details, refer to the JN518x datasheet.

  • PIO=5 (ISP_ENTRY): Similar to TWELITE BLUE/RED, when this pin is in a LOW state at reset, it enters a mode for firmware writing, etc.
  • PIO=4 (ISE_SEL): When ISP_ENTRY is enabled, the subsequent behavior is changed. If it is not set to a HIGH level during firmware writing, firmware cannot be written.
  • PIO=10, 11: These are mainly intended for I2C use, so they differ from other pins that can be used for GPIO. Specifically, there is no pull-up/down setting.

About Peripherals

Please refer to the documentation for the TWENETmwf (which organizes major functions through peripheral procedures) and TWENETcmpt libraries (intended for porting code using AHI).

2 - Startup Functions main(), WarmMain()

Startup functions main(), WarmMain()
This document describes the firmware startup functions.

Startup Functions main(), WarmMain()

This document describes the firmware startup and callback functions for TWELITE GOLD.

main(), WarmMain()

In TWELITE GOLD, the firmware starts from the main() function. A wake-up from sleep is handled by the WarmMain() function. Since these functions have a weak attribute, users can define their own versions.

__attribute__((weak)) int main(void);
__attribute((weak)) void WarmMain(void);

int main(void) {
    ToCoNet_vInit_Cold_Pre();
	ToCoNet_vBoard_Init(FALSE);
	ToCoNet_vInit_Cold(0, 0);
	ToCoNet_vMain(NULL);
}

void WarmMain(void)
{
    vAHI_OnWakeup_MW(FALSE);
    ToCoNet_vInit_Warm_Pre();
	ToCoNet_vBoard_Init(TRUE);
	ToCoNet_vInit_Warm(0);
	ToCoNet_vMain(NULL);
}
// source/twenet_main.c

Call flow for a cold boot (power-on, reset)

main()
  ToCoNet_vInit_Cold_Pre()    <= Memory initialization
                                 Call cbAppColdStart(FALSE) for TWENET C, init_cold() for MWX

  ToCoNet_vBoard_Init(FALSE)
    vAHI_RegEvMgr_MW()        <= Initialize mwf library
    BOARD_InitBootPins()
      BOARD_InitPins()        <= Initializes each pin in this function
    BOARD_BootClockRUN()      <= Clock initialization
    vAHI_FRWTStart_MW()       <= Start FRWT (WTIMER)
    G_TWENET_CHIPSENSOR_AUTO_ON_BOOT()
                              <= Preparation for on-chip temperature sensor acquisition
    GPIO_PortInit()           <= GPIO initialization
    checkIrqPending()         <= Clear interrupt status
    AES_Init()                <= AES encryption initialization
    __enable_irq()            <= Start interrupts

  ToCoNet_vInit_Cold()        <= TWENET initialization
                                 Call cbAppColdStart(TRUE) for TWENET C, setup() for MWX

  ToCoNet_vMain()             <= TWENET main loop

Call flow for a warm boot (wake-up from RAM retention sleep)

WarmMain()
  vAHI_OnWakeup_MW(FALSE)     <= Peripheral processing immediately after wake-up

  ToCoNet_vInit_Warm_Pre()    <= Call cbAppWarmStart(FALSE) for TWENET C, init_warm() for MWX

  ToCoNet_vBoard_Init(TRUE)
    BOARD_BootClockRUN()      <= Clock initialization
    vAHI_FRWTSetBootCount_MW() <= Save FRWT (WTIMER) wake-up count
    G_TWENET_CHIPSENSOR_AUTO_ON_BOOT()
                              <= Preparation for on-chip temperature sensor acquisition
    GPIO_PortInit()           <= GPIO initialization
    vAHI_OnWakeup_MW()        <= Wake-up processing
    checkIrqPending()         <= Clear interrupt status
    AES_Init()                <= AES encryption initialization
    __enable_irq();           <= Start interrupts

  ToCoNet_vInit_Warm()        <= TWENET initialization
                                 Call cbAppWarmStart(TRUE) for TWENET C, wakeup() for MWX

  ToCoNet_vMain()             <= TWENET main loop

Call flow for wake-up from RAM OFF sleep

main()
  vAHI_OnWakeupRamOff_MW(FALSE) <= Peripheral processing (checking DIO state)

  ToCoNet_vInit_Cold_Pre()    <= Memory initialization
                                 Call cbAppColdStart(FALSE) for TWENET C, init_cold() for MWX

  ToCoNet_vBoard_Init(FALSE)
    vAHI_RegEvMgr_MW()        <= Initialize mwf library
    BOARD_BootClockRUN()      <= Clock initialization
    vAHI_FRWTStart_MW()       <= Start FRWT (WTIMER)
    G_TWENET_CHIPSENSOR_AUTO_ON_BOOT()
                              <= Preparation for on-chip temperature sensor acquisition
    GPIO_PortInit()           <= GPIO initialization
	vAHI_OnWakeupRamOff_MW(TRUE) <= Peripheral processing (releasing GPIO RETENTION)
    checkIrqPending()         <= Clear interrupt status
    AES_Init()                <= AES encryption initialization
    __enable_irq()            <= Start interrupts

  ToCoNet_vInit_Cold()        <= TWENET initialization
                                 Call cbAppColdStart(TRUE) for TWENET C, setup() for MWX

  ToCoNet_vMain()             <= TWENET main loop

ToCoNet_vBoard_Init(bool_t)

__attribute__((weak)) void ToCoNet_vBoard_Init(bool_t bWarm);
// source/twenet_main.c

This function performs hardware initialization at startup. Since it has a weak attribute, you can define your own initialization procedures. Refer to the twenet_main.c source code for details.

This series of initialization steps includes the necessary corrections for projects generated by the MCUXpresso project wizard. For function structures and other details, please read through the code, referring to the SDK documentation.

If you need to perform unique hardware initialization, you’ll be modifying this section. Be aware that this is a sensitive part of the code, so careful modification and thorough testing are required.

ToCoNet_vInitCold(uint32)

void ToCoNet_vInit_Cold(uint32 flag, uint32 u32SysHz)

This function performs the TWENET initialization procedure.

ParameterDescription
flagStartup specification. Please use 0 for normal use.
u32SysHzSpecifies the SysTick timer frequency. Please use 0 for normal use.
If needed, specify a multiple of 1000.

ToCoNet_vInit_Warm()

void ToCoNet_vInit_Warm(uint32 flag)

This function performs the TWENET initialization procedure when waking up from sleep.

ParameterDescription
flagStartup specification. Please use 0 for normal use.

ToCoNet_vMain()

void ToCoNet_vMain(PR_TOCONET_MAIN_HOOK fp_hook)

This is the main loop for TWENET.

checkIrqPending()

static bool_t checkIrqPending();

uint64_t g_twenet_irq_bm_on_boot;
WEAK bool_t __twenet_irq_handler_pending_on_boot(int32_t);

This function saves and clears interrupt information that occurred during or after sleep, until this function is called.

  • Each bit of g_twenet_irq_bm_on_boot corresponds to an interrupt source (IRQ_Type) in the form (1uul << IRQ_Type).
  • The interrupt types are defined in typedef enum IRQn in JN5189.h.
  • Interrupts are cleared by calling NVIC_ClearPendingIRQ().
  • If a user defines the __twenet_irq_handler_pending_on_boot(int32_t IRQ_Type) function and the IRQ_Type interrupt is enabled, this function will be called. If the return value is FALSE, NVIC_ClearPendingIRQ(IRQ_Type) is executed. If it’s TRUE, nothing happens.

System Timer Cycle

You can set the SysTick timer cycle in the following ways:

  • Set it in the ToCoNet_vInitCold() function. This setting takes precedence.

  • Set G_TWENET_SYSTICK_HZ() in cbAppColdStart(FALSE).

  • If neither of the above methods is used, the value of sToCoNet_AppContext.u16TickHz will be used.

Please note the following:

  • The TWENET processing cycle (sToCoNet_AppContext.u16TickHz) has a maximum of 1000Hz.
    • The SysTick cycle is an integer multiple of sToCoNet_AppContext.u16TickHz.
  • The standard setting is SysTick = 1000Hz, sToCoNet_AppContext.u16TickHz = 1000Hz.
    • A semi-standard setting is SysTick = 2000Hz or 4000Hz, and sToCoNet_AppContext.u16TickHz = 1000Hz. This helps distribute the load by performing some background processing (like UART) outside of the TWENET cycle. This also allows for processing with a faster SysTick timer interrupt (see AppQAPI_SysTick_Hnd_Reg()).
    • Another semi-standard setting is SysTick = 250Hz, sToCoNet_AppContext.u16TickHz = 250Hz. This is used for power saving or when processing per timer is heavy, as the time per radio packet is about 2-5ms (transmission time after modulation and gaps between packets).
    • We do not confirm the operation of each API or our applications with semi-standard settings. If you need to use them, please perform thorough testing.

Initialization

G_TWENET_B_MAC_ALWAYS_RESET_ON_WAKE()

bool_t G_TWENET_B_MAC_ALWAYS_RESET_ON_WAKE()
  // Macro definition

Setting this variable to 1 will re-initialize the MAC layer when waking up from sleep. This re-initialization adds extra processing time (approximately 400µs at 32MHz).

  • Even with this option enabled, the pre-sleep process (saving the MAC layer state, about 100µs at 32MHz) will still be executed the same way as when the option is disabled.

3 - RAM Allocation

RAM allocation
Describes RAM allocation.

RAM Allocation

The SRAM area of TWWLIET GOLD is as follows.

BASETOP (End + 1)SIZE
SRAM110x0402_C0000x0403_000016KB
SRAM100x0402_80000x0402_C00016KB
SRAM90x0402_40000x0402_800016KB
SRAM80x0402_00000x0402_400016KB
SRAM70x0401_50000x0401_60004KB
SRAM60x0401_40000x0401_50004KB
SRAM50x0401_20000x0401_40008KB
SRAM40x0401_00000x0401_20008KB
SRAM30x0400_C0000x0401_000016KB
SRAM20x0400_80000x0400_C00016KB
SRAM10x0400_40000x0400_800016KB
SRAM00x0400_00000x0400_400016KB

TWENET defines the memory map as follows.

BASETOPSizePurposeRETBank Name
Application Use0x0400_0000Decided at compile time~64KBRequired for MMAC linkingSRAM0..3
Heap0x0401_5000
(Changeable)
0x0401_5C003KBHeap area (malloc, new)SRAM7
Unused0x0402_00000x0402_C00048KBUnused areaSRAM8, 9, 10
SRAM11
Unused0x0402_C0000x0402_F00012KBUnused areaSRAM11
Stack0x0402_F0000x0403_00004KBStack areaSRAM11

(BASE: Start Address, TOP: End Address + 1, RET: RAM Retention during sleep)

  • The application use area is a static memory range determined at compile time. The end address can be referenced with (uint32)&_end_fw_retention. When sleeping, the system is set not to retain unnecessary banks based on the end address.
  • The heap area is a region allocated by malloc() and the new operator. Generally, repeated allocation and deallocation of this area can lead to fragmentation issues, so you should be mindful of this when implementing your application. The default setting allocates memory to SRAM bank 7. However, the last 512 bytes of bank 7 are reserved by the microcontroller’s semiconductor specifications, and the subsequent 512 bytes are reserved by TWENET.
    • This area can be adjusted by writing HEAP_START=0x04014000; and HEAP_SIZE = 8192 - BANK7_RESERVE; in App_User_Defs.ld (placed directly in the build directory).
      • HEAP_START is the start address of the heap, and HEAP_SIZE is the allocated size. Here are some typical combinations:
        • 0x04014000, 8192-BANK7_RESERVE (7KB, BANK6-7)
        • 0x04012000, 16384-BANK7_RESERVE (15KB, BANK5-7)
        • 0x04010000, 24576-BANK7_RESERVE (23KB, BANK4-7)
        • 0x0, 0 (set the maximum area, i.e., from _end_fw_rentention to 0x04016000-BANK7_RESERVE, as the HEAP)
  • The unused areas are SRAM8, 9, and 10, each with 16KB.
  • The stack area is set to the last 4096 bytes of SRAM11 (0x0403_0000) by default.
    • You can change the size of this area by writing STACK_SIZE = 8192; in App_User_Defs.ld (placed directly in the build directory). You can also specify the end address, such as STACK_TOP = 0x04024000;.
    • Please check _vStackTop and __StackLimit in the map file created in the build directory.
    • This area is not retained during sleep.

I’ve translated the text and formatted it as plain text to avoid the issue you mentioned earlier.

Setting the Retention Area during Sleep

The TWENET library is pre-configured to properly retain the necessary SRAM banks for the application use and heap areas during sleep. However, depending on the design and implementation of your application, you may want to retain banks that are not normally held. To do this, you can set the corresponding bits for any additional banks you need in the global variable G_TWENET_POWER_DOWN_RETENTION_CONFIG_ADD() defined in the TWENETcmpt library.

For example, if you want to retain 32KB of banks 8 and 9, specify the following before calling ToCoNet_vSleep() in the TWENET C library or the_twelite.sleep() in the mwx library.

G_TWENET_POWER_DOWN_RETENTION_CONFIG_ADD() = PM_CFG_SRAM_BANK8_RET | PM_CFG_SRAM_BANK9_RET;

Note: Increasing the amount of retained SRAM will also increase the sleep current.

App_User_Defs.ld Configuration Examples

If not specified, the HEAP will be from 0x0401_5000 to 0x0401_5FE0, and the STACK will be from 0x0402_F000 to 0x0403_0000. Below are examples for the .../build/App_User_Defs.ld configuration.

  • To allocate the maximum possible HEAP, from _end_fw_retention to 0x0401_5FE0:
HEAP_START = 0;
HEAP_SIZE = 0;
  • To allocate HEAP in RAM6 and 7 (approx. 8KB):
HEAP_START = 0x04014000;
HEAP_SIZE = 8192 - BANK7_RESERVE;
  • To set the HEAP from _end_fw_retention to 0x0401_5000 and the STACK from 0x0401_5000 to 0x0401_5FE0. (This makes BANK8..11 from 0x0402_0000 to 0x0403_0000 an unused area):
HEAP_TOP = 0x04015000;
HEAP_START = 0;
HEAP_SIZE = 0;
STACK_TOP = 0x04015fe0;
STACK_SIZE = 4096-BANK7_RESERVE;

Note: If both HEAP_START and HEAP_SIZE are 0, you can set HEAP_TOP. In this case, HEAP_TOP cannot be set to a region equal to or greater than 0x0402_0000.

  • To allocate 64KB for the HEAP from 0x0402_0000 and move the STACK area to a region below 0x04015fe0:
HEAP_START = 0x04020000;
HEAP_SIZE = 0x10000;
STACK_TOP = 0x04015fe0;
STACK_SIZE = 4096-BANK7_RESERVE;

OneTime_Heap.c,h

TWENETutils provides a One Time Heap for sequential memory allocation without deallocation. This makes it easy to use unused areas.

#include <OneTimeHeap.h>
OTHEAP_tsContext scOTHeap;

void setup() {
    uint32 u32bytes;
    void *p;

    // Use the 16KB region from 0x0402_0000 to 0x0402_4000.
    OTHEAP_Init(&scOTHeap, 0x04020000, 0x04024000, NULL);

    Serial << crlf << "--- One Time HEAP ---";
    Serial << crlf << format("start %08x", (uint32)OTHEAP_pvGetRegionStart(&scOTHeap));
    Serial << crlf << format("top %08x", (uint32)OTHEAP_pvGetRegionTop(&scOTHeap));

    // Allocate 100 bytes
    u32bytes = 100;
    Serial << crlf;
    Serial << crlf << format("head %08x", (uint32)OTHEAP_pvGetHead(&scOTHeap));
    p = OTHEAP_pvAlloc(&scOTHeap, u32bytes, TRUE);
    		// p=0x0402_0004 (The area after the 4-byte header is available)
    Serial << crlf << format("alloc %dbytes [%08x->%08x)", u32bytes, (uint32)p, (uint32)p+u32bytes);
    if(p) Serial << crlf << format("next %08x", *(uint32*)((uint32)p - 4));
    		// The header is the address of the next block (0x0402_0068)

    // Allocate 10 bytes (actually 12 bytes because it's aligned to a 4-byte boundary)
    u32bytes = 10;
    Serial << crlf;
    Serial << crlf << format("head %08x", (uint32)OTHEAP_pvGetHead(&scOTHeap));
    p = OTHEAP_pvAlloc(&scOTHeap, u32bytes, TRUE);
    		// p=0x0402_006c
    Serial << crlf << format("alloc %dbytes [%08x->%08x)", u32bytes, (uint32)p, (uint32)p+u32bytes);
    if(p) Serial << crlf << format("next %08x", *(uint32*)((uint32)p - 4));
    		// The header is the address of the next block (0x0402_0078)

    // Free the last allocated block. The address of the freed block (p=0x0402_006c)
    p = OTHEAP_pvFreeLastBlock(&scOTHeap);
}

4 - About printf (Debugging, Serial Output)

About printf (Debugging, Serial Output)
This document describes printf (debugging, serial output).

About printf (Debugging, Serial Output)

printf Library

This is the printf() process used within the TWENET library. For more details, please refer to TWENETmuc/printf.

PRINTF for Debugging

The PRINTF() macro in the fsl library provided by NXP is used for debug output and is excluded from compilation during release. The TWENET library has also been adjusted to use PRINTF(), but it doesn’t have all the features of the fsl library.

  • The PRINTF() macro calls printf_() mentioned above.
  • Input macros like GETCHAR() are not supported.

With JN518x SDK 2.6.3 and 2.6.4, NewLib and NewLibNano show garbled or missing data in output and do not behave as expected (this is likely a buffering issue. RedLib does not show this problem, but it cannot be used in a mixed C/C++ project). For this reason, the PRINTF() macro has been changed to use the printf_() function within libTWENETmcu/printf.

SDK_DEBUGCONSOLE Definition

The behavior of the PRINTF() in your application will change based on the value of SDK_DEBUGCONSOLE.

ValueDescription
0Output is sent to the debug console (Semihosting). This process is very slow.
* To enable this output, you must set SDK_DEBUGCONSOLE=0 in the libTWENETmcu (Debug build), rebuild the library, and periodically call _putchar(-1) (approximately every 16ms) in your application code. (See SysTick_Handler() defined in the sample Samp_bare_MMAC)
1This is the setting for a Debug build. PRINTF uses the printf_ function (libTWENETmcu). If _putchar() is not redefined, SERIAL_bTxChar(0, c); is called. This is also the default setting for Debug builds of TWENETxxx libraries.
2This is the setting for a Release build. PRINTF is excluded from compilation, and printf_() outputs nothing. If you want to use printf_(), you must redefine _putchar(int). This is also the default setting for Release builds of TWENETxxx libraries.
  • The _putchar() in the libTWENETmcu library has a weak link specification. The _putchar() you define in your application code will take precedence.
  • To enable PRINTF() within the TWENETxxx library, you must set the same SDK_DEBUGCOSOLE definition in your application and rebuild the library.

About SWO

TWENETmcu/source includes code for SWO output. While we don’t officially support this feature, here’s a breakdown of its code and functionality:

  • Code related to SWO is defined with -DDEBUG and -DSERIAL_PORT_TYPE=1. When these are set, the PRINTF() output is adjusted to be sent to the SWO ITM (main/retarget_itm.c, main/retarget_putchar.c).
  • The SWO port is set to PIO14.
  • Debugging often fails to start when SWO is enabled. Keeping the ISP pin (PIO5) in a LOW state until just before the debugger starts can sometimes resolve this.