Control E-Paper display with SPI.

Hey,

I try to communicate with an E-Paper display (GDEY0579Z93) from my NRF5252832 DK. I used this thread (SPI example for nRF52833 with nRF Connect SDK 2.3.0) and this example (https://github.com/too1/ncs-spi-master-slave-example) as template.

The manufacturer provides an Arduino example, which runs perfectly fine. I have tried to port it into a NRF52 Zephyr project.

However, I can't get any visible feedback from the display. In fact, I get this strange build warning

../src/main.c:55:15: warning: initialization of 'const struct device *' from incompatible pointer type 'struct spi_cs_control *' [-Wincompatible-pointer-types]
55 | .cs = &spim_cs,
| ^
../src/main.c:55:15: note: (near initialization for 'spi_cfg.cs.gpio.port')
../src/main.c:50:42: warning: missing braces around initializer [-Wmissing-braces]
50 | static const struct spi_config spi_cfg = {
| ^
......
55 | .cs = &spim_cs,
| {{
56 | };
| }}

The chip also reboots at:

int error = spi_write(spi_dev, &spi_cfg, &tx);

This is my Overlay:

&pinctrl {
	spi_master_default: spi_master_default {
		group1 {
			psels = <NRF_PSEL(SPIM_SCK, 0, 26)>,
					<NRF_PSEL(SPIM_MOSI, 0, 27)>,
					<NRF_PSEL(SPIM_MISO, 0, 29)>;
		};
	};

	spi_master_sleep: spi_master_sleep {
		group1 {
			psels = <NRF_PSEL(SPIM_SCK, 0, 26)>,
					<NRF_PSEL(SPIM_MOSI, 0, 27)>,
					<NRF_PSEL(SPIM_MISO, 0, 29)>;
			low-power-enable;
		};
	};
};

my_spi_master: &spi1 {
	compatible = "nordic,nrf-spi";
	status = "okay";
	pinctrl-0 = <&spi1_default>;
	pinctrl-1 = <&spi1_sleep>;
	cs-gpios = <&gpio0 02 GPIO_ACTIVE_LOW>;
	reg_my_spi_master: spi-dev-a@0 {
		reg = <0>;
	};
};

/ {
    gpiocustom {
        compatible = "gpio-keys";
        /*
        pinclk: pin_clk {
            gpios = <&gpio0 26 GPIO_ACTIVE_HIGH>;
            label = "CLOCK";
        };
        pinmosi: pin_mosi {
            gpios = <&gpio0 27 GPIO_ACTIVE_HIGH>;
            label = "MOSI";
        };
        pincs: pin_cs {
            gpios = <&gpio0 02 GPIO_ACTIVE_HIGH>;
            label = "CS";
        };
         */
        pinbusy: pin_busy {
            gpios = <&gpio0 23 GPIO_ACTIVE_HIGH>;
            label = "BUSY";
        };
        pinreset: pin_reset {
            gpios = <&gpio0 24 GPIO_ACTIVE_HIGH>;
            label = "RESET";
        };
        pindc: pin_dc {
            gpios = <&gpio0 25 GPIO_ACTIVE_HIGH>;
            label = "D/C";
        };

    };

    aliases {
        /*
        pinclk = &pinclk;
        pinmosi = &pinmosi;
        pincs = &pincs;
         */
        pinbusy = &pinbusy;
        pinreset = &pinreset;
        pindc = &pindc;    
    };
};

Code:

/*
 * Copyright (c) 2016 Intel Corporation
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include <zephyr/kernel.h>
#include <zephyr/drivers/uart.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/drivers/spi.h>


#define EPD_WIDTH   272
#define EPD_HEIGHT  800

#define Source_BYTES    400/8
#define Gate_BITS  272
#define ALLSCREEN_BYTES   Source_BYTES*Gate_BITS

#define NUM_LINE_BYTES 8 // =64/8  
#define NUM_COLUMN_BYTES  30 


#define MY_SPI_MASTER DT_NODELABEL(my_spi_master)

/* Custom Pins */
#define PIN_BUSY_NODE DT_ALIAS(pinbusy)
#define PIN_RESET_NODE DT_ALIAS(pinreset)
#define PIN_DC_NODE DT_ALIAS(pindc)
#define PIN_CS_NODE DT_ALIAS(pincs)
#define PIN_MOSI_NODE DT_ALIAS(pinmosi)
#define PIN_CLK_NODE DT_ALIAS(pinclk)

static const struct gpio_dt_spec pBusy = GPIO_DT_SPEC_GET(PIN_BUSY_NODE, gpios);
static const struct gpio_dt_spec pReset = GPIO_DT_SPEC_GET(PIN_RESET_NODE, gpios);
static const struct gpio_dt_spec pDc = GPIO_DT_SPEC_GET(PIN_DC_NODE, gpios);
//static const struct gpio_dt_spec pMosi = GPIO_DT_SPEC_GET(PIN_MOSI_NODE, gpios);
//static const struct gpio_dt_spec pClk = GPIO_DT_SPEC_GET(PIN_CLK_NODE, gpios);
//static const struct gpio_dt_spec pCs = GPIO_DT_SPEC_GET(PIN_CS_NODE, gpios);

// SPI master functionality
const struct device *spi_dev;
static struct k_poll_signal spi_done_sig = K_POLL_SIGNAL_INITIALIZER(spi_done_sig);

struct spi_cs_control spim_cs = {
	.gpio = SPI_CS_GPIOS_DT_SPEC_GET(DT_NODELABEL(reg_my_spi_master)),
	.delay = 0,
};

static const struct spi_config spi_cfg = {
	.operation = SPI_WORD_SET(8) | SPI_TRANSFER_MSB |
				 SPI_MODE_CPOL | SPI_MODE_CPHA,
	.frequency = 4000000,
	.slave = 0,
	.cs = &spim_cs,
};

static uint8_t tx_buffer[2];

const struct spi_buf tx_buf = {
	.buf = tx_buffer,
	.len = sizeof(tx_buffer)
};
const struct spi_buf_set tx = {
	.buffers = &tx_buf,
	.count = 1
};


void spi_write_cmd(uint8_t cmd)
{
    tx_buffer[0] = cmd;
	//gpio_pin_set_dt(&spim_cs.gpio, 0);
	gpio_pin_set_dt(&pDc, 0);
    
	int error = spi_write(spi_dev, &spi_cfg, &tx);
	if(error != 0){
		printk("SPI write error: %i\n", error);
	}
    //gpio_pin_set_dt(&spim_cs.gpio, 1);
}

void spi_write_data(uint8_t data)
{
	tx_buffer[0] = data;
	//gpio_pin_set_dt(&spim_cs.gpio, 0);
	gpio_pin_set_dt(&pDc, 1);
	int error = spi_write(spi_dev, &spi_cfg, &tx);
	if(error != 0){
		printk("SPI write error: %i\n", error);
	}
    //gpio_pin_set_dt(&spim_cs.gpio, 1);
}


void set_ramMP()
{
	spi_write_cmd(0x11);
	spi_write_data(0x05);
	spi_write_cmd(0x44);
	spi_write_data(0x00);
	spi_write_data(0x31);
	spi_write_cmd(0x45);
	spi_write_data(0x0f);
	spi_write_data(0x01);
	spi_write_data(0x00);
	spi_write_data(0x00);
}

void set_ramMA()
{
	spi_write_cmd(0x4E);
	spi_write_data(0x00);
	spi_write_cmd(0x4f);
	spi_write_data(0x0f);
	spi_write_data(0x01);
}

void set_ramSP(void)  // Set RAM X - address Start / End position  ; Set RAM Y - address Start / End position  -SLAVE
{
  spi_write_cmd(0x91);              
  spi_write_data(0x04); 
  spi_write_cmd(0xc4);              // Set Ram X- address Start / End position
  spi_write_data(0x30);   //392/8-1               // XStart, POR = 00h
  spi_write_data(0x00); 
  spi_write_cmd(0xc5);                   // Set Ram Y- address  Start / End position 
  spi_write_data(0x0f);  
  spi_write_data(0x01);    
  spi_write_data(0x00);                      // YEnd L
  spi_write_data(0x00);                      // YEnd H   
}

void set_ramSA(void)   // Set RAM X address counter ;  Set RAM Y address counter                             -SLAVE
{
  spi_write_cmd(0xce);              
  spi_write_data(0x31); 
  spi_write_cmd(0xcf);  
  spi_write_data(0x0f);  
  spi_write_data(0x01);  
}


void EPD_Update(void)
{   
  spi_write_cmd(0x22); //Display Update Control
  spi_write_data(0xF7);   
  spi_write_cmd(0x20); //Activate Display Update Sequence
  while(1 == gpio_pin_get_dt(&pBusy)){};
}

void EPD_DeepSleep(void)
{   
  spi_write_cmd(0x10); //enter deep sleep
  spi_write_data(0x01); 
  k_msleep(100);
}

void EPD_HW_INIT(void)
{   
	printk("Display Init!\n");
	gpio_pin_set_dt(&pReset, 0);
	k_msleep(100);
	gpio_pin_set_dt(&pReset, 1);
	k_msleep(100);
	while(1 == gpio_pin_get_dt(&pBusy)){};
	spi_write_cmd(0x12); // SWRESET
	while(1 == gpio_pin_get_dt(&pBusy)){};
    printk("Display Init DONE!\n");
}

int main(void)
{
	spi_dev = DEVICE_DT_GET(MY_SPI_MASTER);
	if(!device_is_ready(spi_dev)) {
		printk("SPI master device not ready!\n");
	}
	if(!device_is_ready(spim_cs.gpio.port)){
		printk("SPI master chip select device not ready!\n");
	}

	if (!device_is_ready(pBusy.port) | !device_is_ready(pReset.port) | !device_is_ready(pDc.port)) {
		printk("Pins not ready!\n");
	}

	 // Configure GPIO pins
    int ret_busy = gpio_pin_configure_dt(&pBusy, GPIO_INPUT);
    int ret_reset = gpio_pin_configure_dt(&pReset, GPIO_OUTPUT);
    int ret_dc = gpio_pin_configure_dt(&pDc, GPIO_OUTPUT);
	//int ret_cs = gpio_pin_configure_dt(&pCs, GPIO_OUTPUT);
	//int ret_clk = gpio_pin_configure_dt(&pClk, GPIO_OUTPUT);
	//int ret_mosi = gpio_pin_configure_dt(&pMosi, GPIO_OUTPUT);

  	if (ret_busy < 0 || ret_reset < 0 || ret_dc < 0) {
        printk("Error configuring GPIO pins: %d %d %d \n", ret_busy, ret_reset, ret_dc); 
        return -1;
    }
	else
	{
		 printk("Configuring GPIO pins OK: %d %d %d\n", ret_busy, ret_reset, ret_dc);
	}

	while (1) 
    {
        //Init
        EPD_HW_INIT();

        k_msleep(1000);

        //EPD_WhiteScreen_White
        printk("EPD_WhiteScreen_White!\n");

        set_ramMP();
        set_ramMA();

        spi_write_cmd(0x24);   
        for(uint32_t i = 0 ; i < Source_BYTES*Gate_BITS ; i++)
        {
            spi_write_data(0xff);
        }

        set_ramMA();
        spi_write_cmd(0x26);   
        for(uint32_t i = 0 ; i < Source_BYTES*Gate_BITS ; i++)
        {
            spi_write_data(0x00);
        }

        set_ramSP();
        set_ramSA();
        spi_write_cmd(0xA4);  
        for(uint32_t i = 0 ; i < Source_BYTES*Gate_BITS ; i++)
        {
            spi_write_data(0xFF);
        } 

        set_ramSA();
        spi_write_cmd(0xA6);   
        for(uint32_t i = 0 ; i < Source_BYTES*Gate_BITS ; i++)
        {
            spi_write_data(0x00);
        }

        EPD_Update();
        EPD_DeepSleep();

        printk("EPD_WhiteScreen_White DONE!\n");
        
        k_msleep(5000);
    }

}

Parents Reply Children
  • E-paper display at 4MHz with connections that are not vanishingly short (ie always) require high-drive outputs, aka H0H1 as opposed to the default S0S1; these high-drive output modes are not available on all pins on some parts. The Overlay above has this set in one section but not the SPI definition; suggest try adding this line:

        spi_master_default: spi_master_default {
            group1 {
                psels = <NRF_PSEL(SPIM_SCK, 0, 26)>,
                        <NRF_PSEL(SPIM_MOSI, 0, 27)>,
                        <NRF_PSEL(SPIM_MISO, 0, 29)>;
                        nordic,drive-mode = <NRF_DRIVE_H0H1>; // <<== require high-drive
            };
        };

    Depending on package some pins may (will) affect the radio if used for high-speed signals; on a module these typically are P0.27 to P0.31 and so are not the best choice for SPI.

    "4.3.1 GPIO located near the radio
    Radio performance parameters, such as sensitivity, may be affected by high frequency digital I/O with large sink/source current close to the Radio power supply and antenna pins.
    Table 5: GPIO recommended usage for QFN48 package on page 18 and Table 6: GPIO recommended usage for WLCSP package on page 18 identify some GPIO that have recommended usage guidelines to maximize radio performance in an application."

  • Thanks for that info. I managed to get some feedback from the display using the default DeviceTree for DK and this setup:

    #define GIPO0_NODE DT_NODELABEL(gpio0)
    static const struct device *gpio0_dev = DEVICE_DT_GET(GIPO0_NODE);
    
    #define GPIO_CS 19
    #define GPIO_DC 18
    #define GPIO_RESET 17
    #define GPIO_BUSY 16
    
    #define SPI_NODE DT_NODELABEL(spi1)
    const struct device *spi_dev = DEVICE_DT_GET(SPI_NODE);
    static struct k_poll_signal spi_done_sig = K_POLL_SIGNAL_INITIALIZER(spi_done_sig);
    
    struct spi_cs_control spim_cs = {
        .gpio = SPI_CS_GPIOS_DT_SPEC_GET(DT_NODELABEL(reg_my_spi_master)),
        .delay = 0,
    };
    
    static const struct spi_config spi_cfg = {
        .operation = SPI_WORD_SET(8) | SPI_TRANSFER_MSB |
                    SPI_MODE_CPOL | SPI_MODE_CPHA,
        .frequency = 4000000,
        .slave = 0,
        //.cs = &spim_cs,
    };
        // Configure GPIO pins
        int ret_cs = gpio_pin_configure(gpio0_dev, GPIO_CS, GPIO_OUTPUT | GPIO_INPUT);
        int ret_dc = gpio_pin_configure(gpio0_dev, GPIO_DC, GPIO_OUTPUT | GPIO_INPUT);
        int ret_reset = gpio_pin_configure(gpio0_dev, GPIO_RESET, GPIO_OUTPUT | GPIO_INPUT );
        int ret_busy = gpio_pin_configure(gpio0_dev, GPIO_BUSY, GPIO_OUTPUT | GPIO_INPUT);

    Btw, I tried to simply set GPIO_CS as pin 0 or 1 (so P0.00 and P0.01), but they didn't work at all with gpio_pin_set. Verified it with a simple LED on that pin. Why do the pins 16-19 work but others not?

  • P0.00 and P0.01 may be configured as external crystal pins; worth checking all the pins after they have been initialsed in case something overrides the expected result. After the inits call showPins():

    // Some port pins (P1) are only available on nRF52840
    #if defined(NRF52840_XXAA) || defined(NRF52840_XXAB)
    #define NUMBER_PORT_PINS 48
    #else
    #define NUMBER_PORT_PINS 32
    #endif
    
    const char * FindPinMap(const uint32_t PinId);
    
    void showPins(void)
    {
       uint8_t InfoPacket[80] = "";
       for (uint32_t PinId=0; PinId<NUMBER_PORT_PINS; PinId++)
       {
            // Show any mapped peripheral for this port pin
            snprintf(InfoPacket, sizeof(InfoPacket)-1, "P%u.%02u Map: %s", (PinId & 0x20)>>5, PinId&0x1F, FindPinMap(PinId));
            NRF_LOG_INFO("%s", InfoPacket);
            NRF_LOG_FLUSH();
            //uartSend(InfoPacket, strlen(InfoPacket));
       }
    }
    
    const char * FindPinMap(const uint32_t PinId)
    {
        // 32kHz Osc pins can be used as GPIOs or 32kHz Oscillator, special case
        if ( (PinId == PIN_XL2) && (NRF_CLOCK->LFCLKSRC & 0x000001) ) return "RTC-OSC-OUT";
        if ( (PinId == PIN_XL1) && (NRF_CLOCK->LFCLKSRC & 0x000001) ) return "RTC-OSC-IN";
    
        // NFC pins can be used as GPIOs or NFC, special case
        if ( (PinId == PIN_NFC1) && ((NRF_UICR->NFCPINS & UICR_NFCPINS_PROTECT_Msk) == (UICR_NFCPINS_PROTECT_NFC << UICR_NFCPINS_PROTECT_Pos)) ) return "NFC 1";
        if ( (PinId == PIN_NFC2) && ((NRF_UICR->NFCPINS & UICR_NFCPINS_PROTECT_Msk) == (UICR_NFCPINS_PROTECT_NFC << UICR_NFCPINS_PROTECT_Pos)) ) return "NFC 2";
    
        // Reset pin can be used as GPIOs or nReset, special case
        if ((PinId == PIN_NRESET)                          // 21
         && ((NRF_UICR->PSELRESET[0] & UICR_PSELRESET_CONNECT_Msk) == (UICR_PSELRESET_CONNECT_Connected << UICR_PSELRESET_CONNECT_Pos))
         && ((NRF_UICR->PSELRESET[1] & UICR_PSELRESET_CONNECT_Msk) == (UICR_PSELRESET_CONNECT_Connected << UICR_PSELRESET_CONNECT_Pos))
         &&  (NRF_UICR->PSELRESET[0] == PIN_NRESET)
         &&  (NRF_UICR->PSELRESET[1] == PIN_NRESET) ) return "nRESET";
    
    #if defined(TRACE_D0_PIN) && defined(TRACE_D1_PIN) && defined(TRACE_D2_PIN) && defined(TRACE_D3_PIN) && defined(TRACE_CLK_PIN)
        // TRACE pins can be used as GPIOs or Trace, special case
        if ( (CoreDebug->DEMCR & CoreDebug_DEMCR_TRCENA_Msk) == CoreDebug_DEMCR_TRCENA_Msk )
        {
            if (PinId == TRACE_D0_PIN)  return "TRACE D0";  // 18
            if (PinId == TRACE_D1_PIN)  return "TRACE D1";  // 16
            if (PinId == TRACE_D2_PIN)  return "TRACE D2";  // 15
            if (PinId == TRACE_D3_PIN)  return "TRACE D3";  // 14
            if (PinId == TRACE_CLK_PIN) return "TRACE CLK"; // 20
        }
    #endif
    
    /*
     * Table 20: Peripherals sharing an ID
     *  ===================================
     *  Instance
     *  ID  3 (0x40003000) SPIM SPIS SPI TWIM TWIS TWI
     *  ID  4 (0x40004000) SPIM SPIS SPI TWIM TWIS TWI
     *  ID 35 (0x40023000) SPIM SPIS SPI
     */
    STATIC_ASSERT ((uint32_t)NRF_TWIM0 == (uint32_t)NRF_SPI0, "NRF_TWIM0 differs from NRF_SPI0");
    STATIC_ASSERT ((uint32_t)NRF_TWIM1 == (uint32_t)NRF_SPI1, "NRF_TWIM1 differs from NRF_SPI1");
    
        if (NRF_TWIM0->PSEL.SCL  == PinId) return "SP-TWI0 SCL";
        if (NRF_TWIM0->PSEL.SDA  == PinId) return "SP-TWI0 SDA";
    
        if (NRF_TWIM1->PSEL.SCL  == PinId) return "SP-TWI1 SCL";
        if (NRF_TWIM1->PSEL.SDA  == PinId) return "SP-TWI1 SDA";
    
        if (NRF_SPI0->PSEL.SCK  == PinId) return "SPI0 SCK";
        if (NRF_SPI0->PSEL.MOSI == PinId) return "SPI0 MOSI";
        if (NRF_SPI0->PSEL.MISO == PinId) return "SPI0 MISO";
    
        if (NRF_SPI1->PSEL.SCK  == PinId) return "SPI1 SCK";
        if (NRF_SPI1->PSEL.MOSI == PinId) return "SPI1 MOSI";
        if (NRF_SPI1->PSEL.MISO == PinId) return "SPI1 MISO";
    
        if (NRF_SPI2->PSEL.SCK  == PinId) return "SPI2 SCK";
        if (NRF_SPI2->PSEL.MOSI == PinId) return "SPI2 MOSI";
        if (NRF_SPI2->PSEL.MISO == PinId) return "SPI2 MISO";
    
        if (NRF_UARTE0->PSEL.RTS == PinId) return "UartE0 RTS";
        if (NRF_UARTE0->PSEL.CTS == PinId) return "UartE0 CTS";
        if (NRF_UARTE0->PSEL.TXD == PinId) return "UartE0 Tx";
        if (NRF_UARTE0->PSEL.RXD == PinId) return "UartE0 Rx";
    
        // Analog positive and negative input channels have strange numbering:
        // PSELP_NC           0UL  Not connected
        // PSELP_AnalogInput0 1UL  AIN0
        // PSELP_AnalogInput1 2UL  AIN1
        // PSELP_AnalogInput2 3UL  AIN2
        // PSELP_AnalogInput3 4UL  AIN3
        // PSELP_AnalogInput4 5UL  AIN4
        // PSELP_AnalogInput5 6UL  AIN5
        // PSELP_AnalogInput6 7UL  AIN6
        // PSELP_AnalogInput7 8UL  AIN7
        // PSELP_VDD          9UL  VDD
        if (((PinId >= 2) && (PinId <= 5)) || ((PinId >= 28) && (PinId <= 31)))
        {
          uint32_t PinIdA = 0;
          if ((PinId >= 2) && (PinId <= 5))
          {
            PinIdA = PinId-1;
          }
          else if ((PinId >= 28) && (PinId <= 31))
          {
            PinIdA = 4+(PinId-28);
          }
          if (NRF_SAADC->CH[0].PSELP == PinIdA) return "ADC 0+";
          if (NRF_SAADC->CH[0].PSELN == PinIdA) return "ADC 0-";
          if (NRF_SAADC->CH[1].PSELP == PinIdA) return "ADC 1+";
          if (NRF_SAADC->CH[1].PSELN == PinIdA) return "ADC 1-";
          if (NRF_SAADC->CH[2].PSELP == PinIdA) return "ADC 2+";
          if (NRF_SAADC->CH[2].PSELN == PinIdA) return "ADC 2-";
          if (NRF_SAADC->CH[3].PSELP == PinIdA) return "ADC 3+";
          if (NRF_SAADC->CH[3].PSELN == PinIdA) return "ADC 3-";
          if (NRF_SAADC->CH[4].PSELP == PinIdA) return "ADC 4+";
          if (NRF_SAADC->CH[4].PSELN == PinIdA) return "ADC 4-";
          if (NRF_SAADC->CH[5].PSELP == PinIdA) return "ADC 5+";
          if (NRF_SAADC->CH[5].PSELN == PinIdA) return "ADC 5-";
          if (NRF_SAADC->CH[6].PSELP == PinIdA) return "ADC 6+";
          if (NRF_SAADC->CH[6].PSELN == PinIdA) return "ADC 6-";
          if (NRF_SAADC->CH[7].PSELP == PinIdA) return "ADC 7+";
          if (NRF_SAADC->CH[7].PSELN == PinIdA) return "ADC 7-";
        }
    
        if (NRF_LPCOMP->PSEL      == PinId) return "LP COMP";
        if (NRF_LPCOMP->EXTREFSEL == PinId) return "LP COMP REF";
    
        if (NRF_COMP->PSEL      == PinId) return "COMP";
        if (NRF_COMP->EXTREFSEL == PinId) return "COMP REF";
    
        if (NRF_QDEC->PSEL.LED == PinId) return "QDEC LED";
        if (NRF_QDEC->PSEL.A   == PinId) return "QDEC A";
        if (NRF_QDEC->PSEL.B   == PinId) return "QDEC B";
    
        if (NRF_PDM->PSEL.CLK == PinId) return "PDM CLK";
        if (NRF_PDM->PSEL.DIN == PinId) return "PDM DIN";
    
        if (NRF_PWM0->PSEL.OUT[0] == PinId) return "PWM0 0";
        if (NRF_PWM0->PSEL.OUT[1] == PinId) return "PWM0 1";
        if (NRF_PWM0->PSEL.OUT[2] == PinId) return "PWM0 2";
        if (NRF_PWM0->PSEL.OUT[3] == PinId) return "PWM0 3";
    
        if (NRF_PWM1->PSEL.OUT[0] == PinId) return "PWM1 0";
        if (NRF_PWM1->PSEL.OUT[1] == PinId) return "PWM1 1";
        if (NRF_PWM1->PSEL.OUT[2] == PinId) return "PWM1 2";
        if (NRF_PWM1->PSEL.OUT[3] == PinId) return "PWM1 3";
    
        if (NRF_PWM2->PSEL.OUT[0] == PinId) return "PWM2 0";
        if (NRF_PWM2->PSEL.OUT[1] == PinId) return "PWM2 1";
        if (NRF_PWM2->PSEL.OUT[2] == PinId) return "PWM2 2";
        if (NRF_PWM2->PSEL.OUT[3] == PinId) return "PWM2 3";
    
        if (NRF_I2S->PSEL.SDOUT == PinId) return "I2S SDOUT";
        if (NRF_I2S->PSEL.SDIN  == PinId) return "I2S SDIN";
        if (NRF_I2S->PSEL.SCK   == PinId) return "I2S SCK";
        if (NRF_I2S->PSEL.MCK   == PinId) return "I2S MCK";
        if (NRF_I2S->PSEL.LRCK  == PinId) return "I2S LRCK";
    
        if (NRF_PDM->PSEL.DIN   == PinId) return "PDM DIN";
        if (NRF_PDM->PSEL.CLK   == PinId) return "PDM CLK";
    
    // Some peripherals are only available on nRF52840
    #if defined(NRF52840_XXAA) || defined(NRF52840_XXAB)
        if (NRF_SPIM3->PSEL.SCK  == PinId) return "SPI3 SCK";
        if (NRF_SPIM3->PSEL.MOSI == PinId) return "SPI3 MOSI";
        if (NRF_SPIM3->PSEL.MISO == PinId) return "SPI3 MISO";
        if (NRF_SPIM3->PSEL.CSN  == PinId) return "SPI3 CSN";
        if (NRF_SPIM3->PSELDCX   == PinId) return "SPI3 DCX";
    
        if (NRF_UARTE1->PSEL.RTS == PinId) return "UartE1 RTS";
        if (NRF_UARTE1->PSEL.CTS == PinId) return "UartE1 CTS";
        if (NRF_UARTE1->PSEL.TXD == PinId) return "UartE1 Tx";
        if (NRF_UARTE1->PSEL.RXD == PinId) return "UartE1 Rx";
    
        if (NRF_PWM3->PSEL.OUT[0] == PinId) return "PWM3 0";
        if (NRF_PWM3->PSEL.OUT[1] == PinId) return "PWM3 1";
        if (NRF_PWM3->PSEL.OUT[2] == PinId) return "PWM3 2";
        if (NRF_PWM3->PSEL.OUT[3] == PinId) return "PWM3 3";
    #endif
    
        if ((NRF_GPIOTE->CONFIG[0] & GPIOTE_CONFIG_PSEL_Msk) == (PinId << GPIOTE_CONFIG_PSEL_Pos)) return "GPIOTE 0";
        if ((NRF_GPIOTE->CONFIG[1] & GPIOTE_CONFIG_PSEL_Msk) == (PinId << GPIOTE_CONFIG_PSEL_Pos)) return "GPIOTE 1";
        if ((NRF_GPIOTE->CONFIG[2] & GPIOTE_CONFIG_PSEL_Msk) == (PinId << GPIOTE_CONFIG_PSEL_Pos)) return "GPIOTE 2";
        if ((NRF_GPIOTE->CONFIG[3] & GPIOTE_CONFIG_PSEL_Msk) == (PinId << GPIOTE_CONFIG_PSEL_Pos)) return "GPIOTE 3";
        if ((NRF_GPIOTE->CONFIG[4] & GPIOTE_CONFIG_PSEL_Msk) == (PinId << GPIOTE_CONFIG_PSEL_Pos)) return "GPIOTE 4";
        if ((NRF_GPIOTE->CONFIG[5] & GPIOTE_CONFIG_PSEL_Msk) == (PinId << GPIOTE_CONFIG_PSEL_Pos)) return "GPIOTE 5";
        if ((NRF_GPIOTE->CONFIG[6] & GPIOTE_CONFIG_PSEL_Msk) == (PinId << GPIOTE_CONFIG_PSEL_Pos)) return "GPIOTE 6";
        if ((NRF_GPIOTE->CONFIG[7] & GPIOTE_CONFIG_PSEL_Msk) == (PinId << GPIOTE_CONFIG_PSEL_Pos)) return "GPIOTE 7";
    
        // No hardware pins available with these peripherals:
        // NRF_TIMER0                      ((NRF_TIMER_Type          *) NRF_TIMER0_BASE)
        // NRF_TIMER1                      ((NRF_TIMER_Type          *) NRF_TIMER1_BASE)
        // NRF_TIMER2                      ((NRF_TIMER_Type          *) NRF_TIMER2_BASE)
        // NRF_RTC0                        ((NRF_RTC_Type            *) NRF_RTC0_BASE)
        // NRF_RTC1                        ((NRF_RTC_Type            *) NRF_RTC1_BASE)
        // NRF_RTC2                        ((NRF_RTC_Type            *) NRF_RTC2_BASE)
    
        // Default, no map found
        return "-";
    }

    Sample output (example for nRF52840; also works on the nRF52832 etc):

    <info> app: P0.00 Map: RTC-OSC-IN
    <info> app: P0.01 Map: RTC-OSC-OUT
    <info> app: P0.02 Map: -
    <info> app: P0.03 Map: -
    <info> app: P0.04 Map: -
    <info> app: P0.05 Map: -
    <info> app: P0.06 Map: -
    <info> app: P0.07 Map: TRACE CLK
    <info> app: P0.08 Map: -
    <info> app: P0.09 Map: -
    <info> app: P0.10 Map: -
    <info> app: P0.11 Map: TRACE D2
    <info> app: P0.12 Map: TRACE D1
    <info> app: P0.13 Map: -
    <info> app: P0.14 Map: -
    <info> app: P0.15 Map: -
    <info> app: P0.16 Map: -
    <info> app: P0.17 Map: -
    <info> app: P0.18 Map: nRESET
    <info> app: P0.19 Map: -
    <info> app: P0.20 Map: -
    <info> app: P0.21 Map: -
    <info> app: P0.22 Map: -
    <info> app: P0.23 Map: -
    <info> app: P0.24 Map: UartE0 Rx
    <info> app: P0.25 Map: UartE0 Tx
    <info> app: P0.26 Map: -
    <info> app: P0.27 Map: -
    <info> app: P0.28 Map: -
    <info> app: P0.29 Map: -
    <info> app: P0.30 Map: -
    <info> app: P0.31 Map: -
    <info> app: P1.00 Map: TRACE D0
    <info> app: P1.01 Map: -
    <info> app: P1.02 Map: -
    <info> app: P1.03 Map: -
    <info> app: P1.04 Map: -
    <info> app: P1.05 Map: -
    <info> app: P1.06 Map: -
    <info> app: P1.07 Map: -
    <info> app: P1.08 Map: -
    <info> app: P1.09 Map: TRACE D3
    <info> app: P1.10 Map: PWM0 1
    <info> app: P1.11 Map: -
    <info> app: P1.12 Map: -
    <info> app: P1.13 Map: -
    <info> app: P1.14 Map: -
    <info> app: P1.15 Map: -

Related