STM32 introductory tutorial (SPI communication)

Reference tutorial:[11-1] SPI communication protocol_bilibili_bilibili

1. SPI (Serial Peripheral Interface) is a universal data bus developed by Motorola. It has four communication lines, namely SCK (Serial Clock, which provides clock signals), and MOSI (Master Output Slave Input, which the host sends to the slave). data line), MISO (Master Input Slave Output, the line through which the host receives slave data), SS (Slave Select, slave selection line, one for each slave), adopts synchronous, full-duplex mode, and supports bus hang-up Load multiple devices (one master, multiple slaves).

2. SPI protocol hardware circuit:

(1)The SCK, MOSI, and MISO of all SPI devices are connected together.

(2)The host also leads out multiple SS control lines, which are connected to the SS pins of each slave machine. (SS is used by the host to select a slave. Low level represents selection. Only one slave can be selected at the same time. The MISO pin of an unselected slave must be in a high-impedance state. )

(3)The output pin is configured as a push-pull output, and the input pin is configured as a floating or pull-up input.

3. Shift diagram:

(1) SPI transmission data generally adopts high bit first, each time the shift register receives Clock pulse (the clock source is provided by the baud rate generator of the host), the shift register (whether the host or the slave) will shift to the left, and the data shifted out of the host enters the shift register of the slave through MOSI At the far right end, the data moved out of the slave enters the right end of the shift register of the master through MOSO. In this cycle, the master and the slave realize 1 byte of data exchange.

(2) It can be specified that when the baud rate generator generates a rising edge of the clock, all shift registers shift one bit to the left and are shifted out by The position is on the corresponding pin. When the baud rate generator generates the falling edge of the clock, the bit on the pin is moved into the lowest bit of the shift register, also It is the following mode 1/3; it can also be specified that when the baud rate generator generates the falling edge of the clock, all shift registers will shift one bit to the left and be shifted outThe bit is on the corresponding pin. When the baud rate generator generates the rising edge of the clock, the bit on the pin is moved to the lowest bit of the shift register. That is mode 0/2 below.

(3) Based on data exchange,The host and the slave can realize one-way transmission of data. For example, if the host needs to receive data from the slave, then it can "arbitrarily" use a data Just exchange data with the slave machine (generally use 0x00 or 0xFF for data exchange).

4. SPI timing basic unit:

(1) Starting condition and termination condition: SS is low level, indicating that the slave is selected.

①Start condition:SS switches from high level to low level. (Pictured left)

②Termination condition:SS switches from low level to high level. (pictured right)

(2) Exchange one byte (mode 0, most used):

CPOL (clock polarity) = 0: In idle state, SCK is low level

CPHA (clock phase) = 0: Data is shifted in on the first edge of SCK and data is shifted out on the second edge (one bit of data is shifted out on the falling edge of SS)

(3) Exchange one byte (mode 1):

CPOL (clock polarity) = 0: In idle state, SCK is low level

CPHA (clock phase) = 1: The first edge of SCK shifts out data, and the second edge shifts in data (the rising edge of SS shifts out one bit of data)

(4) Exchange one byte (mode 2):

CPOL (clock polarity) = 1: In idle state, SCK is high level

CPHA (clock phase) = 0: Data is shifted in on the first edge of SCK and data is shifted out on the second edge (one bit of data is shifted out on the falling edge of SS)

(5) Exchange one byte (mode 3):

CPOL (clock polarity) = 1: In idle state, SCK is high level

CPHA (clock phase) = 1: The first edge of SCK shifts out data, and the second edge shifts in data (the rising edge of SS shifts out one bit of data)

5. SPI timing (the byte stream definition of timing may be different for each chip. The following takes W25Q64 as an example):

(1) Send command: Send write enable command (0x06) to the device specified by SS

(2) Specified address write: Send a write command (0x02) to the device specified by SS, and then write the specified data (Data) at the specified address (Address[23:0]). (Address[23:0] occupies 3 bytes and requires 3 byte data exchanges)

(3) Specified address read: Send a read command (0x03) to the device specified by SS, and then read the slave data (Data) at the specified address (Address[23:0]). (Address[23:0] occupies 3 bytes and requires 3 byte data exchanges)

6. The W25Qxx series is a low-cost, miniaturized and easy-to-use non-volatile memory, which is often used in data storage, font storage, firmware program storage and other scenarios.

(1) Storage medium: Nor Flash (flash memory)

(2) Clock frequency: 80MHz / 160MHz (Dual SPI, equivalent frequency in dual SPI mode) / 320MHz (Quad SPI, equivalent frequency in quadruple SPI mode)

(3) Storage capacity (24-bit address):

        W25Q40:   4Mbit / 512KByte

        W25Q80:   8Mbit / 1MByte

        W25Q16:   16Mbit / 2MByte

        W25Q32:   32Mbit / 4MByte

        W25Q64:   64Mbit / 8MByte

        W25Q128:  128Mbit / 16MByte

        W25Q256:  256Mbit / 32MByte

7. W25Qxx hardware circuit:

pin

Function

VCC、GND

Power supply (2.7~3.6V)

CS(SS)

SPI chip select

CLK(SCK)

SPI clock

DI(MOSI)

SPI master output slave input

DO(MISO)

SPI master input slave output

WP

write protect

HOLD

Data retention

8. W25Q64 block diagram:

(1)The total memory capacity is 8MB, divided into 64KB blocks, and each 64KB is further divided into 16 4KB sectors. The entire storage space is also divided into Many pages, each page size 256 bytes.

(2) The lower left corner is the SPI control logic, which is responsible for completing the address latch, data reading and writing and other operations inside the chip. The main control chip uses the SPI protocol to send instructions and data to the SPI control logic of the chip. The control logic will automatically operate the internal circuit. .

(3)There is a status register above the SPI control logic (Status Register), whether the chip is in a busy state, whether writing is enabled, Whether write protection is reflected in this register.

(4) Above the status register is the write control logic, which is connected to the external WP pin and cooperates with the WP pin to achieve hardware write protection.

(5) There is a high voltage generator (High Voltage Generators) on the right side of the SPI control logic, which is programmed with Flash to ensure that data is not lost when power is turned off.

(6) There are also page address latch/counter and byte address latch/counter on the right side of the SPI control logic. The page address selects which page of the storage space to operate. The byte address selects which byte in the storage page to operate on, which is equivalent to an address pointer. It can also increment after a read/write operation. .

(7) Data reading/writing is performed through the RAM buffer (Buffer). The data written to the Flash will be temporarily stored in the buffer. After the timing is completed, the chip will copy the data in the buffer to the Flash. The process of copying data The buffer will set the BUSY bit of the status register to 1 to indicate that the chip is busy. (Flash runs slower than SPI control logic. The buffer is used to solve the problem of mismatch in sending and receiving speeds. However, the buffer only has a capacity of 256 bytes. You cannot write too much data into the buffer at one time.)

9. Precautions for Flash operation:

(1) During write operation:

Write enable must be performed before writing operation.

Each data bit can only be rewritten from 1 to 0, but cannot be rewritten from 0 to 1.

must be erased before writing data. After erasing, all data bits become 1.

Erase must be based on the minimum erase unit (in W25Q64minimum erase unit a> is a sector) is performed.

When writing multiple bytes continuously, up to one page of data can be written. Data beyond the end of the page will be returned to the top of the page and overwritten (Cannot write data across pages).

After the write operation is completed, the chip enters the busy state and does not respond to new read and write operations.

(2) During reading operation:

Directly call the read sequence, no enablement, no additional operations, no page restrictions (data can be read across pages), read After the fetch operation is completed, it will not enter the busy state, but it cannot be read in the busy state.

10. Software SPI reads W25Q64:

(1) Connect the circuit as shown in the figure below, and make a copy of the OLED display project folder to use as a template.

(2) Add the MySPI.h file and MySPI.c file to the Hardware group of the project to encapsulate the code of the SPI module.

①MySPI.h file:

#ifndef __MySPI_H
#define __MySPI_H

void MySPI_Init(void);
void MySPI_Start(void);
void MySPI_Stop(void);
uint8_t MySPI_SwapByte(uint8_t ByteSend);

#endif

②MySPI.c file:

#include "stm32f10x.h"                  // Device header

void MySPI_W_SS(uint8_t BitValue)  //设置SS线电平
{
	GPIO_WriteBit(GPIOA, GPIO_Pin_4, (BitAction)BitValue);
}

void MySPI_W_SCK(uint8_t BitValue) //设置SCK线电平
{
	GPIO_WriteBit(GPIOA, GPIO_Pin_5, (BitAction)BitValue);
}

void MySPI_W_MOSI(uint8_t BitValue)//设置MOSI线电平
{
	GPIO_WriteBit(GPIOA, GPIO_Pin_7, (BitAction)BitValue);
}

uint8_t MySPI_R_MISO(void)         //读MISO线电平
{
	return GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_6);
}

void MySPI_Init(void)
{
	//开启GPIO的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
	//配置端口模式(输出引脚配置为推挽输出,输入引脚配置为浮空或上拉输入)
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;        //推挽输出
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_7;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStructure);
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;           //上拉输入
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStructure);
	
	MySPI_W_SS(1);    //初始状态默认不选择从机
	MySPI_W_SCK(0);   //使用模式0,模式0下SCK开始时是低电平
}

void MySPI_Start(void)
{
	MySPI_W_SS(0);				//拉低SS,开始时序
}

void MySPI_Stop(void)
{
	MySPI_W_SS(1);				//拉高SS,终止时序
}

uint8_t MySPI_SwapByte(uint8_t ByteSend)
{
	uint8_t i, ByteReceive = 0x00;					//定义接收的数据,并赋初值0x00,此处必须赋初值0x00,后面会用到
	
	for (i = 0; i < 8; i ++)						//循环8次,依次交换每一位数据
	{
		MySPI_W_MOSI(ByteSend & (0x80 >> i));		//使用掩码的方式取出ByteSend的指定一位数据并写入到MOSI线
		MySPI_W_SCK(1);								//拉高SCK,上升沿移出数据
		if (MySPI_R_MISO() == 1){ByteReceive |= (0x80 >> i);}	//读取MISO数据,并存储到Byte变量中
																//当MISO为1时,置变量指定位为1,当MISO为0时,不做处理,指定位为默认的初值0
		MySPI_W_SCK(0);								//拉低SCK,下降沿移入数据
	}						
	/*下面这个for循环与上面的for循环等效
	for(i = 0; i < 8; i++)
	{
		MySPI_W_MOSI(ByteSend & 0x80);  //主机将高位写在MOSI上
		ByteSend << 1;                  //ByteSend左移一位
		MySPI_W_SCK(1);                 //SCK上升沿,从机中的移位寄存器将高位数据写在MISO上
		if(MySPI_R_MISO())
		{
			ByteSend |= 0x01;           //ByteSend低位接收从机移位寄存器移出的数据
		}
		MySPI_W_SCK(0);                 //SCK下降沿,MOSI的数据移入从机中的移位寄存器
	}
	*/	
	return ByteReceive;                             //返回接收到的一个字节数据
}

(3) Add the W25Q64_Ins.h file to the Hardware group of the project to store the macro definitions of the instruction set, and add the W25Q64.h file and W25Q64.c file to encapsulate the code of the W25Q64 module.

①W25Q64_Ins.h file:

#ifndef __W25Q64_INS_H
#define __W25Q64_INS_H

#define W25Q64_WRITE_ENABLE							0x06
#define W25Q64_WRITE_DISABLE						0x04
#define W25Q64_READ_STATUS_REGISTER_1				0x05
#define W25Q64_READ_STATUS_REGISTER_2				0x35
#define W25Q64_WRITE_STATUS_REGISTER				0x01
#define W25Q64_PAGE_PROGRAM							0x02
#define W25Q64_QUAD_PAGE_PROGRAM					0x32
#define W25Q64_BLOCK_ERASE_64KB						0xD8
#define W25Q64_BLOCK_ERASE_32KB						0x52
#define W25Q64_SECTOR_ERASE_4KB						0x20
#define W25Q64_CHIP_ERASE							0xC7
#define W25Q64_ERASE_SUSPEND						0x75
#define W25Q64_ERASE_RESUME							0x7A
#define W25Q64_POWER_DOWN							0xB9
#define W25Q64_HIGH_PERFORMANCE_MODE				0xA3
#define W25Q64_CONTINUOUS_READ_MODE_RESET			0xFF
#define W25Q64_RELEASE_POWER_DOWN_HPM_DEVICE_ID		0xAB
#define W25Q64_MANUFACTURER_DEVICE_ID				0x90
#define W25Q64_READ_UNIQUE_ID						0x4B
#define W25Q64_JEDEC_ID								0x9F
#define W25Q64_READ_DATA							0x03
#define W25Q64_FAST_READ							0x0B
#define W25Q64_FAST_READ_DUAL_OUTPUT				0x3B
#define W25Q64_FAST_READ_DUAL_IO					0xBB
#define W25Q64_FAST_READ_QUAD_OUTPUT				0x6B
#define W25Q64_FAST_READ_QUAD_IO					0xEB
#define W25Q64_OCTAL_WORD_READ_QUAD_IO				0xE3

#define W25Q64_DUMMY_BYTE							0xFF   //“无用数据”

#endif

②W25Q64.h file:

#ifndef __W25Q64_H
#define __W25Q64_H

void W25Q64_Init(void);
void W25Q64_ReadID(uint8_t *MID, uint16_t *DID);
void W25Q64_PageProgram(uint32_t Address, uint8_t* DataArray, uint16_t Count);
void W25Q64_SectorErase(uint32_t Address);
void W25Q64_ReadData(uint32_t Address, uint8_t* DataArray, uint32_t Count);

#endif

③W25Q64.c file:

#include "stm32f10x.h"                  // Device header
#include "MySPI.h"
#include "W25Q64_Ins.h"

/**
  * 函    数:W25Q64初始化
  * 参    数:无
  * 返 回 值:无
  */
void W25Q64_Init(void)
{
	MySPI_Init();					//先初始化底层的SPI
}

/**
  * 函    数:W25Q64读取ID号
  * 参    数:MID 工厂ID,使用输出参数的形式返回
  * 参    数:DID 设备ID,使用输出参数的形式返回
  * 返 回 值:无
  */
void W25Q64_ReadID(uint8_t *MID, uint16_t *DID)
{
	MySPI_Start();								//SPI起始
	MySPI_SwapByte(W25Q64_JEDEC_ID);			//交换发送读取ID的指令
	*MID = MySPI_SwapByte(W25Q64_DUMMY_BYTE);	//交换接收MID,通过输出参数返回
	*DID = MySPI_SwapByte(W25Q64_DUMMY_BYTE);	//交换接收DID高8位
	*DID <<= 8;									//高8位移到高位
	*DID |= MySPI_SwapByte(W25Q64_DUMMY_BYTE);	//或上交换接收DID的低8位,通过输出参数返回
	MySPI_Stop();								//SPI终止
}

/**
  * 函    数:W25Q64写使能
  * 参    数:无
  * 返 回 值:无
  */
void W25Q64_WriteEnable(void)
{
	MySPI_Start();								//SPI起始
	MySPI_SwapByte(W25Q64_WRITE_ENABLE);		//交换发送写使能的指令
	MySPI_Stop();								//SPI终止
}

/**
  * 函    数:W25Q64等待忙
  * 参    数:无
  * 返 回 值:无
  */
void W25Q64_WaitBusy(void)
{
	uint32_t Timeout;
	MySPI_Start();								//SPI起始
	MySPI_SwapByte(W25Q64_READ_STATUS_REGISTER_1);				//交换发送读状态寄存器1的指令
	Timeout = 100000;							//给定超时计数时间
	while ((MySPI_SwapByte(W25Q64_DUMMY_BYTE) & 0x01) == 0x01)	//循环等待忙标志位
	{
		Timeout --;								//等待时,计数值自减
		if (Timeout == 0)						//自减到0后,等待超时
		{
			/*超时的错误处理代码,可以添加到此处*/
			break;								//超时,跳出等待
		}
	}
	MySPI_Stop();								//SPI终止
}

/**
  * 函    数:W25Q64页编程
  * 参    数:Address 页编程的起始地址,范围:0x000000~0x7FFFFF
  * 参    数:DataArray	用于写入数据的数组
  * 参    数:Count 要写入数据的数量,范围:0~256
  * 返 回 值:无
  * 注意事项:写入的地址范围不能跨页
  */
void W25Q64_PageProgram(uint32_t Address, uint8_t *DataArray, uint16_t Count)
{
	uint16_t i;
	
	W25Q64_WriteEnable();						//写使能(写入操作前,必须先进行写使能)
	
	MySPI_Start();								//SPI起始
	MySPI_SwapByte(W25Q64_PAGE_PROGRAM);		//交换发送页编程的指令
	MySPI_SwapByte(Address >> 16);				//交换发送地址23~16位
	MySPI_SwapByte(Address >> 8);				//交换发送地址15~8位
	MySPI_SwapByte(Address);					//交换发送地址7~0位
	for (i = 0; i < Count; i ++)				//循环Count次
	{
		MySPI_SwapByte(DataArray[i]);			//从起始地址开始写数据,地址指针会自增
	}
	MySPI_Stop();								//SPI终止
	
	W25Q64_WaitBusy();							//等待忙(忙状态不能进行读写,需要等芯片处理完事情再继续下面的操作)
}

/**
  * 函    数:W25Q64扇区擦除(4KB)
  * 参    数:Address 指定扇区的地址,范围:0x000000~0x7FFFFF
  * 返 回 值:无
  */
void W25Q64_SectorErase(uint32_t Address)
{
	W25Q64_WriteEnable();						//写使能(写入操作前,必须先进行写使能)
	
	MySPI_Start();								//SPI起始
	MySPI_SwapByte(W25Q64_SECTOR_ERASE_4KB);	//交换发送扇区擦除的指令
	MySPI_SwapByte(Address >> 16);				//交换发送地址23~16位
	MySPI_SwapByte(Address >> 8);				//交换发送地址15~8位
	MySPI_SwapByte(Address);					//交换发送地址7~0位
	MySPI_Stop();								//SPI终止
	
	W25Q64_WaitBusy();							//等待忙(忙状态不能进行读写,需要等芯片处理完事情再继续下面的操作)
}

/**
  * 函    数:W25Q64读取数据
  * 参    数:Address 读取数据的起始地址,范围:0x000000~0x7FFFFF
  * 参    数:DataArray 用于接收读取数据的数组,通过输出参数返回
  * 参    数:Count 要读取数据的数量,范围:0~0x800000
  * 返 回 值:无
  */
void W25Q64_ReadData(uint32_t Address, uint8_t *DataArray, uint32_t Count)
{
	uint32_t i;
	MySPI_Start();								//SPI起始
	MySPI_SwapByte(W25Q64_READ_DATA);			//交换发送读取数据的指令
	MySPI_SwapByte(Address >> 16);				//交换发送地址23~16位
	MySPI_SwapByte(Address >> 8);				//交换发送地址15~8位
	MySPI_SwapByte(Address);					//交换发送地址7~0位
	for (i = 0; i < Count; i ++)				//循环Count次
	{
		DataArray[i] = MySPI_SwapByte(W25Q64_DUMMY_BYTE);	//从起始地址开始读取数据,地址指针会自增
	}
	MySPI_Stop();								//SPI终止
}

(4) W25Q64 ID number, related instructions and timing:

(5) Paste the following code in the main.c file, compile it, download the program to the development board, and observe the OLED screen display.

#include "stm32f10x.h"                  // Device headerCmd
#include "OLED.h"
#include "W25Q64.h"

uint8_t MID;
uint16_t DID;

uint8_t ArrayWrite[] = {0x01, 0x02, 0x03, 0x04};
uint8_t ArrayRead[4];

int main()
{
	OLED_Init();
	W25Q64_Init();
	
	OLED_ShowString(1,1,"MID:   DID:");
	OLED_ShowString(2,1,"W:");
	OLED_ShowString(3,1,"R:");
	
	W25Q64_ReadID(&MID, &DID);
	OLED_ShowHexNum(1,5,MID,2);
	OLED_ShowHexNum(1,12,DID,4);
	
	W25Q64_SectorErase(0x000000);   //写入数据前必须先擦除
	W25Q64_PageProgram(0x000000, ArrayWrite, 4);  //在指定地址处往后写4个字节数据
	W25Q64_ReadData(0x000000, ArrayRead, 4);      //读取指定地址往后的4个字节数据
	
	OLED_ShowHexNum(2,3,ArrayWrite[0],2);
	OLED_ShowHexNum(2,6,ArrayWrite[1],2);
	OLED_ShowHexNum(2,9,ArrayWrite[2],2);
	OLED_ShowHexNum(2,12,ArrayWrite[3],2);
	
	OLED_ShowHexNum(3,3,ArrayRead[0],2);
	OLED_ShowHexNum(3,6,ArrayRead[1],2);
	OLED_ShowHexNum(3,9,ArrayRead[2],2);
	OLED_ShowHexNum(3,12,ArrayRead[3],2);
	
	while(1)
	{
		
	}
}

11. Introduction to SPI peripherals:

(1) STM32 integrates a hardware SPI transceiver circuit, which can automatically perform functions such as clock generation and data transmission and reception by hardware, reducing the CPU load. burden (although the SS line still requires software control).

(2) Configurable 8-bit/16-bit data frame, high-end first/low-end first.

(3) Clock frequency: fPCLK / (2, 4, 8, 16, 32, 64, 128, 256).

(4) Supports multi-host model, master or slave operation, can be simplified to half-duplex/simplex communication, supports DMA, and is compatible with I2S protocol.

(5) Hardware SPI resources of STM32F103C8T6: SPI1 (mounted on APB2), SPI2 (mounted on APB1).

12. SPI block diagram:

(1) The shift register (SPI_DR) in the upper left corner is the core part.The working flow of the shift register is described in detail in the "Shift Diagram" .

(2) When STM32 serves as a slave, MOSI is the slave input and MISO is the slave output; when STM32 serves as the master, MOSI and MISO still have their original functions.

(3)The host writes the data to be sent into the sending buffer, and the host reads the received data in the receiving buffer. The two buffers are occupied in the software The same address, this part is very similar to USART.

(4)When there is no data shifting in the shift register, the data in the sending buffer will be immediately transferred to the shift register and the TXE bit of the status register will be automatically set to 1. At this time, the host can write the next data to be sent into the send buffer. After the send buffer receives the data from the host, the TXE bit is automatically set to 0.

(5)When the shift register receives the complete byte data from the slave, if the RXNE bit of the status register is 0, the data in the shift register is transferred to the receive buffer area and automatically sets the RXNE bit to 1. After the host reads the data in the receive buffer, RXNE will be set to 0.

(6) The clock generated by the baud rate generator is synchronized with the clock of the shift register.

(7)The three bits BR0, BR1, and BR2 of the CR1 register are used to control the frequency division coefficient (fPCLK / (2, 4 , 8, 16, 32, 64, 128, 256)), LSBFIRST determines whether the high bit goes first or the low bit goes first, SPE is the SPI enable bit , MSTR is responsible for configuring the master/slave mode , CPOL and CPHA are used to select the 4 modes of SPI exchange bytes.

13. Main mode full-duplex continuous transmission:

14. Discontinuous transmission: The software waits for the shift register to exchange data before writing the data into the sending buffer. Therefore, the efficiency is lower than that of continuous transmission. The difference will be more obvious when the clock frequency is high, but discontinuous transmission is more difficult for program writing. friendly.

15. Hardware SPI reading W25Q64:

(1) Connect the circuit as shown in the figure below, and copy the project folder of the above example to use as a template. (SPI1_NSS is multiplexed in PA4, SPI1_SCK is multiplexed in PA5, SPI1_MISO is multiplexed in PA6, SPI1_MOSI is multiplexed in PA7)

(2) There are SPI-related functions in the stm32f10x_spi.h file.

[1]SPI_I2S_DeInit function: restore default configuration.

[2]SPI_Init function: Initialize SPI using parameters in the structure.

[3]SPI_StructInit function: Assign an initial value to the parameters in the structure.

[4]SPI_Cmd function: Use SPI.

[5]SPI_I2S_ITConfig function: Open the channel from SPI to NVIC.

[6]SPI_I2S_DMACmd function: allows SPI to send requests to DMA.

[7]SPI_I2S_SendData function: Write data to the data register (send buffer).

[8]SPI_I2S_ReceiveData function: Read the data in the data register (receive buffer).

[9]SPI_I2S_GetFlagStatus function: Get the status register flag bit.

[10]SPI_I2S_ClearFlag function: clear the status register flag bit.

[11]SPI_I2S_GetITStatus function: Get the interrupt flag bit.

[12]SPI_I2S_ClearITPendingBit function: clear the interrupt flag bit.

(3) Modify the MySPI.c file without modifying the main.c file. Compile directly, download the program to the development board, and observe the OLED screen display.

#include "stm32f10x.h"                  // Device header

void MySPI_W_SS(uint8_t BitValue)  //设置SS线电平
{
	GPIO_WriteBit(GPIOA, GPIO_Pin_4, (BitAction)BitValue);
}

void MySPI_Init(void)
{
	//开启SPI和GPIO的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
	
	//配置端口模式(SCK、MOSI配置为复用推挽输出,MISO引脚配置为上拉输入,SS配置为通用推挽输出)
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStructure);
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_7;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStructure);
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStructure);

	//配置SPI1
	SPI_InitTypeDef SPI_InitStructure;
	SPI_InitStructure.SPI_Mode = SPI_Mode_Master;  //指定当前设备为主机
	SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;  //双线全双工
	SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;  //8位数据帧
	SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //高位先行
	SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_128;  //时钟频率 = 72MHz / 128
	SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;  //CPHA = 0
	SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;    //CPOL = 0
	SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
	SPI_InitStructure.SPI_CRCPolynomial = 7;
	SPI_Init(SPI1, &SPI_InitStructure);
	
	//使能SPI1
	SPI_Cmd(SPI1, ENABLE);
	
	MySPI_W_SS(1);  //初始时不选择从机
}

void MySPI_Start(void)
{
	MySPI_W_SS(0);				//拉低SS,开始时序
}

void MySPI_Stop(void)
{
	MySPI_W_SS(1);				//拉高SS,终止时序
}

uint8_t MySPI_SwapByte(uint8_t ByteSend)  //非连续传输
{
	while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) != SET);
	//等待TXE=1,也就是发送缓冲区的数据被移位寄存器读走
	SPI_I2S_SendData(SPI1, ByteSend);    //主机往发送缓冲区写数据,TXE被置为0
	while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) != SET);
	//等待移位寄存器完成交换数据的操作,移位寄存器完成数据交换后接收缓冲区会将数据读走,RXNE被置为1
	return SPI_I2S_ReceiveData(SPI1);    //主机读走接收缓冲区的数据,RXNE被置为0
}

Guess you like

Origin blog.csdn.net/Zevalin/article/details/134772953