cancel
Showing results for 
Search instead for 
Did you mean: 

SPI Strange Output

Muthaiah Murugappan
Associate II
Posted on June 09, 2018 at 00:05

Hello, I'm working on a project involving STM32L476RG Microcontroller. I'm trying to write a piece of code for creating an SPI Driver for my project and seem to have some issues. I'm using Keil v5 IDE. The output doesn't seem to be close to my expected behavior of the Microcontroller.

My logic analyzer output is below. My clock is irregular and MOSI has garbage data. Anybody with any leads, please do let me know. Thanks in advance for your valuable time.

0690X0000060LCtQAM.png

My piece of code:

#include <stdint.h>
#include ''stm32l4xx.h'' // Device header
#include <stdbool.h>
volatile uint8_t* txBufferPtr;// tx buffer pointer
uint16_t txBufferLength;// tx buffer size
volatile uint16_t txBufferCounter;// tx buffer counter
volatile uint8_t* rxBufferPtr;// rx buffer pointer
uint16_t rxBufferLength;// rx buffer size
volatile uint16_t rxBufferCounter;// rx buffer counter
uint8_t datasize;// selected data transfer size [bits]
bool master;// whether SPI is master or slave

/**
 * @briefTransfer data over SPI.
 * @paramchannel - 0-based index of the SPI port of interest
 * @param*txArray - pointer to the transmit buffer
 * @param*rxArray - pointer to the receive buffer
 * @paramlength - number of bytes to transmit
 */
void ST_SPI_Transfer( uint8_t *txArray, uint8_t *rxArray, uint32_t length)
{
// Set up transmit buffer.
txBufferPtr = txArray;
txBufferLength = length;
txBufferCounter = length;
// Set up global receive variables.
rxBufferPtr = rxArray;
rxBufferCounter = length;
rxBufferLength = length;
// Read data register once before enabling the receive interrupt.
// This makes sure the data register is empty and avoids spurious interrupts.
uint32_t val = SPI2->DR;
// This prevents a compiler warning about var being an unused variable.
(void)val;
// Enable receive interrupt.
SET_BIT(SPI2->CR2, SPI_CR2_RXNEIE);
// Enable transmit interrupt.
// This will generate an interrupt as soon as the SPI is enabled.
SET_BIT(SPI2->CR2, SPI_CR2_TXEIE);
// Enable the SPI module if its not enabled already.
// This is done last to prevent spurious SPI activity.
// If the peripheral is disabled...
// (Checking prevents us from interrupting anything already going on.)
if (!(SPI2->CR1 & SPI_CR1_SPE))
{
// Set the enable bit in Control Register 1.
SET_BIT(SPI2->CR1, SPI_CR1_SPE);
}
}
/**
 * @briefInitialize a given SPI device.
 * @remarksThis must be called before anything else.
 * @paramchannel - 0-based index of the SPI port of interest
 * @paramconfigPtr - structure pointer with settings
 */
void ST_SPI_Init()
{
 // Enable the clock for the selected SPI port.
RCC->APB1ENR1 |= RCC_APB1ENR1_SPI2EN;
// Configure phase and polarity (a.k.a. SPI mode).
CLEAR_BIT(SPI2->CR1, SPI_CR1_CPOL);
SET_BIT(SPI2->CR1, SPI_CR1_CPHA);
// Set the master mode bit in Control Register 1.
SET_BIT(SPI2->CR1, SPI_CR1_MSTR);
// Configure format for 1 byte transfer.
MODIFY_REG(SPI2->CR2, SPI_CR2_DS, 0x07);
// Enable Software Slave Management bit.
SET_BIT(SPI2->CR1, SPI_CR1_SSM);
// Set the Slave Select pin high.
SET_BIT(SPI2->CR1, SPI_CR1_SSI);
// Configure speed.
MODIFY_REG(SPI2->CR1, SPI_CR1_BR, 4);
// Set direction.
//SET_BIT(SPI2->CR1, SPI_CR1_BIDIOE);
//Set LSBFirst
SET_BIT(SPI2->CR1, SPI_CR1_LSBFIRST);
//Set NSSP Bit
SET_BIT(SPI2->CR2, SPI_CR2_NSSP);
// Enable the appropriate IRQ handler in the NVIC.
NVIC_EnableIRQ(SPI2_IRQn);
}
int main(void)
{
 
uint8_t readData[4]; // data received from slave
uint8_t writeData[] = { 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9}; // data to send to slave 
// Configure SPI clock using GPIOB PIN 
RCC->AHB2ENR = (1 << 1); //Initialize Port
 GPIOB->MODER &= ~(3 << (2 * 13));
GPIOB->MODER |= (2 << (2 * 13)); //Alternate Function mode
GPIOB->OSPEEDR &= ~(3 <<(2 * 13));
GPIOB->OSPEEDR |= (2 << (2 * 13)); //Medium Speed
GPIOB->OTYPER &= ~(1 << 13);
GPIOB->OTYPER |= (2 << 13); //Output Type
GPIOB->AFR[1] |= (5 << (5*4));
GPIOB->PUPDR = 2; //Pull Down

// Configure SPI MISO pin using GPIOB PIN 
// Set settings common to all GPIO pins used in the test.
GPIOB->MODER &= ~(3 << (2 * 14));
GPIOB->MODER |= (2 << (2 * 14)); //Alternate Function mode
GPIOB->OSPEEDR &= ~(3 <<(2 * 14));
GPIOB->OSPEEDR |= (2 << (2 * 14)); //Medium Speed
GPIOB->OTYPER &= ~(1 << 14);
GPIOB->OTYPER |= (2 << 14); //Output Type
GPIOB->AFR[1] |= (5 << (6*4));
GPIOB->PUPDR = 1;
// Configure SPI MOSI pin using GPIOB PIN 
GPIOB->MODER &= ~(3 << (2 * 15));
GPIOB->MODER |= (2 << (2 * 15)); //Alternate Function mode
GPIOB->OSPEEDR &= ~(3 <<(2 * 15));
GPIOB->OSPEEDR |= (2 << (2 * 15)); //Medium Speed
GPIOB->OTYPER &= ~(1 << 15);
GPIOB->OTYPER |= (2 << 15); 
 GPIOB->AFR[1] |= (5 << (7*4)); 
GPIOB->PUPDR = 1;
// Initialize the SPI peripheral.
ST_SPI_Init();
// If the SPI bus is busy...
while (READ_BIT(SPI2->SR, SPI_SR_BSY)); 
while(1)
{
 //Transfer Data to slave
ST_SPI_Transfer( writeData, readData, sizeof(readData)); 
//Wait for the data to send
while(READ_BIT(SPI2->SR, SPI_SR_BSY));
}
}
void SPI2_IRQHandler(void)
{
// If the RXNE is enabled and its flag is set...
if ((SPI2->SR & SPI_SR_RXNE) && (SPI2->CR2 & SPI_CR2_RXNEIE))
{
// Receive data.
// If we're using 8-bit data transfer size...
if (datasize <= 8)
{
if (rxBufferPtr)
{
*(rxBufferPtr) = SPI2->DR;
}
// Increment the pointer by one.
rxBufferPtr++;
// Decrement the byte counter by one.
rxBufferCounter--;
}
// If we have no more data to transfer...
if (rxBufferCounter == 0)
{
// Wait for the SPI peripheral to finish any transmission.
while (READ_BIT(SPI2->SR, SPI_SR_BSY))
// Disable receive interrupt.
CLEAR_BIT(SPI2->CR2, SPI_CR2_RXNEIE);
}
}
// If the TXE is enabled and its flag is set...
else if ((SPI2->SR & SPI_SR_TXE) && (SPI2->CR2 & SPI_CR2_TXEIE))
{
// Transmit data.
// Transmit one byte.
if (datasize <= 8)
{
SPI2->DR = *(txBufferPtr);
// Increment the data pointer.
txBufferPtr++;
// Decrement the counter of bytes left to read.
txBufferCounter--;
 }
// If we have no more data to transfer...
if (txBufferCounter == 0)
{
// Disable the transmit interrupt.
CLEAR_BIT(SPI2->CR2, SPI_CR2_TXEIE);
}
 }
}
�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?

#stm32-spi-mosi #spi #stm32l476 #hal-spi #mosi
14 REPLIES 14
Posted on June 12, 2018 at 15:54

Hello David,

                  Thank you so much for your reply. I'm using a third party device, i'm not sure whether it is possible to reduce number of channels. Instead, i'm using a keysight infiivision oscilloscope instead. I'm not able to control the frequency by varying parameters (like Baud rate,etc.,). If some leads, please do let me know. Thanks in advance.

                      0690X0000060LEfQAM.png

Thanks

Muthaiah Murugappan

Posted on June 12, 2018 at 16:10

Sorry, I'm out.

Posted on June 12, 2018 at 16:13

No problem. Thank you so much for your valuable time.

Posted on June 12, 2018 at 17:08

Muthaiah Murugappan wrote:

I'm not able to control the frequency by varying parameters (like Baud rate,etc.,).

Are you saying you cannot change the SPI clock frequency? Or the sample rate on the o-scope or logic analyzer? TO slow down the SPI clock rate (and allow the logic analyzer to hopefully see things), change your line in ST_SPI_Init() from

MODIFY_REG(SPI2->CR1, SPI_CR1_BR, 4);

to some higher number. 4 means clk/32 on a 32L433, I presume its the same on your CPU, so try 5 (clk/64) or 6 (clk/128) or even 7 (clk/256) - THAT should really slow down your SPI clock. If you are saying that you have tried changing that line and the SPI clock rate remains the same, then something is messed up elsewhere in your code. Or you aren't running the code you THINK you are running.

When using your digital scope - set it to 'single sweep' mode, with manual trigger (or whatever it calls the opposite of 'auto' trigger). The screen shot you posted shows many sweeps over top of each other. The only clock pulse that is clean is the one that the scope triggered on. The others are sometimes a clock pulse and sometimes just 'high' determined by when the scope semi-randomly re-triggers on a clock pulse SOMEWHERE in the bit stream. By using single sweep you will be see one scope sample.

Posted on June 12, 2018 at 17:57

Hello Bob,

Thank you for your suggestion. I got the SCK clock working better right now.

I had previously tried MODIFY_REG(SPI2->CR1, SPI_CR1_BR, 4); with all values, but was not useful. But with little more deeper insight,

used MODIFY_REG(SPI2->CR1, SPI_CR1_BR, SPI_CR1_BR_0 | SPI_CR1_BR_1 | SPI_CR1_BR2 ); This helped me out.

Thanks for the tip on 'single sweep' as well, would use it in the near future.

Thanks

Muthaiah Murugappan