Introduction : Et alors, c'était possible ?
\nImaginez : vous programmez un microcontrôleur, mais en utilisant des lambdas, des coroutines et des builders type-safe. Ça ressemble à de la science-fiction ? Et pourtant non. Le développement embarqué avec Kotlin n'est pas une blague, mais un outil parfaitement fonctionnel qui gagne en popularité.
\nTraditionnellement, le monde du « matériel » est le royaume du C et de l'assembleur. Puis le C++ s'y est timidement introduit, mais avec des réserves. Et voici Kotlin. Un langage que nous avons l'habitude d'associer à Android et au backend, qui se met soudain à parler le langage des registres et des interruptions. Comment ? Via Kotlin/Native – une technologie qui compile votre code directement en code machine, sans passer par la JVM.
\nDans cet article, nous allons voir comment utiliser Kotlin pour les systèmes embarqués, écrire du code réel pour faire clignoter une LED (oui, on n'y échappe pas) et déterminer si le jeu en vaut la chandelle. Si vous en avez assez des macros en C et que vous voulez une syntaxe moderne au niveau des registres, vous êtes au bon endroit.
\n\nKotlin/Native : Comment ça fonctionne sous le capot ?
\nLa question principale que tout ingénieur embarqué se pose : « Est-ce que ça va ralentir ? ».
\nKotlin/Native utilise LLVM comme backend. Cela signifie que votre code est compilé en un binaire natif, qui peut être exécuté sur du métal nu (bare-metal) ou sous RTOS. Pas de garbage collector au runtime (si vous le désactivez), pas de machine virtuelle. Juste vous et le processeur.
\nEn termes de performances, Kotlin pour microcontrôleurs ne le cède pratiquement pas au C. Oui, il y a un léger surcoût dû au modèle objet, mais il est compensé par la commodité et la sécurité du code.
\n\nDe quoi a-t-on besoin pour commencer ?
\n- \n
- Kotlin/Native installé (via le plugin Kotlin Multiplatform ou manuellement) \n
- Une chaîne d'outils pour votre architecture (ARM, RISC-V, x86) – généralement un ensemble de cross-compilateurs \n
- Une bibliothèque pour interagir avec le matériel – par exemple
kotlinx-c-interopou des bindings manuels \n - De la patience – les premiers pas seront inhabituels \n
Écrire du code : Faire clignoter une LED en Kotlin
\nAllons droit au but. Supposons que nous ayons une carte STM32 (ARM Cortex-M). Nous devons allumer une LED sur la broche PA5.
\nPour commencer, connectons-nous aux registres via Kotlin/Native interop :
\n\nimport kotlinx.cinterop.*\nimport platform.posix.*\n\n// Adresses des registres pour STM32F4 (exemple)\nval RCC_AHB1ENR: UIntPointer = 0x40023830.toUInt().ptr\nval GPIOA_MODER: UIntPointer = 0x40020000.toUInt().ptr\nval GPIOA_ODR: UIntPointer = 0x40020014.toUInt().ptr\n\nfun main() {\n // Activation de l'horloge GPIOA\n RCC_AHB1ENR.pointed.value = RCC_AHB1ENR.pointed.value or (1u shl 0)\n \n // Configuration de PA5 en sortie (01 dans les bits 10-11)\n GPIOA_MODER.pointed.value = GPIOA_MODER.pointed.value and (3u shl 10).inv()\n GPIOA_MODER.pointed.value = GPIOA_MODER.pointed.value or (1u shl 10)\n \n // Boucle infinie : clignotement\n while (true) {\n GPIOA_ODR.pointed.value = GPIOA_ODR.pointed.value xor (1u shl 5)\n // Délai (approximatif)\n for (i in 0 until 100000) {\n // vide\n }\n }\n}\n\nCela ressemble presque à du C, mais avec le sucre syntaxique de Kotlin. Notez le u (nombres non signés) et la manipulation des pointeurs via ptr. Ce n'est pas un bug, mais une fonctionnalité – nous indiquons explicitement au compilateur que nous travaillons directement avec la mémoire.
Peut-on faire plus élégant ?
\nOui ! Nous pouvons encapsuler le travail avec les registres dans un DSL. Regardez comme le code est élégant avec les fonctions d'extension :
\n\n// Définition d'une API pratique\nfun UIntPointer.setBit(bit: Int) {\n this.pointed.value = this.pointed.value or (1u shl bit)\n}\n\nfun UIntPointer.clearBit(bit: Int) {\n this.pointed.value = this.pointed.value and (1u shl bit).inv()\n}\n\nfun UIntPointer.toggleBit(bit: Int) {\n this.pointed.value = this.point