IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> 2021-08-13 TM32F103 SRAM 内存扩展管理 -> 正文阅读

[嵌入式]2021-08-13 TM32F103 SRAM 内存扩展管理

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

完整代码 : Git源码下载

在这里插入图片描述


前言

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

在这里插入图片描述
在这里插入图片描述

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

在这里插入图片描述
在这里插入图片描述

图中左侧的是 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 信号线会输出控制信号
在这里插入图片描述
在这里插入图片描述

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

一、 编程要点

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

在这里插入图片描述

二、使用步骤

–理解原理图

在这里插入图片描述

(注意)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 <stdio.h>
#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

在这里插入图片描述

  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2021-08-14 14:14:35  更:2021-08-14 14:18:07 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/11 2:59:50-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码