cancel
Showing results for 
Search instead for 
Did you mean: 

How to do a SPI communication on STM32F7 without DMA

ARami.8
Associate II

HI everyone, i'm trying to do an a SPI communication between the board STM32F746 and the MAX7219 to controller a matrix. i'm working in keil uvision 5 and programming without libraries, DMA, CMSIS or other. i programm through registers putting '1'. i want a full-duplex communication, as slave and onliy are one slave. i add the code that i do. it's on spanish that is my languaje. i hope that all can understand me and solve this. thanks

// Code

/*

Pines:

PB3: SCL

PB4: MISO 

PB5: MOSI 

*/

// ************************ LIBRERIAS ************************

#include <stdio.h>

#include "STM32F7xx.h"

// ***********************************************************

// ************************ VARIABLES GLOBALES ***************

// ***********************************************************

// ************************ FUNCIONES ************************

void enviar_datos(char comando, char dato){

char recibido=0;

// Espera mientras la busy flag esta activa

while(!(SPI3->SR & SPI_SR_TXE)){} // Mientas la busy flago esta en 1 no se

SPI3->DR=comando; //

while(SPI3->SR & SPI_SR_RXNE){} // Tiempo de espera mientras se vacia el buffer

recibido=SPI3->DR;

while(!(SPI3->SR & SPI_SR_TXE)){} // Mientas la busy flago esta en 1 no se puede tr

SPI3->DR=dato; //

while(SPI3->SR & SPI_SR_RXNE){} // Tiempo de espera mientras se vacia el buffer

recibido=SPI3->DR;

}

char leer_datos(char direccion){

char recibido=0;

// Espera mientras la busy flag esta activa

while(!(SPI3->SR & SPI_SR_TXE)){} // Mientas la busy flago esta en 1 no se puede

SPI3->DR=direccion; //

while(SPI3->SR & SPI_SR_RXNE){} // Tiempo de espera mientras se vacia el buffer

recibido=SPI3->DR;

while(!(SPI3->SR & SPI_SR_TXE)){} // Mientas la busy flago esta en 1 no se puede

SPI3->DR=0x00; //

while(SPI3->SR & SPI_SR_RXNE){} // Tiempo de espera mientras se vacia el buffer

return SPI3->DR; 

}

// ***********************************************************

// ************************ INTERRUPCIONES *******************

extern "C"{

}

// ***********************************************************

// ************************ MAIN *****************************

int main(void){

// ******************** PUERTOS **************************

RCC->AHB1ENR |=0x03; // Activo el puerto A y B

// *******************************************************

// ******************** PINES ****************************

GPIOA->MODER |=0x80000000; // Alternativo pin 15

GPIOA->AFR[1] =0x60000000; // Funcion alterna 6

GPIOB->MODER |=0xA80; // Alternativo pines 3 a 5 

GPIOB->AFR[0] =0x666000; // Funcion alterna 6 

// *******************************************************

// ******************** SPI ******************************

RCC->APB1ENR |=RCC_APB1ENR_SPI3EN; // Activo el Habilitador del SPI 3

SPI3->CR1 |=0x31; // Configuracion del BaudRate as 256

SPI3->CR1 |=SPI_CR1_CPHA; // Configuracion de la fase del SLC, la he

SPI3->CR1 |=SPI_CR1_CPOL; // Configuracion de la polaridad del Reloj,

SPI3->CR1 |=SPI_CR1_SSM; // Activo el asistente de slave select

SPI3->CR1 &=~SPI_CR1_RXONLY; // Configuracion para que la Full-duplex

SPI3->CR1 &=~SPI_CR1_BIDIMODE; // Configuracion para tener 2 lineas

SPI3->CR1 &=~SPI_CR1_LSBFIRST; // Configuracion para que se transmita

SPI3->CR1 |=SPI_CR1_CRCEN; // No necesito CRC

SPI3->CR1 |=SPI_CR1_MSTR; // Configuracion del SPI como MASTER

SPI3->CR1 |=SPI_CR1_SSI; 

SPI3->CR1 &=~SPI_CR1_CRCL; // tamaño de 8 bits

// CR2

SPI3->CR2 |=0x700; // Configuracion del tamaño de la informacion a

SPI3->CR2 |=SPI_CR2_SSOE; // Activo el output SS para tener un unico

SPI3->CR2 &=~SPI_CR2_FRF; // SPI en modo normal 

SPI3->CR2 &=~SPI_CR2_NSSP; // No deseo pulsos en la linea SS

SPI3->CR2 |=SPI_CR2_FRXTH; // Se activa la interrupcion Rx si recibe 8 bits

SPI3->CR1 |=SPI_CR1_SPE; // Activo el periferico 

enviar_datos(0x04,0x00);

// *******************************************************

// ******************** MAIN *****************************

while(true){

}

// *******************************************************

}

// ***********************************************************

4 REPLIES 4

Your code looks basically okay -- very similar to what I'm doing on an STM32F767ZI. Without going through every line, and checking the magic numbers (hard-coded numerical hexadecimal constants) there's two things that look suspicious:

SPI3->CR1 |=0x31; // Configuracion del BaudRate as 256

The baud rate is set in bits 3 through 5 of the CR1 register. The above code might not be a problem, but you are setting BR to 0b110 and then also the CPHA bit with "0x31". You probably want to do a "<< 3" of the value to put the bits in the correct position (and also have only 3 bits).

Also, these bits are a divisor of the APB1 clock, which is in turn driven by the main SYSCLK. You aren't doing any explicit RCC clock configuration, so check to make sure you're getting what you want. But in any case the MAX7219 probably isn't picky about baud rate (within some limits) so this might not be a problem. You are setting and clearing many bits, including ones that are by default what you want, in CR1 and CR2 (using many excessive multiple writes), so check that all of them are exactly what you want.

More importantly, you're not explicitly setting the SPI data size via the CR2->DS bits. They should be 8 bits (0b0111) by default, but see https://community.st.com/s/question/0D50X0000AxCESNSQ4/i-have-problem-to-set-up-8-data-bit-serial-peripheral-interface-when-i-set-data-bit-size-8-i-have-8-proper-data-bits-and-16-clocks-in-transmit-mode-how-could-it-be-fixed and related posts about the craziest of all STM crazy implementations whereby things other than this value influence the true data size.

I hope some of this helps, and thanks for your post. I learned a little bit of Spanish using Google Translate to understand the code. 🙂

AVI-crak
Senior

You opened the documentation, and read it - this is a good deed. You do not have enough examples, this problem can be solved.

1 When writing to the register of many small constants - it makes sense to collect all the constants in one record. In this case there will be one write operation, instead of a set of separate ones.

2 If a constant is not used, or its value is zero, there is no point in using it at the given place of assignment.

3 Numeric constants with a range greater than one are written in _VAL2FLD (field, value). This macro reduces numeric input errors to zero damage to the other constants.

4 Data registers have a fixed size, but may accept smaller data. For this it is necessary to show the program the new size of the data register. uint8_t * tmp; tmp = & SPI1-> DR; * tmp = reg;

Below is a sample code for st7735, just an example.

    RCC->APB2ENR |= RCC_APB2ENR_SPI1EN;
    SPI1->CR1  = 0x0;
    SPI1->CR2 = SPI_CR2_SSOE |                      // NSS pin - control output
                          _VAL2FLD(SPI_CR2_DS, 7 ) |  // Data size 8bit
                          SPI_CR2_NSSP;                        // NSS pulse management
 
    SPI1->CR1 = _VAL2FLD(SPI_CR1_BR, 1)|    // Baud rate control (PCLK2/4) 27MGz
                          SPI_CR1_MSTR |                      // Master mode
                          SPI_CR1_BIDIMODE |             // 1 line
                          SPI_CR1_BIDIOE  |                  // only transfer
                          SPI_CR1_SPE;                           // on
 
#define st7735_com_en    GPIOC->BSRR = 1<<(4+16)
#define st7735_com_of    GPIOC->BSRR = 1<<(4)
 
void st7735_comand (uint8_t reg){
    while((SPI1->SR &(SPI_SR_BSY | SPI_SR_TXE)) != SPI_SR_TXE);
    st7735_com_en;
    uint8_t *tmp; tmp = &SPI1->DR; *tmp = reg;
    while(SPI1->SR &SPI_SR_BSY);
    st7735_com_of;
};
 
void st7735_data (uint8_t reg){
    while((SPI1->SR &(SPI_SR_BSY | SPI_SR_TXE)) != SPI_SR_TXE);
    uint8_t *tmp; tmp = &SPI1->DR; *tmp = reg;
};

Hi thanks4opensource, thanks for answer me. I see that you are right about the baud rate, my hex are wrong, i fixed it and prove but doesn't correctly run. i mean, i use my send funtion and in the debugger on Tx Fifo i look that isn't the same. ( i adjunt the photo of the debugger) . if you see, i send 2 bytes, 0x04 and 0x00, in the right i see the state of the register, and Tx Fifo should be the same with the information that i send but it's too different. 0690X000009ZYYoQAO.png

you said about the RCC register, i only turn on the SPI3 clock with the RCC->APB1ENR, if you refer to another please can you said me what it is? and the last, can you sow me communication code ? is only for check the irregular things

A few things ...

  1. RCC clocks. The BR bits in the CR control the baud rate by dividing the APB1 clock which fed to the SPI3 peripheral. The APB1 frequency is set by several RCC registers, and doing so is fairly complex. Read the RCC section of RM0385. But as I said, most SPI slave chips will accept a wide range of SPI baud rates, so I don't think this is your problem.
  2. Your original post has "SPI3->CR1 |=SPI_CR1_CRCEN; // No necesito CRC", but you've circled the RXCRCR and TXCRCR registers in your debugger image, and the refer to how they differ from what you're trying to send with your enviar_datos() function. As bad as my Spanish is, I think you're saying you don't need CRC checking ... but you are turning it on by setting the CRCEN bit in CR1 to 1. Regardless, you do realize what you will see in RXCRCR and TXCRCR will not be the data you sent/received but a CRC value calculated from them?
  3. It is difficult to debug by reading the DR register in the debugger because doing so causes its value to change the next time your code read it. Every read pops one entry from the RX FIFO.
  4. I still think your problem has to do with the very confusing way the STM32F7xx SPI controls the number of bits sent and received in each SPI frame. Read what AVI-crak wrote in his "4 Data registers have a fixed size" description, the code he attached, and also the links to forum posts I included above. You're setting 8 bit frame size with your "SPI3->CR2 |=0x700;" statement, but SPI_TypeDef.DR is declared as uint32_t. To send 8 bit frames you have to both set the CR2 DS bits, and do an explicit 8-bit write of data to the DR register via something like "*((uint8_t*)&SPI3->DR) = dato;" otherwise 16 bits will be sent on the MOSI line (and 16 read on MISO) instead. This is the craziest, stupidest, most confusing, most undocumented thing I've ever seen (I wasted a week trying to find what was wrong with my code before finding the answer here on community.st.com), but that's how the STM32F7 series chips work.

Try this and see if it helps. I still think your code is basically correct.