Added bare-metal blinky

This commit is contained in:
Kevin Thomas
2025-10-05 12:00:31 -07:00
parent 4ea719864f
commit d573bdfcba
23 changed files with 733 additions and 1297 deletions
+258 -591
View File
@@ -1,605 +1,272 @@
# RP2350 GPIO Coprocessor Blink - RAW Assembler, NO SDK
<img src="https://github.com/mytechnotalent/RP2350_Blink_Driver/blob/main/RP2350_Blink_Driver.png?raw=true">
## Overview
## FREE Reverse Engineering Self-Study Course [HERE](https://github.com/mytechnotalent/Reverse-Engineering-Tutorial)
This is a **bare-metal assembly program** for the **Raspberry Pi RP2350** microcontroller that demonstrates the use of the **ARM Cortex-M33 coprocessor interface** to control GPIO pins. Unlike traditional GPIO control using direct memory-mapped register writes, this version uses the RP2350's **hardware GPIO coprocessor (CP0)** with **MCRR (Move to Coprocessor from two ARM Registers)** instructions.
<br>
The program blinks an LED connected to **GPIO16** with a 500ms on/off cycle using the specialized coprocessor instructions for high-performance GPIO operations.
# RP2350 Blink Driver
An RP2350 blink driver written entirely in Assembler.
---
# Code
```assembler
/**
* 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
*/
## What Makes This Special?
.syntax unified // use unified assembly syntax
.cpu cortex-m33 // target Cortex-M33 core
.thumb // use Thumb instruction set
### Traditional GPIO Control vs Coprocessor Control
/**
* 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)
**Traditional Method (Direct Register Access):**
```assembly
LDR R0, =SIO_BASE ; Load SIO base address
LDR R1, =GPIO16_BIT ; Load GPIO bit mask
STR R1, [R0, #GPIO_OUT_SET] ; Write to memory-mapped register
```
/**
* Initialize the .vectors section. The .vectors section contains vector
* table.
*/
.section .vectors, "ax" // vector table section
.align 2 // align to 4-byte boundary
**Coprocessor Method (This Implementation):**
```assembly
MOVS R4, #16 ; GPIO pin number
MOVS R5, #1 ; Value to write
MCRR p0, #4, R4, R5, c0 ; Single coprocessor instruction
```
### Advantages of the Coprocessor Approach:
1. **Faster execution** - Single instruction instead of multiple memory operations
2. **More atomic** - Less chance of race conditions
3. **Hardware-accelerated** - Direct hardware interface
4. **Cleaner code** - No need to manage bit masks and base addresses
5. **RP2350-specific** - Takes advantage of advanced features
---
## Hardware Requirements
- **RP2350-based board** (e.g., Raspberry Pi Pico 2)
- **LED** connected to **GPIO16** (or use built-in LED if available)
- Optional: Current-limiting resistor (typically 330Ω - 1kΩ)
- **USB cable** for programming
### GPIO16 Connection
```
RP2350 GPIO16 ----[330Ω]----[LED]---- GND
(Resistor) (Anode to GPIO, Cathode to GND)
```
---
## Technical Details
### Coprocessor Interface
The RP2350 implements a **custom GPIO coprocessor (CP0)** accessible through the ARM Cortex-M33's coprocessor interface. This is documented in the RP2350 datasheet.
#### MCRR Instruction Format
```
MCRR p0, #opcode, Rt, Rt2, CRm
```
Where:
- `p0` = Coprocessor 0 (GPIO coprocessor)
- `#opcode` = Operation code (4 for GPIO operations)
- `Rt` = First source register (GPIO pin number in R4)
- `Rt2` = Second source register (value in R5)
- `CRm` = Coprocessor register (c0 for OUT, c4 for OE)
### GPIO Coprocessor Operations Used
#### 1. `gpioc_bit_out_put(gpio, value)`
**Purpose:** Set or clear a GPIO output pin
**Encoding:** `MCRR p0, #4, R4, R5, c0`
**Parameters:**
- R4 = GPIO pin number (16 in our case)
- R5 = Output value (0 or 1)
**Example:**
```assembly
MOVS R4, #16 ; GPIO16
MOVS R5, #1 ; Set HIGH
MCRR p0, #4, R4, R5, c0
```
#### 2. `gpioc_bit_oe_put(gpio, enable)`
**Purpose:** Set output enable for a GPIO pin
**Encoding:** `MCRR p0, #4, R4, R5, c4`
**Parameters:**
- R4 = GPIO pin number (16 in our case)
- R5 = Output enable (1 to enable output)
**Example:**
```assembly
MOVS R4, #16 ; GPIO16
MOVS R5, #1 ; Enable output
MCRR p0, #4, R4, R5, c4
```
---
## Memory Map & Register Addresses
### Critical Memory Addresses Used
| Address | Register | Purpose |
|--------------|-----------------------------------|--------------------------------------|
| `0xE000ED88` | CPACR (Coprocessor Access Control)| Enable CP0 access |
| `0x40038044` | PADS_BANK0_GPIO16 | Pad configuration for GPIO16 |
| `0x40028084` | IO_BANK0_GPIO16_CTRL | Function select for GPIO16 |
| `0x20082000` | STACK_TOP | Initial stack pointer |
| `0x2007A000` | STACK_LIMIT | Stack limit for overflow protection |
### Register Details
#### CPACR (Coprocessor Access Control Register) - 0xE000ED88
```
Bits [1:0] - CP0 access privileges
00 = No access
01 = Privileged access only
10 = Reserved
11 = Full access (used in this code)
```
#### PADS_BANK0_GPIO16 - 0x40038044
```
Bit 8 (ISO) - Isolate pad (0 = normal operation)
Bit 7 (OD) - Output disable (0 = output enabled)
Bit 6 (IE) - Input enable (1 = input buffer enabled)
Bits [5:4] - Drive strength
Bits [3:2] - Slew rate
Bit 1 (PDE) - Pull-down enable
Bit 0 (PUE) - Pull-up enable
```
#### IO_BANK0_GPIO16_CTRL - 0x40028084
```
Bits [4:0] (FUNCSEL) - Function select
0 = JTAG (TDI)
1 = SPI
2 = UART
3 = I2C
4 = PWM
5 = SIO (Software controlled I/O) ← Used for GPIO
6-8 = PIO
9 = USB
31 = NULL
```
---
## Code Structure & Execution Flow
### 1. Vector Table (`.vectors` section)
```assembly
.section .vectors, "ax"
.align 2
.global _vectors
/**
* Vector table section.
*/
.global _vectors // export symbol
_vectors:
.word STACK_TOP /* 0x20082000 - Initial Stack Pointer */
.word Reset_Handler + 1 /* Reset Handler (Thumb bit set) */
```
.word STACK_TOP // initial stack pointer
.word Reset_Handler + 1 // reset handler (Thumb bit set)
**Why `+ 1`?**
The Cortex-M33 uses the Thumb instruction set exclusively. The LSB (least significant bit) of function pointers must be set to 1 to indicate Thumb mode. This is automatically handled by the processor.
### 2. Stack Initialization
```assembly
/**
* @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:
/* Initialize stack pointers for Cortex-M33 */
LDR R0, =STACK_TOP
MSR PSP, R0 /* Process Stack Pointer */
LDR R0, =STACK_LIMIT
MSR MSPLIM, R0 /* Main Stack Pointer Limit */
MSR PSPLIM, R0 /* Process Stack Pointer Limit */
LDR R0, =STACK_TOP
MSR MSP, R0 /* Main Stack Pointer */
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
```
**Cortex-M33 Stack Features:**
- **Dual stack pointers:** MSP (Main) and PSP (Process)
- **Stack limit registers:** MSPLIM and PSPLIM for overflow detection
- **Security feature:** Prevents stack overflow from corrupting memory
<br>
### 3. Coprocessor Access Enablement
```assembly
/* Enable coprocessor access - set CP0 to full access */
LDR R0, =0xE000ED88 /* CPACR address */
LDR R1, [R0] /* Read current value */
ORR R1, R1, #0x3 /* Set CP0 bits [1:0] to 11 (full access) */
STR R1, [R0] /* Write back */
DSB /* Data Synchronization Barrier */
ISB /* Instruction Synchronization Barrier */
```
**Critical Step:** Without this, MCRR instructions will trigger a **UsageFault** exception!
**Memory Barriers:**
- `DSB` - Ensures all memory operations complete before proceeding
- `ISB` - Flushes instruction pipeline to ensure new settings take effect
### 4. GPIO Pad Configuration
```assembly
/* Configure pad for GPIO16 */
LDR R3, =0x40038044 /* &pads_bank0_hw->io[16] */
LDR R2, [R3] /* load current config */
BIC R2, R2, #0x80 /* clear OD (bit 7) - enable output */
ORR R2, R2, #0x40 /* set IE (bit 6) - enable input buffer */
STR R2, [R3] /* store updated config */
```
**What this does:**
- Clears OD (Output Disable) bit → Allows pin to drive output
- Sets IE (Input Enable) bit → Enables input buffer for reading
- Preserves other settings (pull-ups, drive strength, etc.)
### 5. Function Selection
```assembly
/* Set GPIO16 function to SIO (Software I/O) */
LDR R3, =0x40028084 /* &io_bank0_hw->io[16].ctrl */
LDR R2, [R3] /* load current config */
BIC R2, R2, #0x1F /* clear FUNCSEL bits [4:0] */
ORR R2, R2, #5 /* set FUNCSEL = 5 (SIO) */
STR R2, [R3] /* store updated config */
```
**Function 5 (SIO):**
- Routes GPIO to software control
- Required before using coprocessor GPIO operations
- Other functions route to hardware peripherals (UART, SPI, etc.)
### 6. Pad Isolation
```assembly
/* Un-isolate the pad */
LDR R3, =0x40038044 /* &pads_bank0_hw->io[16] */
LDR R2, [R3] /* load current config */
BIC R2, R2, #0x100 /* clear ISO bit (bit 8) */
STR R2, [R3] /* store updated config */
```
**ISO bit:**
- When set (1): Pad is isolated from peripherals
- When clear (0): Pad is connected and functional
- Must be cleared for GPIO to work
### 7. Enable GPIO Output
```assembly
/* Enable output using coprocessor */
MOVS R4, #16 /* GPIO16 */
MOVS R5, #1 /* Enable output */
MCRR p0, #4, R4, R5, c4 /* gpioc_bit_oe_put(16, 1) */
```
**This is the first coprocessor instruction!**
- Sets the Output Enable (OE) bit for GPIO16
- Makes the pin drive its output value
- Uses coprocessor register c4 (OE control)
### 8. Main Blink Loop
```assembly
blink_loop:
/* Turn LED ON */
MOVS R4, #16 /* GPIO16 */
MOVS R5, #1 /* HIGH */
MCRR p0, #4, R4, R5, c0 /* gpioc_bit_out_put(16, 1) */
/* Delay ~500ms */
LDR R2, =2000000 /* 2M cycles */
delay_on:
SUBS R2, R2, #1 /* Decrement */
BNE delay_on /* Loop until zero */
/* Turn LED OFF */
MOVS R4, #16 /* GPIO16 */
MOVS R5, #0 /* LOW */
MCRR p0, #4, R4, R5, c0 /* gpioc_bit_out_put(16, 0) */
/* Delay ~500ms */
LDR R2, =2000000 /* 2M cycles */
delay_off:
SUBS R2, R2, #1 /* Decrement */
BNE delay_off /* Loop until zero */
B blink_loop /* Repeat forever */
```
**Delay Calculation:**
- 2,000,000 cycles at ~4-12 MHz boot clock
- Approximately 500ms per delay
- Total period: ~1 second (1Hz blink rate)
---
## Build Process
### Prerequisites
1. **ARM GNU Toolchain** installed and in PATH:
- `arm-none-eabi-as` (Assembler)
- `arm-none-eabi-ld` (Linker)
- `arm-none-eabi-objcopy` (Object copy utility)
2. **Python** (for UF2 conversion)
3. **uf2conv.py** script (located two directories up: `../../uf2conv.py`)
### Build Script (`build.bat`)
```batch
@echo off
REM Build script for RP2350 GPIO16 blink
echo Building GPIO16 blink...
REM Assemble main code
arm-none-eabi-as -mcpu=cortex-m33 -mthumb gpio16_blink.s -o gpio16_blink.o
REM Assemble image definition
arm-none-eabi-as -mcpu=cortex-m33 -mthumb image_def.s -o image_def.o
REM Link with linker script
arm-none-eabi-ld -T linker.ld gpio16_blink.o image_def.o -o gpio16_blink.elf
REM Create raw binary
arm-none-eabi-objcopy -O binary gpio16_blink.elf gpio16_blink.bin
REM Create UF2 bootloader format
python ..\..\uf2conv.py -b 0x10000000 -f 0xe48bff59 -o gpio16_blink.uf2 gpio16_blink.bin
```
### Build Command
```powershell
.\build.bat
```
### Build Flags Explained
**`-mcpu=cortex-m33`**
- Target the Cortex-M33 processor in RP2350
- Enables M33-specific features (stack limits, DSB/ISB instructions, coprocessor)
**`-mthumb`**
- Generate Thumb-2 instruction set (mandatory for Cortex-M)
- Provides 16-bit and 32-bit instruction mix for code density
**`-T linker.ld`**
- Use custom linker script to define memory layout
- Places code at flash start (0x10000000)
**UF2 Parameters:**
- `-b 0x10000000` - Base address (RP2350 flash start)
- `-f 0xe48bff59` - Family ID for RP2350
- `-o gpio16_blink.uf2` - Output filename
---
## Flashing Instructions
### Method 1: UF2 Bootloader (Easiest)
1. **Disconnect** the RP2350 from USB
2. **Hold the BOOTSEL button** on the board
3. **Connect USB cable** while holding BOOTSEL
4. **Release BOOTSEL** - Board appears as USB mass storage device
5. **Copy** `gpio16_blink.uf2` to the RP2350 drive
6. **Board automatically reboots** and starts running the program
### Method 2: OpenOCD with Debug Probe
If you have a debug probe (e.g., Raspberry Pi Debug Probe, CMSIS-DAP):
```bash
openocd -f interface/cmsis-dap.cfg -f target/rp2350.cfg \
-c "adapter speed 5000" \
-c "program gpio16_blink.elf verify reset exit"
```
---
## Debugging & Troubleshooting
### Problem: LED doesn't blink
**Check 1: Coprocessor Access**
- Ensure CPACR is set correctly
- Without this, MCRR triggers UsageFault
- Verify DSB/ISB barriers are present
**Check 2: GPIO Configuration**
- Verify FUNCSEL is set to 5 (SIO)
- Check OD bit is cleared (output enabled)
- Ensure ISO bit is cleared (pad connected)
**Check 3: Hardware Connections**
- LED polarity (anode to GPIO, cathode to GND)
- Current-limiting resistor present
- GPIO16 physically connected
**Check 4: Clock Speed**
- Boot clock may vary (4-12 MHz typical)
- Delay timing depends on actual clock
- Adjust delay constant if needed
### Problem: Build fails
**Missing toolchain:**
```
'arm-none-eabi-as' is not recognized...
```
Solution: Install ARM GNU Toolchain and add to PATH
**Missing uf2conv.py:**
```
python: can't open file 'uf2conv.py'
```
Solution: Ensure uf2conv.py is in `../../` relative to build directory
**Assembly errors:**
```
Error: bad instruction 'mcrr'
```
Solution: Ensure `-mcpu=cortex-m33` flag is used (M33 supports coprocessor)
### Problem: Board doesn't appear as USB drive
**Solution 1:** Try different USB cable (must support data, not just power)
**Solution 2:** Hold BOOTSEL earlier (before connecting USB)
**Solution 3:** Check USB port functionality
---
## Performance Analysis
### Instruction Cycle Counts (Approximate)
**Traditional GPIO Write (3-4 instructions):**
```assembly
LDR R0, =SIO_BASE ; 2 cycles
LDR R1, =GPIO16_BIT ; 2 cycles
STR R1, [R0, #offset] ; 2 cycles
; Total: ~6 cycles + memory latency
```
**Coprocessor GPIO Write (2 instructions):**
```assembly
MOVS R4, #16 ; 1 cycle
MOVS R5, #1 ; 1 cycle
MCRR p0, #4, R4, R5, c0 ; 1 cycle (hardware accelerated)
; Total: ~3 cycles
```
**Performance Gain: ~50% faster execution**
### Memory Usage
| Section | Size | Location |
|-------------|---------|----------------|
| Vector Table| 8 bytes | 0x10000000 |
| Code (.text)| ~140 bytes | 0x10000008 |
| Total Flash | ~148 bytes | Flash |
| Stack | 32 KB | RAM (0x2007A000-0x20082000) |
**UF2 File Size:** 512 bytes (minimum UF2 block size)
---
## Advanced Topics
### Extending to Multiple GPIOs
To control multiple GPIOs with coprocessor:
```assembly
; Blink GPIO16 and GPIO17 alternately
blink_multi:
; Turn GPIO16 ON, GPIO17 OFF
MOVS R4, #16
MOVS R5, #1
MCRR p0, #4, R4, R5, c0
MOVS R4, #17
MOVS R5, #0
MCRR p0, #4, R4, R5, c0
; Delay...
; Turn GPIO16 OFF, GPIO17 ON
MOVS R4, #16
MOVS R5, #0
MCRR p0, #4, R4, R5, c0
MOVS R4, #17
MOVS R5, #1
MCRR p0, #4, R4, R5, c0
```
### Reading GPIO with Coprocessor
The RP2350 also supports GPIO read operations:
```assembly
; Read GPIO state using MRRC (Move to ARM Registers from Coprocessor)
MOVS R4, #16 ; GPIO to read
MRRC p0, #opcode, R4, R5, c0 ; Result in R5
```
*(Exact opcode may vary - consult RP2350 datasheet)*
### Interrupt-Driven Delays
For production code, replace busy-wait delays with timer interrupts:
```assembly
; Configure SysTick for 1ms interrupts
LDR R0, =0xE000E010 ; SysTick base
LDR R1, =4000 ; Reload value for 1ms at 4MHz
STR R1, [R0, #4] ; SYST_RVR
MOV R1, #7 ; Enable + interrupt + clock
STR R1, [R0, #0] ; SYST_CSR
```
---
## Reference Documentation
### Official Documentation
1. **RP2350 Datasheet** - Complete hardware reference
- Section on GPIO Coprocessor (CP0)
- Register maps and bit definitions
- Electrical characteristics
2. **ARM Cortex-M33 Technical Reference Manual**
- Coprocessor interface specification
- MCRR/MRRC instruction encoding
- Stack limit and security features
3. **ARM Architecture Reference Manual (ARMv8-M)**
- Thumb-2 instruction set
- System control registers (CPACR, etc.)
- Exception handling
### Instruction Reference
**MCRR (Move to Coprocessor from ARM Registers):**
```
MCRR coproc, opc1, Rt, Rt2, CRm
coproc: p0-p15 (coprocessor number)
opc1: 4-bit operation code
Rt: First source register
Rt2: Second source register
CRm: Coprocessor register
```
**DSB (Data Synchronization Barrier):**
- Ensures completion of memory operations
- Required after CPACR modification
**ISB (Instruction Synchronization Barrier):**
- Flushes instruction pipeline
- Required after system control changes
---
## Version History
### Version 1.0 (October 2025)
- Initial release
- Implements GPIO16 blink using coprocessor
- 500ms on/off cycle
- CPACR enablement for CP0 access
- Proper initialization sequence
---
## License & Credits
This code is provided as-is for educational and experimental purposes.
**Created for:** Bare-metal RP2350 development
**Target Board:** Raspberry Pi Pico 2 (RP2350)
**Architecture:** ARM Cortex-M33
**Assembly Syntax:** GNU AS (Unified ARM Syntax)
---
## Future Enhancements
Potential improvements:
1. **Add interrupt-driven timing** instead of busy-wait loops
2. **Implement GPIO read operations** using MRRC
3. **Create PWM effects** using variable duty cycles
4. **Multi-GPIO patterns** (Knight Rider, binary counter, etc.)
5. **Add error handling** for fault exceptions
6. **Clock configuration** for precise timing
7. **Low-power modes** using WFI (Wait For Interrupt)
---
## Contact & Support
For issues, questions, or contributions related to this coprocessor implementation, please refer to:
- RP2350 community forums
- ARM Cortex-M33 documentation
- Raspberry Pi Pico SDK examples
**Happy coding with coprocessors!** 🚀
## License
[Apache License 2.0](https://github.com/mytechnotalent/RP2350_Blink_Driver/blob/main/LICENSE)