AVR programación en C – 14 SPI Modo maestro por Hardware

Hola de nuevo a todos, en está ocasión eremos como implementar el protocolo de comunicación SPI en los micros AVR en modo Maestro utilizando el módulo interno de hardware. A diferencia de la entrada anterior en donde se implementó este protocoloe por software (BitBang), en esta entrada se hará la implementación por hardware programando los registro internos del AVR dedicados a este protocolo junto con la conexión de sus pines dedicados.

Contenido

Introducción

Esta entrada pretende ser breve y se dedica únicamente a la programación del módulo SPI de los AVR, si se necesita comprender más a fondo el protocolo les recomiendo leer las entradas donde se explica el protocolo SPI y donde se hace la implementación por software.

El ejemplo ilustrado aquí es para el microcontrolador ATmega16/32, pero puede ser fácilmente migrado a cualquier otro microcontrolador.

Para comenzar veamos qué pines se utilizan para comunicarnos físicamente con los dispositivos SPI, si echamos un ojo al datasheet, veremos que los pines SS, MOSI, MISO y SCK se encuentran en los pines 5, 6, 7 y 8 del microcontrolador, correspondientes al puerto B (PB4 – PB7).

spi-atmega32
Pines SPI del ATmega32

El o los dispositivos esclavos que necesitemos conectar deberán ir a estos pines del microcontrolador. El pin SS tiene relevancia únicamente cuando se configura el módulo SPI en modo esclavo y sirve para detectar la habilitación desde el maestro hacia nuestro microcontrolador esclavo. Para este ejemplo lo utilizaremos como pin de salida para habilitar el dispositivo esclavo, ya que manejaremos únicamente un sólo esclavo.

NOTA: Cabe destacar que estos pines son utilizados por el programador de hardware para realizar operaciones en la memoria flash, eeprom y fusibles del microcontrolador. Hay que tener en cuenta esto a la hora de cargar el programa, ya que si se encuentra otro dispositivo conectado puede presentar problemas ya sea con la comunicación en el esclavo SPI o con la programación del microcontrolador.

Diagrama de conexión

Ya sabemos a que pines conectar nuestro dispositivo de hardware, para este ejemplo el circuito de prueba propuesto será el mismo que utilizamos en la entrada de programación SPI por software si no lo recuerdan se los dejo acá:

74hc595-sch
Conexión del circuito 74xx595 al bus SPI

Descripción de los registros del módulo SPI

Ahora si, entremos en detalle de la programación del módulo. Ya tenemos el diagrama de conexión al microcontrolador, ahora necesitamos saber qué registros son los encargados de controlar el módulo SPI. Este tipo de comunicación es relativamente sencillo y consta únicamente de 3 registros para su configuración y uso: el registro  SPCR, SPSR y SPDR.

SPCR (SPI Control Register): Es el registro de control/configuración del módulo SPI. En él se especifica el modo (Maestro o Esclavo), la frecuencia de trabajo, la polaridad del reloj, el orden de los datos, el muestreo de datos y la habilitación de interrupciones.

SPI-SPCR

  • SPIE: Habilita la interrupción por transmisión completa.
  • SPE: Habilita el módulo SPI.
  • DORD: Establece el orden de los datos. Cuando es 1 los datos se envían desde el bit menos significativo al más significativo. Cuando es 0 los datos se envían desde el bit más significativo al menos significativo.
  • MSTR: Establece el modo de trabajo del módulo SPI, cuando es 1 trabaja en modo maestro y cuando es 0 en modo esclavo. Si el pin SS es configurado como entrada y se le aplica una señal en bajo (0) mientras el bit MSTR es 1, este bit será puesto a nivel lógico 0 y las banderas SPIF en el registro SPSR se pondrá a 1, en este caso se debe configurar de nuevo el modo maestro para poder utilizarlo.
  • CPOL: Establece la polaridad del reloj (señal SCK). Cuando este bit es 1 el pin SCK permanece en alto (1), estableciendo el pulso de reloj como bajada (Falling Edge). Cuando este bit es 0 el pin SCK permanece en bajo (0), estableciendo el pulso de reloj como subida (Rising Edge).

spi-cpol

  • CHPA: Este bit determina cuando se va a muestrear el dato en el bus. Si es 0 el dato de salida es enviado en el primer flanco de la señal de reloj y el dato de entrada es tomado en el segundo flanco (rising o falling dependiendo de CPOL) y si es los datos son enviados y recibidos en el segundo flanco de reloj.

spi-cpha

  • SPR[0:1]: Estos 2 bits establecen la frecuencia de trabajo de la señal SCK dividiendo la frecuencia de reloj del micro por un factor que se muestra en la tabla siguiente:

SPI-FREQ

SPSR (SPI Status Register): Este registro contiene las banderas de estado del módulo SPI.

SPI-SPSR

  • SPIF: Bandera de interrupcion del SPI. Este bit se pone en 1 cuando una transmisión ha sido completada. Si las interrupciones globales se encuentran habilitadas (bit I del registro SREG = 1) se ejecutará la rutina de interrupción asociada al módulo SPI. Esta bandera se limpia ya sea cuando se ejecuta la rutina de interrupción o cuando el registro SPDR es leido.
  • WCOL: Bandera de colisión de escritura. Esta bandera se levanta si se escribe sobre el registro SPDR mientras se esta realizando una trasferencia de datos. Esta bandera es limpiada cuando se lee el registro SPSR o el registro SPDR.
  • SPI2X: Bit de doble velocidad. Cuando se escribe este bit a 1, la frecuencia de la señal SCK se duplica. En modo esclavo sólo se garantiza el funcionamiento con una frecuencia de Fosc/4 o inferior.

SPDR (SPI Data Register): Registro de datos del SPI. Este registro es usado para las transferencias de datos. Escribir en este registro inicia la transferencia y leer de este registro devuelve el dato recibido en la última transmisión.

SPI-SPDR

Ejemplos de programación

Una vez conociendo estos registros es hora de entrar a las entrañas de la programación. A continuación se muestran 2 ejemplos, uno utilizando transmisión de datos simple y otra con interrupciones.

Programa con transmisión simple

El programa transmite una variable de 8 bits cada 150 ms, esta variable se incrementa automáticamente y se reinicia al desbordarse.

//
#include 
#include 

void SPI_MasterInit();
void SPI_MasterTransmit(char data);
void AVRInit();

int main()
{
	char i = 0;
	// Initialize the AVR modules
	AVRInit();

	// Infinite loop
	while(1)
	{
		SPI_MasterTransmit(i++);
		_delay_ms(150);
	}

	return 0;
}

void AVRInit()
{
	/* Configura el módulo SPI en modo maestro */
	SPI_MasterInit();
}

void SPI_MasterInit()
{
	/* Configura MOSI, SCK y SS como salidas, las demas como entradas */
	DDRB = (1<<PB5)|(1<<PB7) | (1 << PB4);

	/* Habilita SPI, Modo Master, establece el reloj a una frecuenca de  fck/16 = 16000000/16 = 1Mhz */
	SPCR = (1 << SPE) | (1 << MSTR) | ( 1<< SPR0);
}

void SPI_MasterTransmit(char data)
{
	/* Habilita el chip esclavo: SS en bajo */
	PORTB &= ~(1<<PB4);

	/* Inicia la transmisión */
	SPDR = data;
	/* Espera a que se complete la transmisión checando el bit SPIF del registro SPSR*/
	while(!(SPSR & (1<<SPIF)));

	/* Deshabilita el chip esclavo: SS en alto */
	PORTB |= (1<<PB4);
}

//

Programa con transmisión por interrupción

El programa tiene el mismo resultado que el anterior, la diferencia es que el incremento de la variable se hace cada que se finaliza la transmisión al ejecutarse la rutina de interrupción.

//

#include 
#include 

void SPI_MasterInit();
void AVRInit();

volatile char i = 0;

/* Rutina de interrupción que se ejecuta con cada tansmisión terminada */
ISR(SPI_STC_vect)
{
    /* Deshabilita el chip esclavo: SS en alto */
	PORTB |= (1<<PB4);
	/* Espera 100ms */
	_delay_ms(100);
	/* Envía el siguiente dato */
	PORTB &= ~(1<<PB4);
	SPDR = i++;
}

int main()
{
	// Initialize the AVR modules
	AVRInit();

	/* Habilita el chip esclavo: SS en bajo */
	PORTB &= ~(1<<PB4);
	/* Inicia la primera transmisión SPI */
	SPDR = i++;

	// Infinite loop
	while(1)
	{
	}

	return 0;
}

void AVRInit()
{
	SPI_MasterInit();
	sei();
}

void SPI_MasterInit()
{
	/* Configura MOSI, SCK y SS como salidas, las demas como entradas */
	DDRB = (1<<PB5)|(1<<PB7) | (1 << PB4);

	/* Habilita SPI, Modo Master, establece el reloj a una frecuenca de  fck/16 = 16000000/16 = 1Mhz */
	SPCR = (1 << SPE) | (1 << MSTR) | ( 1<< SPR0);

	/* Habilita interrupciones por transmision terminada */
	SPCR |= (1 << SPIE);
}
//

Con estos ejemplos termina esta entrada, espero les sea de utilidad y nos seguimos leyendo en otros tutoriales.

Código fuente

c1-blue
SPI Hardware Simple
c1-blue
SPI Hardware Interrupción

 

Dejar un comentario

Crea una web o blog en WordPress.com

Subir ↑