2021-08-13 TM32F103 SRAM 内存扩展管理

SRAM
本文展示了STM32 SRAM 内存扩展管理
内容涉及 :
SRAM 内存扩展管理
FatFs 文件系统移植
SPI函数移植过程
SPI字节数据模拟输出独写 缓存读写
USART串口的识别
IO口输入输出
按键的外部中断处理
32位数据通讯,字符串通讯,单字符通讯

完整代码 : Git源码下载

2021-08-13 TM32F103 SRAM 内存扩展管理_第1张图片

文章目录

  • 前言
  • 一、 编程要点
  • 二、使用步骤
    • --理解原理图
    • (注意)Keil 配置状态
    • 建立主程序 main.c
    • 建立 SRAM 内存管理文件SRAM_book.h
    • 建立 SRAM 内存管理文件SRAM_book.c
    • 整改 系统文件diskio.c
    • 修改头文件文件diskio.h
    • 建立FatFs传输的 头文件 FatFs_book.h
    • 建立FatFs传输的 头文件 FatFs_book.c
    • 建立SPI传输的 头文件 SPI_book.h
    • 建立SPI传输的 头文件 SPI_book.c
    • 建立I2C模拟传输的 头文件 I2C_soft_book.h
    • 建立I2C模拟传输的 头文件 I2C_soft_book.c
    • 建立I2C硬件传输的 头文件 I2C_book.h
    • 建立I2C硬件传输的 头文件 I2C_book.c
    • 建立USART传输的 头文件 USART_book.h
    • 建立USART传输的C文件 USART_book.c
    • 建立DMA传输的 头文件 DMA_book.h
    • 建立DMA传输的C文件 DMA_book.c
    • 建立EXIT的 头文件 Exit_book.h
    • 建立EXIT的C文件 Exit_book.c
    • 建立Key传输的 头文件 Key_book.h
    • 建立Key的C文件 Key_book.c
    • 建立LED 的头文件 Led_book.h
    • 建立LED 的 文件 Led_book.c
    • 建立 Systick传输的 头文件 Systick_book.h
    • 建立 Systick的C文件 Systick_book.c
    • 建立 头文件函数 头文件 PROJ_book.h


前言

STM32F1 系列芯片使用 FSMC 外设来管理扩展的存储器,FSMC 是Flexible StaticMemory Controller 的缩写,译为灵活的静态存储控制器。它可以用于驱动包括 SRAM、NOR FLASH 以及 NAND FLSAH 类型的存储器,不能驱动如 SDRAM 这种动态的存储器而在 STM32F429 系列的控制器中,它具有 FMC 外设,支持控制 SDRAM 存储器。

2021-08-13 TM32F103 SRAM 内存扩展管理_第2张图片
2021-08-13 TM32F103 SRAM 内存扩展管理_第3张图片

在框图的右侧是 FSMC 外设相关的控制引脚,由于控制不同类型存储器的时候会有一些不同的引脚,看起来有非常多,其中地址线 FSMC_A 和数据线 FSMC_D 是所有控制器都共用的。这些 FSMC 引脚具体对应的 GPIO 端口及引脚号可在《STM32F103 规格书》中搜索查找到,不在此列出。针对本示例中的 SRAM 控制器,我们整理出以下的 FSMC 与SRAM 引脚对照表

2021-08-13 TM32F103 SRAM 内存扩展管理_第4张图片
2021-08-13 TM32F103 SRAM 内存扩展管理_第5张图片

图中左侧的是 Cortex-M3 内核的存储空间分配,右侧是 STM32 FSMC 外设的地址映射。可以看到 FSMC 的 NOR/PSRAM/SRAM/NAND FLASH 以及 PC 卡的地址都在 External RAM 地址空间内。正是因为存在这样的地址映射,使得访问 FSMC 控制的存储器时,就跟访问 STM32 的片上外设寄存器一样(片上外设的地址映射即图中左侧的“Peripheral”区域)。

FSMC 把整个 External RAM 存储区域分成了 4 个 Bank 区域,并分配了地址范围及适用的存储器类型,如 NOR 及 SRAM 存储器只能使用 Bank1 的地址。 在每个 Bank 的内部又分成了 4 个小块,每个小块有相应的控制引脚用于连接片选信号,如 FSMC_NE[4:1]信号线可用于选择 BANK1 内部的 4 小块地址区域,见图 27-8,当 STM32 访问 0x68000000-0x6BFFFFFF 地址空间时,会访问到 Bank1 的第 3 小块区域,相应的 FSMC_NE3 信号线会输出控制信号
2021-08-13 TM32F103 SRAM 内存扩展管理_第6张图片
2021-08-13 TM32F103 SRAM 内存扩展管理_第7张图片

当内核发出访问某个指向外部存储器地址时,FSMC 外设会根据配置控制信号线产生时序访问存储器,上图中的是访问外部 SRAM 时 FSMC 外设的读写时序。
以读时序为例,该图表示一个存储器操作周期由地址建立周期(ADDSET)、数据建立周期(DATAST)以及 2 个 HCLK 周期组成。在地址建立周期中,地址线发出要访问的地址,数据掩码信号线指示出要读取地址的高、低字节部分,片选信号使能存储器芯片;地址建立周期结束后读使能信号线发出读使能信号,接着存储器通过数据信号线把目标数据传输给 FSMC,FSMC 把它交给内核。
写时序类似,区别是它的一个存储器操作周期仅由地址建立周期(ADDSET)和数据建立周期(DATAST)组成,且在数据建立周期期间写使能信号线发出写信号,接着 FSMC 把数据通过数据线传输到存储器中。程,主要使用数据线向存储器传输目标数据

一、 编程要点

(1) 初始化通讯使用的目标引脚及端口时钟
(2) 使能 FSMC 外设的时钟;
(3) 配置 FSMC SRAM 的时序、工作模式
(4) 建立机制访问外部 SRAM 存储器
(5) 编写测试程序,对读写数据进行校验

2021-08-13 TM32F103 SRAM 内存扩展管理_第8张图片

二、使用步骤

–理解原理图

2021-08-13 TM32F103 SRAM 内存扩展管理_第9张图片

(注意)Keil 配置状态

我的博客这里有项目配置 设计;
点击链接
(https://blog.csdn.net/u012651389/article/details/119189949)

建立主程序 main.c

代码如下 :

/**
  ******************************************************************************
  * @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);
void fn_FatFs_Document_Init(void);
void fn_SRAM_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 倍频
      fn_Led_Init();              //LED 输出初始化
      fn_Key_Init();              //按键 输入初始化
      fn_USART_Init();            //串口输出初始化
      printf("\n\n\r\n********** IC系统开始运行 **********\r\n");
      fn_LED_Flash_Init();        //RGB 输出测试
      fn_usart_show_Init();       //串口输出测试
      fn_EXTI_GPIO_Config();      //外部中断入口
      fn_DMA_show_Init();         //初始化DMA数据链路
      fn_I2C_EE_Init();           //初始化硬件I2C数据链路
      fn_I2C_EE_Soft_Init();      //初始化软件I2C数据链路
      fn_SPI_FLASH_Soft_Init();   //SPI测试通讯
      fn_FatFs_Document_Init();   //FatFs文件读取测试 
  
      fn_SRAM_Init();             //SRAM文件读取测试 
       
           
      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开始运行 \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("-->串口通信指测试完毕 \n");
  fn_Usart_SendString(_DEBUG_USARTx," : wangqi \n");
}
//======================================================================
//======================================================================
void fn_DMA_show_Init(void){
  printf("\n ---> DMA开始运行 \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运行完毕 \n");
}
//======================================================================
//======================================================================
void fn_I2C_EE_Init(void){
  printf("\n-->I2C_函数写入开始 \n");
  _I2C_EE_Init();
  I2C_Write_fun(writeData ,EEP_Firstpage ,_I2C_BufferSize);
  I2C_Read_fun(ReadData ,EEP_Firstpage ,_I2C_BufferSize);
  printf("--->I2C_函数写入完毕\n\r");
}
//======================================================================
//======================================================================
void fn_I2C_EE_Soft_Init(void){
  printf("\n-->I2C_软件函数写入开始 \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_软件函数写入完毕\n\r");
}
//======================================================================
//======================================================================
void fn_SPI_FLASH_Soft_Init(void){
  uint16_t i,FlashID;
  printf("-->SPI通信指测试开始 \n");
  SPI_FLASH_Init(); 
  FlashID = SPI_Read_ID() ;
  if(FlashID == _SPI_FLASH_ID){
    printf("-->SPI  0x%x \n",FlashID);
  }
  
  SPI_Erase_Sector(0); //清除一个页的空间 
  printf("\n\n-->SPI清空开始 \n");
  SPI_Read_Data(Read_SPI_Data , 0, SPI_PAGE_SIZE);
  SPI_Show_Data(Read_SPI_Data , SPI_PAGE_SIZE);
  printf("\n\n-->SPI清空完成 \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输入完成 \n");
  SPI_Read_Data(Read_SPI_Data , 0x000000, _SPI_BufferSize);
  SPI_Show_Data(Read_SPI_Data , _SPI_BufferSize);
  
  printf("-->SPI通信指测试完毕 \n");
}
//======================================================================
//======================================================================
void fn_FatFs_Document_Init(void){
  /*----------------------- 格式化测试 -----------------*/ 
  FatFs_equipment_flash("1:");
  /*----------------------- 文件系统读写测试 -------------------*/
  FatFs_document_Text(FileTest_ADDR_Buffer_Document, "Wangqi加油!\n" );
   /*----------------------- 文件具体内容识别 -------------------*/
  file_check(FileTest_ADDR_Buffer_Document);
  /*----------------------- 文件目录更改测试 -------------------*/
  FatFs_document_set_content(FileTest_ADDR_Buffer_Document , FileTest_ADDR_Buffer_path , FileTest_ADDR_Buffer_NewDocument2);
  /*----------------------- 文件具体内容识别 -------------------*/
  file_check(FileTest_ADDR_Buffer_NewDocument2);
  /*----------------------- 文件目录查找识别 -------------------*/
  Scan_file_Check("1:");
  /*----------------------- 完成操作 -------------------*/
  FatFs_Close_flash("1:");
}
//======================================================================
//======================================================================

void fn_SRAM_Init(void){             //SRAM文件读取测试
  printf("********** SRAM_扩展内存测试 ********** \n");  
  printf("-->外部SRAM_内存函数初始化开始 \n");
  SRAM_Init();
  printf("\n-->外部SRAM_内存函数测试开始 \n");
  SRAM_Test();
  printf("\n-->外部SRAM_内存函数写入开始 \n");
  SRAM_Write();
  
}

//======================================================================
//======================================================================
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****/

建立 SRAM 内存管理文件SRAM_book.h

代码如下 :

#ifndef  __SRAM_BOOK_H_
#define  __SRAM_BOOK_H_

#include "stm32f10x.h"

//定义基础地址 因为我们的芯片是1M字节的
#define SRAM_BASE_ADDR  (0x68000000)
#define SRAM_CHIP       (1) //1M 
#define SRAM_SIZE       (SRAM_CHIP*1024*1024) 
#define SRAM_END_ADDR   (SRAM_BASE_ADDR + SRAM_SIZE) 
  
/*A地址信号线*/    
#define FSMC_A0_GPIO_PORT        GPIOF
#define FSMC_A0_GPIO_CLK         RCC_APB2Periph_GPIOF
#define FSMC_A0_GPIO_PIN         GPIO_Pin_0

#define FSMC_A1_GPIO_PORT        GPIOF
#define FSMC_A1_GPIO_CLK         RCC_APB2Periph_GPIOF
#define FSMC_A1_GPIO_PIN         GPIO_Pin_1

#define FSMC_A2_GPIO_PORT        GPIOF
#define FSMC_A2_GPIO_CLK         RCC_APB2Periph_GPIOF
#define FSMC_A2_GPIO_PIN         GPIO_Pin_2

#define FSMC_A3_GPIO_PORT        GPIOF
#define FSMC_A3_GPIO_CLK         RCC_APB2Periph_GPIOF
#define FSMC_A3_GPIO_PIN         GPIO_Pin_3

#define FSMC_A4_GPIO_PORT        GPIOF
#define FSMC_A4_GPIO_CLK         RCC_APB2Periph_GPIOF
#define FSMC_A4_GPIO_PIN         GPIO_Pin_4

#define FSMC_A5_GPIO_PORT        GPIOF
#define FSMC_A5_GPIO_CLK         RCC_APB2Periph_GPIOF
#define FSMC_A5_GPIO_PIN         GPIO_Pin_5

#define FSMC_A6_GPIO_PORT        GPIOF
#define FSMC_A6_GPIO_CLK         RCC_APB2Periph_GPIOF
#define FSMC_A6_GPIO_PIN         GPIO_Pin_12

#define FSMC_A7_GPIO_PORT        GPIOF
#define FSMC_A7_GPIO_CLK         RCC_APB2Periph_GPIOF
#define FSMC_A7_GPIO_PIN         GPIO_Pin_13

#define FSMC_A8_GPIO_PORT        GPIOF
#define FSMC_A8_GPIO_CLK         RCC_APB2Periph_GPIOF
#define FSMC_A8_GPIO_PIN         GPIO_Pin_14

#define FSMC_A9_GPIO_PORT        GPIOF
#define FSMC_A9_GPIO_CLK         RCC_APB2Periph_GPIOF
#define FSMC_A9_GPIO_PIN         GPIO_Pin_15

#define FSMC_A10_GPIO_PORT        GPIOG
#define FSMC_A10_GPIO_CLK         RCC_APB2Periph_GPIOG
#define FSMC_A10_GPIO_PIN         GPIO_Pin_0

#define FSMC_A11_GPIO_PORT        GPIOG
#define FSMC_A11_GPIO_CLK         RCC_APB2Periph_GPIOG
#define FSMC_A11_GPIO_PIN         GPIO_Pin_1

#define FSMC_A12_GPIO_PORT        GPIOG
#define FSMC_A12_GPIO_CLK         RCC_APB2Periph_GPIOG
#define FSMC_A12_GPIO_PIN         GPIO_Pin_2

#define FSMC_A13_GPIO_PORT        GPIOG
#define FSMC_A13_GPIO_CLK         RCC_APB2Periph_GPIOG
#define FSMC_A13_GPIO_PIN         GPIO_Pin_3

#define FSMC_A14_GPIO_PORT        GPIOG
#define FSMC_A14_GPIO_CLK         RCC_APB2Periph_GPIOG
#define FSMC_A14_GPIO_PIN         GPIO_Pin_4

#define FSMC_A15_GPIO_PORT        GPIOG
#define FSMC_A15_GPIO_CLK         RCC_APB2Periph_GPIOG
#define FSMC_A15_GPIO_PIN         GPIO_Pin_5

#define FSMC_A16_GPIO_PORT        GPIOD
#define FSMC_A16_GPIO_CLK         RCC_APB2Periph_GPIOD
#define FSMC_A16_GPIO_PIN         GPIO_Pin_11

#define FSMC_A17_GPIO_PORT        GPIOD
#define FSMC_A17_GPIO_CLK         RCC_APB2Periph_GPIOD
#define FSMC_A17_GPIO_PIN         GPIO_Pin_12

#define FSMC_A18_GPIO_PORT        GPIOD
#define FSMC_A18_GPIO_CLK         RCC_APB2Periph_GPIOD
#define FSMC_A18_GPIO_PIN         GPIO_Pin_13

/*D 数据信号线*/
#define FSMC_D0_GPIO_PORT        GPIOD
#define FSMC_D0_GPIO_CLK         RCC_APB2Periph_GPIOD
#define FSMC_D0_GPIO_PIN         GPIO_Pin_14

#define FSMC_D1_GPIO_PORT        GPIOD
#define FSMC_D1_GPIO_CLK         RCC_APB2Periph_GPIOD
#define FSMC_D1_GPIO_PIN         GPIO_Pin_15

#define FSMC_D2_GPIO_PORT        GPIOD
#define FSMC_D2_GPIO_CLK         RCC_APB2Periph_GPIOD
#define FSMC_D2_GPIO_PIN         GPIO_Pin_0

#define FSMC_D3_GPIO_PORT        GPIOD
#define FSMC_D3_GPIO_CLK         RCC_APB2Periph_GPIOD
#define FSMC_D3_GPIO_PIN         GPIO_Pin_1

#define FSMC_D4_GPIO_PORT        GPIOE
#define FSMC_D4_GPIO_CLK         RCC_APB2Periph_GPIOE
#define FSMC_D4_GPIO_PIN         GPIO_Pin_7

#define FSMC_D5_GPIO_PORT        GPIOE
#define FSMC_D5_GPIO_CLK         RCC_APB2Periph_GPIOE
#define FSMC_D5_GPIO_PIN         GPIO_Pin_8

#define FSMC_D6_GPIO_PORT        GPIOE
#define FSMC_D6_GPIO_CLK         RCC_APB2Periph_GPIOE
#define FSMC_D6_GPIO_PIN         GPIO_Pin_9

#define FSMC_D7_GPIO_PORT        GPIOE
#define FSMC_D7_GPIO_CLK         RCC_APB2Periph_GPIOE
#define FSMC_D7_GPIO_PIN         GPIO_Pin_10

#define FSMC_D8_GPIO_PORT        GPIOE
#define FSMC_D8_GPIO_CLK         RCC_APB2Periph_GPIOE
#define FSMC_D8_GPIO_PIN         GPIO_Pin_11

#define FSMC_D9_GPIO_PORT        GPIOE
#define FSMC_D9_GPIO_CLK         RCC_APB2Periph_GPIOE
#define FSMC_D9_GPIO_PIN         GPIO_Pin_12

#define FSMC_D10_GPIO_PORT        GPIOE
#define FSMC_D10_GPIO_CLK         RCC_APB2Periph_GPIOE
#define FSMC_D10_GPIO_PIN         GPIO_Pin_13

#define FSMC_D11_GPIO_PORT        GPIOE
#define FSMC_D11_GPIO_CLK         RCC_APB2Periph_GPIOE
#define FSMC_D11_GPIO_PIN         GPIO_Pin_14

#define FSMC_D12_GPIO_PORT        GPIOE
#define FSMC_D12_GPIO_CLK         RCC_APB2Periph_GPIOE
#define FSMC_D12_GPIO_PIN         GPIO_Pin_15

#define FSMC_D13_GPIO_PORT        GPIOD
#define FSMC_D13_GPIO_CLK         RCC_APB2Periph_GPIOD
#define FSMC_D13_GPIO_PIN         GPIO_Pin_8

#define FSMC_D14_GPIO_PORT        GPIOD
#define FSMC_D14_GPIO_CLK         RCC_APB2Periph_GPIOD
#define FSMC_D14_GPIO_PIN         GPIO_Pin_9

#define FSMC_D15_GPIO_PORT        GPIOD
#define FSMC_D15_GPIO_CLK         RCC_APB2Periph_GPIOD
#define FSMC_D15_GPIO_PIN         GPIO_Pin_10


/*控制信号线*/  
/*CS片选*/
/*NE3 ,对应的基地址0x68000000*/
// 这里一定要注意 那个地址用哪个地址
#define FSMC_CS_GPIO_PORT        GPIOG
#define FSMC_CS_GPIO_CLK         RCC_APB2Periph_GPIOG
#define FSMC_CS_GPIO_PIN         GPIO_Pin_10

/*WE写使能*/
#define FSMC_WE_GPIO_PORT        GPIOD
#define FSMC_WE_GPIO_CLK         RCC_APB2Periph_GPIOD
#define FSMC_WE_GPIO_PIN         GPIO_Pin_5

/*OE读使能*/
#define FSMC_OE_GPIO_PORT        GPIOD
#define FSMC_OE_GPIO_CLK         RCC_APB2Periph_GPIOD
#define FSMC_OE_GPIO_PIN         GPIO_Pin_4


/*UB数据掩码*/
#define FSMC_UDQM_GPIO_PORT        GPIOE
#define FSMC_UDQM_GPIO_CLK         RCC_APB2Periph_GPIOE
#define FSMC_UDQM_GPIO_PIN         GPIO_Pin_1

/*LB数据掩码*/
#define FSMC_LDQM_GPIO_PORT        GPIOE
#define FSMC_LDQM_GPIO_CLK         RCC_APB2Periph_GPIOE
#define FSMC_LDQM_GPIO_PIN         GPIO_Pin_0


void SRAM_Init(void); 
uint8_t SRAM_Test(void);
void SRAM_Write(void);
#endif

建立 SRAM 内存管理文件SRAM_book.c

代码如下 :

#include "SRAM_book.h" 
#include "Systick_book.h"
#include "USART_book.h"

/**
  * @brief  初始化控制SRAM的IO
  * @param  无
  * @retval 无
  */
static void SRAM_GPIO_Config(void)
{
	GPIO_InitTypeDef  GPIO_InitStructure;
 
  /* 使能SRAM相关的GPIO时钟 */

                         /*地址信号线*/
  RCC_APB2PeriphClockCmd(FSMC_A0_GPIO_CLK | FSMC_A1_GPIO_CLK | FSMC_A2_GPIO_CLK | 
                         FSMC_A3_GPIO_CLK | FSMC_A4_GPIO_CLK | FSMC_A5_GPIO_CLK |
                         FSMC_A6_GPIO_CLK | FSMC_A7_GPIO_CLK | FSMC_A8_GPIO_CLK |
                         FSMC_A9_GPIO_CLK | FSMC_A10_GPIO_CLK| FSMC_A11_GPIO_CLK| 
												 FSMC_A12_GPIO_CLK| FSMC_A13_GPIO_CLK|FSMC_A14_GPIO_CLK|
												 FSMC_A15_GPIO_CLK|FSMC_A16_GPIO_CLK|FSMC_A17_GPIO_CLK|FSMC_A18_GPIO_CLK|
                         /*数据信号线*/
                         FSMC_D0_GPIO_CLK | FSMC_D1_GPIO_CLK | FSMC_D2_GPIO_CLK | 
                         FSMC_D3_GPIO_CLK | FSMC_D4_GPIO_CLK | FSMC_D5_GPIO_CLK |
                         FSMC_D6_GPIO_CLK | FSMC_D7_GPIO_CLK | FSMC_D8_GPIO_CLK |
                         FSMC_D9_GPIO_CLK | FSMC_D10_GPIO_CLK| FSMC_D11_GPIO_CLK|
                         FSMC_D12_GPIO_CLK| FSMC_D13_GPIO_CLK| FSMC_D14_GPIO_CLK|
                         FSMC_D15_GPIO_CLK|  
                         /*控制信号线*/
                         FSMC_CS_GPIO_CLK  | FSMC_WE_GPIO_CLK | FSMC_OE_GPIO_CLK |
                         FSMC_UDQM_GPIO_CLK|FSMC_LDQM_GPIO_CLK, ENABLE);
												 //UDQM UB引脚


	 /*-- GPIO 配置 -----------------------------------------------------*/

  /* 通用 GPIO 配置 */
  GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF_PP;       //配置为复用功能
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;      //都是复用推完功能因为这个是数据手册的规定
  
  /*A地址信号线 针对引脚配置*/
  GPIO_InitStructure.GPIO_Pin = FSMC_A0_GPIO_PIN; 
  GPIO_Init(FSMC_A0_GPIO_PORT, &GPIO_InitStructure);
  
  GPIO_InitStructure.GPIO_Pin = FSMC_A1_GPIO_PIN; 
  GPIO_Init(FSMC_A1_GPIO_PORT, &GPIO_InitStructure);
  
  GPIO_InitStructure.GPIO_Pin = FSMC_A2_GPIO_PIN; 
  GPIO_Init(FSMC_A2_GPIO_PORT, &GPIO_InitStructure);
  
  GPIO_InitStructure.GPIO_Pin = FSMC_A3_GPIO_PIN; 
  GPIO_Init(FSMC_A3_GPIO_PORT, &GPIO_InitStructure);
  
  GPIO_InitStructure.GPIO_Pin = FSMC_A4_GPIO_PIN; 
  GPIO_Init(FSMC_A4_GPIO_PORT, &GPIO_InitStructure);
  
  GPIO_InitStructure.GPIO_Pin = FSMC_A5_GPIO_PIN; 
  GPIO_Init(FSMC_A5_GPIO_PORT, &GPIO_InitStructure);
  
  GPIO_InitStructure.GPIO_Pin = FSMC_A6_GPIO_PIN; 
  GPIO_Init(FSMC_A6_GPIO_PORT, &GPIO_InitStructure);
  
  GPIO_InitStructure.GPIO_Pin = FSMC_A7_GPIO_PIN; 
  GPIO_Init(FSMC_A7_GPIO_PORT, &GPIO_InitStructure);
  
  GPIO_InitStructure.GPIO_Pin = FSMC_A8_GPIO_PIN; 
  GPIO_Init(FSMC_A8_GPIO_PORT, &GPIO_InitStructure);
  
  GPIO_InitStructure.GPIO_Pin = FSMC_A9_GPIO_PIN; 
  GPIO_Init(FSMC_A9_GPIO_PORT, &GPIO_InitStructure);
  
  GPIO_InitStructure.GPIO_Pin = FSMC_A10_GPIO_PIN; 
  GPIO_Init(FSMC_A10_GPIO_PORT, &GPIO_InitStructure);
  
  GPIO_InitStructure.GPIO_Pin = FSMC_A11_GPIO_PIN; 
  GPIO_Init(FSMC_A11_GPIO_PORT, &GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Pin = FSMC_A12_GPIO_PIN; 
  GPIO_Init(FSMC_A12_GPIO_PORT, &GPIO_InitStructure);
  
  GPIO_InitStructure.GPIO_Pin = FSMC_A13_GPIO_PIN; 
  GPIO_Init(FSMC_A13_GPIO_PORT, &GPIO_InitStructure);
  
  GPIO_InitStructure.GPIO_Pin = FSMC_A14_GPIO_PIN; 
  GPIO_Init(FSMC_A14_GPIO_PORT, &GPIO_InitStructure);
  
  GPIO_InitStructure.GPIO_Pin = FSMC_A15_GPIO_PIN; 
  GPIO_Init(FSMC_A15_GPIO_PORT, &GPIO_InitStructure);	
	
	GPIO_InitStructure.GPIO_Pin = FSMC_A16_GPIO_PIN; 
  GPIO_Init(FSMC_A16_GPIO_PORT, &GPIO_InitStructure);
  
  GPIO_InitStructure.GPIO_Pin = FSMC_A17_GPIO_PIN; 
  GPIO_Init(FSMC_A17_GPIO_PORT, &GPIO_InitStructure);
  
  GPIO_InitStructure.GPIO_Pin = FSMC_A18_GPIO_PIN; 
  GPIO_Init(FSMC_A18_GPIO_PORT, &GPIO_InitStructure);
    
  /*DQ数据信号线 针对引脚配置*/
  GPIO_InitStructure.GPIO_Pin = FSMC_D0_GPIO_PIN; 
  GPIO_Init(FSMC_D0_GPIO_PORT, &GPIO_InitStructure);
  
  GPIO_InitStructure.GPIO_Pin = FSMC_D1_GPIO_PIN; 
  GPIO_Init(FSMC_D1_GPIO_PORT, &GPIO_InitStructure);
    
  GPIO_InitStructure.GPIO_Pin = FSMC_D2_GPIO_PIN; 
  GPIO_Init(FSMC_D2_GPIO_PORT, &GPIO_InitStructure);
  
  GPIO_InitStructure.GPIO_Pin = FSMC_D3_GPIO_PIN; 
  GPIO_Init(FSMC_D3_GPIO_PORT, &GPIO_InitStructure);
  
  GPIO_InitStructure.GPIO_Pin = FSMC_D4_GPIO_PIN; 
  GPIO_Init(FSMC_D4_GPIO_PORT, &GPIO_InitStructure);
  
  GPIO_InitStructure.GPIO_Pin = FSMC_D5_GPIO_PIN; 
  GPIO_Init(FSMC_D5_GPIO_PORT, &GPIO_InitStructure);
  
  GPIO_InitStructure.GPIO_Pin = FSMC_D6_GPIO_PIN; 
  GPIO_Init(FSMC_D6_GPIO_PORT, &GPIO_InitStructure);
  
  GPIO_InitStructure.GPIO_Pin = FSMC_D7_GPIO_PIN; 
  GPIO_Init(FSMC_D7_GPIO_PORT, &GPIO_InitStructure);
  
  GPIO_InitStructure.GPIO_Pin = FSMC_D8_GPIO_PIN; 
  GPIO_Init(FSMC_D8_GPIO_PORT, &GPIO_InitStructure);
  
  GPIO_InitStructure.GPIO_Pin = FSMC_D9_GPIO_PIN; 
  GPIO_Init(FSMC_D9_GPIO_PORT, &GPIO_InitStructure);
  
  GPIO_InitStructure.GPIO_Pin = FSMC_D10_GPIO_PIN; 
  GPIO_Init(FSMC_D10_GPIO_PORT, &GPIO_InitStructure);
  
  GPIO_InitStructure.GPIO_Pin = FSMC_D11_GPIO_PIN; 
  GPIO_Init(FSMC_D11_GPIO_PORT, &GPIO_InitStructure);
  
  GPIO_InitStructure.GPIO_Pin = FSMC_D12_GPIO_PIN; 
  GPIO_Init(FSMC_D12_GPIO_PORT, &GPIO_InitStructure);
  
  GPIO_InitStructure.GPIO_Pin = FSMC_D13_GPIO_PIN; 
  GPIO_Init(FSMC_D13_GPIO_PORT, &GPIO_InitStructure);
  
  GPIO_InitStructure.GPIO_Pin = FSMC_D14_GPIO_PIN; 
  GPIO_Init(FSMC_D14_GPIO_PORT, &GPIO_InitStructure);
  
  GPIO_InitStructure.GPIO_Pin = FSMC_D15_GPIO_PIN; 
  GPIO_Init(FSMC_D15_GPIO_PORT, &GPIO_InitStructure);
  
  /*控制信号线*/
  GPIO_InitStructure.GPIO_Pin = FSMC_CS_GPIO_PIN; 
  GPIO_Init(FSMC_CS_GPIO_PORT, &GPIO_InitStructure);
    
  GPIO_InitStructure.GPIO_Pin = FSMC_WE_GPIO_PIN; 
  GPIO_Init(FSMC_WE_GPIO_PORT, &GPIO_InitStructure);
  
  GPIO_InitStructure.GPIO_Pin = FSMC_OE_GPIO_PIN; 
  GPIO_Init(FSMC_OE_GPIO_PORT, &GPIO_InitStructure);    
  
  GPIO_InitStructure.GPIO_Pin = FSMC_UDQM_GPIO_PIN; 
  GPIO_Init(FSMC_UDQM_GPIO_PORT, &GPIO_InitStructure);
  
  GPIO_InitStructure.GPIO_Pin = FSMC_LDQM_GPIO_PIN; 
  GPIO_Init(FSMC_LDQM_GPIO_PORT, &GPIO_InitStructure);	
}				  			


//  写时钟周期 
  //ADDSET +1 + DATAST +1 > 55ns;
  //DATAST +1 >40ns
  //ADDSET +1 > 0  ns
  //  读的时钟
  //ADDSET +1 > 25ns
  //DATAST +1 > 0 ns
  /时钟单位 1.38*10-8S = 13.8ns
  
  //写时序
  //ADDSET
  //DATAST
  //
  //读时序
  //ADDSET = 0
  //DATAST = 2
  //
  //ADDSET +1 + DATAST +1 = (0+1+2+1)(13.8)=55.2> 55ns;
  //DATAST +1 >=2+1*(13.8 = 41.4)40ns
  //ADDSET +1  = 0+1*(13.8) = 13.8>0  ns
static void FSMC_ModleConfig(void){
  //对寄存器进行配置
  FSMC_NORSRAMTimingInitTypeDef readTimingInitStruct ;  //读时序
  FSMC_NORSRAMTimingInitTypeDef writeTimingInitStruct ; //写时序
  FSMC_NORSRAMInitTypeDef SRAMInitStruct;
  RCC_AHBPeriphClockCmd(RCC_AHBPeriph_FSMC,ENABLE);     //注意时钟使能
  
  //读
  readTimingInitStruct.FSMC_AccessMode = FSMC_AccessMode_A ;/*设置访问模式 */
  readTimingInitStruct.FSMC_AddressHoldTime = 0; //SRAM 没有用到
  readTimingInitStruct.FSMC_BusTurnAroundDuration = 0;//SRAM 没有用到
  readTimingInitStruct.FSMC_CLKDivision = 0;//SRAM 没有用到
  readTimingInitStruct.FSMC_DataLatency = 0;//SRAM 没有用到
  readTimingInitStruct.FSMC_AddressSetupTime = 0; //代表ADDSET单位13.8ns
  readTimingInitStruct.FSMC_DataSetupTime =2;//代表DATAST单位13.8ns 
  //写
  writeTimingInitStruct.FSMC_AccessMode = FSMC_AccessMode_A ;/*设置访问模式 */
  writeTimingInitStruct.FSMC_AddressHoldTime = 0; //SRAM 没有用到
  writeTimingInitStruct.FSMC_BusTurnAroundDuration = 0;//SRAM 没有用到
  writeTimingInitStruct.FSMC_CLKDivision = 0;//SRAM 没有用到
  writeTimingInitStruct.FSMC_DataLatency = 0;//SRAM 没有用到
  writeTimingInitStruct.FSMC_AddressSetupTime = 0; //代表ADDSET单位13.8ns
  writeTimingInitStruct.FSMC_DataSetupTime =2;//代表DATAST单位13.8ns 
  //到此读时序结构体完成
  
  SRAMInitStruct.FSMC_Bank = FSMC_Bank1_NORSRAM3;
  SRAMInitStruct.FSMC_ExtendedMode = FSMC_ExtendedMode_Enable; //读写时序
  SRAMInitStruct.FSMC_MemoryDataWidth = FSMC_MemoryDataWidth_16b;
  SRAMInitStruct.FSMC_MemoryType = FSMC_MemoryType_SRAM;
  //SRAM 没有用到
  SRAMInitStruct.FSMC_BurstAccessMode =  FSMC_BurstAccessMode_Disable ;
  SRAMInitStruct.FSMC_AsynchronousWait = FSMC_AsynchronousWait_Disable;
  SRAMInitStruct.FSMC_DataAddressMux = FSMC_DataAddressMux_Disable;
  SRAMInitStruct.FSMC_WaitSignal = FSMC_WaitSignal_Disable;
  SRAMInitStruct.FSMC_WaitSignalActive = FSMC_WaitSignalActive_BeforeWaitState;
  SRAMInitStruct.FSMC_WaitSignalPolarity = FSMC_WaitSignalPolarity_Low;
  SRAMInitStruct.FSMC_WrapMode = FSMC_WrapMode_Disable;
  SRAMInitStruct.FSMC_WriteBurst = FSMC_WriteBurst_Disable;
  SRAMInitStruct.FSMC_WriteOperation = FSMC_WriteOperation_Enable;
  //读写操作结构化评估
  SRAMInitStruct.FSMC_ReadWriteTimingStruct = &readTimingInitStruct;//读
  SRAMInitStruct.FSMC_WriteTimingStruct = &writeTimingInitStruct;//写 
  //写操作 注意FSMC_ExtendedMode 配置成 FSMC_ExtendedMode_Enable 时候有用
  
  // 吧配置写入寄存器
  FSMC_NORSRAMInit(&SRAMInitStruct);
  //使能FSMC
  FSMC_NORSRAMCmd(FSMC_Bank1_NORSRAM3,ENABLE);
  
}

/**
  * @brief  测试SRAM是否正常 
  * @param  None
  * @retval 正常返回1,异常返回0
  */
uint8_t SRAM_Test(void){
  /*写入数据计数器*/
  uint32_t counter=0;
  
  /* 8位的数据 */
  uint8_t ubWritedata_8b = 0, ubReaddata_8b = 0;  
  
  /* 16位的数据 */
  uint16_t uhWritedata_16b = 0, uhReaddata_16b = 0; 
  
  printf("正在检测SRAM,以8位、16位的方式读写sram...");


  /*按8位格式读写数据,并校验*/
  
  /* 把SRAM数据全部重置为0 ,IS62WV51216_SIZE是以8位为单位的 */
  for (counter = 0x00; counter < SRAM_SIZE; counter++)
  {
    *(__IO uint8_t*) (SRAM_BASE_ADDR + counter) = (uint8_t)0x0;
  }
  
  /* 向整个SRAM写入数据  8位 */
  for (counter = 0; counter < SRAM_SIZE; counter++)
  {
    *(__IO uint8_t*) (SRAM_BASE_ADDR + counter) = (uint8_t)(ubWritedata_8b + counter);
  }
  
  /* 读取 SRAM 数据并检测*/
  for(counter = 0; counter<SRAM_SIZE;counter++ )
  {
    ubReaddata_8b = *(__IO uint8_t*)(SRAM_BASE_ADDR + counter);  //从该地址读出数据
    
    if(ubReaddata_8b != (uint8_t)(ubWritedata_8b + counter))      //检测数据,若不相等,跳出函数,返回检测失败结果。
    {
      printf("8位数据读写错误!");
      return 0;
    }
  }
	 
  /*按16位格式读写数据,并检测*/
  
  /* 把SRAM数据全部重置为0 */
  for (counter = 0x00; counter < SRAM_SIZE/2; counter++)
  {
    *(__IO uint16_t*) (SRAM_BASE_ADDR + 2*counter) = (uint16_t)0x00;
  }
  
  /* 向整个SRAM写入数据  16位 */
  for (counter = 0; counter < SRAM_SIZE/2; counter++)
  {
    *(__IO uint16_t*) (SRAM_BASE_ADDR + 2*counter) = (uint16_t)(uhWritedata_16b + counter);
  }
  
    /* 读取 SRAM 数据并检测*/
  for(counter = 0; counter<SRAM_SIZE/2;counter++ )
  {
    uhReaddata_16b = *(__IO uint16_t*)(SRAM_BASE_ADDR + 2*counter);  //从该地址读出数据
    
    if(uhReaddata_16b != (uint16_t)(uhWritedata_16b + counter))      //检测数据,若不相等,跳出函数,返回检测失败结果。
    {
      printf("16位数据读写错误!\n");

      return 0;
    }
  }  
  printf("SRAM读写测试正常!\n"); 
  /*检测正常,return 1 */
  return 1;

}

/**
  * @brief  void SRAM_Init(void)
  * @param  初始化
  * @retval  
  */
void SRAM_Init(void){
  SRAM_GPIO_Config();
  FSMC_ModleConfig();
}


static uint8_t testSRAM_Value __attribute__ ((at (SRAM_BASE_ADDR)));

/**
  * @brief  void SRAM_Write(void) 
  * @param  None
  * @retval 
  */  
void SRAM_Write(void){
  uint8_t* SRAM_P ;
  uint16_t* SRAM_P16 ;
  float* SRAM_Pf ;
  //---------------------------------
  SRAM_P = (uint8_t*)SRAM_BASE_ADDR;
  *SRAM_P = 0xA2;
  printf("读出的数据为1: 0x%x\n",*SRAM_P); 
  //---------------------------------
  SRAM_P16 = (uint16_t*)SRAM_BASE_ADDR;
  *SRAM_P16 = 0x2AA2;
  printf("读出的数据为2: 0x%x\n",*SRAM_P16);
  //---------------------------------
  SRAM_Pf = (float*)SRAM_BASE_ADDR;
  *SRAM_Pf = 83.65;
  printf("读出的数据为3: 0x%.2f\n",*SRAM_Pf);
  //-----------利用常量来设计----------------------
  testSRAM_Value = 0x6A;
  printf("读出的数据为1: 0x%x\n",testSRAM_Value); 
}

整改 系统文件diskio.c

代码如下 :

/*-----------------------------------------------------------------------*/
/* Low level disk I/O module skeleton for FatFs     (C)ChaN, 2014        */
/*-----------------------------------------------------------------------*/
/* If a working storage control module is available, it should be        */
/* attached to the FatFs via a glue function rather than modifying it.   */
/* This is an example of glue functions to attach various exsisting      */
/* storage control modules to the FatFs module with a defined API.       */
/*-----------------------------------------------------------------------*/

#include "diskio.h"		/* FatFs lower layer API */
#include "ff.h"
#include "SPI_book.h"

//#include "usbdisk.h"	/* Example: Header file of existing USB MSD control module */
//#include "atadrive.h"	/* Example: Header file of existing ATA harddisk control module */
//#include "sdcard.h"		/* Example: Header file of existing MMC/SDC contorl module */

/* Definitions of physical drive number for each drive */
#define ATA		0	/* Example: Map ATA harddisk to physical drive 0 */
#define MMC		1	/* Example: Map MMC/SD card to physical drive 1 */
#define USB		2	/* Example: Map USB MSD to physical drive 2 */

#define SD_CARD   0
#define SPI_FLASH 1

/*-----------------------------------------------------------------------*/
/* Get Drive Status                                                      */
/*-----------------------------------------------------------------------*/

DSTATUS disk_status (
	BYTE pdrv		/* Physical drive nmuber to identify the drive */
)
{
	DSTATUS stat = STA_NOINIT;
//	int result;

	switch (pdrv) {
    case SD_CARD : break;
    
    case SPI_FLASH :
      if(SPI_Read_ID() == _SPI_FLASH_ID){
        stat = RES_OK;
      }else{
        stat = RES_ERROR;
      }
      break;
    default:
			stat = STA_NOINIT;  
	}
	return stat;
}

//disk_status 函数只有一个参数 pdrv,表示物理编号。一般我们都是使用 switch 函数实
//现对 pdrv 的分支判断。对于 SD 卡只是预留接口,留空即可。对于 SPI Flash 芯片,我们直
//接调用在 SPI_FLASH_ReadID()获取设备 ID,

/*-----------------------------------------------------------------------*/
/* Inidialize a Drive                                                    */
/*-----------------------------------------------------------------------*/

DSTATUS disk_initialize (
	BYTE pdrv				/* Physical drive nmuber to identify the drive */
)
{
	DSTATUS stat = STA_NOINIT;
//	int result;

	switch (pdrv) {
    case SD_CARD : break;
  
    case SPI_FLASH :
      SPI_FLASH_Init();
      SPI_Flash_WAKEUP();
      stat = disk_status(SPI_FLASH);
      break;
    default:
			stat = STA_NOINIT;  
	}
	return stat;
}

//disk_initialize 函数也是有一个参数 pdrv,用来指定设备物理编号。对于 SPI Flash 芯片
//我们调用 SPI_FLASH_Init()函数实现对 SPI Flash 芯片引脚 GPIO 初始化配置以及 SPI 通信
//参数配置。SPI_Flash_WAKEUP()函数唤醒 SPI Flash 芯片,当 SPI Flash 芯片处于睡眠模式
//时需要唤醒芯片才可以进行读写操作。

/*-----------------------------------------------------------------------*/
/* Read Sector(s)                                                        */
/*-----------------------------------------------------------------------*/

DRESULT disk_read (
	BYTE pdrv,		/* Physical drive nmuber to identify the drive */
	BYTE *buff,		/* Data buffer to store read data */
	DWORD sector,	/* Sector address in LBA */
	UINT count		/* Number of sectors to read */
)
{
	DRESULT res = RES_PARERR;
	//int result;

	switch (pdrv) {
    case SD_CARD : break;
  
    case SPI_FLASH :
      // 扇区偏移2MB  外部Flash 文件系统空间放在 SPI_FLAH后面的6MB空间
      sector += 512;
      SPI_Read_Data(buff,(sector << 12),(count << 12));
      res = RES_OK ; 
      break;
    default:
			res = RES_PARERR;  
	}

	return res;
}

//SPI Flash 芯片型号为 W25Q64FV,每个扇区大小为 4096 个字节(4KB),
//总共有 8M 字节空间,为兼容后面实验程序,我们只将后部分 6MB 空间分配给 FatFs 使用,
//前部分 2MB 空间用于其他实验需要,即 FatFs 是从 2MB 空间开始,为实现这个效果需要
//将所有的读写地址都偏移 512 个扇区空间

/*-----------------------------------------------------------------------*/
/* Write Sector(s)                                                       */
/*-----------------------------------------------------------------------*/

#if _USE_WRITE
DRESULT disk_write (
	BYTE pdrv,			/* Physical drive nmuber to identify the drive */
	const BYTE *buff,	/* Data to be written */
	DWORD sector,		/* Sector address in LBA */
	UINT count			/* Number of sectors to write */
)
{
	DRESULT res = RES_PARERR;
	//int result;
  if(!count){return res;}
	switch (pdrv) {
	  case SD_CARD : break;
  
    case SPI_FLASH :
      // 扇区偏移2MB  外部Flash 文件系统空间放在 SPI_FLAH后面的6MB空间
      sector += 512;
      SPI_Erase_Sector(sector << 12);
      SPI_BufferWrite_Data((uint8_t *)buff ,sector << 12,count << 12);
      res = RES_OK ; 
      break;
    default:
			res = RES_PARERR;  
	}

	return res;
}
#endif
//disk_write 函数有四个形参,pdrv 为设备物理编号。buff 指向待写入扇区数据的首地址。
//sector,指定要写入数据的扇区首地址。count 指定扇区数量。对于 SPI Flash 芯片,在写入
//数据之前需要先擦除,所以用到扇区擦除函数(SPI_FLASH_SectorErase)。然后就是在调用
//数据写入函数(SPI_FLASH_BufferWrite)把数据写入到指定位置内。

/*-----------------------------------------------------------------------*/
/* Miscellaneous Functions                                               */
/*-----------------------------------------------------------------------*/

#if _USE_IOCTL
DRESULT disk_ioctl (
	BYTE pdrv,		/* Physical drive nmuber (0..) */
	BYTE cmd,		/* Control code */
	void *buff		/* Buffer to send/receive control data */
)
{
	DRESULT res = RES_PARERR;
//	int result;

	switch (pdrv) {
    case SD_CARD : break;
  
    case SPI_FLASH :{
       switch(cmd){
          //返回 扇区的个数 1280*4096/1024/1024 = 5MB
         case GET_SECTOR_COUNT:
           *(DWORD * )buff = 1280;
           break;
         //返回每一个扇区的大小
         case GET_SECTOR_SIZE:
           *(DWORD * )buff = 4096;
           break;
         case GET_BLOCK_SIZE:
           *(DWORD * )buff = 1;
           break;    
         default:
          return RES_PARERR; 
       }
       res = RES_OK;
       break;
    }
   default:
      res =  RES_PARERR;     
	}
	return res;
}
#endif

 DWORD get_fattime(void) {
	/* 返回当前时间戳 */
	return	  ((DWORD)(2015 - 1980) << 25)	/* Year 2015 */
			| ((DWORD)1 << 21)				/* Month 1 */
			| ((DWORD)1 << 16)				/* Mday 1 */
			| ((DWORD)0 << 11)				/* Hour 0 */
			| ((DWORD)0 << 5)				  /* Min 0 */
			| ((DWORD)0 >> 1);				/* Sec 0 */
}

修改头文件文件diskio.h

代码如下 :

/*---------------------------------------------------------------------------/
/  FatFs - FAT file system module configuration file  R0.11a (C)ChaN, 2015
/---------------------------------------------------------------------------*/

#define _FFCONF 64180	/* Revision ID */

/*---------------------------------------------------------------------------/
/ Function Configurations
/---------------------------------------------------------------------------*/

#define _FS_READONLY	0
/* This option switches read-only configuration. (0:Read/Write or 1:Read-only)
/  Read-only configuration removes writing API functions, f_write(), f_sync(),
/  f_unlink(), f_mkdir(), f_chmod(), f_rename(), f_truncate(), f_getfree()
/  and optional writing functions as well. */


#define _FS_MINIMIZE	0
/* This option defines minimization level to remove some basic API functions.
/
/   0: All basic functions are enabled.
/   1: f_stat(), f_getfree(), f_unlink(), f_mkdir(), f_chmod(), f_utime(),
/      f_truncate() and f_rename() function are removed.
/   2: f_opendir(), f_readdir() and f_closedir() are removed in addition to 1.
/   3: f_lseek() function is removed in addition to 2. */


#define	_USE_STRFUNC	1
/* This option switches string functions, f_gets(), f_putc(), f_puts() and
/  f_printf().
/
/  0: Disable string functions.
/  1: Enable without LF-CRLF conversion.
/  2: Enable with LF-CRLF conversion. */


#define _USE_FIND		0
/* This option switches filtered directory read feature and related functions,
/  f_findfirst() and f_findnext(). (0:Disable or 1:Enable) */


#define	_USE_MKFS		1 
/* This option switches f_mkfs() function. (0:Disable or 1:Enable) */
//格式化功能选择,为使用 FatFs 格式化功能,需要把它设置为 1

#define	_USE_FASTSEEK	0
/* This option switches fast seek feature. (0:Disable or 1:Enable) */


#define _USE_LABEL		0
/* This option switches volume label functions, f_getlabel() and f_setlabel().
/  (0:Disable or 1:Enable) */


#define	_USE_FORWARD	0
/* This option switches f_forward() function. (0:Disable or 1:Enable)
/  To enable it, also _FS_TINY need to be set to 1. */


/*---------------------------------------------------------------------------/
/ Locale and Namespace Configurations
/---------------------------------------------------------------------------*/

#define _CODE_PAGE	936
//语言功能选择
/* This option specifies the OEM code page to be used on the target system.
/  Incorrect setting of the code page can cause a file open failure.
/
/   1   - ASCII (No extended character. Non-LFN cfg. only)
/   437 - U.S.
/   720 - Arabic
/   737 - Greek
/   771 - KBL
/   775 - Baltic
/   850 - Latin 1
/   852 - Latin 2
/   855 - Cyrillic
/   857 - Turkish
/   860 - Portuguese
/   861 - Icelandic
/   862 - Hebrew
/   863 - Canadian French
/   864 - Arabic
/   865 - Nordic
/   866 - Russian
/   869 - Greek 2
/   932 - Japanese (DBCS)
/   936 - Simplified Chinese (DBCS)
/   949 - Korean (DBCS)
/   950 - Traditional Chinese (DBCS)
*/


#define	_USE_LFN	2
//2: Enable LFN with dynamic working buffer on the STACK.
//长文件名支持
#define	_MAX_LFN	255
/* The _USE_LFN option switches the LFN feature.
/
/   0: Disable LFN feature. _MAX_LFN has no effect.
/   1: Enable LFN with static working buffer on the BSS. Always NOT thread-safe.
/   2: Enable LFN with dynamic working buffer on the STACK.
/   3: Enable LFN with dynamic working buffer on the HEAP.
/
/  When enable the LFN feature, Unicode handling functions (option/unicode.c) must
/  be added to the project. The LFN working buffer occupies (_MAX_LFN + 1) * 2 bytes.
/  When use stack for the working buffer, take care on stack overflow. When use heap
/  memory for the working buffer, memory management functions, ff_memalloc() and
/  ff_memfree(), must be added to the project. */


#define	_LFN_UNICODE	0
/* This option switches character encoding on the API. (0:ANSI/OEM or 1:Unicode)
/  To use Unicode string for the path name, enable LFN feature and set _LFN_UNICODE
/  to 1. This option also affects behavior of string I/O functions. */


#define _STRF_ENCODE	3
/* When _LFN_UNICODE is 1, this option selects the character encoding on the file to
/  be read/written via string I/O functions, f_gets(), f_putc(), f_puts and f_printf().
/
/  0: ANSI/OEM
/  1: UTF-16LE
/  2: UTF-16BE
/  3: UTF-8
/
/  When _LFN_UNICODE is 0, this option has no effect. */


#define _FS_RPATH	0
/* This option configures relative path feature.
/
/   0: Disable relative path feature and remove related functions.
/   1: Enable relative path feature. f_chdir() and f_chdrive() are available.
/   2: f_getcwd() function is available in addition to 1.
/
/  Note that directory items read via f_readdir() are affected by this option. */


/*---------------------------------------------------------------------------/
/ Drive/Volume Configurations
/---------------------------------------------------------------------------*/

#define _VOLUMES	2
/* Number of volumes (logical drives) to be used. */
//包括预留 SD 卡和 SPI Flash 芯片

#define _STR_VOLUME_ID	0
#define _VOLUME_STRS	"RAM","NAND","CF","SD1","SD2","USB1","USB2","USB3"
/* _STR_VOLUME_ID option switches string volume ID feature.
/  When _STR_VOLUME_ID is set to 1, also pre-defined strings can be used as drive
/  number in the path name. _VOLUME_STRS defines the drive ID strings for each
/  logical drives. Number of items must be equal to _VOLUMES. Valid characters for
/  the drive ID strings are: A-Z and 0-9. */


#define	_MULTI_PARTITION	0
/* This option switches multi-partition feature. By default (0), each logical drive
/  number is bound to the same physical drive number and only an FAT volume found on
/  the physical drive will be mounted. When multi-partition feature is enabled (1),
/  each logical drive number is bound to arbitrary physical drive and partition
/  listed in the VolToPart[]. Also f_fdisk() funciton will be available. */


#define	_MIN_SS		512
#define	_MAX_SS		4096
//SPI Flash 芯片扇区大小一般设置为 4096 字节
/* These options configure the range of sector size to be supported. (512, 1024,
/  2048 or 4096) Always set both 512 for most systems, all type of memory cards and
/  harddisk. But a larger value may be required for on-board flash memory and some
/  type of optical media. When _MAX_SS is larger than _MIN_SS, FatFs is configured
/  to variable sector size and GET_SECTOR_SIZE command must be implemented to the
/  disk_ioctl() function. */


#define	_USE_TRIM	0
/* This option switches ATA-TRIM feature. (0:Disable or 1:Enable)
/  To enable Trim feature, also CTRL_TRIM command should be implemented to the
/  disk_ioctl() function. */


#define _FS_NOFSINFO	0
/* If you need to know correct free space on the FAT32 volume, set bit 0 of this
/  option, and f_getfree() function at first time after volume mount will force
/  a full FAT scan. Bit 1 controls the use of last allocated cluster number.
/
/  bit0=0: Use free cluster count in the FSINFO if available.
/  bit0=1: Do not trust free cluster count in the FSINFO.
/  bit1=0: Use last allocated cluster number in the FSINFO if available.
/  bit1=1: Do not trust last allocated cluster number in the FSINFO.
*/



/*---------------------------------------------------------------------------/
/ System Configurations
/---------------------------------------------------------------------------*/

#define	_FS_TINY	0
/* This option switches tiny buffer configuration. (0:Normal or 1:Tiny)
/  At the tiny configuration, size of the file object (FIL) is reduced _MAX_SS
/  bytes. Instead of private sector buffer eliminated from the file object,
/  common sector buffer in the file system object (FATFS) is used for the file
/  data transfer. */


#define _FS_NORTC	0
#define _NORTC_MON	1
#define _NORTC_MDAY	1
#define _NORTC_YEAR	2015
/* The _FS_NORTC option switches timestamp feature. If the system does not have
/  an RTC function or valid timestamp is not needed, set _FS_NORTC to 1 to disable
/  the timestamp feature. All objects modified by FatFs will have a fixed timestamp
/  defined by _NORTC_MON, _NORTC_MDAY and _NORTC_YEAR.
/  When timestamp feature is enabled (_FS_NORTC == 0), get_fattime() function need
/  to be added to the project to read current time form RTC. _NORTC_MON,
/  _NORTC_MDAY and _NORTC_YEAR have no effect. 
/  These options have no effect at read-only configuration (_FS_READONLY == 1). */


#define	_FS_LOCK	0
/* The _FS_LOCK option switches file lock feature to control duplicated file open
/  and illegal operation to open objects. This option must be 0 when _FS_READONLY
/  is 1.
/
/  0:  Disable file lock feature. To avoid volume corruption, application program
/      should avoid illegal open, remove and rename to the open objects.
/  >0: Enable file lock feature. The value defines how many files/sub-directories
/      can be opened simultaneously under file lock control. Note that the file
/      lock feature is independent of re-entrancy. */


#define _FS_REENTRANT	0
#define _FS_TIMEOUT		1000
#define	_SYNC_t			HANDLE
/* The _FS_REENTRANT option switches the re-entrancy (thread safe) of the FatFs
/  module itself. Note that regardless of this option, file access to different
/  volume is always re-entrant and volume control functions, f_mount(), f_mkfs()
/  and f_fdisk() function, are always not re-entrant. Only file/directory access
/  to the same volume is under control of this feature.
/
/   0: Disable re-entrancy. _FS_TIMEOUT and _SYNC_t have no effect.
/   1: Enable re-entrancy. Also user provided synchronization handlers,
/      ff_req_grant(), ff_rel_grant(), ff_del_syncobj() and ff_cre_syncobj()
/      function, must be added to the project. Samples are available in
/      option/syscall.c.
/
/  The _FS_TIMEOUT defines timeout period in unit of time tick.
/  The _SYNC_t defines O/S dependent sync object type. e.g. HANDLE, ID, OS_EVENT*,
/  SemaphoreHandle_t and etc.. A header file for O/S definitions needs to be
/  included somewhere in the scope of ff.c. */


#define _WORD_ACCESS	0
/* The _WORD_ACCESS option is an only platform dependent option. It defines
/  which access method is used to the word data on the FAT volume.
/
/   0: Byte-by-byte access. Always compatible with all platforms.
/   1: Word access. Do not choose this unless under both the following conditions.
/
/  * Address misaligned memory access is always allowed to ALL instructions.
/  * Byte order on the memory is little-endian.
/
/  If it is the case, _WORD_ACCESS can also be set to 1 to reduce code size.
/  Following table shows allowable settings of some type of processors.
/
/  ARM7TDMI   0   *2          ColdFire   0    *1         V850E      0    *2
/  Cortex-M3  0   *3          Z80        0/1             V850ES     0/1
/  Cortex-M0  0   *2          x86        0/1             TLCS-870   0/1
/  AVR        0/1             RX600(LE)  0/1             TLCS-900   0/1
/  AVR32      0   *1          RL78       0    *2         R32C       0    *2
/  PIC18      0/1             SH-2       0    *1         M16C       0/1
/  PIC24      0   *2          H8S        0    *1         MSP430     0    *2
/  PIC32      0   *1          H8/300H    0    *1         8051       0/1
/
/  *1:Big-endian.
/  *2:Unaligned memory access is not supported.
/  *3:Some compilers generate LDM/STM for mem_cpy function.
*/

建立FatFs传输的 头文件 FatFs_book.h

代码如下 :

#ifndef  __FATFS_BOOK_H_
#define  __FATFS_BOOK_H_

#include "ff.h"			/* Declarations of FatFs API */

extern FATFS     fs;                   /* FatFs 文件系统对象 */
extern FIL       fnew;                 /* 文件对象 */
extern FRESULT   res_flash;            /* 文件操作结果 */
extern UINT      fnum;                 /* 文件成功读写数量 */
extern BYTE      FATFS_buffer[0];      /* 读缓冲区 */
extern BYTE      textFile_Buffer[];
extern BYTE      textFile_Buffer2[];
extern BYTE      File_Read_Buffer[1024];
 
#define  textFile_Buffer         "欢迎自己完成第一阶段回国隔离任务,后面加油 今天是个好日子,新建文件系统测试文件\r\n" 
 
#define  FileTest_ADDR_Buffer_Document        "1:Wangqi.txt" 
#define  FileTest_ADDR_Buffer_NewDocument     "1:Wangqi/Wangqi.txt" 
#define  FileTest_ADDR_Buffer_path            "1:test" 
#define  FileTest_ADDR_Buffer_NewDocument2    "1:test/testdir.txt" 
    
FRESULT FatFs_equipment_flash(
  const TCHAR* path
);
FRESULT FatFs_Close_flash(
  const TCHAR* path 
);
BYTE* FatFs_document_Text(
  const TCHAR* _File_ADDR ,
  BYTE* Text_Buffer 
);
  
void FatFs_document_set_content(
  const TCHAR* _File_ADDR , 
  const TCHAR* _File_OldPath , 
  const TCHAR* _File_NewPath 
);
  
FRESULT file_check(const TCHAR*  FileTest_ADDR); 
FRESULT Scan_file_Check(const TCHAR*  File_ADDR);
  
#endif

建立FatFs传输的 头文件 FatFs_book.c

代码如下 :

#include "FatFs_book.h"
#include "Systick_book.h"
#include "USART_book.h"
#include "string.h"
FATFS     fs;                   /* FatFs 文件系统对象 */
FIL       fnew;                 /* 文件对象 */
FRESULT   res_flash;            /* 文件操作结果 */
UINT      fnum;                 /* 文件成功读写数量 */
BYTE      FATFS_buffer[0];      /* 读缓冲区 */
 
/**************************************************************
* @brief  
* FRESULT FatFs_equipment_flash(const TCHAR* path )
* @param  
* 
*FatFs 的第一步工作就是使用 f_mount 函数挂载工作区。f_mount 函数有三个形参,第
*一个参数是指向 FATFS 变量指针,如果赋值为 NULL 可以取消物理设备挂载。第二个参数
*为逻辑设备编号,使用设备根路径表示,与物理设备编号挂钩,在代码清单 26-1 中我们定
*义 SPI Flash 芯片物理编号为 1,所以这里使用“1:”。第三个参数可选 0 或 1,1 表示立
*即挂载,0 表示不立即挂载,延迟挂载。 f_mount 函数会返回一个 FRESULT 类型值,指示
*运行情况。
*如果 f_mount 函数返回值为 FR_NO_FILESYSTEM,说明没有 FAT 文件系统,比如新
*出厂的 SPI Flash 芯片就没有 FAT 文件系统。我们就必须对物理设备进行格式化处理。使用
*f_mkfs 函数可以实现格式化操作。f_mkfs 函数有三个形参,第一个参数为逻辑设备编号;
*第二参数可选 0 或者 1,0 表示设备为一般硬盘,1 表示设备为软盘。第三个参数指定扇区
*大小,如果为 0,表示通过代码清单 26-6 中 disk_ioctl 函数获取。格式化成功后需要先取消
*挂载原来设备,再重新挂载设备。
*
*/
FRESULT FatFs_equipment_flash(
  const TCHAR* path 
){
  //在外部 SPI Flash 挂载文件系统,文件系统挂载时会对 SPI 设备初始化
  //初始化函数调用流程如下
  //f_mount()->find_volume()->disk_initialize->SPI_FLASH_Init()
  DIR     dir;
  FATFS   *pfs;
  DWORD   fre_clust , fre_sect , tot_sect ;
  printf("\n********** 外部SPIFlash挂载文件系统 **********\r\n");
  printf(" 在外部SPI Flash挂载文件系统,文件系统挂载时会对SPI设备初始化\r\n");
  res_flash = f_mount(&fs ,path,1); //SPI Flash 芯片物理编号为 1,所以这里使用“1:”。
  /* Pointer to the file system object (NULL:unmount)*/
  /* Logical drive number to be mounted/unmounted */
  /* 0:Do not mount (delayed mount), 1:Mount immediately */
  /*----------------------- 格式化测试 -----------------*/
  /* 如果没有文件系统就格式化创建创建文件系统 */  
  if(res_flash ==  FR_NO_FILESYSTEM ){  
    printf("》FLASH 还没有文件系统,即将进行Flash FatFS 格式化...\r\n");
    /* 格式化 */
    res_flash=f_mkfs(path,0,0);/* Create a file system on the volume */
     /* Logical drive number */
	   /* Partitioning rule 0:FDISK, 1:SFD */
	   /* Size of allocation unit in unit of byte or sector */
    //格式完成,先取消挂载
    if(res_flash == FR_OK ){
      printf("》FLASH 已成功格式化文件系统。\r\n");
      res_flash = f_mount(NULL ,path ,1); 
      //重新挂载
      res_flash = f_mount(&fs ,path ,1); 
    }else{
      printf("》格式化失败。 \r\n");
      return res_flash;
    } 
  }
  else if(res_flash != FR_OK){
    printf("!外部 Flash 挂载文件系统失败。(%d)\r\n",res_flash);
    printf("!可能原因:SPI Flash 初始化不成功。\r\n");
    return res_flash;
  }
  else{
    printf("》文件系统挂载成功,可以进行读写测试\r\n");
  } 
  
  //获取设备信息和空间的大小
  res_flash  = f_getfree(path ,&fre_clust ,&pfs );
  //计算得到的总扇区个数和空扇区个数
  tot_sect = (pfs->n_fatent-2)*pfs->csize ;
  fre_sect = fre_clust * pfs->csize ;
  /* 打印信息(4096 字节/扇区) */
  printf("》设备总空间:%10lu KB。\n》可用空间:  %10lu KB。\r\n", tot_sect<<2, fre_sect<<2);
  return FR_OK;
}

/**************************************************************
* @brief  
* FRESULT FatFs_Close_flash( const TCHAR* path )
* @param  
*  关闭Flash操作
**************************************************************/
FRESULT FatFs_Close_flash(
  const TCHAR* path 
){
  res_flash=f_mkfs(path,0,0);
  /* 不再使用文件系统,取消挂载文件系统 */
 return f_mount(NULL ,path ,1);
}

/**************************************************************
* @brief  
* void FatFs_document_Text(
*  const TCHAR* _File_ADDR , 文件的目录
*  BYTE* Text_Buffer       需要写入的内容
*)
* @param  
*  关闭Flash文件的读写操作
**************************************************************/
BYTE* FatFs_document_Text(
  const TCHAR* _File_ADDR ,
  BYTE* Text_Buffer   
){
  BYTE  File_Read_Buffer[1024]={0}  ;
 
  printf("\n******** 文件%s定位和格式化写入功能测试 ********\r\n",File_Read_Buffer);
  res_flash = f_open(&fnew , _File_ADDR, FA_OPEN_ALWAYS|FA_WRITE|FA_READ );
  if( res_flash == FR_OK ){
    //res_flash = f_write(&fnew,Text_Buffer ,sizeof(Text_Buffer), &fnum);  这个函数有点问题:sizeof(Text_Buffer)
    //f_printf(&fnew ,Text_Buffer );
    printf("******** 文件定位追加内容 ********\r\n");
    res_flash = f_lseek(&fnew , f_size(&fnew));	//文件定位到 文件的末尾   Move file pointer of a file object  
    if( res_flash == FR_OK ){
      //格式化写入 参数格式类似printf函数
      f_printf(&fnew ,Text_Buffer );  
      res_flash = f_lseek(&fnew,0);
      res_flash = f_read(&fnew , File_Read_Buffer , f_size(&fnew),&fnum );
      if(res_flash == FR_OK){
        printf("> 文件内容:\n%s\n" ,File_Read_Buffer );
      }
    }  
  }
  else{
    printf("!! 打开文件失败:%d\n",res_flash);
    printf("!! 或许需要再次运行“FatFs移植与读写测试”工程\n");
  }
  f_close(&fnew);
  return File_Read_Buffer;
}
 

/**************************************************************
* @brief  
*void FatFs_document_set_content(
*  const TCHAR* _File_ADDR ,  //文件原始名字路径
*  const TCHAR* _File_OldPath ,//建立新的文件夹目录 
*  const TCHAR* _File_NewPath  //文件移动以后的文件目录
*)
* @param  
*  文件目录的更改
**************************************************************/
void FatFs_document_set_content(
  const TCHAR* _File_ADDR , 
  const TCHAR* _File_OldPath , 
  const TCHAR* _File_NewPath 
){
   DIR     dir;
  //尝试打开目录、
  printf("\n********** 目录创建和重命名功能测试 **********\r\n");
  res_flash = f_opendir(&dir,_File_OldPath);
  if(res_flash != FR_OK){
    printf("!! 打开目录%s 失败开始尝试创建新的目录\n",_File_OldPath);
    res_flash = f_mkdir(_File_OldPath);
      res_flash = f_opendir(&dir,_File_OldPath);
      if(res_flash != FR_OK){
         printf("!! 创建新的目录还是失败(%d) \n",res_flash);
         return;
      } 
  }
  else{
    printf("!! 如果目录已经存在,关闭它\n"); 
    res_flash = f_closedir(&dir);
    //删除文件
    f_unlink(_File_NewPath);
  }
    //重命名并移动文件
    res_flash=f_rename(_File_ADDR , _File_NewPath);
    printf(">  重命名并移动文件%d完成 \n",res_flash);
}

/**************************************************************
* @brief  
* FRESULT file_check(const TCHAR*  File_ADDR)
* @param  
* 文件属性的查看
**************************************************************/
FRESULT file_check(const TCHAR*  File_ADDR){
  static FILINFO fno;
  printf("\n********** file_check 测试 **********\r\n");
  /* 获取文件信息,必须确保文件存在*/
  res_flash = f_stat(File_ADDR,&fno);
  if(res_flash==FR_OK){
    printf("%s 文件信息:\n",File_ADDR);
    printf("》文件大小: %ld(字节)\n", fno.fsize);
    printf("》时间戳: %u/%02u/%02u, %02u:%02u\n",
      (fno.fdate >> 9) + 1980, fno.fdate >> 5 & 15, fno.fdate & 31,
       fno.ftime >> 11, fno.ftime >> 5 & 63);
    printf("》属性: %c%c%c%c%c\n\n",
      (fno.fattrib & AM_DIR) ? 'D' : '-', // 是一个目录
      (fno.fattrib & AM_RDO) ? 'R' : '-', // 只读文件
      (fno.fattrib & AM_HID) ? 'H' : '-', // 隐藏文件
      (fno.fattrib & AM_SYS) ? 'S' : '-', // 系统文件
      (fno.fattrib & AM_ARC) ? 'A' : '-'); // 档案文件
  }  
  else{
     printf("》错误 %ld(字节)\n",res_flash);
  }
  return res_flash;
} 

/**************************************************************
* @brief  
* FRESULT scan_file(char*  File_ADDR)
* @param  
* 文件目录内容的查看
**************************************************************/
static FRESULT scan_file(char*  File_ADDR){
  FRESULT res;  //部分在递归过程被修改的变了
  FILINFO fno;
  DIR     dir;
  int     i;
  TCHAR*  fn;
#if _USE_LFN  
  //长文件名支持
  //简体中文需要2个字节保存一个字
  static char lfn[_MAX_LFN*2 + 1];
  fno.lfname = lfn;
  fno.lfsize = sizeof(lfn);
#endif
  //打开目录 
  res = f_opendir(&dir, File_ADDR);
  if(res == FR_OK){
    i = strlen(File_ADDR);
    while(1){
      //读取 目录下的内容,再读会自动读取下一个文件
      res = f_readdir(&dir , &fno);
      //为空时候表示读取完毕
      if((res != FR_OK) || (fno.fname[0] == 0 )){
        break;}
#if _USE_LFN  
      fn = *fno.lfname ?fno.lfname : fno.fname ;
#else
      fn = fno.fname;
#endif 
      //表示当前目录跳过
      if(*fn == '.'){continue;}
      //目录 递归读取
      if( fno.fattrib & AM_DIR ){
        //合成完整目录名
        sprintf(&File_ADDR[i], "/%s", fn); 
        //递归遍历
        res = scan_file(File_ADDR);
        //打开失败,跳出循环
        File_ADDR[i] = 0;
        if(res != FR_OK ){break;}
      }else{
        printf( "%s/%s\r\n" , File_ADDR  , fn);
      }
    }
  }
  else{
    printf("》错误 %d \n",res);
  }
  return res;
}

/**************************************************************
* @brief  
* FRESULT Scan_file_Check(const TCHAR*  File_ADDR)
* @param  
* 文件目录内容的查看
**************************************************************/
FRESULT Scan_file_Check(const TCHAR*  File_ADDR){
  BYTE      fpath[100];                  /* 保存当前扫描路径 */
  printf("\n********** file_路径测试 **********\r\n");
  strcpy(fpath,File_ADDR);
  return scan_file(fpath); 
}


建立SPI传输的 头文件 SPI_book.h

代码如下 :

#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 通讯配置信息 -------------------
#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 引脚配置
#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
 
/*通讯等待超时时间*/
#define  FLASH_SPI_TIMEOUT        ((uint32_t)0x6000)
#define  FLASH_SPI_LONG_TIMEOUT   ((uint32_t)(10*FLASH_SPI_TIMEOUT))

//信息输出
#define FLASH_ERROR(fmt,arg...)          printf("<<-FLASH-ERROR->> "fmt"\n",##arg)

//
#define  SPI_PAGE_SIZE   4096
#define  SPI_PAGE_Write_SIZE   256
 

//FLASH 指令
#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 

建立SPI传输的 头文件 SPI_book.c

代码如下 :

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


static __IO  uint32_t SPITimeout = FLASH_SPI_LONG_TIMEOUT;


/**
  * @brief  SPII/O配置
  * @param  无
  * @retval 无
  */
static void SPI_GPIO_Config(void){
  GPIO_InitTypeDef  GPIO_InitStructure;
  //使能与SPI 有关的时钟
  _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);
  
  //初始化CSS引脚,使能软件控制,所以直接设置为推挽输出
  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) 配置
  * @param  无
  * @retval 无
  */
static void SPI_Mode_Config(void){
  SPI_InitTypeDef  SPI_InitStructure;
  
  SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2 ; //波特率预分频值为 2
  SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge ;        //数据捕获于第二个时钟沿 
  SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;          //时钟悬空高
  SPI_InitStructure.SPI_CRCPolynomial = 0;             //不使用CRC功能,数值随便写
  SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;    //SPI 发送接收 8 位帧结构
  SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex ; //双线全双工
  SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;   //数据传输从 MSB 位开始
  SPI_InitStructure.SPI_Mode = SPI_Mode_Master ;       //设置为主 SPI
  SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;            //内部 NSS 信号有 SSI 位控制
  
  SPI_Init(_FLASH_SPIx , &SPI_InitStructure ); //写入配置到寄存器
  SPI_Cmd(_FLASH_SPIx , ENABLE);   //使能SPI
}

/**
  * @brief  void SPI_FLASH_Init(void) 初始化
  * @param  无
  * @retval 无
  */
void SPI_FLASH_Init(void){
  SPI_GPIO_Config();
  SPI_Mode_Config();
}

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//                         通讯建立操作 
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
/**
  * @brief  Basic management of the timeout situation.
  * @param  errorCode:错误代码,可以用来定位是哪个环节出错.
  * @retval 返回0,表示SPI读取失败.
  */
static  uint32_t SPI_TIMEOUT_UserCallback(uint8_t errorCode)
{
  /* Block communication and all processes */
  FLASH_ERROR("SPI 等待超时!errorCode = %d",errorCode);
  
  return 0;
}
/**
  * @brief  uint8_t SPI_FLASH_Send_Byte(uint8_t data) 初始化
  * @param  发送并且接收一个字节
  * @retval 无
  */
static uint8_t SPI_FLASH_Send_Byte(uint8_t data){
  SPITimeout = FLASH_SPI_TIMEOUT;
  //检查并等待至TX缓冲区
  while(SPI_I2S_GetFlagStatus(_FLASH_SPIx,SPI_I2S_FLAG_TXE) == RESET){//发送缓存空标志位
    if(SPITimeout--==0) {return SPI_TIMEOUT_UserCallback(0);}
  }
  //判断程序已经为空
  SPI_I2S_SendData(_FLASH_SPIx , data);
  //判断接受缓存非空 
  SPITimeout = FLASH_SPI_TIMEOUT;
  while(SPI_I2S_GetFlagStatus(_FLASH_SPIx,SPI_I2S_FLAG_RXNE) == RESET){//接受缓存非空标志位标志位
    if(SPITimeout--==0) {return SPI_TIMEOUT_UserCallback(1);}
  }
  //程序发送完毕.并且需要接收一个字节
  return SPI_I2S_ReceiveData(_FLASH_SPIx);
}

/**
  * @brief  uint8_t SPI_FLASH_Send_Byte(uint8_t data) 初始化
  * @param  发送并且接收一个字节
  * @retval 无
  */
static uint8_t SPI_FLASH_SendHalf_Byte(uint16_t Halfdata){
  SPITimeout = FLASH_SPI_TIMEOUT;
  //检查并等待至TX缓冲区
  while(SPI_I2S_GetFlagStatus(_FLASH_SPIx,SPI_I2S_FLAG_TXE) == RESET){//发送缓存空标志位
    if(SPITimeout--==0) {return SPI_TIMEOUT_UserCallback(2);}
  }
  //判断程序已经为空
  SPI_I2S_SendData(_FLASH_SPIx , Halfdata);
  //判断接受缓存非空 
  SPITimeout = FLASH_SPI_TIMEOUT;
  while(SPI_I2S_GetFlagStatus(_FLASH_SPIx,SPI_I2S_FLAG_RXNE) == RESET){//接受缓存非空标志位标志位
    if(SPITimeout--==0) {return SPI_TIMEOUT_UserCallback(3);}
  }
  //程序发送完毕.并且需要接收一个字节
  return SPI_I2S_ReceiveData(_FLASH_SPIx);
}

/**
  * @brief  uint32_t SPI_Read_ID(void)
  * @param  读取ID号
  * @retval  
  */
uint32_t SPI_Read_ID(void){
  uint32_t flash_id;
  //片选使能
  _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  读取ID号
  * @retval  
  */
//uint32_t SPI_Read_DeviceID(void){
//  uint32_t flash_id;
//  //片选使能
//  _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  写入使能
  * @retval  
  */
static void SPI_Write_Enable(void){
  //片选使能
  _FLASH_CSS_LOW();
  SPI_FLASH_Send_Byte(FLASH_SPI_WRITE_ENABLE);
  _FLASH_CSS_HIGH();  
}

/**
  * @brief  static void SPI_WaitForWriteEnd(void);
  * @param  //等待FLASH内部时序操作完成
  * @retval  
  */
static SPI_WaitForWriteEnd(void){
  uint8_t status_reg = 0;  //判断最低位S0 erse or write in progress
  // 片选指令
  _FLASH_CSS_LOW();
  SPI_FLASH_Send_Byte(FLASH_SPI_READ_STATUS);
  do{
    status_reg = SPI_FLASH_Send_Byte(FLASH_SPI_DUMMY); //想要读取数据需要继续发送
  }while((status_reg & 0x01)==1);  //校验最低位进行校验
  _FLASH_CSS_HIGH(); 
 
}
/** 
  * @brief  svoid SPI_Erase_Sector(uint32_t addr)
  * @param  擦除FLASH指定扇区
  * @retval  
  */
void SPI_Erase_Sector(uint32_t addr){
  SPI_Write_Enable();
  /* 擦除扇区 */
  /* 选择FLASH: CS低电平 */
  _FLASH_CSS_LOW();
  /* 发送扇区擦除指令*/
  SPI_FLASH_Send_Byte(FLASH_SPI_REASE_SECTOR);
  /*发送擦除扇区地址的高位*/
  SPI_FLASH_Send_Byte((addr & 0xFF0000) >> 16);
  /* 发送擦除扇区地址的中位 */
  SPI_FLASH_Send_Byte((addr & 0xFF00) >> 8);
  /* 发送擦除扇区地址的低位 */
  SPI_FLASH_Send_Byte(addr & 0xFF);
  /* 停止信号 FLASH: CS 高电平 */
  _FLASH_CSS_HIGH(); 
  /* 等待擦除完毕*/
  SPI_WaitForWriteEnd();
}
  
 /**
  * @brief  擦除FLASH扇区,整片擦除
  * @param  无
  * @retval 无
  */
void SPI_FLASH_BulkErase(void){
  //发送FLASH 写使能命令
  SPI_Write_Enable();
  //整块Erase
  //选择FLASH :CS 低电平
  _FLASH_CSS_LOW();
  SPI_FLASH_Send_Byte(FLASH_SPI_ChipErase);
  /* 停止信号 FLASH: CS 高电平 */
  _FLASH_CSS_HIGH(); 
  /* 等待擦除完毕*/
  SPI_WaitForWriteEnd();
}


//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//                         读写操作 
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  
/** 
  * @brief  void SPI_Read_Data(uint8_t *readBuffer , uint32_t addr ,uint32_t  numByteToRead ));
  * @param  读取FLASH的内容
  * @retval  
  */
void SPI_Read_Data(uint8_t *readBuffer , uint32_t addr ,uint16_t  numByteToRead ){
  //片选使能
  _FLASH_CSS_LOW();
  //发送地址
  /* 发送 读 指令 */
  SPI_FLASH_Send_Byte(FLASH_SPI_READ_DATA);
  /* 发送 读 地址高位 */
  SPI_FLASH_Send_Byte((addr>>16)&0xff);
  /* 发送 读 地址中位 */
  SPI_FLASH_Send_Byte((addr>>8)&0xff);
  /* 发送 读 地址低位 */
  SPI_FLASH_Send_Byte(addr&0xff);
  
//  if(numByteToRead > SPI_PAGE_SIZE){
//    numByteToRead = SPI_PAGE_SIZE;
//    printf("SPI_FLASH_PageWrite too large!\n");
//  }
  /* 读取数据 */
  while(numByteToRead--){
    /* 读取一个字节*/
    *readBuffer = SPI_FLASH_Send_Byte(FLASH_SPI_DUMMY);
    /* 指向下一个字节缓冲区 */
    readBuffer++;
  }
  /* 停止信号 FLASH: CS 高电平 */
  _FLASH_CSS_HIGH();
}

 


/** 
  * @brief  void SPI_Write_Data(uint8_t *readBuffer , uint32_t addr ,uint32_t  numByteToRead ));
  * @param  读取FLASH的内容
  * @retval  
  */
void SPI_Write_Data(uint8_t *writeBuffer , uint32_t addr ,uint16_t  numByteToRead ){
  SPI_Write_Enable();
  //片选使能
  _FLASH_CSS_LOW();
  /* 写页写指令*/
  SPI_FLASH_Send_Byte(FLASH_SPI_WRITE_DATA);
  /*发送写地址的高位*/
  SPI_FLASH_Send_Byte((addr&0xff0000)>>16);
  /*发送写地址的中位*/
  SPI_FLASH_Send_Byte((addr&0xff00)>>8);
  /*发送写地址的低位*/
  SPI_FLASH_Send_Byte(addr&0xff);
  
  if(numByteToRead > SPI_PAGE_SIZE){
    numByteToRead = SPI_PAGE_SIZE;
    printf("SPI_FLASH_PageWrite too large!\n");
  }
  //写入数据
  while(numByteToRead--){
    //发送当前要写入的字节数据
    SPI_FLASH_Send_Byte(*writeBuffer);
    //指向先亿字节数据
    writeBuffer++;
  }
  /* 停止信号 FLASH: CS 高电平 */
  _FLASH_CSS_HIGH();
  /* 等待写入完毕*/
  SPI_WaitForWriteEnd();
}

/** 
  * @brief  SPI_Write_Data(uint8_t *writeBuffer , uint32_t addr ,uint32_t  numByteToRead ){
  * @param  读取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;
  /*地址对应页的前方对齐数量*/
  count = SPI_PAGE_Write_SIZE - BufferAddr;
  /*当前页剩下的全部地址数量*/
  if(count >= numByteToWrite){
    //剩下的内容可以一行写完
     SPI_Write_Data(writeBuffer ,WriteAddr ,numByteToWrite );
     return;
  }
  SPI_Write_Data(writeBuffer ,WriteAddr ,(uint16_t)count );//分割写入单独页面
  temp = numByteToWrite -  count ;  //排除多余部分
  WriteAddr += count;
  writeBuffer+=count;
  NumOfPage = temp / SPI_PAGE_Write_SIZE ; //对大面积分割输入
  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  读取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){ //每隔256字节换行
      printf("\r\n ");
    }
     printf("0x%x ",readBuffer[i]);
  }
}

 
与所有使用到 GPIO 的外设一样,都要先把使用到的 GPIO 引脚模式初始化,配置好复 用功能。GPIO 初始化流程如下:
(1) 使用 GPIO_InitTypeDef定义 GPIO初始化结构体变量,以便下面用于存储 GPIO 配置;
(2) 调用库函数 RCC_APB2PeriphClockCmd 来使能 SPI 引脚使用的 GPIO 端口时钟。
(3) 向 GPIO 初始化结构体赋值,把 SCK/MOSI/MISO 引脚初始化成复用推挽模式。而 CS(NSS)引脚由于使用软件控制,我们把它配置为普通的推挽输出模式。
(4) 使用以上初始化结构体的配置,调用 GPIO_Init 函数向寄存器写入参数,完成 GPIO 的 初始化

SPI_FLASH_SendByte 函数实现了前面讲解的“SPI 通讯过程”: (1) 本函数中不包含 SPI 起始和停止信号,只是收发的主要过程,所以在调用本函数 前后要做好起始和停止信号的操作;
(2) 对 SPITimeout 变量赋值为宏 SPIT_FLAG_TIMEOUT。这个 SPITimeout 变量在下 面的 while 循环中每次循环减 1,该循环通过调用库函数 SPI_I2S_GetFlagStatus 检 测事件,若检测到事件,则进入通讯的下一阶段,若未检测到事件则停留在此处 一直检测,当检测 SPIT_FLAG_TIMEOUT 次都还没等待到事件则认为通讯失败, 调用的 SPI_TIMEOUT_UserCallback 输出调试信息,并退出通讯;
(3) 通过检测 TXE 标志,获取发送缓冲区的状态,若发送缓冲区为空,则表示可能存 在的上一个数据已经发送完毕;
(4) 等待至发送缓冲区为空后,调用库函数 SPI_I2S_SendData 把要发送的数据“byte” 写入到 SPI 的数据寄存器 DR,写入 SPI 数据寄存器的数据会存储到发送缓冲区, 由 SPI 外设发送出去;
(5) 写入完毕后等待 RXNE 事件,即接收缓冲区非空事件。由于 SPI 双线全双工模式 下 MOSI 与 MISO 数据传输是同步的(请对比“SPI 通讯过程”阅读),当接收缓冲 区非空时,表示上面的数据发送完毕,且接收缓冲区也收到新的数据;
(6) 等待至接收缓冲区非空时,通过调用库函数 SPI_I2S_ReceiveData 读取 SPI 的数据 寄存器 DR,就可以获取接收缓冲区中的新数据了。代码中使用关键字“return” 把接收到的这个数据作为 SPI_FLASH_SendByte 函数的返回值,所以我们可以看 到在下面定义的 SPI 接收数据函数 SPI_FLASH_ReadByte,它只是简单地调用了 SPI_FLASH_SendByte 函数发送数据“Dummy_Byte”,然后获取其返回值(因为 不关注发送的数据,所以此时的输入参数“Dummy_Byte”可以为任意值)。可以 这样做的原因是 SPI 的接收过程和发送过程实质是一样的,收发同步进行,关键 在于我们的上层应用中,关注的是发送还是接收的数据。

建立I2C模拟传输的 头文件 I2C_soft_book.h

代码如下 :

#ifndef  __I2C_SOFT_BOOK_H_
#define  __I2C_SOFT_BOOK_H_

#include "stm32f10x.h"
 
//---------------- 这里封装了 I2C 通讯配置信息 -------------------
 
#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		/* 写控制bit */
#define I2C_RD	1		/* 读控制bit */

//----------------器件地址--------------------
/* 
 * 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 */
 
/*读取数据的格式以及字符数量*/
#define   _I2C_Soft_PageSize      8
#define   _I2C_Soft_SIZE				256			  /* 24xx02总容量 */
/*I2C 存储地址*/
#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

建立I2C模拟传输的 头文件 I2C_soft_book.c

代码如下 :

#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;  //开漏输出
  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;  //开漏输出
  GPIO_Init(_Soft_I2C_SDA_PORT,&GPIO_InitStructure);
}

/**
  * @brief  I2C_EE_Init 程序初始化
  * @param  无
  * @retval 无
  */
static void I2C_Start(void){
  // 当SCL高电平时候SDA 出现一个下降沿编号位I2C 总线启动信号
   _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 程序初始化
  * @param  无
  * @retval 无
  */

static void I2C_Stop(void){
  // 当SCL高电平,SDA出现上升沿表示I2C总线停止信号
  _I2C_SDA_0();
  _I2C_SCL_1();
  fn_Systick_Delay(50,_Systick_us);
  _I2C_SDA_1(); 
}


/**
  * @brief  I2C_SendByte 程序初始化
  * @param  无
  * @retval 无
  */

static void I2C_SendByte(uint8_t _ucByte){
  uint8_t i;
  //发送字节的高位
  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;               //------注意这里不太一样
    fn_Systick_Delay(50,_Systick_us);
  }
  _I2C_SDA_1();// 释放总线
}

/**
  * @brief  I2C_ReadByte 程序初始化
  * @param  无
  * @retval 无
  */
static uint8_t I2C_ReadByte(void){
  uint8_t i;
  uint8_t value;
  
  //读到第1个bit 为数据的bit7
  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  无
  * @retval 无
  */

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  无
  * @retval 无
  */

static void I2C_ACK(void){
  _I2C_SDA_0();  //CPU 驱动SDA = 0;
  fn_Systick_Delay(50,_Systick_us);
  _I2C_SCL_1();  //CPU 产生1个时钟
  fn_Systick_Delay(50,_Systick_us);
  _I2C_SCL_0();
  fn_Systick_Delay(50,_Systick_us);
  _I2C_SDA_1(); //CPU 释放SDA总线
}



/**
  * @brief  I2C_ACK  
  * @param  无
  * @retval 无
  */

static void I2C_NACK(void){
  _I2C_SDA_1();  //CPU 驱动SDA = 1;
  fn_Systick_Delay(50,_Systick_us);
  _I2C_SCL_1();  //CPU 产生1个时钟
  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应答 */
  
  I2C_Stop(); /* 发送停止信号 */
  
  return ucAck;
}


//----------------------I2C 独写操作--------------------
//------------------------------------------------------

/**
  * @brief  EE_Soft_Check_State  
  * @param  判断串行EERPOM是否正常
  * @retval 无
  */

static uint8_t EE_Soft_Check_State(void){
  if(I2C_CheckDevice(Soft_EEPROM_ADDRESS)==0){return 1;}
  else{I2C_Stop(); /* 发送停止信号 */ return 0;}
}

/**
  * @brief  uint8_t I2C_Soft_BufferRead(uint8_t* pBuffer, 
  *      uint8_t ReadAddr, uint16_t NumByteToRead)
  * @param  判断串行EERPOM是否正常
  * @retval 无
  */
static uint8_t I2C_Soft_BufferRead(uint8_t* pBuffer, uint8_t ReadAddr, uint16_t NumByteToRead){
  uint16_t i ;
  //连续取得若干个字节
  // 发起I2C总线启动信号
  I2C_Start();
  //发送控制字节地址和读取数据信号
  I2C_SendByte(Soft_EEPROM_ADDRESS | I2C_WR);
  //等待应答状态
  if(I2C_WaitAck()!=0){printf("EEPROM 错误 1 !\r\n"); goto  CMD_Fail;}
  //发送数据读取位置信息信号
  I2C_SendByte((uint8_t)ReadAddr);
  //等待应答状态
  if(I2C_WaitAck()!=0){printf("EEPROM 错误 2 !\r\n");goto  CMD_Fail;}
  
  //--------------
  //重新启动I2C总线 
  I2C_Start();
  //发送器件地址
  I2C_SendByte(Soft_EEPROM_ADDRESS| I2C_RD);
  //等待应答状态
  if(I2C_WaitAck()!=0){printf("EEPROM 错误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  判断串行EERPOM是否正常
  * @retval 无
  */

static uint8_t EE_Soft_WriteBytes(uint8_t* pBuffer, uint8_t ReadAddr, uint16_t NumByteToRead){
  uint16_t i , m;
  uint16_t usAddr;
  /*
    写串行EEPROM 不像读操作可以连续读取很多字节每次写操作只能在同一个page
    对于24C page size = 8 
    简单的处理方法为 按字节写操作模式,写一个字节都发送地址
    为了提高连续写的效率 笨函数采用Page write操作
  */
  usAddr = ReadAddr;
  for(i=0 ;i<NumByteToRead;i++ ){
    // 当发送第一个字节或者页面首地址时,需要重新发起启动信号和地址
    if((i==0)||(usAddr)&(_I2C_Soft_PageSize-1)==0){
      // 发送停止信号
      I2C_Stop();
      //通过检测器判断内存写入是否成功
      m = 100;
      for (m = 0; m < 100; m++){	
        //启动I2C总线 
        I2C_Start();
        //发送器件地址
        I2C_SendByte(Soft_EEPROM_ADDRESS| I2C_WR);
        //等待应答状态
        if(I2C_WaitAck()==0){break;}
      }
      if(m==100){printf("EEPROM 错误 4 !\r\n"); goto CMD_FAIL_bytes ; }
      I2C_SendByte((uint8_t)usAddr);
      if(I2C_WaitAck()!=0){printf("EEPROM 错误 5 !\r\n"); goto CMD_FAIL_bytes;}
    }
    // 开始写入数据 
    I2C_SendByte(pBuffer[i]);
    //等待应答状态
    if(I2C_WaitAck()!=0){printf("EEPROM 错误 7 !\r\n"); goto CMD_FAIL_bytes;}
    usAddr++;
  }
  // 发送停止信号
  I2C_Stop();
  return 1;
  
CMD_FAIL_bytes:
  // 发送停止信号
  I2C_Stop();
  return 0;
}



/**
  * @brief  void  EE_Soft_Trase(void)  
  * @param  判断串行EERPOM是否正常
  * @retval 无
  */
void  EE_Soft_Trase(void){
  uint16_t i ;
  uint8_t buf[_I2C_Soft_SIZE]={0};
  
  // 填充缓冲区
  for(i=0 ;i<_I2C_Soft_SIZE ;i++ ){
    buf[i] = 0xFF;
  }
  //写EEPROM 起始地址= 0 数据长度为256
  if(EE_Soft_WriteBytes(buf,0,_I2C_Soft_SIZE)==0){
    printf("擦除EEPROM出错!\r\n");
		return;
  }else{
    printf("擦除EEPROM出错!\r\n");
  }
}


/**
  * @brief  void I2C_Soft_Init(void)
  * @param   
  * @retval 无
  */
void I2C_Soft_Init(void){
  if(EE_Soft_Check_State()==0){
      /* 没有检测到EEPROM */
      printf("没有检测到串行EEPROM!\r\n");
  }
}


/**
  * @brief  void I2C_Soft_Write_fun(uint8_t* pBuffer, uint8_t WriteAddr, uint16_t NumByteToWrite) 
  * @param  判断串行EERPOM是否正常
  * @retval 无
  */
uint8_t I2C_Soft_Write_fun(uint8_t* pBuffer, uint8_t WriteAddr, uint16_t NumByteToWrite){
  uint16_t i;
   
  //-------------------------------
  if(EE_Soft_Check_State()==0){
    /* 没有检测到EEPROM */
		printf("没有检测到串行EEPROM!\r\n");
    return 1;
  }  
  //------------写入I2C-------------------
  if(EE_Soft_WriteBytes(pBuffer,WriteAddr ,NumByteToWrite)==0){
    /* 没有检测到EEPROM */
		printf("写EEPROM错误!\r\n");
    return 1;
  }else{
    /* 没有检测到EEPROM */
		printf("写EEPROM成功!\r\n");
  }
  fn_Systick_Delay(150,_Systick_us);
  //--------------数据检查--------------
  printf("EEPROM写入数据检查检查\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;
  //-------------读取I2C------------------
  if(I2C_Soft_BufferRead(pBuffer,WriteAddr,NumByteToWrite)==0){
    /* 没有检测到EEPROM */
		printf("读EEPROM错误!\r\n");
    return 1;
  }else{
    /* 没有检测到EEPROM */
		printf("\n读EEPROM成功!\r\n");
  }
  //--------------数据检查--------------
  printf("EEPROM读取数据数据检查 \r\n");
  for(i=0 ;i<NumByteToWrite ;i++ ){
     printf(" %d ",pBuffer[i]);
    if((i & 15)==15){
      printf("\r\n");
    }
  }
  return 1;
}
/*********************END OF FILE**********************/

建立I2C硬件传输的 头文件 I2C_book.h

代码如下 :

#ifndef  __I2C_BOOK_H_
#define  __I2C_BOOK_H_

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

//---------------- 这里封装了 I2C 通讯配置信息 -------------------
#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 速度模式  */
#define   _I2C_Speed       400000
/* I2C 器件地址 */
#define   _I2Cx_OWN_ADDRESS7              0x5f
/*读取数据的格式以及字符数量*/
#define   _I2C_PageSize      8
/*I2C 存储地址*/
#define  EEP_Firstpage      0x90
#define  EEP_SIZE           0xFF
//----------------器件地址--------------------
/* 
 * 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 */

//----------------函数声明--------------------
 
//I2C 应用函数
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

建立I2C硬件传输的 头文件 I2C_book.c

代码如下 :

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

/**
  * @brief  I2C_EE_Init 程序初始化
  * @param  无
  * @retval 无
  */
static void I2C_GPIO_Config(void){
  GPIO_InitTypeDef   GPIO_InitStructure;
  //  初始化 I2C 相关时钟
  _EEPROM_I2C_APBxClock_FUN(_EEPROM_I2C_CLK,ENABLE);
  _EEPROM_I2C_GPIO_APBxClock_FUN(_EEPROM_I2C_GPIO_CLK,ENABLE);
  //  初始化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;  //开漏输出
  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;  //开漏输出
  GPIO_Init(_EEPROM_I2C_SDA_PORT,&GPIO_InitStructure);
}

/**
  * @brief  I2C_EE_Init 程序初始化
  * @param  无
  * @retval 无
  */
static void I2C_Mode_Config(void){
  I2C_InitTypeDef I2C_InitStructure;
  /* i2C 配置 */
  I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
  // 高电平数据稳定,低电平数据变化 SCL 时钟线的占空比
  I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
  
  I2C_InitStructure.I2C_OwnAddress1 = _I2Cx_OWN_ADDRESS7;
  
  I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
  
  //I2C 寻址模式
  I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
  //通讯频率
  I2C_InitStructure.I2C_ClockSpeed = _I2C_Speed;
  //I2C 初始化
  I2C_Init(_EEPROM_I2Cx,&I2C_InitStructure);
  //使能I2C
  I2C_Cmd(_EEPROM_I2Cx,ENABLE);
}
  
/**************************************/

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

/*通讯等待超时时间*/
#define  I2CT_FLAG_TIMEOUT  ((uint32_t)0x6000)
#define  I2CT_LONG_TIMEOUT  ((uint32_t)(10*I2CT_FLAG_TIMEOUT))
static uint16_t I2CTimeout;
/**************************************/
/**
* @brief  写一个字节到 I2C EEPROM 中
* @param  pBuffer:缓冲区指针
* @param  WriteAddr:写地址
* @retval 正常返回 1,异常返回 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);}
  }
 
  //产生I2C起始信号
  I2C_GenerateSTART(_EEPROM_I2Cx , ENABLE);
  I2CTimeout = I2CT_LONG_TIMEOUT; //这个变量是延时异常时间
  //检测EV5 事件并清除标识位
  while(!I2C_CheckEvent(_EEPROM_I2Cx,I2C_EVENT_MASTER_MODE_SELECT)){
    if((I2CTimeout--)==0){return I2C_TIMEOUT_UserCallback(5);}
  } 
  
  //发送EEPROM 设备地址
  I2C_Send7bitAddress(_EEPROM_I2Cx,EEPROM_ADDRESS,I2C_Direction_Transmitter);
  I2CTimeout = I2CT_LONG_TIMEOUT; //这个变量是延时异常时间
  //检测EV6 事件并清除标识位
  while(I2C_CheckEvent(_EEPROM_I2Cx,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) == ERROR){
    if((I2CTimeout--)==0){return I2C_TIMEOUT_UserCallback(6);}
  }
  
  //发送要写入的EEPROM 内部地址(即EEPROM内部存储其地址);
  I2C_SendData(_EEPROM_I2Cx,WriteAddr);
  I2CTimeout = I2CT_LONG_TIMEOUT;
  //检测EV8 事件清除标志位
  while(!I2C_CheckEvent(_EEPROM_I2Cx,I2C_EVENT_MASTER_BYTE_TRANSMITTED)){
    if((I2CTimeout--)==0){return I2C_TIMEOUT_UserCallback(2);}
  }
   
  //发送要写入的EEPROM 内部的数据;
  I2C_SendData(_EEPROM_I2Cx,*pBuffer);
  I2CTimeout = I2CT_LONG_TIMEOUT;
  //检测EV8 事件清除标志位
  while(!I2C_CheckEvent(_EEPROM_I2Cx,I2C_EVENT_MASTER_BYTE_TRANSMITTED)){
    if((I2CTimeout--)==0){return I2C_TIMEOUT_UserCallback(3);}
  }
  
  //发送要写入的EEPROM 内部的数据;
  I2C_SendData(_EEPROM_I2Cx,ENABLE);
  
  return 1;
}

 /**
  * @brief 将缓冲区中的数据写到 I2C EEPROM 中,采用单字节写入的方式,速度比页写入慢
  * @param pBuffer:缓冲区指针
  * @param WriteAddr:写地址
  * @param NumByteToWrite:写的字节数
  */

static void I2C_EE_WaitEepromStandbyState(void){
  vu16 SR1_Tmp = 0;
  do{
    //产生I2C起始信号
    I2C_GenerateSTART(_EEPROM_I2Cx,ENABLE);
    //读取I2C1 SR1 寄存器
    SR1_Tmp = I2C_ReadRegister(_EEPROM_I2Cx, I2C_Register_SR1);
    //发送EEPROM 地址+ 方向
    I2C_Send7bitAddress(_EEPROM_I2Cx,EEPROM_ADDRESS,I2C_Direction_Transmitter);
  }while(!(I2C_ReadRegister(_EEPROM_I2Cx, I2C_Register_SR1) & 0x0002));
  /* 清除 AF 位 */
  I2C_ClearFlag(_EEPROM_I2Cx, I2C_FLAG_AF);
  //发送停止位信号
  I2C_GenerateSTOP(_EEPROM_I2Cx , ENABLE);
}

//zuozuo04-30 

/**
* @brief 在 EEPROM 的一个写循环中可以写多个字节,但一次写入的字节数
* 不能超过 EEPROM 页的大小,AT24C02 每页有 8 个字节
* @param
* @param pBuffer:缓冲区指针
* @param WriteAddr:写地址
* @param NumByteToWrite:要写的字节数要求 NumByToWrite 小于页大小
* @retval 正常返回 1,异常返回 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);}
  }
  //产生I2C起始信号
  I2C_GenerateSTART(_EEPROM_I2Cx , ENABLE);
  I2CTimeout = I2CT_LONG_TIMEOUT; //这个变量是延时异常时间
  //检测EV5 事件并清除标识位
  while(!I2C_CheckEvent(_EEPROM_I2Cx,I2C_EVENT_MASTER_MODE_SELECT)){
    if((I2CTimeout--)==0){return I2C_TIMEOUT_UserCallback(5);}
  }
  
  //发送EEPROM 设备地址
  I2C_Send7bitAddress(_EEPROM_I2Cx,EEPROM_ADDRESS,I2C_Direction_Transmitter);
  I2CTimeout = I2CT_LONG_TIMEOUT; //这个变量是延时异常时间
  //检测EV6 事件并清除标识位
  while(I2C_CheckEvent(_EEPROM_I2Cx,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) == ERROR){
    if((I2CTimeout--)==0){return I2C_TIMEOUT_UserCallback(6);}
  }
  
  //发送要写入的EEPROM内部地址(EEPROM内部存储器地址)
  I2C_SendData(_EEPROM_I2Cx,WriteAddr);
  I2CTimeout = I2CT_LONG_TIMEOUT; //这个变量是延时异常时间
  //检测EV7 事件并清除标识位
  while(!I2C_CheckEvent(_EEPROM_I2Cx,I2C_EVENT_MASTER_BYTE_TRANSMITTING)){
    if((I2CTimeout--)==0){return I2C_TIMEOUT_UserCallback(7);}
  }

  //循环发送 NumByteToWrite个数据
  while(NumByteToWrite--){
    //发送缓冲区的数据
    I2C_SendData(_EEPROM_I2Cx,*pBuffer++);
    I2CTimeout = I2CT_FLAG_TIMEOUT; //这个变量是延时异常时间
    //检测EV8 事件并清除标识位
    while(!I2C_CheckEvent(_EEPROM_I2Cx,I2C_EVENT_MASTER_BYTE_TRANSMITTED)){
      if((I2CTimeout--)==0){return I2C_TIMEOUT_UserCallback(8);}
    }       
  }
  //发送停止信号
  I2C_GenerateSTOP(_EEPROM_I2Cx,ENABLE);
  return 1;
}



/**  快速写入一页
* @brief 将缓冲区中的数据写到 I2C EEPROM 中
* @param
* @arg pBuffer:缓冲区指针
* @arg WriteAddr:写地址
* @arg NumByteToWrite:写的字节数
* @retval 无
*/
#define I2C_PageSize 8  //AT24C01 02 每页有8个字节
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 求余运算,如果writeAddr 是 I2C_PageSize 整书倍,运算结果位Addr为0
  Addr = WriteAddr % I2C_PageSize;
  //差count个数值,刚好可以对齐到页面地址
  count = I2C_PageSize - Addr; 
  //计算出要写多少整书页
  NumOfPage = NumByteToWrite / I2C_PageSize;
  //mod运算求余计算出不满一页的字节数
  NumOfSingle = NumByteToWrite % I2C_PageSize;
  // Addr = 0,则WriteAddr 刚好按页对齐aligned
  // 这样就很简单了,直接写就可以写完整页后
  // 把剩下的不满一页的写完即可
  if(Addr == 0){
    //如果 NumByteToWrite < I2C_PageSize
    if(NumOfPage==0){
      I2C_EE_PageWrite(pBuffer , WriteAddr, NumOfSingle);
      I2C_EE_WaitEepromStandbyState();
    }//如果NumByteToWrite > I2C_PageSize
    else{
      //先把整数页写了
      while(NumOfPage--){
        I2C_EE_PageWrite(pBuffer , WriteAddr, I2C_PageSize);
        I2C_EE_WaitEepromStandbyState();
        WriteAddr += I2C_PageSize ;
        pBuffer += I2C_PageSize ;
      }
      //若有多余的不满一页的数据,把它写完
      if(NumOfSingle != 0){
        I2C_EE_PageWrite(pBuffer , WriteAddr, NumOfSingle);
        I2C_EE_WaitEepromStandbyState();
      }
    }
  }
  //如果 WriteAddr 不是按 I2C_PageSize 对齐
  //那就算出对齐到页地址还需要多少数据,然后先把这几个数据写完,剩下开始的地址就已经对齐
  //到页地址了,代码重复上面的即可
  else{
    //如果NumByteToWrite < I2C_PageSize
    if(NumOfPage == 0){
      //若NumOfSingle > count,当前面写不完,要写下一页
      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{//若count 比 NumOfSingle大
        I2C_EE_PageWrite(pBuffer , WriteAddr, NumByteToWrite);
        I2C_EE_WaitEepromStandbyState(); 
      }
    }
    //如果 NumByteToWrite > I2C_PageSize
    else{
      //地址不对齐多出的Count 分开处理,不加入这个运算
      NumByteToWrite -= count;
      NumOfPage = NumByteToWrite / I2C_PageSize ;
      NumOfSingle = NumByteToWrite % I2C_PageSize;
      //先把 WriteAddr 所在页的剩余字节写了
      if(count!=0){
        I2C_EE_PageWrite(pBuffer , WriteAddr, count);
        I2C_EE_WaitEepromStandbyState();
        //加上 count 后,地址就对齐到页了
        WriteAddr += count ;
        pBuffer += count ;
      }
      //把整页都写了
      while(NumOfPage--){
        I2C_EE_PageWrite(pBuffer , WriteAddr, I2C_PageSize);
        I2C_EE_WaitEepromStandbyState();
        WriteAddr += I2C_PageSize ;
        pBuffer += I2C_PageSize ;
      }
      //若多余的不满足一页,就把它写完
      if(NumOfSingle !=0){
        I2C_EE_PageWrite(pBuffer , WriteAddr, NumOfSingle);
        I2C_EE_WaitEepromStandbyState(); 
      }
    }
    
  }
}

/*  EEPROM 读取
* @brief 从 EEPROM 里面读取一块数据
* @param pBuffer:存放从 EEPROM 读取的数据的缓冲区指针
* @param ReadAddr:接收数据的 EEPROM 的地址
* @param NumByteToRead:要从 EEPROM 读取的字节数
* @retval 正常返回 1,异常返回 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);}
    }
    
    //产生I2C起始信号
    I2C_GenerateSTART(_EEPROM_I2Cx , ENABLE);
    I2CTimeout = I2CT_FLAG_TIMEOUT;
    //检测EV10 事件并清除标注
    while(! I2C_CheckEvent(_EEPROM_I2Cx , I2C_EVENT_MASTER_MODE_SELECT)){
      if((I2CTimeout--)==0){ return I2C_TIMEOUT_UserCallback(10);}
    }
    
    //发送EEPROM 设备地址
    I2C_Send7bitAddress(_EEPROM_I2Cx , EEPROM_ADDRESS , I2C_Direction_Transmitter);
    I2CTimeout = I2CT_FLAG_TIMEOUT;
    //检测EV11 事件并清除
    while(! I2C_CheckEvent(_EEPROM_I2Cx , I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)){
      if((I2CTimeout--)==0){ return I2C_TIMEOUT_UserCallback(11);}
    }
    
    //通过重新设置PE位清除EV12事件
    I2C_Cmd(_EEPROM_I2Cx ,ENABLE );
    //发送要读取的EEPROM内部地址(即EEPROM内部存储器地址)
    I2C_SendData(_EEPROM_I2Cx, ReadAddr);
    I2CTimeout = I2CT_FLAG_TIMEOUT ;
    //检测EV12 事件并清除
    while(! I2C_CheckEvent(_EEPROM_I2Cx , I2C_EVENT_MASTER_BYTE_TRANSMITTED)){
      if((I2CTimeout--)==0){ return I2C_TIMEOUT_UserCallback(12);}
    }
    
    //产生第二次I2C起始信号
    I2C_GenerateSTART(_EEPROM_I2Cx , ENABLE);
    I2CTimeout = I2CT_FLAG_TIMEOUT;
    //检测EV13 事件并清除
    while(! I2C_CheckEvent(_EEPROM_I2Cx ,  I2C_EVENT_MASTER_MODE_SELECT)){
      if((I2CTimeout--)==0){ return I2C_TIMEOUT_UserCallback(13);}
    }
    
    //发送EEPROM 设备地址
    I2C_Send7bitAddress(_EEPROM_I2Cx , EEPROM_ADDRESS , I2C_Direction_Receiver);
    I2CTimeout = I2CT_FLAG_TIMEOUT;
    //检测EV14 事件并清除
    while(! I2C_CheckEvent(_EEPROM_I2Cx , I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED)){
      if((I2CTimeout--)==0){ return I2C_TIMEOUT_UserCallback(14);}
    }
    
    //读取NumByteToRead个数据
    while(NumByteToRead){
      //如果 NumByteToRead=1,表示已经收到最后一个数据了
      //发送应答信号结束输出
      if(NumByteToRead == 1){
        //发送非应答信号
        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);}
      }
      
      //通过I2C,从设备中读取一个字节的数据
      *pBuffer = I2C_ReceiveData(_EEPROM_I2Cx);
      //存储数据的指针指以下地址
      pBuffer++;
      //接受数据自减
      NumByteToRead--;
    }
    //发送停止信号
        I2C_GenerateSTOP(_EEPROM_I2Cx , ENABLE);
    //使能大应,方便一下I2C输出
    I2C_AcknowledgeConfig(_EEPROM_I2Cx , ENABLE);
    return 1;
}

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

/**
* @brief void _I2C_EE_Init(void) 
* @param 无
* @retval 正常返回 1 ,不正常返回 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 无
* @retval 正常返回 1 ,不正常返回 0
*/
void I2C_Write_fun(uint8_t* pBuffer, uint8_t WriteAddr, uint16_t NumByteToWrite){
  u16 i;
  
  printf("I2C_写入数据 \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_写入数据完成 \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))读写测试
* @param 无
* @retval EEP_SIZE
*/
void I2C_Read_fun(uint8_t* pBuffer, uint8_t WriteAddr, uint16_t NumByteToWrite){
  u16 i;
  
  printf("I2C_数据检测 \n");
  I2C_EE_BufferRead(pBuffer,WriteAddr,NumByteToWrite);
  printf("\nI2C_数据读取完毕 \n");
  for(i=0 ;i<NumByteToWrite ;i++ ){
    printf("%d ", pBuffer[i]);
    if(i%16 == 15)    
        printf("\n\r");
  }
   printf("\n--->I2C_数据检测完成\n");
}
   

建立USART传输的 头文件 USART_book.h

代码如下 :

#ifndef  __USART_BOOK_H_
#define  __USART_BOOK_H_

#include "stm32f10x.h"
#include 
#include "stm32f10x_usart.h"
#include "stm32f10x_rcc.h"
 //串口的宏定义  不同的串口挂在的总线和IO不一样
 
 //串口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 引脚定义
#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

建立USART传输的C文件 USART_book.c

代码如下 :

#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  
* //串口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;
  /* 嵌套向量中断控制寄存器组选择*/
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
  
  /* 配置 USART 为中断源 */
  NVIC_InitStructure.NVIC_IRQChannel = _DEBUG_NVIC_USART_IRQ;
  /* 抢断优先级为 1 */
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
  /* 子优先级为 1 */
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
  /* 使能中断 */
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  /* 初始化配置 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  
* //串口1    
*    // USART  GPIO 引脚定义
*    #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;
  // 打开串口 GPIO 的时钟
  _DEBUG_USART_GPIO_APBxCLKCmd(_DEBUG_USART_GPIO_CLK , ENABLE);
  
//将USART TX 的GPIO配置为推挽模式
  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);
   //将USART RX 的GPIO配置为浮空输入
  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  
* //串口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;
 
  // 打开串口外设的时钟
  _DEBUG_USART_APBxClkCmd(_DEBUG_USART_CLK , ENABLE);
  
  //配置串口的工作参数
  USART_InitStructure.USART_BaudRate = _DEBUG_USART_BAUDRATE;
   //配置波特率
  USART_InitStructure.USART_WordLength = USART_WordLength_8b;
  // 配置 针数据字长
  USART_InitStructure.USART_StopBits = USART_StopBits_1;
  // 配置停止位
  USART_InitStructure.USART_Parity = USART_Parity_No;
  // 配置校验位
  USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
  // 配置硬件流控制
  USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx ;
  // 配置工作模式,收发一起
  
  USART_Init(_DEBUG_USARTx , &USART_InitStructure);// 完成串口的初始化配置
  
  NVIC_Configuration();// 串口中断优先级配置
  
  USART_ITConfig(_DEBUG_USARTx , USART_IT_RXNE , ENABLE);// 使能串口接收中断
  
  USART_Cmd(_DEBUG_USARTx , ENABLE);// 使能串口
}

/**************************************************************
* @brief  
* void fn_Usart_Send_Byte(USART_TypeDef * pUSARTx , uint8_t ch );
* @param  
* //串口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 ){
  /*发送一个字节数据到USART*/
  USART_SendData(pUSARTx , ch);
  /*等待发送数据寄存器为空*/
  while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TXE)==RESET);
}

/**************************************************************
* @brief  
* void fn_Usart_SendString(USART_TypeDef *pUSARTx , char * str);
* @param  
* //串口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');
  
  /*等待发送完成*/
  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);
  }
  /*等待发送完成*/
  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();
}

//须在 MDK 的工程选项把“Use MicroLIB”勾选上,MicoroLIB 是缺省 C 库的备选库,它对
//标准 C 库进行了高度优化使代码更少,占用更少资源。
/**************************************************************
* @brief  
* int fputc (int ch , FILE *f)
* @param  重新定向C库函数Printf 到USART1
* @retval 
*************************************************************/ 
int fputc (int ch , FILE *f){ 
  /*发送一个字节数据到USART*/
  USART_SendData(_DEBUG_USARTx , (uint8_t)ch);
  /*等待发送数据寄存器为空*/
  while(USART_GetFlagStatus(_DEBUG_USARTx,USART_FLAG_TXE)==RESET);
  return (ch);
}

/**************************************************************
* @brief  
* int fgetc(FILE *f);
* @param  重新定向C库函数Printf 到USART1
* @retval 
*************************************************************/  
int fgetc(FILE *f){
  //等待串口1输入数据
  while(USART_GetITStatus(_DEBUG_USARTx,USART_IT_RXNE)==RESET);
  return (int)USART_ReceiveData(_DEBUG_USARTx);
}

/**************************************************************
* @brief  
* void USART1_IRQHandler(void);  中断服务
* @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 );
  }
}

建立DMA传输的 头文件 DMA_book.h

代码如下 :

#ifndef  __DMA_BOOK_H_
#define  __DMA_BOOK_H_

#include "stm32f10x.h"

#define   DMA_CLOCK     RCC_AHBPeriph_DMA1    //DMA  时钟

/******  A   ****************** ROM 到 RAM 的DMA输出 *******************************/
#define   Map_DMA_CHANNEL     DMA1_Channel6    // 当使用存储器到存储器模式时候,通道可以随便选,没有硬性的规定
#define   Map_BUFFER_SIZE     20             // 要发送的数据大小
#define   DMA_FLAG_TC   DMA1_FLAG_TC6         // 传输完成标志
/* 定义 aSRC_Const_Buffer 数组作为 DMA 传输数据源
* const 关键字将 aSRC_Const_Buffer 数组变量定义为常量类型
* 表示数据存储在内部的 FLASH 中*/
extern  const uint32_t  aSRC_Cont_Buffer[Map_BUFFER_SIZE] ;
 /* 定义 DMA 传输目标存储器存储在内部的 SRAM 中*/
extern    uint32_t aDST_Buffer[Map_BUFFER_SIZE];
/*************************************************************************************/

/******** B   **************** USART 到 RAM 的DMA输出 *******************************/
#define   USART_DMA_CHANNEL     DMA1_Channel4         //串口对应的 DMA 请求通道 
#define   USART_Source_ADDR     (USART1_BASE+0x04)    //串口数据的地址
extern    uint32_t              USART_BUFFER_SIZE ;   // 要发送的数据大小
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对内存ROM数据的取出
void _DMA_ROM_TO_RAM(uint32_t _BUFFER_SIZE , uint32_t  _DMA_Source_ADDR,  uint32_t  _DMA_AIM_ADDR );
//DMA对RAM到USART数据的取出
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)
//  ROM 到 RAM 的DMA输出  的程序初始化   DMA_DIR_PeripheralSRC:为方向外设到内存
#define   _USART_DMA_Config_   _USART_DMA_Config(USART_DMA_CHANNEL ,USART_BUFFER_SIZE ,USART_Source_ADDR , USART_DMA_Buffer , DMA_DIR_PeripheralDST)
//  ROM 到 RAM 的DMA输出  的程序初始化  DMA_DIR_PeripheralDST:为方向外设到内存
#define   _DMA_InnerChange_    _Buffercmp(aSRC_Cont_Buffer , aDST_Buffer, Map_BUFFER_SIZE)
//  ROM 到 RAM 的DMA输出  的数据验证
#define   _RMA_InnerShow_      _Buffer_Show(aDST_Buffer, Map_BUFFER_SIZE)

#endif

建立DMA传输的C文件 DMA_book.c

代码如下 :

#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 ;
  //开启DMA时钟
  RCC_AHBPeriphClockCmd(DMA_CLOCK,ENABLE);
  //源数据缓存地址(外设地址)
  DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)_DMA_Source_ADDR ; 
  //转换缓存地址地址(内存地址)
  DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)_DMA_AIM_ADDR;
  
  //方向:外设到存储器(这里的外设是内部的FLASH)DMA_DIR_PeripheralSRC:为方向外设到内存 DMA_DIR_PeripheralDST:为方向外设到内存
  DMA_InitStructure.DMA_DIR = _DMA_DIR ;
  //传输大小
  DMA_InitStructure.DMA_BufferSize = _BUFFER_SIZE;
  //外设(内部的FLASH)地址递增
  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable;
  //内存地址递增
  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
  //外设数据单位
  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;
  //内存数据单位
  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;
  //DMA模式,一次或者循环模式
  DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
  //DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
  //优先级:高
  DMA_InitStructure.DMA_Priority = DMA_Priority_High;
  //使能内存到内存的传输
  DMA_InitStructure.DMA_M2M = DMA_M2M_Enable;
  //配置DMA通道
  DMA_Init(_DMAy_Channelx , &DMA_InitStructure);
  //使能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 ;
  //开启DMA时钟
  RCC_AHBPeriphClockCmd(DMA_CLOCK,ENABLE);  
  //源数据缓存地址(外设地址)
  DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)_DMA_Source_ADDR ; 
  //转换缓存地址地址(内存地址)
  DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)_DMA_AIM_ADDR;
  
  //方向:外设到存储器(这里的外设是内部的FLASH)DMA_DIR_PeripheralSRC:为方向外设到内存 DMA_DIR_PeripheralDST:为方向外设到内存
  DMA_InitStructure.DMA_DIR = _DMA_DIR ;
  //传输大小
  DMA_InitStructure.DMA_BufferSize = _BUFFER_SIZE;
  //外设(内部的FLASH)地址递增
  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
  //内存地址递增
  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
  //外设数据单位
  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;//DMA_PeripheralDataSize_Byte; //注意这里需要根据数据类型经行修改
  //内存数据单位
  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;//DMA_MemoryDataSize_Byte; //注意这里需要根据数据类型经行修改
  //DMA模式,一次或者循环模式
  DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
  //DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
  //优先级:高
  DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;
  //使能内存到内存的传输
  DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
  //配置DMA通道
  DMA_Init(_DMAy_Channelx , &DMA_InitStructure);
  //使能DMA
  DMA_Cmd(_DMAy_Channelx , ENABLE);
}
 
///源数据与目标地址数据对比
uint8_t  _Buffercmp(const uint32_t *pBuffer, uint32_t * pBuffer1 , uint16_t BufferLength){
  /*数据长度递减*/
  while(BufferLength--){
//  Usart_SendHalf_32_Word(_DEBUG_USARTx,*pBuffer);  
//  Usart_SendHalf_32_Word(_DEBUG_USARTx,*pBuffer1);
    /*判断两个数据源是否相等*/
    if(*pBuffer != *pBuffer1){
      /* 对应数据源不相等马上退出函数,并返回 0 */
      return 0;
    }
    /* 递增两个数据源的地址指针 */
    pBuffer++;
    pBuffer1++;
  }
  /* 完成判断并且对应数据相对 */
  return 1;
}


//对RAM数据进行展示
void  _Buffer_Show(uint32_t * pBuffer , uint16_t BufferLength){
  /*数据长度递减*/
  while(BufferLength--){
    Usart_SendHalf_32_Word(_DEBUG_USARTx,*pBuffer++);  
  }
}

//DMA对内存ROM数据的取出
void _DMA_ROM_TO_RAM(uint32_t _BUFFER_SIZE , uint32_t  _DMA_Source_ADDR,  uint32_t  _DMA_AIM_ADDR ){
  //----------------------------------------------------------------
  printf("开始 ROM内存到RAM内存的DMA操作 \n");
  //内存到内存DMA初始化
  _DMA_Config(Map_DMA_CHANNEL ,_BUFFER_SIZE ,_DMA_Source_ADDR , _DMA_AIM_ADDR , DMA_DIR_PeripheralSRC);       
  while(DMA_GetFlagStatus(DMA_FLAG_TC) == RESET);  //判断DMA传输结果是否正确          
  if(_DMA_InnerChange_== 0 ){
      printf("ROM内存到DMA操作异常 \n");  
  }else{
      printf("ROM内存到DMA操作正常 \n");      
  }  
  _RMA_InnerShow_;
}

//DMA对RAM到USART数据的取出
void _DMA_RAM_TO_USART(uint32_t _BUFFER_SIZE , uint32_t  _DMA_Source_ADDR,  uint32_t  _DMA_AIM_ADDR ){
  // 开始 USART内存到RAM内存的DMA操作 
  printf("\n开始 ROM到USART的传送初始化\n"); 
  USART_BUFFER_SIZE = _BUFFER_SIZE;
  USART_DMA_Buffer = _DMA_AIM_ADDR;
  //内存到USART DMA初始化
  _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);  //串口DMA使能
  /*USART_DMACmd 函数用于控制 USART 的 DMA 请求的启动和关闭。它接收三个参
  数,第一个参数用于设置串口外设,可以是 USART1/2/3 和 UART4/5 这 5 个参数可选,第
  二个参数设置串口的具体 DMA 请求,有串口发送请求 USART_DMAReq_Tx 和接收请求
  USART_DMAReq_Rx 可选,第三个参数用于设置启动请求 ENABLE 或者关闭请求*/
  
  fn_Systick_Delay(250,_Systick_ms);   //DMA 传输进程中进行LED输出闪烁  
  while(USART_GetFlagStatus(_DEBUG_USARTx,USART_FLAG_TXE)==RESET);
  printf("\rROM内存到USART外设的DMA操作完毕\n");//这个函数需要Delay 一段时间才可以用
}
 
//----------------------------------------------------------------


建立EXIT的 头文件 Exit_book.h

代码如下 :

#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

建立EXIT的C文件 Exit_book.c

代码如下 :

#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);  
  //注意:我们除了开 GPIO 的端口时钟外,我们还打开了 AFIO 的时钟
  GPIO_EXTILineConfig(_EXTI_IN_GPIO_PortSource | _EXTI_IN2_GPIO_PortSource , _EXTI_IN_GPIO_PinSource | _EXTI_IN2_GPIO_PinSource);
  /* 选择 EXTI 的信号源 */
  // GPIO_EXTILineConfig 函数用来指定中断/事件线的输入源,它实际是设定外部中断配
  // 置寄存器的 AFIO_EXTICRx 值,该函数接收两个参数,第一个参数指定 GPIO 端口源,第
  // 二个参数为选择对应 GPIO 引脚源编号。
  
  
  EXIT_InitStruck.EXTI_Line = _EXTI_IN_EXTI_Line ; /* 选择 EXTI 的信号源 */
  EXIT_InitStruck.EXTI_Mode = _EXTI_IN_EXTI_Mode;   /* EXTI 为中断模式 */
  EXIT_InitStruck.EXTI_Trigger = _EXTI_IN_EXTI_Trigger ; /* 上升沿中断 */
  EXIT_InitStruck.EXTI_LineCmd = _EXTI_IN_EXTI_LineCmd; /* 使能中断 */
  EXTI_Init(&EXIT_InitStruck);
  //  EXTI初始化配置的变量
  //  fn_NVIC_Config();
  //  调用 NVIC_Configuration函数完成对按键 1、按键 2 优先级配置并使能中断通道
  
  EXIT_InitStruck.EXTI_Line = _EXTI_IN2_EXTI_Line; /* 选择 EXTI 的信号源 */
  EXIT_InitStruck.EXTI_Mode = _EXTI_IN2_EXTI_Mode;   /* EXTI 为中断模式 */
  EXIT_InitStruck.EXTI_Trigger = _EXTI_IN2_EXTI_Trigger; /* 下降沿中断 */
  EXIT_InitStruck.EXTI_LineCmd = _EXTI_IN_EXTI_LineCmd;/* 使能中断 */
  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;
  /* 配置 NVIC 为优先级组 1 */
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
  
  /* 配置中断源:  */
  NVIC_InitStruct.NVIC_IRQChannel = _NVIC_IN_EXTI_IRQChannel; //EXTI0_IRQn;
  /* 配置抢占优先级:1 */
  NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;
  /* 配置子优先级:1 */
  NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;
  /* 使能中断通道 */
  NVIC_InitStruct.NVIC_IRQChannelCmd = _NVIC_IN_EXTI_IRQChannelCmd; //ENABLE
  NVIC_Init(&NVIC_InitStruct);
  
  /* 配置中断源:  */
  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_GetITStatus 函数用来获取 EXTI 的中断标志位状态,如果 EXTI 线有中断发生函
//数返回“SET”否则返回“RESET”。实际上,EXTI_GetITStatus 函数是通过读取
//EXTI_PR寄存器值来判断 EXTI线状态的。
  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);  // 重要的清除中断标志位 
}


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);  // 重要的清除中断标志位 
}
 

建立Key传输的 头文件 Key_book.h

代码如下 :

#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   //浮空输入


#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   //浮空输入


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

建立Key的C文件 Key_book.c

代码如下 :


#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   //浮空输入
*     
*     #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   //浮空输入
* @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();
}


建立LED 的头文件 Led_book.h

代码如下 :

#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


建立LED 的 文件 Led_book.c

代码如下 :

#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);		 
// }	 


建立 Systick传输的 头文件 Systick_book.h

代码如下 :

#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


建立 Systick的C文件 Systick_book.c

代码如下 :

#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();
}

建立 头文件函数 头文件 PROJ_book.h

代码如下 :

#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

你可能感兴趣的:(STM32,STM32)