Interrupts and SPI peripherals

BobTPH

Joined Jun 5, 2013
11,517
You seem to think the SPI peripheral is buffering an entire message in hardware. That is not the case. If you want the message buffered, that is what the interrupt handler does. Each time it gets the interrupt, it pulls the byte out, puts it in a buffer, then starts the transfer of the next byte. If you do it that way, you can treat it similarly to a UART.

But you do not have to use interrupts at all, since the SPI peripheral does not receive data asynchronously like a UART. You can simply be in a loop sending / receiving (which is one operation) data and buffering it until the message is complete.

Unless you see a compelling need, like needing to do other processing during the byte transfer, I would not use interrupts at all. It only makes the code more complex and difficult.
 

Thread Starter

camerart

Joined Feb 25, 2013
3,830
You seem to think the SPI peripheral is buffering an entire message in hardware. That is not the case. If you want the message buffered, that is what the interrupt handler does. Each time it gets the interrupt, it pulls the byte out, puts it in a buffer, then starts the transfer of the next byte. If you do it that way, you can treat it similarly to a UART.

But you do not have to use interrupts at all, since the SPI peripheral does not receive data asynchronously like a UART. You can simply be in a loop sending / receiving (which is one operation) data and buffering it until the message is complete.

Unless you see a compelling need, like needing to do other processing during the byte transfer, I would not use interrupts at all. It only makes the code more complex and difficult.
Hi B,
No, I understand that SPI READs BYTES and puts them in a BUFFER, while any HW INTERRUPT puts the BYTES in a BUFFER.

The INTERRUPTs I'm talking about are PIC in HW. The example at the bottom of #14 is a Comparison HW INTERRUPT, that controls servos. This is used to stop jitter, from conflicts.

NOTE: I'm going from memory, and hope I understood previous explanations over the years, but I'm happy to be corrected.
EDITED
C
 
Last edited:

Ian0

Joined Aug 7, 2020
13,132
Hi B,
I'm assuming that you mean the HW INTERRUPT BYTES.
The initial question is for a general answer, but an example from my programs may be 50 to 70 BYTEs long.
C
The PIC stores the return address when it receives a hardware interrupt. It doesn't store anything else.
So I don't know what you mean by "HW INTERRUPT BYTES"
Please give me the reference from the PIC manual.
 

BobTPH

Joined Jun 5, 2013
11,517
Hi B,
I'm assuming that you mean the HW INTERRUPT BYTES.
The initial question is for a general answer, but an example from my programs may be 50 to 70 BYTEs long.
C
As I said before, you have a misconception about buffering in hardware. See Ian’s answer above.
 

nsaspook

Joined Aug 27, 2009
16,328
SPI is a shift-register interface. On the bare basic interface interrupts CAN happen on: SPI transmit (a byte/word completely clocked SCK out MOSI) and/or SPI receive (byte/word completely clocked SCK in MISO) during SS/Chip-Select

https://www.analog.com/en/analog-dialogue/articles/introduction-to-spi-interface.html
1679272419471.png

The K20 PIC you have doesn't have FIFO, DMA or general memory buffering capabilities on the SPI module. General memory buffering is handled in the applications software or an ISR usually. There is a holding buffer that allows for the last byte/word received to be read while another byte/word is being clocked into the actual shift register..
http://ww1.microchip.com/downloads/...-With-SPI-Using-MSSP-on-PIC18-DS90003265A.pdf
1679338933805.png
 

Thread Starter

camerart

Joined Feb 25, 2013
3,830
The PIC stores the return address when it receives a hardware interrupt. It doesn't store anything else.
So I don't know what you mean by "HW INTERRUPT BYTES"
Please give me the reference from the PIC manual.
Hi I0,
I'm a bit lost then!
An example of HW INTERRUPTs: 18F46K20 section 9.
These are what I've been using, and I thought they are working?
C
 
Last edited:

nsaspook

Joined Aug 27, 2009
16,328
Hi I0,
I'm a bit lost then!
An example of HW INTERRUPTs: 18F46K20 section 9.
These are what I've been using, I and I thought they are working?
C
9.3 Interrupt Response
When an interrupt is responded to, the global interrupt
enable bit is cleared to disable further interrupts. The
GIE bit is the global interrupt enable when the IPEN bit
is cleared. When the IPEN bit is set, enabling interrupt
priority levels, the GIEH bit is the high priority global
interrupt enable and the GIEL bit is the low priority
global interrupt enable. High priority interrupt sources
can interrupt a low priority interrupt. Low priority
interrupts are not processed while high priority
interrupts are in progress.
The return address is pushed onto the stack and the
PC is loaded with the interrupt vector address (0008h
or 0018h). Once in the Interrupt Service Routine, the
source(s) of the interrupt can be determined by polling
the interrupt flag bits in the INTCONx and PIRx
registers. The interrupt flag bits must be cleared by
software before re-enabling interrupts to avoid
repeating the same interrupt.
The “return from interrupt” instruction, RETFIE, exits
the interrupt routine and sets the GIE bit (GIEH or GIEL
if priority levels are used), which re-enables interrupts.
Some old C18 code that shows a ISR that checks for several types of interrupts per pass. This is the NON-vectored interrupt style ISR a K20 would use when using the C language. I don't have SPI in this example but it should be easy to see how it could be added.

C:
#pragma code tm_interrupt = 0x8  // set the ISR vector

void tm_int(void) // code at the interrupt vector address
{
    _asm goto tm_handler _endasm  // jump to the actual ISR code away from the ISR vector address
}
#pragma code

#pragma interrupt tm_handler // define a ISR handler that uses RETFIE to return to the address saved on the stack.

void tm_handler(void) // timer/serial functions are handled here // most of the actual applications code removed
{
    static uint8_t led_cache = 0xff;

    /* check for expected interrupts */
    V.valid = FALSE;

    if (INTCONbits.INT0IF) { // external interrupt 0
        V.valid = TRUE;
        INTCONbits.INT0IF = FALSE;
// stuff
    }

    if (PIR1bits.RCIF) { // is data from RS-232 port
        V.valid = TRUE;
       V.rx_data = RCREG; // read the serial data register to auto clear the interrupt flag
// stuff
    }

    if (PIR1bits.TMR1IF) { //      Timer1 int handler for RPM frequency generator
        V.valid = TRUE;
        PIR1bits.TMR1IF = FALSE; //      clear int flag
// stuff
    }

    if (INTCONbits.TMR0IF) { //      check timer0 irq time timer
        V.valid = TRUE;
        INTCONbits.TMR0IF = FALSE; //      clear interrupt flag
        WriteTimer0(timer0_off);

 // stuff
        }


    /*
     * spurious interrupt handler
     */
    if (!V.valid) {
        if (V.spurious_int++ > MAX_SPURIOUS)
            Reset();
    }

}
 

Thread Starter

camerart

Joined Feb 25, 2013
3,830
9.3 Interrupt Response


Some old C18 code that shows a ISR that checks for several types of interrupts per pass. This is the NON-vectored interrupt style ISR a K20 would use when using the C language. I don't have SPI in this example but it should be easy to see how it could be added.

C:
#pragma code tm_interrupt = 0x8  // set the ISR vector

void tm_int(void) // code at the interrupt vector address
{
    _asm goto tm_handler _endasm  // jump to the actual ISR code away from the ISR vector address
}
#pragma code

#pragma interrupt tm_handler // define a ISR handler that uses RETFIE to return to the address saved on the stack.

void tm_handler(void) // timer/serial functions are handled here // most of the actual applications code removed
{
    static uint8_t led_cache = 0xff;

    /* check for expected interrupts */
    V.valid = FALSE;

    if (INTCONbits.INT0IF) { // external interrupt 0
        V.valid = TRUE;
        INTCONbits.INT0IF = FALSE;
// stuff
    }

    if (PIR1bits.RCIF) { // is data from RS-232 port
        V.valid = TRUE;
       V.rx_data = RCREG; // read the serial data register to auto clear the interrupt flag
// stuff
    }

    if (PIR1bits.TMR1IF) { //      Timer1 int handler for RPM frequency generator
        V.valid = TRUE;
        PIR1bits.TMR1IF = FALSE; //      clear int flag
// stuff
    }

    if (INTCONbits.TMR0IF) { //      check timer0 irq time timer
        V.valid = TRUE;
        INTCONbits.TMR0IF = FALSE; //      clear interrupt flag
        WriteTimer0(timer0_off);

// stuff
        }


    /*
     * spurious interrupt handler
     */
    if (!V.valid) {
        if (V.spurious_int++ > MAX_SPURIOUS)
            Reset();
    }

}
Hi N,
Sadly I can't read 'C', but thanks for your efforts.

I have CODE for both SPI and (what I'm calling) HW INTERRUPT.

I'm asking, can they both run at together, without affecting each other?
C
 

Ian0

Joined Aug 7, 2020
13,132
Hi N,
Sadly I can't read 'C', but thanks for your efforts.

I have CODE for both SPI and (what I'm calling) HW INTERRUPT.

I'm asking, can they both run at together, without affecting each other?
C
Depends what is in your interrupt service routine. If it involves the SPI interface, then "No".
 

Ian0

Joined Aug 7, 2020
13,132
So what is in your interrupt service routine? And what sort of peripheral is generating your hardware interrupt?
 

Thread Starter

camerart

Joined Feb 25, 2013
3,830
So what is in your interrupt service routine? And what sort of peripheral is generating your hardware interrupt?
Hi I0,
This is a general question, so no specifics.
I'm going to accept, that it can't be done, so previous advice I had from other sources is incorrect.
Thank you and all for you efforts.
C
 

Thread Starter

camerart

Joined Feb 25, 2013
3,830
How is what you want to do different from Post #6?
Hi I0,
I don't know, I'm not skilled enough to answer, sorry.

If the thread showed it is possible to run both happily, then I would make the effort to go into more detail, but I now don't know what to do apart from accept it can't be done.
C
 
Top