PIC24 SPI erzeugt Dauerschleife?

Gast #5490455
Lesenswert?

Moin Leute,

ich habe das Problem, dass mein Controller (PIC24EP) in eine Art 
Dauerschleife geht sobald ich "SPI1BUF = Wert;" ausführe. Ich verschicke 
am Anfang der Main per UART einen String. Dieser wird einmal verschickt, 
sobald ich den SPI1BUF auskommentiere. Wenn ich ihn nicht 
auskommentiere, verschickt mein Controller in wiederholter Schleife 
immer wieder den String vom Anfang der Main. Wisst ihr, woran das liegen 
könnte?

Vielen Dank!
Gast #5490503
Lesenswert?

Das würde ich auch annehmen. Kann die SPI Routine ein Reset erzeugen?

Im Grunde mache ich nur:
1
int SPIWriteRead(int wert){
2
    SPI1BUF = wert;
3
    Nop();
4
    while (!SPI1STATbits.SPIRBF);
5
    return SPI1BUF;
6
}
Gast #5490520
Lesenswert?

1
#include "libpic30.h"
2

3
#include <stdio.h>
4
#include <stdlib.h>
5
#include <stddef.h>
6
#include <stdbool.h>
7
#include "xc.h"
8

9
#define FP 3685000
10
#define BAUDRATE 9600
11
#define BRGVAL (((FP/BAUDRATE)/16)-1)
12
unsigned int i;
13
#define DELAY_105uS asm volatile ("REPEAT, #4201"); Nop(); // 105uS delay
14

15
int befehl = 0;
16
int dummy = 0;
17

18
void InitGPIO(){
19
    
20
    __builtin_write_OSCCONL(OSCCON & ~(1<<6));
21
    
22
    RPOR6bits.RP56R = 0b000001;
23
    RPINR18bits.U1RXR = 57;
24
    
25
    ANSELB = 0;  
26

27
    //Reset   
28
    LATBbits.LATB9 = 0;
29
    ODCBbits.ODCB9 = 0;
30
    TRISBbits.TRISB9 = 0;
31
    //DIAG   
32
    LATCbits.LATC4 = 0;
33
    ODCCbits.ODCC4 = 0;
34
    TRISCbits.TRISC4 = 0;
35

36
    //CS
37
    LATBbits.LATB0 = 0;
38
    ODCBbits.ODCB0 = 0;
39
    TRISBbits.TRISB0 = 0;
40
    
41
    __builtin_write_OSCCONL(OSCCON | (1<<6));
42
    
43
    PORTBbits.RB0 = 1;
44
}
45

46
void InitSPI(){
47
    
48
    /* The following code sequence shows SPI register configuration for    Master mode */
49
    IFS0bits.SPI1IF = 0; // Clear the Interrupt flag
50
    IEC0bits.SPI1IE = 0; // Disable the interrupt
51

52
    // SPI1CON1 Register Settings
53
    SPI1CON1bits.DISSCK = 0; // Internal serial clock is enabled
54
    SPI1CON1bits.DISSDO = 0; // SDOx pin is controlled by the module
55
    SPI1CON1bits.MODE16 = 1; // Communication is word-wide (16 bits)
56
    SPI1CON1bits.MSTEN = 1; // Master mode enabled
57
    SPI1CON1bits.SMP = 0; // Input data is sampled at the middle of data output time
58
    SPI1CON1bits.CKE = 0; // Serial output data changes on transition from
59

60
    // Idle clock state to active clock state
61
    SPI1CON1bits.CKP = 0; // Idle state for clock is a low level;
62

63
    // active state is a high level
64
    SPI1STATbits.SPIEN = 1; // Enable SPI module
65
    SPI1CON1bits.SSEN = 0;
66

67
    // Interrupt Controller Settings
68
    IFS0bits.SPI1IF = 0; // Clear the Interrupt flag
69
    IEC0bits.SPI1IE = 1; // Enable the interrupt
70
   
71
    DELAY_105uS
72
    
73
}
74

75
void InitUART(){
76
    
77
    U1MODEbits.STSEL = 0;       // 1-Stop bit
78
    U1MODEbits.PDSEL = 0;       // No Parity, 8-Data bits
79
    U1MODEbits.ABAUD = 0;       // Auto-Baud disabled
80
    U1MODEbits.BRGH = 0;        // Standard-Speed mode
81
    U1BRG = BRGVAL;             // Baud Rate setting for 9600
82
    U1STAbits.UTXISEL0 = 0;     // Interrupt after one TX character is transmitted
83
    U1STAbits.UTXISEL1 = 0;
84
    IEC0bits.U1TXIE = 1;        // Enable UART TX interrupt
85
    U1MODEbits.UARTEN = 1;      // Enable UART
86
    U1STAbits.UTXEN = 1;        // Enable UART TX
87
                                /* Wait at least 105 microseconds (1/9600) before sending first char */
88

89
    DELAY_105uS
90
}
91

92
void UARTSendString(const char* str, const uint8_t length) {
93
    int i = 0;
94
    for (i=0 ; i<length && str[i]!='\0' ; i++) {
95
        UARTSendChar(str[i]);
96
    }
97
}
98

99
void UARTSendChar(const char c) {
100
    while (U1STAbits.TRMT == 0);    // Wait for buffer to be empty
101
    U1TXREG = c;
102
}
103

104
int SPIWriteRead(int wert){
105
    SPI1BUF = wert;
106
    Nop();
107
    while (!SPI1STATbits.SPIRBF);
108
    return SPI1BUF;
109
}
110

111
int main(void)
112
{
113
    
114
    if(OSSCON.HFIOFS == 1)
115
    
116
    InitGPIO();
117
    InitUART();
118
    InitSPI();
119

120
    
121
    char* str = "Hello\n\r";
122
    UARTSendString(str,29);
123

124
    PORTBbits.RB9 = 1;
125
    befehl = 0b1010101010111000;
126
    
127
    PORTBbits.RB0 = 0;
128
    dummy = SPIWriteRead(befehl);
129
    PORTBbits.RB0 = 1;
130
    
131
    while(1){
132
    
133
    }
134
    
135
}
136

137
void __attribute__((interrupt,auto_psv)) _U1TXInterrupt(void)
138
{
139
IFS0bits.U1TXIF = 0; // Clear TX Interrupt flag
140
}
Gast #5490547
Lesenswert?

PIC schrieb im Beitrag #5490543:
> Ist das denn notwendig?

Selbstverständlich, da du ja

PIC schrieb im Beitrag #5490520:
> IEC0bits.SPI1IE = 1; // Enable the interrupt

im Code hast. Da hast du ja sogar im Kommentar geschrieben, daß du einen 
Interrupt haben willst.

MfG Klaus
Gast #5490549
Lesenswert?

Klaus schrieb:
> PIC schrieb im Beitrag #5490543:
>> Ist das denn notwendig?
>
> Selbstverständlich, da du ja
>
> PIC schrieb im Beitrag #5490520:
>> IEC0bits.SPI1IE = 1; // Enable the interrupt
>
> im Code hast. Da hast du ja sogar im Kommentar geschrieben, daß du einen
> Interrupt haben willst.
>
> MfG Klaus

Und ein fehlender Interrupt erzeugt mit ein Reset?

Mir ist nicht ganz klar, wie ich den Interrupt programmiere. Ist es das 
selbe wie beim UART?
Gast #5490559
Lesenswert?

Daniel V. schrieb:
> Aktivere den Interrupt einfach nicht, dann brauchst du auch keinen
> Handler dazu.
> Falls du ihn aktivierst, wir der Controller den entsprechenden
> Interruptvektor anspringen. Wenn da nix steht, dann gibts halt einen
> Reset.

Ah klasse! Vielen lieben Dank! Da habe ich nicht ganz zuende gedacht.

Wozu wäre ein Interrupt bei einer SPI Routine notwendig?
Gast #5493108
Lesenswert?

Hallo Leute,

ich habe das ganze jetzt als Interrupt probiert. Leider funktionert die 
SPI Ansteuerung dann nicht. Wisst ihr woran das liegen könnte? Interrupt 
ist aktiviert.

void __attribute__((interrupt,auto_psv)) _SPI1Interrupt(void)
{
IFS0bits.SPI1IF = 0; // Clear SPI Interrupt flag
PORTBbits.RB0 = 0;
SPI1BUF = 0x3D21;
while (!SPI1STATbits.SPIRBF);
PORTBbits.RB0 = 1;
}
Gast #5672117
Lesenswert?

Felix schrieb:
> Hallo Leute,
>
> ich habe das ganze jetzt als Interrupt probiert. Leider funktionert die
> SPI Ansteuerung dann nicht. Wisst ihr woran das liegen könnte? Interrupt
> ist aktiviert.
>
> void __attribute__((interrupt,auto_psv)) _SPI1Interrupt(void)
> {
> IFS0bits.SPI1IF = 0; // Clear SPI Interrupt flag
> PORTBbits.RB0 = 0;
> SPI1BUF = 0x3D21;
> while (!SPI1STATbits.SPIRBF);
> PORTBbits.RB0 = 1;
> }

Das da:
> while (!SPI1STATbits.SPIRBF);
tut man nicht..
In einer ISR sollte man niemals warten. Warum? Weil man alle 
niederpriorigeren Interrupts blockiert. So kann man beispielsweise ein 
paar Bytes bei der UART-Kommunikation verpassen. Darum ist das ganz 
allgemein keine gute Idee.

Du möchtest vermutlich das Chipselect bedienen, oder? Da wäre es 
sinnvoller, das Chipselect des SPI-Modules zu verwenden. Dein Controller 
hat Peripheral Pin select und SS kann man auf RB0 mappen, soweit ich das 
gesehen habe.

Du hast ein PICkit3, oder? häng mal einen Breakpoint in die ISR.

Im Übrigen ist SPIRBF für den Recieve, du meinst vermutlich SPITBF?
Gast #5672188
Lesenswert?

jemand schrieb:
> Das da:
>> while (!SPI1STATbits.SPIRBF);
> tut man nicht..
> In einer ISR sollte man niemals warten. Warum? Weil man alle
> niederpriorigeren Interrupts blockiert. So kann man beispielsweise ein
> paar Bytes bei der UART-Kommunikation verpassen. Darum ist das ganz
> allgemein keine gute Idee.


Du widersprichst Dir. "Niemals warten" ist natürlich Bullshit.
Beim STM32 z.B. ist es sehr sinnvoll, im DMA Stream Interrupt nach dem 
Transfer Complete auf das Zurücksetzen des BSY-Flags zu warten, bevor 
man den SPI abschaltet.

Generell sind solche Pauschalaussagen Unsinn, auch wenn hier im Forum 
einige sie penetrant wiederholen.
Daher: Hirn einschalten!
Gast #5672258
Lesenswert?

Weihnachtsmann schrieb:
> Du widersprichst Dir. "Niemals warten" ist natürlich Bullshit.
> Beim STM32 z.B. ist es sehr sinnvoll, im DMA Stream Interrupt nach dem
> Transfer Complete auf das Zurücksetzen des BSY-Flags zu warten, bevor
> man den SPI abschaltet.

In der ISR? Das ist nicht dein Ernst, oder?

Wie gesagt, ein PIC24 hat priorisierte Interrupts. Du legst alle 
niederpriorigeren lahm.

Warum, zum Henker, sollte man das wollen?
Der Controller kan im Endeffekt nicht mehr reagieren. Wenn man Dinge wie 
Regler oder Überstromabschaltungen drin hat, geht die Schaltung hoch, 
nur weil man in der ISR auf ein Flag wartet. Das ist sch...schlecht.

In diesem Fall gibt es mehrere besser Möglichkeiten:
- Chipselect vom SPI-Modul verwenden
- Den TX Interrupt so konfigurieren, dass er kommt, sobalt das Senden 
fertig ist

Ich würde das so lösen, wenn man ein Chipselect UNBEDINGT manuell machen 
will:
- TX-Interrupt so konfigurieren, dass er triggert, wenn er mit dem 
senden fertig ist (Yupp, ein PIC24 kann das)
- Chipselect setzen, senden starten
- In der ISR Pin rücksetzen oder SPI abschalten oder weitersenden

Aber man blockiert in der ISR nicht den ganzen Controller. Auch keinen 
STM32.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren