Biblioteca SPI para a placa FRDM-KL25Z

Olá caro leitor, este é terceiro artigo da série Bibliotecas de software para a FRDM-KL25Z. Neste artigo abordarei o uso da comunicação SPI (Serial Peripheral Interface) na Freedom Board. Para saber mais sobre a comunicação SPI, recomendo a série de artigos sobre Comunicação SPI produzida Francesco Sacco.

As bibliotecas apresentadas são compatíveis com o Kinetis Design Studio IDE e CodeWarrior IDE. Também são facilmente portáveis para as demais placas Freedom Board.

 

Como já abordado anteriormente a Freedom Board KL25Z contém como microcontrolador alvo o MKL25Z128VLK4, que por sua vez possui dois módulos de comunicação SPI (SPI0 e SPI1) que trabalham com dados de 8 bits.

O periférico de SPI da FRDM-KL25Z suporta trabalhar tanto no modo Mestre (MASTER) quanto no modo Escravo (SLAVE). O módulo de comunicação SPI deste microcontrolador possui também interface DMA (Direct Memory Access).

A seguir serão apresentadas as configurações mínimas para trabalhar com o SPI da Freedom Board KL25Z no modo Mestre. Apresentando as funções de inicialização do periférico e funções de escrita e leitura e, por fim, um exemplo de utilização da comunicação SPI.

 

 

Inicializando o SPI

 

Configurando a fonte de Clock:

O primeiro item a ser configurado é habilitar o Clock para o periférico. Para realizar esta tarefa é utilizado o registrador System Clock Gating Control Register 4 (SIM_SCGC4). Para escrever no registrador utiliza as macros SIM_SCGC4_SPI0_MASK para o SPI0 e SIM_SCGC4_SPI1_MASK para o SPI1.

A seguir imagens para ilustrar os parâmetros do registrador.

 

SPI para a placa FRDM-KL25Z

 

Para habilitar o Clock do periférico de SPI deve-se utilizar as seguintes macros:

 

/* habilita clock*/
SIM_SCGC4 |= SIM_SCGC4_SPI0_MASK; // Habilita Clock para SPI0
SIM_SCGC4 |= SIM_SCGC4_SPI1_MASK; // Habilita Clock para SPI1

 

Configurando os pinos do barramento da comunicação SPI:

Como já explicado acima, o microcontrolador presente na FRDM-KL25Z possui dois módulos SPI e cada módulo possui duas opções de pinos para arramento de comunicação SPI.

A tabela a seguir demonstra as opções de pinos para o módulo SPI0.

 

Pinos SPIAlternativa 0Alternativa 1
CSPTC4PTA14
SCKPTC5PTA15
MOSIPTC6PTA16
MISOPTC7PTA17 

 

A próxima tabela demonstra as opções de pinos para o módulo SPI1.

 

Pinos SPIAlternativa 0Alternativa 1
CSPTE4PTB10
SCKPTE2PTB11
MOSIPTE1PTB16
MISOPTE3PTB17 

 

Para configurar os pinos como barramento de comunicação SPI, deve habilitar o Clock para o pinos. Essa operação é feita através do registrador SIM_SCGC5, e deve-se utilizar a macro SIM_SCGC5_PORTC_MASK.

O segundo item a ser configurado é o Signal Multiplexing and Pin Assignments (Multiplexador de sinais do pino), que define a funcionalidade do pino. Para configurar deve utilizar o Pin Control Register n (PORTx_PCRn). Conforme é apresentado nas figuras abaixo, a comunicação SPI é a alternativa de número 2 (ALT2). Deve utilizar a macro PORT_PCR_MUX(x) para configurar o multiplexador de sinais do pino, onde x é alternativa de funcionalidade do pino.

Infelizmente nem todos os pinos estão disponíveis no PinOut da Freedom Board KL25Z. A seguir imagens com os pinos do barramento de comunicação SPI.

 

 

 

Configurando os parâmetros  comunicação SPI:

O próximo item que devemos configurar é o registrador SPI Control Register 1 (SPIx_C1). Trata-se de um registrador de 8 bits, que contém algumas configurações da comunicação SPI.  Conforme podemos observar na figura abaixo, cada bit do registrador corresponde a uma configuração.

 

 

Na tabela abaixo temos cada bit do registrador SPIx_C1 com sua macro de acesso e sua descrição.

 

BitMacroDescrição
SPIESPI_C1_SPIE_MASKSPI interrupt enable
SPESPI_C1_SPE_MASKSPI system enable
SPTIESPI_C1_SPE_MASKPermissão de interrupção de transmissão SP
MSTRSPI_C1_MSTR_MASKMaster/slave mode select
CPOLSPI_C1_CPOL_MASKClock polarity
CPHASPI_C1_CPHA_MASKClock phase
SSOESPI_C1_SSOE_MASKSlave select output enable
LSBFESPI_C1_LSBFE_MASKLSB first (shifter direction)

 

Outro registrador que devemos configurar é o Control Register 2 (SPIx_C2). Trata-se de um registrador de 8 bits, que contém algumas configurações da comunicação SPI.

 

Conforme podemos observar na figura abaixo, cada bit do registrador corresponde a uma configuração.

 

 

Na tabela abaixo temos cada bit do registrador SPIx_C2 com sua macro de acesso e sua descrição.

 

BitMacroDescrição
SPMIESPI_C2_SPMIE_MASKSPI match interrupt enable
Reserved--This field is reserved.
TXDMAESPI_C2_TXDMAE_MASKTransmit DMA enable
MODFENSPI_C2_MODFEN_MASKMaster mode-fault function enabl
BIDIROESPI_C2_BIDIROE_MASKBidirectional mode output enable
RXDMAESPI_C2_RXDMAE_MASKReceive DMA enable
SPISWAISPI_C2_SPISWAI_MASKSPI stop in wait mode
SPC0SPI_C2_SPC0_MASKSPI pin control 0

 

E por fim devemos configurar o registrador Baud Rate Register (SPIx_BR). Esse registrador é responsável pela frequência do clock da transmissão, onde é configurado pelo  Baud Rate Prescale e o Baud Rate Divisor. A seguir temos imagens que ilustra os itens do registrador.

 

 

Na tabela abaixo temos cada bit do registrador SPIx_BR com sua macro de acesso e sua descrição.

 

BitMacroDescrição
Reserved--This field is reserved.
SPPRSPI_BR_SPPR(x)SPI baud rate prescale divisor
SPRSPI_BR_SPR(x)SPI baud rate divisor

 

Os registradores apresentado acima são os itens mínimos que devemos configurar para iniciarmos a comunicação SPI com a Freedom Board KL25Z

 

 

Leitura e Escrita da comunicação SPI

 

Para a leitura e escrita no barramento de comunicação SPI utilizaremos mais dois registradores, são eles: SPI data register (SPIx_D) e SPI status register (SPIx_S).

 

O registrador SPI status register (SPIx_S) é utilizado apenas para leitura. Os seus quatros primeiros bits são reservados. Os bits restantes são para sinalizações, são algumas Flags de leitura.

 

A seguir são apresentadas imagens detalhando o registador SPIx_S.

 

 

Na tabela abaixo temos cada bit do registrador SPIx_S com sua macro de acesso e sua descrição.

 

BitMacroDescrição
SPRFSPI_S_SPRF_MASKSPI read buffer full flag
SPMFSPI_S_SPMF_MASKSPI transmit buffer empty flag
SPTEFSPI_S_SPTEF_MASKSPI transmit buffer empty flag
MODFSPI_S_MODF_MASKMaster mode fault flag
Reserved--This field is reserved

 

O registrador SPI data register (SPIx_D) é utilizado tanto para leitura quanto para escrita. Quando a comunicação está configurada no modo Mestre (Master), é utilizado para carregar o buffer de transmissão. Quando a comunicação estiver configurada Escravo (Slave), é através desse registrador que devemos ler o conteúdo do buffer.

 

A seguir é apresentada imagem que ilustra os detalhes do registrador SPI data register (SPIx_D).

 

 

A seguir é apresentada a biblioteca de software que desenvolvi para utilizar com a Freedom Board KL25Z.

 

Código fonte do arquivo spi.h:

 

/*
 * spi.h
 * Author: Evandro
 */

#ifndef SOURCES_SPI_H_
#define SOURCES_SPI_H_

#include "MKL25Z4.h"
#include "stdbool.h"

#define SPI_0	0
#define SPI_1	1
#define ALT_0	0
#define ALT_1   1
#define PRESCALE_0	1
#define PRESCALE_1	2
#define PRESCALE_2	3
#define PRESCALE_3	4
#define PRESCALE_4	5
#define PRESCALE_5	6
#define PRESCALE_6	7
#define PRESCALE_7	8
#define DIVISOR_0	2
#define DIVISOR_1	4
#define DIVISOR_2	8
#define DIVISOR_3	16
#define DIVISOR_4	32
#define DIVISOR_5	64
#define DIVISOR_6	128
#define DIVISOR_7	256
#define DIVISOR_8	512
#define CS_AUT		1
#define CS_MAN		0

bool spi_init(	bool spi,	// SPI0 ou SPI1
		bool alt,	// SPI0 - PTC ou PTA || SPI1 - PTE ou PTB
		uint8_t pre, 	// Prescale
		uint16_t div,	// Divisor
		bool cs);	// Chip Selet Automatico / Manual

bool spi_send(	bool spi,	// SPI0 ou SPI1
		uint8_t data);	// dado a ser enviado

uint8_t spi_read(bool spi);     // SPI0 ou SPI1

#endif /* SOURCES_SPI_H_ */

 

Código fonte do arquivo spi.h:

 

/*
 * spi.c
 * Author: Evandro
 */
#include "spi.h"

bool spi_init(bool spi,bool alt,uint8_t pre, uint16_t div,bool cs)
{
	switch(spi)
	{
		case SPI_0:
			switch(alt)
			{
				case ALT_0:
					SIM_SCGC5 |= SIM_SCGC5_PORTC_MASK;      //Turn on clock to C module
					//SIM_SCGC4 |= SIM_SCGC4_SPI0_MASK;  	//Enable SPI0 clock
					if(cs == CS_AUT)						//Chip Select Auto
						PORTC_PCR4 = PORT_PCR_MUX(0x2);     //Set PTC4 to mux 2 [SPI0_PCS0]
					PORTC_PCR5 = PORT_PCR_MUX(0x2);    		//Set PTC5 to mux 2 [SPI0_SCK]
					PORTC_PCR6 = PORT_PCR_MUX(0x2);    		//Set PTC6 to mux 2 [SPI0_MOSI]
					PORTC_PCR7 = PORT_PCR_MUX(0x2);    		//Set PTC7 to mux 2 [SPIO_MISO]
				break;

				case ALT_1:
					SIM_SCGC5 |= SIM_SCGC5_PORTA_MASK;      //Turn on clock to A module
					//SIM_SCGC4 |= SIM_SCGC4_SPI0_MASK;   	//Enable SPI0 clock
					if(cs == CS_AUT)						//Chip Select Auto
						PORTA_PCR14 = PORT_PCR_MUX(0x2);   	//Set PTA14 to mux 2 [SPI0_PCS0]
					PORTA_PCR15 = PORT_PCR_MUX(0x2);    	//Set PTA15 to mux 2 [SPI0_SCK]
					PORTA_PCR16 = PORT_PCR_MUX(0x2);    	//Set PTA16 to mux 2 [SPI0_MOSI]
					PORTA_PCR17 = PORT_PCR_MUX(0x2);    	//Set PTA17 to mux 2 [SPIO_MISO]
				break;

				default:
					return false;
				break;
			}
			//Enable SPI0 clock
			SIM_SCGC4 |= SIM_SCGC4_SPI0_MASK;

			//Chip Select Auto
			if(cs == CS_AUT)
				// Set SPI0 to Master & SS pin to auto SS & spi mode in 0,0
				SPI0_C1 = SPI_C1_MSTR_MASK | SPI_C1_SSOE_MASK;
			else
				// Set SPI0 to Master
				SPI0_C1 = SPI_C1_MSTR_MASK;

			// Configure SPI Register C2
			SPI0_C2 = SPI_C2_MODFEN_MASK;   //Master SS pin acts as slave select output

			// Set baud rate prescale divisor to 6 & set baud rate divisor to 4 for baud rate of 1 Mhz
			SPI0_BR = (SPI_BR_SPPR(pre) | SPI_BR_SPR(div));    //  Mhz

			// Enable SPI0
			SPI0_C1 |= SPI_C1_SPE_MASK;

			return true;
		break;

		case SPI_1:
			switch(alt)
			{
				case ALT_0:
					SIM_SCGC5 |= SIM_SCGC5_PORTE_MASK;      //Turn on clock to E module
					//SIM_SCGC4 |= SIM_SCGC4_SPI1_MASK;   	//Enable SPI1 clock
					if(cs == CS_AUT)						//Chip Select Auto
						PORTE_PCR4 = PORT_PCR_MUX(0x2);     //Set PTE4 to mux 2 [SPI1_PCS0]
					PORTE_PCR2 = PORT_PCR_MUX(0x2);    		//Set PTE2 to mux 2 [SPI1_SCK]
					PORTE_PCR1 = PORT_PCR_MUX(0x2);    		//Set PTE1 to mux 2 [SPI1_MOSI]
					PORTE_PCR3 = PORT_PCR_MUX(0x2);    		//Set PTE3 to mux 2 [SPI1_MISO]
				break;

				case ALT_1:
					SIM_SCGC5  |= SIM_SCGC5_PORTB_MASK;     //Turn on clock to B module
					//SIM_SCGC4  |= SIM_SCGC4_SPI1_MASK;  	//Enable SPI1 clock
					if(cs == CS_AUT)						//Chip Select Auto
						PORTB_PCR10 = PORT_PCR_MUX(0x2);    //Set PTB10 to mux 2 [SPI1_PCS0]
					PORTB_PCR11 = PORT_PCR_MUX(0x2);    	//Set PTB11 to mux 2 [SPI1_SCK]
					PORTB_PCR16 = PORT_PCR_MUX(0x2);    	//Set PTB16 to mux 2 [SPI1_MOSI]
					PORTB_PCR17 = PORT_PCR_MUX(0x2);    	//Set PTB17 to mux 2 [SPI1_MISO]
				break;

				default:
					return false;
				break;
			}
			//Enable SPI1 clock
			SIM_SCGC4 |= SIM_SCGC4_SPI1_MASK;

			// Chip Select Auto
			if(cs == CS_AUT)
				// Set SPI0 to Master & SS pin to auto SS & spi mode in 0,0
				SPI1_C1 = SPI_C1_MSTR_MASK | SPI_C1_SSOE_MASK;
			else
				// Set SPI0 to Master
				SPI1_C1 = SPI_C1_MSTR_MASK;

			// Configure SPI Register C2
			SPI1_C2 = SPI_C2_MODFEN_MASK;   //Master SS pin acts as slave select output

			// Set baud rate prescale divisor to 6 & set baud rate divisor to 4 for baud rate of 1 Mhz
			SPI1_BR = (SPI_BR_SPPR(pre) | SPI_BR_SPR(div));    //  Mhz

			// Enable SPI0
			SPI1_C1 |= SPI_C1_SPE_MASK;

			return true;
		break;

		default:
			return false; // error
		break;
	}
}

bool spi_send(bool spi,uint8_t data)
{
	switch(spi)
	{
		case SPI_0:
			//While buffer is not empty do nothing
			while(!(SPI_S_SPTEF_MASK & SPI0_S))
			{
				__asm("nop");
			}
			//Write char to SPI
			SPI0_D = data;

			return true;
		break;

		case SPI_1:
			//While buffer is not empty do nothing
			while(!(SPI_S_SPTEF_MASK & SPI1_S))
			{
				__asm("nop");
			}
			//Write char to SPI
			SPI1_D = data;

			return true;
		break;

		default:
			return false; //erro
		break;
	}
}

uint8_t spi_read(bool spi)
{
	switch(spi)
	{
		case SPI_0:
			  // Wait for receive flag to set
			  while(!(SPI0_S & SPI_S_SPRF_MASK))
			  {
			    __asm("nop");
			  }
			  // SPI0_D;  //Read SPI data from slave
			  return SPI0_D;
		break;

		case SPI_1:
			  // Wait for receive flag to set
			  while(!(SPI1_S & SPI_S_SPRF_MASK))
			  {
			    __asm("nop");
			  }
			  // SPI1_D;  //Read SPI data from slave
			  return SPI1_D;
		break;

		default:
			return false;
		break;
	}
}

 

 

Aplicação 

 

Para demonstrar a utilização da biblioteca de software apresentada acima, criei um circuito divisor de tensão utilizando o potenciômetro digital MCP41010 da empresa Microchip Technology. A ferramenta utilizada para o desenvolvimento da aplicação foi o Kinetis Design Studio IDE.

 

O MCP41010 é potenciômetro digital de 10k com 256 posições. A seguir temos imagem do circuito elétrico da aplicação.

 

 

A seguir temos o código fonte de aplicação:

 

/*
 * main.c
 * Autor: Evandro Teixeira
 */

#include "MKL25Z4.h"
#include "spi.h"

void delay(uint32_t t);

uint8_t ch;

int main(void)
{

	// Incializa PTD1 - LED AZUL
	SIM_SCGC5  |= SIM_SCGC5_PORTD_MASK;
	PORT_PCR_REG(PORTD_BASE_PTR,1) = PORT_PCR_MUX(1);
	GPIOD_PDDR |= (1 << 1);

	// Inicializa PTE4 - Pino CS
	SIM_SCGC5  |= SIM_SCGC5_PORTE_MASK;
	PORT_PCR_REG(PORTE_BASE_PTR,4) = PORT_PCR_MUX(1);
	GPIOE_PDDR |= (1 << 4);

	// Set pino CS = 1
	GPIOE_PSOR |= (1 << 4);

	// Inicaliza SPI 1 - Alternativa de Pino 0
	// PTE2 - SPI1_SCK
	// PTE1 - SPI1_MOSI
	// PTE3 - SPI1_MISO
	// Clock = 165 KHz - Prescale = 3 - Divisor = 4
	spi_init(SPI_1,ALT_0,PRESCALE_2,DIVISOR_1,CS_MAN);

    for (;;)
    {
    	// Intervalo de tempo
    	delay(50000);
    	// CS = 0 - Inicia a comunicação com o MCP41010
    	GPIOE_PDOR &=~ (1 << 4);
    	// Aguarda perido antes de eviar os dados para o MCP41010
    	delay(20);
    	// Envia comando de Escrita para o MCP41010
    	spi_send(SPI_1,0b00010001);
    	// Envia dado  para configurar o valor do potenciometro
    	spi_send(SPI_1,ch);
    	// Aguarda um perido de tempo para encerrar a comunicação com o MCP41010
    	delay(200);
    	// CS = 1 - Encerra a comunicação com o MCP41010
    	GPIOE_PSOR |= (1 << 4);
    	// Incrementa a variavel
    	ch++;
    	if(ch >= 255) ch = 0;
    	// Inverter o valor do pino do LED Azul-(ON ~ OFF)
    	GPIOD_PTOR = (1 << 1);
    }

    return 0;
}

void delay(uint32_t t)
{
	uint32_t i = 0;

	for(i=0;i<=t;i++)
	{
		__asm("nop");
	}
}

 

A seguir temos imagens capturadas do osciloscópio com os sinais da comunicação SPI. Na primeira imagem temos os sinais do CS e Clock. Já na segunda imagem temos os sinais do CS e MOSI.

 

A seguir temos imagem de saída do circuito do divisor de tensão com o MCP41010. E podemos observar no osciloscópio sinal de saída do divisor tensão.

 

 

 

Conclusão

 

Neste artigo foi apresentada mais uma biblioteca de software para a Freedom Board KL25Z e projeto de demonstração com o potenciômetro digital MCP41010.

 

Nos próximos artigos vamos apresentar outras bibliotecas de software (Timer, UART e entre outras) para utilizar com FRDM-KL25. A biblioteca apresentada aqui está disponível no meu GitHub.

 

 

Referências

 

MCP41010

FRDM-KL25Z

KL25 Sub-Family Reference Manual 

Outros artigos da série

<< Bibliotecas para ADC da FRDM-KL25ZBiblioteca I2C para FRDM-KL25Z >>
NEWSLETTER

Receba os melhores conteúdos sobre sistemas eletrônicos embarcados, dicas, tutoriais e promoções.

Obrigado! Sua inscrição foi um sucesso.

Ops, algo deu errado. Por favor tente novamente.

Licença Creative Commons Esta obra está licenciada com uma Licença Creative Commons Atribuição-CompartilhaIgual 4.0 Internacional.

Evandro Teixeira
Desenvolvedor de Sistemas Embarcados. Sou formado Técnico em Instrumentação e Automação Industrial/Mecatrônica pelo Colégio Salesiano Dom Bosco de Americana-SP, cursei o Engenharia Elétrica com Ênfase em Eletrônica pela UNISAL Centro Universitário Salesiano de São Paulo e atualmente estou cursando Superior de Tecnologia em Análise e Desenvolvimento de Sistemas pela UNIP Universidade Paulista.

Deixe um comentário

avatar
 
  Notificações  
Notificar