Files
Embedded-Hacking/RA-0x0001-GPIO-Output
2025-10-05 12:00:31 -07:00
..
2025-10-05 12:00:31 -07:00
2025-10-05 12:00:31 -07:00
2025-10-05 12:00:31 -07:00
2025-10-05 08:30:01 -07:00
2025-10-05 12:00:31 -07:00
2025-10-05 12:00:31 -07:00
2025-10-05 08:30:01 -07:00
2025-10-05 08:30:01 -07:00

FREE Reverse Engineering Self-Study Course HERE


RP2350 Blink Driver

An RP2350 blink driver written entirely in Assembler.

Code

/**
 * FILE: main.s
 *
 * DESCRIPTION:
 * RP2350 Bare-Metal GPIO16 Blink, Coprocessor Version.
 * Minimal baremetal LED blink on the RP2350 using direct coprocessor
 * (MCRR) instructions to manipulate GPIO control registers. This bypasses
 * SDK abstractions and demonstrates registerlevel control in assembler.
 *
 * AUTHOR: Kevin Thomas
 * CREATION DATE: October 5, 2025
 * UPDATE DATE: October 5, 2025
 */

.syntax unified                                       // use unified assembly syntax
.cpu cortex-m33                                       // target Cortex-M33 core
.thumb                                                // use Thumb instruction set

/**
 * Memory addresses and constants.
 */
.equ IO_BANK0_BASE,   0x40028000                      // base address of IO_BANK0
.equ PADS_BANK0_BASE, 0x40038000                      // base address of PADS_BANK0
.equ SIO_BASE,        0xD0000000                      // base address of SIO block
.equ GPIO16_CTRL,     0x84                            // io[16].ctrl offset
.equ GPIO16_PAD,      0x44                            // pads io[16] offset
.equ GPIO16_BIT,      (1<<16)                         // bit mask for GPIO16
.equ GPIO_OUT_SET,    0x18                            // SIO->GPIO_OUT_SET offset
.equ GPIO_OUT_XOR,    0x28                            // SIO->GPIO_OUT_XOR offset
.equ GPIO_OE_SET,     0x38                            // SIO->GPIO_OE_SET offset
.equ STACK_TOP,       0x20082000                      // top of non-secure SRAM
.equ STACK_LIMIT,     0x2007A000                      // stack limit (32 KB below top)

/**
 * Initialize the .vectors section. The .vectors section contains vector
 * table.
 */
.section .vectors, "ax"                               // vector table section
.align 2                                              // align to 4-byte boundary

/**
 * Vector table section.
 */
.global _vectors                                      // export symbol
_vectors:
  .word STACK_TOP                                     // initial stack pointer
  .word Reset_Handler + 1                             // reset handler (Thumb bit set)

/**
 * @brief   Reset handler for RP2350.
 *
 * @details Entry point after reset. Performs:
 *          - Stack initialization
 *          - Coprocessor enable
 *          - GPIO16 pad/function configuration
 *          - Branches to main() which contains the blink loop
 *
 * @param   None
 * @retval  None
 */
.global Reset_Handler                                 // export Reset_Handler
.type Reset_Handler, %function                        // mark as function
Reset_Handler:
  BL    Init_Stack                                    // initialize MSP/PSP and limits
  BL    Enable_Coprocessor                            // enable CP0 in CPACR for MCRR
  B     main                                          // branch to main loop
.size Reset_Handler, . - Reset_Handler

/**
 * @brief   Initialize stack pointers.
 *
 * @details Sets Main and Process Stack Pointers (MSP/PSP) and their limits.
 *
 * @param   None
 * @retval  None
 */
.type Init_Stack, %function
Init_Stack:
  LDR   R0, =STACK_TOP                                // load stack top
  MSR   PSP, R0                                       // set PSP
  LDR   R0, =STACK_LIMIT                              // load stack limit
  MSR   MSPLIM, R0                                    // set MSP limit
  MSR   PSPLIM, R0                                    // set PSP limit
  LDR   R0, =STACK_TOP                                // reload stack top
  MSR   MSP, R0                                       // set MSP
  BX    LR                                            // return

/**
 * @brief   Enable coprocessor access.
 *
 * @details Grants full access to coprocessor 0 (CP0) via CPACR.
 *
 * @param   None
 * @retval  None
 */
.type Enable_Coprocessor , %function
Enable_Coprocessor:
  LDR   R0, =0xE000ED88                               // CPACR address
  LDR   R1, [R0]                                      // read CPACR
  ORR   R1, R1, #0x3                                  // set CP0 full access
  STR   R1, [R0]                                      // write CPACR
  DSB                                                 // data sync barrier
  ISB                                                 // instruction sync barrier
  BX    LR                                            // return

/**
 * Initialize the .text section. 
 * The .text section contains executable code.
 */
.section .text                                        // code section
.align 2                                              // align to 4-byte boundary

/**
 * @brief   Main application entry point.
 *
 * @details Implements the infinite blink loop:
 *          - Set GPIO16 high
 *          - Delay ~500 ms
 *          - Set GPIO16 low
 *          - Delay ~500 ms
 *          - Repeat forever
 *
 * @param   None
 * @retval  None
 */
.global main                                          // export main
.type main, %function                                 // mark as function
main:
.Push_Registers:
  PUSH  {R4-R12, LR}                                  // push registers R4-R12, LR to the stack
.GPIO16_Config:
  BL    GPIO16_Config                                 // configure pads and FUNCSEL for GPIO16
.Loop:
  BL    GPIO16_Set                                    // set GPIO16 high
  BL    Delay_500ms                                   // ~500 ms delay
  BL    GPIO16_Clear                                  // set GPIO16 low
  BL    Delay_500ms                                   // ~500 ms delay
  B     Loop                                          // loop forever
.Pop_Registers:
  POP   {R4-R12, LR}                                  // pop registers R4-R12, LR from the stack
  BX    LR                                            // return to caller

/**
 * @brief   Configure GPIO16 for SIO control.
 *
 * @details Sets pad control (IE, OD, ISO) and FUNCSEL = 5 (SIO). Enables OE.
 *
 * @param   None
 * @retval  None
 */
.type GPIO16_Config, %function
GPIO16_Config:
.GPIO16_Config_Push_Registers:
  PUSH  {R4-R12, LR}                                  // push registers R4-R12, LR to the stack
.GPIO16_Config_Modify_Pad:
  LDR   R3, =PADS_BANK0_BASE + GPIO16_PAD             // pad control address
  LDR   R2, [R3]                                      // read pad config
  BIC   R2, R2, #0x80                                 // clear OD
  ORR   R2, R2, #0x40                                 // set IE
  BIC   R2, R2, #0x100                                // clear ISO
  STR   R2, [R3]                                      // write pad config
.GPIO16_Config_Modify_IO:
  LDR   R3, =IO_BANK0_BASE + GPIO16_CTRL              // IO control address
  LDR   R2, [R3]                                      // read IO config
  BIC   R2, R2, #0x1F                                 // clear FUNCSEL
  ORR   R2, R2, #5                                    // set FUNCSEL=5
  STR   R2, [R3]                                      // write IO config
.GPIO16_Config_Enable_OE:
  MOVS  R4, #16                                       // GPIO number
  MOVS  R5, #1                                        // enable output
  MCRR  p0, #4, R4, R5, c4                            // gpioc_bit_oe_put(16, 1)
.GPIO16_Config_Pop_Registers:
  POP   {R4-R12, LR}                                  // pop registers R4-R12, LR from the stack
  BX    LR                                            // return

/**
 * @brief   Set GPIO16 high.
 *
 * @details Drives GPIO16 output = 1 via coprocessor MCRR.
 *
 * @param   None
 * @retval  None
 */
.type GPIO16_Set, %function
GPIO16_Set:
.GPIO16_Set_Push_Registers:
  PUSH  {R4-R12, LR}                                  // push registers R4-R12, LR to the stack
.GPIO16_Set_Load_Operands:
  MOVS  R4, #16                                       // GPIO number
  MOVS  R5, #1                                        // logic high
.GPIO16_Set_Execute:
  MCRR  p0, #4, R4, R5, c0                            // gpioc_bit_out_put(16, 1)
.GPIO16_Set_Pop_Registers:
  POP   {R4-R12, LR}                                  // pop registers R4-R12, LR from the stack
  BX    LR                                            // return to caller

/**
 * @brief   Clear GPIO16 (set low).
 *
 * @details Drives GPIO16 output = 0 via coprocessor MCRR.
 *
 * @param   None
 * @retval  None
 */
.type GPIO16_Clear, %function
GPIO16_Clear:
.GPIO16_Clear_Push_Registers:
  PUSH  {R4-R12, LR}                                  // push registers R4-R12, LR to the stack
.GPIO16_Clear_Load_Operands:
  MOVS  R4, #16                                       // GPIO number
  MOVS  R5, #0                                        // logic low
.GPIO16_Clear_Execute:
  MCRR  p0, #4, R4, R5, c0                            // gpioc_bit_out_put(16, 0)
.GPIO16_Clear_Pop_Registers:
  POP   {R4-R12, LR}                                  // pop registers R4-R12, LR from the stack
  BX    LR                                            // return to caller

/**
 * @brief   Busywait Delay_500ms loop.
 *
 * @details Consumes ~2,000,000 cycles to approximate ~500 ms at boot clock.
 *
 * @param   None
 * @retval  None
 */
.type Delay_500ms, %function
Delay_500ms:
.Delay_500ms_Push_Registers:
  PUSH  {R4-R12, LR}                                  // push registers R4-R12, LR to the stack
.Delay_500ms_Setup:
  LDR   R2, =2000000                                  // loop count (~500 ms)
.Delay_500ms_Loop:
  SUBS  R2, R2, #1                                    // decrement counter
  BNE   .Delay_500ms_Loop                             // branch until zero
.Delay_500ms_Pop_Registers:
  POP   {R4-R12, LR}                                  // pop registers R4-R12, LR from the stack
  BX    LR                                            // return to caller

/**
 * Test data and constants.
 * The .rodata section is used for constants and static data.
 */
.section .rodata                                      // read-only data section

/**
 * Initialized global data.
 * The .data section is used for initialized global or static variables.
 */
.section .data                                        // data section

/**
 * Uninitialized global data.
 * The .bss section is used for uninitialized global or static variables.
 */
.section .bss                                         // BSS section

License

Apache License 2.0