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 10, 2018 at 00:32

Inadequate LA sampling rate? Grounding problems? Inadequate pin speed setting or overloaded pin?

The picture is illegible. Please make a smaller screenshot, or zip it up and insert as attachment.

JW

Posted on June 10, 2018 at 15:49

Got an bunch of blind writes to the registers.

GPIOB->PUPDR = 2; //Pull Down

(overwrite this several times)

Perhaps use subroutines, I'm not convinced of the 'efficiency' of accessing registers in this manner, and high opportunity for error.

This is only going to be NULL once

if (rxBufferPtr)
{
*(rxBufferPtr) = SPI2->DR;
}
// Increment the pointer by one.
rxBufferPtr++;

In other circumstances it won't read the DR and the RXNE will persist.

Not sure what I'm supposed to see in the screen shot, show one sequence that describes the behaviour you want to show.

Is the SPI peripheral expecting 8-bit wide writes? Your code doesn't seem to achieve that.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
Posted on June 11, 2018 at 15:49

Hello Jan,

               Thank you so much for your valuable time on my question. Screenshot attached below for further inputs. Working on your other questions.

Thanks

Muthaiah Murugappan

0690X0000060LDXQA2.png
Posted on June 11, 2018 at 15:56

Hello Jan,

               Thank you so much for your valuable time on my question. The issue with the behavior of the clock and MOSI pin(their irregularities) is the sequence i wanted to show. Working on your other questions to check the respective pins.

Thanks

Muthaiah Murugappan

0690X0000060LDcQAM.png
Posted on June 11, 2018 at 16:47

I'd suggest slowing your SPI clock and increasing the Saleae sample rate to the point you have a 4x ratio.  That should clean up the display.

Posted on June 11, 2018 at 19:54

Hello David,

                   Thank you for your suggestion, will get back to you with the result.

Thanks

Muthaiah Murugappan

Posted on June 11, 2018 at 20:57

Hello Jan,

               I tried to use a keysight infivisionx MSOenabled 3104T Oscilloscope for viewing the clock frequency. It is not the expected one. It shows a constant frequency (of 2MHz), although varying the input parameters( Like Baud Rate,etc.,). If you got some leads, let me know. Thanks for your time again. 

Muthaiah Murugappan

Posted on June 11, 2018 at 20:59

Hello David, 

                   Thank you for the suggestion, but my Saleae hardware doesn't support larger sample rate. I guess, 4x ratio might be difficult with this piece of hardware. Working with slowing down the SPI clock, let me know if you have any leads on this.

Thanks

Muthaiah Murugappan

Posted on June 11, 2018 at 21:17

If I'm reading the displays you've posted correctly it's configured for a 50 kHz sample rate.  If you reduce the number of active channels to only the SPI signals you need you should be able to configure it for a much higher sample rate.  (My old Logic 16 is currently configured for 7 channels @ up to 40 MHz.)