<center> <h1> PIC32MM GPIO with Assembly</h1> </center> *Notes before proceeding:* * This is pureley supplementary and just aims to help those struggling a bit with programming in assembly. * It is assumed that prior knowledge on the basic assembly commands and syntax is already known by the reader. * The code provided is meant to be run on MPLABX; it is assumed that you are already familiar with the interface. # Objectives * To read and write from the PIC32 I/O ports using assembly. * To understand how each instruction is executed and use system delays to our advantage. # Introduction Programming in assembly might not be intuitive for most especially if one is accustomed to programming in high-level languages. In this note, I will do my best to relate high level constructs you would usually take for granted in high level languages that are apparently not that straightforward in assembly. # The Input and Output Pins Like with any other integrated chip, each microcontroller pin serves a specific or even multiple purposes. Below is a pin diagram of the 36-pin PC32MM variant included in your DevBoards: ![picture](https://drive.google.com/uc?export=view&id=1EJoeqdRHjs7m-QBLxEgiK1VyC7wgwGY2) The PIC32MM has 3 general purpose I/O (GPIO) ports: PORTA, PORTB, and PORTC. Each can have up to 16 pins (Rx0 to Rx15) but in low pin-count variants, you would expect only a fraction these available. # Input or Output? Since each pin can function as both an input and output, how woould the PIC32 know which pins it would expect data from or write data on? This is done by configuring the `TRISx` register where `x` is the specific port (e.g. `TRISA` to configure port A pins). Each bit of the `TRISx` register corresponds to the pin in the respective bit position (e.g. `TRISx[4]` corresponds to `Rx4`). A value of `0` on the register bit means that the corresponding pin is an `0utput`. Conversely, a value of `1` on the register bit mean that the pin is an `1nput`. Unfortunately, in assembly, you cannot access individual bits directly (e.g. `TRISA[4] = 1` has no equivalent assembly instruction). You'll have to write on all the bits at the same time (e.g. you have to write `0x0010` on `TRISA` to achieve `TRISA[4] = 1`). Writing on the `TRISx` register, or any register for that matter, is not straightforward as well. The sample code below demonstrates how this would be usually done in assembly: ``` ori t0, $0, 0x0010 // t0 = 0x0010 la s0, TRISA // s0 = &TRISA sw t0, 0(s0) // *(s0 + 0) = t0 or TRISA = t0 ``` An `ori` command needs to be issued first to store the value we want to write on the register; note that other commands can accomplish the same functionality. Next, the `la` instruction **l**oads the **a**ddress of `TRISA` on `s0` since the CPU cannot access these directly. Lastly, the `sw` instruction **s**aves the **w**ord or data stored in `t0` to the memory location srored in `s0`. The preceeding `0` just indicates the memory offset (i.e. using `1(s0)` would instead write the data `1` byte after the specified address). # Reading and Writing Reading from an input pin is accomplished by reading the `PORTx` register. Similary, `x` corresponds to the port and each individual bit corresponds to the specific pin (e.g. `RB7` can be read from `PORTB[7]`). Storing the `PORTx` value in a CPU register is also not straightforward but looks similar to the one above: ``` la s0, PORTB // s0 = &PORTB lw t0, 0(s0) // t0 = *(s0 + 0) or t0 = PORTB ``` The main difference here is that we are **l**oading the **w**ord or data stored in `s0` and writing it on `t0` with `lw`. To write a value on an output pin, we instead use the `LATx` register which is structured similarly to the previous registers. Writing on the `LATx` registers is similar to writing on the `TRISx` registers: ``` ori t0, $0, 0x0200 // t0 = 0x0200 la s0, LATA // s0 = &LATA sw t0, 0(s0) // *(s0 + 0) = t0 or LATA = t0 ``` # Taking our Time Each instruction takes a finite amount of time to execute. This finite amount of time is determined by the instruction frequency ($f_{cyc}$). If $f_{cyc}$ is $1 MHz$ or 1 MIPS, that means up to 1 million instructions can be executed in 1 second. That also means $1 \mu s$ per instruction. Thus, if we have the following code: ``` sw t1, 0(s0) // *(s0 + 0) = t1 or LATA = t1 addi t0, $0, 0x0001 addi t0, $0, 0x0002 addi t0, $0, 0x0003 addi t0, $0, 0x0004 addi t0, $0, 0x0005 addi t0, $0, 0x0006 addi t0, $0, 0x0007 addi t0, $0, 0x0008 addi t0, $0, 0x0009 addi t0, $0, 0x000A sw t2, 0(s0) // *(s0 + 0) = t2 or LATA = t2 ``` The value of `LATA` would be `t1` for approximately $10 \mu s$ before it is changed to `t2`. If we add a million `addi` instructions between them, then $1 s$ would elapse before `LATA` changes. Of course, these instructions do not need to be written out explicitly; you could maybe use a loop to do that for you ;) # References / Additional Information [PIC32MM0064GPL036 Datasheet](http://ww1.microchip.com/downloads/en/devicedoc/60001324b.pdf) [PIC32 Family Reference Manual Section 12. I/O Ports](https://ww1.microchip.com/downloads/en/DeviceDoc/60001120F.pdf) [The microMIPS32™ Instruction Set](https://s3-eu-west-1.amazonaws.com/downloads-mips/documents/MD00582-2B-microMIPS32-AFP-05.04.pdf)