0

I am trying to use the SPI communication to read data from the ADXL345 accelerometer. I configured the different pins and SPI in master mode, and tried reading the x, y and z axis accelerations.

My issue is that the SPI readings are always 0. I tried debugging to find the issue and I realized that RXNE is never set even though I'm transmitting data and I don't really get why.

I'm using STM32F103 Board.

Here's my code:

#include "Driver_GPIO.h"

#include "stm32f10x.h"

uint8_t RxData[6];
int x,y,z;
float   x_acc,y_acc,z_acc;

void GPIO_Config (void)
{

    
    MyGPIO_Struct_TypeDef NSS={GPIOA,4,Out_OD}; // Output Open Drain
    MyGPIO_Struct_TypeDef SCK={GPIOA,5,AltOut_Ppull}; // Alternate Output Push-Pull
    MyGPIO_Struct_TypeDef MISO={GPIOA,6,In_Floating}; // Input Floating
    MyGPIO_Struct_TypeDef MOSI={GPIOA,7,AltOut_Ppull}; // Alternate Output Push-Pull

    RCC->APB2ENR |= RCC_APB2ENR_IOPAEN; //enable GPIOA clk
    
    MyGPIO_Init(&NSS);
    MyGPIO_Init(&SCK);
    MyGPIO_Init(&MISO);
    MyGPIO_Init(&MOSI);
}
    

void SPI_Enable (void)
{
    SPI1->CR1 |= (SPI_CR1_SPE);   // SPE=1, Peripheral enabled
}

void SPI_Disable (void)
{
    SPI1->CR1 &= ~(SPI_CR1_SPE);   // SPE=0, Peripheral Disabled
}

void CS_Enable (void)
{
    GPIOA->BSRR |= GPIO_BSRR_BR9;
}

void CS_Disable (void)
{
    GPIOA->BSRR |= GPIO_BSRR_BS9;
}

void SPI_Config(void){
  RCC->APB2ENR |= RCC_APB2ENR_SPI1EN;  // Enable SPI1 CLock
    
  SPI1->CR1 |= SPI_CR1_CPOL| SPI_CR1_CPHA;   // CPOL=1, CPHA=1
    
  SPI1->CR1 |= SPI_CR1_MSTR;  // Master Mode
    
  SPI1->CR1 |= (SPI_CR1_BR_0)| (SPI_CR1_BR_1);  // BR[2:0] = 400: fPCLK/16, PCLK2 = 72MHz, SPI clk = 3.375MHz
    
  SPI1->CR1 &= ~SPI_CR1_LSBFIRST;  // LSBFIRST = 0, MSB first
    
  SPI1->CR1 |= (SPI_CR1_SSM) | (SPI_CR1_SSI);  // SSM=1, SSI=1 -> Software Slave Management
    
  SPI1->CR1 &= ~SPI_CR1_RXONLY;  // RXONLY = 0, full-duplex
    
  SPI1->CR1 &= ~SPI_CR1_DFF;  // DFF=0, 8 bit data
    
  SPI1->CR2 = 0;

}

void SPI_Transmission(uint8_t *data, int size){
    uint8_t clear;
    //check flag TxE //
    int i=0;
    while (i<size)
    {
        while (!((SPI1->SR)&(SPI_SR_TXE))){};  // buffer is empty
        *(volatile uint8_t *)&SPI1->DR = data[i];
        i++;
    }
    
    while (!((SPI1->SR)&(SPI_SR_TXE))){};  // buffer is empty

    while (((SPI1->SR)&(SPI_SR_BSY))){}; // buffer not communicating

    
    clear= SPI1->DR; // empty Overrun flag
    clear= SPI1->SR;
}

void SPI_Receive (uint8_t *data,int size)
{
    while (size)
    {
        while (((SPI1->SR)&(SPI_SR_BSY))) {};  // buffer not communicating
        *(volatile uint8_t *)&SPI1->DR = 0;  // dummy data
        while (!((SPI1->SR) &(SPI_SR_RXNE))){};
        // buffer is not empty
        *data++= *(volatile uint8_t *)&SPI1->DR;
        size--;
    }
}

void adxl345_write (uint8_t address, uint8_t value)
{
    uint8_t data[2];
    data[0] = address|0x40;  // multibyte write
    data[1] = value;
    CS_Enable ();  // pull the cs pin low
    SPI_Transmission (data,2);  // write data to register
    CS_Disable ();  // pull the cs pin high
}
    

void adxl345_read (uint8_t address, uint8_t *RxData)
{
    address |= 0x80;  // read operation
    address |= 0x40;  // multibyte read
    CS_Enable ();  // pull the pin low
    SPI_Transmission (&address,1);  // send address
    SPI_Receive (RxData,6);  // receive 6 bytes data
    CS_Disable ();;  // pull the pin high
}

void adxl345_init (void)
{
    adxl345_write (0x31, 0x01);  // data_format range= +- 4g
    adxl345_write (0x2d, 0x00);  // reset all bits
    adxl345_write (0x2d, 0x08);  // power_cntl measure and wake up 8hz
}
int main(void)
    
{ 
    GPIO_Config();
    SPI_Config();
    SPI_Enable();
    adxl345_init();

    do{
        adxl345_read(0x32,RxData);
        x = ((RxData[1]<<8)|RxData[0]); // DATA X0, X1
        y = ((RxData[3]<<8)|RxData[2]); // DATA Y0, Y1
        z = ((RxData[5]<<8)|RxData[4]); // DATA Z0, Z1
        
        // Scale Factor for Xout, Yout and Zout is 7.8 mg/LSB for a +-4g, 10-bit resolution
        // ==> multiply by 0.0078 to get real acceleration values
        
        x_acc= x * 0.0078; 
        y_acc= y * 0.0078;
        z_acc= z * 0.0078;
        
    }while(1);
    

}

zalix
  • 1
  • 2
  • Please use the same format rule for all your code, don't mix different coding styles. What is inside `Driver_GPIO.h`? Did you see something with an oscilloscope when you measure any of the used SPI lines while transmitting data? – 12431234123412341234123 Oct 25 '21 at 19:45
  • Driver_GPIO.h is just used to configure my 4 pins (SCK,MOSI,MISO,NSS). I also don't have an oscilloscope so I didn't try it. – zalix Oct 25 '21 at 20:26
  • 1
    Shouldn't you actually check the TXE=1 flag for being ready to transmit, and not just the BUSY flag? And btw. can you actually first try to read the DEVID, to check your SPI communication? Then check the settings of the chip. – kesselhaus Oct 25 '21 at 23:06
  • @kesselhaus That makes sense, I will add that. Also by DEVID, do you mean reading the 0x00 address? (I'm kind of a beginner so my apologies). If yes, I did try it and couldn't read anything (0 again..). – zalix Oct 25 '21 at 23:21
  • 1
    `*(volatile uint8_t *)&SPI1->DR` is very fishy. Why the cast? Hiding bugs doesn't make them go away. – Lundin Oct 26 '21 at 10:00
  • "I also don't have an oscilloscope" Then I suggest you pick up this (any embedded systems) project when you have access to one. Apart from staring at source code being an inefficient way of troubleshooting, any errors you have might as well be caused by hardware. – Lundin Oct 26 '21 at 10:03
  • regarding this kind of statement: *data++= *(volatile uint8_t *)&SPI1->DR;` it is very necessary to assure the desired order of operations for: `*data++` to be exactly what you want because the `++` has higher precedence than `*` – user3629249 Oct 26 '21 at 22:55
  • @zalix yes, I meant this, since this contains a defined non-zero value. Also see my comment below to 0___ answer, the manual states, registers have to be accessed 16bit- or 32bit-wise, so remove this cast `(volatile uint8_t*)` when reading/writing the DR register, since the compiler might create here an 8bit wise access. This could impact the transfer from/to the SPI shift register `uint16_t x = 0; SPI->DR=x; ... x = SPI->DR; *data = (uint8_t)x; data++;` – kesselhaus Oct 28 '21 at 00:16

2 Answers2

3

As already stated you have a lot of issues here

  1. Why NSS pin is configured open-drain? Typically CS lines are push-pull. I don't know the schematics, but this is the first time I see an open-drain CS
  2. In GPIO_Config NSS is pin 4, yet pin 9 is toggled from CS_Enable
  3. If F1 series there is separate clocking bit for the alternate functions, it's not enabled

It is also weird that you are telling that RXNE is always zero and the readings returns zero. Your code should stuck in while loops if RXNE stays zero

Flexz
  • 686
  • 1
  • 3
  • 13
  • I agree with all, may be first item no matter because I see pull-up resistors on ADXL345 schematics. – mryldz Oct 26 '21 at 09:14
  • 1
    @mryldz OD with pull-up resistor will lead to a slow rising edge. It wouldn't matter if exchanges are rare. But if exchnages are frequent, with no pause - CS may not rise to the logical one level between transfers, that in turn may break subsequent transfers. You'd need a oscilloscope to tell for sure – Flexz Oct 26 '21 at 11:27
  • @Flexz Since NSS is configured in software mode, RM says "Not used. Can be used as a GPIO", so I wasn't really sure what to do about it. I still need to pull the cs pin low/high whenever I write data. Also my code gets stuck in the first RXNE while loop since it's always 0. – zalix Oct 26 '21 at 11:57
  • @zalix, so which pin is connected to the ADXL345's nCS input? If it's PA9, then where is it configred? It must be push-pull gpio output. By default all pins are inputs. If PA4 is not used in SPI communications - it's confiration doesn't matter – Flexz Oct 26 '21 at 13:05
1

I will not analyze magic numbers as I do not have time check every number in the RM. But you have many obvious issues.

  1. Deley or readback is required after enabling the peripheral clock. You instantly set the registers which is wrong. Add __DSB(); or readback (for example (void)RCC->APB2ENR;). Same for all peripherals
0___________
  • 60,014
  • 4
  • 34
  • 74
  • Hmm .. better read the docs before such comment .. `The peripheral registers have to be accessed by half-words (16 bits) or words (32 bits).` So, even `DR` register should be accessed in 16bit, not 8bits. – kesselhaus Oct 25 '21 at 22:35
  • @kesselhaus you right - Iwas not using f1 for many years. Newer ones have FIFO and this kind casting is necessary – 0___________ Oct 26 '21 at 00:30