Tuesday, February 25, 2014

Writing C on ARM Cortex M0 (NXP LPC1114/301)


To write assembly on the MCU: Writing Assembly Code for ARM Cortex M0

Example assembly and C code for LPC1114/301: Example Assembly and C Code for ARM Cortex M0 (NXP LPC1114/301)



Step 1. Create a Project with uVision 4 (Free evaluation of Keil MDK ARM v4: https://www.keil.com/download/product/)

Figure 1




Step 2. Select NXP LPC1114/310 as the target device.

Figure 2




Step 3. Copy “startup_LPC111x.s” into the project directory.

Figure 3




Step 4. Copy the following files into the project directory.



File Name
Source (Assuming Keil uVision4 is installed in D:\Keil)
LPC11xx.h
D:\Keil\ARM\INC\NXP\LPC11xx
system_LPC11xx.h
D:\Keil\ARM\INC\NXP\LPC11xx
system_LPC11xx.c
D:\Keil\ARM\Startup\NXP\LPC11xx



Step 5. Right click on the “Source Group 1” and add “system_LPC11xx.c” to the current project.

Figure 4
Figure 5





Step 5. Again right click on the “Source Group 1” and add a new .c file to the project. This file will contain the “__main()” function and interrupt service routines.

Figure 6
Figure 7





Step 6. Write the application code in the .c file recently created.

Figure 8




Code:




#include "LPC11xx.h"



void __main(void)
{
        // Write user code here
        unsigned int tempData1 = 0;
   
        __NOP();
        __NOP();
        __NOP();
        __NOP();
       
        // Infinite while loop
        while(1)
        {
             tempData1++;
        } // end of while(1)
}






Compilation, Simulation, HEX file generation, and programming the MCU with the generated HEX file steps are same as in Writing Assembly Code for ARM Cortex M0.

Monday, February 24, 2014

Writing Assembly Code for ARM Cortex M0 (Device NXP LPC1114/301)


To write C on the MCU: Writing C on ARM Cortex M0

Example assembly and C code for LPC1114/301: Example Assembly and C Code for ARM Cortex M0 (NXP LPC1114/301)




Step 1. Install Keil MDK ARM v4. Free evaluation version is code size limited to 32k: https://www.keil.com/download/product/

Figure 1


Step 2: Launch “uVision 4” and create a new project.
Figure 2



Step 3: Select NXP “LPC1114/301” as the target device.


Figure 3



Step 4. Copy “startup_LPC11xx.s” to the project folder and add it to the current project.
Figure 4


“startup_LPC11xx.s” will appear in the project tree.
Figure 5


Step 5. Right click on “Source Group 1” in the project tree and add a new assembly source file to the project. This source file will contain the “__main” function and the interrupt service routines.

Figure 6


Select “Asm file” and name it. Then add the .s file to the project.
Figure 7

Step 6. Write the assembly code for LPC1114 in “ApplicationCode.s”. Use the template provided in the following figure.
Figure 8




Code for "ApplicationCode.s":

  AREA main, CODE, READONLY
EXPORT __main
EXPORT __use_two_region_memory


__use_two_region_memory EQU 0
EXPORT SystemInit


ENTRY
; System Init routine
SystemInit
; Add code here to initialize I/O and peripherals
BX LR; Return from SystemInit


; __main routine starts here
__main
NOP;
NOP;
NOP;
NOP;


; Loop infinitely between "_LoopHere1" and "B _LoopHere1"
_LoopHere1
LDR R0, =0; Test load immediate 1
LDR R0, =1; Test load immediate 2


; Test 1 cycle 32-bit hardware multiplier
LDR R2, =5; Load 5 in R2
LDR R3, =2; Load 2 in R3
MULS R2, R3, R2; 1 cycle multiply operation R2 = R2 x R3
MOV R5, R2; Move R2 into R5
MOV R3, R2; Move R2 into R3
B _LoopHere1


NOP;
B __main; End of __main



END; End of File





Another example is provided here in which interrupt service routine is used to toggle one of the MCU pins once every 4.8 million clock cycles.

Step 7. To compile the source code, right click on “Target1” or “Source Group 1” and select “Rebuild all target files”
Figure 9


Figure 10
Few warnings may appear. Ignore them for now.




Step 8. Simulating the code.

Set “Use Simulator” radio button under the “Debug” tab of “Flash->Configure Flash Tools”. “Options for Target” window will appear. Keep the rest of the settings to the default values.
Figure 11
(Quick: Press “Alt+F7” to bring the “Option for Target” window)


Select “Use Simulator” under the “Debug” tab.
Figure 12

Start the simulation with “Ctrl+F5” or from “Debug->Start/Stop Debug Session”. Evaluation Mode info window will appear. Click “ok” to continue.
Press F11 to single step through the code.
Figure 14

Anytime during the simulation, all the peripheral registers can be observed from the “Peripherals” in the main menu.
Figure 15

Stop the simulation with “Ctrl+F5” or from “Debug->Start/Stop Debug Session”.


Step 9. Generate the .hex file to program the MCU with.

Check “Create .HEX file” box under the “Output” tab of “Flash->Configure Flash Tools”. Then recompile the project. The output .hex file be created in the project directory.

(Quick: Press “Alt+F7” to bring the “Option for Target” window)
Figure 16


Step 10. Program the MCU with the generated .hex file and the LPC21isp tool(http://lpc21isp.sourceforge.net/). A compiled version of the executable “lpcprog.exe” can be downloaded from here.


10a. Connect the development kit to the PC using USB-A to USB-B cable. Download and install the FTDI Vitrual Communication Port Driver, if not installed already.
10b. Make a note of the COM port number at which the the development kit is discovered. Say it is COM7.
10c. To place the MCU into ISP mode, press and hold “RESET + PROG” buttons together on the development kit. Release the “RESET” button FIRST, then release the “PROG” button. The MCU is now in ISP mode.
10d. Open a command prompt and navigate to the place where “lpcprog.exe” is located. Then, type in the following command and press enter.
lpcprog -wipe -verify PATH COM# BaudRate CrystalFrequnecyInKHz
where,
PATH = Absolute path of the .hex file that was generated by Keil
COM# = ‘#’ should be replaced by the serial port number at which the development kit is connected to. If the port number is greater than 9, write “\\.\COMXX” instead of just “COMXX”


Example:
lpcprog -wipe -verify D:\Projects\Project1.hex COM7 115200 12000


Here,
PATH = D:\Projects\Project1.hex
COM# = COM7
BaudRate = 115200
CrystalFrequnecyInKHz = 12000

 10e. Once the MCU is programmed, press “RESET” once to launch the new code.


ARM Cortex M0/M3 development kit



Top surface render of the development kit pcb





Bottom surface render of the development kit pcb


Thursday, September 15, 2011

PWM Controlled By PC Serial Port

MCUs Programmed:

  • 1. ATMEGA88PA: UART0, SPI
  • 2. ATTINY261: PWM, USI (Universal Serial Interface) configured as SPI
PC ---(UART)---> ATMEGA88PA ---(SPI)---> ATTINY261 -------> PWM


Assembly Draft Code for ATMEGA88PA:



.include "m88padef.inc"


; Setup MCU to receive data at 2400 baud rate on the UART0

; -------- Register map (START) ----------
; R10 (KEY A) = Address of the I2C Slave (Read/~Write bit included) --> Address byte will be preceeded by 0xAA
; R3  (KEY M) = Byte RG_ADD1_HIGH
; R4  (KEY N) = Byte RG_ADD1_LOW
; R5  (KEY O) = Byte DATA1_HIGH
; R6  (KEY P) = Byte DATA1_LOW

; R22 = Flag for the jump instructions indicating if the next byte is a separator or a data byte (only the bit counts, if last bit == 0, current is a separator byte; if last bit == 1, current byte is a data byte)
; R23 = Dispatch for the loading of the registers R10, R3, R4, R5, and R6

; R18 = Timer0 prescalar
; R19 = Fast PWM R11 or R12 register load indicator (R11 < R12; and R11 + R12 = Total Base frequency counter)
; R11 = Fast PWM pulse width counter
; R12 = Fast PWM base frequency counter
; -------- Register map (END) ------------

.CSEG

.ORG 0x0000
RJMP RESET;

.ORG OC0Aaddr
RJMP OC0Aaddr_ISR;

.ORG OC0Baddr
RJMP OC0Baddr_ISR;

.ORG URXCaddr
RJMP URXCaddr_Rx_Complete_ISR;



.EQU I2C_SLAVE_ADDR_WRITE            = 0x0A;
.EQU I2C_STAT_START_SENT             = 0x08;
.EQU I2C_STAT_ADDRESS_SENT_ACK_RCVD  = 0x18;
.EQU I2C_STAT_DATA_SENT_ACK_RCVD     = 0x28;

; Separator bytes
.EQU SEPARATOR_ADDRESS                     = 0xAA;
.EQU SEPARATOR_RG_ADD1_HIGH                = 0x4D;
.EQU SEPARATOR_RG_ADD1_LOW           = 0x4E;
.EQU SEPARATOR_DATA1_HIGH            = 0x4F;
.EQU SEPARATOR_DATA1_LOW             = 0x50;
.EQU SEPARATOR_PWM_BASE_FREQ         = 0x51;
.EQU SEPARATOR_PWM_PULSE_WIDTH       = 0x52;
.EQU SEPARATOR_SIGNAL_SEND           = 0x53;

RESET:
     ; ----- Set the stack pointer (START) ------
     LDI R16, LOW(RAMEND);
     LDI R17, HIGH(RAMEND);
     OUT SPH, R17;  
     OUT SPL, R16;
     ; ----- Set the stack pointer (END) --------

     ; ------ Setup input and output ports (START) -------
     ; Set portC as output
     SER R16;
     OUT DDRC, R16;

     ; Set all the leds to the zero connected to portC
     SER R16;
     OUT PORTC, R16;

     ; Set portB as output
     SER R16;
     OUT DDRB, R16;
     ; Set all the leds to the zero connected to portC
     SER R16;
     OUT PORTB, R16;
    
     ; Set portB5 as output for the PWM
     CBI PORTB, PORTB5;
     ; ------ Setup input and output ports (END) ---------
    

     ; ------- Setup USART0 2400, 8-bit, 1 stop bit , No parity (START) ----------
     ; Set the baud rate to 2400 -> UBRR would be set to 520d = (0x0208)
     LDI R17, 0x02;
     LDI R16, 0x08;
     STS UBRR0H, R17;
     STS UBRR0L, R16;

     ; Set the character size to 8 bits, No polarity, and 1 stop bit in UCSR0C
     LDI R16, (1<<UCSZ01)|(1<<UCSZ00)|(0<<UPM00)|(0<<UPM01)|(0<<USBS0);
     STS UCSR0C, R16;

     ; Enable the receiver only (RXEN0) and also enable receive data complete interrupt (RXCIE0) in UCSR0B
     LDI R16, (1<<RXEN0) | (1<<RXCIE0);
     STS UCSR0B, R16;
     ; ------- Setup USART0 2400, 8-bit, 1 stop bit , No parity (END) ------------

     ; ----------- SETUP I2C BIT RATE GENERATOR (START) --------
     ; Set the SCL clock to 200 KHz With TWBR = 42d = 0x2A and Prescalar value set to 1
     ;LDI R16, 0x2A;
     ;STS TWBR, R16;
     ; Set the SCL clock to 38 KHz With TWBR = 255d = 0xFF and Prescalar value set to 1
     ;LDI R16, 0xFF;
     ;STS TWBR, R16;
     ; Set the SCL clock to 8 KHz With TWBR = 20d = 0x14 and Prescalar value set to 64
     LDI R16, (1<<TWPS1)|(1<<TWPS0);
     STS TWSR , R16;
     LDI R16, 0x14;
     STS TWBR, R16;
     ; ----------- SETUP I2C BIT RATE GENERATOR (END) ----------
    
     ; ----------- SETUP TIMER0 FOR PWM GENERATION (START) ------
     ; Load the related registers
     CLR R19;
    
     ; Load base frequency counter
     LDI R16, 0xB4;
     MOV R12, R16;

     ; Load pulse width counter
     LDI R16, 0x14;
     MOV R11, R16;
    
     ; Initially load the OCR0A with the pulse width counter   
     OUT OCR0A, R11;
     ; Initially load the OCR0B with the base frequency counter
     OUT OCR0B, R12;

     ; Enable output compare A interrupt
     ;LDI R16, (1<<OCIE0B)|(1<<OCIE0A);
     ;STS TIMSK0, R16;

     ; Load the prescalar and start the clock
     ;LDI R18, (0<<CS02)|(0<<CS01)|(1<<CS00);
     ;OUT TCCR0B, R18;
     ; ----------- SETUP TIMER0 FOR PWM GENERATION (END) --------
    

     ; ------------ Enable SPI Communication on PortB(START) ----
     ; Enable the pin functions
     ; PB5 (SCK) to output
     SBI DDRB, 5;
     ; PB4 (MISO) to input
     CBI DDRB, 4;
     ; PB3 (MOSI) to output
     SBI DDRB, 3;
     ; PB2 (~SS) to output
     SBI DDRB, 2;
     ; Drive the slave select to LOW ALWAYS, as if the connected slave is always selected
     CBI PORTB, 2;

     ; Enable SPI as master in 0-0 mode, and set the clock prescalar to fscl/128
     LDI R16, (1<<SPE)|(1<<MSTR)|(1<<SPR1)|(1<<SPR0);
     OUT SPCR, R16;
     ; ------------ Enable SPI Communication on PortB(END) ------

     ; Enable global interrupt and go..
     SEI;
    
     ; --------- Blink some leds (START) ---------
    
     ; Clear all the registers involved
     CLR R10;
     CLR R3;
     CLR R4;
     CLR R5;
     CLR R6;

     CLR R22;
     CLR R23;


     MAIN_LOOP1:
           NOP;
           NOP;
           NOP;
           NOP;
          
           NOP;
           NOP;
           NOP;
           NOP;

     RJMP MAIN_LOOP1;
     ; --------- Blink some leds (END) -----------


RETI;



URXCaddr_Rx_Complete_ISR:
     ; Read the data from UDR0 into R16;
     LDS R16, UDR0;

     ; ------ Process the data read on the serial port (START) --
     MOV R7, R16;
     OUT PORTC, R7; Dubugging the serial port

     ; Increment R22
     INC R22;
    
     ; Turn off the LEDs if the parsing error occurred in the last byte
     CLR R21; To flag the instant when the error occurred..
    
     SER R20;
     ;;;;OUT PORTB, R20;


     ; If R22 & 0x01 == 0x01, current byte is a separator byte; if R22 & 0x01 == 0x00, current byte is data byte
     SBRC R22, 0;
     RJMP PROCESS_SEPARATOR_BYTE;
     RJMP PROCESS_DATA_BYTE;
    

     PROCESS_SEPARATOR_BYTE:
           ; CHECK which separator byte is received in R7;
          
           ; MOVE R7 into the temporary resister R24 and work from there
           MOV R24, R7;
          
           ; Compare the data in R24 with all the SEPARATORs and set R23 appropriately
           CPI R24, SEPARATOR_ADDRESS;
           BREQ LOAD_SEPARATOR_ADDRESS;
          
           CPI R24, SEPARATOR_RG_ADD1_HIGH;
           BREQ LOAD_SEPARATOR_RG_ADD1_HIGH;

           CPI R24, SEPARATOR_RG_ADD1_LOW;
           BREQ LOAD_SEPARATOR_RG_ADD1_LOW;

           CPI R24, SEPARATOR_DATA1_HIGH;
           BREQ LOAD_SEPARATOR_DATA1_HIGH;
          
           CPI R24, SEPARATOR_DATA1_LOW;
           BREQ LOAD_SEPARATOR_DATA1_LOW;

           CPI R24, SEPARATOR_PWM_BASE_FREQ;
           BREQ LOAD_SEPARATOR_PWM_BASE_FREQ;

           CPI R24, SEPARATOR_PWM_PULSE_WIDTH;
           BREQ LOAD_SEPARATOR_PWM_PULSE_WIDTH;
          
           CPI R24, SEPARATOR_SIGNAL_SEND;
           BREQ LOAD_SEPARATOR_SIGNAL_SEND;
          
           RJMP WRONG_SEPARATOR_RECEIVED;
          


           LOAD_SEPARATOR_ADDRESS:
                LDI R23, 0x01;
           RJMP END_OF_PROCESS_SEPARATOR_BYTE;

           LOAD_SEPARATOR_RG_ADD1_HIGH:
                LDI R23, 0x02;
           RJMP END_OF_PROCESS_SEPARATOR_BYTE;

           LOAD_SEPARATOR_RG_ADD1_LOW:
                LDI R23, 0x03;
           RJMP END_OF_PROCESS_SEPARATOR_BYTE;

           LOAD_SEPARATOR_DATA1_HIGH:
                LDI R23, 0x04;
           RJMP END_OF_PROCESS_SEPARATOR_BYTE;

           LOAD_SEPARATOR_DATA1_LOW:
                LDI R23, 0x05;
           RJMP END_OF_PROCESS_SEPARATOR_BYTE;
               
           LOAD_SEPARATOR_PWM_BASE_FREQ:
                LDI R23, 0x06;
                ; Send out SEPARATOR_PWM_BASE_FREQ to the SPI slave
                LDI R16, SEPARATOR_PWM_BASE_FREQ;
                OUT SPDR, R16;
                ; Wait for SPI Transmission complete
                WAIT_SPI_TX_SEPARATOR_PWM_BASE_FREQ:
                     IN R16, SPSR;
                     SBRS R16, SPIF;
                RJMP WAIT_SPI_TX_SEPARATOR_PWM_BASE_FREQ;
                ; Dummy read to clear SPIF flag in SPSR
                IN R16, SPDR;
           RJMP END_OF_PROCESS_SEPARATOR_BYTE;


           LOAD_SEPARATOR_PWM_PULSE_WIDTH:
                LDI R23, 0x07;
                ; Send out SEPARATOR_PWM_PULSE_WIDTH to the SPI slave
                LDI R16, SEPARATOR_PWM_PULSE_WIDTH;
                OUT SPDR, R16;
                ; Wait for SPI Transmission complete
                WAIT_SPI_TX_SEPARATOR_PWM_PULSE_WIDTH:
                     IN R16, SPSR;
                     SBRS R16, SPIF;
                RJMP WAIT_SPI_TX_SEPARATOR_PWM_PULSE_WIDTH;
                ; Dummy read to clear SPIF flag in SPSR
                IN R16, SPDR;
           RJMP END_OF_PROCESS_SEPARATOR_BYTE;

           WRONG_SEPARATOR_RECEIVED:
                DEC R22;
                CLR R23;
           RJMP END_OF_PROCESS_SEPARATOR_BYTE;
          
           LOAD_SEPARATOR_SIGNAL_SEND:
                DEC R22;
                CLR R23;
           RJMP SEND_DATA_TO_I2C_SLAVE;
          

           END_OF_PROCESS_SEPARATOR_BYTE:
     RJMP Rx_Complete_ISR_End; ; end of level PROCESS_SEPARATOR_BYTE:
    

    

     PROCESS_DATA_BYTE:
           ; Compare the data in R23 with all the possible combinations
           ; if R23 == 0x01; load data from R7 into R10; --> Address byte (ADB)
           ; if R23 == 0x02; load data from R7 into R3;  --> Byte RG_ADD1_HIGH
           ; if R23 == 0x03; load data from R7 into R4;  --> Byte RG_ADD1_LOW
           ; if R23 == 0x04; load data from R7 into R5;  --> Byte DATA1_HIGH
           ; if R23 == 0x05; load data from R7 into R6;  --> Byte DATA1_LOW

           CPI R23, 0x01;
           BREQ LOAD_R10_WITH_ADDRESS;
          
           CPI R23, 0x02;
           BREQ LOAD_R3_WITH_RG_ADD1_HIGH;

           CPI R23, 0x03;
           BREQ LOAD_R4_WITH_RG_ADD1_LOW;

           CPI R23, 0x04;
           BREQ LOAD_R5_WITH_DATA1_HIGH;

           CPI R23, 0x05;
           BREQ LOAD_R6_WITH_DATA1_LOW;

           CPI R23, 0x06;
           BREQ LOAD_R12_WITH_PWM_BASE_FREQ;

           CPI R23, 0x07;
           BREQ LOAD_R11_WITH_PWM_PULSE_WIDTH;

           RJMP END_OF_PROCESS_DATA_BYTE;

          
           LOAD_R10_WITH_ADDRESS:
                MOV R10, R7;
           RJMP END_OF_PROCESS_DATA_BYTE;

           LOAD_R3_WITH_RG_ADD1_HIGH:
                MOV R3, R7;
           RJMP END_OF_PROCESS_DATA_BYTE;

           LOAD_R4_WITH_RG_ADD1_LOW:
                MOV R4, R7;
           RJMP END_OF_PROCESS_DATA_BYTE;

           LOAD_R5_WITH_DATA1_HIGH:
                MOV R5, R7;
           RJMP END_OF_PROCESS_DATA_BYTE;

           LOAD_R6_WITH_DATA1_LOW:
                MOV R6, R7;
           RJMP END_OF_PROCESS_DATA_BYTE;
          
           LOAD_R12_WITH_PWM_BASE_FREQ:
                MOV R12, R7;
                OUT OCR0B, R12;
                ; Send out PWM_BASE_FREQ (R12) to the SPI slave
                OUT SPDR, R12;
                ; Wait for SPI Transmission complete
                WAIT_SPI_TX_PWM_BASE_FREQ:
                     IN R16, SPSR;
                     SBRS R16, SPIF;
                RJMP WAIT_SPI_TX_PWM_BASE_FREQ;
                ; Dummy read to clear SPIF flag in SPSR
                IN R16, SPDR;
           RJMP END_OF_PROCESS_DATA_BYTE;
          
           LOAD_R11_WITH_PWM_PULSE_WIDTH:
                MOV R11, R7;
                OUT OCR0A, R11;
                ; Send out PWM_PULSE_WIDTH (R11) to the SPI slave
                OUT SPDR, R11;
                ; Wait for SPI Transmission complete
                WAIT_SPI_TX_PWM_PULSE_WIDTH:
                     IN R16, SPSR;
                     SBRS R16, SPIF;
                RJMP WAIT_SPI_TX_PWM_PULSE_WIDTH;
                ; Dummy read to clear SPIF flag in SPSR
                IN R16, SPDR;
           RJMP END_OF_PROCESS_DATA_BYTE;

    
           END_OF_PROCESS_DATA_BYTE:
     RJMP Rx_Complete_ISR_End; ; end of level PROCESS_DATA_BYTE:
     ; ------ Process the data read on the serial port (END) ----

    
     ;  ----------------- Send data from R3 (DB1), R4 (DB2), R5 (CB), and R6 (BB) to the I2C slave addressed by R10 (START) -----
     SEND_DATA_TO_I2C_SLAVE:
           ; Load the I2C start condition into R16
           LDI R16, (1<<TWEN)|(1<<TWSTA)|(1<<TWINT);
           STS TWCR, R16;
          
           ; Wait for the START condition to be sent on the line
           START_SENT_LOOP:
                LDS R16, TWCR;
                SBRS R16, TWINT;
           RJMP START_SENT_LOOP;
          
           INC R21;
           ; Read the status code
           LDS R16, TWSR;
           ANDI R16, 0xF8; Masking out the prescalar bits
           CPI R16, I2C_STAT_START_SENT;
           BRNE I2C_ERROR_ADDRESS;

           ; Load the TWDR with SLA+R/W address from R10 
           MOV R16, R10;
           STS TWDR, R16;
          
           ; Send out the address by clearing the TWINT bit in TWCR
           LDI R16, (1<<TWEN)|(1<<TWINT);
           STS TWCR, R16;

           ; Wait for the address ACK to be received from the slave
           WAIT_SLAVE_ADDRESS_ACK:
                LDS R16, TWCR;
                SBRS R16, TWINT;
           RJMP WAIT_SLAVE_ADDRESS_ACK;
          
           INC R21;
           ; Read the status code
           LDS R16, TWSR;
           ANDI R16, 0xF8; Masking out the prescalar bits
           CPI R16, I2C_STAT_ADDRESS_SENT_ACK_RCVD;
           BRNE I2C_ERROR_ADDRESS;
           RJMP SEND_DATA_OK;

           I2C_ERROR_ADDRESS:
                ; Disable the TWI interface and let go off the SCL line
                LDI R16, (0<<TWEN)|(1<<TWINT);
                STS TWCR, R16;
                ; Turn on Alternating LEDS to indicate I2C error
                LDI R20, 0x50;
                OR R20, R21;
                COM R20;
                OUT PORTB, R20;
           RJMP Rx_Complete_ISR_End;
          
           SEND_DATA_OK:
                ; ++++++++++++++++ SENDING R3 BYTE DB1 (START) 
                ; Load the data into TWDR from R3
                STS TWDR, R3;

                ; Send out the data by clearing the TWINT bit in TWCR
                LDI R16, (1<<TWEN)|(1<<TWINT);
                STS TWCR, R16;
          
                ; Wait for the data ACK to be received from the slave
                WAIT_SLAVE_DATA_ACK_FOR_R3_DB1:
                     LDS R16, TWCR;
                     SBRS R16, TWINT;
                RJMP WAIT_SLAVE_DATA_ACK_FOR_R3_DB1;
          
                INC R21;
                ; Read the status code
                LDS R16, TWSR;
                ANDI R16, 0xF8; Masking out the prescalar bits
                CPI R16, I2C_STAT_DATA_SENT_ACK_RCVD;
                BRNE I2C_ERROR;
                ; ++++++++++++++++ SENDING R3 BYTE DB1 (END) 

                ; ++++++++++++++++ SENDING R4 BYTE DB2 (START) 
                ; Load the data into TWDR from R4
                STS TWDR, R4;

                ; Send out the data by clearing the TWINT bit in TWCR
                LDI R16, (1<<TWEN)|(1<<TWINT);
                STS TWCR, R16;
          
                ; Wait for the data ACK to be received from the slave
                WAIT_SLAVE_DATA_ACK_FOR_R4_DB2:
                     LDS R16, TWCR;
                     SBRS R16, TWINT;
                RJMP WAIT_SLAVE_DATA_ACK_FOR_R4_DB2;
          
                INC R21;
                ; Read the status code
                LDS R16, TWSR;
                ANDI R16, 0xF8; Masking out the prescalar bits
                CPI R16, I2C_STAT_DATA_SENT_ACK_RCVD;
                BRNE I2C_ERROR;
                ; ++++++++++++++++ SENDING R4 BYTE DB2 (END) 

                ; ++++++++++++++++ SENDING R5 BYTE CB (START) 
                ; Load the data into TWDR from R5
                STS TWDR, R5;

                ; Send out the data by clearing the TWINT bit in TWCR
                LDI R16, (1<<TWEN)|(1<<TWINT);
                STS TWCR, R16;
          
                ; Wait for the data ACK to be received from the slave
                WAIT_SLAVE_DATA_ACK_FOR_R5_CB:
                     LDS R16, TWCR;
                     SBRS R16, TWINT;
                RJMP WAIT_SLAVE_DATA_ACK_FOR_R5_CB;
          
                INC R21;
                ; Read the status code
                LDS R16, TWSR;
                ANDI R16, 0xF8; Masking out the prescalar bits
                CPI R16, I2C_STAT_DATA_SENT_ACK_RCVD;
                BRNE I2C_ERROR;
                ; ++++++++++++++++ SENDING R5 BYTE CB (END) 

                ; ++++++++++++++++ SENDING R6 BYTE BB (START) 
                ; Load the data into TWDR from R6
                STS TWDR, R6;

                ; Send out the data by clearing the TWINT bit in TWCR
                LDI R16, (1<<TWEN)|(1<<TWINT);
                STS TWCR, R16;
          
                ; Wait for the data ACK to be received from the slave
                WAIT_SLAVE_DATA_ACK_FOR_R6_BB:
                     LDS R16, TWCR;
                     SBRS R16, TWINT;
                RJMP WAIT_SLAVE_DATA_ACK_FOR_R6_BB;
          
                INC R21;
                ; Read the status code
                LDS R16, TWSR;
                ANDI R16, 0xF8; Masking out the prescalar bits
                CPI R16, I2C_STAT_DATA_SENT_ACK_RCVD;
                BRNE I2C_ERROR;
                ; ++++++++++++++++ SENDING R6 BYTE BB (END) 
          
                ; Send a stop condition on the line
                LDI R16, (1<<TWEN)|(1<<TWINT)|(1<<TWSTO);
                STS TWCR, R16;
          
     RJMP Rx_Complete_ISR_End;

    
     I2C_ERROR:
           ; Disable the TWI interface and let go off the SCL line
           LDI R16, (0<<TWEN)|(1<<TWINT);
           STS TWCR, R16;
           ; Turn on Alternating LEDS to indicate I2C error
           LDI R20, 0x50;
           OR R20, R21;
           COM R20;
           OUT PORTB, R20;
     RJMP Rx_Complete_ISR_End;
     ;  ----------------- Send data from R3 (DB1), R4 (DB2), R5 (CB), and R6 (BB) to the I2C slave addressed by R10 (END) -------


     Rx_Complete_ISR_End:
RETI;


; For the pulse width counter
OC0Aaddr_ISR:
     ; Turn off the pulse on portB5
     CBI PORTB, PORTB5;
RETI;



; For the base frequency counter
OC0Baddr_ISR:  
     ; Stop the timer
     CLR R16;
     OUT TCCR0B, R16;
    
     ; Clear the timer
     OUT TCNT0, R16;
    
     ; Turn on the pulse on portB5
     SBI PORTB, PORTB5;

     ; Load the prescalar and start the Timer
     LDI R18, (0<<CS02)|(0<<CS01)|(1<<CS00);
     OUT TCCR0B, R18;
RETI;



;------------------------------ END OF FILE ---------------------



Draft Assembly Code for ATTINY261:



.include "tn261def.inc"

;++++++++++++++++++++++++++++++++++++++++++++
; I/O Assignments
; Swap the SPI default port operation from PortB to PortA using USIPP
; PA0 = Input, DI, MISO
; PA1 = Output, DO, MOSI
; PA2 = Input, SCK
; PORTB(PB3-PB0) AND PORTA(PA7-PA4) as the debug output
;
; -------- SPI MESSAGE DECODE (START) ------
; R20 = Last bit of R20 indicates the current received byte is a separator (if 1) or data (if 0)
; R21 = Indicates which register to load with (R11 or R12)
; -------- SPI MESSAGE DECODE (END) --------
;
; ------- PWM SETUP TIMER-1 (START) --------
; OCR1C = Base frequency (R12)
; OCR1A = Pulse width (R11)
; ------- PWM SETUP TIMER-1 (END) ----------

;++++++++++++++++++++++++++++++++++++++++++++

.CSEG

.ORG 0x0000
RJMP RESET;

.ORG USI_OVFaddr
RJMP USI_OVFaddr_ISR; indicates that a byte has been received over the SPI


.EQU SEPARATOR_PWM_BASE_FREQ         = 0x51;
.EQU SEPARATOR_PWM_PULSE_WIDTH       = 0x52;


RESET:
     ; Setup stack pointer
     LDI R17, high(RAMEND);
     LDI R16, low(RAMEND);
     ;OUT SPH, R17;
     OUT SPL, R16;
    
     ;+++++++++++++ I/O setup (START) +++++++++++

     ; Setup PORTA PA2:PA0 for the SPI communication(PA2->I, PA1->O; PA0->I)
     CLR R16;
     OUT DDRA, R16;
     SBI DDRA, 1;

     ; Setup PortB PB3-PB0 as the debug output
     LDI R16, 0x0F;
     OUT DDRB, R16;

     ; Setup PortB PA7-PA4 as the debug output
     SBI DDRA, 7;
     SBI DDRA, 6;
     SBI DDRA, 5;
     SBI DDRA, 4;

     ; Initally set all output ports to zeros
     CBI PORTB, 0;
     CBI PORTB, 1;
     CBI PORTB, 2;
     CBI PORTB, 3;

     CBI PORTA, 4;
     CBI PORTA, 5;
     CBI PORTA, 6;
     CBI PORTA, 7;
     ;+++++++++++++ I/O setup (END) +++++++++++++
    

     ; ++++++++++++ SETUP SPI USING USI FROM EXTERNAL CLOCK (START) ++++++++++++++
     ; Swap the SPI default port operation from PortB to PortA using USIPP
     SBI USIPP, 0;

     ; Enable counter-overflow interrupt
     ;LDI R16, (1<<USIOIE)|(1<<USIWM0)|(1<<USICLK);
     LDI R16, (1<<USIOIE)|(1<<USIWM0)|(1<<USICS1);
     OUT USICR, R16;
    
     ; Clear the counter-overflow flag by writing 1 to USIOIF of USISR register
     LDI R16, (1<<USIOIF);
     OUT USISR, R16;
     ; ++++++++++++ SETUP SPI USING USI FROM EXTERNAL CLOCK (START) ++++++++++++++
    

     ; +++++++++++++++  TIMER1-PWM SETUP (START) ++++++++++++
     ; Enable PLL for the MCU
     LDI R16, (1<<PLLE);
     OUT PLLCSR, R16;

     ; Wait for PLL to lock (~100ms)
     WAIT_FOR_PLL_LOCK:
           IN R16, PLLCSR;
           SBRS R16, PLOCK;          
     RJMP WAIT_FOR_PLL_LOCK;

     ; Set FAST-PWM mode; Clear OC1A(PB1) on compare match
     LDI R16, (1<<COM1A0)|(1<<PWM1A);
     OUT TCCR1A, R16;

     ; Set the base frequency OCR1C (R12) to the top value 0xFF
     SER R16;
     MOV R12, R16;
     OUT OCR1C, R12;
     ; Set the base frequency OCR1A (R11) to the half of maximum 0x0F
     LDI R16, 0x0F;
     MOV R11, R16;
     OUT OCR1A, R11;

     ; Start off TIMER-1 by setting the prescalar in TCCR1B
     LDI R16, (1<<CS10); No prescaling-> Full Speed
     OUT TCCR1B, R16;
     ; +++++++++++++++  TIMER1-PWM SETUP (END) ++++++++++++++
    
     ; ++++++++++++ SPI MESSAGE DECODER SETUP (START) +++++++
     CLR R20;
     ; ++++++++++++ SPI MESSAGE DECODER SETUP (END) +++++++++

     ; Enable interrupts and go
     SEI;

     MASTER_LOOP1:
          
     RJMP MASTER_LOOP1;  
RET;


USI_OVFaddr_ISR: ; use R16 and R17 as the temporary registers
     ; Read the data from USIDR into R18;
     IN R18, USIDR;
    
     ; Clear the counter-overflow flag by writing 1 to USIOIF of USISR register
     LDI R17, (1<<USIOIF);
     OUT USISR, R17;

     ; Process the rest of the data here
     INC R20; Separator or data indicator; if the LSB is 1, received byte is a separator or data otherwise
    
     MOV R16, R20;
     ANDI R16, 0x01;
     CPI R16, 0x00;
     BREQ LOAD_DATA;

     ; Load Separator here and check which separator was received
     MOV R16, R18;
     CPI R16, SEPARATOR_PWM_BASE_FREQ;
     BREQ LOAD_SEPARATOR_PWM_BASE_FREQ;
    
     MOV R16, R18;
     CPI R16, SEPARATOR_PWM_PULSE_WIDTH;
     BREQ LOAD_SEPARATOR_PWM_PULSE_WIDTH;

     ; Wrong Separator received; try again
     CLR R20;
     CLR R21;
     RJMP END_OF_USI_OVFaddr_ISR;
    
     LOAD_SEPARATOR_PWM_BASE_FREQ:
           LDI R21, 0x01; Load next byte into R12 for the base frequency (OCR1C)
     RJMP END_OF_USI_OVFaddr_ISR;

     LOAD_SEPARATOR_PWM_PULSE_WIDTH:
           LDI R21, 0x02; Load next byte into R11 for the pulse width (OCR1A)
     RJMP END_OF_USI_OVFaddr_ISR;


     LOAD_DATA:
           ; Check with register to load
           MOV R16, R21;
           CPI R16, 0x01;
           BREQ LOAD_BASE_FREQ_DATA_INTO_R12;

           ; Check with register to load
           MOV R16, R21;
           CPI R16, 0x02;
           BREQ LOAD_PULSE_WIDTH_DATA_INTO_R11;
          
           ; Else if jump to end
           RJMP END_OF_USI_OVFaddr_ISR;

           LOAD_BASE_FREQ_DATA_INTO_R12:
                MOV R12, R18;
           RJMP END_OF_LOAD_DATA;

           LOAD_PULSE_WIDTH_DATA_INTO_R11:
                MOV R11, R18;
           RJMP END_OF_LOAD_DATA;
          
           END_OF_LOAD_DATA:
                ; Stop the PWM TIMER-1
                CLR R16;
                OUT TCCR1B, R16;
                ; Update the base frequency
                OUT OCR1C, R12;
                ; Update the pulse width
                OUT OCR1A, R11;
                ; Restart the TIMER-1
                LDI R16, (1<<CS10);
                OUT TCCR1B, R16;
           RJMP END_OF_USI_OVFaddr_ISR;

     END_OF_USI_OVFaddr_ISR:
     ; Debug Display upper 4 nibble at PORTA(PA7-PA4)
     ;MOV R18, R16;
     ;ANDI R18, 0xF0;
     ;OUT PORTA, R18;
RETI;





;-------------------------------- END OF FILE -------------------