2021-07-31 STM32F103 Buffer SPI Transfer buffer communication

SPI
This article shows STM32 SPI Transfer
The content involves:
SPI byte data analog output independent write cache read and write
Identification of USART serial port
IO port input / output
External interrupt processing of key
32-bit data communication, string communication, single character communication

Full code:
: GIT source code

preface

Introduction to spi of STM32 SPI protocol is a communication protocol (Serial Peripheral Interface) proposed by Motorola, that is, serial peripheral interface. It is a high-speed full duplex communication bus. It is widely used between ADC, LCD and MCU, which requires high communication rate.
According to the level of SCK in idle state, it can be divided into two cases. When the SCK signal line is at low level in idle state, CPOL=0; When the idle state is high, CPOL=1.
Whether CPOL=0 or = 1, because the clock phase CPHA configured by us is equal to 0, it can be seen in the figure that the sampling time is at the odd edge of SCK. Note that when CPOL=0, the odd edge of the clock is the rising edge, while when CPOL=1, the odd edge of the clock is the falling edge. Therefore, the sampling time of SPI is not determined by the rising / falling edge. The effective signals of MOSI and MISO data lines remain unchanged at the odd edge of SCK, and the data signals will be sampled at the odd edge of SCK. At the non sampling time, the effective signals of MOSI and MISO will be switched.



SPI communication protocol is a very concise communication protocol from the physical layer. There are two buses in total, one SCL (clock bus) and one SDA (data bus). The communication principle is to generate the signals required by I2C bus protocol for data transmission by controlling the high and low level timing of SCL and SDA lines. When the bus is idle, these two lines are generally pulled up by the pull-up resistance connected above to maintain the high level (original link: ttps://blog.csdn.net/qq_42660303/article/details/81154995 )

1, Programming points

SPI:
(1) Initialize the target pin and port clock used for communication
(2) Enable SPI peripheral clock
(3) Configure the mode, address, rate and other parameters of SPI peripherals and enable SPI peripherals
(4) Write the function of sending and receiving basic SPI by bytes;
(5) Write functions for FLASH erase and read / write operations
(6) Write a test program to verify the read and write data

2, Use steps

1. Understand schematic diagram





Communication process

(note) Keil configuration status

My blog has project configuration design here;
Click the link
(https://blog.csdn.net/u012651389/article/details/119189949)

2. Establish the main program main c

Insert picture description here

The code is as follows:

/**
  ******************************************************************************
  * @file    GPIO/JTAG_Remap/main.c 
  * @author  MCD Application Team
  * @version V3.5.0
  * @date    08-April-2011
  * @brief   Main program body
  ******************************************************************************
  * @attention
  *    
  *    
  ******************************************************************************
  */ 

/* Includes ------------------------------------------------------------------*/
#include "stm32f10x.h"
#include "PROJ_book.h" 

/* Private functions ---------------------------------------------------------*/

/**
  * @brief  Main program.
  * @param  None
  * @retval None
  */

void fn_LED_Flash_Init(void);
void fn_usart_show_Init(void);
void fn_DMA_show_Init(void);
void fn_I2C_EE_Init(void);
void fn_I2C_EE_Soft_Init(void);
void fn_SPI_FLASH_Soft_Init(void);

#define countof(a)      (sizeof(a) / sizeof(*(a)))
  
#define  _I2C_BufferSize (countof(writeData)-1)
static uint8_t writeData[_I2C_PageSize]={4,5,6,7,8,9,10,11};
static uint8_t writeData2[_I2C_PageSize]={24,25,26,27,28,29,30,31};
static uint8_t ReadData[_I2C_BufferSize]={0};

#define  _SPI_BufferSize  SPI_PAGE_SIZE   //(countof(write_SPI_Data)-1)
static uint8_t write_SPI_Data[_SPI_BufferSize]={0};
static uint8_t Read_SPI_Data[_SPI_BufferSize]={0};

int main(void)
{ 
      
      fn_RCC_Init();          //CPU frequency doubling
      fn_Led_Init();          //LED output initialization
      fn_Key_Init();          //Key input initialization
      fn_USART_Init();        //Serial output initialization
      fn_LED_Flash_Init();    //RGB output test
      fn_usart_show_Init();   //Serial port output test
      fn_EXTI_GPIO_Config();  //External interrupt entry
      fn_DMA_show_Init();     //Initialize DMA data link
      fn_I2C_EE_Init();       //Initialize hardware I2C data link
      fn_I2C_EE_Soft_Init();  //Initialize the software I2C data link
 
      fn_SPI_FLASH_Soft_Init();  //SPI test communication
      
      while(1){
        fn_LED_ALL_OFF();
         fn_Systick_Delay(500,_Systick_ms);
        __G_OUT__;
        fn_Systick_Delay(500,_Systick_ms);
      }
}


void fn_LED_Flash_Init(void){
  uint16_t  count_Init = 2;
  printf("\n ---> LED Start running \n");
  while(count_Init-->0){
    fn_LED_ALL_OFF();
    __R_OUT__;
    fn_Systick_Delay(500,_Systick_ms);
    fn_LED_ALL_OFF();
    __G_OUT__;
    fn_Systick_Delay(500,_Systick_ms);
    fn_LED_ALL_OFF();
    __B_OUT__;
    fn_Systick_Delay(500,_Systick_ms);
    fn_LED_ALL_OFF();
    __R_OUT__;
  } 
}

void fn_usart_show_Init(void){ 
  fn_Usart_Send_Byte(_DEBUG_USARTx,'\r');
  printf("-->Serial communication means that the test is completed \n");
  fn_Usart_SendString(_DEBUG_USARTx," : wangqi \n");
}

void fn_DMA_show_Init(void){
  printf("\n ---> DMA Start running \n");
  _DMA_ROM_TO_RAM(Map_BUFFER_SIZE ,aSRC_Cont_Buffer , aDST_Buffer);
  _DMA_RAM_TO_USART(Map_BUFFER_SIZE ,USART_Source_ADDR , aDST_Buffer); 
  printf("---> DMA Operation completed \n");
}
void fn_I2C_EE_Init(void){
  printf("\n-->I2C_Function write start \n");
  _I2C_EE_Init();
  I2C_Write_fun(writeData ,EEP_Firstpage ,_I2C_BufferSize);
  I2C_Read_fun(ReadData ,EEP_Firstpage ,_I2C_BufferSize);
  printf("--->I2C_Function write complete\n\r");
}

void fn_I2C_EE_Soft_Init(void){
  printf("\n-->I2C_Software function write start \n");
  I2C_Soft_Init();
  I2C_Soft_Write_fun(writeData2 ,EEP_Firstpage ,_I2C_BufferSize);
  I2C_Soft_Read_fun(ReadData ,EEP_Firstpage ,_I2C_BufferSize);
  printf("\n--->I2C_Software function written\n\r");
}

void fn_SPI_FLASH_Soft_Init(void){
  uint16_t i,FlashID;
  printf("-->SPI Communication means the start of the test \n");
  SPI_FLASH_Init(); 
  FlashID = SPI_Read_ID() ;
  if(FlashID == _SPI_FLASH_ID){
    printf("-->SPI  0x%x \n",FlashID);
  }
  
  SPI_Erase_Sector(0); //Clear space for one page 
  printf("\n\n-->SPI Emptying start \n");
  SPI_Read_Data(Read_SPI_Data , 0, SPI_PAGE_SIZE);
  SPI_Show_Data(Read_SPI_Data , SPI_PAGE_SIZE);
  printf("\n\n-->SPI Emptying complete \n");
   
  for(i=0 ; i < _SPI_BufferSize ; i++){
    write_SPI_Data[i] = 0xA7;
  }
  SPI_Show_Data(write_SPI_Data , SPI_PAGE_SIZE);
  
  SPI_BufferWrite_Data(write_SPI_Data ,0x000000,_SPI_BufferSize);
  
  
  printf("\n\n-->SPI Input complete \n");
  SPI_Read_Data(Read_SPI_Data , 0x000000, _SPI_BufferSize);
  SPI_Show_Data(Read_SPI_Data , _SPI_BufferSize);
  
  printf("-->SPI Communication means that the test is completed \n");
}

void delay(int x){
	int y = 0xFFFFF;
	while((x--)>0){
		while((y--)>0){
			__NOP();
			__NOP();
			__NOP();
			__NOP();
			__NOP();
		}
	}
}
/******************* (C) COPYRIGHT 2011 STMicroelectronics *****END OF FILE****/

3. Establish SPI transmission header file_ book. h

The code is as follows:

#ifndef  __SPI_BOOK_H_
#define  __SPI_BOOK_H_

#include "stm32f10x.h"

//#define  _SPI_FLASH_ID              0xEF3015   //W25X16
//#define  _SPI_FLASH_ID              0xEF4015	 //W25Q16
//#define  _SPI_FLASH_ID              0XEF4018   //W25Q128
#define  _SPI_FLASH_ID              0XEF4017    //W25Q64
//----------------I2C communication configuration information is encapsulated here-------------------
#define   _FLASH_SPIx                     SPI1
#define   _FLASH_SPI_APBxClock_FUN        RCC_APB2PeriphClockCmd    
#define   _FLASH_SPI_CLK                  RCC_APB2Periph_SPI1
#define   _FLASH_SPI_GPIO_APBxClock_FUN   RCC_APB2PeriphClockCmd

#define   _FLASH_SPI_GPIO_CLK             RCC_APB2Periph_GPIOA
#define   _FLASH_SPI_SCL_PORT             GPIOA
#define   _FLASH_SPI_SCL_PIN              GPIO_Pin_5
#define   _FLASH_SPI_MISO_PORT            GPIOA
#define   _FLASH_SPI_MISO_PIN             GPIO_Pin_6
#define   _FLASH_SPI_MOSI_PORT            GPIOA
#define   _FLASH_SPI_MOSI_PIN             GPIO_Pin_7
#define   _FLASH_SPI_CSS_PORT             GPIOA
#define   _FLASH_SPI_CSS_PIN              GPIO_Pin_4

//FLASH_SPI pin configuration
#define   _FLASH_CSS_HIGH()      _FLASH_SPI_CSS_PORT->BSRR = _FLASH_SPI_CSS_PIN
#define   _FLASH_CSS_LOW()       _FLASH_SPI_CSS_PORT->BRR =  _FLASH_SPI_CSS_PIN
 
/*Communication waiting timeout*/
#define  FLASH_SPI_TIMEOUT        ((uint32_t)0x6000)
#define  FLASH_SPI_LONG_TIMEOUT   ((uint32_t)(10*FLASH_SPI_TIMEOUT))

//Information output
#define FLASH_ERROR(fmt,arg...)          printf("<<-FLASH-ERROR->> "fmt"\n",##arg)

//
#define  SPI_PAGE_SIZE   4096
#define  SPI_PAGE_Write_SIZE   256
 

//FLASH instruction
#define  FLASH_SPI_DUMMY            0x00
#define  FLASH_SPI_READ_JEDEC_ID    0x9f
#define  FLASH_SPI_REASE_SECTOR     0x20
#define  FLASH_SPI_READ_STATUS      0x05
#define  FLASH_SPI_READ_DATA        0x03
#define  FLASH_SPI_WRITE_ENABLE     0x06
#define  FLASH_SPI_WRITE_DATA       0x02
#define  FLASH_SPI_ChipErase        0xC7


void SPI_FLASH_Init(void);
uint32_t SPI_Read_ID(void);
uint32_t SPI_Read_DeviceID(void);
 
void SPI_Erase_Sector(uint32_t addr);
void SPI_FLASH_BulkErase(void);
void SPI_Read_Data(uint8_t *readBuffer , uint32_t addr ,uint16_t  numByteToRead );
void SPI_BufferRead_Data(uint8_t *writeBuffer , uint32_t WriteAddr ,uint16_t  numByteToWrite );
void SPI_Write_Data(uint8_t *writeBuffer , uint32_t addr ,uint16_t  numByteToRead );
void SPI_BufferWrite_Data(uint8_t *writeBuffer , uint32_t WriteAddr ,uint16_t  numByteToWrite );
void SPI_Show_Data(uint8_t *readBuffer , uint16_t  numByteToRead);
#endif 

4. Establish SPI transmission header file_ book. c

The code is as follows:

#include "SPI_book.h"
#include "Systick_book.h"


static __IO  uint32_t SPITimeout = FLASH_SPI_LONG_TIMEOUT;


/**
  * @brief  SPII/O to configure
  * @param  nothing
  * @retval nothing
  */
static void SPI_GPIO_Config(void){
  GPIO_InitTypeDef  GPIO_InitStructure;
  //Enable SPI related clocks
  _FLASH_SPI_APBxClock_FUN(_FLASH_SPI_CLK , ENABLE);
  _FLASH_SPI_GPIO_APBxClock_FUN(_FLASH_SPI_GPIO_CLK , ENABLE);
  
  //MISO MOSI SCK
  GPIO_InitStructure.GPIO_Pin = _FLASH_SPI_SCL_PIN;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  
  GPIO_Init(_FLASH_SPI_SCL_PORT,&GPIO_InitStructure);
  
  GPIO_InitStructure.GPIO_Pin = _FLASH_SPI_MISO_PIN;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;  
  GPIO_Init(_FLASH_SPI_SCL_PORT,&GPIO_InitStructure);
    
  GPIO_InitStructure.GPIO_Pin = _FLASH_SPI_MOSI_PIN;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  
  GPIO_Init(_FLASH_SPI_SCL_PORT,&GPIO_InitStructure);
  
  //Initialize CSS pin to enable software control, so it is directly set to push-pull output
  GPIO_InitStructure.GPIO_Pin = _FLASH_SPI_CSS_PIN;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;  
  GPIO_Init(_FLASH_SPI_SCL_PORT,&GPIO_InitStructure);
  
  _FLASH_CSS_HIGH();
}



/**
  * @brief  static void SPI_Mode_Config(void) to configure
  * @param  nothing
  * @retval nothing
  */
static void SPI_Mode_Config(void){
  SPI_InitTypeDef  SPI_InitStructure;
  
  SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2 ; //The baud rate prescaled value is 2
  SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge ;        //Data is captured on the second clock edge 
  SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;          //Clock hanging height
  SPI_InitStructure.SPI_CRCPolynomial = 0;             //The CRC function is not used, and the value is written casually
  SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;    //SPI transmit receive 8-bit frame structure
  SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex ; //two-wire full duplex
  SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;   //Data transmission starts from the MSB bit
  SPI_InitStructure.SPI_Mode = SPI_Mode_Master ;       //Set as master SPI
  SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;            //The internal NSS signal is controlled by SSI bit
  
  SPI_Init(_FLASH_SPIx , &SPI_InitStructure ); //Write configuration to register
  SPI_Cmd(_FLASH_SPIx , ENABLE);   //Enable SPI
}

/**
  * @brief  void SPI_FLASH_Init(void) initialization
  * @param  nothing
  * @retval nothing
  */
void SPI_FLASH_Init(void){
  SPI_GPIO_Config();
  SPI_Mode_Config();
}

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//                         Communication establishment operation 
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
/**
  * @brief  Basic management of the timeout situation.
  * @param  errorCode: Error code, which can be used to locate the error link
  * @retval Return 0, indicating SPI read failure
  */
static  uint32_t SPI_TIMEOUT_UserCallback(uint8_t errorCode)
{
  /* Block communication and all processes */
  FLASH_ERROR("SPI Wait timeout!errorCode = %d",errorCode);
  
  return 0;
}
/**
  * @brief  uint8_t SPI_FLASH_Send_Byte(uint8_t data) initialization
  * @param  Send and receive a byte
  * @retval nothing
  */
static uint8_t SPI_FLASH_Send_Byte(uint8_t data){
  SPITimeout = FLASH_SPI_TIMEOUT;
  //Check and wait to TX buffer
  while(SPI_I2S_GetFlagStatus(_FLASH_SPIx,SPI_I2S_FLAG_TXE) == RESET){//Send cache empty flag bit
    if(SPITimeout--==0) {return SPI_TIMEOUT_UserCallback(0);}
  }
  //Judge that the program is empty
  SPI_I2S_SendData(_FLASH_SPIx , data);
  //Judge whether the acceptance cache is not empty 
  SPITimeout = FLASH_SPI_TIMEOUT;
  while(SPI_I2S_GetFlagStatus(_FLASH_SPIx,SPI_I2S_FLAG_RXNE) == RESET){//Accept cache non null flag bit
    if(SPITimeout--==0) {return SPI_TIMEOUT_UserCallback(1);}
  }
  //The program has been sent And need to receive a byte
  return SPI_I2S_ReceiveData(_FLASH_SPIx);
}

/**
  * @brief  uint8_t SPI_FLASH_Send_Byte(uint8_t data) initialization
  * @param  Send and receive a byte
  * @retval nothing
  */
static uint8_t SPI_FLASH_SendHalf_Byte(uint16_t Halfdata){
  SPITimeout = FLASH_SPI_TIMEOUT;
  //Check and wait to TX buffer
  while(SPI_I2S_GetFlagStatus(_FLASH_SPIx,SPI_I2S_FLAG_TXE) == RESET){//Send cache empty flag bit
    if(SPITimeout--==0) {return SPI_TIMEOUT_UserCallback(2);}
  }
  //Judge that the program is empty
  SPI_I2S_SendData(_FLASH_SPIx , Halfdata);
  //Judge whether the acceptance cache is not empty 
  SPITimeout = FLASH_SPI_TIMEOUT;
  while(SPI_I2S_GetFlagStatus(_FLASH_SPIx,SPI_I2S_FLAG_RXNE) == RESET){//Accept cache non null flag bit
    if(SPITimeout--==0) {return SPI_TIMEOUT_UserCallback(3);}
  }
  //The program has been sent And need to receive a byte
  return SPI_I2S_ReceiveData(_FLASH_SPIx);
}

/**
  * @brief  uint32_t SPI_Read_ID(void)
  * @param  Read ID number
  * @retval  
  */
uint32_t SPI_Read_ID(void){
  uint32_t flash_id;
  //Film selection enable
  _FLASH_CSS_LOW();
  SPI_FLASH_Send_Byte(FLASH_SPI_READ_JEDEC_ID);
  flash_id = SPI_FLASH_Send_Byte(FLASH_SPI_DUMMY);//Memory typeID
  flash_id<<=8;
  flash_id|=SPI_FLASH_Send_Byte(FLASH_SPI_DUMMY);//Capcity typeID
  flash_id<<=8;
  flash_id|=SPI_FLASH_Send_Byte(FLASH_SPI_DUMMY);//Capcity typeID
  _FLASH_CSS_HIGH();
  return flash_id;
}

/**
  * @brief  uint32_t SPI_Read_ID(void)
  * @param  Read ID number
  * @retval  
  */
//uint32_t SPI_Read_DeviceID(void){
//  uint32_t flash_id;
//  //Film selection enable
//  _FLASH_CSS_LOW();
//  SPI_FLASH_Send_Byte(FLASH_SPI_READ_JEDEC_ID);
//  SPI_FLASH_Send_Byte(FLASH_SPI_DUMMY);//Memory typeID
//  SPI_FLASH_Send_Byte(FLASH_SPI_DUMMY);//Capcity typeID
//  SPI_FLASH_Send_Byte(FLASH_SPI_DUMMY);//Capcity typeID
//  flash_id = SPI_FLASH_Send_Byte(FLASH_SPI_DUMMY);//Capcity typeID
//  _FLASH_CSS_HIGH();
//  return flash_id;
//}

/**
  * @brief  void SPI_Write_Enable(void)
  * @param  Write enable
  * @retval  
  */
static void SPI_Write_Enable(void){
  //Film selection enable
  _FLASH_CSS_LOW();
  SPI_FLASH_Send_Byte(FLASH_SPI_WRITE_ENABLE);
  _FLASH_CSS_HIGH();  
}

/**
  * @brief  static void SPI_WaitForWriteEnd(void);
  * @param  //Wait for the FLASH internal timing operation to complete
  * @retval  
  */
static SPI_WaitForWriteEnd(void){
  uint8_t status_reg = 0;  //Judge the lowest S0 erse or write in progress
  // Chip selection instruction
  _FLASH_CSS_LOW();
  SPI_FLASH_Send_Byte(FLASH_SPI_READ_STATUS);
  do{
    status_reg = SPI_FLASH_Send_Byte(FLASH_SPI_DUMMY); //To read data, you need to continue sending
  }while((status_reg & 0x01)==1);  //Verify the lowest bit
  _FLASH_CSS_HIGH(); 
 
}
/** 
  * @brief  svoid SPI_Erase_Sector(uint32_t addr)
  * @param  Erase FLASH specified sector
  * @retval  
  */
void SPI_Erase_Sector(uint32_t addr){
  SPI_Write_Enable();
  /* Erase sector */
  /* Select FLASH: CS low level */
  _FLASH_CSS_LOW();
  /* Send sector erase instruction*/
  SPI_FLASH_Send_Byte(FLASH_SPI_REASE_SECTOR);
  /*Send high bit of erase sector address*/
  SPI_FLASH_Send_Byte((addr & 0xFF0000) >> 16);
  /* Send the median of the erase sector address */
  SPI_FLASH_Send_Byte((addr & 0xFF00) >> 8);
  /* Send low order of erase sector address */
  SPI_FLASH_Send_Byte(addr & 0xFF);
  /* Stop signal FLASH: CS high level */
  _FLASH_CSS_HIGH(); 
  /* Wait for the erase to complete*/
  SPI_WaitForWriteEnd();
}
  
 /**
  * @brief  Erase the FLASH sector and erase the whole chip
  * @param  nothing
  * @retval nothing
  */
void SPI_FLASH_BulkErase(void){
  //Send FLASH write enable command
  SPI_Write_Enable();
  //Block Erase
  //Select flash: CS low level
  _FLASH_CSS_LOW();
  SPI_FLASH_Send_Byte(FLASH_SPI_ChipErase);
  /* Stop signal FLASH: CS high level */
  _FLASH_CSS_HIGH(); 
  /* Wait for the erase to complete*/
  SPI_WaitForWriteEnd();
}


//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//                         Read / write operation 
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  
/** 
  * @brief  void SPI_Read_Data(uint8_t *readBuffer , uint32_t addr ,uint32_t  numByteToRead ));
  * @param  Read the contents of FLASH
  * @retval  
  */
void SPI_Read_Data(uint8_t *readBuffer , uint32_t addr ,uint16_t  numByteToRead ){
  //Film selection enable
  _FLASH_CSS_LOW();
  //Sending address
  /* Send read instruction */
  SPI_FLASH_Send_Byte(FLASH_SPI_READ_DATA);
  /* Send read address high bit */
  SPI_FLASH_Send_Byte((addr>>16)&0xff);
  /* Send read address median */
  SPI_FLASH_Send_Byte((addr>>8)&0xff);
  /* Send read address low order */
  SPI_FLASH_Send_Byte(addr&0xff);
  
//  if(numByteToRead > SPI_PAGE_SIZE){
//    numByteToRead = SPI_PAGE_SIZE;
//    printf("SPI_FLASH_PageWrite too large!\n");
//  }
  /* Read data */
  while(numByteToRead--){
    /* Read a byte*/
    *readBuffer = SPI_FLASH_Send_Byte(FLASH_SPI_DUMMY);
    /* Point to next byte buffer */
    readBuffer++;
  }
  /* Stop signal FLASH: CS high level */
  _FLASH_CSS_HIGH();
}

 


/** 
  * @brief  void SPI_Write_Data(uint8_t *readBuffer , uint32_t addr ,uint32_t  numByteToRead ));
  * @param  Read the contents of FLASH
  * @retval  
  */
void SPI_Write_Data(uint8_t *writeBuffer , uint32_t addr ,uint16_t  numByteToRead ){
  SPI_Write_Enable();
  //Film selection enable
  _FLASH_CSS_LOW();
  /* Page write instruction*/
  SPI_FLASH_Send_Byte(FLASH_SPI_WRITE_DATA);
  /*High bit of send write address*/
  SPI_FLASH_Send_Byte((addr&0xff0000)>>16);
  /*The median of the send write address*/
  SPI_FLASH_Send_Byte((addr&0xff00)>>8);
  /*Send low order of write address*/
  SPI_FLASH_Send_Byte(addr&0xff);
  
  if(numByteToRead > SPI_PAGE_SIZE){
    numByteToRead = SPI_PAGE_SIZE;
    printf("SPI_FLASH_PageWrite too large!\n");
  }
  //Write data
  while(numByteToRead--){
    //Send byte data currently to be written
    SPI_FLASH_Send_Byte(*writeBuffer);
    //Point to 100 million bytes of data
    writeBuffer++;
  }
  /* Stop signal FLASH: CS high level */
  _FLASH_CSS_HIGH();
  /* Wait for writing to complete*/
  SPI_WaitForWriteEnd();
}

/** 
  * @brief  SPI_Write_Data(uint8_t *writeBuffer , uint32_t addr ,uint32_t  numByteToRead ){
  * @param  Read the contents of FLASH
  * @retval  
  */
void SPI_BufferWrite_Data(uint8_t *writeBuffer , uint32_t WriteAddr ,uint16_t  numByteToWrite ){
  uint32_t NumOfPage , NumOfSingle , BufferAddr ,count , temp;
  
  if(numByteToWrite == 0){printf("SPI_FLASH_PageWrite too small!\n"); return;}
  BufferAddr = WriteAddr % SPI_PAGE_Write_SIZE;
  /*Number of front alignments of the page corresponding to the address*/
  count = SPI_PAGE_Write_SIZE - BufferAddr;
  /*Number of all addresses left on the current page*/
  if(count >= numByteToWrite){
    //The rest can be written in one line
     SPI_Write_Data(writeBuffer ,WriteAddr ,numByteToWrite );
     return;
  }
  SPI_Write_Data(writeBuffer ,WriteAddr ,(uint16_t)count );//Split write to separate page
  temp = numByteToWrite -  count ;  //Eliminate excess parts
  WriteAddr += count;
  writeBuffer+=count;
  NumOfPage = temp / SPI_PAGE_Write_SIZE ; //For large area split input
  NumOfSingle = temp % SPI_PAGE_Write_SIZE ;
  
  if(NumOfPage == 0){
    SPI_Write_Data(writeBuffer ,WriteAddr ,(uint16_t)NumOfSingle );
    return;
  }else{
    while(NumOfPage--){
      SPI_Write_Data(writeBuffer ,WriteAddr ,SPI_PAGE_Write_SIZE );
      WriteAddr += SPI_PAGE_Write_SIZE;
      writeBuffer += SPI_PAGE_Write_SIZE;
    }
    SPI_Write_Data(writeBuffer ,WriteAddr ,(uint16_t)NumOfSingle );
    return;
  }
  
}

/** 
  * @brief  void SPI_Show_Data(uint8_t *readBuffer);
  * @param  Read the contents of FLASH
  * @retval  
  */
void SPI_Show_Data(uint8_t *readBuffer , uint16_t  numByteToRead){
  uint32_t i;
  for(i=0 ;i<numByteToRead ;i++ ){
    if(i%SPI_PAGE_Write_SIZE == 0){ //Wrap every 256 bytes
      printf("\r\n ");
    }
     printf("0x%x ",readBuffer[i]);
  }
}

 
Like all peripherals using GPIO, the GPIO pin mode used must be initialized first and the multiplexing function must be configured. The GPIO initialization process is as follows:
(1) Using GPIO_InitTypeDef defines GPIO initialization structure variables to store GPIO configuration below;
(2) Call library function RCC_APB2PeriphClockCmd enables the GPIO port clock used by the SPI pin.
(3) Assign a value to the GPIO initialization structure and initialize the SCK/MOSI/MISO pin to the multiplex push-pull mode. Since the CS(NSS) pin is controlled by software, we configure it as a normal push-pull output mode.
(4) Use the configuration of the initialization structure above to call GPIO_Init function writes parameters to the register to complete the initialization of GPIO

SPI_ FLASH_ The sendbyte function implements the "SPI communication process" explained earlier: (1) this function does not contain SPI start and stop signals, but the main process of sending and receiving, so the start and stop signals should be operated before and after calling this function;
(2) Assign the SPITimeout variable to the macro SPIT_FLAG_TIMEOUT. This SPITimeout variable is subtracted by 1 each time in the following while loop, which calls the library function SPI_I2S_GetFlagStatus detects events. If an event is detected, it enters the next stage of communication. If no event is detected, it stays here for detection. When detecting spit_ FLAG_ If the timeout has not yet waited for the event, it is considered that the communication has failed and the SPI is called_ TIMEOUT_ Usercallback outputs debugging information and exits communication;
(3) Obtain the status of the transmission buffer by detecting the TXE flag. If the transmission buffer is empty, it indicates that the last possible data has been transmitted;
(4) wait until the sending buffer is empty, then call the library function SPI_. I2S_ SendData writes the data "byte" to be sent to the SPI data register DR, and the data written to the SPI data register will be stored in the transmission buffer and sent by the SPI peripheral;
(5) Wait for RXNE event after writing, that is, receive non empty buffer event. Since MOSI and MISO data transmission are synchronous under SPI two-wire full duplex mode (please refer to "SPI communication process"), when the receiving buffer is not empty, it means that the above data has been sent and the receiving buffer has also received new data;
(6) Wait until the receive buffer is not empty by calling the library function SPI_I2S_ReceiveData reads the data register DR of SPI to obtain new data in the receive buffer. The code uses the keyword "return" to take the received data as SPI_ FLASH_ The return value of the sendbyte function, so we can see the SPI receiving data function SPI defined below_ FLASH_ Readbyte, which simply calls SPI_ FLASH_ The sendbyte function sends the data "Dummy_Byte" and obtains its return value (because it does not pay attention to the sent data, the input parameter "Dummy_Byte" can be any value at this time). The reason why we can do this is that the receiving process of SPI is essentially the same as the sending process. The key is that in our upper application, we focus on the data sent or received.

5. Create I2C header file for I2C analog transmission_ soft_ book. h

The code is as follows:

#ifndef  __I2C_SOFT_BOOK_H_
#define  __I2C_SOFT_BOOK_H_

#include "stm32f10x.h"
 
//----------------I2C communication configuration information is encapsulated here-------------------
 
#define   _Soft_I2C_GPIO_APBxClock_FUN   RCC_APB2PeriphClockCmd
#define   _Soft_I2C_GPIO_CLK             RCC_APB2Periph_GPIOB
#define   _Soft_I2C_SCL_PORT             GPIOB
#define   _Soft_I2C_SCL_PIN              GPIO_Pin_6
#define   _Soft_I2C_SDA_PORT             GPIOB
#define   _Soft_I2C_SDA_PIN              GPIO_Pin_7
 

#define   _I2C_SCL_1()   _Soft_I2C_SCL_PORT->BSRR = _Soft_I2C_SCL_PIN
#define   _I2C_SCL_0()   _Soft_I2C_SCL_PORT->BRR =  _Soft_I2C_SCL_PIN
#define   _I2C_SDA_1()   _Soft_I2C_SCL_PORT->BSRR = _Soft_I2C_SDA_PIN
#define   _I2C_SDA_0()   _Soft_I2C_SCL_PORT->BRR =  _Soft_I2C_SDA_PIN
#define   _I2C_SDA_READ()  ((_Soft_I2C_SCL_PORT->IDR & _Soft_I2C_SDA_PIN)!=0)

#define I2C_WR 	 0 		/*  Write control bit*/
#define I2C_RD 	 one 		/*  Read control bit*/

//----------------Device address--------------------
/* 
 * AT24C02 2kb = 2048bit = 2048/8 B = 256 B
 * 32 pages of 8 bytes each
 *
 * Device Address
 * 1 0 1 0 A2 A1 A0 R/W
 * 1 0 1 0 0  0  0  0 = 0XA0
 * 1 0 1 0 0  0  0  1 = 0XA1 
 */
/* EEPROM Addresses defines */
#define Soft_EEPROM_ADDRESS 0xA0   /* E2 = 0 */
//#define EEPROM_ADDRESS 0xA2 /* E2 = 0 */
//#define EEPROM_ADDRESS 0xA4 /* E2 = 0 */
//#define EEPROM_ADDRESS 0xA6 /* E2 = 0 */
 
/*Format of read data and number of characters*/
#define   _I2C_Soft_PageSize      8
#define   _I2C_Soft_SIZE 				 two hundred and fifty-six 			  /*  24xx02 total capacity*/
/*I2C Storage address*/
#define  EEP_Soft_Firstpage      0x90

void  I2C_Soft_Init(void);
void  EE_Soft_Trase(void);
uint8_t I2C_Soft_Write_fun(uint8_t* pBuffer, uint8_t WriteAddr, uint16_t NumByteToWrite);
uint8_t I2C_Soft_Read_fun(uint8_t* pBuffer, uint8_t WriteAddr, uint16_t NumByteToWrite);

#endif

6. Create I2C header file for I2C analog transmission_ soft_ book. c

The code is as follows:

#include "I2C_soft_book.h"
#include "Systick_book.h"

static I2C_GPIO_Soft_Config(void){
  GPIO_InitTypeDef   GPIO_InitStructure;
  _Soft_I2C_GPIO_APBxClock_FUN(_Soft_I2C_GPIO_CLK , ENABLE);
  
  GPIO_InitStructure.GPIO_Pin = _Soft_I2C_SCL_PIN;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;  //Open drain output
  GPIO_Init(_Soft_I2C_SCL_PORT,&GPIO_InitStructure);
  
  GPIO_InitStructure.GPIO_Pin = _Soft_I2C_SDA_PIN;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;  //Open drain output
  GPIO_Init(_Soft_I2C_SDA_PORT,&GPIO_InitStructure);
}

/**
  * @brief  I2C_EE_Init Program initialization
  * @param  nothing
  * @retval nothing
  */
static void I2C_Start(void){
  // When SCL is high, SDA shows a falling edge number bit I2C bus start signal
   _I2C_SCL_1();
   _I2C_SDA_1();
   fn_Systick_Delay(50,_Systick_us);
   _I2C_SDA_0();
   fn_Systick_Delay(50,_Systick_us);
   _I2C_SCL_0();
   fn_Systick_Delay(50,_Systick_us);
}


/**
  * @brief  I2C_Stop Program initialization
  * @param  nothing
  * @retval nothing
  */

static void I2C_Stop(void){
  // When SCL is high, the rising edge of SDA indicates I2C bus stop signal
  _I2C_SDA_0();
  _I2C_SCL_1();
  fn_Systick_Delay(50,_Systick_us);
  _I2C_SDA_1(); 
}


/**
  * @brief  I2C_SendByte Program initialization
  * @param  nothing
  * @retval nothing
  */

static void I2C_SendByte(uint8_t _ucByte){
  uint8_t i;
  //High bit of send byte
  for( i=0; i<8;i++ ){
    if(_ucByte & 0x80){
      _I2C_SDA_1();
    }else{
      _I2C_SDA_0();
    }
    fn_Systick_Delay(50,_Systick_us);
    _I2C_SCL_1();
    fn_Systick_Delay(50,_Systick_us);
    _I2C_SCL_0();
    _ucByte <<=1;               //------Note that it's different here
    fn_Systick_Delay(50,_Systick_us);
  }
  _I2C_SDA_1();// Release bus
}

/**
  * @brief  I2C_ReadByte Program initialization
  * @param  nothing
  * @retval nothing
  */
static uint8_t I2C_ReadByte(void){
  uint8_t i;
  uint8_t value;
  
  //Read that the first bit is bit7 of data
  value = 0;
  for(i=0 ;i<8 ;i++ ){
    value <<=1;
    _I2C_SCL_1();
    fn_Systick_Delay(50,_Systick_us);
    if(_I2C_SDA_READ()){
      value++;
    }
    _I2C_SCL_0();
    fn_Systick_Delay(50,_Systick_us);
  }
  return value;
}


/**
  * @brief  I2C_WaitAck  
  * @param  nothing
  * @retval nothing
  */

static uint8_t I2C_WaitAck(void){
  uint8_t re;
  _I2C_SDA_1();
  fn_Systick_Delay(50,_Systick_us);
  _I2C_SCL_1();
  fn_Systick_Delay(50,_Systick_us);
  if(_I2C_SDA_READ()){
    re = 1;
  }else{
    re = 0;
  }
  _I2C_SCL_0();
  fn_Systick_Delay(50,_Systick_us);
  return re;
}


/**
  * @brief  I2C_ACK  
  * @param  nothing
  * @retval nothing
  */

static void I2C_ACK(void){
  _I2C_SDA_0();  //CPU drive SDA = 0;
  fn_Systick_Delay(50,_Systick_us);
  _I2C_SCL_1();  //CPU generates 1 clock
  fn_Systick_Delay(50,_Systick_us);
  _I2C_SCL_0();
  fn_Systick_Delay(50,_Systick_us);
  _I2C_SDA_1(); //CPU releases SDA bus
}



/**
  * @brief  I2C_ACK  
  * @param  nothing
  * @retval nothing
  */

static void I2C_NACK(void){
  _I2C_SDA_1();  //CPU driven SDA = 1;
  fn_Systick_Delay(50,_Systick_us);
  _I2C_SCL_1();  //CPU generates 1 clock
  fn_Systick_Delay(50,_Systick_us);
  _I2C_SCL_0();
  fn_Systick_Delay(50,_Systick_us);
}

static uint8_t I2C_CheckDevice(uint8_t _Address){
  uint8_t ucAck;
  
  I2C_GPIO_Soft_Config();
  I2C_Start();
  
  I2C_SendByte(_Address | I2C_WR);
  ucAck = I2C_WaitAck();  /* ACK response of detection equipment */
  
  I2C_Stop(); /* Send stop signal */
  
  return ucAck;
}


//----------------------I2C write alone operation--------------------
//------------------------------------------------------

/**
  * @brief  EE_Soft_Check_State  
  * @param  Judge whether the serial EERPOM is normal
  * @retval nothing
  */

static uint8_t EE_Soft_Check_State(void){
  if(I2C_CheckDevice(Soft_EEPROM_ADDRESS)==0){return 1;}
  else{I2C_Stop(); /* Send stop signal */ return 0;}
}

/**
  * @brief  uint8_t I2C_Soft_BufferRead(uint8_t* pBuffer, 
  *      uint8_t ReadAddr, uint16_t NumByteToRead)
  * @param  Judge whether the serial EERPOM is normal
  * @retval nothing
  */
static uint8_t I2C_Soft_BufferRead(uint8_t* pBuffer, uint8_t ReadAddr, uint16_t NumByteToRead){
  uint16_t i ;
  //Get several bytes in succession
  // Initiate I2C bus start signal
  I2C_Start();
  //Send control byte address and read data signal
  I2C_SendByte(Soft_EEPROM_ADDRESS | I2C_WR);
  //Waiting for response status
  if(I2C_WaitAck()!=0){printf("EEPROM Error 1 !\r\n"); goto  CMD_Fail;}
  //Send data read position information signal
  I2C_SendByte((uint8_t)ReadAddr);
  //Waiting for response status
  if(I2C_WaitAck()!=0){printf("EEPROM Error 2 !\r\n");goto  CMD_Fail;}
  
  //--------------
  //Restart I2C bus 
  I2C_Start();
  //Sending device address
  I2C_SendByte(Soft_EEPROM_ADDRESS| I2C_RD);
  //Waiting for response status
  if(I2C_WaitAck()!=0){printf("EEPROM Error 3 !\r\n"); goto  CMD_Fail;}
  for(i=0 ;i<NumByteToRead ;i++ ){
    pBuffer[i] = I2C_ReadByte();
    if(i!=NumByteToRead-1){
      I2C_ACK();
    }else{
      I2C_NACK();
    }
  }
  I2C_Stop();
  return 1;
 
 CMD_Fail:
  I2C_Stop();
  return 0;
}

/**
  * @brief  uint8_t EE_Soft_WriteBytes(uint8_t* pBuffer, 
  *                   uint8_t ReadAddr, uint16_t NumByteToRead)
  * @param  Judge whether the serial EERPOM is normal
  * @retval nothing
  */

static uint8_t EE_Soft_WriteBytes(uint8_t* pBuffer, uint8_t ReadAddr, uint16_t NumByteToRead){
  uint16_t i , m;
  uint16_t usAddr;
  /*
    Unlike read operation, write Serial EEPROM can read many bytes continuously, and each write operation can only be on the same page
    For 24C page size = 8 
    The simple processing method is the byte by byte write operation mode. Write a byte and send the address
    In order to improve the efficiency of continuous writing, the function uses the Page write operation
  */
  usAddr = ReadAddr;
  for(i=0 ;i<NumByteToRead;i++ ){
    // When sending the first byte or the first address of the page, it is necessary to restart the start signal and address
    if((i==0)||(usAddr)&(_I2C_Soft_PageSize-1)==0){
      // Send stop signal
      I2C_Stop();
      //Judge whether the memory write is successful through the detector
      m = 100;
      for (m = 0; m < 100; m++){	
        //Start I2C bus 
        I2C_Start();
        //Sending device address
        I2C_SendByte(Soft_EEPROM_ADDRESS| I2C_WR);
        //Waiting for response status
        if(I2C_WaitAck()==0){break;}
      }
      if(m==100){printf("EEPROM Error 4 !\r\n"); goto CMD_FAIL_bytes ; }
      I2C_SendByte((uint8_t)usAddr);
      if(I2C_WaitAck()!=0){printf("EEPROM Error 5 !\r\n"); goto CMD_FAIL_bytes;}
    }
    // Start writing data 
    I2C_SendByte(pBuffer[i]);
    //Waiting for response status
    if(I2C_WaitAck()!=0){printf("EEPROM Error 7 !\r\n"); goto CMD_FAIL_bytes;}
    usAddr++;
  }
  // Send stop signal
  I2C_Stop();
  return 1;
  
CMD_FAIL_bytes:
  // Send stop signal
  I2C_Stop();
  return 0;
}



/**
  * @brief  void  EE_Soft_Trase(void)  
  * @param  Judge whether the serial EERPOM is normal
  * @retval nothing
  */
void  EE_Soft_Trase(void){
  uint16_t i ;
  uint8_t buf[_I2C_Soft_SIZE]={0};
  
  // Fill buffer
  for(i=0 ;i<_I2C_Soft_SIZE ;i++ ){
    buf[i] = 0xFF;
  }
  //Write EEPROM start address = 0, data length is 256
  if(EE_Soft_WriteBytes(buf,0,_I2C_Soft_SIZE)==0){
    printf("Erase EEPROM Error!\r\n");
		return;
  }else{
    printf("Erase EEPROM Error!\r\n");
  }
}


/**
  * @brief  void I2C_Soft_Init(void)
  * @param   
  * @retval nothing
  */
void I2C_Soft_Init(void){
  if(EE_Soft_Check_State()==0){
      /* No EEPROM detected */
      printf("Serial not detected EEPROM!\r\n");
  }
}


/**
  * @brief  void I2C_Soft_Write_fun(uint8_t* pBuffer, uint8_t WriteAddr, uint16_t NumByteToWrite) 
  * @param  Judge whether the serial EERPOM is normal
  * @retval nothing
  */
uint8_t I2C_Soft_Write_fun(uint8_t* pBuffer, uint8_t WriteAddr, uint16_t NumByteToWrite){
  uint16_t i;
   
  //-------------------------------
  if(EE_Soft_Check_State()==0){
    /* No EEPROM detected */
		printf("Serial not detected EEPROM!\r\n");
    return 1;
  }  
  //------------Write I2C-------------------
  if(EE_Soft_WriteBytes(pBuffer,WriteAddr ,NumByteToWrite)==0){
    /* No EEPROM detected */
		printf("write EEPROM error!\r\n");
    return 1;
  }else{
    /* No EEPROM detected */
		printf("write EEPROM success!\r\n");
  }
  fn_Systick_Delay(150,_Systick_us);
  //--------------Data check--------------
  printf("EEPROM Write data check\r\n");
  for(i=0 ;i<NumByteToWrite ;i++ ){
     
     printf(" %d ",pBuffer[i]);
    if((i & 15)==15){
      printf("\r\n");
    }
  }
  return 0;
  
}


uint8_t I2C_Soft_Read_fun(uint8_t* pBuffer, uint8_t WriteAddr, uint16_t NumByteToWrite){
  uint16_t i;
  //-------------Read I2C------------------
  if(I2C_Soft_BufferRead(pBuffer,WriteAddr,NumByteToWrite)==0){
    /* No EEPROM detected */
		printf("read EEPROM error!\r\n");
    return 1;
  }else{
    /* No EEPROM detected */
		printf("\n read EEPROM success!\r\n");
  }
  //--------------Data check--------------
  printf("EEPROM Read data check \r\n");
  for(i=0 ;i<NumByteToWrite ;i++ ){
     printf(" %d ",pBuffer[i]);
    if((i & 15)==15){
      printf("\r\n");
    }
  }
  return 1;
}
/*********************END OF FILE**********************/

7. Establish the header file I2C for I2C hardware transmission_ book. h

The code is as follows:

#ifndef  __I2C_BOOK_H_
#define  __I2C_BOOK_H_

#include "stm32f10x.h"
#include "stm32f10x_rcc.h"
 
#include "USART_book.h"

//----------------I2C communication configuration information is encapsulated here-------------------
#define   _EEPROM_I2Cx                     I2C1
#define   _EEPROM_I2C_APBxClock_FUN        RCC_APB1PeriphClockCmd    
#define   _EEPROM_I2C_CLK                  RCC_APB1Periph_I2C1
#define   _EEPROM_I2C_GPIO_APBxClock_FUN   RCC_APB2PeriphClockCmd
#define   _EEPROM_I2C_GPIO_CLK             RCC_APB2Periph_GPIOB
#define   _EEPROM_I2C_SCL_PORT             GPIOB
#define   _EEPROM_I2C_SCL_PIN              GPIO_Pin_6
#define   _EEPROM_I2C_SDA_PORT             GPIOB
#define   _EEPROM_I2C_SDA_PIN              GPIO_Pin_7

/*STM32 I2C Speed mode  */
#define   _I2C_Speed       400000
/* I2C Device address */
#define   _I2Cx_OWN_ADDRESS7              0x5f
/*Format of read data and number of characters*/
#define   _I2C_PageSize      8
/*I2C Storage address*/
#define  EEP_Firstpage      0x90
#define  EEP_SIZE           0xFF
//----------------Device address--------------------
/* 
 * AT24C02 2kb = 2048bit = 2048/8 B = 256 B
 * 32 pages of 8 bytes each
 *
 * Device Address
 * 1 0 1 0 A2 A1 A0 R/W
 * 1 0 1 0 0  0  0  0 = 0XA0
 * 1 0 1 0 0  0  0  1 = 0XA1 
 */
/* EEPROM Addresses defines */
#define EEPROM_ADDRESS 0xA0   /* E2 = 0 */
//#define EEPROM_ADDRESS 0xA2 /* E2 = 0 */
//#define EEPROM_ADDRESS 0xA4 /* E2 = 0 */
//#define EEPROM_ADDRESS 0xA6 /* E2 = 0 */

//----------------Function declaration--------------------
 
//I2C application function
void _I2C_EE_Init(void);
void I2C_Write_fun(uint8_t* pBuffer, uint8_t WriteAddr, uint16_t NumByteToWrite);
void I2C_Read_fun(uint8_t* pBuffer, uint8_t WriteAddr, uint16_t NumByteToWrite);

#endif

8. Establish the header file I2C for I2C hardware transmission_ book. c

The code is as follows:

#include "I2C_book.h"
#include "Systick_book.h"

/**
  * @brief  I2C_EE_Init Program initialization
  * @param  nothing
  * @retval nothing
  */
static void I2C_GPIO_Config(void){
  GPIO_InitTypeDef   GPIO_InitStructure;
  //  Initialize I2C related clock
  _EEPROM_I2C_APBxClock_FUN(_EEPROM_I2C_CLK,ENABLE);
  _EEPROM_I2C_GPIO_APBxClock_FUN(_EEPROM_I2C_GPIO_CLK,ENABLE);
  //  Initialize I2C_SCL SDA
  
  GPIO_InitStructure.GPIO_Pin = _EEPROM_I2C_SCL_PIN;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;  //Open drain output
  GPIO_Init(_EEPROM_I2C_SCL_PORT,&GPIO_InitStructure);
  
  GPIO_InitStructure.GPIO_Pin = _EEPROM_I2C_SDA_PIN;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;  //Open drain output
  GPIO_Init(_EEPROM_I2C_SDA_PORT,&GPIO_InitStructure);
}

/**
  * @brief  I2C_EE_Init Program initialization
  * @param  nothing
  * @retval nothing
  */
static void I2C_Mode_Config(void){
  I2C_InitTypeDef I2C_InitStructure;
  /* i2C to configure */
  I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
  // The high-level data is stable, and the low-level data changes the duty cycle of SCL clock line
  I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
  
  I2C_InitStructure.I2C_OwnAddress1 = _I2Cx_OWN_ADDRESS7;
  
  I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
  
  //I2C addressing mode
  I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
  //Communication frequency
  I2C_InitStructure.I2C_ClockSpeed = _I2C_Speed;
  //I2C initialization
  I2C_Init(_EEPROM_I2Cx,&I2C_InitStructure);
  //Enable I2C
  I2C_Cmd(_EEPROM_I2Cx,ENABLE);
}
  
/**************************************/

static uint32_t I2C_TIMEOUT_UserCallback(uint8_t errorCode){
  fn_Usart_SendString(_DEBUG_USARTx,"I2C Wait timeout!errorCode =");
  printf("%d\n",errorCode);
 
  return 0;
}
/**************************************/

/*Communication waiting timeout*/
#define  I2CT_FLAG_TIMEOUT  ((uint32_t)0x6000)
#define  I2CT_LONG_TIMEOUT  ((uint32_t)(10*I2CT_FLAG_TIMEOUT))
static uint16_t I2CTimeout;
/**************************************/
/**
* @brief  Write a byte to I2C EEPROM
* @param  pBuffer:Buffer pointer
* @param  WriteAddr:Write address
* @retval Normal returns 1 and abnormal returns 0
*/
static uint32_t  I2C_EE_ByteWrite(u8* pBuffer, uint8_t WriteAddr ){
  
  I2CTimeout = I2CT_LONG_TIMEOUT;
  while(I2C_GetFlagStatus(_EEPROM_I2Cx , ENABLE)){
    if((I2CTimeout--) == 0){return  I2C_TIMEOUT_UserCallback(4);}
  }
 
  //Generate I2C start signal
  I2C_GenerateSTART(_EEPROM_I2Cx , ENABLE);
  I2CTimeout = I2CT_LONG_TIMEOUT; //This variable is the delay exception time
  //Detect EV5 events and clear the identification bit
  while(!I2C_CheckEvent(_EEPROM_I2Cx,I2C_EVENT_MASTER_MODE_SELECT)){
    if((I2CTimeout--)==0){return I2C_TIMEOUT_UserCallback(5);}
  } 
  
  //Send EEPROM device address
  I2C_Send7bitAddress(_EEPROM_I2Cx,EEPROM_ADDRESS,I2C_Direction_Transmitter);
  I2CTimeout = I2CT_LONG_TIMEOUT; //This variable is the delay exception time
  //Detect EV6 event and clear identification bit
  while(I2C_CheckEvent(_EEPROM_I2Cx,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) == ERROR){
    if((I2CTimeout--)==0){return I2C_TIMEOUT_UserCallback(6);}
  }
  
  //Send the internal address of EEPROM to be written (i.e. its address is stored in EEPROM);
  I2C_SendData(_EEPROM_I2Cx,WriteAddr);
  I2CTimeout = I2CT_LONG_TIMEOUT;
  //Detect EV8 event clear flag bit
  while(!I2C_CheckEvent(_EEPROM_I2Cx,I2C_EVENT_MASTER_BYTE_TRANSMITTED)){
    if((I2CTimeout--)==0){return I2C_TIMEOUT_UserCallback(2);}
  }
   
  //Send the data inside the EEPROM to be written;
  I2C_SendData(_EEPROM_I2Cx,*pBuffer);
  I2CTimeout = I2CT_LONG_TIMEOUT;
  //Detect EV8 event clear flag bit
  while(!I2C_CheckEvent(_EEPROM_I2Cx,I2C_EVENT_MASTER_BYTE_TRANSMITTED)){
    if((I2CTimeout--)==0){return I2C_TIMEOUT_UserCallback(3);}
  }
  
  //Send the data inside the EEPROM to be written;
  I2C_SendData(_EEPROM_I2Cx,ENABLE);
  
  return 1;
}

 /**
  * @brief The data in the buffer is written to I2C EEPROM in the way of single byte writing, which is slower than page writing
  * @param pBuffer:Buffer pointer
  * @param WriteAddr:Write address
  * @param NumByteToWrite:Bytes written
  */

static void I2C_EE_WaitEepromStandbyState(void){
  vu16 SR1_Tmp = 0;
  do{
    //Generate I2C start signal
    I2C_GenerateSTART(_EEPROM_I2Cx,ENABLE);
    //Read I2C1 SR1 register
    SR1_Tmp = I2C_ReadRegister(_EEPROM_I2Cx, I2C_Register_SR1);
    //Send EEPROM address + direction
    I2C_Send7bitAddress(_EEPROM_I2Cx,EEPROM_ADDRESS,I2C_Direction_Transmitter);
  }while(!(I2C_ReadRegister(_EEPROM_I2Cx, I2C_Register_SR1) & 0x0002));
  /* Clear AF bit */
  I2C_ClearFlag(_EEPROM_I2Cx, I2C_FLAG_AF);
  //Send stop bit signal
  I2C_GenerateSTOP(_EEPROM_I2Cx , ENABLE);
}

//zuozuo04-30 

/**
* @brief Multiple bytes can be written in one write cycle of EEPROM, but the number of bytes written at one time
* The size of EEPROM page cannot be exceeded. AT24C02 has 8 bytes per page
* @param
* @param pBuffer:Buffer pointer
* @param WriteAddr:Write address
* @param NumByteToWrite:The number of bytes to write requires NumByToWrite to be less than the page size
* @retval Normal returns 1 and abnormal returns 0
*/
static uint8_t I2C_EE_PageWrite(uint8_t* pBuffer, uint8_t WriteAddr, uint16_t NumByteToWrite){
  I2CTimeout = I2CT_LONG_TIMEOUT;
  while(I2C_GetFlagStatus(_EEPROM_I2Cx , ENABLE)){
    if((I2CTimeout--) == 0){return  I2C_TIMEOUT_UserCallback(4);}
  }
  //Generate I2C start signal
  I2C_GenerateSTART(_EEPROM_I2Cx , ENABLE);
  I2CTimeout = I2CT_LONG_TIMEOUT; //This variable is the delay exception time
  //Detect EV5 events and clear the identification bit
  while(!I2C_CheckEvent(_EEPROM_I2Cx,I2C_EVENT_MASTER_MODE_SELECT)){
    if((I2CTimeout--)==0){return I2C_TIMEOUT_UserCallback(5);}
  }
  
  //Send EEPROM device address
  I2C_Send7bitAddress(_EEPROM_I2Cx,EEPROM_ADDRESS,I2C_Direction_Transmitter);
  I2CTimeout = I2CT_LONG_TIMEOUT; //This variable is the delay exception time
  //Detect EV6 event and clear identification bit
  while(I2C_CheckEvent(_EEPROM_I2Cx,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) == ERROR){
    if((I2CTimeout--)==0){return I2C_TIMEOUT_UserCallback(6);}
  }
  
  //Send the EEPROM internal address to be written (EEPROM internal memory address)
  I2C_SendData(_EEPROM_I2Cx,WriteAddr);
  I2CTimeout = I2CT_LONG_TIMEOUT; //This variable is the delay exception time
  //Detect EV7 events and clear the identification bit
  while(!I2C_CheckEvent(_EEPROM_I2Cx,I2C_EVENT_MASTER_BYTE_TRANSMITTING)){
    if((I2CTimeout--)==0){return I2C_TIMEOUT_UserCallback(7);}
  }

  //Loop sending NumByteToWrite data
  while(NumByteToWrite--){
    //Send buffer data
    I2C_SendData(_EEPROM_I2Cx,*pBuffer++);
    I2CTimeout = I2CT_FLAG_TIMEOUT; //This variable is the delay exception time
    //Detect EV8 events and clear the identification bit
    while(!I2C_CheckEvent(_EEPROM_I2Cx,I2C_EVENT_MASTER_BYTE_TRANSMITTED)){
      if((I2CTimeout--)==0){return I2C_TIMEOUT_UserCallback(8);}
    }       
  }
  //Send stop signal
  I2C_GenerateSTOP(_EEPROM_I2Cx,ENABLE);
  return 1;
}



/**  Write one page quickly
* @brief Write the data in the buffer to I2C EEPROM
* @param
* @arg pBuffer:Buffer pointer
* @arg WriteAddr:Write address
* @arg NumByteToWrite:Bytes written
* @retval nothing
*/
#define I2C_ PageSize 8 / / at24c01 02 8 bytes per page
static void I2C_EE_BufferWrite(uint8_t* pBuffer, uint8_t WriteAddr, uint16_t NumByteToWrite){
  u8 NumOfPage = 0, NumOfSingle = 0 , Addr = 0 , count = 0,temp = 0;
  //Mod remainder operation, if writeAddr is I2C_PageSize is an integral multiple of the book, and the operation result bit Addr is 0
  Addr = WriteAddr % I2C_PageSize;
  //The difference is count, which can be aligned to the page address
  count = I2C_PageSize - Addr; 
  //Figure out how many whole pages to write
  NumOfPage = NumByteToWrite / I2C_PageSize;
  //The mod operation calculates the number of bytes less than one page
  NumOfSingle = NumByteToWrite % I2C_PageSize;
  // If Addr = 0, WriteAddr is aligned by page
  // This is very simple. You can write the whole page directly
  // Just finish the rest that is less than one page
  if(Addr == 0){
    //If numbytetowrite < I2C_ PageSize
    if(NumOfPage==0){
      I2C_EE_PageWrite(pBuffer , WriteAddr, NumOfSingle);
      I2C_EE_WaitEepromStandbyState();
    }//If numbytetowrite > I2C_ PageSize
    else{
      //Write the integer page first
      while(NumOfPage--){
        I2C_EE_PageWrite(pBuffer , WriteAddr, I2C_PageSize);
        I2C_EE_WaitEepromStandbyState();
        WriteAddr += I2C_PageSize ;
        pBuffer += I2C_PageSize ;
      }
      //If there is more than one page of data, write it out
      if(NumOfSingle != 0){
        I2C_EE_PageWrite(pBuffer , WriteAddr, NumOfSingle);
        I2C_EE_WaitEepromStandbyState();
      }
    }
  }
  //If WriteAddr is not pressed I2C_PageSize alignment
  //Then calculate how much data is needed to align to the page address, and then write these data first, and the remaining starting addresses have been aligned
  //To the page address, repeat the code above
  else{
    //If numbytetowrite < I2C_ PageSize
    if(NumOfPage == 0){
      //If numofsingle > count, if the previous page cannot be completed, write the next page
      if(NumOfSingle > count){
        temp = NumOfSingle - count;
        I2C_EE_PageWrite(pBuffer , WriteAddr, count);
        I2C_EE_WaitEepromStandbyState();
        WriteAddr += count ;
        pBuffer += count ;
        
        I2C_EE_PageWrite(pBuffer , WriteAddr, temp);
        I2C_EE_WaitEepromStandbyState();
      }else{//If count is larger than NumOfSingle
        I2C_EE_PageWrite(pBuffer , WriteAddr, NumByteToWrite);
        I2C_EE_WaitEepromStandbyState(); 
      }
    }
    //If numbytetowrite > I2C_ PageSize
    else{
      //If the address is out of alignment, the Count will be processed separately, and this operation will not be added
      NumByteToWrite -= count;
      NumOfPage = NumByteToWrite / I2C_PageSize ;
      NumOfSingle = NumByteToWrite % I2C_PageSize;
      //First write the remaining bytes of the page where WriteAddr is located
      if(count!=0){
        I2C_EE_PageWrite(pBuffer , WriteAddr, count);
        I2C_EE_WaitEepromStandbyState();
        //After adding count, the address is aligned to the page
        WriteAddr += count ;
        pBuffer += count ;
      }
      //Write the whole page
      while(NumOfPage--){
        I2C_EE_PageWrite(pBuffer , WriteAddr, I2C_PageSize);
        I2C_EE_WaitEepromStandbyState();
        WriteAddr += I2C_PageSize ;
        pBuffer += I2C_PageSize ;
      }
      //If the extra is not enough for one page, finish it
      if(NumOfSingle !=0){
        I2C_EE_PageWrite(pBuffer , WriteAddr, NumOfSingle);
        I2C_EE_WaitEepromStandbyState(); 
      }
    }
    
  }
}



/*  EEPROM read
* @brief Read a piece of data from EEPROM
* @param pBuffer:Buffer pointer to store data read from EEPROM
* @param ReadAddr:Address of EEPROM receiving data
* @param NumByteToRead:Number of bytes to read from EEPROM
* @retval Normal returns 1 and abnormal returns 0
*/

static uint8_t I2C_EE_BufferRead(uint8_t* pBuffer, uint8_t ReadAddr, uint16_t NumByteToRead){
    I2CTimeout = I2CT_LONG_TIMEOUT ;
    while(I2C_GetFlagStatus(_EEPROM_I2Cx , I2C_FLAG_BUSY)){
      if((I2CTimeout--)==0){return I2C_TIMEOUT_UserCallback(9);}
    }
    
    //Generate I2C start signal
    I2C_GenerateSTART(_EEPROM_I2Cx , ENABLE);
    I2CTimeout = I2CT_FLAG_TIMEOUT;
    //Detect EV10 events and clear labels
    while(! I2C_CheckEvent(_EEPROM_I2Cx , I2C_EVENT_MASTER_MODE_SELECT)){
      if((I2CTimeout--)==0){ return I2C_TIMEOUT_UserCallback(10);}
    }
    
    //Send EEPROM device address
    I2C_Send7bitAddress(_EEPROM_I2Cx , EEPROM_ADDRESS , I2C_Direction_Transmitter);
    I2CTimeout = I2CT_FLAG_TIMEOUT;
    //Detect EV11 events and clear
    while(! I2C_CheckEvent(_EEPROM_I2Cx , I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)){
      if((I2CTimeout--)==0){ return I2C_TIMEOUT_UserCallback(11);}
    }
    
    //Clear the EV12 event by resetting the PE bit
    I2C_Cmd(_EEPROM_I2Cx ,ENABLE );
    //Send the EEPROM internal address to be read (i.e. EEPROM internal memory address)
    I2C_SendData(_EEPROM_I2Cx, ReadAddr);
    I2CTimeout = I2CT_FLAG_TIMEOUT ;
    //Detect EV12 events and clear
    while(! I2C_CheckEvent(_EEPROM_I2Cx , I2C_EVENT_MASTER_BYTE_TRANSMITTED)){
      if((I2CTimeout--)==0){ return I2C_TIMEOUT_UserCallback(12);}
    }
    
    //Generate the second I2C start signal
    I2C_GenerateSTART(_EEPROM_I2Cx , ENABLE);
    I2CTimeout = I2CT_FLAG_TIMEOUT;
    //Detect EV13 events and clear
    while(! I2C_CheckEvent(_EEPROM_I2Cx ,  I2C_EVENT_MASTER_MODE_SELECT)){
      if((I2CTimeout--)==0){ return I2C_TIMEOUT_UserCallback(13);}
    }
    
    //Send EEPROM device address
    I2C_Send7bitAddress(_EEPROM_I2Cx , EEPROM_ADDRESS , I2C_Direction_Receiver);
    I2CTimeout = I2CT_FLAG_TIMEOUT;
    //Detect EV14 events and clear
    while(! I2C_CheckEvent(_EEPROM_I2Cx , I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED)){
      if((I2CTimeout--)==0){ return I2C_TIMEOUT_UserCallback(14);}
    }
    
    //Read NumByteToRead data
    while(NumByteToRead){
      //If NumByteToRead=1, the last data has been received
      //Send response signal to end output
      if(NumByteToRead == 1){
        //Send non reply signal
        I2C_AcknowledgeConfig(_EEPROM_I2Cx , DISABLE);
        
      }
      I2CTimeout = I2CT_LONG_TIMEOUT;
      while(I2C_CheckEvent(_EEPROM_I2Cx , I2C_EVENT_MASTER_BYTE_RECEIVED)==0){
        if((I2CTimeout--)==0){return I2C_TIMEOUT_UserCallback(3);}
      }
      
      //Read one byte of data from the device through I2C
      *pBuffer = I2C_ReceiveData(_EEPROM_I2Cx);
      //The pointer to the stored data refers to the following address
      pBuffer++;
      //Accept data self subtraction
      NumByteToRead--;
    }
    //Send stop signal
        I2C_GenerateSTOP(_EEPROM_I2Cx , ENABLE);
    //Enable large response to facilitate I2C output
    I2C_AcknowledgeConfig(_EEPROM_I2Cx , ENABLE);
    return 1;
}

//--------------------------------------------------------

/**
* @brief void _I2C_EE_Init(void) 
* @param nothing
* @retval Normal return 1, abnormal return 0
*/
void _I2C_EE_Init(void){
  I2C_GPIO_Config(); 
  I2C_Mode_Config();
}

/**
* @brief I2C_Write_fun(uint8_t* pBuffer, uint8_t WriteAddr, uint16_t NumByteToWrite) 
* @param nothing
* @retval Normal return 1, abnormal return 0
*/
void I2C_Write_fun(uint8_t* pBuffer, uint8_t WriteAddr, uint16_t NumByteToWrite){
  u16 i;
  
  printf("I2C_Write data \n");
  I2C_EE_WaitEepromStandbyState();
  I2C_EE_PageWrite(pBuffer,WriteAddr, NumByteToWrite);
  for(i=0 ;i<NumByteToWrite ;i++ ){
    printf("%d ", *pBuffer++);
    if(i%16 == 15)    
        printf("\n\r");
  }
  printf("\nI2C_Write data complete \n");
  I2C_EE_WaitEepromStandbyState();
   
  for(i=0 ;i<NumByteToWrite ;i++ ){
    printf("%d ", pBuffer[i]);
    if(i%16 == 15)    
        printf("\n\r");
  }
}

/**
* @brief I2C(void I2C_Read_fun(uint8_t* pBuffer, uint8_t WriteAddr, uint16_t NumByteToWrite))Read write test
* @param nothing
* @retval EEP_SIZE
*/
void I2C_Read_fun(uint8_t* pBuffer, uint8_t WriteAddr, uint16_t NumByteToWrite){
  u16 i;
  
  printf("I2C_Data detection \n");
  I2C_EE_BufferRead(pBuffer,WriteAddr,NumByteToWrite);
  printf("\nI2C_Data read complete \n");
  for(i=0 ;i<NumByteToWrite ;i++ ){
    printf("%d ", pBuffer[i]);
    if(i%16 == 15)    
        printf("\n\r");
  }
   printf("\n--->I2C_Data detection completed\n");
}
   

9. Establish the header file of USART transmission_ book. h

The code is as follows:

#ifndef  __USART_BOOK_H_
#define  __USART_BOOK_H_

#include "stm32f10x.h"
#include <stdio.h>
#include "stm32f10x_usart.h"
#include "stm32f10x_rcc.h"
 //The macro definition of the serial port is different. The bus attached to the serial port is different from that of IO
 
 //Serial port 1
#define  _DEBUG_USARTx                  USART1
#define  _DEBUG_USART_CLK               RCC_APB2Periph_USART1
#define  _DEBUG_USART_APBxClkCmd        RCC_APB2PeriphClockCmd
#define  _DEBUG_USART_BAUDRATE          115200
 
// USART GPIO pin definition
#define  _DEBUG_USART_GPIO_CLK          RCC_APB2Periph_GPIOA
#define  _DEBUG_USART_GPIO_APBxCLKCmd   RCC_APB2PeriphClockCmd

#define  _DEBUG_USART_TX_GPIO_PORT      GPIOA
#define  _DEBUG_USART_TX_GPIO_PIN       GPIO_Pin_9
#define  _DEBUG_USART_TX_GPIO_MODE      GPIO_Mode_AF_PP
#define  _DEBUG_USART_RX_GPIO_PORT      GPIOA
#define  _DEBUG_USART_RX_GPIO_PIN       GPIO_Pin_10
#define  _DEBUG_USART_RX_GPIO_MODE      GPIO_Mode_IN_FLOATING

#define  _DEBUG_NVIC_USART_IRQ          USART1_IRQn
#define  _DRBUG_USART_IRQHandler        USART1_IRQHandler
 
void  fn_USART_IO_Config(void); 
void  fn_USART_Config(void); 
void  fn_USART_Init(void);

void  fn_Usart_Send_Byte(USART_TypeDef * pUSARTx , uint8_t ch );
void  fn_Usart_SendString(USART_TypeDef *pUSARTx , char * str);
void  Usart_SendHalf_32_Word( USART_TypeDef * pUSARTx, uint32_t ch);


int fputc (int ch , FILE *f);
int fgetc(FILE *f);
void  _DRBUG_USART_IRQHandler(void);

#endif

10. Establish the C file transmitted by USART_book.c

The code is as follows:

#include "USART_book.h"


/**************************************************************
* @brief  
* void fn_LED_Corporate(GPIO_TypeDef*  _GPIO_x , uint16_t  _GPIO_Pin_x , 
*            LED_Corporate_state_t  _LED_Corporate_state_t );
* @param  
* //Serial port 1
*    #define  _DEBUG_NVIC_USART_IRQ               USART1_IRQn
*    #define  _DRBUG_NVIC_USART_IRQHandler        USART1_IRQHandler
* @retval 
*************************************************************/ 
static void NVIC_Configuration(void){
  NVIC_InitTypeDef  NVIC_InitStructure;
  /* Nested vector interrupt control register group selection*/
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
  
  /* Configure USART as interrupt source */
  NVIC_InitStructure.NVIC_IRQChannel = _DEBUG_NVIC_USART_IRQ;
  /* The preemption priority is 1 */
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
  /* The sub priority is 1 */
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
  /* Enable interrupt */
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  /* Initialize and configure NVIC */
  NVIC_Init(&NVIC_InitStructure);
}

/**************************************************************
* @brief  
* void fn_LED_Corporate(GPIO_TypeDef*  _GPIO_x , uint16_t  _GPIO_Pin_x , 
*            LED_Corporate_state_t  _LED_Corporate_state_t );
* @param  
* //Serial port 1    
*    // USART  GPIO Pin definition
*    #define  _DEBUG_USART_GPIO_CLK          RCC_APB2Periph_GPIOA
*    #define  _DEBUG_USART_GPIO_APBxCLKCmd   RCC_APB2PeriphClockCmd
*    
*    #define  _DEBUG_USART_TX_GPIO_PORT      GPIOA
*    #define  _DEBUG_USART_TX_GPIO_PIN       GPIO_Pin_9
*    #define  _DEBUG_USART_TX_GPIO_MODE      GPIO_Mode_AF_PP
*    #define  _DEBUG_USART_RX_GPIO_PORT      GPIOA
*    #define  _DEBUG_USART_RX_GPIO_PIN       GPIO_Pin_10
*    #define  _DEBUG_USART_RX_GPIO_MODE      GPIO_Mode_AF_FLOATING
* @retval 
*************************************************************/ 
void  fn_USART_IO_Config(void){
  GPIO_InitTypeDef    GPIO_InitStructure;
  // Open the clock of serial port GPIO
  _DEBUG_USART_GPIO_APBxCLKCmd(_DEBUG_USART_GPIO_CLK , ENABLE);
  
//Configure the GPIO of USART TX to push-pull mode
  GPIO_InitStructure.GPIO_Pin = _DEBUG_USART_TX_GPIO_PIN;
  GPIO_InitStructure.GPIO_Mode = _DEBUG_USART_TX_GPIO_MODE;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_Init(_DEBUG_USART_TX_GPIO_PORT,&GPIO_InitStructure);
   //Configure GPIO of USART RX as floating input
  GPIO_InitStructure.GPIO_Pin = _DEBUG_USART_RX_GPIO_PIN;
  GPIO_InitStructure.GPIO_Mode = _DEBUG_USART_RX_GPIO_MODE;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_Init(_DEBUG_USART_RX_GPIO_PORT,&GPIO_InitStructure);
}

/**************************************************************
* @brief  
* void fn_LED_Corporate(GPIO_TypeDef*  _GPIO_x , uint16_t  _GPIO_Pin_x , 
*            LED_Corporate_state_t  _LED_Corporate_state_t );
* @param  
* //Serial port 1
*    #define  _DEBUG_USARTx                  USART1
*    #define  _DEBUG_USART_CLK               RCC_APB2Periph_USART1
*    #define  _DEBUG_USART_APBxClkCmd        RCC_APB2PeriphClockCmd
*    #define  _DEBUG_USART_BAUDRATE          115200
* @retval 
*************************************************************/ 
void  fn_USART_Config(void){
  USART_InitTypeDef   USART_InitStructure;
 
  // Turn on the clock of the serial peripheral
  _DEBUG_USART_APBxClkCmd(_DEBUG_USART_CLK , ENABLE);
  
  //Configure the working parameters of the serial port
  USART_InitStructure.USART_BaudRate = _DEBUG_USART_BAUDRATE;
   //Configure baud rate
  USART_InitStructure.USART_WordLength = USART_WordLength_8b;
  // Configure pin data word length
  USART_InitStructure.USART_StopBits = USART_StopBits_1;
  // Configure stop bit
  USART_InitStructure.USART_Parity = USART_Parity_No;
  // Configure check bit
  USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
  // Configure hardware flow control
  USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx ;
  // Configure the working mode and send and receive together
  
  USART_Init(_DEBUG_USARTx , &USART_InitStructure);// Complete the initialization configuration of the serial port
  
  NVIC_Configuration();// Serial port interrupt priority configuration
  
  USART_ITConfig(_DEBUG_USARTx , USART_IT_RXNE , ENABLE);// Enable serial port to receive interrupt
  
  USART_Cmd(_DEBUG_USARTx , ENABLE);// Enable serial port
}

/**************************************************************
* @brief  
* void fn_Usart_Send_Byte(USART_TypeDef * pUSARTx , uint8_t ch );
* @param  
* //Serial port 1
*    #define  _DEBUG_USARTx                  USART1
*    #define  _DEBUG_USART_CLK               RCC_APB2Periph_USART1
*    #define  _DEBUG_USART_APBxClkCmd        RCC_APB2PeriphClockCmd
*    #define  _DEBUG_USART_BAUDRATE          115200
* @retval 
*************************************************************/ 
void fn_Usart_Send_Byte(USART_TypeDef * pUSARTx , uint8_t ch ){
  /*Send a byte of data to USART*/
  USART_SendData(pUSARTx , ch);
  /*The waiting to send data register is empty*/
  while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TXE)==RESET);
}

/**************************************************************
* @brief  
* void fn_Usart_SendString(USART_TypeDef *pUSARTx , char * str);
* @param  
* //Serial port 1
*    #define  _DEBUG_USARTx                  USART1
*    #define  _DEBUG_USART_CLK               RCC_APB2Periph_USART1
*    #define  _DEBUG_USART_APBxClkCmd        RCC_APB2PeriphClockCmd
*    #define  _DEBUG_USART_BAUDRATE          115200
* @retval 
*************************************************************/ 
void fn_Usart_SendString(USART_TypeDef *pUSARTx , char * str){
  unsigned int k = 0;
  do{
    fn_Usart_Send_Byte(pUSARTx,*(str + k++));
    
  }while(*(str + k)!='\0');
  
  /*Wait for sending to complete*/
  while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TC));
}

/**************************************************************
* @brief  
* void Usart_SendHalf_32_Word( USART_TypeDef * pUSARTx, uint32_t ch);
* @param  
* @retval 
*************************************************************/ 
void Usart_SendHalf_32_Word( USART_TypeDef * pUSARTx, uint32_t ch){
  uint32_t temp_Half32;
  uint8_t temp_Half=0,i_Half=4; 
  temp_Half32 =ch;
  while(i_Half-->0){
     temp_Half=(temp_Half32 & 0xFF000000)>>24;
     temp_Half32<<=8;
     fn_Usart_Send_Byte(pUSARTx,temp_Half);
  }
  /*Wait for sending to complete*/
  while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TC));
}

/**************************************************************
* @brief  
* void fn_USART_Init(void);
* @param  
* @retval 
*************************************************************/ 
void fn_USART_Init(void){
  fn_USART_IO_Config();
  fn_USART_Config();
}

//"Use MicroLIB" must be checked in the engineering option of MDK. MicoroLIB is an alternative library to the default C library
//The standard C library is highly optimized to make less code and occupy less resources.
/**************************************************************
* @brief  
* int fputc (int ch , FILE *f)
* @param  Redirect C library function Printf to USART1
* @retval 
*************************************************************/ 
int fputc (int ch , FILE *f){ 
  /*Send a byte of data to USART*/
  USART_SendData(_DEBUG_USARTx , (uint8_t)ch);
  /*The waiting to send data register is empty*/
  while(USART_GetFlagStatus(_DEBUG_USARTx,USART_FLAG_TXE)==RESET);
  return (ch);
}

/**************************************************************
* @brief  
* int fgetc(FILE *f);
* @param  Redirect C library function Printf to USART1
* @retval 
*************************************************************/  
int fgetc(FILE *f){
  //Wait for serial port 1 to input data
  while(USART_GetITStatus(_DEBUG_USARTx,USART_IT_RXNE)==RESET);
  return (int)USART_ReceiveData(_DEBUG_USARTx);
}

/**************************************************************
* @brief  
* void USART1_IRQHandler(void);  Interrupt service
* @param  
* @retval 
*************************************************************/ 
void _DRBUG_USART_IRQHandler(void){
  uint8_t ucTemp =  0; 
  if(USART_GetITStatus(_DEBUG_USARTx,USART_IT_RXNE)!=RESET){
    ucTemp = USART_ReceiveData(_DEBUG_USARTx);
    USART_SendData(_DEBUG_USARTx ,ucTemp );
  }
}


11. Establish DMA transfer header file_ book. h

The code is as follows:

#ifndef  __DMA_BOOK_H_
#define  __DMA_BOOK_H_

#include "stm32f10x.h"

#define   DMA_ CLOCK     RCC_ AHBPeriph_ Dma1 / / DMA clock

/******  A   ****************** ROM DMA output to RAM*******************************/
#define   Map_DMA_CHANNEL     DMA1_Channel6 / / when the memory to memory mode is used, any channel can be selected. There are no hard rules
#define   Map_ BUFFER_ Size 20 / / data size to be sent
#define   DMA_FLAG_TC   DMA1_FLAG_TC6 / / transmission completion flag
/* Define aSRC_Const_Buffer array as DMA transmission data source
* const Keyword will ASRC_ Const_ The buffer array variable is defined as a constant type
* Indicates that the data is stored in internal FLASH*/
extern  const uint32_t  aSRC_Cont_Buffer[Map_BUFFER_SIZE] ;
 /* The definition DMA transfer target memory is stored in the internal SRAM*/
extern    uint32_t aDST_Buffer[Map_BUFFER_SIZE];
/*************************************************************************************/

/******** B   **************** USART DMA output to RAM*******************************/
#define   USART_DMA_CHANNEL     DMA1_Channel4 / / the DMA request channel corresponding to the serial port 
#define   USART_ Source_ Addr (usart1_base + 0x04) / / address of serial port data
extern    uint32_t              USART_BUFFER_SIZE ;   // Size of data to send
extern    uint32_t*             USART_DMA_Buffer ;
/************************************************************************************/

void      _DMA_Config(DMA_Channel_TypeDef* _DMAy_Channelx , uint32_t _BUFFER_SIZE , uint32_t  _DMA_OutSource_ADDR,  uint32_t  _DMA_InSource_ADDR , uint32_t  _DMA_DIR);
void      _USART_DMA_Config(DMA_Channel_TypeDef* _DMAy_Channelx , uint32_t _BUFFER_SIZE , uint32_t  _DMA_OutSource_ADDR,  uint32_t  _DMA_InSource_ADDR , uint32_t  _DMA_DIR);
uint8_t   _Buffercmp(const uint32_t *pBuffer, uint32_t * pBuffer1 , uint16_t BufferLength);
void      _Buffer_Show(uint32_t * pBuffer , uint16_t BufferLength);
//DMA fetching ROM data from memory
void _DMA_ROM_TO_RAM(uint32_t _BUFFER_SIZE , uint32_t  _DMA_Source_ADDR,  uint32_t  _DMA_AIM_ADDR );
//DMA fetching RAM to USART data
void _DMA_RAM_TO_USART(uint32_t _BUFFER_SIZE , uint32_t  _DMA_Source_ADDR,  uint32_t  _DMA_AIM_ADDR );

  
#define   _Map_DMA_Config_    _DMA_Config(Map_DMA_CHANNEL ,Map_BUFFER_SIZE ,aSRC_Cont_Buffer , aDST_Buffer , DMA_DIR_PeripheralSRC)
//  The program of DMA output from ROM to RAM initializes DMA_DIR_PeripheralSRC: peripheral to memory for direction
#define   _USART_DMA_Config_   _USART_DMA_Config(USART_DMA_CHANNEL ,USART_BUFFER_SIZE ,USART_Source_ADDR , USART_DMA_Buffer , DMA_DIR_PeripheralDST)
//  The program of DMA output from ROM to RAM initializes DMA_DIR_PeripheralDST: peripherals to memory for direction
#define   _DMA_InnerChange_    _Buffercmp(aSRC_Cont_Buffer , aDST_Buffer, Map_BUFFER_SIZE)
//  Data verification of DMA output from ROM to RAM
#define   _RMA_InnerShow_      _Buffer_Show(aDST_Buffer, Map_BUFFER_SIZE)

#endif

12. Establish C file for DMA transmission_ book. c

The code is as follows:

#include "DMA_book.h"
#include "USART_book.h"
#include "Systick_book.h"

const uint32_t  aSRC_Cont_Buffer  [Map_BUFFER_SIZE]={
   'W','E','L','L',
   'C','O','M','E',
   ' ','S','T','M',
   '3','2',' ','S',
   'T','U','D','Y',
   
};
uint32_t    aDST_Buffer[Map_BUFFER_SIZE] ;
uint32_t*   USART_DMA_Buffer ;
uint32_t  USART_BUFFER_SIZE ;

void _DMA_Config(DMA_Channel_TypeDef* _DMAy_Channelx , uint32_t _BUFFER_SIZE , uint32_t  _DMA_Source_ADDR,  uint32_t  _DMA_AIM_ADDR , uint32_t  _DMA_DIR){
  DMA_InitTypeDef   DMA_InitStructure ;
  //Turn on DMA clock
  RCC_AHBPeriphClockCmd(DMA_CLOCK,ENABLE);
  //Source data cache address (peripheral address)
  DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)_DMA_Source_ADDR ; 
  //Translation cache address (memory address)
  DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)_DMA_AIM_ADDR;
  
  //Direction: peripheral to memory (peripheral here is internal FLASH)DMA_DIR_PeripheralSRC: DMA to memory for directional peripherals_ DIR_ Peripheraldst: peripherals to memory for direction
  DMA_InitStructure.DMA_DIR = _DMA_DIR ;
  //Transmission size
  DMA_InitStructure.DMA_BufferSize = _BUFFER_SIZE;
  //Peripheral (internal FLASH) address increment
  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable;
  //Memory address increment
  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
  //Peripheral data unit
  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;
  //Memory data unit
  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;
  //DMA mode, one-time or cyclic mode
  DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
  //DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
  //Priority: high
  DMA_InitStructure.DMA_Priority = DMA_Priority_High;
  //Enable memory to memory transfer
  DMA_InitStructure.DMA_M2M = DMA_M2M_Enable;
  //Configure DMA channel
  DMA_Init(_DMAy_Channelx , &DMA_InitStructure);
  //Enable DMA
  DMA_Cmd(_DMAy_Channelx , ENABLE);
}

void _USART_DMA_Config(DMA_Channel_TypeDef* _DMAy_Channelx , uint32_t _BUFFER_SIZE , uint32_t  _DMA_Source_ADDR,  uint32_t  _DMA_AIM_ADDR , uint32_t  _DMA_DIR){
  DMA_InitTypeDef   DMA_InitStructure ;
  //Turn on DMA clock
  RCC_AHBPeriphClockCmd(DMA_CLOCK,ENABLE);  
  //Source data cache address (peripheral address)
  DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)_DMA_Source_ADDR ; 
  //Translation cache address (memory address)
  DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)_DMA_AIM_ADDR;
  
  //Direction: peripheral to memory (peripheral here is internal FLASH)DMA_DIR_PeripheralSRC: DMA to memory for directional peripherals_ DIR_ Peripheraldst: peripherals to memory for direction
  DMA_InitStructure.DMA_DIR = _DMA_DIR ;
  //Transmission size
  DMA_InitStructure.DMA_BufferSize = _BUFFER_SIZE;
  //Peripheral (internal FLASH) address increment
  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
  //Memory address increment
  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
  //Peripheral data unit
  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;//DMA_PeripheralDataSize_Byte; // Note that it needs to be modified by row according to the data type
  //Memory data unit
  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;//DMA_MemoryDataSize_Byte; // Note that it needs to be modified by row according to the data type
  //DMA mode, one-time or cyclic mode
  DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
  //DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
  //Priority: high
  DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;
  //Enable memory to memory transfer
  DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
  //Configure DMA channel
  DMA_Init(_DMAy_Channelx , &DMA_InitStructure);
  //Enable DMA
  DMA_Cmd(_DMAy_Channelx , ENABLE);
}
 
///Comparison of source data and destination address data
uint8_t  _Buffercmp(const uint32_t *pBuffer, uint32_t * pBuffer1 , uint16_t BufferLength){
  /*Decreasing data length*/
  while(BufferLength--){
//  Usart_SendHalf_32_Word(_DEBUG_USARTx,*pBuffer);  
//  Usart_SendHalf_32_Word(_DEBUG_USARTx,*pBuffer1);
    /*Determine whether the two data sources are equal*/
    if(*pBuffer != *pBuffer1){
      /* If the corresponding data sources are not equal, exit the function immediately and return 0 */
      return 0;
    }
    /* Increment the address pointers of two data sources */
    pBuffer++;
    pBuffer1++;
  }
  /* The judgment is completed and the corresponding data is relative */
  return 1;
}


//Display RAM data
void  _Buffer_Show(uint32_t * pBuffer , uint16_t BufferLength){
  /*Decreasing data length*/
  while(BufferLength--){
    Usart_SendHalf_32_Word(_DEBUG_USARTx,*pBuffer++);  
  }
}

//DMA fetching ROM data from memory
void _DMA_ROM_TO_RAM(uint32_t _BUFFER_SIZE , uint32_t  _DMA_Source_ADDR,  uint32_t  _DMA_AIM_ADDR ){
  //----------------------------------------------------------------
  printf("start ROM Memory to RAM Memory DMA operation \n");
  //Memory to memory DMA initialization
  _DMA_Config(Map_DMA_CHANNEL ,_BUFFER_SIZE ,_DMA_Source_ADDR , _DMA_AIM_ADDR , DMA_DIR_PeripheralSRC);       
  while(DMA_GetFlagStatus(DMA_FLAG_TC) == RESET);  //Judge whether the DMA transmission result is correct          
  if(_DMA_InnerChange_== 0 ){
      printf("ROM Memory to DMA Abnormal operation \n");  
  }else{
      printf("ROM Memory to DMA Normal operation \n");      
  }  
  _RMA_InnerShow_;
}

//DMA fetching RAM to USART data
void _DMA_RAM_TO_USART(uint32_t _BUFFER_SIZE , uint32_t  _DMA_Source_ADDR,  uint32_t  _DMA_AIM_ADDR ){
  // Start DMA operation from USART memory to RAM memory 
  printf("\n start ROM reach USART Transfer initialization for\n"); 
  USART_BUFFER_SIZE = _BUFFER_SIZE;
  USART_DMA_Buffer = _DMA_AIM_ADDR;
  //Memory to USART DMA initialization
  _USART_DMA_Config(USART_DMA_CHANNEL ,USART_BUFFER_SIZE ,_DMA_Source_ADDR , USART_DMA_Buffer , DMA_DIR_PeripheralDST);
  USART_DMACmd(_DEBUG_USARTx , USART_DMAReq_Tx , ENABLE);  //Serial DMA enable
  /*USART_DMACmd The function is used to control the startup and shutdown of DMA requests of USART. It receives three parameters
  The first parameter is used to set serial port peripherals, which can be USART1/2/3 and UART4/5. The fifth parameter is optional
  The two parameters set the specific DMA request of the serial port, including the serial port sending request USART_DMAReq_Tx and receive request
  USART_DMAReq_Rx Optional. The third parameter is used to set ENABLE or close the start request*/
  
  fn_Systick_Delay(250,_Systick_ms);   //LED output flashes during DMA transfer  
  while(USART_GetFlagStatus(_DEBUG_USARTx,USART_FLAG_TXE)==RESET);
  printf("\rROM Memory to USART Peripheral DMA Operation completed\n");//This function takes a while to use Delay
}
 
//----------------------------------------------------------------


13. Create EXIT header file Exit_book.h

The code is as follows:

#ifndef  __EXIT_BOOK_H_
#define  __EXIT_BOOK_H_

#include "stm32f10x.h"

#define  _KEY_EXTI_IN_GPIO_Port      GPIOA
#define  _KEY_EXTI_IN_GPIO_Pin       GPIO_Pin_0
#define  _EXTI_IN_GPIO_PortSource     GPIO_PortSourceGPIOA
#define  _EXTI_IN_GPIO_PinSource      GPIO_PinSource0
#define  _EXTI_IN_EXTI_Line           EXTI_Line0 
#define  _EXTI_IN_EXTI_Trigger        EXTI_Trigger_Rising
#define  _EXTI_IN_GPIO_Clock          RCC_APB2Periph_AFIO
#define  _EXTI_IN_EXTI_Mode           EXTI_Mode_Interrupt
#define  _EXTI_IN_EXTI_LineCmd        ENABLE

#define  _NVIC_IN_EXTI_IRQChannel     EXTI0_IRQn
#define  _NVIC_IN_EXTI_IRQChannelCmd  ENABLE

#define  _KEY2_EXTI_IN_GPIO_Port     GPIOC
#define  _KEY2_EXTI_IN_GPIO_Pin      GPIO_Pin_13
#define  _EXTI_IN2_GPIO_PortSource    GPIO_PortSourceGPIOC
#define  _EXTI_IN2_GPIO_PinSource     GPIO_PinSource13
#define  _EXTI_IN2_EXTI_Line          EXTI_Line13
#define  _EXTI_IN2_EXTI_Trigger       EXTI_Trigger_Falling
#define  _EXTI_IN2_GPIO_Clock         RCC_APB2Periph_AFIO
#define  _EXTI_IN2_EXTI_Mode          EXTI_Mode_Interrupt
#define  _EXTI_IN2_EXTI_LineCmd       ENABLE

#define  _NVIC_IN2_EXTI_IRQChannel    EXTI15_10_IRQn
#define  _NVIC_IN2_EXTI_IRQChannelCmd  ENABLE

void  fn_EXTI_GPIO_Config(void);
void  fn_NVIC_Config(void);
void  EXTI0_IRQHandler(void);

#endif

14. Create EXIT C file Exit_book.c

The code is as follows:

#include "Exit_book.h"
#include "Led_book.h"

/**************************************************************
* @brief  
* void  fn_EXTI_GPIO_Config(void)
* @param  
*    
*   #define  _KEY_EXTI_IN_GPIO_Port      GPIOA
*   #define  _KEY_EXTI_IN_GPIO_Pin       GPIO_Pin_0
*   #define  _EXTI_IN_GPIO_PortSource     GPIO_PortSourceGPIOA
*   #define  _EXTI_IN_GPIO_PinSource      GPIO_PinSource0
*   #define  _EXTI_IN_EXTI_Line           EXTI_Line0 
*   #define  _EXTI_IN_EXTI_Trigger        EXTI_Trigger_Rising
*   #define  _EXTI_IN_GPIO_Clock          RCC_APB2Periph_AFIO
*   #define  _EXTI_IN_EXTI_Mode           EXTI_Mode_Interrupt
*   #define  _EXTI_IN_EXTI_LineCmd        ENABLE
*   
*   #define  _KEY2_EXTI_IN_GPIO_Port     GPIOC
*   #define  _KEY2_EXTI_IN_GPIO_Pin      GPIO_Pin_13
*   #define  _EXTI_IN2_GPIO_PortSource    GPIO_PortSourceGPIOC
*   #define  _EXTI_IN2_GPIO_PinSource     GPIO_PinSource13
*   #define  _EXTI_IN2_EXTI_Line          EXTI_Line13
*   #define  _EXTI_IN2_EXTI_Trigger       EXTI_Trigger_Falling   
*   #define  _EXTI_IN2_GPIO_Clock         RCC_APB2Periph_AFIO
*   #define  _EXTI_IN2_EXTI_Mode          EXTI_Mode_Interrupt
*   #define  _EXTI_IN2_EXTI_LineCmd       ENABLE
* @retval 
*************************************************************/ 
void  fn_EXTI_GPIO_Config(void){
  EXTI_InitTypeDef   EXIT_InitStruck;
  RCC_APB2PeriphClockCmd(_EXTI_IN_GPIO_Clock , ENABLE);  
  //Note: in addition to opening the GPIO port clock, we also opened the AFIO clock
  GPIO_EXTILineConfig(_EXTI_IN_GPIO_PortSource | _EXTI_IN2_GPIO_PortSource , _EXTI_IN_GPIO_PinSource | _EXTI_IN2_GPIO_PinSource);
  /* Select signal source of EXTI */
  // GPIO_ The exilineconfig function is used to specify the input source of the interrupt / event line. It actually sets the external interrupt configuration
  // Afio setting register_ Exicrx value. This function receives two parameters. The first parameter specifies the GPIO port source, and the second parameter specifies the GPIO port source
  // The second parameter is to select the corresponding GPIO pin source number.
  
  
  EXIT_InitStruck.EXTI_Line = _EXTI_IN_EXTI_Line ; /* Select signal source of EXTI */
  EXIT_InitStruck.EXTI_Mode = _EXTI_IN_EXTI_Mode;   /* EXTI Interrupt mode */
  EXIT_InitStruck.EXTI_Trigger = _EXTI_IN_EXTI_Trigger ; /* Rising edge interrupt */
  EXIT_InitStruck.EXTI_LineCmd = _EXTI_IN_EXTI_LineCmd; /* Enable interrupt */
  EXTI_Init(&EXIT_InitStruck);
  //  EXTI initializes the configured variables
  //  fn_NVIC_Config();
  //  Call NVIC_ The configuration function configures the priority of key 1 and key 2 and enables the channel to be interrupted
  
  EXIT_InitStruck.EXTI_Line = _EXTI_IN2_EXTI_Line; /* Select signal source of EXTI */
  EXIT_InitStruck.EXTI_Mode = _EXTI_IN2_EXTI_Mode;   /* EXTI Interrupt mode */
  EXIT_InitStruck.EXTI_Trigger = _EXTI_IN2_EXTI_Trigger; /* Falling edge interrupt */
  EXIT_InitStruck.EXTI_LineCmd = _EXTI_IN_EXTI_LineCmd;/* Enable interrupt */
  EXTI_Init(&EXIT_InitStruck);
  
  fn_NVIC_Config();
}

/**************************************************************
* @brief  
* void  fn_NVIC_Config(void)
* @param  
*   #define  _NVIC_IN_EXTI_IRQChannel     EXTI0_IRQn
*   #define  _NVIC_IN_EXTI_IRQChannelCmd  ENABLE
*   #define  _NVIC_IN2_EXTI_IRQChannel    EXTI15_10_IRQn
*   #define  _NVIC_IN2_EXTI_IRQChannelCmd  ENABLE
* @retval 
*************************************************************/ 
void  fn_NVIC_Config(void){
  NVIC_InitTypeDef NVIC_InitStruct;
  /* Configure NVIC as priority group 1 */
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
  
  /* Configure interrupt source:  */
  NVIC_InitStruct.NVIC_IRQChannel = _NVIC_IN_EXTI_IRQChannel; //EXTI0_IRQn;
  /* Configure preemption priority: 1 */
  NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;
  /* Configure sub priority: 1 */
  NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;
  /* Enable interrupt channel */
  NVIC_InitStruct.NVIC_IRQChannelCmd = _NVIC_IN_EXTI_IRQChannelCmd; //ENABLE
  NVIC_Init(&NVIC_InitStruct);
  
  /* Configure interrupt source:  */
  NVIC_InitStruct.NVIC_IRQChannel = _NVIC_IN2_EXTI_IRQChannel; //EXTI0_IRQn;
  NVIC_Init(&NVIC_InitStruct);
}

/**************************************************************
* @brief  
* void  fn_NVIC_Config(void)
* @param  
*   #define   _KEY_EXTI_IN_GPIO_Port      GPIOA
*   #define   _KEY_EXTI_IN_GPIO_Pin       GPIO_Pin_0
* @retval 
*************************************************************/ 
void EXTI0_IRQHandler(void){
//  EXTI_ The getitstatus function is used to obtain the interrupt flag bit status of exti. If an interrupt occurs on the exti line
//The number returns "SET", otherwise it returns "RESET". In fact, exti_ The getitstatus function reads
//EXTI_PR register value to judge the status of the exti line.
  if(EXTI_GetITStatus(_EXTI_IN_EXTI_Line)!= RESET){
    if(GPIO_ReadInputDataBit(_KEY_EXTI_IN_GPIO_Port, _KEY_EXTI_IN_GPIO_Pin)==1){
      __LED_Change__;
    }
  }
  EXTI_ClearITPendingBit(_EXTI_IN_EXTI_Line);  // Important clear interrupt flag bit 
}


void EXTI15_10_IRQHandler(void){
 if(EXTI_GetITStatus(_EXTI_IN2_EXTI_Line)!= RESET){
    if(GPIO_ReadInputDataBit(_KEY2_EXTI_IN_GPIO_Port, _KEY2_EXTI_IN_GPIO_Pin)==0){
      __LED_Change__;
    }
  }
  EXTI_ClearITPendingBit(_EXTI_IN2_EXTI_Line);  // Important clear interrupt flag bit 
}
 

15. Create the header file Key for Key transfer_ book. h

The code is as follows:

#ifndef  __KEY_BOOK_H_
#define  __KEY_BOOK_H_


#include "stm32f10x.h"
#include "Led_book.h"

#define   KEY_IN_GPIO_Port      GPIOA
#define   KEY_IN_GPIO_Clock     RCC_APB2Periph_GPIOA
#define   KEY_IN_GPIO_Pin       GPIO_Pin_0
#define   KEY_IN_GPIO_Pin_Bit   0
#define   Key_IN_GPIO_Modle     GPIO_Mode_IN_FLOATING / / floating input


#define   KEY2_IN_GPIO_Port      GPIOC
#define   KEY2_IN_GPIO_Clock     RCC_APB2Periph_GPIOC
#define   KEY2_IN_GPIO_Pin       GPIO_Pin_13
#define   KEY2_IN_GPIO_Pin_Bit   13
#define   Key2_IN_GPIO_Modle     GPIO_Mode_IN_FLOATING / / floating input


typedef union {
  struct{
    unsigned char BIT0:1;unsigned char BIT1:1;unsigned char BIT2:1;unsigned char BIT3:1;
    unsigned char BIT4:1;unsigned char BIT5:1;unsigned char BIT6:1;unsigned char BIT7:1;
    //unsigned char BIT8:1;unsigned char BIT9:1;unsigned char BIT10:1;unsigned char BIT11:1;
    //unsigned char BIT12:1;unsigned char BIT13:1;unsigned char BIT14:1;unsigned char BIT15:1;
  }DATA_BIT;
  uint8_t DATA_BYTE;
}Per_key_type;

extern volatile  Per_key_type key_flag;
  #define bkey_10ms         key_flag.DATA_BIT.BIT0
  #define bkey_judge        key_flag.DATA_BIT.BIT1
  #define bkey_judge_long   key_flag.DATA_BIT.BIT2
  #define bkey_Effect       key_flag.DATA_BIT.BIT3
  #define bkey_LongEffect   key_flag.DATA_BIT.BIT4
  #define bkey_Effect_Lose  key_flag.DATA_BIT.BIT5
  #define bkey_Effect_LLose key_flag.DATA_BIT.BIT6
void  fn_Key_GPIO_Config( GPIO_TypeDef* _GPIO_x , uint32_t _GPIO_Clock , uint16_t _GPIO_Pin_x , GPIOMode_TypeDef  _GPIOMode_TypeDef );
void  fn_Key_Init(void);
void  fn_key_judge(void);
void  fn_key_Effect(void);  
void  fn_key_Check(void);
#endif

16. Create the C file Key of the Key_book.c

The code is as follows:

#include  "Key_book.h"
 

volatile  Per_key_type key_flag;

/**************************************************************
* @brief  
* void  fn_Key_GPIO_Config( GPIO_TypeDef* _GPIO_x , uint32_t _GPIO_Clock , 
*                  uint16_t _GPIO_Pin_x , GPIOMode_TypeDef  _GPIOMode_TypeDef );
* @param  
*     #define   KEY_IN_GPIO_Port      GPIOA
*     #define   KEY_IN_GPIO_Clock     RCC_APB2Periph_GPIOA
*     #define   KEY_IN_GPIO_Pin       GPIO_Pin_0
*     #define   KEY_IN_GPIO_Pin_Bit   0
*     #define   Key_IN_GPIO_Modle     GPIO_Mode_IN_FLOATING   //Floating input
*     
*     #define   KEY2_IN_GPIO_Port      GPIOC
*     #define   KEY2_IN_GPIO_Clock     RCC_APB2Periph_GPIOC
*     #define   KEY2_IN_GPIO_Pin       GPIO_Pin_13
*     #define   KEY2_IN_GPIO_Pin_Bit   13
*     #define   Key2_IN_GPIO_Modle     GPIO_Mode_IN_FLOATING   //Floating input
* @retval 
*************************************************************/ 
void  fn_Key_GPIO_Config( GPIO_TypeDef* _GPIO_x , uint32_t _GPIO_Clock , uint16_t _GPIO_Pin_x , GPIOMode_TypeDef  _GPIOMode_TypeDef ){
    GPIO_InitTypeDef  GPIO_InitStruct;
    GPIO_InitStruct.GPIO_Mode = _GPIOMode_TypeDef;
    GPIO_InitStruct.GPIO_Pin = _GPIO_Pin_x;
    RCC_APB2PeriphClockCmd(_GPIO_Clock,ENABLE);
    GPIO_Init(_GPIO_x , &GPIO_InitStruct);  
}

/**************************************************************
* @brief  
* void fn_Key_Init(void);
* @param  
* @retval 
*************************************************************/ 
void  fn_Key_Init(void){
    fn_Key_GPIO_Config(KEY_IN_GPIO_Port,KEY_IN_GPIO_Clock,KEY_IN_GPIO_Pin,Key_IN_GPIO_Modle);
    fn_Key_GPIO_Config(KEY2_IN_GPIO_Port,KEY2_IN_GPIO_Clock,KEY2_IN_GPIO_Pin,Key2_IN_GPIO_Modle);
}

/************************************************************
* @brief  
* void  fn_key_judge(void);
* @param  
* @retval 
**************************************************************/ 
#define  _LONG_key  30
static uint16_t count_key ;
void  fn_key_judge(void){
   
   if(!bkey_10ms){return;}
   bkey_10ms = 0;
   if(GPIO_ReadInputDataBit(KEY_IN_GPIO_Port,KEY_IN_GPIO_Pin)){
     if(count_key++<3){return;}
     if(!bkey_judge){
       bkey_judge = 1;
       bkey_Effect = 1; 
     }else{
       if(count_key>_LONG_key){
          bkey_judge_long = 1;
          bkey_LongEffect = 1;
       }
     }
   }
   else{
     count_key = 0;
     if(bkey_judge){
        bkey_judge = 0;
        if(bkey_judge_long){
            bkey_judge_long = 0;
            bkey_Effect_LLose = 1;
        }else{
            bkey_judge_long = 0;
            bkey_Effect_Lose = 1;
        }
     }else{
        bkey_judge = 0;         
     }
  }
}

/************************************************************
* @brief  
* void fn_key_Effect(void);
* @param  
* @retval 
*************************************************************/ 
void  fn_key_Effect(void){
  if(bkey_Effect){
    bkey_Effect = 0;
    fn_LED_Corporate(LED_OUT_GPIO_Port,LED_OUT_GPIO_Pin,LED_Corporate_Toggle);
  }
}

/**************************************************************
* @brief  
* void fn_key_Check(void);
* @param  
* @retval 
*************************************************************/ 
void fn_key_Check(void){
  fn_key_judge();
  fn_key_Effect();
}


17. Create led header file Led_book.h

The code is as follows:

#ifndef  __LED_BOOK_H_
#define  __LED_BOOK_H_

#include "stm32f10x.h"
 

#define   LED_OUT_GPIO_Port     GPIOB                 //GPIO Point
#define   LED_OUT_GPIO_Clock    RCC_APB2Periph_GPIOB  //GPIO clock
#define   LED_OUT_GPIO_Pin      GPIO_Pin_5             
#define   LED_OUT_GPIO_Pin_Bit  5
#define   LED_OUT_GPIO_Modle    GPIO_Mode_Out_PP

#define   LED_R_OUT_GPIO_Pin      GPIO_Pin_5             
#define   LED_G_OUT_GPIO_Pin     GPIO_Pin_0             
#define   LED_B_OUT_GPIO_Pin     GPIO_Pin_1             
typedef enum {
		LED_Corporate_On = 1,
		LED_Corporate_OFF = 2,
		LED_Corporate_Toggle = 3, 
} LED_Corporate_state_t;

void fn_LED_GPIO_Config(GPIO_TypeDef* _GPIO_x , uint32_t _GPIO_Clock ,\
          uint16_t _GPIO_Pin_x , GPIOMode_TypeDef _GPIOMode_TypeDef);
void fn_Led_Init(void);
void fn_LED_Corporate(GPIO_TypeDef* _GPIO_x , uint16_t _GPIO_Pin_x , \
          LED_Corporate_state_t _LED_Corporate_state_t );
  
void  fn_LED_ALL_OFF(void);
#define __LED_Change__  fn_LED_Corporate(LED_OUT_GPIO_Port,LED_OUT_GPIO_Pin,LED_Corporate_Toggle)

#define __R_OUT__  GPIO_ResetBits(LED_OUT_GPIO_Port,LED_R_OUT_GPIO_Pin)
#define __G_OUT__  GPIO_ResetBits(LED_OUT_GPIO_Port,LED_G_OUT_GPIO_Pin)
#define __B_OUT__  GPIO_ResetBits(LED_OUT_GPIO_Port,LED_B_OUT_GPIO_Pin)

#endif


18. Establish led file Led_book.c

The code is as follows:

#include "Led_book.h"

/**************************************************************
* @brief  
* void fn_LED_GPIO_Config(GPIO_TypeDef* _GPIO_x , uint32_t _GPIO_Clock ,
*             uint16_t _GPIO_Pin_x , GPIOMode_TypeDef _GPIOMode_TypeDef);
* @param  
* @retval 
*************************************************************/ 
#define LED_GPIO_Speed GPIO_Speed_10MHz 
void fn_LED_GPIO_Config(GPIO_TypeDef* _GPIO_x , uint32_t _GPIO_Clock ,uint16_t _GPIO_Pin_x , GPIOMode_TypeDef _GPIOMode_TypeDef){
  GPIO_InitTypeDef  GPIO_InitStruct;
  GPIO_InitStruct.GPIO_Mode = _GPIOMode_TypeDef;
  GPIO_InitStruct.GPIO_Pin = _GPIO_Pin_x;
  GPIO_InitStruct.GPIO_Speed = LED_GPIO_Speed;
  RCC_APB2PeriphClockCmd(_GPIO_Clock ,ENABLE); 
  GPIO_Init(_GPIO_x , &GPIO_InitStruct) ; 
  GPIO_SetBits(_GPIO_x,_GPIO_Pin_x);
}

/**************************************************************
* @brief  
* void fn_Led_Init(void);
* @param  
* @retval 
*************************************************************/ 
void fn_Led_Init(void){
  fn_LED_GPIO_Config (LED_OUT_GPIO_Port,LED_OUT_GPIO_Clock,LED_OUT_GPIO_Pin,LED_OUT_GPIO_Modle);
  fn_LED_GPIO_Config (LED_OUT_GPIO_Port,LED_OUT_GPIO_Clock,LED_R_OUT_GPIO_Pin,LED_OUT_GPIO_Modle);
  fn_LED_GPIO_Config (LED_OUT_GPIO_Port,LED_OUT_GPIO_Clock,LED_G_OUT_GPIO_Pin,LED_OUT_GPIO_Modle);
  fn_LED_GPIO_Config (LED_OUT_GPIO_Port,LED_OUT_GPIO_Clock,LED_B_OUT_GPIO_Pin,LED_OUT_GPIO_Modle);
  fn_LED_ALL_OFF();
}

/**************************************************************
* @brief  
* void fn_LED_Corporate(GPIO_TypeDef*  _GPIO_x , uint16_t  _GPIO_Pin_x , 
*            LED_Corporate_state_t  _LED_Corporate_state_t );
* @param  
* @retval 
*************************************************************/ 
void fn_LED_Corporate(GPIO_TypeDef*  _GPIO_x , uint16_t  _GPIO_Pin_x , LED_Corporate_state_t  _LED_Corporate_state_t ){
  switch(_LED_Corporate_state_t){
    case  LED_Corporate_On :
      GPIO_SetBits(_GPIO_x,_GPIO_Pin_x);
      break;
		case  LED_Corporate_OFF:
      GPIO_ResetBits(_GPIO_x,_GPIO_Pin_x);
      break;
		case  LED_Corporate_Toggle:
      GPIO_ReadOutputDataBit(_GPIO_x,_GPIO_Pin_x)?GPIO_ResetBits(_GPIO_x,_GPIO_Pin_x):GPIO_SetBits(_GPIO_x,_GPIO_Pin_x);
      break;    
  }
}

void  fn_LED_ALL_OFF(void){
  GPIO_SetBits(LED_OUT_GPIO_Port,LED_R_OUT_GPIO_Pin);
  GPIO_SetBits(LED_OUT_GPIO_Port,LED_G_OUT_GPIO_Pin);
  GPIO_SetBits(LED_OUT_GPIO_Port,LED_B_OUT_GPIO_Pin);
}

//practice
//fn_LED_GPIO_Config (LED_OUT_GPIO_Port,LED_OUT_GPIO_Clock,LED_OUT_GPIO_Pin,LED_OUT_GPIO_Modle);
// while(1){
//  delay(10000);
//  fn_LED_Corporate(LED_OUT_GPIO_Port,LED_OUT_GPIO_Pin,LED_Corporate_Toggle);		 
// }	 


19. Create a header file for Systick transmission_ book. h

The code is as follows:

#ifndef  __SYSTIC_BOOK_H_
#define  __SYSTIC_BOOK_H_

#include "stm32f10x.h"
#include  "Key_book.h"
 
typedef enum {
		_Systick_us = 1,
		_Systick_ms = 2,
		_Systick_s = 3, 
} Systick_time_state_t;

void fn_Systick_Delay(uint32_t  _Delay_time , Systick_time_state_t  _Systick_time_state_t);
void fn_Systick_Delay_Handler_set(uint32_t  _Delay_ms , Systick_time_state_t  _Systick_time_state_t);
void fn_SysTick_delay_decrement(void);
void SysTick_Handler(void);


#define  __Systick_Delay_Handler_set__      fn_Systick_Delay_Handler_set(10,_Systick_ms)
#endif


20. Establish Systick's C file Systick_book.c

The code is as follows:

#include "Systick_book.h"

/************************************************************
* @brief  
* void fn_Systick_Delay(uint32_t  _Delay_time , \
Systick_time_state_t  _Systick_time_state_t){
* @param  
* @retval 
*************************************************************/ 
void fn_Systick_Delay(uint32_t  _Delay_time , Systick_time_state_t  _Systick_time_state_t){
   uint32_t  i;
   if(_Systick_time_state_t == _Systick_us){SysTick_Config(SystemCoreClock/1000000);}
   if(_Systick_time_state_t == _Systick_ms){
    SysTick_Config(SystemCoreClock/1000);
   }  
   else{SysTick_Config(SystemCoreClock);}      
   for( i=0;i<_Delay_time ; i++){
    while(!((SysTick->CTRL)&(1<<16)));
   }
   SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;

}

/************************************************************
* @brief  
* void fn_Systick_Delay_Handler_set(uint32_t  _Delay_ms , \
*       Systick_time_state_t  _Systick_time_state_t){
* @param  
* @retval 
*************************************************************/ 
static uint32_t _SysTick_delay  = 0 ;
void fn_Systick_Delay_Handler_set(uint32_t  _Delay_ms , Systick_time_state_t  _Systick_time_state_t){
  if(_Systick_time_state_t == _Systick_us){SysTick_Config(SystemCoreClock/1000000);}
  if(_Systick_time_state_t == _Systick_ms){
      SysTick_Config(SystemCoreClock/1000);
  }  
  else{SysTick_Config(SystemCoreClock);}      
  _SysTick_delay = _Delay_ms ;
}

/************************************************************
* @brief  
* void fn_SysTick_delay_decrement(void)
* @param  
* @retval 
*************************************************************/ 
static uint32_t SysTick_delay = 0 ;
void fn_SysTick_delay_decrement(void){
  if(SysTick_delay++ > _SysTick_delay){
    SysTick_delay = 0;
    bkey_10ms = 1;
  }
}

/************************************************************
* @brief  
* void SysTick_Handler(void)
* @param  
* @retval 
*************************************************************/ 
void SysTick_Handler(void){
  fn_SysTick_delay_decrement();
}

21. Create header file function header file PROJ_book.h

The code is as follows:

#ifndef __PROJ_BOOK_H__
#define __PROJ_BOOK_H__
 
#include "stm32f10x.h"
#include "Led_book.h"
#include "Key_book.h"	 
#include "RCC_book.h"
#include "Systick_book.h"
#include "Exit_book.h"
#include "USART_book.h"
#include "DMA_book.h"
#include "I2C_book.h"
#include "I2C_soft_book.h"

#endif

Keywords: C stm32

Added by Adarmo on Mon, 27 Dec 2021 23:32:20 +0200