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

完整代码 :Git 完整代码下载

文章目录

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

前言

FatFs 文件系统 FatFs 是面向小型嵌入式系统的一种通用的 FAT 文件系统。它完全是由 ANSI C 语言编 写并且完全独立于底层的 I/O 介质。因此它可以很容易地不加修改地移植到其他的处理器


FatFs 组件是 FatFs 的主体,文件都在源码 src 文件夹中,其中 ff.c、ff.h、integer.h 以及
diskio.h 四个文件我们不需要改动,只需要修改 ffconf.h 和 diskio.c 两个文件。

一、 编程要点

底层设备驱动函数是存放在 diskio.c 文件,我们的目的就是把 diskio.c 中的函数接口与 SPI Flash 芯片驱动连接起来。总共有五个函数,分别为设备状态获取(disk_status)、设备初 始化(disk_initialize)、扇区读取(disk_read)、扇区写入(disk_write)、其他控制(disk_ioctl)。

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

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

读取扇区:disk_read 函数有四个形参。pdrv 为设备物理编号。buff 是一个 BYTE 类型指针变量,
buff指向用来存放读取到数据的存储区首地址。sector 是一个 DWORD 类型变量,指定要读
取数据的扇区首地址。count 是一个 UINT 类型变量,指定扇区数量

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

Disk_ioctl 函数有三个形参,pdrv 为设备物理编号,cmd 为控制指令,包括发出同步信号、获取扇区数目、获取扇区大小、获取擦除块数量等等指令,buff 为指令对应的数据指针。

二、使用步骤

–理解原理图

(注意)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);#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********** 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();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 delay(int x){int y = 0xFFFFF;while((x--)>0){while((y--)>0){__NOP();__NOP();__NOP();__NOP();__NOP();}}
}
/******************* (C) COPYRIGHT 2011 STMicroelectronics *****END OF FILE****/

整改 系统文件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 = 5MBcase 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;
}
#endifDWORD 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 ;
#elsefn = 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        0xC7void 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 SCKGPIO_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 ; //波特率预分频值为 2SPI_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 ;       //设置为主 SPISPI_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 typeIDflash_id<<=8;flash_id|=SPI_FLASH_Send_Byte(FLASH_SPI_DUMMY);//Capcity typeIDflash_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      0x90void  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 为数据的bit7value = 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 数据长度为256if(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 SDAGPIO_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);//使能I2CI2C_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为0Addr = 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_PageSizeif(NumOfPage==0){I2C_EE_PageWrite(pBuffer , WriteAddr, NumOfSingle);I2C_EE_WaitEepromStandbyState();}//如果NumByteToWrite > I2C_PageSizeelse{//先把整数页写了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_PageSizeif(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_PageSizeelse{//地址不对齐多出的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_IRQHandlervoid  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);//使能DMADMA_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);//使能DMADMA_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  ENABLEvoid  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; //ENABLENVIC_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

2021-08-11 TM32F103 Buffer FatFs 文件系统移植相关推荐

  1. 纯Go实现的Firebase的替代品 | Gopher Daily (2021.08.11) ʕ◔ϖ◔ʔ

    每日一谚:Global variables should have longer names. Go技术生态 如何才能成功将Python切换到Go - https://itnext.io/opinio ...

  2. elm FatFs文件系统移植总结

    1 前言 本文将根据我的一些理解,针对elm FatFs文件系统做一个初步总结. 2 elm FatFs文件系统介绍 顾名思义FatFs文件系统就是针对FAT文件系统来的,主要是应用于MCU中,STM ...

  3. 模拟SPI进行TF卡操作+Fatfs文件系统移植

    FATFS版本:FATFS R0.13b SD卡容量:16G 概述 本文的重点是进行Fatfs文件系统的移植和初步的使用.TF卡的操作实际上是指令操作,即你想它发送固定的CMD指令,它接收到指令给你返 ...

  4. STM32+雷龙SD NAND(贴片SD卡)完成FATFS文件系统移植与测试

    一.前言 在STM32项目开发中,经常会用到存储芯片存储数据. 比如:关机时保存机器运行过程中的状态数据,上电再从存储芯片里读取数据恢复:在存储芯片里也会存放很多资源文件.比如,开机音乐,界面上的菜单 ...

  5. FatFs文件系统移植过程及中度分析

    FatFs 是一个通用的文件系统(FAT/exFAT)模块,用于在小型嵌入式系统中实现FAT文件系统. FatFs 组件的编写遵循ANSI C(C89),完全分离于磁盘 I/O 层,因此不依赖于硬件平 ...

  6. 基于STM32采用CS创世 SD NAND(贴片SD卡)完成FATFS文件系统移植与测试

    一.前言 在STM32项目开发中,经常会用到存储芯片存储数据. 比如:关机时保存机器运行过程中的状态数据,上电再从存储芯片里读取数据恢复:在存储芯片里也会存放很多资源文件.比如,开机音乐,界面上的菜单 ...

  7. 基于stm32、spi协议的Fatfs文件系统移植(附完整代码下载)

    开发环境:Window 7 32bit 开发工具:Keil uVision4 硬件:stm32f103vct6 目录 1.硬件设计: 2.软件设计 1.SPI收发数据 2.向SD卡发送的命令格式: 3 ...

  8. 基于STM32采用CS创世 SD NAND(贴片SD卡)完成FATFS文件系统移植与测试(中篇)

    3.2 SPI硬件时序方式 上面的3.1小节是采用SPI模拟时序驱动SD NAND,STM32本身集成有SPI硬件模块,可以直接利用STM32硬件SPI接口读写. 下面贴出底层的适配代码. 上面贴出的 ...

  9. 【FatFs】基于STM32 SD卡移植FatFs文件系统

    相关文章 <[SDIO]SDIO.SD卡.FatFs文件系统相关文章索引> 1.前言 FatFs是一个通用的FAT/exFAT文件系统模块,用于小型嵌入式系统.它完全是由 ANSI C 语 ...

最新文章

  1. linux useradd
  2. Resin3.19 连接池配置
  3. python返回类型为anytype_python判断变量类型时,为什么不推荐使用type()方法
  4. 使用 Pandas 的 to_excel() 方法来将多个 csv 文件合并到一个 xlsx 的不同 sheets 内
  5. Java基础知识之方法的返回值与重载
  6. hdu2066一个人的旅行(disjkstra)
  7. ORACLE 11R2 RAC EM 安装日记
  8. 【学习】从HttpClient3迁移到HttpClient4
  9. 计算机图形学中的抗锯齿
  10. Ubuntu设置自动登录
  11. 问题 B: 不同出栈情况(栈和队列)
  12. ubuntu 18.04 安装caffe
  13. 北京房租大涨?6个维度,数万条数据帮你揭穿(附详情代码)
  14. AtCoder ABC 250 总结
  15. HTML实现九九乘法表
  16. 前端鸡汤奉上-好好解解腻
  17. 实现一个app的签到功能,没你想的那么复杂!
  18. 《首先,打破一切常规》笔记(转)
  19. npm i 的几种方式区别
  20. 【论文速递】9位院士Science88页长文:人工智能的进展、挑战与未来

热门文章

  1. 苹果7服务器是什么系统版本,最新系统 iOS14.7 Beta1 版本推出!
  2. python数据处理工具-Pandas笔记
  3. 面试:MySQL篇,详尽知识点总结
  4. 基于ASP.NET的电商系统的设计与实现
  5. Double S 曲线轨迹规划——不同速度加速度条件下综合
  6. 中亦安图oracle培训,【中亦安图】Systemstate Dump分析经典案例(8)
  7. 面试题:在浏览器输入 URL 回车之后发生了什么?(超详细版)
  8. 阿里云两分钟一键极速搭建个人网盘
  9. Redis集群端口等
  10. 老毛桃u盘重装win7教程