For suitable output, we recommend to use Google Chrome (15+) or Microsoft Edge (79+).
As of 2025-09-10TWENETmcu - Microcontroller Library
Library supporting the basic operation of the microcontroller
Contains source code for basic microcontroller operations.
TWENETmcu - Microcontroller Library
TWENETmcu is designed for TWELITE GOLD.
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.
TWENETmcu code is not compatible with the features of MCUXpresso such as Pins, Clocks, and Peripherals. It cannot be modified by these tools.
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.
Parameter | Description |
---|
flag | Startup specification. Please use 0 for normal use. |
u32SysHz | Specifies 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.
Parameter | Description |
---|
flag | Startup 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.
It is not compatible with the functions of C/C++ Build>MCU Settings in MCUXpresso. To change the RAM allocation, write a user-defined linker script or modify the linker scripts in TWENETmcs/linker/linkscripts
.
RAM Allocation
The SRAM area of TWWLIET GOLD is as follows.
| BASE | TOP (End + 1) | SIZE |
---|
SRAM11 | 0x0402_C000 | 0x0403_0000 | 16KB |
SRAM10 | 0x0402_8000 | 0x0402_C000 | 16KB |
SRAM9 | 0x0402_4000 | 0x0402_8000 | 16KB |
SRAM8 | 0x0402_0000 | 0x0402_4000 | 16KB |
| | | |
SRAM7 | 0x0401_5000 | 0x0401_6000 | 4KB |
SRAM6 | 0x0401_4000 | 0x0401_5000 | 4KB |
SRAM5 | 0x0401_2000 | 0x0401_4000 | 8KB |
SRAM4 | 0x0401_0000 | 0x0401_2000 | 8KB |
SRAM3 | 0x0400_C000 | 0x0401_0000 | 16KB |
SRAM2 | 0x0400_8000 | 0x0400_C000 | 16KB |
SRAM1 | 0x0400_4000 | 0x0400_8000 | 16KB |
SRAM0 | 0x0400_0000 | 0x0400_4000 | 16KB |
TWENET defines the memory map as follows.
| BASE | TOP | Size | Purpose | RET | Bank Name |
---|
Application Use | 0x0400_0000 | Decided at compile time | ~64KB | Required for MMAC linking | 〇 | SRAM0..3 |
Heap | 0x0401_5000 (Changeable) | 0x0401_5C00 | 3KB | Heap area (malloc, new) | 〇 | SRAM7 |
Unused | 0x0402_0000 | 0x0402_C000 | 48KB | Unused area | | SRAM8, 9, 10 SRAM11 |
Unused | 0x0402_C000 | 0x0402_F000 | 12KB | Unused area | | SRAM11 |
Stack | 0x0402_F000 | 0x0403_0000 | 4KB | Stack area | | SRAM11 |
| | | | | | |
(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
.
Value | Description |
---|
0 | Output 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 ) |
1 | This 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. |
2 | This 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.