Forum: Mikrocontroller und Digitale Elektronik PIC24 SPI erzeugt Dauerschleife?


von PIC (Gast)


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!

von Daniel V. (danvet)


Lesenswert?

Meine Glaskugel sagt:
Es passiert ein Reset.

von PIC (Gast)


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
}

von Daniel V. (danvet)


Lesenswert?

Vielleicht solltest du deinen Code komplett posten und jemand mit PIC24 
Erfahrung könnte dir weiter helfen.
Gibt's einen Watchdog?

von PIC (Gast)


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
}

von PIC (Gast)


Lesenswert?

Das
1
if(OSSCON.HFIOFS == 1)
 ist quatsch und muss weg.

von PIC (Gast)


Lesenswert?

Daniel V. schrieb:
> Vielleicht solltest du deinen Code komplett posten und jemand mit
> PIC24
> Erfahrung könnte dir weiter helfen.
> Gibt's einen Watchdog?

Ich habe keinen Watchdog implementiert.

von Daniel V. (danvet)


Lesenswert?

Du hast keinen SPI InterruptHandler implementiert....

von PIC (Gast)


Lesenswert?

Daniel V. schrieb:
> Du hast keinen SPI InterruptHandler implementiert....

Ist das denn notwendig? Ich finde nirgends Literatur dazu. Laut ISBN: 
978-3-645-65273-5 ist keiner notwendig.

von Klaus (Gast)


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

von PIC (Gast)


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?

von Daniel V. (danvet)


Lesenswert?

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.
Ansonsten musst du ihn programmieren wie beim UART.

: Bearbeitet durch User
von PIC (Gast)


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?

von Daniel V. (danvet)


Lesenswert?

PIC schrieb im Beitrag #5490559:
> Wozu wäre ein Interrupt bei einer SPI Routine notwendig?

Gegenfrage:
Wozu wäre der Interrupt beim UART notwendig? Immerhin hast du einen 
implementiert, wenn auch etwas merkwürdig :-)

von Felix (Gast)


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

von jemand (Gast)


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?

von Weihnachtsmann (Gast)


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!

von jemand (Gast)


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.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.