mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik PIC24 SPI erzeugt Dauerschleife?


Autor: PIC (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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!

Autor: Daniel V. (danvet)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Meine Glaskugel sagt:
Es passiert ein Reset.

Autor: PIC (Gast)
Datum:

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

Im Grunde mache ich nur:
int SPIWriteRead(int wert){
    SPI1BUF = wert;
    Nop();
    while (!SPI1STATbits.SPIRBF);
    return SPI1BUF;
}

Autor: Daniel V. (danvet)
Datum:

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

Autor: PIC (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert

#include "libpic30.h"

#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <stdbool.h>
#include "xc.h"

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

int befehl = 0;
int dummy = 0;

void InitGPIO(){
    
    __builtin_write_OSCCONL(OSCCON & ~(1<<6));
    
    RPOR6bits.RP56R = 0b000001;
    RPINR18bits.U1RXR = 57;
    
    ANSELB = 0;  

    //Reset   
    LATBbits.LATB9 = 0;
    ODCBbits.ODCB9 = 0;
    TRISBbits.TRISB9 = 0;
    //DIAG   
    LATCbits.LATC4 = 0;
    ODCCbits.ODCC4 = 0;
    TRISCbits.TRISC4 = 0;

    //CS
    LATBbits.LATB0 = 0;
    ODCBbits.ODCB0 = 0;
    TRISBbits.TRISB0 = 0;
    
    __builtin_write_OSCCONL(OSCCON | (1<<6));
    
    PORTBbits.RB0 = 1;
}

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

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

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

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

    // Interrupt Controller Settings
    IFS0bits.SPI1IF = 0; // Clear the Interrupt flag
    IEC0bits.SPI1IE = 1; // Enable the interrupt
   
    DELAY_105uS
    
}

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

    DELAY_105uS
}

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

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

int SPIWriteRead(int wert){
    SPI1BUF = wert;
    Nop();
    while (!SPI1STATbits.SPIRBF);
    return SPI1BUF;
}

int main(void)
{
    
    if(OSSCON.HFIOFS == 1)
    
    InitGPIO();
    InitUART();
    InitSPI();

    
    char* str = "Hello\n\r";
    UARTSendString(str,29);

    PORTBbits.RB9 = 1;
    befehl = 0b1010101010111000;
    
    PORTBbits.RB0 = 0;
    dummy = SPIWriteRead(befehl);
    PORTBbits.RB0 = 1;
    
    while(1){
    
    }
    
}

void __attribute__((interrupt,auto_psv)) _U1TXInterrupt(void)
{
IFS0bits.U1TXIF = 0; // Clear TX Interrupt flag
}


Autor: PIC (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Das
if(OSSCON.HFIOFS == 1)
 ist quatsch und muss weg.

Autor: PIC (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Daniel V. (danvet)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Du hast keinen SPI InterruptHandler implementiert....

Autor: PIC (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Klaus (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: PIC (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Daniel V. (danvet)
Datum:

Bewertung
0 lesenswert
nicht 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
Autor: PIC (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Daniel V. (danvet)
Datum:

Bewertung
0 lesenswert
nicht 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 :-)

Autor: Felix (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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;
}

Autor: jemand (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Weihnachtsmann (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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!

Autor: jemand (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.