led_blink.c
The low-level hello world of the nRF52840: the LED blinks by writing GPIO registers directly, with no SDK and no clock setup. Understanding OUTSET/OUTCLR and PIN_CNF is the foundation of everything Nordic.
Hardware
An nRF52840-MDK board (Nordic nRF52840, Cortex-M4F @ 64 MHz, 1 MB flash / 256 KB RAM, BLE 5 + 802.15.4) with onboard debugger, programmed in C with nRF5 SDK v17 and the S140 SoftDevice. nRF52840-MDK powered over USB; user LED P0.06 (or an external LED + 330 Ω on any free pin).
Wiring
None for the onboard LED. External: anode through 330 Ω to the chosen pin, cathode to GND (pins tolerate 3.3 V).
How it works
PIN_CNF sets direction and drive; OUTSET/OUTCLR drive the pin high/low atomically, no read-modify-write. The delay is a NOP loop calibrated by eye.
Running it
Open the matching nRF5 SDK v17 example (custom_board target), paste this code into main.c, build with Segger Embedded Studio or the gcc Makefile, then flash through the onboard debugger.
Variants
Replace the delay with the RTC timer to sleep between blinks (low-power version), or alternate two LEDs.
led_blink.c
// nRF52840-MDK : clignotement LED en accès direct aux registres (zéro SDK).
// LED utilisateur MDK : P0.06 (vérifier la sérigraphie de votre révision).
#include <stdint.h>
#include "nrf.h"
#define LED_PIN 6 // P0.06 sur MDK
#define PORT0_BASE 0x50000000UL
typedef struct { volatile uint32_t t[12]; } NRF_GPIO_Type;
#define NRF_GPIO0 ((NRF_GPIO_Type *)PORT0_BASE)
static void delay_ms(uint32_t ms) { // boucle d'attente grossière
for (volatile uint32_t i = 0; i < ms * 8000UL; i++) { __NOP(); }
}
int main(void) {
// Configure P0.06 en sortie push-pull, drive standard.
NRF_GPIO->PIN_CNF[LED_PIN] =
(GPIO_PIN_CNF_DIR_Output << GPIO_PIN_CNF_DIR_Pos) |
(GPIO_PIN_CNF_DRIVE_S0S1 << GPIO_PIN_CNF_DRIVE_Pos);
for (;;) {
NRF_GPIO->OUTSET = (1UL << LED_PIN); // LED ON (cathode vers GND)
delay_ms(500);
NRF_GPIO->OUTCLR = (1UL << LED_PIN); // LED OFF
delay_ms(500);
}
}