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 -------------------

No comments:

Post a Comment