ATMega168 SPI-Übertragung startet nicht

Szenario

Ich habe einen ATMega168 mit einem externen 10-MHz-Quarz. Das Sicherungsbit DIV/8 ist nicht gesetzt. Der Prozessor übernimmt die Ausgänge von 4 Quadratur-Drehgebern. Ihre Drehrichtungsinformation wird mit Hilfe von Pinwechsel-Interrupts und den Pinzuständen dekodiert. Die Rotationsinformationen werden in Steuercodes umgewandelt und über SPI an einen anderen Prozessor gesendet, der einige Motorantriebe übernimmt.

Problem

In dem aufgelisteten Code werden die Drehrichtungsinformationen wie vorgesehen decodiert und in Steuercodes umgewandelt und in Hardware verifiziert. Die SPI-Übertragung startet nicht. Betrachtet man die SPI-Pins am Oszilloskop, bleibt der !SS-Pin hoch und SCK und MOSI bleiben niedrig.

Außer mir haben sich zwei andere Personen diesen Code angesehen. Ich weiß, dass ich etwas Einfaches übersehen muss. Warum startet die Übertragung nicht?

/*Ports:
D.7..D.0 - 4x quadrature rotary encoders, triggering pin change interrupts
B.5 - SCK (SPI clock line)
B.3 - MOSI(SPI data line)
B.2 - !SS (SPI chip select)

This module interfaces with the rotary encoders, converts their 
rotation to control commands, and transfers these commands over 
the serial peripheral interface to the motor controller.
*/

#define LASER_X_L 28
#define LASER_X_R 56
#define LASER_Y_D 84
#define LASER_Y_U 112
#define MIRROR_X_L 140
#define MIRROR_X_R 168
#define MIRROR_Y_D 196
#define MIRROR_Y_U 224
#define NOTHING 0

#include <avr/io.h>
#include <avr/interrupt.h>

void initPorts(void); //set up function for GPIO ports
void initExtInt(void); //set up function for external interrupts
void initSPI(void); //set up function for SPI

int main(void)
{
    initPorts(); //call port set up function
    initExtInt(); //call interrupt set up function
    initSPI(); //call SPI set up function

    while(1)
    {

    }
}

void initPorts(void)
{
    DDRB = 0b00101100; //set SPI pins as outputs, unused pins as inputs
    PORTB = 0b11010011; //pull unused pins high
    DDRD = 0x00; //rotary encoder pins as inputs
    PORTD = 0x00; //pull ups off
}

void initExtInt(void)
{
    sei(); //global interrupt enable
    PCICR = 0x04; //enable Port D pin change interrupts
    PCMSK2 = 0xFF; //enable pin change interrupt on all Port D pins
}

void initSPI(void)
{
    SPCR = 0b01010001; //SPI interrupt disabled, SPI enabled, MSB trasmitted first, 
                        //master, rising edge triggered, sample then set up, fosc/8
    SPSR = SPSR | 0x01; //2x clock speed for fosc/8
}

ISR(PCINT2_vect)
{
    unsigned char reg = PCMSK2; //read pin change mask register into intermediate register
    unsigned char send;

    //laser x - left
    if(((reg & 0x80) > 0) && ((PIND & 0x40) > 0)) 
    {
        send = LASER_X_L;
    }

    //laser x - right
    else if(((reg & 0x40) > 0) && ((PIND & 0x80) > 0)) 
    {
        send = LASER_X_R;
    }

    //laser y - down
    else if(((reg & 0x20) > 0) && ((PIND & 0x10) > 0)) 
    {
        send = LASER_Y_D;
    }

    //laser y - up
    else if(((reg & 0x10) > 0) && ((PIND & 0x20) > 0)) 
    {
        send = LASER_Y_U;
    }

    //mirror x - left
    else if(((reg & 0x08) > 0) && ((PIND & 0x04) > 0)) 
    {
        send = MIRROR_X_L;
    }

    //mirror x - right
    else if(((reg & 0x04) > 0) && ((PIND & 0x08) > 0)) 
    {
        send = MIRROR_X_R;
    }

    //mirror y - down
    else if(((reg & 0x02) > 0) && ((PIND & 0x01) > 0)) 
    {
        send = MIRROR_Y_D;
    }

    //mirror y - up
    else if(((reg & 0x01) > 0) && ((PIND & 0x02) > 0)) 
    {
        send = MIRROR_Y_U; 
    }
    else
    {
        send = NOTHING;
    }

    if(send != NOTHING)
    {
        SPDR = send; //start transmission
    }   
}
Sie decodieren ISR sieht ziemlich verdächtig aus. Haben Sie überprüft, ob tatsächlich Daten in "Senden" abgelegt werden? Löschen Sie Interrupt-Flags richtig? Was ist, wenn Sie mehrere Messwerte gleichzeitig erhalten? Im Moment verarbeiten Sie nur 1 Eingang nach einem Prioritätsschema, ist dies beabsichtigt? Woher wissen Sie, dass diese Eingänge sauber sind? Sie führen keine Entprellung oder digitale Filterung durch. Hast du einen externen Hardwarefilter? Wenn ja, warum, scheint es unnötig teuer zu sein. Was SPI betrifft, erfordert es nicht eine Überprüfung/Löschung des Status-Flags, bevor Daten in das Datenregister geschrieben werden?
Verwenden Sie außerdem niemals NULL als ganzzahligen Wert, es sollte nur für Zeiger verwendet werden.
Sie decodieren ISR sieht ziemlich verdächtig aus. Haben Sie überprüft, ob tatsächlich Daten in "Senden" abgelegt werden? Wie in der Frage angegeben, bereits dort gewesen und in Hardware verifiziert. Löschen Sie Interrupt-Flags richtig? AVR behandelt das Löschen der einzigen erzeugten Flags. Was ist, wenn Sie mehrere Messwerte gleichzeitig erhalten? Das ist ein egal Zustand, weil es extrem unwahrscheinlich ist, dass es passiert.
Im Moment verarbeiten Sie nur 1 Eingang nach einem Prioritätsschema, ist dies beabsichtigt? Ja, jede Arretierung des Drehgebers erzeugt 4 Interrupts, es gibt genug Zeit zwischen ihnen, dass die SPI-Übertragung vor der zweiten enden würde, und der Rest wird keine gültigen Fälle erzeugen. Woher wissen Sie, dass diese Eingänge sauber sind? Indem man sie auf einem Zielfernrohr betrachtet.
Sie führen keine Entprellung oder digitale Filterung durch. Hast du einen externen Hardwarefilter? Die Hardware-Entprellung war schneller und einfacher. Wenn ja, warum, erscheint es unnötig teuer. Der Software-Overhead war unerwünscht, und das ist nicht für die Produktion. Was SPI betrifft, erfordert es nicht eine Überprüfung/Löschung des Status-Flags, bevor Daten in das Datenregister geschrieben werden? Soweit ich irgendwo finden konnte, gehen mir wirklich die Ideen aus.
Zur Entprellung: es würde überhaupt nicht viel Overhead bringen, ein einfacher Vergleich gegen einen alten Wert oder einen Medianfilter mit 3 Samples sollte ausreichen. In Bezug auf SPI: Ich weiß nicht, wie es bei Atmel SPI gemacht wird, aber bei Motorola / Freescale SPI, das sehr ähnlich zu sein scheint (Motorola hat den SPI-Standard erfunden), müssen Sie Flags löschen, indem Sie ein Statusregister lesen. Solche "Lesezugriff löscht Flags"-Implementierungen sind ärgerlich, da ein Debug die gewünschten Flags versehentlich löschen kann. Ich würde RTFM ein zweites Mal über SPI informieren, nur um sicherzugehen.
Warum wurde dies auf einen Elektronikstandort verschoben? Es ist eine 100% softwarebezogene Frage und damit 100% themenbezogen zum Stapelüberlauf. Wenn der Moderator ein PC-Programmierer ohne Erfahrung mit eingebetteten Systemen ist, dann spielen Sie bitte nicht mit Fragen herum, die als eingebettet gekennzeichnet sind.
@Lundin Diese Frage wurde nicht migriert, und Matt Young hat keine ähnliche Frage zu SO offen.

Antworten (1)

Endlich die Lösung für dieses Problem gefunden.

Zunächst musste die SPI-Setup-Funktion angepasst werden.

void initSPI(void)
{
PRR = 0b11111011; //turn power saving on for all peripherals other than SPI
SPCR = 0b11111001; //SPI interrupt enabled, SPI enabled, MSB trasmitted last, 
                    //master, falling edge triggered, set up then sample, fosc/8
SPSR = 0x01; //2x clock speed for fosc/8
}

Außerdem verarbeitet der Prozessor den !SS-Pin im Mastermodus nicht. Es muss zu Beginn der Übertragung niedrig eingestellt und danach manuell wieder hoch gesetzt werden.

if(send != NOTHING)
{
    PORTB =  PORTB & 0b11111011;
    SPDR = send; //start transmission
    while(!(SPSR & (1<<SPIF))); //wait for transmission to finish
    PORTB =  PORTB & 0b11111111;
}
Ich glaube, dass kein Mikrocontroller den SS / CS-Pin verarbeiten wird, da Sie denselben Bus mit mehreren Geräten teilen können und SS / CS verwenden, um auszuwählen, mit welchem ​​​​Gerät Sie kommunizieren.