STM32 Programming. Part 6: SPI
Posted: 16 Oct 2023, 02:59
learn how to work with SPI module in STM32F103C8 microcontroller in Master mode using interrupts and without them.
SPI is the most popular serial synchronous data transfer interface between the microcontroller and peripherals. There are two SPI modules in the STM32F103C8. This interface can operate in Master (bus master) or Slave (bus slave) mode. Generally speaking, the SPI interface is quite a tricky thing. Specifically in STM32 SPI can calculate the checksum of received and transmitted frames by a given polynomial, work in Multimaster mode, hardware to work with the output NSS, as well as communicate in half-duplex mode (MOSI and MISO go on the same wire). Therefore, to properly configure SPI you need to carefully study all the registers of this module. Well and one more thing: SPI in STM32 can work in I2S mode (not to be confused with I2C!!!). I2S is an SPI-like interface for data transfer between digital audio devices. Don't let this confuse you, by default this module works in SPI mode and we will not consider registers that are needed only for I2S mode.
To connect two or more devices, you need 4 wires (+ground, where without it):
SPI registers
Here I will describe the registers that apply only to SPI. Everything that relates to I2S has been thrown out.
SPI control register 1 (SPI_CR1)
BIDIMODE: Enable bidirectional data output operation mode.
This bit, in conjunction with the BIDIMODE bit, selects the direction of transmission in bidirectional mode. In master mode, the MOSI pin is used for data transmission, while in slave mode, the MISO pin is used.
LSBFIRST: Frame Format
TXEIE: Interrupt emptying of the Tx data transfer buffer
RXDMAEN: When this bit is set, a DMA request occurs when the RXNE flag is set.
SPI status register (SPI_SR)
BSY: Busy Flag. This flag is set and reset by hardware
DR[15:0]: Data register. This register is divided into two buffers, one for writing (transmitter buffer) and one for reading (receiver buffer). A write operation to the SPI_DR register writes data to the transmitter buffer, and a read operation from SPI_DR returns a value from the receiver buffer.
SPI CRC polynomial register (SPI_CRCPR)
CRCPOLY[15:0]: CRC polynomial register, default is 0007h
SPI RX CRC register (SPI_RXCRCR)
RXCRC[15:0]: The CRC value of the received data. When CRC calculation is enabled, RXCRC contains the calculated CRC value of the received data. This register is reset to zero when the CRCEN bit in the SPI_CR1 register is set to one.
SPI TX CRC register (SPI_TXCRCR)
TXCRC[15:0]: The CRC value of the transmitted data. When CRC calculation is enabled, TXCRC contains the calculated CRC value of the transmitted data. This register is reset to zero when the CRCEN bit in the SPI_CR1 register is set to one.
Configuring SPI in Master mode without interrupts
After we have studied the SPI registers, let's start practicing. The task is to configure SPI1 in Master mode and start continuous sending of one byte without using interrupts. Well, let's go!
Let's call the initialization function SPI1_Init():
Now we need to determine which pins of the microcontroller SPI1 is connected to. Open Reference manual, go to the section about GPIO, find 9.3.10 SPI1 alternate function remapping. There is a table like this:
Let's not bother with Remap for now. From Fig. 7 shows that SPI1 is connected to the GPIOA port to the following pins:
The red rectangles highlight the settings for our case. So, we have the necessary information, now we can customize. As we already know, before we start working with any peripheral, it is necessary to turn on the clock signal:
Next, configure the GPIO.
Do not touch the NSS output as we will not use it. Next, the SPI setting:
We set it up like this: 8 bits, MSB first, CPOL/CPHA 00. It is worth paying special attention to SSM and SSI. Initialization of SPI module in Master mode is possible only with SS signal equal to one. I will not explain why it is so, I will only say that it comes from Multimaster mode. The SS signal can be received either from the NSS pin or the SSI bit of the CR1 register. If SSM is set to zero (default value), it will do a status check on NSS when SPI is enabled, and NSS is set as Input floating by default. Thus, if the NSS pin is a logic one, the initialization will complete successfully, otherwise nothing will happen and the MODF bit will be set in the SR register, which indicates a mode error. In addition, even after successful initialization, a low level on the NSS will disable SPI and reset the MSTR bit (from master mode will switch to slave). And if NSS is just hanging in the air, the system will not work at all. Therefore, set SSM and SSI to one.
All that remains now is to enable SPI1:
That's it, initialization in Master mode is complete! Here is the full code of the function:
Now let's move on to data exchange. But first we need to touch a little bit on the SPI module device.
SPI has a Shift register, a transmit buffer (Tx buffer) and a receiver buffer (Rx buffer). There are three very interesting flags in the SR register: BSY, TXE and RXNE. The TXE flag is set if the transmitter buffer (Tx buffer) is empty and the next value can be loaded into it, RXNE is set to one if a new value has arrived in the receiver buffer (Rx buffer) and can be read. BSY is set if the SPI module is busy with a communication operation or if the transmitter buffer is not empty.
The logic of operation is as follows: the operation of writing to the DR register fills the transmitter buffer with a data frame (8 or 16 bits, depending on the setting), and the BSY flag is set, and TXE is reset. The value from the transmitter buffer is then loaded into the shift register and the SPI data transfer process is started, and the TXE flag is set to one, indicating that a new value can be loaded into the Tx buffer. If another value is loaded into the Tx buffer, TXE is reset to zero until the current data frame transfer is completed and the next Tx buffer value is loaded into the shift register.
With each new period of the SCK synchronization signal, the shift register spits out another bit into MOSI and pops a new data bit from MISO into its tail (this is true for Master mode, vice versa for Slave). After the last bit has been received, the shift register value is loaded from the receiver buffer (Rx buffer) and the RXNE flag is set. If no new value has been loaded into the Tx buffer, the data transfer is terminated and the BSY flag is reset to zero.
It should be understood that the TXE flag only indicates that a new value can be added to the transmitter buffer, while the previous data frame can be transmitted at this time. If you want to make sure that ALL data has already been successfully sent to the slave device, use the BSY flag.
Here is the data reception:
For the test, here is this main():
ClockInit() - initialization of the clocking system, see this article. Then SPI1 initialization and infinite loop with sending the value 0x34. To prove the correct operation of the program, here is an oscillogram:
Figure 11 shows that the data is streaming continuously without delay. That's great!
Configuring SPI in Master mode with interrupts
Now let's do the same thing, but only on interrupts. The task is as follows: we have a certain array of bytes, which must be spit out to SPI using interrupts. I will not go into the details of interrupts in STM32, for this will be a separate article, I will limit myself to the necessary minimum.
The peripheral module can have several events that can cause an interrupt, for SPI it is TXEIE, RXNEIE and ERRIE (see Fig. 2). However, the interrupt handler in most cases is only one: SPI1_IRQHandler(). Thus, if we have multiple event interrupts enabled, we need to look at the SR status register in SPI1_IRQHandler() to understand what happened.
In order for the interrupt to be triggered, we need to perform 3 actions:
Next, we will need 3 global variables:
Then comes the function to start the SPI data transfer. It takes as input a pointer to the uint8_t array and the number of bytes to be transferred:
void SPI1_Tx(uint8_t *data, int32_t len)
{
if(len<=0)
return;
//Wait until SPI is free from the previous transfer
while(SPI1->SR & SPI_SR_BSY)
;
//Set the variables that will be
//used in the SPI interrupt handler
tx_index = 0;
tx_len = len;
tx_data = data;
//Resolve TXEIE interrupt and start the exchange
SPI1->CR2 |= (1<<SPI_CR2_TXEIE_Pos);
}
It works like this. In the initial state, the SPI is not transmitting any data and the TXE flag in the SR register is set to one. This means that if the TXEIE interrupt is enabled, it will be triggered immediately. After all the preliminary settings we enable the TXEIE interrupt, thus starting the process of sending data over SPI. The interrupt handler, where all the main work takes place, looks like this:
I think it's clear from the comments. Let's sketch a small main() for demonstration:
And here are the oscillograms of the data transfer process. This is for data[] buffer length of 3 bytes:
Everything works correctly, as much as said - so much and sent Bytes go one after another without delay. That's great.
And this is how sending 10 bytes looks like:
That's all for now, the article has already turned out to be a big one. I haven't decided yet what will be in the next part, but we should make articles about NVIC interrupt controller and DMA direct memory access controller. And SPI in Slave mode should be considered.
SPI is the most popular serial synchronous data transfer interface between the microcontroller and peripherals. There are two SPI modules in the STM32F103C8. This interface can operate in Master (bus master) or Slave (bus slave) mode. Generally speaking, the SPI interface is quite a tricky thing. Specifically in STM32 SPI can calculate the checksum of received and transmitted frames by a given polynomial, work in Multimaster mode, hardware to work with the output NSS, as well as communicate in half-duplex mode (MOSI and MISO go on the same wire). Therefore, to properly configure SPI you need to carefully study all the registers of this module. Well and one more thing: SPI in STM32 can work in I2S mode (not to be confused with I2C!!!). I2S is an SPI-like interface for data transfer between digital audio devices. Don't let this confuse you, by default this module works in SPI mode and we will not consider registers that are needed only for I2S mode.
To connect two or more devices, you need 4 wires (+ground, where without it):
- MOSI (Master Out / Slave In) - this wire carries data from master to slave device
- MISO (Master In / Slave Out) - and here it is the other way around: data goes from the slave to the master.
- SCK (Serial Clock) - a clock signal that goes from the master to the slave. At each new period of the clock signal, the bus Master sends a new data bit to the Slave, and the Slave in turn sends a data bit to the Master.
- NSS (Slave select) is an optional wire that is needed if we have several slaves on the SPI bus. Thus, with the help of NSS we can select with which Slave we want to exchange data.
SPI registers
Here I will describe the registers that apply only to SPI. Everything that relates to I2S has been thrown out.
SPI control register 1 (SPI_CR1)
BIDIMODE: Enable bidirectional data output operation mode.
- 0: 2-wire operation mode with unidirectional data line transmission
- 1: 1-wire operation mode with bidirectional data line transmission
This bit, in conjunction with the BIDIMODE bit, selects the direction of transmission in bidirectional mode. In master mode, the MOSI pin is used for data transmission, while in slave mode, the MISO pin is used.
- 0: Output disabled (receive only)
- 1: Output enabled (transmit only)
- 0: CRC calculation disabled
- 1: CRC calculation enabled
- 0: Data transfer stage
- 1: The next transmission will be completed by RCR transmission.
- 0: Transmission frame size 8 bits
- 1: Transmission frame size 16 bits
- 0: Full duplex - transmit and receive
- 1: Output disabled - receive only
- 0: Software Slave Management disabled
- 1: Software Slave Management enabled
LSBFIRST: Frame Format
- 0: MSB transmitted first
- 1: LSB is transmitted first
- 0: SPI disabled
- 1: SPI enabled
- 000: fPCLK/2
- 001: fPCLK/4
- 010: fPCLK/8
- 011: fPCLK/16
- 100: fPCLK/32
- 101: fPCLK/64
- 110: fPCLK/128
- 111: fPCLK/256
- 0: Slave mode
- 1: Master mode
- 0: CK to 0 at idle
- 1: CK to 1 when idle
- 0: The first clock transition is a data capture edge
- 1: The second clock transition is the edge of data capture
TXEIE: Interrupt emptying of the Tx data transfer buffer
- 0: TXE interrupt prohibited
- 1: TXE interrupt enabled. Used to generate an interrupt when the TXE flag is set.
- 0: RXNE interrupt disabled
- 1: RXNE interrupt is enabled. Used to generate an interrupt when the RXNE flag is set.
- 0: Interrupt on error occurrence prohibited
- 1: Error interrupts are enabled.
- 0: SS output is disabled in master mode and it is possible to work in multimaster mode.
- 1: SS output is enabled in master mode and no multimaster operation is possible.
RXDMAEN: When this bit is set, a DMA request occurs when the RXNE flag is set.
SPI status register (SPI_SR)
BSY: Busy Flag. This flag is set and reset by hardware
- 0: SPI is not busy
- 1: SPI is busy with communication or the Tx transmit buffer is not empty.
- 0: No overflow occurred
- 1: Overflow occurred
- 0: The received CRC value matched the value of the SPI_RXCRCR register.
- 1: The received CRC value did not match the value of the register SPI_RXCRCR
- 0: Tx buffer is not empty
- 1: Tx buffer empty
- 0: Rx buffer empty
- 1: Rx buffer not empty
DR[15:0]: Data register. This register is divided into two buffers, one for writing (transmitter buffer) and one for reading (receiver buffer). A write operation to the SPI_DR register writes data to the transmitter buffer, and a read operation from SPI_DR returns a value from the receiver buffer.
SPI CRC polynomial register (SPI_CRCPR)
CRCPOLY[15:0]: CRC polynomial register, default is 0007h
SPI RX CRC register (SPI_RXCRCR)
RXCRC[15:0]: The CRC value of the received data. When CRC calculation is enabled, RXCRC contains the calculated CRC value of the received data. This register is reset to zero when the CRCEN bit in the SPI_CR1 register is set to one.
SPI TX CRC register (SPI_TXCRCR)
TXCRC[15:0]: The CRC value of the transmitted data. When CRC calculation is enabled, TXCRC contains the calculated CRC value of the transmitted data. This register is reset to zero when the CRCEN bit in the SPI_CR1 register is set to one.
Configuring SPI in Master mode without interrupts
After we have studied the SPI registers, let's start practicing. The task is to configure SPI1 in Master mode and start continuous sending of one byte without using interrupts. Well, let's go!
Let's call the initialization function SPI1_Init():
Code: Select all
void SPI1_Init(void)
{
}Let's not bother with Remap for now. From Fig. 7 shows that SPI1 is connected to the GPIOA port to the following pins:
- NSS - PA4
- SCK - PA5
- MISO - PA6
- MOSI - PA7
The red rectangles highlight the settings for our case. So, we have the necessary information, now we can customize. As we already know, before we start working with any peripheral, it is necessary to turn on the clock signal:
Code: Select all
//Enable SPI1 and GPIOA clocking
RCC->APB2ENR |= RCC_APB2ENR_SPI1EN | RCC_APB2ENR_IOPAEN;Code: Select all
//First reset all configuration bits to zeros
GPIOA->CRL &= ~(GPIO_CRL_CNF5_Msk | GPIO_CRL_MODE5_Msk
| GPIO_CRL_CNF6_Msk | GPIO_CRL_MODE6_Msk
| GPIO_CRL_CNF7_Msk | GPIO_CRL_MODE7_Msk);
//Customize
//SCK: MODE5 = 0x03 (11b); CNF5 = 0x02 (10b)
GPIOA->CRL |= (0x02<<GPIO_CRL_CNF5_Pos) | (0x03<<GPIO_CRL_MODE5_Pos);
//MISO: MODE6 = 0x00 (00b); CNF6 = 0x01 (01b)
GPIOA->CRL |= (0x01<<GPIO_CRL_CNF6_Pos) | (0x00<<GPIO_CRL_MODE6_Pos);
//MOSI: MODE7 = 0x03 (11b); CNF7 = 0x02 (10b)
GPIOA->CRL |= (0x02<<GPIO_CRL_CNF7_Pos) | (0x03<<GPIO_CRL_MODE7_Pos);Code: Select all
SPI1->CR1 = 0<<SPI_CR1_DFF_Pos //Frame size 8 bits
| 0<<SPI_CR1_LSBFIRST_Pos //MSB first
| 1<<SPI_CR1_SSM_Pos //SSS program control
| 1<<SPI_CR1_SSI_Pos //SS in high state
| 0x04<<SPI_CR1_BR_Pos //Baud rate: F_PCLK/32
| 1<<SPI_CR1_MSTR_Pos //Master mode (master)
| 0<<SPI_CR1_CPOL_Pos | 0<<SPI_CR1_CPHA_Pos; // SPI operating mode: 0All that remains now is to enable SPI1:
Code: Select all
SPI1->CR1 |= 1<<SPI_CR1_SPE_Pos; //Enable SPICode: Select all
void SPI1_Init(void)
{
//Turn on SPI1 and GPIOA clocking
RCC->APB2ENR |= RCC_APB2ENR_SPI1EN | RCC_APB2ENR_IOPAEN;
/**********************************************************/
/*** Configuring GPIOA pins to work together with SPI1 ***/
/**********************************************************/
//PA7 - MOSI
//PA6 - MISO
//PA5 - SCK
//First reset all configuration bits to zeros
GPIOA->CRL &= ~(GPIO_CRL_CNF5_Msk | GPIO_CRL_MODE5_Msk
| GPIO_CRL_CNF6_Msk | GPIO_CRL_MODE6_Msk
| GPIO_CRL_CNF7_Msk | GPIO_CRL_MODE7_Msk);
//Customize
//SCK: MODE5 = 0x03 (11b); CNF5 = 0x02 (10b)
GPIOA->CRL |= (0x02<<GPIO_CRL_CNF5_Pos) | (0x03<<GPIO_CRL_MODE5_Pos);
//MISO: MODE6 = 0x00 (00b); CNF6 = 0x01 (01b)
GPIOA->CRL |= (0x01<<GPIO_CRL_CNF6_Pos) | (0x00<<GPIO_CRL_MODE6_Pos);
//MOSI: MODE7 = 0x03 (11b); CNF7 = 0x02 (10b)
GPIOA->CRL |= (0x02<<GPIO_CRL_CNF7_Pos) | (0x03<<GPIO_CRL_MODE7_Pos);
/**********************/
/*** Setting SPI1 ***/
/**********************/
SPI1->CR1 = 0<<SPI_CR1_DFF_Pos //Frame size 8 bits
| 0<<SPI_CR1_LSBFIRST_Pos //MSB first
| 1<<SPI_CR1_SSM_Pos //SSS program control
| 1<<SPI_CR1_SSI_Pos //SS in high state
| 0x04<<SPI_CR1_BR_Pos //Baud rate: F_PCLK/32
| 1<<SPI_CR1_MSTR_Pos //Master mode (master)
| 0<<SPI_CR1_CPOL_Pos | 0<<SPI_CR1_CPHA_Pos; // SPI operating mode: 0
SPI1->CR1 |= 1<<SPI_CR1_SPE_Pos; //Enable SPI
}SPI has a Shift register, a transmit buffer (Tx buffer) and a receiver buffer (Rx buffer). There are three very interesting flags in the SR register: BSY, TXE and RXNE. The TXE flag is set if the transmitter buffer (Tx buffer) is empty and the next value can be loaded into it, RXNE is set to one if a new value has arrived in the receiver buffer (Rx buffer) and can be read. BSY is set if the SPI module is busy with a communication operation or if the transmitter buffer is not empty.
The logic of operation is as follows: the operation of writing to the DR register fills the transmitter buffer with a data frame (8 or 16 bits, depending on the setting), and the BSY flag is set, and TXE is reset. The value from the transmitter buffer is then loaded into the shift register and the SPI data transfer process is started, and the TXE flag is set to one, indicating that a new value can be loaded into the Tx buffer. If another value is loaded into the Tx buffer, TXE is reset to zero until the current data frame transfer is completed and the next Tx buffer value is loaded into the shift register.
With each new period of the SCK synchronization signal, the shift register spits out another bit into MOSI and pops a new data bit from MISO into its tail (this is true for Master mode, vice versa for Slave). After the last bit has been received, the shift register value is loaded from the receiver buffer (Rx buffer) and the RXNE flag is set. If no new value has been loaded into the Tx buffer, the data transfer is terminated and the BSY flag is reset to zero.
Code: Select all
Sending data to [i]SPI [/i]will look like this:
void SPI1_Write(uint16_t data)
{
//Wait until the transmitter buffer is empty
while(!(SPI1->SR & SPI_SR_TXE))
;
//fill the transmitter buffer
SPI1->DR = data;
}Here is the data reception:
Code: Select all
uint16_t SPI1_Read(void)
{
SPI1->DR = 0; //start exchange
//Wait until a new value appears
//in the receiver buffer
while(!(SPI1->SR & SPI_SR_RXNE))
;
//return the value of the receiver buffer
return SPI1->DR;
}Code: Select all
void main()
{
ClockInit();
SPI1_Init();
for(;;)
{
SPI1_Write(0x34);
}
}Figure 11 shows that the data is streaming continuously without delay. That's great!
Configuring SPI in Master mode with interrupts
Now let's do the same thing, but only on interrupts. The task is as follows: we have a certain array of bytes, which must be spit out to SPI using interrupts. I will not go into the details of interrupts in STM32, for this will be a separate article, I will limit myself to the necessary minimum.
The peripheral module can have several events that can cause an interrupt, for SPI it is TXEIE, RXNEIE and ERRIE (see Fig. 2). However, the interrupt handler in most cases is only one: SPI1_IRQHandler(). Thus, if we have multiple event interrupts enabled, we need to look at the SR status register in SPI1_IRQHandler() to understand what happened.
In order for the interrupt to be triggered, we need to perform 3 actions:
- Enable the interrupt in the SPI module.
- Enable the interrupt from the SPI in the NVIC. When any SPI enabled interrupt occurs, the SPI1_IRQHandler() handler will be called.
- Allow interrupts globally (by default, after resetting the microcontroller, they are allowed).
Code: Select all
void SPI1_Init(void)
{
//Turn on SPI1 and GPIOA clocking
RCC->APB2ENR |= RCC_APB2ENR_SPI1EN | RCC_APB2ENR_IOPAEN;
/**********************************************************/
/*** Configuring GPIOA pins to work together with SPI1 ***/
/**********************************************************/
//PA7 - MOSI
//PA6 - MISO
//PA5 - SCK
//First reset all configuration bits to zeros
GPIOA->CRL &= ~(GPIO_CRL_CNF5_Msk | GPIO_CRL_MODE5_Msk
| GPIO_CRL_CNF6_Msk | GPIO_CRL_MODE6_Msk
| GPIO_CRL_CNF7_Msk | GPIO_CRL_MODE7_Msk);
//Customize
//SCK: MODE5 = 0x03 (11b); CNF5 = 0x02 (10b)
GPIOA->CRL |= (0x02<<GPIO_CRL_CNF5_Pos) | (0x03<<GPIO_CRL_MODE5_Pos);
//MISO: MODE6 = 0x00 (00b); CNF6 = 0x01 (01b)
GPIOA->CRL |= (0x01<<GPIO_CRL_CNF6_Pos) | (0x00<<GPIO_CRL_MODE6_Pos);
//MOSI: MODE7 = 0x03 (11b); CNF7 = 0x02 (10b)
GPIOA->CRL |= (0x02<<GPIO_CRL_CNF7_Pos) | (0x03<<GPIO_CRL_MODE7_Pos);
/*
//SS MODE4 = 0x03 (11b); CNF4 = 0x02 (10b)
GPIOA->CRL |= (0x02<<GPIO_CRL_CNF4_Pos) | (0x03<<GPIO_CRL_MODE4_Pos);
*/
/**********************/
/*** Setting SPI1 ***/
/**********************/
SPI1->CR1 = 0<<SPI_CR1_DFF_Pos //Frame size 8 bits
| 0<<SPI_CR1_LSBFIRST_Pos //MSB first
| 1<<SPI_CR1_SSM_Pos //SSS program control
| 1<<SPI_CR1_SSI_Pos //SS in high state
| 0x04<<SPI_CR1_BR_Pos //Baud rate: F_PCLK/32
| 1<<SPI_CR1_MSTR_Pos //Master mode (master)
| 0<<SPI_CR1_CPOL_Pos | 0<<SPI_CR1_CPHA_Pos; // SPI operating mode: 0
NVIC_EnableIRQ(SPI1_IRQn); //Allow interrupts from SPI1
SPI1->CR1 |= 1<<SPI_CR1_SPE_Pos; //Enable SPI
}Code: Select all
int32_t tx_index = 0; //this stores the number of bytes transferred
int32_t tx_len = 0; //how many bytes to transfer
uint8_t *tx_data; //pointer to the array with transferred datavoid SPI1_Tx(uint8_t *data, int32_t len)
{
if(len<=0)
return;
//Wait until SPI is free from the previous transfer
while(SPI1->SR & SPI_SR_BSY)
;
//Set the variables that will be
//used in the SPI interrupt handler
tx_index = 0;
tx_len = len;
tx_data = data;
//Resolve TXEIE interrupt and start the exchange
SPI1->CR2 |= (1<<SPI_CR2_TXEIE_Pos);
}
It works like this. In the initial state, the SPI is not transmitting any data and the TXE flag in the SR register is set to one. This means that if the TXEIE interrupt is enabled, it will be triggered immediately. After all the preliminary settings we enable the TXEIE interrupt, thus starting the process of sending data over SPI. The interrupt handler, where all the main work takes place, looks like this:
Code: Select all
void SPI1_IRQHandler(void)
{
SPI1->DR = tx_data[tx_index]; //Write the new value to DR
tx_index++; //increase the counter of transferred bytes by one
//if all the bytes have been transferred, then disable the interrupt,
// thus ending the data transfer
if(tx_index >= tx_len)
SPI1->CR2 &= ~(1<<SPI_CR2_TXEIE_Pos);
}Code: Select all
uint8_t data[10];
void main()
{
ClockInit(); //initialization of the clocking system
SPI1_Init(); //initialization of SPI1
//fill the data[] array with data
for(int i=0; i<sizeof(data); i++)
{
data[i] = i+1;
}
//start data transfer
SPI1_Tx(data, sizeof(data));
//infinite loop
//you can do something useful here
for(;;)
{
}
}Everything works correctly, as much as said - so much and sent Bytes go one after another without delay. That's great.
And this is how sending 10 bytes looks like:
That's all for now, the article has already turned out to be a big one. I haven't decided yet what will be in the next part, but we should make articles about NVIC interrupt controller and DMA direct memory access controller. And SPI in Slave mode should be considered.