The registers for a given IO peripheral are placed in 4K blocks. The 4K block is replicated 4 times (so we have 16K overall). This gives us 4 ways to access a given register as follows:
The registers are 32 bits (4 bytes) in size.
If you want to just set a bit, you do a 32 bit write
with a mask for the bit(s) you want to set to the SET address.
If you want to just clear a bit, you do a 32 bit write
with a mask for the bit(s) you want to clear to the CLR address..
If you want to toggle a bit, you do a 32 bit write
with a mask for the bit(s) you want to toggle to the XOR address..
The basic reset "device" has 3 32 bit registers like so:
#define RESET_BASE 0x4000c000 #define RESET_R_BASE RESET_BASE + 0 #define RESET_WD_BASE RESET_BASE + 4 #define RESET_DONE_BASE RESET_BASE + 8So, let's define the flavor offsets:
#define FL_RW 0x0000 #define FL_XOR 0x1000 #define FL_SET 0x2000 #define FL_CLR 0x3000Here is how I would write C code to clear the reset bit for IO_BANK0.
#define RESET_BASE 0x4000c000 #define RESET_BASE_RW RESET_BASE + FL_RW #define RESET_BASE_XOR RESET_BASE + FL_XOR #define RESET_BASE_SET RESET_BASE + FL_SET #define RESET_BASE_CLR RESET_BASE + FL_CLR struct resets { u32 reset; u32 wdsel; u32 done; }; #define R_IO_BANK0 (1<<5) void reset_io_bank0 ( void ) { struct resets *rp; rp = (struct resets *) RESET_BASE_CLR; rp->reset = R_IO_BANK0; rp = (struct resets *) RESET_BASE_RW; while ( rp->done & R_IO_BANK) ) ; }Some people get crazy about writing code like this to do IO, but it works fine and I find it clearer that invoking unnecessary functions (or inline assembly) to do register access. Some might argue about portability, but we are writing code for just the rp2040 here -- so the heck with them.
Tom's Computer Info / tom@mmto.org