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.


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 SPI | Alternativa 0 | Alternativa 1 |
| CS | PTC4 | PTA14 |
| SCK | PTC5 | PTA15 |
| MOSI | PTC6 | PTA16 |
| MISO | PTC7 | PTA17 |
A próxima tabela demonstra as opções de pinos para o módulo SPI1.
| Pinos SPI | Alternativa 0 | Alternativa 1 |
| CS | PTE4 | PTB10 |
| SCK | PTE2 | PTB11 |
| MOSI | PTE1 | PTB16 |
| MISO | PTE3 | PTB17 |
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.
| Bit | Macro | Descrição |
| SPIE | SPI_C1_SPIE_MASK | SPI interrupt enable |
| SPE | SPI_C1_SPE_MASK | SPI system enable |
| SPTIE | SPI_C1_SPE_MASK | Permissão de interrupção de transmissão SP |
| MSTR | SPI_C1_MSTR_MASK | Master/slave mode select |
| CPOL | SPI_C1_CPOL_MASK | Clock polarity |
| CPHA | SPI_C1_CPHA_MASK | Clock phase |
| SSOE | SPI_C1_SSOE_MASK | Slave select output enable |
| LSBFE | SPI_C1_LSBFE_MASK | LSB 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.
| Bit | Macro | Descrição |
| SPMIE | SPI_C2_SPMIE_MASK | SPI match interrupt enable |
| Reserved | — | This field is reserved. |
| TXDMAE | SPI_C2_TXDMAE_MASK | Transmit DMA enable |
| MODFEN | SPI_C2_MODFEN_MASK | Master mode-fault function enabl |
| BIDIROE | SPI_C2_BIDIROE_MASK | Bidirectional mode output enable |
| RXDMAE | SPI_C2_RXDMAE_MASK | Receive DMA enable |
| SPISWAI | SPI_C2_SPISWAI_MASK | SPI stop in wait mode |
| SPC0 | SPI_C2_SPC0_MASK | SPI 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.
| Bit | Macro | Descrição |
| Reserved | — | This field is reserved. |
| SPPR | SPI_BR_SPPR(x) | SPI baud rate prescale divisor |
| SPR | SPI_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.
| Bit | Macro | Descrição |
| SPRF | SPI_S_SPRF_MASK | SPI read buffer full flag |
| SPMF | SPI_S_SPMF_MASK | SPI transmit buffer empty flag |
| SPTEF | SPI_S_SPTEF_MASK | SPI transmit buffer empty flag |
| MODF | SPI_S_MODF_MASK | Master 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




