
# [nano RTOS](/) / arduino

### realtime operating system for time-critical applications

#### Notes
nano RTOS can be integrated in Arduino to provide tasks, synchronisation
primitives, enhanced drivers, printf, etc.
Some Arduino libraries utilise Arduino timing functions, that might not
function optimally when an operating system is in control of the *SysTick*
timer. The following compatibility macros affect the behaviour of Arduino
timing functions.

☂︎ **Default configuration**  
The default configuration uses a dynamic (tickless) scheduler, where
the *SysTick* period is adjusted to reduce unnecessary interrupts, while
also providing very precise wake-up events with microsecond accuracy.
In this mode, forwarding events to an external handler, e.g. Arduino
is still possible, but is disabled to reduce overhead, since the updates
may take up to one second. This can be resolved by patching the Arduino
board libraries to utilise the nano RTOS timing functions, see below.

``` makefile
NANO_TICKLESS=1
NANO_SYSTICK_EXT=0
```

☂︎ **Compatibility mode**  
In compatibility mode, the *SysTick* period is fixed. Timer events can be
forwarded to Arduino, which makes the Arduino timing functions fully
functional. In this mode however, the precision of wake-up events is
reduced to a millisecond. For optimal performance, please use the dynamic
scheduler, which is enabled in the default configuration.

``` makefile
NANO_TICKLESS=0
NANO_SYSTICK_EXT=1
```


### Prerequisites for nano RTOS integration in Arduino
#### [nano RTOS Arduino library](Arduino/nanoRTOS.7z)
☂︎ Download and extract in `~/Documents/Arduino/libraries`  
☂︎ Navigate to `nanoRTOS/src/cortex-m3` and create a symlink to `nanoRTOS.a`

☂︎ macOS  
```bash
cd ~/Documents/Arduino/libraries/nanoRTOS/src/cortex-m3
ln -s ~/nano/lib/nanoRTOS.a nanoRTOS.a
```

☂︎ Windows  
```bash
cd %USERPROFILE%\Documents\Arduino\libraries\nanoRTOS\src\cortex-m3
set NANO_RTOS=c:\nano
mklink nanoRTOS.a %NANO_RTOS%\lib\nanoRTOS.a
```

#### Install the Arm GNU Toolchain
☂︎ macOS: install from [brew](https://brew.sh/) and create a link

``` bash
brew install cask gcc-arm-embedded
cd ~/Library/Arduino15/packages/arduino/tools/arm-none-eabi-gcc
ln -s /Applications/ArmGNUToolchain/*/arm-none-eabi ARM
rm -rf 4.8.3-2014q1
```

☂︎ Or download the [Arm GNU Toolchain](https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads)
and extract to

☂︎ macOS  
`~/Library/Arduino15/packages/arduino/tools/arm-none-eabi-gcc`

☂︎ Windows  
`%LOCALAPPDATA%\Arduino15\packages\arduino\tools\arm-none-eabi-gcc`


#### Fix the return type of `spiRec`
The return type of `spiRec` is `uint8_t`, which is incompatible with the
declaration of `byte` in new versions of `arm-none-eabi-gcc`

☂︎ macOS  
`/Applications/Arduino.app/Contents/Java/libraries/SD/src/utility/Sd2Card.cpp`

☂︎ Windows  
`%ProgramFiles% (x86)\Arduino\Java\libraries\SD\src\utility\Sd2Card.cpp`

``` patch
--- Sd2Card.cpp
+++ Sd2Card.cpp
@@ -770,7 +770,7 @@
 */
 uint8_t Sd2Card::isBusy(void) {
   chipSelectLow();
-  byte b = spiRec();
+  uint8_t b = spiRec();
   chipSelectHigh();

   return (b != 0XFF);
```


#### Add a menu to select between **nano RTOS** and **bare metal** in Arduino
☂︎ When **bare metal** is selected, the original behaviour is preserved  
☂︎ When **nano RTOS** is selected, the Arduino timing functions are replaced

☂︎ macOS  
`~/Library/Arduino15/packages/STMicroelectronics/hardware/stm32/2.6.0/cores/boards.txt`

☂︎ Windows  
`%LOCALAPPDATA%\Arduino15\packages\STMicroelectronics\hardware\stm32\2.6.0\cores\boards.txt`

``` patch
--- boards.txt
+++ boards.txt
@@ -8,6 +8,7 @@
 menu.virtio=Virtual serial support

 menu.opt=Optimize
+menu.rtos=RTOS
 menu.dbg=Debug symbols and core logs
 menu.rtlib=C Runtime Library
 menu.upload_method=Upload method
@@ -2159,6 +2160,9 @@
 GenF1.menu.pnum.BLUEPILL_F103C8.build.product_line=STM32F103xB
 GenF1.menu.pnum.BLUEPILL_F103C8.build.variant=STM32F1xx/F103C8T_F103CB(T-U)
 GenF1.menu.pnum.BLUEPILL_F103C8.build.variant_h=variant_PILL_F103Cx.h
+GenF1.menu.pnum.BLUEPILL_F103C8.rtos.nano=nano RTOS
+GenF1.menu.pnum.BLUEPILL_F103C8.rtos.nano.build.flags.rtos=-DUSE_NANO_RTOS=1
+GenF1.menu.pnum.BLUEPILL_F103C8.rtos.bare_metal=bare metal

 GenF1.menu.pnum.BLUEPILL_F103CB=BluePill F103CB (or C8 with 128k)
 GenF1.menu.pnum.BLUEPILL_F103CB.upload.maximum_size=131072
@@ -2167,6 +2171,9 @@
 GenF1.menu.pnum.BLUEPILL_F103CB.build.product_line=STM32F103xB
 GenF1.menu.pnum.BLUEPILL_F103CB.build.variant_h=variant_PILL_F103Cx.h
 GenF1.menu.pnum.BLUEPILL_F103CB.build.variant=STM32F1xx/F103C8T_F103CB(T-U)
+GenF1.menu.pnum.BLUEPILL_F103CB.rtos.nano=nano RTOS
+GenF1.menu.pnum.BLUEPILL_F103CB.rtos.nano.build.flags.rtos=-DUSE_NANO_RTOS=1
+GenF1.menu.pnum.BLUEPILL_F103CB.rtos.bare_metal=bare metal

 # BLACKPILL_F103C8 board
 GenF1.menu.pnum.BLACKPILL_F103C8=BlackPill F103C8
```

#### Add `build.flags.rtos` to the build process

☂︎ macOS  
`~/Library/Arduino15/packages/STMicroelectronics/hardware/stm32/2.6.0/cores/platform.txt`

☂︎ Windows  
`%LOCALAPPDATA%\Arduino15\packages\STMicroelectronics\hardware\stm32\2.6.0\cores\platform.txt`

``` patch
--- platform.txt
+++ platform.txt
@@ -30,11 +30,11 @@

 compiler.extra_flags=-mcpu={build.mcu} {build.fpu} {build.float-abi} -DVECT_TAB_OFFSET={build.flash_offset} -DUSE_FULL_LL_DRIVER -mthumb "@{build.opt.path}"

-compiler.S.flags={compiler.extra_flags} -c -x assembler-with-cpp {compiler.stm.extra_include}
+compiler.S.flags={compiler.extra_flags} -c -x assembler-with-cpp {compiler.stm.extra_include} {build.flags.rtos}

-compiler.c.flags={compiler.extra_flags} -c {build.flags.optimize} {build.flags.debug} {compiler.warning_flags} -std={compiler.c.std} -ffunction-sections -fdata-sections --param max-inline-insns-single=500 -MMD {compiler.stm.extra_include}
+compiler.c.flags={compiler.extra_flags} -c {build.flags.optimize} {build.flags.debug} {compiler.warning_flags} -std={compiler.c.std} -ffunction-sections -fdata-sections --param max-inline-insns-single=500 -MMD {compiler.stm.extra_include} {build.flags.rtos}

-compiler.cpp.flags={compiler.extra_flags} -c {build.flags.optimize} {build.flags.debug} {compiler.warning_flags} -std={compiler.cpp.std} -ffunction-sections -fdata-sections -fno-threadsafe-statics --param max-inline-insns-single=500 -fno-rtti -fno-exceptions -fno-use-cxa-atexit -MMD {compiler.stm.extra_include}
+compiler.cpp.flags={compiler.extra_flags} -c {build.flags.optimize} {build.flags.debug} {compiler.warning_flags} -std={compiler.cpp.std} -ffunction-sections -fdata-sections -fno-threadsafe-statics --param max-inline-insns-single=500 -fno-rtti -fno-exceptions -fno-use-cxa-atexit -MMD {compiler.stm.extra_include} {build.flags.rtos}

 compiler.ar.flags=rcs

@@ -105,6 +105,7 @@
 build.startup_file=
 build.fpu=
 build.float-abi=
+build.flags.rtos=
 build.flags.optimize=-Os
 build.flags.debug=-DNDEBUG
 build.flags.ldspecs=--specs=nano.specs
```


#### Conditionally patch the Arduino timing functions  
☂︎ When **bare metal** is selected, the original behaviour is preserved  
☂︎ When **nano RTOS** is selected, the Arduino timing functions are replaced

☂︎ macOS  
`~/Library/Arduino15/packages/STMicroelectronics/hardware/stm32/2.6.0/cores/arduino/wiring_time.c`

☂︎ Windows  
`%LOCALAPPDATA%\Arduino15\packages\STMicroelectronics\hardware\stm32\2.6.0\cores\arduino\wiring_time.c`

``` patch
--- wiring_time.c
+++ wiring_time.c
@@ -22,6 +22,28 @@
 extern "C" {
 #endif

+#if USE_NANO_RTOS
+extern uint64_t get_timestamp_ms(void);
+extern uint64_t get_timestamp_us(void);
+extern uint64_t task_sleep(uint64_t us);
+
+uint32_t millis(void)
+{
+  // ToDo: ensure no interrupts
+  return (uint32_t)get_timestamp_ms();
+}
+
+// Interrupt-compatible version of micros
+uint32_t micros(void)
+{
+  return (uint32_t)get_timestamp_us();
+}
+
+void delay(uint32_t ms)
+{
+  task_sleep(ms * 1000);
+}
+#else
 uint32_t millis(void)
 {
   // ToDo: ensure no interrupts
@@ -43,6 +65,7 @@
     } while (getCurrentMillis() - start < ms);
   }
 }
+#endif

 #ifdef __cplusplus
 }
```

#### © 2022-2023 Georgi Valkov
https://httpstorm.com/  
https://nanortos.com/  
<keyfunc>
	<span>g</span><span>v</span><span>a</span><span>l</span><span>k</span><span>o</span><span>v</span><span>&#64;</span><span>g</span><span>m</span><span>a</span><span>i</span><span>l</span><span>&#46;</span><span>c</span><span>o</span><span>m</span>
</keyfunc>
