AVR-GCC. Serial Peripheral Interface Bus (SPI).

Το Serial Peripheral Interface , ή όπως είναι ευρέος γνωστό SPI, είναι ένα full-duplex πρωτόκολλο που δημιούργησε η εταιρία Motorola για την σύνδεση περιφερειακών συσκευών. Το πρωτόκολλο αυτό χρησιμοποιεί τέσσερις γραμμές επικοινωνίας. Τις SCLK (Clock), MISO (Master In, Slave Out), MOSI (Master Out, Slave In) και SS (Slave Select). Όπως και στο I2C Θα υπάρχει μία συσκευή που θα ελέγχει τις υπόλοιπες και ονομάζεται Master. Οι υπόλοιπες ονομάζονται Slaves.

Τεκμηρίωση του ATmega8.
AVR151,Setup And Use of The SPI.
Τεκμηρίωση του MCP3204.
Τεκμηρίωση του MCP4922.

Οι AVR μπορούν να λειτουργήσουν και σε λειτουργία Master και σε λειτουργία Slave. Θα ασχοληθούμε με την περίπτωση που ένας AVR χρησιμοποιείτε σε λειτουργία Master που είναι και η ποιο συνηθισμένη χρήση. Ο δίαυλος SPI έχει τέσσερις τρόπους λειτουργίας. Οι τρόποι αυτοί ρυθμίζουν τον τρόπο με τον οποίο τα δεδομένα συγχρονίζονται προς και από την συσκευή. Έχουμε έλεγχο πολικότητας του clock CPOL και την φάση εισαγωγής τον δεδομένων σε σχέση με το clock, CPHA. Οι συσκευές που που μετέχουν στον δίαυλο πρέπει να λειτουργούν στον ίδιο τρόπο. Πρέπει λοιπόν να προσέχουμε κατά την ρύθμιση του διαύλου.

Ο έλεγχος του SPI στους AVR γίνεται με την βοήθεια τριών καταχωρητών.

SPCR. SPI Control Register.

SPIE (SPI Interrupt Enable):
Το bit αυτό προκαλεί ένα interrupt αν το bit SPIF στον καταχωρητή SPSR και το I bit στον SREG είναι ενεργοποιημένα.

SPE (SPI Enable):
Το bit αυτό ενεργοποιεί τον δίαυλο SPI όταν ενεργοποιείται.

DORD (Data Order):
Όταν το bit αυτό είναι ένα μεταφέρεται πρώτα το LSB. Όταν είναι μηδέν μεταφέρεται το MSB πρώτα.

MSTR (Master/Slave Select):
Όταν το bit αυτό είναι ένα ενεργοποιείτε η λειτουργία Master. Όταν είναι μηδέν ενεργοποιείτε η λειτουργία Slave. Αν το pin SS είναι ρυθμισμένο ως είσοδος και είναι σε λογικό μηδέν ενώ το MSTR είναι ενεργό το MSTR θα απενεργοποιηθεί και το SPIF στον SPSR θα ενεργοποιηθεί. Ο χρήστης θα πρέπει να ενεργοποιήσει εκ νέου του MSTR για να ενεργοποιηθεί η λειτουργία Master.

CPOL (Clock Polarity):
Όταν το bit αυτό είναι σε λογικό ένα το clock σε κατάσταση ηρεμίας είναι σε λογικό ένα. Όταν είναι σε λογικό μηδέν το clock σε κατάσταση ηρεμίας είναι σε λογικό μηδέν.

CPHA (Clock Phase):
Όταν το bit αυτό είναι σε λογικό ένα η λήψη των δεδομένων γίνεται στο ανερχόμενο μέτωπο του clock. Όταν είναι σε λογικό μηδέν η λήψη των δεδομένων γίνεται στο κατερχόμενο μέτωπο του clock.

SPR1..0 (SPI Clock Rate Select):
Τα bit αυτά σε συνδυασμό με το SPI2X από τον SPSR ρυθμίζουν την συχνότητα του clock όταν είμαστε σε λειτουργία Master. Σε λειτουργία Slave δεν κάνουν τίποτε.

SPSR (SPI Status Register).

SPIF (SPI Interrupt Flag):
Όταν ολοκληρώνεται μία μεταφορά δεδομένων το bit αυτό γίνεται ένα και εκτελείτε το αντίστοιχο interrupt αν είναι ενεργοποιημένο το SPIE στον SPCR.

WCOL (Write COLlision Flag):
Το bit αυτό γίνεται ένα όταν εγγράφεται ο SPDR κατά την διάρκεια μεταφοράς δεδομένων. Το bit αυτό γίνεται μηδέν διαβάζοντας αρχικά τον SPDR ενώ είναι ένα και στην συνέχεια έχοντας πρόσβαση στον SPDR.

Bit5..1:
Καθορισμένα για μελλοντικές λειτουργίες.

SPI2X (Double SPI Speed Bit):
Το bit αυτό σε συνδυασμό με τα SPR1 και SPR0 του SPCR ελέγχουν την συχνότητα του clock.

SPDR (SPI Data Register).

Ο καταχωρητής αυτός περιέχει τα προς αποστολή/ανάγνωση δεδομένα του SPI. Εγγραφή στον SPDR ξεκινά την μετάδοση δεδομένων. Ανάγνωση του SPDR προκαλεί την ανάγνωση του buffer του shift register λήψης.

Ας δούμε ένα παράδειγμα επικοινωνίας με τον δίαυλο SPI. Στο παράδειγμα αυτό χρησιμοποιούμε έναν ATmega8 για να επικοινωνήσουμε με έναν ADC τον MCP3204 και έναν DAC τον MCP4922. Με τον ADC Διαβάζουμε την τιμή ενός ποτενσιόμετρου, μεταφέρουμε την τιμή στον ATmega8 (μέσω SPI) και στην συνέχεια μεταφέρουμε την τιμή αυτή (μέσω SPI) στον DAC που δημιουργεί την τάση που διαβάσαμε από το ποτενσιόμετρο.

Τα αρχεία. spi.zip.

#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <stdio.h>
#include "uart.h"

/*Η παρακάτω δήλωση ελέγχει αν στον compiler έχουμε βάλει την παράμετρο F_CPU.
  Αν όχι, την περνά. */

#ifndef F_CPU
    #warning "H F_CPU den exei oristei 8a parei thn timh 4000000"
    #define F_CPU 4000000UL  // Use unsigned long (UL)
#endif

#define BAUD 4800UL //Ρυθμός Baud

// Υπολογισμοί.
#define UBRR_VAL ((F_CPU+BAUD*8)/(BAUD*16)-1) // ”Έξυπνη” στρογγυλοποίηση
#define BAUD_REAL (F_CPU/(16*(UBRR_VAL+1)))   // Πραγματικός Ρυθμός Baud
#define BAUD_ERROR ((BAUD_REAL*1000)/BAUD)    // Σφάλμα ανά 1000 μέρη.

#if ((BAUD_ERROR<990) || (BAUD_ERROR>1010))
  #error To sfalma ston ry8mo Baud einai megalytero toy 1%, mataiosh!
#endif

//adjust UARTsendChar() function for stream
static int UARTsendstream(char c, FILE *stream);
//----set output stream to UART----
static FILE uart_str = FDEV_SETUP_STREAM(UARTsendstream, NULL, _FDEV_SETUP_WRITE);

static int UARTsendstream(char c , FILE *stream){
    uart_putc(c);
    return 0;
}

#define SELECT_ADC PORTB &= ~(1<<PB1)
#define DESELECT_ADC PORTB |= (1<<PB1)

#define SELECT_DAC PORTB &= ~(1<<PB0)
#define DESELECT_DAC PORTB |= (1<<PB0)

void spi_init();
uint16_t read_adc(void);
uint8_t spi_send(uint8_t);


int main(void){

    uint8_t i=0;
    uint16_t adc=0;


    DDRB |= (1<<PB0); // chip select for DAC
    DDRB |= (1<<PB1); // chip select for ADC

      // Αρχικοποίηση του UART.
    uart_init(UART_BAUD_SELECT(BAUD,F_CPU));

    sei(); // Ενεργοποίηση των interrupt;

    stdout = &uart_str;                           // Stream initialization.
    uart_init( UART_BAUD_SELECT(BAUD,F_CPU) );    // UART initialization.

    printf_P(PSTR("AVR SPI Test\n\n"));


  // make sure ADC,DAC is unselected and setup spi
  DESELECT_ADC;
  DESELECT_DAC;

  spi_init();

  while (1) {

    SELECT_ADC;
    adc=read_adc();
    DESELECT_ADC;

    printf_P(PSTR("ADC value: %d\n"),adc);

    SELECT_DAC;
    spi_send(0b11110000|(adc>>8));
    spi_send(adc);
    DESELECT_DAC;
    i++;
    _delay_ms(1000);
  }
}

uint8_t spi_send(uint8_t data){

  SPDR = data;
  while (!(SPSR & (1<<SPIF)));
  return SPDR;
}

void spi_init(){

  // specify pin directions for SPI pins on port B

  // PB5 (SCK) έξοδος.
  DDRB |= (1<<PB5);

  // PB4 (MISO) είσοδος.
  DDRB &= ~(1<<PB4);

  // PB3 (MOSI) έξοδος.
  DDRB |= (1<<PB3);

  // PB2 (SS) enable.
  DDRB |= (1<<PB2);

  SPCR |= (1<<SPE)|(1<<MSTR)|(1<<SPR0);

}

uint16_t read_adc(void){

    uint8_t high_byte,low_byte;
    uint16_t value;

    SELECT_ADC;

    _delay_us(100);

    // Αποστολή του 1ου byte.
    // Μας ενδιαφέρουν τα 3 LSB τα υπόλοιπα
    // μπορεί να έχουν οποιαδήποτε τιμή.
    // STAR bit, SGL bit, D2 bit.
    // Σελίδα 15 του MCP3204.
    spi_send(0b00000110);

    _delay_us(100);

    // Αποστολή 2ου byte.
    // Μας ενδιαφέρουν τα 2 MSB τα υπόλοιπα
    // μπορεί να έχουν οποιαδήποτε τιμή.
    // D1 bit, D2 bit.
    // Σελίδα 15 του MCP3204.
    high_byte = spi_send(0b00000000);

    _delay_us(100);

    // Αποστολή 3ου byte.
    // Μπορεί να έχει οποιαδήποτε τιμή.
    // Σελίδα 15 του MCP3204.
    low_byte = spi_send(0b0000000);

    DESELECT_ADC;

    // Ο ADC επιστρέφει τιμή των 12 bit. Από το high_byte
    // μόνο τα 4 LSB έχουν χρήσιμα δεδομένα.
    value = (high_byte&0b00001111); // Κρατάμε τα 4 LSB.

    // Τα 4 bit που διαβάσαμε είναι τα 4 MSB στα 12 bit
    // που επιστρέψει ο ADC. οπότε τα μετακινούμε
    // 8 θέσεις αριστερά.
    value <<= 8;

    // Προσθήκη των 8 LSB.
    value |= low_byte;

    return value;

}

Παρακάτω βλέπουμε την μεταφορά του byte 0b10011001 με SPI με έναν παλμογράφο.

Advertisements
This entry was posted in AVR, Electronics and tagged , , , , , , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s