ソースを参照

nand_flash\fatfs等驱动添加

樊春春 2 年 前
コミット
4dc59e4b5c
50 ファイル変更2926 行追加244 行削除
  1. 4 10
      User/Bsp/armfly_bsp.c
  2. 2 0
      User/Bsp/armfly_bsp.h
  3. 1 0
      User/Bsp/dwt/dwt.c
  4. 20 3
      User/Bsp/interface/interface.c
  5. 9 0
      User/Bsp/interface/interface.h
  6. 1632 0
      User/Bsp/nand_flash/nand_flash.c
  7. 208 0
      User/Bsp/nand_flash/nand_flash.h
  8. 0 2
      User/Bsp/rtc/rtc.c
  9. 214 0
      User/Bsp/uart/uart6.c
  10. 20 0
      User/Bsp/uart/uart6.h
  11. 178 0
      User/app/esp8266/esp8266.c
  12. 28 0
      User/app/esp8266/esp8266.h
  13. 3 2
      User/app/fly_param.c
  14. 4 3
      User/app/fly_param.h
  15. 21 80
      User/app/key/key.c
  16. 8 8
      User/app/key/key.h
  17. 0 21
      User/app/led/led.c
  18. 0 8
      User/app/led/led.h
  19. 99 0
      User/conf/fatfs/bsp_fatfs.c
  20. 9 0
      User/conf/fatfs/bsp_fatfs.h
  21. 338 0
      User/conf/fatfs/diskio.c
  22. 33 56
      User/conf/fatfs/ffconf.h
  23. 0 0
      User/conf/lwip/lwip_dm9k.c
  24. 0 0
      User/conf/lwip/lwip_dm9k.h
  25. 0 0
      User/conf/lwip/lwip_eth.c
  26. 0 0
      User/conf/lwip/lwip_eth.h
  27. 0 0
      User/conf/lwip/lwip_init.c
  28. 0 0
      User/conf/lwip/lwip_init.h
  29. 0 0
      User/conf/lwip/lwipopts.h
  30. 0 0
      User/conf/lwip/port/arch/bpstruct.h
  31. 0 0
      User/conf/lwip/port/arch/cc.h
  32. 0 0
      User/conf/lwip/port/arch/epstruct.h
  33. 0 0
      User/conf/lwip/port/arch/init.h
  34. 0 0
      User/conf/lwip/port/arch/lib.h
  35. 0 0
      User/conf/lwip/port/arch/lwip_cpu.h
  36. 0 0
      User/conf/lwip/port/arch/perf.h
  37. 0 0
      User/conf/lwip/port/arch/sys_arch.c
  38. 0 0
      User/conf/lwip/port/arch/sys_arch.h
  39. 0 0
      User/conf/lwip/port/ethernetif_dm9k.c
  40. 0 0
      User/conf/lwip/port/ethernetif_dm9k.h
  41. 0 0
      User/conf/lwip/port/ethernetif_eth.c
  42. 0 0
      User/conf/lwip/port/ethernetif_eth.h
  43. 0 0
      User/conf/ucos/app_hooks.c
  44. 0 0
      User/conf/ucos/cpu_cfg.h
  45. 0 0
      User/conf/ucos/includes.h
  46. 0 0
      User/conf/ucos/lib_cfg.h
  47. 0 0
      User/conf/ucos/os_cfg.h
  48. 65 37
      User/main.c
  49. 4 1
      User/main.h
  50. 26 13
      platformio.ini

+ 4 - 10
User/Bsp/armfly_bsp.c

@@ -1,11 +1,3 @@
-/*
- * @Author: 樊春春 [email protected]
- * @Date: 2022-07-06 19:12:52
- * @LastEditors: 樊春春 [email protected]
- * @LastEditTime: 2022-08-08 19:56:29
- * @FilePath: /ArmFly/User/bsp/armfly_bsp.c
- * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
- */
 #include "armfly_bsp.h"
 
 void bsp_init(void)
@@ -27,10 +19,12 @@ void bsp_init(void)
     spi1_init();
     uart1_init();
     uart3_init();
-    nor_flash_init();
-    lwip_setup();
     LCD_Init();
     key_init();
+    uart6_init();
+    nor_flash_init();
+    fatfs_init();
+    lwip_setup();
     // can初始化
     can_network_init();
 }

+ 2 - 0
User/Bsp/armfly_bsp.h

@@ -3,6 +3,7 @@
 
 #include "ad7606.h"
 #include "am2303.h"
+#include "bsp_fatfs.h"
 #include "can.h"
 #include "dwt.h"
 #include "fly_param.h"
@@ -17,5 +18,6 @@
 #include "timer.h"
 #include "uart1.h"
 #include "uart3.h"
+#include "uart6.h"
 
 #endif

+ 1 - 0
User/Bsp/dwt/dwt.c

@@ -15,6 +15,7 @@
 *
 *********************************************************************************************************
 */
+#include "dwt.h"
 #include "bsp.h"
 
 /*

+ 20 - 3
User/Bsp/interface/interface.c

@@ -21,6 +21,8 @@ Gpio_Clock clock_info[] = {
     {.type = kSPI1, .RCC_AXBPeriph = RCC_APB2Periph, .AF_Clock = RCC_APB2Periph_SPI1},
     {.type = kUart1, .RCC_AXBPeriph = RCC_APB2Periph, .AF_Clock = RCC_APB2Periph_USART1},
     {.type = kUart3, .RCC_AXBPeriph = RCC_APB1Periph, .AF_Clock = RCC_APB1Periph_USART3},
+    {.type = kSDIO, .RCC_AXBPeriph = RCC_APB2Periph, .AF_Clock = RCC_APB2Periph_SDIO},
+    {.type = kSDIO, .RCC_AXBPeriph = RCC_AHB1Periph, .AF_Clock = RCC_AHB1Periph_DMA2},
     {.type = kCAN1, .RCC_AXBPeriph = RCC_APB1Periph, .AF_Clock = RCC_APB1Periph_CAN1},
     {.type = kCAN2, .RCC_AXBPeriph = RCC_APB1Periph, .AF_Clock = RCC_APB1Periph_CAN2},
     {.type = kEthernet_dm9101, .RCC_AXBPeriph = RCC_AHB1Periph, .AF_Clock = RCC_AHB1Periph_ETH_MAC},
@@ -28,6 +30,7 @@ Gpio_Clock clock_info[] = {
     {.type = kEthernet_dm9101, .RCC_AXBPeriph = RCC_AHB1Periph, .AF_Clock = RCC_AHB1Periph_ETH_MAC_Rx},
     {.type = kEthernet_dm9101, .RCC_AXBPeriph = RCC_AHB1Periph, .AF_Clock = RCC_AHB1Periph_ETH_MAC_PTP},
     {.type = kFSMC, .RCC_AXBPeriph = RCC_AHB3Periph, .AF_Clock = RCC_AHB3Periph_FSMC},
+    {.type = kUSB, .RCC_AXBPeriph = RCC_AHB1Periph, .AF_Clock = RCC_AHB1Periph_OTG_HS},
     // {.type = kPMU, .AXBPeriph_Clock = RCU_PMU},
     {.type = kDMA1, .RCC_AXBPeriph = RCC_AHB1Periph, .AF_Clock = RCC_AHB1Periph_DMA1},
     // //    {.type = kTIMER1,   .AXBPeriph_Clock = RCU_TIMER1},
@@ -74,10 +77,18 @@ Interface_struct interface_info[] = {
     {.type = kUart3, .GPIOx = GPIOB, .GPIO_Pin = GPIO_Pin_11, .GPIO_Mode = GPIO_Mode_AF, .AF_Info = {.GPIOx = GPIOB, .GPIO_PinSource = GPIO_PinSource11, .GPIO_AF = GPIO_AF_USART3}, .GPIO_OType = GPIO_OType_PP, .GPIO_Speed = GPIO_Speed_100MHz, .GPIO_PuPd = GPIO_PuPd_UP},
     {.type = kUart3, .GPIOx = UART3_ENABLE_PORT, .GPIO_Pin = UART3_ENABLE_PIN, .GPIO_Mode = GPIO_Mode_OUT, .GPIO_OType = GPIO_OType_PP, .GPIO_Speed = GPIO_Speed_100MHz, .GPIO_PuPd = GPIO_PuPd_NOPULL},
 
-    // //uart6
-    // {.type = kUart6, .GPIOx = GPIOC, .GPIO_Pin = GPIO_Pin_6, .GPIO_Mode = GPIO_Mode_AF, .AF_Info = {.GPIOx = GPIOC, .GPIO_PinSource = GPIO_PinSource6, .GPIO_AF = GPIO_AF_USART6}, .GPIO_OType = GPIO_OType_PP, .GPIO_Speed = GPIO_Speed_100MHz, .GPIO_PuPd = GPIO_PuPd_UP},
-    // {.type = kUart6, .GPIOx = GPIOC, .GPIO_Pin = GPIO_Pin_7, .GPIO_Mode = GPIO_Mode_AF, .AF_Info = {.GPIOx = GPIOC, .GPIO_PinSource = GPIO_PinSource7, .GPIO_AF = GPIO_AF_USART6}, .GPIO_OType = GPIO_OType_PP, .GPIO_Speed = GPIO_Speed_100MHz, .GPIO_PuPd = GPIO_PuPd_UP},
+    // uart6
+    {.type = kUart6, .GPIOx = GPIOC, .GPIO_Pin = GPIO_Pin_7, .GPIO_Mode = GPIO_Mode_AF, .AF_Info = {.GPIOx = GPIOC, .GPIO_PinSource = GPIO_PinSource7, .GPIO_AF = GPIO_AF_USART6}, .GPIO_OType = GPIO_OType_PP, .GPIO_Speed = GPIO_Speed_100MHz, .GPIO_PuPd = GPIO_PuPd_UP},
+    {.type = kUart6, .GPIOx = GPIOG, .GPIO_Pin = GPIO_Pin_14, .GPIO_Mode = GPIO_Mode_AF, .AF_Info = {.GPIOx = GPIOG, .GPIO_PinSource = GPIO_PinSource14, .GPIO_AF = GPIO_AF_USART6}, .GPIO_OType = GPIO_OType_PP, .GPIO_Speed = GPIO_Speed_100MHz, .GPIO_PuPd = GPIO_PuPd_UP},
     // {.type = kUart6, .GPIOx = UART6_ENABLE_PORT, .GPIO_Pin = UART6_ENABLE_PIN, .GPIO_Mode = GPIO_Mode_OUT, .GPIO_OType = GPIO_OType_PP, .GPIO_Speed = GPIO_Speed_100MHz, .GPIO_PuPd = GPIO_PuPd_UP},
+    // sdio
+    {.type = kSDIO, .GPIOx = GPIOC, .GPIO_Pin = GPIO_Pin_8, .GPIO_Mode = GPIO_Mode_AF, .AF_Info = {.GPIOx = GPIOC, .GPIO_PinSource = GPIO_PinSource8, .GPIO_AF = GPIO_AF_SDIO}, .GPIO_OType = GPIO_OType_PP, .GPIO_Speed = GPIO_Speed_100MHz, .GPIO_PuPd = GPIO_PuPd_UP},
+    {.type = kSDIO, .GPIOx = GPIOC, .GPIO_Pin = GPIO_Pin_9, .GPIO_Mode = GPIO_Mode_AF, .AF_Info = {.GPIOx = GPIOC, .GPIO_PinSource = GPIO_PinSource9, .GPIO_AF = GPIO_AF_SDIO}, .GPIO_OType = GPIO_OType_PP, .GPIO_Speed = GPIO_Speed_100MHz, .GPIO_PuPd = GPIO_PuPd_UP},
+    {.type = kSDIO, .GPIOx = GPIOC, .GPIO_Pin = GPIO_Pin_10, .GPIO_Mode = GPIO_Mode_AF, .AF_Info = {.GPIOx = GPIOC, .GPIO_PinSource = GPIO_PinSource10, .GPIO_AF = GPIO_AF_SDIO}, .GPIO_OType = GPIO_OType_PP, .GPIO_Speed = GPIO_Speed_100MHz, .GPIO_PuPd = GPIO_PuPd_UP},
+    {.type = kSDIO, .GPIOx = GPIOC, .GPIO_Pin = GPIO_Pin_11, .GPIO_Mode = GPIO_Mode_AF, .AF_Info = {.GPIOx = GPIOC, .GPIO_PinSource = GPIO_PinSource11, .GPIO_AF = GPIO_AF_SDIO}, .GPIO_OType = GPIO_OType_PP, .GPIO_Speed = GPIO_Speed_100MHz, .GPIO_PuPd = GPIO_PuPd_UP},
+    {.type = kSDIO, .GPIOx = GPIOC, .GPIO_Pin = GPIO_Pin_12, .GPIO_Mode = GPIO_Mode_AF, .AF_Info = {.GPIOx = GPIOC, .GPIO_PinSource = GPIO_PinSource12, .GPIO_AF = GPIO_AF_SDIO}, .GPIO_OType = GPIO_OType_PP, .GPIO_Speed = GPIO_Speed_100MHz, .GPIO_PuPd = GPIO_PuPd_NOPULL},
+    {.type = kSDIO, .GPIOx = GPIOD, .GPIO_Pin = GPIO_Pin_2, .GPIO_Mode = GPIO_Mode_AF, .AF_Info = {.GPIOx = GPIOD, .GPIO_PinSource = GPIO_PinSource2, .GPIO_AF = GPIO_AF_SDIO}, .GPIO_OType = GPIO_OType_PP, .GPIO_Speed = GPIO_Speed_100MHz, .GPIO_PuPd = GPIO_PuPd_UP},
+
     // ethernet-dm9161
     {.type = kEthernet_dm9101, .GPIOx = GPIOA, .GPIO_Pin = GPIO_Pin_1, .GPIO_Mode = GPIO_Mode_AF, .AF_Info = {.GPIOx = GPIOA, .GPIO_PinSource = GPIO_PinSource1, .GPIO_AF = GPIO_AF_ETH}, .GPIO_OType = GPIO_OType_PP, .GPIO_Speed = GPIO_Speed_100MHz, .GPIO_PuPd = GPIO_PuPd_NOPULL},
     {.type = kEthernet_dm9101, .GPIOx = GPIOA, .GPIO_Pin = GPIO_Pin_2, .GPIO_Mode = GPIO_Mode_AF, .AF_Info = {.GPIOx = GPIOA, .GPIO_PinSource = GPIO_PinSource2, .GPIO_AF = GPIO_AF_ETH}, .GPIO_OType = GPIO_OType_PP, .GPIO_Speed = GPIO_Speed_100MHz, .GPIO_PuPd = GPIO_PuPd_NOPULL},
@@ -95,6 +106,7 @@ Interface_struct interface_info[] = {
     {.type = kFSMC, .GPIOx = GPIOD, .GPIO_Pin = GPIO_Pin_1, .GPIO_Mode = GPIO_Mode_AF, .AF_Info = {.GPIOx = GPIOD, .GPIO_PinSource = GPIO_PinSource1, .GPIO_AF = GPIO_AF_FSMC}, .GPIO_OType = GPIO_OType_PP, .GPIO_Speed = GPIO_Speed_100MHz, .GPIO_PuPd = GPIO_PuPd_UP},
     {.type = kFSMC, .GPIOx = GPIOD, .GPIO_Pin = GPIO_Pin_4, .GPIO_Mode = GPIO_Mode_AF, .AF_Info = {.GPIOx = GPIOD, .GPIO_PinSource = GPIO_PinSource4, .GPIO_AF = GPIO_AF_FSMC}, .GPIO_OType = GPIO_OType_PP, .GPIO_Speed = GPIO_Speed_100MHz, .GPIO_PuPd = GPIO_PuPd_UP},
     {.type = kFSMC, .GPIOx = GPIOD, .GPIO_Pin = GPIO_Pin_5, .GPIO_Mode = GPIO_Mode_AF, .AF_Info = {.GPIOx = GPIOD, .GPIO_PinSource = GPIO_PinSource5, .GPIO_AF = GPIO_AF_FSMC}, .GPIO_OType = GPIO_OType_PP, .GPIO_Speed = GPIO_Speed_100MHz, .GPIO_PuPd = GPIO_PuPd_UP},
+    {.type = kFSMC, .GPIOx = GPIOD, .GPIO_Pin = GPIO_Pin_7, .GPIO_Mode = GPIO_Mode_AF, .AF_Info = {.GPIOx = GPIOD, .GPIO_PinSource = GPIO_PinSource5, .GPIO_AF = GPIO_AF_FSMC}, .GPIO_OType = GPIO_OType_PP, .GPIO_Speed = GPIO_Speed_100MHz, .GPIO_PuPd = GPIO_PuPd_UP},
     {.type = kFSMC, .GPIOx = GPIOD, .GPIO_Pin = GPIO_Pin_8, .GPIO_Mode = GPIO_Mode_AF, .AF_Info = {.GPIOx = GPIOD, .GPIO_PinSource = GPIO_PinSource8, .GPIO_AF = GPIO_AF_FSMC}, .GPIO_OType = GPIO_OType_PP, .GPIO_Speed = GPIO_Speed_100MHz, .GPIO_PuPd = GPIO_PuPd_UP},
     {.type = kFSMC, .GPIOx = GPIOD, .GPIO_Pin = GPIO_Pin_9, .GPIO_Mode = GPIO_Mode_AF, .AF_Info = {.GPIOx = GPIOD, .GPIO_PinSource = GPIO_PinSource9, .GPIO_AF = GPIO_AF_FSMC}, .GPIO_OType = GPIO_OType_PP, .GPIO_Speed = GPIO_Speed_100MHz, .GPIO_PuPd = GPIO_PuPd_UP},
     {.type = kFSMC, .GPIOx = GPIOD, .GPIO_Pin = GPIO_Pin_10, .GPIO_Mode = GPIO_Mode_AF, .AF_Info = {.GPIOx = GPIOD, .GPIO_PinSource = GPIO_PinSource10, .GPIO_AF = GPIO_AF_FSMC}, .GPIO_OType = GPIO_OType_PP, .GPIO_Speed = GPIO_Speed_100MHz, .GPIO_PuPd = GPIO_PuPd_UP},
@@ -141,9 +153,14 @@ Interface_struct interface_info[] = {
     // am2303
     {.type = kOutput, .Out_Type = kAM2303, .GPIOx = AM2303_PORT, .GPIO_Pin = AM2303_PIN, .GPIO_Mode = GPIO_Mode_OUT, .GPIO_OType = GPIO_OType_OD, .GPIO_Speed = GPIO_Speed_100MHz, .GPIO_PuPd = GPIO_PuPd_NOPULL},
 
+    // USB U盘
+    {.type = kUSB, .GPIOx = GPIOB, .GPIO_Pin = GPIO_Pin_14, .GPIO_Mode = GPIO_Mode_AF, .AF_Info = {.GPIOx = GPIOB, .GPIO_PinSource = GPIO_PinSource14, .GPIO_AF = GPIO_AF_OTG2_FS}, .GPIO_OType = GPIO_OType_PP, .GPIO_Speed = GPIO_Speed_100MHz, .GPIO_PuPd = GPIO_PuPd_NOPULL},
+    {.type = kUSB, .GPIOx = GPIOB, .GPIO_Pin = GPIO_Pin_15, .GPIO_Mode = GPIO_Mode_AF, .AF_Info = {.GPIOx = GPIOB, .GPIO_PinSource = GPIO_PinSource15, .GPIO_AF = GPIO_AF_OTG2_FS}, .GPIO_OType = GPIO_OType_PP, .GPIO_Speed = GPIO_Speed_100MHz, .GPIO_PuPd = GPIO_PuPd_NOPULL},
+
     // input
     {.type = kInput, .In_Type = DM9K_INIT, .GPIOx = DM9K_RESET_PORT, .GPIO_Pin = DM9K_RESET_PIN, .GPIO_Mode = GPIO_Mode_IN, .GPIO_OType = GPIO_OType_PP, .GPIO_Speed = GPIO_Speed_100MHz, .GPIO_PuPd = GPIO_PuPd_NOPULL},
     {.type = kInput, .In_Type = NOR_FLASH_STATE, .GPIOx = NOR_FLASH_STATE_PORT, .GPIO_Pin = NOR_FLASH_STATE_PIN, .GPIO_Mode = GPIO_Mode_IN, .GPIO_OType = GPIO_OType_PP, .GPIO_Speed = GPIO_Speed_100MHz, .GPIO_PuPd = GPIO_PuPd_NOPULL},
+    {.type = kInput, .In_Type = NAND_FLASH_STATE, .GPIOx = NAND_FLASH_STATE_PORT, .GPIO_Pin = NAND_FLASH_STATE_PIN, .GPIO_Mode = GPIO_Mode_IN, .GPIO_OType = GPIO_OType_OD, .GPIO_Speed = GPIO_Speed_100MHz, .GPIO_PuPd = GPIO_PuPd_UP},
 
     {.type = kInput, .In_Type = kKey, .GPIOx = GPIO_PORT_K1, .GPIO_Pin = GPIO_PIN_K1, .GPIO_Mode = GPIO_Mode_IN, .GPIO_OType = GPIO_OType_PP, .GPIO_Speed = GPIO_Speed_100MHz, .GPIO_PuPd = GPIO_PuPd_NOPULL},
     {.type = kInput, .In_Type = kKey, .GPIOx = GPIO_PORT_K2, .GPIO_Pin = GPIO_PIN_K2, .GPIO_Mode = GPIO_Mode_IN, .GPIO_OType = GPIO_OType_PP, .GPIO_Speed = GPIO_Speed_100MHz, .GPIO_PuPd = GPIO_PuPd_NOPULL},

+ 9 - 0
User/Bsp/interface/interface.h

@@ -15,6 +15,8 @@ typedef enum
     kSPI1,
     kUart1,
     kUart3,
+    kUart6,
+    kSDIO,
     kCAN1,
     kCAN2,
     kEthernet_dm9101,
@@ -24,6 +26,7 @@ typedef enum
     kPMU,
     kDMA1,
     kAM2303,
+    kUSB,
     //    kTIMER1,
     kInterfaceEnd,
 } interface_type;
@@ -71,6 +74,7 @@ typedef enum
 {
     DM9K_INIT,
     NOR_FLASH_STATE,
+    NAND_FLASH_STATE,
     kInputEnd,
     kKey,
 } Input_type;
@@ -153,6 +157,11 @@ typedef struct
 #define NOR_FLASH_STATE_ON   GPIO_ResetBits(NOR_FLASH_STATE_PORT, NOR_FLASH_STATE_PIN)
 #define NOR_FLASH_STATE_OFF  GPIO_SetBits(NOR_FLASH_STATE_PORT, NOR_FLASH_STATE_PIN)
 
+#define NAND_FLASH_STATE_PORT GPIOG
+#define NAND_FLASH_STATE_PIN  GPIO_Pin_6
+#define NAND_FLASH_STATE_ON   GPIO_ResetBits(NAND_FLASH_STATE_PORT, NAND_FLASH_STATE_PIN)
+#define NAND_FLASH_STATE_OFF  GPIO_SetBits(NAND_FLASH_STATE_PORT, NAND_FLASH_STATE_PIN)
+
 #define GPIO_PORT_K1 GPIOI
 #define GPIO_PIN_K1  GPIO_Pin_8
 

+ 1632 - 0
User/Bsp/nand_flash/nand_flash.c

@@ -0,0 +1,1632 @@
+/*
+*********************************************************************************************************
+*
+*	模块名称 : NAND Flash驱动模块
+*	文件名称 : bsp_nand.c
+*	版    本 : V1.0
+*	说    明 : 提供NAND Flash (HY27UF081G2A, 8bit 128K字节 大页)的底层接口函数。【安富莱原创】
+*
+*	修改记录 :
+*		版本号  日期        作者     说明
+*		V1.0    2013-02-01 armfly  正式发布
+*
+*	Copyright (C), 2013-2014, 安富莱电子 www.armfly.com
+*
+*********************************************************************************************************
+*/
+#include "nand_flash.h"
+
+/*
+    如果在IAR或KEIL的编辑器中阅读,请将编辑器的字体设置为新宋体(9号/五号),缩进的TAB设置为4。
+    否则,方框处出现不对齐的问题。
+
+    【待完善的地方】
+    (1)在操作NAND Flash时,如下语句是一个死循环。如果硬件出现异常,将导致软件死机
+        while( GPIO_ReadInputDataBit(GPIOG, GPIO_Pin_6) == 0 )
+
+    (2)没有增加ECC校验功能。ECC可以检查1个或2个bit错误,如果只有1个bit错误,则可以修复这个bit。如果
+        多余2个bit错误,则可能检测不到。
+
+    (3)正常写文件操作时,会导致重建LUT。目前,重建LUT的代码执行效率还不够高,有待完善。
+
+    【硬件说明】
+    安富莱STM32F103ZE-EK开发板配置的NAND Flahs为海力士的HY27UF081G2A
+    (1)NAND Flash的片选信号连接到CPU的FSMC_NCE2,这决定了NAND Flash的地址空间为 0x70000000(见CPU的数据
+        手册的FSMC章节)
+    (2)有FSMC总线上有多个总线设备(如TFT、SRAM、CH374T、NOR),因此必须确保其他总线设备的片选处于禁止
+        状态,否则将出现总线冲突问题 (参见本文件初始化FSMC GPIO的函数)
+
+
+    【NAND Flash 结构定义】
+     备用区有16x4字节,每page 1024字节,每512字节一个扇区,每个扇区对应16自己的备用区:
+
+     每个PAGE的逻辑结构,前面512Bx4是主数据区,后面16Bx4是备用区
+    ┌──────┐┌──────┐┌──────┐┌──────┐┌──────┐┌──────┐┌──────┐┌──────┐
+    │ Main area  ││ Main area  ││ Main area  ││Main area   ││ Spare area ││ Spare area ││ Spare area ││Spare area  │
+    │            ││            ││            ││            ││            ││            ││            ││            │
+    │   512B     ││    512B    ││    512B    ││    512B    ││    16B     ││     16B    ││     16B    ││    16B     │
+    └──────┘└──────┘└──────┘└──────┘└──────┘└──────┘└──────┘└──────┘
+
+     每16B的备用区的逻辑结构如下:(三星推荐标准)
+    ┌───┐┌───┐┌──┐┌──┐┌──┐┌───┐┌───┐┌───┐┌──┐┌──┐┌──┐┌──┐┌───┐┌───┐┌───┐┌───┐┌───┐
+    │  BI  ││RESER ││LSN0││LSN1││LSN2││RESER ││RESER ││RESER ││ECC0││ECC1││ECC2││ECC0││S-ECC1││S-ECC0││RESER ││RESER ││RESER │
+    │      ││ VED  ││    ││    ││    ││ VED  ││ VED  ││ VED  ││    ││    ││    ││    ││      ││      ││ VED  ││ VED  ││ VED  │
+    └───┘└───┘└──┘└──┘└──┘└───┘└───┘└───┘└──┘└──┘└──┘└──┘└───┘└───┘└───┘└───┘└───┘
+
+    K9F1G08U0A 和 HY27UF081G2A 是兼容的。芯片出厂时,厂商保证芯片的第1个块是好块。如果是坏块,则在该块的第1个PAGE的第1个字节
+    或者第2个PAGE(当第1个PAGE坏了无法标记为0xFF时)的第1个字节写入非0xFF值。坏块标记值是随机的,软件直接判断是否等于0xFF即可。
+
+    注意:网上有些资料说NAND Flash厂商的默认做法是将坏块标记定在第1个PAGE的第6个字节处。这个说法是错误。坏块标记在第6个字节仅针对部分小扇区(512字节)的NAND Flash
+    并不是所有的NAND Flash都是这个标准。大家在更换NAND Flash时,请仔细阅读芯片的数据手册。
+
+
+    为了便于在NAND Flash 上移植Fat文件系统,我们对16B的备用区采用以下分配方案:
+    ┌──┐┌──┐┌──┐┌──┐┌──┐┌──┐┌──┐┌──┐┌──┐┌──┐┌───┐┌───┐┌──┐┌──┐┌──┐┌──┐
+    │ BI ││USED││LBN0││LBN1││ECC0││ECC1││ECC2││ECC3││ECC4││ECC5││S-ECC1││S-ECC0││RSVD││RSVD││RSVD││RSVD│
+    │    ││    ││    ││    ││    ││    ││    ││    ││    ││    ││      ││      ││    ││    ││    ││    │
+    └──┘└──┘└──┘└──┘└──┘└──┘└──┘└──┘└──┘└──┘└───┘└───┘└──┘└──┘└──┘└──┘
+    - BI : 坏块标志(Bad Block Identifier)。每个BLOCK的第1个PAGE或者第2个PAGE的第1个字节指示该块是否坏块。0xFF表示好块,不是0xFF表示坏块。
+    - USED : 该块使用标志。0xFF表示空闲块;0xF0表示已用块。
+    - LBN0 LBN1 : 逻辑块号(Logic Block No) 。从0开始编码。只在每个BLOCK的第1个PAGE有效,其它PAGE该字段固定为0xFF FF
+    - ECC0 ~ ECC6 : 512B主数据区的ECC校验 (按照三星提供ECC算法,256字节对应3个字节的ECC)
+    - S-ECC1 S-ECC0 : LSN0和LSN2的ECC校验
+    - RSVD : 保留字节,Reserved
+
+    【坏块管理 & 磨损平衡】
+    (1) 内部全局数组s_usLUT[]按次序保存物理块号。用于物理块和逻辑块的地址映射。
+    (2) 格式化时,将98%的好块用于主数据存储。剩余的2%用于备用区(坏块替换)。
+    (3) 写扇区(512B)时,如果扇区内容为空,则直接写入,减少不必要的块擦除操作。有效提高NAND Flash的寿命和读写性能。
+    (4) 写扇区时,如果扇区内容不为空,则从末尾开始查找一个空闲块替换掉旧块,替换并改写数据完成后,将旧块擦除,并标注为空闲,之后重建LUT。
+    (5) 块复制时,充分利用NAND Flash硬件的Copy-Back功能,无需读源页到内存再写入目标页。这样可显著提高读写效率。
+    (6) 磨损平衡还存在缺陷,效果不好。ECC校验暂未实现。
+
+*/
+
+/* 定义NAND Flash的物理地址。这个是由硬件决定的 */
+#define Bank2_NAND_ADDR ((uint32_t)0x70000000)
+#define Bank_NAND_ADDR  Bank2_NAND_ADDR
+
+/* 定义操作NAND Flash用到3个宏 */
+#define NAND_CMD_AREA  *(__IO uint8_t *)(Bank_NAND_ADDR | CMD_AREA)
+#define NAND_ADDR_AREA *(__IO uint8_t *)(Bank_NAND_ADDR | ADDR_AREA)
+#define NAND_DATA_AREA *(__IO uint8_t *)(Bank_NAND_ADDR | DATA_AREA)
+
+/* 逻辑块号映射表。好块总数的2%用于备份区,因此数组维数低于1024。 LUT = Look Up Table */
+static uint16_t s_usLUT[NAND_BLOCK_COUNT];
+
+static uint16_t s_usValidDataBlockCount; /* 有效的数据块个数 */
+
+static uint8_t s_ucTempBuf[NAND_PAGE_TOTAL_SIZE]; /* 大缓冲区,2112字节. 用于读出比较 */
+
+static uint8_t  NAND_BuildLUT(void);
+static uint8_t  FSMC_NAND_GetStatus(void);
+static uint16_t NAND_FindFreeBlock(void);
+static uint8_t  NAND_MarkUsedBlock(uint32_t _ulBlockNo);
+static void     NAND_MarkBadBlock(uint32_t _ulBlockNo);
+static uint16_t NAND_AddrToPhyBlockNo(uint32_t _ulMemAddr);
+static uint8_t  NAND_IsBufOk(uint8_t *_pBuf, uint32_t _ulLen, uint8_t _ucValue);
+uint8_t         NAND_WriteToNewBlock(uint32_t _ulPhyPageNo, uint8_t *_pWriteBuf, uint16_t _usOffset, uint16_t _usSize);
+static uint8_t  NAND_IsFreeBlock(uint32_t _ulBlockNo);
+
+static uint16_t NAND_LBNtoPBN(uint32_t _uiLBN);
+
+/*
+*********************************************************************************************************
+*	函 数 名: FSMC_NAND_Init
+*	功能说明: 配置FSMC和GPIO用于NAND Flash接口。这个函数必须在读写nand flash前被调用一次。
+*	形    参:  无
+*	返 回 值: 无
+*********************************************************************************************************
+*/
+static void FSMC_NAND_Init(void)
+{
+    FSMC_NANDInitTypeDef              FSMC_NANDInitStructure;
+    FSMC_NAND_PCCARDTimingInitTypeDef p;
+
+    /*--NAND Flash GPIOs 配置  ------
+        PD0/FSMC_D2
+        PD1/FSMC_D3
+        PD4/FSMC_NOE
+        PD5/FSMC_NWE
+        PD7/FSMC_NCE2
+        PD11/FSMC_A16
+        PD12/FSMC_A17
+        PD14/FSMC_D0
+        PD15/FSMC_D1
+
+        PE7/FSMC_D4
+        PE8/FSMC_D5
+        PE9/FSMC_D6
+        PE10/FSMC_D7
+
+        PG6/FSMC_INT2	(本例程用查询方式判忙,此口线作为普通GPIO输入功能使用)
+    */
+
+    /*  配置GPIOG, PG6作为忙信息,配置为输入 */
+
+    /* INT2 引脚配置为内部上来输入,用于忙信号 */
+
+    /* 配置 FSMC 时序 */
+    /*
+        Defines the number of HCLK cycles to setup address before the command assertion for NAND-Flash
+        read or write access  to common/Attribute or I/O memory space (depending on the memory space
+        timing to be configured).This parameter can be a value between 0 and 0xFF.
+    */
+    // p.FSMC_SetupTime = 0x01;
+    p.FSMC_SetupTime = 0x1;
+
+    /*
+        Defines the minimum number of HCLK cycles to assert the command for NAND-Flash read or write
+        access to common/Attribute or I/O memory space (depending on the memory space timing to be
+        configured). This parameter can be a number between 0x00 and 0xFF
+    */
+    // p.FSMC_WaitSetupTime = 0x03;
+    p.FSMC_WaitSetupTime = 0x3;
+
+    /*
+        Defines the number of HCLK clock cycles to hold address (and data for write access) after the
+        command deassertion for NAND-Flash read or write access to common/Attribute or I/O memory space
+         (depending on the memory space timing to be configured).
+        This parameter can be a number between 0x00 and 0xFF
+    */
+    // p.FSMC_HoldSetupTime = 0x02;
+    p.FSMC_HoldSetupTime = 0x2;
+
+    /*
+        Defines the number of HCLK clock cycles during which the databus is kept in HiZ after the start
+        of a NAND-Flash  write access to common/Attribute or I/O memory space (depending on the memory
+        space timing to be configured). This parameter can be a number between 0x00 and 0xFF
+    */
+    // p.FSMC_HiZSetupTime = 0x01;
+    p.FSMC_HiZSetupTime = 0x1;
+
+    FSMC_NANDInitStructure.FSMC_Bank                       = FSMC_Bank2_NAND;            /* 定义FSMC NAND BANK 号 */
+    FSMC_NANDInitStructure.FSMC_Waitfeature                = FSMC_Waitfeature_Disable;   /* 插入等待时序使能 */
+    FSMC_NANDInitStructure.FSMC_MemoryDataWidth            = FSMC_MemoryDataWidth_8b;    /* 数据宽度 8bit */
+    FSMC_NANDInitStructure.FSMC_ECC                        = FSMC_ECC_Enable;            /* ECC错误检查和纠正功能使能 */
+    FSMC_NANDInitStructure.FSMC_ECCPageSize                = FSMC_ECCPageSize_2048Bytes; /* ECC 页面大小 */
+    FSMC_NANDInitStructure.FSMC_TCLRSetupTime              = 0x01;                       /* CLE低和RE低之间的延迟,HCLK周期数 */
+    FSMC_NANDInitStructure.FSMC_TARSetupTime               = 0x01;                       /* ALE低和RE低之间的延迟,HCLK周期数 */
+    FSMC_NANDInitStructure.FSMC_CommonSpaceTimingStruct    = &p;                         /* FSMC Common Space Timing */
+    FSMC_NANDInitStructure.FSMC_AttributeSpaceTimingStruct = &p;                         /* FSMC Attribute Space Timing */
+
+    FSMC_NANDInit(&FSMC_NANDInitStructure);
+
+    /* FSMC NAND Bank 使能 */
+    FSMC_NANDCmd(FSMC_Bank2_NAND, ENABLE);
+}
+
+/*
+*********************************************************************************************************
+*	函 数 名: NAND_ReadID
+*	功能说明: 读NAND Flash的ID。ID存储到形参指定的结构体变量中。
+*	形    参:  无
+*	返 回 值: 32bit的NAND Flash ID
+*********************************************************************************************************
+*/
+uint32_t NAND_ReadID(void)
+{
+    uint32_t data = 0;
+
+    /* 发送命令 Command to the command area */
+    NAND_CMD_AREA  = 0x90;
+    NAND_ADDR_AREA = 0x00;
+
+    /* 顺序读取NAND Flash的ID */
+    data = *(__IO uint32_t *)(Bank_NAND_ADDR | DATA_AREA);
+    data = ((data << 24) & 0xFF000000) |
+           ((data << 8) & 0x00FF0000) |
+           ((data >> 8) & 0x0000FF00) |
+           ((data >> 24) & 0x000000FF);
+    return data;
+}
+
+/*
+*********************************************************************************************************
+*	函 数 名: FSMC_NAND_PageCopyBack
+*	功能说明: 将一页数据复制到另外一个页。源页和目标页必须同为偶数页或同为奇数页。
+*	形    参:  - _ulSrcPageNo: 源页号
+*             - _ulTarPageNo: 目标页号
+*	返 回 值: 执行结果:
+*				- NAND_FAIL 表示失败
+*				- NAND_OK 表示成功
+*
+*	说    明:数据手册推荐:在页复制之前,先校验源页的位校验,否则可能会积累位错误。本函数未实现。
+*
+*********************************************************************************************************
+*/
+static uint8_t FSMC_NAND_PageCopyBack(uint32_t _ulSrcPageNo, uint32_t _ulTarPageNo)
+{
+    uint8_t i;
+
+    NAND_CMD_AREA = NAND_CMD_COPYBACK_A;
+
+    /* 发送源页地址 , 对于 HY27UF081G2A
+                  Bit7 Bit6 Bit5 Bit4 Bit3 Bit2 Bit1 Bit0
+        第1字节: A7   A6   A5   A4   A3   A2   A1   A0		(_usPageAddr 的bit7 - bit0)
+        第2字节: 0    0    0    0    A11  A10  A9   A8		(_usPageAddr 的bit11 - bit8, 高4bit必须是0)
+        第3字节: A19  A18  A17  A16  A15  A14  A13  A12
+        第4字节: A27  A26  A25  A24  A23  A22  A21  A20
+    */
+    NAND_ADDR_AREA = 0;
+    NAND_ADDR_AREA = 0;
+    NAND_ADDR_AREA = _ulSrcPageNo;
+    NAND_ADDR_AREA = (_ulSrcPageNo & 0xFF00) >> 8;
+
+    NAND_CMD_AREA = NAND_CMD_COPYBACK_B;
+
+    /* 必须等待,否则读出数据异常, 此处应该判断超时 */
+    for (i = 0; i < 20; i++)
+        ;
+    while (GPIO_ReadInputDataBit(GPIOG, GPIO_Pin_6) == 0)
+        ;
+
+    NAND_CMD_AREA = NAND_CMD_COPYBACK_C;
+
+    /* 发送目标页地址 , 对于 HY27UF081G2A
+                  Bit7 Bit6 Bit5 Bit4 Bit3 Bit2 Bit1 Bit0
+        第1字节: A7   A6   A5   A4   A3   A2   A1   A0		(_usPageAddr 的bit7 - bit0)
+        第2字节: 0    0    0    0    A11  A10  A9   A8		(_usPageAddr 的bit11 - bit8, 高4bit必须是0)
+        第3字节: A19  A18  A17  A16  A15  A14  A13  A12
+        第4字节: A27  A26  A25  A24  A23  A22  A21  A20
+    */
+    NAND_ADDR_AREA = 0;
+    NAND_ADDR_AREA = 0;
+    NAND_ADDR_AREA = _ulTarPageNo;
+    NAND_ADDR_AREA = (_ulTarPageNo & 0xFF00) >> 8;
+
+    NAND_CMD_AREA = NAND_CMD_COPYBACK_D;
+
+    /* 检查操作状态 */
+    if (FSMC_NAND_GetStatus() == NAND_READY)
+    {
+        return NAND_OK;
+    }
+    return NAND_FAIL;
+}
+
+/*
+*********************************************************************************************************
+*	函 数 名: FSMC_NAND_PageCopyBackEx
+*	功能说明: 将一页数据复制到另外一个页,并更新目标页中的部分数据。源页和目标页必须同为偶数页或同为奇数页。
+*	形    参:  - _ulSrcPageNo: 源页号
+*             - _ulTarPageNo: 目标页号
+*			  - _usOffset: 页内偏移地址,pBuf的内容将写入这个地址开始单元
+*			  - _pBuf: 数据缓冲区
+*			  - _usSize: 数据大小
+*	返 回 值: 执行结果:
+*				- NAND_FAIL 表示失败
+*				- NAND_OK 表示成功
+*
+*	说    明:数据手册推荐:在页复制之前,先校验源页的位校验,否则可能会积累位错误。本函数未实现。
+*
+*********************************************************************************************************
+*/
+static uint8_t FSMC_NAND_PageCopyBackEx(uint32_t _ulSrcPageNo, uint32_t _ulTarPageNo, uint8_t *_pBuf, uint16_t _usOffset, uint16_t _usSize)
+{
+    uint16_t i;
+
+    NAND_CMD_AREA = NAND_CMD_COPYBACK_A;
+
+    /* 发送源页地址 , 对于 HY27UF081G2A
+                  Bit7 Bit6 Bit5 Bit4 Bit3 Bit2 Bit1 Bit0
+        第1字节: A7   A6   A5   A4   A3   A2   A1   A0		(_usPageAddr 的bit7 - bit0)
+        第2字节: 0    0    0    0    A11  A10  A9   A8		(_usPageAddr 的bit11 - bit8, 高4bit必须是0)
+        第3字节: A19  A18  A17  A16  A15  A14  A13  A12
+        第4字节: A27  A26  A25  A24  A23  A22  A21  A20
+    */
+    NAND_ADDR_AREA = 0;
+    NAND_ADDR_AREA = 0;
+    NAND_ADDR_AREA = _ulSrcPageNo;
+    NAND_ADDR_AREA = (_ulSrcPageNo & 0xFF00) >> 8;
+
+    NAND_CMD_AREA = NAND_CMD_COPYBACK_B;
+
+    /* 必须等待,否则读出数据异常, 此处应该判断超时 */
+    for (i = 0; i < 20; i++)
+        ;
+    while (GPIO_ReadInputDataBit(GPIOG, GPIO_Pin_6) == 0)
+        ;
+
+    NAND_CMD_AREA = NAND_CMD_COPYBACK_C;
+
+    /* 发送目标页地址 , 对于 HY27UF081G2A
+                  Bit7 Bit6 Bit5 Bit4 Bit3 Bit2 Bit1 Bit0
+        第1字节: A7   A6   A5   A4   A3   A2   A1   A0		(_usPageAddr 的bit7 - bit0)
+        第2字节: 0    0    0    0    A11  A10  A9   A8		(_usPageAddr 的bit11 - bit8, 高4bit必须是0)
+        第3字节: A19  A18  A17  A16  A15  A14  A13  A12
+        第4字节: A27  A26  A25  A24  A23  A22  A21  A20
+    */
+    NAND_ADDR_AREA = 0;
+    NAND_ADDR_AREA = 0;
+    NAND_ADDR_AREA = _ulTarPageNo;
+    NAND_ADDR_AREA = (_ulTarPageNo & 0xFF00) >> 8;
+
+    /* 中间无需带数据, 也无需等待 */
+
+    NAND_CMD_AREA = NAND_CMD_COPYBACK_C;
+
+    NAND_ADDR_AREA = _usOffset;
+    NAND_ADDR_AREA = _usOffset >> 8;
+
+    /* 发送数据 */
+    for (i = 0; i < _usSize; i++)
+    {
+        NAND_DATA_AREA = _pBuf[i];
+    }
+
+    NAND_CMD_AREA = NAND_CMD_COPYBACK_D;
+
+    /* 检查操作状态 */
+    if (FSMC_NAND_GetStatus() == NAND_READY)
+    {
+        return NAND_OK;
+    }
+    return NAND_FAIL;
+}
+
+/*
+*********************************************************************************************************
+*	函 数 名: FSMC_NAND_WritePage
+*	功能说明: 写一组数据至NandFlash指定页面的指定位置,写入的数据长度不大于一页的大小。
+*	形    参:  - _pBuffer: 指向包含待写数据的缓冲区
+*             - _ulPageNo: 页号,所有的页统一编码,范围为:0 - 65535
+*			  - _usAddrInPage : 页内地址,范围为:0-2111
+*             - _usByteCount: 写入的字节个数
+*	返 回 值: 执行结果:
+*				- NAND_FAIL 表示失败
+*				- NAND_OK 表示成功
+*********************************************************************************************************
+*/
+static uint8_t FSMC_NAND_WritePage(uint8_t *_pBuffer, uint32_t _ulPageNo, uint16_t _usAddrInPage, uint16_t _usByteCount)
+{
+    uint16_t i;
+
+    /* 发送页写命令 */
+    NAND_CMD_AREA = NAND_CMD_WRITE0;
+
+    /* 发送页内地址 , 对于 HY27UF081G2A
+                  Bit7 Bit6 Bit5 Bit4 Bit3 Bit2 Bit1 Bit0
+        第1字节: A7   A6   A5   A4   A3   A2   A1   A0		(_usPageAddr 的bit7 - bit0)
+        第2字节: 0    0    0    0    A11  A10  A9   A8		(_usPageAddr 的bit11 - bit8, 高4bit必须是0)
+        第3字节: A19  A18  A17  A16  A15  A14  A13  A12
+        第4字节: A27  A26  A25  A24  A23  A22  A21  A20
+    */
+    NAND_ADDR_AREA = _usAddrInPage;
+    NAND_ADDR_AREA = _usAddrInPage >> 8;
+    NAND_ADDR_AREA = _ulPageNo;
+    NAND_ADDR_AREA = (_ulPageNo & 0xFF00) >> 8;
+
+    /* 写数据 */
+    for (i = 0; i < _usByteCount; i++)
+    {
+        NAND_DATA_AREA = _pBuffer[i];
+    }
+    NAND_CMD_AREA = NAND_CMD_WRITE_TRUE1;
+
+    /* 检查操作状态 */
+    if (FSMC_NAND_GetStatus() == NAND_READY)
+    {
+        return NAND_OK;
+    }
+    return NAND_FAIL;
+}
+
+/*
+*********************************************************************************************************
+*	函 数 名: FSMC_NAND_ReadPage
+*	功能说明: 从NandFlash指定页面的指定位置读一组数据,读出的数据长度不大于一页的大小。
+*	形    参:  - _pBuffer: 指向包含待写数据的缓冲区
+*             - _ulPageNo: 页号,所有的页统一编码,范围为:0 - 65535
+*			  - _usAddrInPage : 页内地址,范围为:0-2111
+*             - _usByteCount: 字节个数
+*	返 回 值: 执行结果:
+*				- NAND_FAIL 表示失败
+*				- NAND_OK 表示成功
+*********************************************************************************************************
+*/
+static uint8_t FSMC_NAND_ReadPage(uint8_t *_pBuffer, uint32_t _ulPageNo, uint16_t _usAddrInPage, uint16_t _usByteCount)
+{
+    uint16_t i;
+
+    /* 发送页面读命令 */
+    NAND_CMD_AREA = NAND_CMD_AREA_A;
+
+    /* 发送页内地址 , 对于 HY27UF081G2A
+                  Bit7 Bit6 Bit5 Bit4 Bit3 Bit2 Bit1 Bit0
+        第1字节: A7   A6   A5   A4   A3   A2   A1   A0		(_usPageAddr 的bit7 - bit0)
+        第2字节: 0    0    0    0    A11  A10  A9   A8		(_usPageAddr 的bit11 - bit8, 高4bit必须是0)
+        第3字节: A19  A18  A17  A16  A15  A14  A13  A12
+        第4字节: A27  A26  A25  A24  A23  A22  A21  A20
+    */
+    NAND_ADDR_AREA = _usAddrInPage;
+    NAND_ADDR_AREA = _usAddrInPage >> 8;
+    NAND_ADDR_AREA = _ulPageNo;
+    NAND_ADDR_AREA = (_ulPageNo & 0xFF00) >> 8;
+
+    NAND_CMD_AREA = NAND_CMD_AREA_TRUE1;
+
+    /* 必须等待,否则读出数据异常, 此处应该判断超时 */
+    for (i = 0; i < 20; i++)
+        ;
+    while (GPIO_ReadInputDataBit(GPIOG, GPIO_Pin_6) == 0)
+        ;
+
+    /* 读数据到缓冲区pBuffer */
+    for (i = 0; i < _usByteCount; i++)
+    {
+        _pBuffer[i] = NAND_DATA_AREA;
+    }
+
+    return NAND_OK;
+}
+
+/*
+*********************************************************************************************************
+*	函 数 名: FSMC_NAND_WriteSpare
+*	功能说明: 向1个PAGE的Spare区写入数据
+*	形    参:  - _pBuffer: 指向包含待写数据的缓冲区
+*             - _ulPageNo: 页号,所有的页统一编码,范围为:0 - 65535
+*			  - _usAddrInSpare : 页内备用区的偏移地址,范围为:0-63
+*             - _usByteCount: 写入的字节个数
+*	返 回 值: 执行结果:
+*				- NAND_FAIL 表示失败
+*				- NAND_OK 表示成功
+*********************************************************************************************************
+*/
+static uint8_t FSMC_NAND_WriteSpare(uint8_t *_pBuffer, uint32_t _ulPageNo, uint16_t _usAddrInSpare, uint16_t _usByteCount)
+{
+    if (_usByteCount > NAND_SPARE_AREA_SIZE)
+    {
+        return NAND_FAIL;
+    }
+
+    return FSMC_NAND_WritePage(_pBuffer, _ulPageNo, NAND_PAGE_SIZE + _usAddrInSpare, _usByteCount);
+}
+
+/*
+*********************************************************************************************************
+*	函 数 名: FSMC_NAND_ReadSpare
+*	功能说明: 读1个PAGE的Spare区的数据
+*	形    参:  - _pBuffer: 指向包含待写数据的缓冲区
+*             - _ulPageNo: 页号,所有的页统一编码,范围为:0 - 65535
+*			  - _usAddrInSpare : 页内备用区的偏移地址,范围为:0-63
+*             - _usByteCount: 写入的字节个数
+*	返 回 值: 执行结果:
+*				- NAND_FAIL 表示失败
+*				- NAND_OK 表示成功
+*********************************************************************************************************
+*/
+static uint8_t FSMC_NAND_ReadSpare(uint8_t *_pBuffer, uint32_t _ulPageNo, uint16_t _usAddrInSpare, uint16_t _usByteCount)
+{
+    if (_usByteCount > NAND_SPARE_AREA_SIZE)
+    {
+        return NAND_FAIL;
+    }
+
+    return FSMC_NAND_ReadPage(_pBuffer, _ulPageNo, NAND_PAGE_SIZE + _usAddrInSpare, _usByteCount);
+}
+
+/*
+*********************************************************************************************************
+*	函 数 名: FSMC_NAND_WriteData
+*	功能说明: 向1个PAGE的主数据区写入数据
+*	形    参:  - _pBuffer: 指向包含待写数据的缓冲区
+*             - _ulPageNo: 页号,所有的页统一编码,范围为:0 - 65535
+*			  - _usAddrInPage : 页内数据区的偏移地址,范围为:0-2047
+*             - _usByteCount: 写入的字节个数
+*	返 回 值: 执行结果:
+*				- NAND_FAIL 表示失败
+*				- NAND_OK 表示成功
+*********************************************************************************************************
+*/
+static uint8_t FSMC_NAND_WriteData(uint8_t *_pBuffer, uint32_t _ulPageNo, uint16_t _usAddrInPage, uint16_t _usByteCount)
+{
+    if (_usByteCount > NAND_PAGE_SIZE)
+    {
+        return NAND_FAIL;
+    }
+
+    return FSMC_NAND_WritePage(_pBuffer, _ulPageNo, _usAddrInPage, _usByteCount);
+}
+
+/*
+*********************************************************************************************************
+*	函 数 名: FSMC_NAND_ReadData
+*	功能说明: 读1个PAGE的主数据的数据
+*	形    参:  - _pBuffer: 指向包含待写数据的缓冲区
+*             - _ulPageNo: 页号,所有的页统一编码,范围为:0 - 65535
+*			  - _usAddrInPage : 页内数据区的偏移地址,范围为:0-2047
+*             - _usByteCount: 写入的字节个数
+*	返 回 值: 执行结果:
+*				- NAND_FAIL 表示失败
+*				- NAND_OK 表示成功
+*********************************************************************************************************
+*/
+static uint8_t FSMC_NAND_ReadData(uint8_t *_pBuffer, uint32_t _ulPageNo, uint16_t _usAddrInPage, uint16_t _usByteCount)
+{
+    if (_usByteCount > NAND_PAGE_SIZE)
+    {
+        return NAND_FAIL;
+    }
+
+    return FSMC_NAND_ReadPage(_pBuffer, _ulPageNo, _usAddrInPage, _usByteCount);
+}
+
+/*
+*********************************************************************************************************
+*	函 数 名: FSMC_NAND_EraseBlock
+*	功能说明: 擦除NAND Flash一个块(block)
+*	形    参:  - _ulBlockNo: 块号,范围为:0 - 1023
+*	返 回 值: NAND操作状态,有如下几种值:
+*             - NAND_TIMEOUT_ERROR  : 超时错误
+*             - NAND_READY          : 操作成功
+*********************************************************************************************************
+*/
+static uint8_t FSMC_NAND_EraseBlock(uint32_t _ulBlockNo)
+{
+    /* 发送擦除命令 */
+    NAND_CMD_AREA = NAND_CMD_ERASE0;
+
+    _ulBlockNo <<= 6; /* 块号转换为页编号 */
+    NAND_ADDR_AREA = _ulBlockNo;
+    NAND_ADDR_AREA = _ulBlockNo >> 8;
+
+    NAND_CMD_AREA = NAND_CMD_ERASE1;
+
+    return (FSMC_NAND_GetStatus());
+}
+
+/*
+*********************************************************************************************************
+*	函 数 名: FSMC_NAND_Reset
+*	功能说明: 复位NAND Flash
+*	形    参:  无
+*	返 回 值: 无
+*********************************************************************************************************
+*/
+static uint8_t FSMC_NAND_Reset(void)
+{
+    NAND_CMD_AREA = NAND_CMD_RESET;
+
+    /* 检查操作状态 */
+    if (FSMC_NAND_GetStatus() == NAND_READY)
+    {
+        return NAND_OK;
+    }
+
+    return NAND_FAIL;
+}
+
+/*
+*********************************************************************************************************
+*	函 数 名: FSMC_NAND_ReadStatus
+*	功能说明: 使用Read statuc 命令读NAND Flash内部状态
+*	形    参:  - Address: 被擦除的快内任意地址
+*	返 回 值: NAND操作状态,有如下几种值:
+*             - NAND_BUSY: 内部正忙
+*             - NAND_READY: 内部空闲,可以进行下步操作
+*             - NAND_ERROR: 先前的命令执行失败
+*********************************************************************************************************
+*/
+static uint8_t FSMC_NAND_ReadStatus(void)
+{
+    uint8_t ucData;
+    uint8_t ucStatus = NAND_BUSY;
+
+    /* 读状态操作 */
+    NAND_CMD_AREA = NAND_CMD_STATUS;
+    ucData        = *(__IO uint8_t *)(Bank_NAND_ADDR);
+
+    if ((ucData & NAND_ERROR) == NAND_ERROR)
+    {
+        ucStatus = NAND_ERROR;
+    }
+    else if ((ucData & NAND_READY) == NAND_READY)
+    {
+        ucStatus = NAND_READY;
+    }
+    else
+    {
+        ucStatus = NAND_BUSY;
+    }
+
+    return (ucStatus);
+}
+
+/*
+*********************************************************************************************************
+*	函 数 名: FSMC_NAND_GetStatus
+*	功能说明: 获取NAND Flash操作状态
+*	形    参:  - Address: 被擦除的快内任意地址
+*	返 回 值: NAND操作状态,有如下几种值:
+*             - NAND_TIMEOUT_ERROR  : 超时错误
+*             - NAND_READY          : 操作成功
+*********************************************************************************************************
+*/
+static uint8_t FSMC_NAND_GetStatus(void)
+{
+    uint32_t ulTimeout = 0x10000;
+    uint8_t  ucStatus  = NAND_READY;
+
+    ucStatus = FSMC_NAND_ReadStatus();
+
+    /* 等待NAND操作结束,超时后会退出 */
+    while ((ucStatus != NAND_READY) && (ulTimeout != 0x00))
+    {
+        ucStatus = FSMC_NAND_ReadStatus();
+        ulTimeout--;
+    }
+
+    if (ulTimeout == 0x00)
+    {
+        ucStatus = NAND_TIMEOUT_ERROR;
+    }
+
+    /* 返回操作状态 */
+    return (ucStatus);
+}
+
+/*
+*********************************************************************************************************
+*	函 数 名: nand_flash_init
+*	功能说明: 初始化NAND Flash接口
+*	形    参:  无
+*	返 回 值: 执行结果:
+*				- NAND_FAIL 表示失败
+*				- NAND_OK 表示成功
+*********************************************************************************************************
+*/
+uint8_t nand_flash_init(void)
+{
+    uint8_t Status;
+
+    FSMC_NAND_Init(); /* 配置FSMC和GPIO用于NAND Flash接口 */
+
+    FSMC_NAND_Reset(); /* 通过复位命令复位NAND Flash到读状态 */
+
+    Status = NAND_BuildLUT(); /* 建立块管理表 LUT = Look up table */
+    return Status;
+}
+
+/*
+*********************************************************************************************************
+*	函 数 名: NAND_WriteToNewBlock
+*	功能说明: 将旧块的数据复制到新块,并将新的数据段写入这个新块
+*	形    参:  	_ulPhyPageNo : 源页号
+*				_pWriteBuf : 数据缓冲区
+*				_usOffset : 页内偏移地址
+*				_usSize :数据长度,必须是4字节的整数倍
+*	返 回 值: 执行结果:
+*				- NAND_FAIL 表示失败
+*				- NAND_OK 表示成功
+*********************************************************************************************************
+*/
+uint8_t NAND_WriteToNewBlock(uint32_t _ulPhyPageNo, uint8_t *_pWriteBuf, uint16_t _usOffset, uint16_t _usSize)
+{
+    uint16_t n, i;
+    uint16_t usNewBlock;
+    uint16_t ulSrcBlock;
+    uint16_t usOffsetPageNo;
+
+    ulSrcBlock     = _ulPhyPageNo / NAND_BLOCK_SIZE; /* 根据物理页号反推块号 */
+    usOffsetPageNo = _ulPhyPageNo % NAND_BLOCK_SIZE; /* 根据物理页号计算物理页号在块内偏移页号 */
+    /* 增加循环的目的是处理目标块为坏块的情况 */
+    for (n = 0; n < 10; n++)
+    {
+        /* 如果不是全0xFF, 则需要寻找一个空闲可用块,并将页内的数据全部移到新块中,然后擦除这个块 */
+        usNewBlock = NAND_FindFreeBlock(); /* 从最后一个Block开始,搜寻一个可用块 */
+        if (usNewBlock >= NAND_BLOCK_COUNT)
+        {
+            return NAND_FAIL; /* 查找空闲块失败 */
+        }
+
+        /* 使用page-copy功能,将当前块(usPBN)的数据全部搬移到新块(usNewBlock) */
+        for (i = 0; i < NAND_BLOCK_SIZE; i++)
+        {
+            if (i == usOffsetPageNo)
+            {
+                /* 如果写入的数据在当前页,则需要使用带随机数据的Copy-Back命令 */
+                if (FSMC_NAND_PageCopyBackEx(ulSrcBlock * NAND_BLOCK_SIZE + i, usNewBlock * NAND_BLOCK_SIZE + i,
+                                             _pWriteBuf, _usOffset, _usSize) == NAND_FAIL)
+                {
+                    NAND_MarkBadBlock(usNewBlock); /* 将新块标记为坏块 */
+                    NAND_BuildLUT();               /* 重建LUT表 */
+                    break;
+                }
+            }
+            else
+            {
+                /* 使用NAND Flash 提供的整页Copy-Back功能,可以显著提高操作效率 */
+                if (FSMC_NAND_PageCopyBack(ulSrcBlock * NAND_BLOCK_SIZE + i,
+                                           usNewBlock * NAND_BLOCK_SIZE + i) == NAND_FAIL)
+                {
+                    NAND_MarkBadBlock(usNewBlock); /* 将新块标记为坏块 */
+                    NAND_BuildLUT();               /* 重建LUT表 */
+                    break;
+                }
+            }
+        }
+        /* 目标块更新成功 */
+        if (i == NAND_BLOCK_SIZE)
+        {
+            /* 标记新块为已用块 */
+            if (NAND_MarkUsedBlock(usNewBlock) == NAND_FAIL)
+            {
+                NAND_MarkBadBlock(usNewBlock); /* 将新块标记为坏块 */
+                NAND_BuildLUT();               /* 重建LUT表 */
+                continue;
+            }
+
+            /* 擦除源BLOCK */
+            if (FSMC_NAND_EraseBlock(ulSrcBlock) != NAND_READY)
+            {
+                NAND_MarkBadBlock(ulSrcBlock); /* 将源块标记为坏块 */
+                NAND_BuildLUT();               /* 重建LUT表 */
+                continue;
+            }
+            NAND_BuildLUT(); /* 重建LUT表 */
+            break;
+        }
+    }
+
+    return NAND_OK; /* 写入成功 */
+}
+
+/*
+*********************************************************************************************************
+*	函 数 名: NAND_Write
+*	功能说明: 写一个扇区
+*	形    参:  	_MemAddr : 内存单元偏移地址
+*				_pReadbuff :存放待写数据的缓冲区的指针
+*				_usSize :数据长度,必须是4字节的整数倍
+*	返 回 值: 执行结果:
+*				- NAND_FAIL 表示失败
+*				- NAND_OK 表示成功
+*********************************************************************************************************
+*/
+uint8_t NAND_Write(uint32_t _ulMemAddr, uint32_t *_pWriteBuf, uint16_t _usSize)
+{
+    uint16_t usPBN;        /* 物理块号 */
+    uint32_t ulPhyPageNo;  /* 物理页号 */
+    uint16_t usAddrInPage; /* 页内偏移地址 */
+    uint32_t ulTemp;
+
+    /* 数据长度必须是4字节整数倍 */
+    if ((_usSize % 4) != 0)
+    {
+        return NAND_FAIL;
+    }
+    /* 数据长度不能超过512字节(遵循 Fat格式) */
+    if (_usSize > 512)
+    {
+        // return NAND_FAIL;
+    }
+
+    usPBN = NAND_AddrToPhyBlockNo(_ulMemAddr); /* 查询LUT表获得物理块号 */
+
+    ulTemp       = _ulMemAddr % (NAND_BLOCK_SIZE * NAND_PAGE_SIZE);
+    ulPhyPageNo  = usPBN * NAND_BLOCK_SIZE + ulTemp / NAND_PAGE_SIZE; /* 计算物理页号 */
+    usAddrInPage = ulTemp % NAND_PAGE_SIZE;                           /* 计算页内偏移地址 */
+
+    /* 读出扇区的内容,判断是否全FF */
+    if (FSMC_NAND_ReadData(s_ucTempBuf, ulPhyPageNo, usAddrInPage, _usSize) == NAND_FAIL)
+    {
+        return NAND_FAIL; /* 读NAND Flash失败 */
+    }
+    /* 如果是全0xFF, 则可以直接写入,无需擦除 */
+    if (NAND_IsBufOk(s_ucTempBuf, _usSize, 0xFF))
+    {
+        if (FSMC_NAND_WriteData((uint8_t *)_pWriteBuf, ulPhyPageNo, usAddrInPage, _usSize) == NAND_FAIL)
+        {
+            /* 将数据写入到另外一个块(空闲块) */
+            return NAND_WriteToNewBlock(ulPhyPageNo, (uint8_t *)_pWriteBuf, usAddrInPage, _usSize);
+        }
+
+        /* 标记该块已用 */
+        if (NAND_MarkUsedBlock(ulPhyPageNo) == NAND_FAIL)
+        {
+            /* 标记失败,将数据写入到另外一个块(空闲块) */
+            return NAND_WriteToNewBlock(ulPhyPageNo, (uint8_t *)_pWriteBuf, usAddrInPage, _usSize);
+        }
+        return NAND_OK; /* 写入成功 */
+    }
+
+    /* 将数据写入到另外一个块(空闲块) */
+    return NAND_WriteToNewBlock(ulPhyPageNo, (uint8_t *)_pWriteBuf, usAddrInPage, _usSize);
+}
+
+/*
+*********************************************************************************************************
+*	函 数 名: NAND_Read
+*	功能说明: 读一个扇区
+*	形    参:  	_MemAddr : 内存单元偏移地址
+*				_pReadbuff :存放读出数据的缓冲区的指针
+*				_usSize :数据长度,必须是4字节的整数倍
+*	返 回 值: 执行结果:
+*				- NAND_FAIL 表示失败
+*				- NAND_OK 表示成功
+*********************************************************************************************************
+*/
+uint8_t NAND_Read(uint32_t _ulMemAddr, uint32_t *_pReadBuf, uint16_t _usSize)
+{
+    uint16_t usPBN;        /* 物理块号 */
+    uint32_t ulPhyPageNo;  /* 物理页号 */
+    uint16_t usAddrInPage; /* 页内偏移地址 */
+    uint32_t ulTemp;
+
+    /* 数据长度必须是4字节整数倍 */
+    if ((_usSize % 4) != 0)
+    {
+        return NAND_FAIL;
+    }
+
+    usPBN = NAND_AddrToPhyBlockNo(_ulMemAddr); /* 查询LUT表获得物理块号 */
+    if (usPBN >= NAND_BLOCK_COUNT)
+    {
+        /* 没有格式化,usPBN = 0xFFFF */
+        return NAND_FAIL;
+    }
+
+    ulTemp       = _ulMemAddr % (NAND_BLOCK_SIZE * NAND_PAGE_SIZE);
+    ulPhyPageNo  = usPBN * NAND_BLOCK_SIZE + ulTemp / NAND_PAGE_SIZE; /* 计算物理页号 */
+    usAddrInPage = ulTemp % NAND_PAGE_SIZE;                           /* 计算页内偏移地址 */
+
+    if (FSMC_NAND_ReadData((uint8_t *)_pReadBuf, ulPhyPageNo, usAddrInPage, _usSize) == NAND_FAIL)
+    {
+        return NAND_FAIL; /* 读NAND Flash失败 */
+    }
+
+    /* 成功 */
+    return NAND_OK;
+}
+
+/*
+*********************************************************************************************************
+*	函 数 名: NAND_WriteMultiSectors
+*	功能说明: 该函数用于文件系统,连续写多个扇区数据。扇区大小可以是512字节或2048字节
+*	形    参:  	_pBuf : 存放数据的缓冲区的指针
+*				_SectorNo :扇区号
+*				_SectorSize :每个扇区的大小
+*				_SectorCount : 扇区个数
+*	返 回 值: 执行结果:
+*				- NAND_FAIL 表示失败
+*				- NAND_OK 表示成功
+*********************************************************************************************************
+*/
+uint8_t NAND_WriteMultiSectors(uint8_t *_pBuf, uint32_t _SectorNo, uint16_t _SectorSize, uint32_t _SectorCount)
+{
+    uint32_t i;
+    uint32_t usLBN;        /* 逻辑块号 */
+    uint32_t usPBN;        /* 物理块号 */
+    uint32_t uiPhyPageNo;  /* 物理页号 */
+    uint16_t usAddrInPage; /* 页内偏移地址 */
+    uint32_t ulTemp;
+    uint8_t  ucReturn;
+
+    /*
+        HY27UF081G2A = 128M Flash.  有 1024个BLOCK, 每个BLOCK包含64个PAGE, 每个PAGE包含2048+64字节,
+        擦除最小单位是BLOCK, 编程最小单位是字节。
+
+        每个PAGE在逻辑上可以分为4个512字节扇区。
+    */
+
+    for (i = 0; i < _SectorCount; i++)
+    {
+        /* 根据逻辑扇区号和扇区大小计算逻辑块号 */
+        // usLBN = (_SectorNo * _SectorSize) / (NAND_BLOCK_SIZE * NAND_PAGE_SIZE);
+        /* (_SectorNo * _SectorSize) 乘积可能大于32位,因此换下面这种写法 */
+        usLBN = (_SectorNo + i) / (NAND_BLOCK_SIZE * (NAND_PAGE_SIZE / _SectorSize));
+        usPBN = NAND_LBNtoPBN(usLBN); /* 查询LUT表获得物理块号 */
+        if (usPBN >= NAND_BLOCK_COUNT)
+        {
+            /* 没有格式化,usPBN = 0xFFFF */
+            return NAND_FAIL;
+        }
+
+        // ulTemp = ((uint64_t)(_SectorNo + i) * _SectorSize) % (NAND_BLOCK_SIZE * NAND_PAGE_SIZE);
+        ulTemp       = ((_SectorNo + i) % (NAND_BLOCK_SIZE * (NAND_PAGE_SIZE / _SectorSize))) * _SectorSize;
+        uiPhyPageNo  = usPBN * NAND_BLOCK_SIZE + ulTemp / NAND_PAGE_SIZE; /* 计算物理页号 */
+        usAddrInPage = ulTemp % NAND_PAGE_SIZE;                           /* 计算页内偏移地址 */
+
+        /* 如果 _SectorCount > 0, 并且是页面首地址,则可以进行优化 */
+        if (usAddrInPage == 0)
+        {
+            /* 暂未处理 */
+        }
+
+        /* 读出扇区的内容,判断是否全FF */
+        if (FSMC_NAND_ReadData(s_ucTempBuf, uiPhyPageNo, usAddrInPage, _SectorSize) == NAND_FAIL)
+        {
+            return NAND_FAIL; /* 失败 */
+        }
+
+        /* 如果是全0xFF, 则可以直接写入,无需擦除 */
+        if (NAND_IsBufOk(s_ucTempBuf, _SectorSize, 0xFF))
+        {
+            if (FSMC_NAND_WriteData(&_pBuf[i * _SectorSize], uiPhyPageNo, usAddrInPage, _SectorSize) == NAND_FAIL)
+            {
+                /* 将数据写入到另外一个块(空闲块) */
+                ucReturn = NAND_WriteToNewBlock(uiPhyPageNo, &_pBuf[i * _SectorSize], usAddrInPage, _SectorSize);
+                if (ucReturn != NAND_OK)
+                {
+                    return NAND_FAIL; /* 失败 */
+                }
+                continue;
+            }
+
+            /* 标记该块已用 */
+            if (NAND_MarkUsedBlock(uiPhyPageNo) == NAND_FAIL)
+            {
+                /* 标记失败,将数据写入到另外一个块(空闲块) */
+                ucReturn = NAND_WriteToNewBlock(uiPhyPageNo, &_pBuf[i * _SectorSize], usAddrInPage, _SectorSize);
+                if (ucReturn != NAND_OK)
+                {
+                    return NAND_FAIL; /* 失败 */
+                }
+                continue;
+            }
+        }
+        else /* 目标区域已经有数据,不是全FF, 则直接将数据写入另外一个空闲块 */
+        {
+            /* 将数据写入到另外一个块(空闲块) */
+            ucReturn = NAND_WriteToNewBlock(uiPhyPageNo, &_pBuf[i * _SectorSize], usAddrInPage, _SectorSize);
+            if (ucReturn != NAND_OK)
+            {
+                return NAND_FAIL; /* 失败 */
+            }
+            continue;
+        }
+    }
+    return NAND_OK; /* 成功 */
+}
+
+/*
+*********************************************************************************************************
+*	函 数 名: NAND_ReadMultiSectors
+*	功能说明: 该函数用于文件系统,按扇区读数据。读1个或多个扇区,扇区大小可以是512字节或2048字节
+*	形    参:  	_pBuf : 存放读出数据的缓冲区的指针
+*				_SectorNo :扇区号
+*				_SectorSize :每个扇区的大小
+*				_SectorCount : 扇区个数
+*	返 回 值: 执行结果:
+*				- NAND_FAIL 表示失败
+*				- NAND_OK 表示成功
+*********************************************************************************************************
+*/
+uint8_t NAND_ReadMultiSectors(uint8_t *_pBuf, uint32_t _SectorNo, uint16_t _SectorSize, uint32_t _SectorCount)
+{
+    uint32_t i;
+    uint32_t usLBN;        /* 逻辑块号 */
+    uint32_t usPBN;        /* 物理块号 */
+    uint32_t uiPhyPageNo;  /* 物理页号 */
+    uint16_t usAddrInPage; /* 页内偏移地址 */
+    uint32_t ulTemp;
+
+    /*
+        HY27UF081G2A = 128M Flash.  有 1024个BLOCK, 每个BLOCK包含64个PAGE, 每个PAGE包含2048+64字节,
+        擦除最小单位是BLOCK, 编程最小单位是字节。
+
+        每个PAGE在逻辑上可以分为4个512字节扇区。
+    */
+
+    for (i = 0; i < _SectorCount; i++)
+    {
+        /* 根据逻辑扇区号和扇区大小计算逻辑块号 */
+        // usLBN = (_SectorNo * _SectorSize) / (NAND_BLOCK_SIZE * NAND_PAGE_SIZE);
+        /* (_SectorNo * _SectorSize) 乘积可能大于32位,因此换下面这种写法 */
+        usLBN = (_SectorNo + i) / (NAND_BLOCK_SIZE * (NAND_PAGE_SIZE / _SectorSize));
+        usPBN = NAND_LBNtoPBN(usLBN); /* 查询LUT表获得物理块号 */
+        if (usPBN >= NAND_BLOCK_COUNT)
+        {
+            /* 没有格式化,usPBN = 0xFFFF */
+            return NAND_FAIL;
+        }
+
+        ulTemp       = ((uint64_t)(_SectorNo + i) * _SectorSize) % (NAND_BLOCK_SIZE * NAND_PAGE_SIZE);
+        uiPhyPageNo  = usPBN * NAND_BLOCK_SIZE + ulTemp / NAND_PAGE_SIZE; /* 计算物理页号 */
+        usAddrInPage = ulTemp % NAND_PAGE_SIZE;                           /* 计算页内偏移地址 */
+
+        if (FSMC_NAND_ReadData((uint8_t *)&_pBuf[i * _SectorSize], uiPhyPageNo, usAddrInPage, _SectorSize) == NAND_FAIL)
+        {
+            return NAND_FAIL; /* 读NAND Flash失败 */
+        }
+    }
+
+    /* 成功 */
+    return NAND_OK;
+}
+
+/*
+*********************************************************************************************************
+*	函 数 名: NAND_BuildLUT
+*	功能说明: 在内存中创建坏块管理表
+*	形    参:  ZoneNbr :区号
+*	返 回 值: NAND_OK: 成功; 	NAND_FAIL:失败
+*********************************************************************************************************
+*/
+static uint8_t NAND_BuildLUT(void)
+{
+    uint16_t i;
+    uint8_t  buf[VALID_SPARE_SIZE];
+    uint16_t usLBN; /* 逻辑块号 */
+
+    /* */
+    for (i = 0; i < NAND_BLOCK_COUNT; i++)
+    {
+        s_usLUT[i] = 0xFFFF; /* 填充无效值,用于重建LUT后,判断LUT是否合理 */
+    }
+    for (i = 0; i < NAND_BLOCK_COUNT; i++)
+    {
+        /* 读每个块的第1个PAGE,偏移地址为LBN0_OFFSET的数据 */
+        FSMC_NAND_ReadSpare(buf, i * NAND_BLOCK_SIZE, 0, VALID_SPARE_SIZE);
+
+        /* 如果是好块,则记录LBN0 LBN1 */
+        if (buf[BI_OFFSET] == 0xFF)
+        {
+            usLBN = buf[LBN0_OFFSET] + buf[LBN1_OFFSET] * 256; /* 计算读出的逻辑块号 */
+            if (usLBN < NAND_BLOCK_COUNT)
+            {
+                /* 如果已经登记过了,则判定为异常 */
+                if (s_usLUT[usLBN] != 0xFFFF)
+                {
+                    return NAND_FAIL;
+                }
+
+                s_usLUT[usLBN] = i; /* 更新LUT表 */
+            }
+        }
+    }
+
+    /* LUT建立完毕,检查是否合理 */
+    for (i = 0; i < NAND_BLOCK_COUNT; i++)
+    {
+        if (s_usLUT[i] >= NAND_BLOCK_COUNT)
+        {
+            s_usValidDataBlockCount = i;
+            break;
+        }
+    }
+    if (s_usValidDataBlockCount < 100)
+    {
+        /* 错误: 最大的有效逻辑块号小于100。可能是没有格式化 */
+        return NAND_FAIL;
+    }
+    for (; i < s_usValidDataBlockCount; i++)
+    {
+        if (s_usLUT[i] != 0xFFFF)
+        {
+            return NAND_FAIL; /* 错误:LUT表逻辑块号存在跳跃现象,可能是没有格式化 */
+        }
+    }
+
+    /* 重建LUT正常 */
+    return NAND_OK;
+}
+
+/*
+*********************************************************************************************************
+*	函 数 名: NAND_AddrToPhyBlockNo
+*	功能说明: 内存逻辑地址转换为物理块号
+*	形    参:  _ulMemAddr:逻辑内存地址
+*	返 回 值: 物理页号, 如果是 0xFFFFFFFF 则表示错误
+*********************************************************************************************************
+*/
+static uint16_t NAND_AddrToPhyBlockNo(uint32_t _ulMemAddr)
+{
+    uint16_t usLBN; /* 逻辑块号 */
+    uint16_t usPBN; /* 物理块号 */
+
+    usLBN = _ulMemAddr / (NAND_BLOCK_SIZE * NAND_PAGE_SIZE); /* 计算逻辑块号 */
+    /* 如果逻辑块号大于有效的数据块个数则固定返回0xFFFF, 调用该函数的代码应该检查出这种错误 */
+    if (usLBN >= s_usValidDataBlockCount)
+    {
+        return 0xFFFF;
+    }
+    /* 查询LUT表,获得物理块号 */
+    usPBN = s_usLUT[usLBN];
+    return usPBN;
+}
+
+/*
+*********************************************************************************************************
+*	函 数 名: NAND_LBNtoPBN
+*	功能说明: 逻辑块号转换为物理块号
+*	形    参: _uiLBN : 逻辑块号 Logic Block No
+*	返 回 值: 物理块号, 如果是 0xFFFFFFFF 则表示错误
+*********************************************************************************************************
+*/
+static uint16_t NAND_LBNtoPBN(uint32_t _uiLBN)
+{
+    uint16_t usPBN; /* 物理块号 */
+
+    /* 如果逻辑块号大于有效的数据块个数则固定返回0xFFFF, 调用该函数的代码应该检查出这种错误 */
+    if (_uiLBN >= s_usValidDataBlockCount)
+    {
+        return 0xFFFF;
+    }
+    /* 查询LUT表,获得物理块号 */
+    usPBN = s_usLUT[_uiLBN];
+    return usPBN;
+}
+
+/*
+*********************************************************************************************************
+*	函 数 名: NAND_FindFreeBlock
+*	功能说明: 从最后一个块开始,查找一个可用的块。
+*	形    参:  ZoneNbr :区号
+*	返 回 值: 块号,如果是0xFFFF表示失败
+*********************************************************************************************************
+*/
+static uint16_t NAND_FindFreeBlock(void)
+{
+    uint16_t i;
+    uint16_t n;
+
+    n = NAND_BLOCK_COUNT - 1;
+    for (i = 0; i < NAND_BLOCK_COUNT; i++)
+    {
+        if (NAND_IsFreeBlock(n))
+        {
+            return n;
+        }
+        n--;
+    }
+    return 0xFFFF;
+}
+
+/*
+*********************************************************************************************************
+*	函 数 名: NAND_IsBufOk
+*	功能说明: 判断内存缓冲区的数据是否全部为指定值
+*	形    参:  - _pBuf : 输入缓冲区
+*			  - _ulLen : 缓冲区长度
+*			  - __ucValue : 缓冲区每个单元的正确数值
+*	返 回 值: 1 :全部正确; 0 :不正确
+*********************************************************************************************************
+*/
+static uint8_t NAND_IsBufOk(uint8_t *_pBuf, uint32_t _ulLen, uint8_t _ucValue)
+{
+    uint32_t i;
+
+    for (i = 0; i < _ulLen; i++)
+    {
+        if (_pBuf[i] != _ucValue)
+        {
+            return 0;
+        }
+    }
+
+    return 1;
+}
+
+/*
+*********************************************************************************************************
+*	函 数 名: NAND_IsBadBlock
+*	功能说明: 根据坏块标记检测NAND Flash指定的块是否坏块
+*	形    参: _ulBlockNo :块号 0 - 1023 (对于128M字节,2K Page的NAND Flash,有1024个块)
+*	返 回 值: 0 :该块可用; 1 :该块是坏块
+*********************************************************************************************************
+*/
+uint8_t NAND_IsBadBlock(uint32_t _ulBlockNo)
+{
+    uint8_t ucFlag;
+
+    /* 如果NAND Flash出厂前已经标注为坏块了,则就认为是坏块 */
+    FSMC_NAND_ReadSpare(&ucFlag, _ulBlockNo * NAND_BLOCK_SIZE, BI_OFFSET, 1);
+    if (ucFlag != 0xFF)
+    {
+        return 1;
+    }
+
+    FSMC_NAND_ReadSpare(&ucFlag, _ulBlockNo * NAND_BLOCK_SIZE + 1, BI_OFFSET, 1);
+    if (ucFlag != 0xFF)
+    {
+        return 1;
+    }
+    return 0; /* 是好块 */
+}
+
+/*
+*********************************************************************************************************
+*	函 数 名: NAND_IsFreeBlock
+*	功能说明: 根据坏块标记和USED标志检测是否可用块
+*	形    参: _ulBlockNo :块号 0 - 1023 (对于128M字节,2K Page的NAND Flash,有1024个块)
+*	返 回 值: 1 :该块可用; 0 :该块是坏块或者已占用
+*********************************************************************************************************
+*/
+static uint8_t NAND_IsFreeBlock(uint32_t _ulBlockNo)
+{
+    uint8_t ucFlag;
+
+    /* 如果NAND Flash出厂前已经标注为坏块了,则就认为是坏块 */
+    if (NAND_IsBadBlock(_ulBlockNo))
+    {
+        return 0;
+    }
+
+    FSMC_NAND_ReadPage(&ucFlag, _ulBlockNo * NAND_BLOCK_SIZE, USED_OFFSET, 1);
+    if (ucFlag == 0xFF)
+    {
+        return 1;
+    }
+    return 0;
+}
+
+/*
+*********************************************************************************************************
+*	函 数 名: NAND_ScanBlock
+*	功能说明: 扫描测试NAND Flash指定的块
+*			【扫描测试算法】
+*			1) 第1个块(包括主数据区和备用数据区),擦除后检测是否全0xFF, 正确的话继续测试改块,否则该块
+                是坏块,函数返回
+*			2) 当前块写入全 0x00,然后读取检测,正确的话继续测试改块,否则退出
+*			3) 重复第(2)步;如果循环次数达50次都没有发生错误,那么该块正常,函数返回,否则该块是坏块,
+*				函数返回
+*			【注意】
+*			1) 该函数测试完毕后,会删除块内所有数据,即变为全0xFF;
+*			2) 该函数除了测试主数据区外,也对备用数据区进行测试。
+*			3) 擦写测试循环次数可以宏指定。#define BAD_BALOK_TEST_CYCLE 50
+*	形    参:  _ulPageNo :页号 0 - 65535 (对于128M字节,2K Page的NAND Flash,有1024个块)
+*	返 回 值: NAND_OK :该块可用; NAND_FAIL :该块是坏块
+*********************************************************************************************************
+*/
+uint8_t NAND_ScanBlock(uint32_t _ulBlockNo)
+{
+    uint32_t i, k;
+    uint32_t ulPageNo;
+
+#if 1
+    /* 如果NAND Flash出厂前已经标注为坏块了,则就认为是坏块 */
+    if (NAND_IsBadBlock(_ulBlockNo))
+    {
+        return NAND_FAIL;
+    }
+#endif
+
+    /* 下面的代码将通过反复擦除、编程的方式来测试NAND Flash每个块的可靠性 */
+    memset(s_ucTempBuf, 0x00, NAND_PAGE_TOTAL_SIZE);
+    for (i = 0; i < BAD_BALOK_TEST_CYCLE; i++)
+    {
+        /* 第1步:擦除这个块 */
+        if (FSMC_NAND_EraseBlock(_ulBlockNo) != NAND_READY)
+        {
+            return NAND_FAIL;
+        }
+
+        /* 第2步:读出块内每个page的数据,并判断是否全0xFF */
+        ulPageNo = _ulBlockNo * NAND_BLOCK_SIZE; /* 计算该块第1个页的页号 */
+        for (k = 0; k < NAND_BLOCK_SIZE; k++)
+        {
+            /* 读出整页数据 */
+            FSMC_NAND_ReadPage(s_ucTempBuf, ulPageNo, 0, NAND_PAGE_TOTAL_SIZE);
+
+            /* 判断存储单元是不是全0xFF */
+            if (NAND_IsBufOk(s_ucTempBuf, NAND_PAGE_TOTAL_SIZE, 0xFF) != NAND_OK)
+            {
+                return NAND_FAIL;
+            }
+
+            ulPageNo++; /* 继续写下一个页 */
+        }
+
+        /* 第2步:写全0,并读回判断是否全0 */
+        ulPageNo = _ulBlockNo * NAND_BLOCK_SIZE; /* 计算该块第1个页的页号 */
+        for (k = 0; k < NAND_BLOCK_SIZE; k++)
+        {
+            /* 填充buf[]缓冲区为全0,并写入NAND Flash */
+            memset(s_ucTempBuf, 0x00, NAND_PAGE_TOTAL_SIZE);
+            if (FSMC_NAND_WritePage(s_ucTempBuf, ulPageNo, 0, NAND_PAGE_TOTAL_SIZE) != NAND_OK)
+            {
+                return NAND_FAIL;
+            }
+
+            /* 读出整页数据, 判断存储单元是不是全0x00 */
+            FSMC_NAND_ReadPage(s_ucTempBuf, ulPageNo, 0, NAND_PAGE_TOTAL_SIZE);
+            if (NAND_IsBufOk(s_ucTempBuf, NAND_PAGE_TOTAL_SIZE, 0x00) != NAND_OK)
+            {
+                return NAND_FAIL;
+            }
+
+            ulPageNo++; /* 继续一个页 */
+        }
+    }
+
+    /* 最后一步:擦除整个块 */
+    if (FSMC_NAND_EraseBlock(_ulBlockNo) != NAND_READY)
+    {
+        return NAND_FAIL;
+    }
+    ulPageNo = _ulBlockNo * NAND_BLOCK_SIZE; /* 计算该块第1个页的页号 */
+    for (k = 0; k < NAND_BLOCK_SIZE; k++)
+    {
+        /* 读出整页数据 */
+        FSMC_NAND_ReadPage(s_ucTempBuf, ulPageNo, 0, NAND_PAGE_TOTAL_SIZE);
+
+        /* 判断存储单元是不是全0xFF */
+        if (NAND_IsBufOk(s_ucTempBuf, NAND_PAGE_TOTAL_SIZE, 0xFF) != NAND_OK)
+        {
+            return NAND_FAIL;
+        }
+
+        ulPageNo++; /* 继续写下一个页 */
+    }
+
+    return NAND_OK;
+}
+
+/*
+*********************************************************************************************************
+*	函 数 名: NAND_MarkUsedBlock
+*	功能说明: 标记NAND Flash指定的块为已用块
+*	形    参: _ulBlockNo :块号 0 - 1023 (对于128M字节,2K Page的NAND Flash,有1024个块)
+*	返 回 值: NAND_OK:标记成功; NAND_FAIL:标记失败,上级软件应该进行坏块处理。
+*********************************************************************************************************
+*/
+static uint8_t NAND_MarkUsedBlock(uint32_t _ulBlockNo)
+{
+    uint32_t ulPageNo;
+    uint8_t  ucFlag;
+
+    /* 计算块的第1个页号 */
+    ulPageNo = _ulBlockNo * NAND_BLOCK_SIZE; /* 计算该块第1个页的页号 */
+
+    /* 块内第1个page备用区的第6个字节写入非0xFF数据表示坏块 */
+    ucFlag = NAND_USED_BLOCK_FLAG;
+    if (FSMC_NAND_WriteSpare(&ucFlag, ulPageNo, USED_OFFSET, 1) == NAND_FAIL)
+    {
+        /* 如果标记失败,则需要标注这个块为坏块 */
+        return NAND_FAIL;
+    }
+    return NAND_OK;
+}
+
+/*
+*********************************************************************************************************
+*	函 数 名: NAND_MarkBadBlock
+*	功能说明: 标记NAND Flash指定的块为坏块
+*	形    参: _ulBlockNo :块号 0 - 1023 (对于128M字节,2K Page的NAND Flash,有1024个块)
+*	返 回 值: 固定NAND_OK
+*********************************************************************************************************
+*/
+static void NAND_MarkBadBlock(uint32_t _ulBlockNo)
+{
+    uint32_t ulPageNo;
+    uint8_t  ucFlag;
+
+    /* 计算块的第1个页号 */
+    ulPageNo = _ulBlockNo * NAND_BLOCK_SIZE; /* 计算该块第1个页的页号 */
+
+    /* 块内第1个page备用区的第6个字节写入非0xFF数据表示坏块 */
+    ucFlag = NAND_BAD_BLOCK_FLAG;
+    if (FSMC_NAND_WriteSpare(&ucFlag, ulPageNo, BI_OFFSET, 1) == NAND_FAIL)
+    {
+        /* 如果第1个页标记失败,则在第2个页标记 */
+        FSMC_NAND_WriteSpare(&ucFlag, ulPageNo + 1, BI_OFFSET, 1);
+    }
+}
+
+/*
+*********************************************************************************************************
+*	函 数 名: NAND_Format
+*	功能说明: NAND Flash格式化,擦除所有的数据,重建LUT
+*	形    参:  无
+*	返 回 值: NAND_OK : 成功; NAND_Fail :失败(一般是坏块数量过多导致)
+*********************************************************************************************************
+*/
+uint8_t NAND_Format(void)
+{
+    uint16_t i, n;
+    uint16_t usGoodBlockCount;
+
+    /* 擦除每个块 */
+    usGoodBlockCount = 0;
+    for (i = 0; i < NAND_BLOCK_COUNT; i++)
+    {
+        /* 如果是好块,则擦除 */
+        if (!NAND_IsBadBlock(i))
+        {
+            FSMC_NAND_EraseBlock(i);
+            usGoodBlockCount++;
+        }
+    }
+
+    /* 如果好块的数量少于100,则NAND Flash报废 */
+    if (usGoodBlockCount < 100)
+    {
+        return NAND_FAIL;
+    }
+
+    usGoodBlockCount = (usGoodBlockCount * 98) / 100; /* 98%的好块用于存储数据 */
+
+    /* 重新搜索一次 */
+    n = 0; /* 统计已标注的好块 */
+    for (i = 0; i < NAND_BLOCK_COUNT; i++)
+    {
+        if (!NAND_IsBadBlock(i))
+        {
+            /* 如果是好块,则在该块的第1个PAGE的LBN0 LBN1处写入n值 (前面已经执行了块擦除) */
+            FSMC_NAND_WriteSpare((uint8_t *)&n, i * NAND_BLOCK_SIZE, LBN0_OFFSET, 2);
+            n++;
+
+            /* 计算并写入每个扇区的ECC值 (暂时未作)*/
+
+            if (n == usGoodBlockCount)
+            {
+                break;
+            }
+        }
+    }
+
+    NAND_BuildLUT(); /* 初始化LUT表 */
+    return NAND_OK;
+}
+
+/*
+*********************************************************************************************************
+*	函 数 名: NAND_FormatCapacity
+*	功能说明: NAND Flash格式化后的有效容量
+*	形    参:  无
+*	返 回 值: NAND_OK : 成功; NAND_Fail :失败(一般是坏块数量过多导致)
+*********************************************************************************************************
+*/
+uint32_t NAND_FormatCapacity(void)
+{
+    uint16_t usCount;
+
+    /* 计算用于存储数据的数据块个数,按照总有效块数的98%来计算 */
+    usCount = (s_usValidDataBlockCount * DATA_BLOCK_PERCENT) / 100;
+
+    return (usCount * NAND_BLOCK_SIZE * NAND_PAGE_SIZE);
+}
+
+/*
+*********************************************************************************************************
+*	函 数 名: NAND_DispBadBlockInfo
+*	功能说明: 通过串口打印出NAND Flash的坏块信息
+*	形    参:  无
+*	返 回 值: 无
+*********************************************************************************************************
+*/
+void NAND_DispBadBlockInfo(void)
+{
+    uint32_t id;
+    uint32_t i;
+    uint32_t n;
+
+    FSMC_NAND_Init(); /* 初始化FSMC */
+
+    id = NAND_ReadID();
+
+    printf("NAND Flash ID = 0x%04X, Type = ", id);
+    if (id == HY27UF081G2A)
+    {
+        printf("HY27UF081G2A\r\n  1024 Blocks, 64 pages per block, 2048 + 64 bytes per page\r\n");
+    }
+    else if (id == K9F1G08U0A)
+    {
+        printf("K9F1G08U0A\r\n  1024 Blocks, 64 pages per block, 2048 + 64 bytes per page\r\n");
+    }
+    else if (id == K9F1G08U0B)
+    {
+        printf("K9F1G08U0B\r\n  1024 Blocks, 64 pages per block, 2048 + 64 bytes per page\r\n");
+    }
+    else
+    {
+        printf("unkonow\r\n");
+        return;
+    }
+
+    printf("Block Info : 0 is OK, * is Bad\r\n");
+    n = 0; /* 坏块统计 */
+    for (i = 0; i < NAND_BLOCK_COUNT; i++)
+    {
+        if (NAND_IsBadBlock(i))
+        {
+            printf("*");
+            n++;
+        }
+        else
+        {
+            printf("0");
+        }
+
+        if (((i + 1) % 8) == 0)
+        {
+            printf(" ");
+        }
+
+        if (((i + 1) % 64) == 0)
+        {
+            printf("\r\n");
+        }
+    }
+    printf("Bad Block Count = %d\r\n", n);
+}
+
+/*
+*********************************************************************************************************
+*	函 数 名: NAND_DispPhyPageData
+*	功能说明: 通过串口打印出指定页的数据(2048+64)
+*	形    参: _uiPhyPageNo : 物理页号
+*	返 回 值: 无
+*********************************************************************************************************
+*/
+void NAND_DispPhyPageData(uint32_t _uiPhyPageNo)
+{
+    uint32_t i, n;
+    uint32_t ulBlockNo;
+    uint16_t usOffsetPageNo;
+
+    ulBlockNo      = _uiPhyPageNo / NAND_BLOCK_SIZE; /* 根据物理页号反推块号 */
+    usOffsetPageNo = _uiPhyPageNo % NAND_BLOCK_SIZE; /* 根据物理页号计算物理页号在块内偏移页号 */
+
+    if (NAND_OK != FSMC_NAND_ReadPage(s_ucTempBuf, _uiPhyPageNo, 0, NAND_PAGE_TOTAL_SIZE))
+    {
+        printf("FSMC_NAND_ReadPage Failed() \r\n");
+        return;
+    }
+
+    printf("Block = %d, Page = %d\r\n", ulBlockNo, usOffsetPageNo);
+
+    /* 打印前面 2048字节数据,每512字节空一行 */
+    for (n = 0; n < 4; n++)
+    {
+        for (i = 0; i < 512; i++)
+        {
+            printf(" %02X", s_ucTempBuf[i + n * 512]);
+
+            if ((i & 31) == 31)
+            {
+                printf("\r\n"); /* 每行显示32字节数据 */
+            }
+            else if ((i & 31) == 15)
+            {
+                printf(" - ");
+            }
+        }
+        printf("\r\n");
+    }
+
+    /* 打印前面 2048字节数据,每512字节空一行 */
+    for (i = 0; i < 64; i++)
+    {
+        printf(" %02X", s_ucTempBuf[i + 2048]);
+
+        if ((i & 15) == 15)
+        {
+            printf("\r\n"); /* 每行显示32字节数据 */
+        }
+    }
+}
+
+/*
+*********************************************************************************************************
+*	函 数 名: NAND_DispLogicPageData
+*	功能说明: 通过串口打印出指定页的数据(2048+64)
+*	形    参: _uiLogicPageNo : 逻辑页号
+*	返 回 值: 无
+*********************************************************************************************************
+*/
+void NAND_DispLogicPageData(uint32_t _uiLogicPageNo)
+{
+    uint32_t uiPhyPageNo;
+    uint16_t usLBN; /* 逻辑块号 */
+    uint16_t usPBN; /* 物理块号 */
+
+    usLBN = _uiLogicPageNo / NAND_BLOCK_SIZE;
+    usPBN = NAND_LBNtoPBN(usLBN); /* 查询LUT表获得物理块号 */
+    if (usPBN >= NAND_BLOCK_COUNT)
+    {
+        /* 没有格式化,usPBN = 0xFFFF */
+        return;
+    }
+
+    printf("LogicBlock = %d, PhyBlock = %d\r\n", _uiLogicPageNo, usPBN);
+
+    /* 计算物理页号 */
+    uiPhyPageNo = usPBN * NAND_BLOCK_SIZE + _uiLogicPageNo % NAND_BLOCK_SIZE;
+    NAND_DispPhyPageData(uiPhyPageNo); /* 显示指定页数据 */
+}
+
+/***************************** 安富莱电子 www.armfly.com (END OF FILE) *********************************/

+ 208 - 0
User/Bsp/nand_flash/nand_flash.h

@@ -0,0 +1,208 @@
+/*
+*********************************************************************************************************
+*
+*	模块名称 : NAND Flash 驱动模块
+*	文件名称 : bsp_nand.h
+*	版    本 : V1.0
+*	说    明 : 头文件
+*
+*	Copyright (C), 2013-2014, 安富莱电子 www.armfly.com
+*
+*********************************************************************************************************
+*/
+
+#ifndef __NAND_H
+#define __NAND_H
+
+#include "interface.h"
+
+typedef struct
+{
+    uint8_t Maker_ID;
+    uint8_t Device_ID;
+    uint8_t Third_ID;
+    uint8_t Fourth_ID;
+} NAND_IDTypeDef;
+
+typedef struct
+{
+    uint16_t Zone;
+    uint16_t Block;
+    uint16_t Page;
+} NAND_ADDRESS_T;
+
+/* NAND Flash 型号 */
+typedef enum
+{
+    HY27UF081G2A = 0,
+    K9F1G08U0A,
+    K9F1G08U0B,
+    NAND_UNKNOW
+} NAND_TYPE_E;
+
+#define NAND_TYPE HY27UF081G2A
+
+/*
+    定义有效的 NAND ID
+    HY27UF081G2A  	= 0xAD 0xF1 0x80 0x1D
+    K9F1G08U0A		= 0xEC 0xF1 0x80 0x15
+    K9F1G08U0B		= 0xEC 0xF1 0x00 0x95
+*/
+#define NAND_MAKER_ID  0xAD
+#define NAND_DEVICE_ID 0xF1
+#define NAND_THIRD_ID  0x80
+#define NAND_FOURTH_ID 0x1D
+
+#define HY27UF081G2A 0xADF1801D
+#define K9F1G08U0A   0xECF18015
+#define K9F1G08U0B   0xECF10095
+
+/* Exported constants --------------------------------------------------------*/
+/* NAND Area definition  for STM3210E-EVAL Board RevD */
+#define CMD_AREA  (uint32_t)(1 << 16) /* A16 = CLE  high */
+#define ADDR_AREA (uint32_t)(1 << 17) /* A17 = ALE high */
+#define DATA_AREA ((uint32_t)0x00000000)
+
+/* FSMC NAND memory command */
+#define NAND_CMD_AREA_A     ((uint8_t)0x00)
+#define NAND_CMD_AREA_B     ((uint8_t)0x01)
+#define NAND_CMD_AREA_C     ((uint8_t)0x50)
+#define NAND_CMD_AREA_TRUE1 ((uint8_t)0x30)
+
+#define NAND_CMD_WRITE0      ((uint8_t)0x80)
+#define NAND_CMD_WRITE_TRUE1 ((uint8_t)0x10)
+
+#define NAND_CMD_ERASE0 ((uint8_t)0x60)
+#define NAND_CMD_ERASE1 ((uint8_t)0xD0)
+
+#define NAND_CMD_READID ((uint8_t)0x90)
+
+#define NAND_CMD_LOCK_STATUS ((uint8_t)0x7A)
+#define NAND_CMD_RESET       ((uint8_t)0xFF)
+
+/* NAND memory status */
+#define NAND_BUSY          ((uint8_t)0x00)
+#define NAND_ERROR         ((uint8_t)0x01)
+#define NAND_READY         ((uint8_t)0x40)
+#define NAND_TIMEOUT_ERROR ((uint8_t)0x80)
+
+/* FSMC NAND memory parameters */
+/* 用于HY27UF081G2A    K9F1G08 */
+#if NAND_TYPE == HY27UF081G2A
+#define NAND_PAGE_SIZE       ((uint16_t)0x0800) /* 2 * 1024 bytes per page w/o Spare Area */
+#define NAND_BLOCK_SIZE      ((uint16_t)0x0040) /* 64 pages per block */
+#define NAND_ZONE_SIZE       ((uint16_t)0x0400) /* 1024 Block per zone */
+#define NAND_SPARE_AREA_SIZE ((uint16_t)0x0040) /* last 64 bytes as spare area */
+#define NAND_MAX_ZONE        ((uint16_t)0x0001) /* 1 zones of 1024 block */
+
+/* 命令代码定义 */
+#define NAND_CMD_COPYBACK_A ((uint8_t)0x00) /* PAGE COPY-BACK 命令序列 */
+#define NAND_CMD_COPYBACK_B ((uint8_t)0x35)
+#define NAND_CMD_COPYBACK_C ((uint8_t)0x85)
+#define NAND_CMD_COPYBACK_D ((uint8_t)0x10)
+
+#define NAND_CMD_STATUS ((uint8_t)0x70) /* 读NAND Flash的状态字 */
+
+#define MAX_PHY_BLOCKS_PER_ZONE 1024 /* 每个区最大物理块号 */
+#define MAX_LOG_BLOCKS_PER_ZONE 1000 /* 每个区最大逻辑块号 */
+
+#define NAND_BLOCK_COUNT     1024                                    /* 块个数 */
+#define NAND_PAGE_TOTAL_SIZE (NAND_PAGE_SIZE + NAND_SPARE_AREA_SIZE) /* 页面总大小 */
+
+#else
+#define NAND_PAGE_SIZE       ((uint16_t)0x0200) /* 512 bytes per page w/o Spare Area */
+#define NAND_BLOCK_SIZE      ((uint16_t)0x0020) /* 32x512 bytes pages per block */
+#define NAND_ZONE_SIZE       ((uint16_t)0x0400) /* 1024 Block per zone */
+#define NAND_SPARE_AREA_SIZE ((uint16_t)0x0010) /* last 16 bytes as spare area */
+#define NAND_MAX_ZONE        ((uint16_t)0x0004) /* 4 zones of 1024 block */
+#endif
+
+#define NAND_BAD_BLOCK_FLAG  0x00 /* 块内第1个page备用区的第1个字节写入非0xFF数据表示坏块 */
+#define NAND_USED_BLOCK_FLAG 0xF0 /* 块内第1个page备用区的第2个字节写入非0xFF数据表示已使用的块 */
+
+#define BI_OFFSET        0 /* 块内第1个page备用区的第1个字节是坏块标志 */
+#define USED_OFFSET      1 /* 块内第1个page备用区的第1个字节是已用标志 */
+#define LBN0_OFFSET      2 /* 块内第1个page备用区的第3个字节表示逻辑块号低8bit */
+#define LBN1_OFFSET      3 /* 块内第1个page备用区的第4个字节表示逻辑块号高8bit */
+#define VALID_SPARE_SIZE 4 /* 实际使用的备用区大小,用于函数内部声明数据缓冲区大小 */
+
+/* FSMC NAND memory address computation */
+#define ADDR_1st_CYCLE(ADDR) (uint8_t)((ADDR)&0xFF)               /* 1st addressing cycle */
+#define ADDR_2nd_CYCLE(ADDR) (uint8_t)(((ADDR)&0xFF00) >> 8)      /* 2nd addressing cycle */
+#define ADDR_3rd_CYCLE(ADDR) (uint8_t)(((ADDR)&0xFF0000) >> 16)   /* 3rd addressing cycle */
+#define ADDR_4th_CYCLE(ADDR) (uint8_t)(((ADDR)&0xFF000000) >> 24) /* 4th addressing cycle */
+
+/* Exported macro ------------------------------------------------------------*/
+/* Exported functions ------------------------------------------------------- */
+#define NAND_OK   0
+#define NAND_FAIL 1
+
+#define FREE_BLOCK  (1 << 12)
+#define BAD_BLOCK   (1 << 13)
+#define VALID_BLOCK (1 << 14)
+#define USED_BLOCK  (1 << 15)
+
+/*
+        LUT[]的格式:
+        uint16_t usGoodBlockFirst;				 // 第1个好块
+        uint16_t usDataBlockCount;	             // 可用于数据存储的块个数, 从第2个好块开始
+        uint16_t usBakBlockStart;				 // 备份块起始块号
+        uint32_t usPhyBlockNo[ulDataBlockCount]; // 物理块号数组。低字节在前,高字节在后。
+*/
+#define DATA_BLOCK_PERCENT   98 /* 数据块占总有效块数的百分比 */
+#define LUT_FIRST_GOOD_BLOCK 0  /* LUT[] 第1个单元用于存储第1个有效块号 */
+#define LUT_DATA_BLOCK_COUNT 1  /* LUT[] 第2个单元用于存储第有效块号个数 */
+#define LUT_BAK_BLOCK_START  2  /* LUT[] 第3个单元用于备份区起始块号 */
+#define LUT_GOOD_BLOCK_START 3  /* LUT[] 第4个单元用于数据区起始块号 */
+
+/* Private Structures---------------------------------------------------------*/
+typedef struct __SPARE_AREA
+{
+    uint16_t LogicalIndex;
+    uint16_t DataStatus;
+    uint16_t BlockStatus;
+} SPARE_AREA;
+
+typedef enum
+{
+    WRITE_IDLE = 0,
+    POST_WRITE,
+    PRE_WRITE,
+    WRITE_CLEANUP,
+    WRITE_ONGOING
+} WRITE_STATE;
+
+typedef enum
+{
+    OLD_BLOCK = 0,
+    UNUSED_BLOCK
+} BLOCK_STATE;
+
+/* Private macro --------------------------------------------------------------*/
+//#define WEAR_LEVELLING_SUPPORT		磨损平衡支持
+#define WEAR_DEPTH    10 /* 磨损深度 */
+#define PAGE_TO_WRITE (Transfer_Length / 512)
+
+#define BAD_BALOK_TEST_CYCLE 5 /* 判别坏块算法的重复擦写次数  */
+
+/* Private variables ----------------------------------------------------------*/
+/* Private function prototypes ------------------------------------------------*/
+/* exported functions ---------------------------------------------------------*/
+uint8_t  nand_flash_init(void);
+uint8_t  NAND_Write(uint32_t _ulMemAddr, uint32_t *_pWriteBuf, uint16_t _usSize);
+uint8_t  NAND_Read(uint32_t _ulMemAddr, uint32_t *_pReadBuf, uint16_t _usSize);
+uint8_t  NAND_Format(void);
+void     NAND_DispBadBlockInfo(void);
+uint8_t  NAND_ScanBlock(uint32_t _ulPageNo);
+uint32_t NAND_FormatCapacity(void);
+uint32_t NAND_ReadID(void);
+
+void NAND_DispPhyPageData(uint32_t _uiPhyPageNo);
+void NAND_DispLogicPageData(uint32_t _uiLogicPageNo);
+
+uint8_t NAND_WriteMultiSectors(uint8_t *_pBuf, uint32_t _SectorNo, uint16_t _SectorSize, uint32_t _SectorCount);
+uint8_t NAND_ReadMultiSectors(uint8_t *_pBuf, uint32_t _SectorNo, uint16_t _SectorSize, uint32_t _SectorCount);
+
+#endif /* __FSMC_NAND_H */
+
+/***************************** 安富莱电子 www.armfly.com (END OF FILE) *********************************/

+ 0 - 2
User/Bsp/rtc/rtc.c

@@ -1,6 +1,4 @@
 #include "rtc.h"
-#include "led.h"
-//#include "delay.h"
 #include "dwt.h"
 #include <time.h>
 

+ 214 - 0
User/Bsp/uart/uart6.c

@@ -0,0 +1,214 @@
+/**
+************************************************************************************************
+* @文件    : uart3.c
+* @作者    : 樊春春
+* @版本    : V1.0
+* @时间    : 2022/05/31 20:15:29
+* @邮箱    : [email protected]
+* @说明    : 485底层驱动
+************************************************************************************************
+**/
+
+#include "uart6.h"
+
+static uint8_t uart6_tx_buf[UART6_TX_LEN]  = {0};
+static uint8_t uart6_rx_buf[UART6_REC_LEN] = {0};
+
+UartFrame_TypeDef Uart6FrameStruct[UART6_MAX_MSG_NUM];
+
+extern OS_EVENT *uart6_mbox;
+
+void uart6_nvic_config(void)
+{
+    NVIC_InitTypeDef NVIC_InitStructure;
+    // Usart3 NVIC 配置
+    NVIC_InitStructure.NVIC_IRQChannel                   = USART6_IRQn; //串口3中断通道
+    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;           //抢占优先级
+    NVIC_InitStructure.NVIC_IRQChannelSubPriority        = 0;           //子优先级
+    NVIC_InitStructure.NVIC_IRQChannelCmd                = ENABLE;      // IRQ通道使能
+    NVIC_Init(&NVIC_InitStructure);
+}
+
+void uart6_dma_init(void)
+{
+    DMA_InitTypeDef DMA_InitStructure;
+    if ((uint32_t)UART6_DMA_TXCH > (uint32_t)DMA2)
+    {
+        RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE); // DMA2时钟使能
+    }
+    else
+    {
+        RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE); // DMA1时钟使能
+    }
+    DMA_DeInit(UART6_DMA_TXCH);
+    while (DMA_GetCmdStatus(UART6_DMA_TXCH) != DISABLE)
+    {
+    } //等待DMA可配置
+    /* 配置 DMA Stream */
+    DMA_InitStructure.DMA_Channel            = UART6_DMA;                   //通道选择
+    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&USART6->DR;       // DMA外设地址
+    DMA_InitStructure.DMA_Memory0BaseAddr    = (uint32_t)uart6_tx_buf;      // DMA 存储器0地址
+    DMA_InitStructure.DMA_DIR                = DMA_DIR_MemoryToPeripheral;  //存储器到外设模式
+    DMA_InitStructure.DMA_BufferSize         = UART6_TX_LEN;                //数据传输量
+    DMA_InitStructure.DMA_PeripheralInc      = DMA_PeripheralInc_Disable;   //外设非增量模式
+    DMA_InitStructure.DMA_MemoryInc          = DMA_MemoryInc_Enable;        //存储器增量模式
+    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //外设数据长度:8位
+    DMA_InitStructure.DMA_MemoryDataSize     = DMA_MemoryDataSize_Byte;     //存储器数据长度:8位
+    DMA_InitStructure.DMA_Mode               = DMA_Mode_Normal;             // 使用普通模式
+    DMA_InitStructure.DMA_Priority           = DMA_Priority_Medium;         //中等优先级6
+    DMA_InitStructure.DMA_FIFOMode           = DMA_FIFOMode_Disable;
+    DMA_InitStructure.DMA_FIFOThreshold      = DMA_FIFOThreshold_Full;
+    DMA_InitStructure.DMA_MemoryBurst        = DMA_MemoryBurst_Single;     //存储器突发单次传输
+    DMA_InitStructure.DMA_PeripheralBurst    = DMA_PeripheralBurst_Single; //外设突发单次传输
+    DMA_Init(UART6_DMA_TXCH, &DMA_InitStructure);                          //初始化DMA Stream
+
+    if ((uint32_t)UART6_DMA_RXCH > (uint32_t)DMA2) //得到当前stream是属于DMA2还是DMA1
+    {
+        RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE); // DMA2时钟使能
+    }
+    else
+    {
+        RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE); // DMA1时钟使能
+    }
+
+    DMA_DeInit(UART6_DMA_RXCH);
+    while (DMA_GetCmdStatus(UART6_DMA_RXCH) != DISABLE)
+    {
+    } //等待DMA可配置
+    /* 配置 DMA Stream */
+    DMA_InitStructure.DMA_Channel            = UART6_DMA;                   //通道选择
+    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&USART6->DR;       // DMA外设地址
+    DMA_InitStructure.DMA_Memory0BaseAddr    = (uint32_t)uart6_rx_buf;      // DMA 存储器0地址
+    DMA_InitStructure.DMA_DIR                = DMA_DIR_PeripheralToMemory;  //外设到存储器模式
+    DMA_InitStructure.DMA_BufferSize         = UART6_REC_LEN;               //数据传输量
+    DMA_InitStructure.DMA_PeripheralInc      = DMA_PeripheralInc_Disable;   //外设非增量模式
+    DMA_InitStructure.DMA_MemoryInc          = DMA_MemoryInc_Enable;        //存储器增量模式
+    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //外设数据长度:8位
+    DMA_InitStructure.DMA_MemoryDataSize     = DMA_MemoryDataSize_Byte;     //存储器数据长度:8位
+    DMA_InitStructure.DMA_Mode               = DMA_Mode_Circular;           // 使用循环模式
+    DMA_InitStructure.DMA_Priority           = DMA_Priority_Medium;         //中等优先级
+    DMA_InitStructure.DMA_FIFOMode           = DMA_FIFOMode_Disable;
+    DMA_InitStructure.DMA_FIFOThreshold      = DMA_FIFOThreshold_Full;
+    DMA_InitStructure.DMA_MemoryBurst        = DMA_MemoryBurst_Single;     //存储器突发单次传输
+    DMA_InitStructure.DMA_PeripheralBurst    = DMA_PeripheralBurst_Single; //外设突发单次传输
+    DMA_Init(UART6_DMA_RXCH, &DMA_InitStructure);
+}
+
+void uart6_config(void)
+{
+    USART_InitTypeDef USART_InitStructure;
+    USART_InitStructure.USART_BaudRate            = 9600;                           //波特率设置
+    USART_InitStructure.USART_WordLength          = USART_WordLength_8b;            //字长为8位数据格式
+    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(USART6, &USART_InitStructure);                                       //初始化串口1
+
+    USART_Cmd(USART6, ENABLE); //使能串口3
+}
+
+void Uart6DMA_Enable(DMA_Stream_TypeDef *DMA_Streamx, u16 count)
+{
+    DMA_Cmd(DMA_Streamx, DISABLE); //关闭DMA传输
+    while (DMA_GetCmdStatus(DMA_Streamx) != DISABLE)
+    {
+    } //确保DMA可以被设置
+
+    DMA_SetCurrDataCounter(DMA_Streamx, count); //数据传输量
+    DMA_Cmd(DMA_Streamx, ENABLE);               //开启DMA传输
+}
+
+void uart6_init(void)
+{
+    uart6_config();
+    uart6_dma_init();
+    uart6_nvic_config();
+    /* USART DMA enable*/
+    USART_DMACmd(USART6, USART_DMAReq_Rx, ENABLE); // 使能串口DMA接收数据
+    Uart6DMA_Enable(UART6_DMA_RXCH, UART6_REC_LEN);
+
+    /*configure DMA interrupt*/
+    USART_ITConfig(USART6, USART_IT_IDLE, ENABLE); //开启空闲中断
+    USART_ITConfig(USART6, USART_IT_TC, ENABLE);
+
+    // UART6_RX_ENABLE;
+}
+
+/****************************************************
+ *  函 数 名:Uart6_Send_Data
+ *  函数功能:串口3发送数据
+ *  入口参数:buf 待发送数据  len 数据长度
+ *  说    明:
+ *****************************************************/
+void Uart6_Send_Data(const uint8_t *buf, uint16_t len)
+{
+    uint8_t i;
+
+    // UART6_TX_ENABLE;          // 485 发送使能
+    for (i = 0; i < len; i++) //循环发送数据
+    {
+        while (USART_GetFlagStatus(USART6, USART_FLAG_TC) == RESET)
+            ;
+        USART_SendData(USART6, buf[i]);
+    }
+    while (USART_GetFlagStatus(USART6, USART_FLAG_TC) == RESET)
+        ;
+
+    // UART6_RX_ENABLE; // 接收使能
+}
+
+void uart6_dma_send(const uint8_t *buf, uint16_t len)
+{
+    if (DMA_GetFlagStatus(UART6_DMA_TXCH, DMA_FLAG_TCIF3) != RESET)
+    {
+        DMA_ClearFlag(UART6_DMA_TXCH, DMA_FLAG_TCIF3);
+    }
+
+    // UART6_TX_ENABLE; // 485 发送使能
+    if (len > UART6_TX_LEN)
+    {
+        len = UART6_TX_LEN;
+    }
+    memcpy(uart6_tx_buf, buf, len);
+    USART_DMACmd(USART6, USART_DMAReq_Tx, len);
+    Uart6DMA_Enable(UART6_DMA_TXCH, len);
+}
+
+void USART6_IRQHandler(void)
+{
+    static uint8_t   u3_index = 0;
+    volatile uint8_t clear    = 0;
+    uint8_t          rec_cnt  = 0;
+    if (RESET != USART_GetITStatus(USART6, USART_IT_IDLE))
+    {
+        clear = USART6->SR;
+        clear = USART6->DR; // 先读SR, 再读DR, 就是为了消除IDLE中断
+        DMA_Cmd(UART6_DMA_RXCH, DISABLE);
+        DMA_ClearFlag(UART6_DMA_RXCH, DMA_FLAG_TCIF1);
+        rec_cnt = UART6_REC_LEN - DMA_GetCurrDataCounter(UART6_DMA_RXCH);
+
+        memcpy(Uart6FrameStruct[u3_index].buf, uart6_rx_buf, rec_cnt);
+        Uart6FrameStruct[u3_index].len = rec_cnt;
+
+        OSMboxPost(uart6_mbox, &Uart6FrameStruct[u3_index]);
+        if (u3_index < UART6_MAX_MSG_NUM - 1)
+        {
+            u3_index++;
+        }
+        else
+        {
+            u3_index = 0;
+        }
+        DMA_SetCurrDataCounter(UART6_DMA_RXCH, UART6_REC_LEN);
+        DMA_Cmd(UART6_DMA_RXCH, ENABLE);
+    }
+    else if (RESET != USART_GetITStatus(USART6, USART_IT_TC))
+    {
+        USART_ClearITPendingBit(USART6, USART_IT_TC);
+        // UART6_RX_ENABLE;
+        Uart6DMA_Enable(UART6_DMA_RXCH, UART6_REC_LEN); // DMA接收使能
+        DMA_Cmd(UART6_DMA_TXCH, DISABLE);               // 关闭发送DMA
+        DMA_SetCurrDataCounter(UART6_DMA_TXCH, 0);      // 清除发送数据长度
+    }
+}

+ 20 - 0
User/Bsp/uart/uart6.h

@@ -0,0 +1,20 @@
+#ifndef __UART6_H
+#define __UART6_H
+
+#include "interface.h"
+#include "queue.h"
+#include "stm32f4xx_usart.h"
+#include "string.h"
+
+#define UART6_REC_LEN     128
+#define UART6_TX_LEN      128
+#define UART6_MAX_MSG_NUM 10
+
+#define UART6_DMA      DMA_Channel_5
+#define UART6_DMA_RXCH DMA1_Stream1
+#define UART6_DMA_TXCH DMA1_Stream3
+
+void uart6_init(void);
+void uart6_dma_send(const uint8_t *buf, uint16_t len);
+
+#endif // __UART3_H

+ 178 - 0
User/app/esp8266/esp8266.c

@@ -0,0 +1,178 @@
+// #include "esp8266.h"
+// ///////////////////////////////////////////////////////////////////////////////////////////////////////////
+// //用户配置区
+
+// //连接端口号:8086,可自行修改为其他端口.
+// const uint8_t *portnum = "8086";
+
+// // WIFI STA模式,设置要去连接的路由器无线参数,请根据你自己的路由器设置,自行修改.
+// const uint8_t *wifista_ssid       = "FCC";         //路由器SSID号
+// const uint8_t *wifista_encryption = "wpawpa2_aes"; // wpa/wpa2 aes加密方式
+// const uint8_t *wifista_password   = "347470868";   //连接密码
+
+// // WIFI AP模式,模块对外的无线参数,可自行修改.
+// const uint8_t *wifiap_ssid       = "ATK-ESP8266"; //对外SSID号
+// const uint8_t *wifiap_encryption = "wpawpa2_aes"; // wpa/wpa2 aes加密方式
+// const uint8_t *wifiap_password   = "12345678";    //连接密码
+
+// ///////////////////////////////////////////////////////////////////////////////////////////////////////////
+// // 4个网络模式
+// const uint8_t *ATK_ESP8266_CWMODE_TBL[3] = {"STA模式 ", "AP模式 ", "AP&STA模式 "}; // ATK-ESP8266,3种网络模式,默认为路由器(ROUTER)模式
+// // 4种工作模式
+// const uint8_t *ATK_ESP8266_WORKMODE_TBL[3] = {"TCP服务器", "TCP客户端", " UDP 模式"}; // ATK-ESP8266,4种工作模式
+// // 5种加密方式
+// const uint8_t *ATK_ESP8266_ECN_TBL[5] = {"OPEN", "WEP", "WPA_PSK", "WPA2_PSK", "WPA_WAP2_PSK"};
+// ///////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+// //向ATK-ESP8266发送命令
+// // cmd:发送的命令字符串
+// // ack:期待的应答结果,如果为空,则表示不需要等待应答
+// // waittime:等待时间(单位:10ms)
+// //返回值:0,发送成功(得到了期待的应答结果)
+// //       1,发送失败
+// uint8_t atk_8266_send_cmd(uint8_t *cmd, uint8_t *ack, uint16_t waittime)
+// {
+//     uint8_t            res = 0;
+//     INT8U              err = 0;
+//     UartFrame_TypeDef *msg;
+//     uart6_dma_send(cmd, sizeof(cmd));
+
+//     if (ack && waittime) //需要等待应答
+//     {
+//         while (--waittime) //等待倒计时
+//         {
+//             ms_delay(10);
+//             // iwdg_feed(UART6_DOG);
+//             msg = (UartFrame_TypeDef *)OSMboxPend(uart6_mbox, 50, &err);
+//             if ((err == OS_ERR_NONE) && (msg->len >= 2)) //接收到期待的应答结果
+//             {
+//                 printf(msg);
+//                 // if (msg->buf)
+//                 //     if (atk_8266_check_cmd(ack))
+//                 //     {
+//                 //         printf("ack:%s\r\n", (uint8_t *)ack);
+//                 //         break; //得到有效数据
+//                 //     }
+//             }
+//         }
+//         if (waittime == 0)
+//             res = 1;
+//     }
+//     return res;
+// }
+// //向ATK-ESP8266发送指定数据
+// // data:发送的数据(不需要添加回车了)
+// // ack:期待的应答结果,如果为空,则表示不需要等待应答
+// // waittime:等待时间(单位:10ms)
+// //返回值:0,发送成功(得到了期待的应答结果)luojian
+// uint8_t atk_8266_send_data(uint8_t *data, uint8_t *ack, uint16_t waittime)
+// {
+//     uint8_t            res = 0;
+//     INT8U              err = 0;
+//     UartFrame_TypeDef *msg;
+//     uart6_dma_send(data, sizeof(data)); //发送命令
+//     if (ack && waittime)                //需要等待应答
+//     {
+//         while (--waittime) //等待倒计时
+//         {
+//             ms_delay(10);
+//             msg = (UartFrame_TypeDef *)OSMboxPend(uart6_mbox, 50, &err);
+//             if ((err == OS_ERR_NONE) && (msg->len >= 2)) //接收到期待的应答结果
+//             {
+//                 printf(msg);
+//                 // if (atk_8266_check_cmd(ack))
+//                 // {
+//                 //     printf("ack:%s\r\n", (uint8_t *)ack);
+//                 //     break; //得到有效数据
+//                 // }
+//             }
+//         }
+//         if (waittime == 0)
+//             res = 1;
+//     }
+//     return res;
+// }
+// // ATK-ESP8266退出透传模式
+// //返回值:0,退出成功;
+// //        1,退出失败
+// uint8_t atk_8266_quit_trans(void)
+// {
+//     while ((USART6->SR & 0X40) == 0)
+//         ; //等待发送空
+//     USART6->DR = '+';
+//     ms_delay(15); //大于串口组帧时间(10ms)
+//     while ((USART6->SR & 0X40) == 0)
+//         ; //等待发送空
+//     USART6->DR = '+';
+//     ms_delay(15); //大于串口组帧时间(10ms)
+//     while ((USART6->SR & 0X40) == 0)
+//         ; //等待发送空
+//     USART6->DR = '+';
+//     ms_delay(500);                            //等待500ms
+//     return atk_8266_send_cmd("AT", "OK", 20); //退出透传判断.
+// }
+// //获取ATK-ESP8266模块的AP+STA连接状态
+// //返回值:0,未连接;1,连接成功
+// uint8_t atk_8266_apsta_check(void)
+// {
+//     if (atk_8266_quit_trans())
+//         return 0;                               //退出透传
+//     atk_8266_send_cmd("AT+CIPSTATUS", ":", 50); //发送AT+CIPSTATUS指令,查询连接状态
+//     if (atk_8266_check_cmd("+CIPSTATUS:0") &&
+//         atk_8266_check_cmd("+CIPSTATUS:1") &&
+//         atk_8266_check_cmd("+CIPSTATUS:2") &&
+//         atk_8266_check_cmd("+CIPSTATUS:4"))
+//         return 0;
+//     else
+//         return 1;
+// }
+// // //获取ATK-ESP8266模块的连接状态
+// // //返回值:0,未连接;1,连接成功.
+// // uint8_t atk_8266_consta_check(void)
+// // {
+// //     uint8_t *p;
+// //     uint8_t  res;
+// //     if (atk_8266_quit_trans())
+// //         return 0;                               //退出透传
+// //     atk_8266_send_cmd("AT+CIPSTATUS", ":", 50); //发送AT+CIPSTATUS指令,查询连接状态
+// //     p   = atk_8266_check_cmd("+CIPSTATUS:");
+// //     res = *p; //得到连接状态
+// //     return res;
+// // }
+
+// //获取Client ip地址
+// // ipbuf:ip地址输出缓存区
+// void atk_8266_get_wanip(uint8_t *ipbuf)
+// {
+//     uint8_t *p, *p1;
+//     if (atk_8266_send_cmd("AT+CIFSR", "OK", 50)) //获取WAN IP地址失败
+//     {
+//         ipbuf[0] = 0;
+//         return;
+//     }
+//     p   = atk_8266_check_cmd("\"");
+//     p1  = (uint8_t *)strstr((const char *)(p + 1), "\"");
+//     *p1 = 0;
+//     sprintf((char *)ipbuf, "%s", p + 1);
+// }
+
+// // ATK-ESP8266模块测试主函数
+// void esp_8266_task(void)
+// {
+//     //	uint16_t rlen=0;
+//     uint8_t key;
+//     uint8_t timex;
+
+//     while (atk_8266_send_cmd("AT", "OK", 20)) //检查WIFI模块是否在线
+//     {
+//         atk_8266_quit_trans();                        //退出透传
+//         atk_8266_send_cmd("AT+CIPMODE=0", "OK", 200); //关闭透传模式
+//     }
+//     while (atk_8266_send_cmd("ATE0", "OK", 20))
+//         ; //关闭回显
+//     while (1)
+//     {
+//         ms_delay(10);
+//         // atk_8266_at_response(1); //检查ATK-ESP8266模块发送过来的数据,及时上传给电脑
+//     }
+// }

+ 28 - 0
User/app/esp8266/esp8266.h

@@ -0,0 +1,28 @@
+// #ifndef __ESP8266_H__
+// #define __ESP8266_H__
+
+// #include "dwt.h"
+// #include "fly_param.h"
+// #include "interface.h"
+// #include "string.h"
+// #include "uart6.h"
+
+// void esp_8266_init(void);
+
+// extern const uint8_t *portnum;
+
+// extern const uint8_t *wifista_ssid;
+// extern const uint8_t *wifista_encryption;
+// extern const uint8_t *wifista_password;
+
+// extern const uint8_t *wifiap_ssid;
+// extern const uint8_t *wifiap_encryption;
+// extern const uint8_t *wifiap_password;
+
+// extern uint8_t       *kbd_fn_tbl[2];
+// extern const uint8_t *ATK_ESP8266_CWMODE_TBL[3];
+// extern const uint8_t *ATK_ESP8266_WORKMODE_TBL[3];
+// extern const uint8_t *ATK_ESP8266_ECN_TBL[5];
+
+// void esp_8266_task(void);
+// #endif

+ 3 - 2
User/app/fly_param.c

@@ -10,13 +10,14 @@ OS_EVENT *can2_sem     = NULL;
 OS_EVENT *net_sem      = NULL;
 OS_EVENT *uart1_mbox   = NULL;
 OS_EVENT *uart3_mbox   = NULL;
+OS_EVENT *uart6_mbox   = NULL;
 
 SqQueue CanQueueCan1;
 SqQueue CanQueueCan2;
 
 CPU_STK uart1_task_stk[UART1_TASK_STK_SIZE];
 CPU_STK uart3_task_stk[UART3_TASK_STK_SIZE];
+CPU_STK uart6_task_stk[UART6_TASK_STK_SIZE];
 CPU_STK init_task_stk[INIT_STK_SIZE];
 CPU_STK net_task_stk[NET_TASK_STK_SIZE];
-CPU_STK misc_task_stk[MISC_TASK_STK_SIZE];
-CPU_STK LED_TASK_STK[LED0_STK_SIZE];
+CPU_STK misc_task_stk[MISC_TASK_STK_SIZE];

+ 4 - 3
User/app/fly_param.h

@@ -9,9 +9,9 @@
 
 #define CAN_MSG_LEN 8
 
-#define LED_RX_PRIO     7
 #define UART1_TASK_PRIO 23
 #define UART3_TASK_PRIO 24
+#define UART6_TASK_PRIO 24
 #define MISC_PRIO       28
 #define NET_PRIO        30
 #define INIT_TASK_PRIO  35
@@ -19,16 +19,17 @@
 #define INIT_STK_SIZE       128
 #define MISC_TASK_STK_SIZE  512
 #define NET_TASK_STK_SIZE   512
-#define LED0_STK_SIZE       512
 #define LED1_STK_SIZE       512
 #define UART1_TASK_STK_SIZE 256
 #define UART3_TASK_STK_SIZE 256
+#define UART6_TASK_STK_SIZE 256
 
 extern OS_EVENT *net_mutex;
 extern OS_EVENT *can1_sem;
 extern OS_EVENT *can2_sem;
 extern OS_EVENT *uart1_mbox;
 extern OS_EVENT *uart3_mbox;
+extern OS_EVENT *uart6_mbox;
 
 extern SqQueue CanQueueCan1;
 extern SqQueue CanQueueCan2;
@@ -36,8 +37,8 @@ extern SqQueue CanQueueCan2;
 extern CPU_STK init_task_stk[INIT_STK_SIZE];
 extern CPU_STK uart1_task_stk[UART1_TASK_STK_SIZE];
 extern CPU_STK uart3_task_stk[UART3_TASK_STK_SIZE];
+extern CPU_STK uart6_task_stk[UART6_TASK_STK_SIZE];
 extern CPU_STK misc_task_stk[MISC_TASK_STK_SIZE];
 extern CPU_STK net_task_stk[NET_TASK_STK_SIZE];
-extern CPU_STK LED_TASK_STK[LED0_STK_SIZE];
 
 #endif

+ 21 - 80
User/Bsp/key/key.c → User/app/key/key.c

@@ -44,64 +44,7 @@ static void bsp_DetectKey(uint8_t i);
 *	返 回 值: 返回值1 表示按下,0表示未按下
 *********************************************************************************************************
 */
-#ifdef STM32_X3 /* 安富莱 STM32-X3 开发板 */
-static uint8_t IsKeyDown1(void)
-{
-    if ((GPIO_PORT_K1->IDR & GPIO_PIN_K1) == 0)
-        return 1;
-    else
-        return 0;
-}
-static uint8_t IsKeyDown2(void)
-{
-    if ((GPIO_PORT_K2->IDR & GPIO_PIN_K2) == 0)
-        return 1;
-    else
-        return 0;
-}
-static uint8_t IsKeyDown3(void)
-{
-    if ((GPIO_PORT_K3->IDR & GPIO_PIN_K3) == 0)
-        return 1;
-    else
-        return 0;
-}
-static uint8_t IsKeyDown4(void)
-{
-    if ((GPIO_PORT_K4->IDR & GPIO_PIN_K4) == 0)
-        return 1;
-    else
-        return 0;
-}
-static uint8_t IsKeyDown5(void)
-{
-    if ((GPIO_PORT_K5->IDR & GPIO_PIN_K5) == 0)
-        return 1;
-    else
-        return 0;
-}
-static uint8_t IsKeyDown6(void)
-{
-    if ((GPIO_PORT_K6->IDR & GPIO_PIN_K6) == 0)
-        return 1;
-    else
-        return 0;
-}
-static uint8_t IsKeyDown7(void)
-{
-    if ((GPIO_PORT_K7->IDR & GPIO_PIN_K7) == 0)
-        return 1;
-    else
-        return 0;
-}
-static uint8_t IsKeyDown8(void)
-{
-    if ((GPIO_PORT_K8->IDR & GPIO_PIN_K8) == 0)
-        return 1;
-    else
-        return 0;
-}
-#else /* 安富莱 STM32-V5 开发板 */
+
 static uint8_t IsKeyDown1(void)
 {
     if ((GPIO_PORT_K1->IDR & GPIO_PIN_K1) == 0)
@@ -158,7 +101,7 @@ static uint8_t IsKeyDown8(void)
     else
         return 0;
 }
-#endif
+
 static uint8_t IsKeyDown9(void)
 {
     if (IsKeyDown1() && IsKeyDown2())
@@ -176,13 +119,13 @@ static uint8_t IsKeyDown10(void)
 
 /*
 *********************************************************************************************************
-*	函 数 名: bsp_PutKey
+*	函 数 名: key_put
 *	功能说明: 将1个键值压入按键FIFO缓冲区。可用于模拟一个按键。
 *	形    参:  _KeyCode : 按键代码
 *	返 回 值: 无
 *********************************************************************************************************
 */
-void bsp_PutKey(uint8_t _KeyCode)
+void key_put(uint8_t _KeyCode)
 {
     s_tKey.Buf[s_tKey.Write] = _KeyCode;
 
@@ -194,13 +137,13 @@ void bsp_PutKey(uint8_t _KeyCode)
 
 /*
 *********************************************************************************************************
-*	函 数 名: bsp_GetKey
+*	函 数 名: key_get
 *	功能说明: 从按键FIFO缓冲区读取一个键值。
 *	形    参:  无
 *	返 回 值: 按键代码
 *********************************************************************************************************
 */
-uint8_t bsp_GetKey(void)
+uint8_t key_get(void)
 {
     uint8_t ret;
 
@@ -222,13 +165,13 @@ uint8_t bsp_GetKey(void)
 
 /*
 *********************************************************************************************************
-*	函 数 名: bsp_GetKey2
+*	函 数 名: key_get_fifo
 *	功能说明: 从按键FIFO缓冲区读取一个键值。独立的读指针。
 *	形    参:  无
 *	返 回 值: 按键代码
 *********************************************************************************************************
 */
-uint8_t bsp_GetKey2(void)
+uint8_t key_get_fifo(void)
 {
     uint8_t ret;
 
@@ -250,20 +193,20 @@ uint8_t bsp_GetKey2(void)
 
 /*
 *********************************************************************************************************
-*	函 数 名: bsp_GetKeyState
+*	函 数 名: key_state
 *	功能说明: 读取按键的状态
 *	形    参:  _ucKeyID : 按键ID,从0开始
 *	返 回 值: 1 表示按下, 0 表示未按下
 *********************************************************************************************************
 */
-uint8_t bsp_GetKeyState(KEY_ID_E _ucKeyID)
+uint8_t key_state(KEY_ID_E _ucKeyID)
 {
     return s_tBtn[_ucKeyID].State;
 }
 
 /*
 *********************************************************************************************************
-*	函 数 名: bsp_SetKeyParam
+*	函 数 名: key_state
 *	功能说明: 设置按键参数
 *	形    参:_ucKeyID : 按键ID,从0开始
 *			_LongTime : 长按事件时间
@@ -271,7 +214,7 @@ uint8_t bsp_GetKeyState(KEY_ID_E _ucKeyID)
 *	返 回 值: 无
 *********************************************************************************************************
 */
-void bsp_SetKeyParam(uint8_t _ucKeyID, uint16_t _LongTime, uint8_t _RepeatSpeed)
+void key_set(uint8_t _ucKeyID, uint16_t _LongTime, uint8_t _RepeatSpeed)
 {
     s_tBtn[_ucKeyID].LongTime    = _LongTime;    /* 长按时间 0 表示不检测长按键事件 */
     s_tBtn[_ucKeyID].RepeatSpeed = _RepeatSpeed; /* 按键连发的速度,0表示不支持连发 */
@@ -280,13 +223,13 @@ void bsp_SetKeyParam(uint8_t _ucKeyID, uint16_t _LongTime, uint8_t _RepeatSpeed)
 
 /*
 *********************************************************************************************************
-*	函 数 名: bsp_ClearKey
+*	函 数 名: key_clear_fifo
 *	功能说明: 清空按键FIFO缓冲区
 *	形    参:无
 *	返 回 值: 按键代码
 *********************************************************************************************************
 */
-void bsp_ClearKey(void)
+void key_clear_fifo(void)
 {
     s_tKey.Read = s_tKey.Write;
 }
@@ -388,7 +331,7 @@ static void bsp_DetectKey(uint8_t i)
                 pBtn->State = 1;
 
                 /* 发送按钮按下的消息 */
-                bsp_PutKey((uint8_t)(3 * i + 1));
+                key_put((uint8_t)(3 * i + 1));
             }
 
             if (pBtn->LongTime > 0)
@@ -399,7 +342,7 @@ static void bsp_DetectKey(uint8_t i)
                     if (++pBtn->LongCount == pBtn->LongTime)
                     {
                         /* 键值放入按键FIFO */
-                        bsp_PutKey((uint8_t)(3 * i + 3));
+                        key_put((uint8_t)(3 * i + 3));
                     }
                 }
                 else
@@ -410,7 +353,7 @@ static void bsp_DetectKey(uint8_t i)
                         {
                             pBtn->RepeatCount = 0;
                             /* 常按键后,每隔10ms发送1个按键 */
-                            bsp_PutKey((uint8_t)(3 * i + 1));
+                            key_put((uint8_t)(3 * i + 1));
                         }
                     }
                 }
@@ -434,7 +377,7 @@ static void bsp_DetectKey(uint8_t i)
                 pBtn->State = 0;
 
                 /* 发送按钮弹起的消息 */
-                bsp_PutKey((uint8_t)(3 * i + 2));
+                key_put((uint8_t)(3 * i + 2));
             }
         }
 
@@ -445,13 +388,13 @@ static void bsp_DetectKey(uint8_t i)
 
 /*
 *********************************************************************************************************
-*	函 数 名: bsp_KeyScan
+*	函 数 名: key_scan
 *	功能说明: 扫描所有按键。非阻塞,被systick中断周期性的调用
 *	形    参:  无
 *	返 回 值: 无
 *********************************************************************************************************
 */
-void bsp_KeyScan(void)
+void key_scan(void)
 {
     uint8_t i;
 
@@ -459,6 +402,4 @@ void bsp_KeyScan(void)
     {
         bsp_DetectKey(i);
     }
-}
-
-/***************************** 安富莱电子 www.armfly.com (END OF FILE) *********************************/
+}

+ 8 - 8
User/Bsp/key/key.h → User/app/key/key.h

@@ -148,14 +148,14 @@ typedef struct
 
 /* 供外部调用的函数声明 */
 void    key_init(void);
-void    bsp_KeyScan(void);
-void    bsp_PutKey(uint8_t _KeyCode);
-uint8_t bsp_GetKey(void);
-uint8_t bsp_GetKey2(void);
-uint8_t bsp_GetKeyState(KEY_ID_E _ucKeyID);
-void    bsp_SetKeyParam(uint8_t _ucKeyID, uint16_t _LongTime, uint8_t _RepeatSpeed);
-void    bsp_ClearKey(void);
-
+void    key_scan(void);
+void    key_put(uint8_t _KeyCode);
+uint8_t key_get(void);
+uint8_t key_get_fifo(void);
+uint8_t key_state(KEY_ID_E _ucKeyID);
+void    key_set(uint8_t _ucKeyID, uint16_t _LongTime, uint8_t _RepeatSpeed);
+void    key_clear_fifo(void);
+void    key_task(void);
 #endif
 
 /***************************** 安富莱电子 www.armfly.com (END OF FILE) *********************************/

+ 0 - 21
User/app/led/led.c

@@ -1,21 +0,0 @@
-#include "led.h"
-#include "interface.h"
-
-void led_task(void)
-{
-    while (1)
-    {
-        LED0_RUN_ON;
-        OSTimeDly(100);
-        LED1_RUN_ON;
-        OSTimeDly(100);
-        LED0_RUN_OFF;
-        OSTimeDly(100);
-        LED1_RUN_OFF;
-        OSTimeDly(100);
-        // LED2_RUN_ON;
-        // OSTimeDly(100);
-        // LED2_RUN_OFF;
-        // OSTimeDly(100);
-    }
-}

+ 0 - 8
User/app/led/led.h

@@ -1,8 +0,0 @@
-#ifndef __LED_H
-#define __LED_H
-
-#include "includes.h"
-
-void led_task(void);
-
-#endif

+ 99 - 0
User/conf/fatfs/bsp_fatfs.c

@@ -0,0 +1,99 @@
+#include "bsp_fatfs.h"
+#include "ext_sram.h"
+#include "interface.h"
+#include "stm32f4xx.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+FATFS FatFs[_VOLUMES]; /* FatFs文件系统对象  4144字节*/
+FIL   file_object;     /* 文件对象           4132字节*/
+
+/****************************************************
+ *  函 数 名:get_fattime
+ *  函数功能:文件系统获取时间
+ *  入口参数:
+ *  返 回 值:时间
+ *****************************************************/
+DWORD get_fattime(void)
+{
+    RTC_TimeTypeDef RTC_TimeStruct;
+    RTC_DateTypeDef RTC_DateStruct;
+
+    RTC_GetTime(RTC_Format_BIN, &RTC_TimeStruct);
+    RTC_GetDate(RTC_Format_BIN, &RTC_DateStruct);
+    /* 返回当前时间戳 */
+    return ((DWORD)(2000 + RTC_DateStruct.RTC_Year - 1980) << 25) /* Year 2019 */
+           | ((DWORD)RTC_DateStruct.RTC_Month << 21)              /* Month */
+           | ((DWORD)RTC_DateStruct.RTC_Date << 16)               /* Mday */
+           | ((DWORD)RTC_TimeStruct.RTC_Hours << 11)              /* Hour */
+           | ((DWORD)RTC_TimeStruct.RTC_Minutes << 5)             /* Min */
+           | ((DWORD)RTC_TimeStruct.RTC_Seconds >> 1);            /* Sec */
+}
+
+/****************************************************
+ *  函 数 名:FileSystem_Init
+ *  函数功能:文件系统初始化
+ *  入口参数:
+ *  说    明:无
+ *****************************************************/
+void fatfs_init(void)
+{
+    FRESULT res;                       /* 文件操作结果 */
+    res = f_mount(&FatFs[0], "0:", 1); // 文件系统挂载时会对TF初始化
+    /* 如果没有文件系统就格式化创建创建文件系统 */
+    if (res == FR_NO_FILESYSTEM) // TF 卡没有文件系统
+    {
+        res = f_mkfs("0:", 0, 0); // 格式化
+        if (res == FR_OK)
+        {
+            printf("TF format ok\r\n");
+            res = f_mount(NULL, "0:", 1);      /* 格式化后,先取消挂载 */
+            res = f_mount(&FatFs[0], "0:", 1); /* 重新挂载	*/
+        }
+        else
+        {
+            LED1_RUN_OFF;
+            printf("TF format fail\r\n");
+        }
+    }
+    else if (res != FR_OK)
+    {
+        LED1_RUN_OFF;
+        printf("!!!TF mount file system fail (%d)\r\n", res);
+    }
+    else
+    {
+        LED1_RUN_ON;
+        printf("File system mount ok !\r\n");
+    }
+
+    res = f_mount(&FatFs[1], "1:", 1); // 文件系统挂载时会对NAND初始化
+    /* 挂载文件系统 */
+    // if (res == FR_NO_FILESYSTEM) // NAND 没有文件系统
+    // {
+    //     res = f_mkfs("1:", 0, 0); // 格式化
+    //     if (res == FR_OK)
+    //     {
+    //         printf("TF format ok\r\n");
+    //         res = f_mount(NULL, "1:", 1); /* 格式化后,先取消挂载 */
+    //         res = f_mount(&fs, "1:", 1);  /* 重新挂载	*/
+    //     }
+    //     else
+    //     {
+    //         // LED_FAULT_ON;
+    //         printf("TF format fail\r\n");
+    //     }
+    // }
+    // else
+    if (res != FR_OK)
+    {
+        LED0_RUN_OFF;
+        printf("!!!Nand flash mount file system fail (%d)\r\n", res);
+    }
+    else
+    {
+        LED0_RUN_ON;
+        printf("Nand flash file system mount ok !\r\n");
+    }
+}

+ 9 - 0
User/conf/fatfs/bsp_fatfs.h

@@ -0,0 +1,9 @@
+#ifndef _FATFS_CONF_H
+#define _FATFS_CONF_H
+
+#include "ff.h"
+#include "stm32f4xx.h"
+
+void fatfs_init(void); // 文件系统初始化
+
+#endif

+ 338 - 0
User/conf/fatfs/diskio.c

@@ -0,0 +1,338 @@
+/*-----------------------------------------------------------------------*/
+/* 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 "atadrive.h" /* Example: ATA drive control */
+#include "nand_flash.h"
+#include "sdcard.h" /* Example: MMC/SDC contorl */
+// #include "usbdisk.h"  /* Example: USB drive control */
+
+/* Definitions of physical drive number for each drive */
+/* 为每个设备定义一个物理编号 */
+#define ATA 0 // SD卡
+#define MMC 1 // 预留外部SPI Flash使用
+// #define ATA       0 /* Example: Map ATA drive to drive number 0 */
+// #define MMC       1 /* Example: Map MMC/SD card to drive number 1 */
+#define USB 2 /* Example: Map USB drive to drive number 2 */
+
+#define SD_BLOCKSIZE 512
+
+extern SD_CardInfo SDCardInfo;
+
+/*-----------------------------------------------------------------------*/
+/* 获取设备状态                                                          */
+/*-----------------------------------------------------------------------*/
+DSTATUS disk_status(
+    BYTE pdrv /* Physical drive nmuber to identify the drive */
+)
+{
+    DSTATUS stat = STA_NOINIT;
+    int     result;
+
+    switch (pdrv)
+    {
+    case ATA:
+        stat = 0;
+
+        return stat;
+
+    case MMC:
+        stat = 0;
+        return stat;
+
+    case USB:
+        // result = USB_disk_status();
+
+        // translate the reslut code here
+
+        return stat;
+    }
+    return STA_NOINIT;
+}
+
+/*-----------------------------------------------------------------------*/
+/* Inidialize a Drive                                                    */
+/*-----------------------------------------------------------------------*/
+
+DSTATUS disk_initialize(
+    BYTE pdrv /* Physical drive nmuber to identify the drive */
+)
+{
+    DSTATUS  stat = STA_NOINIT;
+    SD_Error result;
+
+    switch (pdrv)
+    {
+    case ATA:
+        if (SD_Init() == SD_OK)
+        {
+            stat = RES_OK;
+        }
+        else
+        {
+            stat = STA_NODISK;
+        }
+
+        return stat;
+
+    case MMC:
+        if (nand_flash_init() == NAND_OK)
+        {
+            stat = RES_OK;
+        }
+        else
+        {
+            /* 如果初始化失败,请执行低级格式化 */
+            printf("NAND_Init() Error!  \r\n");
+            stat = RES_ERROR;
+        }
+        return stat;
+
+    case USB:
+        // result = USB_disk_initialize();
+
+        // translate the reslut code here
+
+        return stat;
+    }
+    return STA_NOINIT;
+}
+
+/*-----------------------------------------------------------------------*/
+/* 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;
+    DRESULT status = RES_PARERR;
+    int     result;
+
+    switch (pdrv)
+    {
+    case ATA:
+    {
+        SD_Error Status = SD_OK;
+
+        if (count == 1)
+        {
+            Status = SD_ReadBlock(buff, sector << 9, SD_BLOCKSIZE);
+        }
+        else
+        {
+            Status = SD_ReadMultiBlocks(buff, sector << 9, SD_BLOCKSIZE, count);
+        }
+        if (Status != SD_OK)
+        {
+            res = RES_ERROR;
+            break;
+        }
+
+#ifdef SD_DMA_MODE
+        /* SDIO工作在DMA模式,需要检查操作DMA传输是否完成 */
+        Status = SD_WaitReadOperation();
+        if (Status != SD_OK)
+        {
+            res = RES_ERROR;
+            break;
+        }
+
+        while (SD_GetStatus() != SD_TRANSFER_OK)
+            ;
+#endif
+
+        res = RES_OK;
+        break;
+    }
+
+    case MMC:
+        if (NAND_OK == NAND_ReadMultiSectors(buff, sector, 512, count))
+        {
+            res = RES_OK;
+        }
+        else
+        {
+            printf("NAND_ReadMultiSectors() Error! sector = %d, count = %d \r\n", sector, count);
+            res = RES_ERROR;
+        }
+        return res;
+
+    case USB:
+        // translate the arguments here
+
+        // result = USB_disk_read(buff, sector, count);
+
+        // translate the reslut code here
+
+        return res;
+    }
+
+    return RES_PARERR;
+}
+
+/*-----------------------------------------------------------------------*/
+/* 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;
+    int      result;
+    SD_Error SD_state = SD_OK;
+
+    switch (pdrv)
+    {
+    case ATA: /* SD CARD */
+    {
+        SD_Error Status = SD_OK;
+
+        if (count == 1)
+        {
+            Status = SD_WriteBlock((uint8_t *)buff, sector << 9, SD_BLOCKSIZE);
+
+            if (Status != SD_OK)
+            {
+                res = RES_ERROR;
+                break;
+            }
+
+#ifdef SD_DMA_MODE
+            /* SDIO工作在DMA模式,需要检查操作DMA传输是否完成 */
+            Status = SD_WaitReadOperation();
+            if (Status != SD_OK)
+            {
+                res = RES_ERROR;
+                break;
+            }
+            while (SD_GetStatus() != SD_TRANSFER_OK)
+                ;
+#endif
+            res = RES_OK;
+        }
+        else
+        {
+            /* 此处存在疑问: 扇区个数如果写 count ,将导致最后1个block无法写入 */
+            // Status = SD_WriteMultiBlocks((uint8_t *)buff, sector << 9 ,SECTOR_SIZE, count);
+            Status = SD_WriteMultiBlocks((uint8_t *)buff, sector << 9, SD_BLOCKSIZE, count + 1);
+
+            if (Status != SD_OK)
+            {
+                res = RES_ERROR;
+                break;
+            }
+
+#ifdef SD_DMA_MODE
+            /* SDIO工作在DMA模式,需要检查操作DMA传输是否完成 */
+            Status = SD_WaitReadOperation();
+            if (Status != SD_OK)
+            {
+                res = RES_ERROR;
+                break;
+            }
+            while (SD_GetStatus() != SD_TRANSFER_OK)
+                ;
+#endif
+            res = RES_OK;
+        }
+        break;
+    }
+
+    case MMC:
+        if (NAND_OK == NAND_WriteMultiSectors((uint8_t *)buff, sector, 512, count))
+        {
+            res = RES_OK;
+        }
+        else
+        {
+            printf("NAND_ReadMultiSectors() Error! sector = %d, count = %d \r\n", sector, count);
+            res = RES_ERROR;
+        }
+        return res;
+
+    case USB:
+        // translate the arguments here
+
+        // result = USB_disk_write(buff, sector, count);
+
+        // translate the reslut code here
+
+        return res;
+    }
+
+    return RES_PARERR;
+}
+#endif
+
+/*-----------------------------------------------------------------------*/
+/* 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;
+    int     result;
+
+    switch (pdrv)
+    {
+    case ATA: /* SD CARD */
+        /* SD卡磁盘容量: SDCardInfo.CardCapacity */
+        res = RES_OK;
+        return res;
+
+    case MMC:
+        res = RES_OK;
+        return res;
+
+    case USB:
+
+        // Process of the command the USB drive
+
+        return res;
+    }
+
+    return RES_PARERR;
+}
+
+/*
+*********************************************************************************************************
+*	函 数 名: get_fattime
+*	功能说明: 获得系统时间,用于改写文件的创建和修改时间。
+*	形    参:无
+*	返 回 值: 无
+*********************************************************************************************************
+*/
+// weak DWORD get_fattime(void)
+// {
+//     /* 如果有全局时钟,可按下面的格式进行时钟转换. 这个例子是2013-01-01 00:00:00 */
+
+//     return ((DWORD)(2013 - 1980) << 25) /* Year = 2013 */
+//            | ((DWORD)1 << 21)           /* Month = 1 */
+//            | ((DWORD)1 << 16)           /* Day_m = 1*/
+//            | ((DWORD)0 << 11)           /* Hour = 0 */
+//            | ((DWORD)0 << 5)            /* Min = 0 */
+//            | ((DWORD)0 >> 1);           /* Sec = 0 */
+// }
+
+#endif

+ 33 - 56
MiddleWare/FATFS/ffconf.h → User/conf/fatfs/ffconf.h

@@ -2,28 +2,26 @@
 /  FatFs - FAT file system module configuration file  R0.10c (C)ChaN, 2014
 /---------------------------------------------------------------------------*/
 
-#define _FFCONF 80376	/* Revision ID */
+#define _FFCONF 80376 /* Revision ID */
 
 /*---------------------------------------------------------------------------/
 / Functions and Buffer Configurations
 /---------------------------------------------------------------------------*/
 
-#define	_FS_TINY		0
+#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_READONLY	0
+#define _FS_READONLY 0
 /* This option switches read-only configuration. (0:Read/Write or 1:Read-only)
 /  Read-only configuration removes basic 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
+#define _FS_MINIMIZE 0
 /* This option defines minimization level to remove some API functions.
 /
 /   0: All basic functions are enabled.
@@ -32,8 +30,7 @@
 /   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	0
+#define _USE_STRFUNC 1
 /* This option switches string functions, f_gets(), f_putc(), f_puts() and
 /  f_printf().
 /
@@ -41,31 +38,26 @@
 /  1: Enable without LF-CRLF conversion.
 /  2: Enable with LF-CRLF conversion. */
 
-
-#define	_USE_MKFS		0
+#define _USE_MKFS 1
 /* This option switches f_mkfs() function. (0:Disable or 1:Enable)
 /  To enable it, also _FS_READONLY need to be set to 0. */
 
-
-#define	_USE_FASTSEEK	0
+#define _USE_FASTSEEK 0
 /* This option switches fast seek feature. (0:Disable or 1:Enable) */
 
-
-#define _USE_LABEL		0
+#define _USE_LABEL 0
 /* This option switches volume label functions, f_getlabel() and f_setlabel().
 /  (0:Disable or 1:Enable) */
 
-
-#define	_USE_FORWARD	0
+#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	932
+#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.
 /
@@ -96,9 +88,8 @@
 /   874  - Thai (OEM, Windows)
 /   1    - ASCII (No extended character. Valid for only non-LFN configuration.) */
 
-
-#define	_USE_LFN	0
-#define	_MAX_LFN	255
+#define _USE_LFN 1 //使能长文件名,分配静态内存用于存储长文件名
+#define _MAX_LFN 255
 /* The _USE_LFN option switches the LFN feature.
 /
 /   0: Disable LFN feature. _MAX_LFN has no effect.
@@ -112,14 +103,12 @@
 /  memory for the working buffer, memory management functions, ff_memalloc() and
 /  ff_memfree(), must be added to the project. */
 
-
-#define	_LFN_UNICODE	0
+#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
+#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().
 /
@@ -130,8 +119,7 @@
 /
 /  When _LFN_UNICODE is 0, this option has no effect. */
 
-
-#define _FS_RPATH	0
+#define _FS_RPATH 0
 /* This option configures relative path feature.
 /
 /   0: Disable relative path feature and remove related functions.
@@ -140,34 +128,30 @@
 /
 /  Note that directory items read via f_readdir() are affected by this option. */
 
-
 /*---------------------------------------------------------------------------/
 / Drive/Volume Configurations
 /---------------------------------------------------------------------------*/
 
-#define _VOLUMES	1
+#define _VOLUMES 2 //【为了同时支持SD卡和NAND Flash两种介质,将 1 修改为 2】
 /* Number of volumes (logical drives) to be used. */
 
-
-#define _STR_VOLUME_ID	0
-#define _VOLUME_STRS	"RAM","NAND","CF","SD1","SD2","USB1","USB2","USB3"
+#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
+#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 enabled. */
 
-
-#define	_MIN_SS		512
-#define	_MAX_SS		512
+#define _MIN_SS 512
+#define _MAX_SS 512
 /* 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
@@ -175,14 +159,12 @@
 /  to variable sector size and GET_SECTOR_SIZE command must be implemented to the
 /  disk_ioctl() function. */
 
-
-#define	_USE_TRIM	0
+#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
+#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.
@@ -193,27 +175,25 @@
 /  bit1=1: Do not trust last allocated cluster number in the FSINFO.
 */
 
-
-
 /*---------------------------------------------------------------------------/
 / System Configurations
 /---------------------------------------------------------------------------*/
 
-#define _FS_NORTC	0
-#define _NORTC_MON	11
-#define _NORTC_MDAY	9
-#define _NORTC_YEAR	2014
+#define _FS_NORTC   0
+#define _NORTC_MON  11
+#define _NORTC_MDAY 9
+#define _NORTC_YEAR 2014
 /* 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. 
+/  _NORTC_MDAY and _NORTC_YEAR have no effect.
 /  These options have no effect at read-only configuration (_FS_READONLY == 1). */
 
+#define _FS_LOCK 3 //可以同时打开3个文件,0表示只能打开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.
@@ -224,10 +204,9 @@
 /      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
+#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()
@@ -244,8 +223,7 @@
 /  The _SYNC_t defines O/S dependent sync object type. e.g. HANDLE, ID, OS_EVENT*,
 /  SemaphoreHandle_t and etc.. */
 
-
-#define _WORD_ACCESS	0
+#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.
 /
@@ -267,4 +245,3 @@
 /   PIC24       0           H8S         0           MSP430      0
 /   PIC32       0           H8/300H     0           8051        0/1
 */
-

+ 0 - 0
User/config/lwip/lwip_dm9k.c → User/conf/lwip/lwip_dm9k.c


+ 0 - 0
User/config/lwip/lwip_dm9k.h → User/conf/lwip/lwip_dm9k.h


+ 0 - 0
User/config/lwip/lwip_eth.c → User/conf/lwip/lwip_eth.c


+ 0 - 0
User/config/lwip/lwip_eth.h → User/conf/lwip/lwip_eth.h


+ 0 - 0
User/config/lwip/lwip_init.c → User/conf/lwip/lwip_init.c


+ 0 - 0
User/config/lwip/lwip_init.h → User/conf/lwip/lwip_init.h


+ 0 - 0
User/config/lwip/lwipopts.h → User/conf/lwip/lwipopts.h


+ 0 - 0
User/config/lwip/port/arch/bpstruct.h → User/conf/lwip/port/arch/bpstruct.h


+ 0 - 0
User/config/lwip/port/arch/cc.h → User/conf/lwip/port/arch/cc.h


+ 0 - 0
User/config/lwip/port/arch/epstruct.h → User/conf/lwip/port/arch/epstruct.h


+ 0 - 0
User/config/lwip/port/arch/init.h → User/conf/lwip/port/arch/init.h


+ 0 - 0
User/config/lwip/port/arch/lib.h → User/conf/lwip/port/arch/lib.h


+ 0 - 0
User/config/lwip/port/arch/lwip_cpu.h → User/conf/lwip/port/arch/lwip_cpu.h


+ 0 - 0
User/config/lwip/port/arch/perf.h → User/conf/lwip/port/arch/perf.h


+ 0 - 0
User/config/lwip/port/arch/sys_arch.c → User/conf/lwip/port/arch/sys_arch.c


+ 0 - 0
User/config/lwip/port/arch/sys_arch.h → User/conf/lwip/port/arch/sys_arch.h


+ 0 - 0
User/config/lwip/port/ethernetif_dm9k.c → User/conf/lwip/port/ethernetif_dm9k.c


+ 0 - 0
User/config/lwip/port/ethernetif_dm9k.h → User/conf/lwip/port/ethernetif_dm9k.h


+ 0 - 0
User/config/lwip/port/ethernetif_eth.c → User/conf/lwip/port/ethernetif_eth.c


+ 0 - 0
User/config/lwip/port/ethernetif_eth.h → User/conf/lwip/port/ethernetif_eth.h


+ 0 - 0
User/config/ucos/app_hooks.c → User/conf/ucos/app_hooks.c


+ 0 - 0
User/config/ucos/cpu_cfg.h → User/conf/ucos/cpu_cfg.h


+ 0 - 0
User/config/ucos/includes.h → User/conf/ucos/includes.h


+ 0 - 0
User/config/ucos/lib_cfg.h → User/conf/ucos/lib_cfg.h


+ 0 - 0
User/config/ucos/os_cfg.h → User/conf/ucos/os_cfg.h


+ 65 - 37
User/main.c

@@ -102,26 +102,16 @@ void init_task(void *pvParameters)
     /* configure the systick handler priority */
     NVIC_SetPriority(SysTick_IRQn, 0x00U);
 
-    OSTaskCreateExt((void (*)(void *))led_task,                                       /* 启动任务函数指针 */
-                    (void *)0,                                                        /* 传递给任务的参数 */
-                    (OS_STK *)&LED_TASK_STK[LED0_STK_SIZE - 1],                       /* 指向任务栈栈顶的指针 */
-                    LED_RX_PRIO,                                                      /* 任务的优先级,必须唯一,数字越低优先级越高 */
-                    LED_RX_PRIO,                                                      /* 任务ID,一般和任务优先级相同 */
-                    (OS_STK *)&LED_TASK_STK[0],                                       /* 指向任务栈栈底的指针。OS_STK_GROWTH 决定堆栈增长方向 */
-                    LED0_STK_SIZE,                                                    /* 任务栈大小 */
-                    (void *)0,                                                        /* 一块用户内存区的指针,用于任务控制块TCB的扩展功能
-                                                                                      (如任务切换时保存CPU浮点寄存器的数据)。一般不用,填0即可 */
-                    OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR | OS_TASK_OPT_SAVE_FP); /* 任务选项字 */
-
-    OSTaskCreateExt((void (*)(void *))misc_task,
-                    (void *)0,
-                    (OS_STK *)&misc_task_stk[MISC_TASK_STK_SIZE - 1],
-                    (INT8U)MISC_PRIO,
-                    (INT16U)MISC_PRIO,
-                    (OS_STK *)&misc_task_stk[0],
-                    (INT32U)MISC_TASK_STK_SIZE,
-                    (void *)0,
-                    (INT16U)OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR | OS_TASK_OPT_SAVE_FP);
+    OSTaskCreateExt((void (*)(void *))misc_task,                                              /* 启动任务函数指针 */
+                    (void *)0,                                                                /* 传递给任务的参数 */
+                    (OS_STK *)&misc_task_stk[MISC_TASK_STK_SIZE - 1],                         /* 指向任务栈栈顶的指针 */
+                    (INT8U)MISC_PRIO,                                                         /* 任务的优先级,必须唯一,数字越低优先级越高 */
+                    (INT16U)MISC_PRIO,                                                        /* 任务ID,一般和任务优先级相同 */
+                    (OS_STK *)&misc_task_stk[0],                                              /* 指向任务栈栈底的指针。OS_STK_GROWTH 决定堆栈增长方向 */
+                    (INT32U)MISC_TASK_STK_SIZE,                                               /* 任务栈大小 */
+                    (void *)0,                                                                /* 一块用户内存区的指针,用于任务控制块TCB的扩展功能
+                                                                                              (如任务切换时保存CPU浮点寄存器的数据)。一般不用,填0即可 */
+                    (INT16U)OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR | OS_TASK_OPT_SAVE_FP); /* 任务选项字 */
 
     // OSTaskCreateExt((void (*)(void *))modbus_master_task,
     //                 (void *)0,
@@ -153,6 +143,17 @@ void init_task(void *pvParameters)
                     (INT32U)NET_TASK_STK_SIZE,
                     (void *)0,
                     (INT16U)OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR | OS_TASK_OPT_SAVE_FP);
+
+    // /* ESP8266 STA任务*/
+    // OSTaskCreateExt((void (*)(void *))esp_8266_task,
+    //                 (void *)0,
+    //                 (OS_STK *)&uart6_task_stk[UART6_TASK_STK_SIZE - 1],
+    //                 (INT8U)UART6_TASK_PRIO,
+    //                 (INT16U)UART6_TASK_PRIO,
+    //                 (OS_STK *)&uart6_task_stk[0],
+    //                 (INT32U)UART6_TASK_STK_SIZE,
+    //                 (void *)0,
+    //                 (INT16U)OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR | OS_TASK_OPT_SAVE_FP);
 }
 
 void misc_task(void)
@@ -162,6 +163,7 @@ void misc_task(void)
     INT8U  temperature;
     INT8U  humidity;
     INT8U  ret;
+    INT8U  key_state;
     AD7606_SetOS(AD_OS_NO);  /* 无过采样 */
     AD7606_SetInputRange(0); /* 0表示输入量程为正负5V, 1表示正负10V */
 
@@ -171,19 +173,45 @@ void misc_task(void)
     while (1)
     {
         iwdg_feed(MISC_DOG);
-        // if ((cnt % 10 == 0) && (cnt != 0))
-        // {
-        //     return;
-        // }
+        key_scan();
+        key_state = key_get();
+        if (key_state != KEY_NONE)
+        {
+            switch (key_state)
+            {
+            case KEY_1_UP:
+                printf("1111111\r\n");
+                break;
+            case KEY_1_DOWN:
+                printf("wwwwwwwwwwww\r\n");
+                break;
+            case KEY_1_LONG:
+                printf("333333333\r\n");
+                break;
+            default:
+                printf("44444444\r\n");
+                break;
+            }
+        }
+        if ((cnt % 10 == 0) && (cnt != 0))
+        {
+            // printf("%ld\r\n", NAND_ReadID());
+            // printf("%c\r\n", NOR_ReadByte(0x4000));
+        }
         if ((cnt % 25 == 0) && (cnt != 0))
         {
+            // printf("%ld\r\n", NAND_ReadID());
             LED2_RUN_TOGGLE;
         }
 
-        // if ((cnt % 15 == 0) && (cnt != 0))
-        // {
-        //     return;
-        // }
+        if ((cnt % 15 == 0) && (cnt != 0))
+        {
+            // if (NOR_EraseSector(0x4000) == 0)
+            // {
+            //     NOR_WriteByte(0x4000, 0xFF);
+            // }
+        }
+
         if (cnt == 10)
         {
             // if (am2303_init())
@@ -193,15 +221,15 @@ void misc_task(void)
             // else
             // {
             ret = am2303_read_data(&temperature, &humidity); //读取温湿度值
-            if (ret == 1)
-            {
-                printf("Temp: (%d).\n", temperature);
-                printf("Humi: (%d).\n", humidity);
-            }
-            else
-            {
-                printf("\r\n未发现DHT11温湿度传感器\r\n");
-            }
+            // if (ret == 1)
+            // {
+            //     printf("Temp: (%d).\n", temperature);
+            //     printf("Humi: (%d).\n", humidity);
+            // }
+            // else
+            // {
+            //     printf("\r\n未发现DHT11温湿度传感器\r\n");
+            // }
             // }
         }
         if (cnt >= 25)

+ 4 - 1
User/main.h

@@ -3,13 +3,16 @@
 
 #include "ad7606.h"
 #include "am2303.h"
+#include "esp8266.h"
 #include "fly_param.h"
 #include "iec10x_type.h"
 #include "includes.h"
 #include "interface.h"
-#include "led.h"
+#include "key.h"
 #include "modbus.h"
+#include "nand_flash.h"
 #include "net.h"
+#include "nor_flash.h"
 #include "stm32f4x7_phy.h"
 #include "stm32f4xx.h"
 #include <stdio.h>

+ 26 - 13
platformio.ini

@@ -22,30 +22,39 @@ build_flags =
   -IUser/bsp/eth
   -IUser/bsp/ext_sram
   -IUser/bsp/dwt
+  -IUser/bsp/esp8266
   -IUser/bsp/interface
   -IUser/bsp/iwdg
-  -IUser/bsp/key
   -IUser/bsp/lcd
+  -IUser/bsp/nand_flash
   -IUser/bsp/nor_flash
   -IUser/bsp/sdio
   -IUser/bsp/spi
   -IUser/bsp/timer
   -IUser/bsp/uart 
+  -IUser/bsp/usb
   -IUser/app
+  -IUser/app/esp8266
   -IUser/app/iec104
+  -IUser/app/key
   -IUser/app/queue
   -IUser/app/led
   -IUser/app/memory
   -IUser/app/modbus
   -IUser/app/net
-  -IUser/config/lwip
-  -IUser/config/lwip/port
-  -IUser/config/lwip/port/arch
-  -IUser/config/ucos
+  -IUser/conf/fatfs
+  -IUser/conf/lwip
+  -IUser/conf/lwip/port
+  -IUser/conf/lwip/port/arch
+  -IUser/conf/ucos
   -ILibraries/CMSIS/Include
   -ILibraries/CMSIS/Device/ST/STM32F4xx/Include
   -ILibraries/STM32F4xx_StdPeriph_Driver/inc
   -ILibraries/STM32F4x7_ETH_Driver/inc
+  -ILibraries/STM32_USB_Device_Library/Core/inc
+  -ILibraries/STM32_USB_HOST_Library/Core/inc
+  -ILibraries/STM32_USB_HOST_Library/Class/MSC/inc
+  -ILibraries/STM32_USB_OTG_Driver/inc
   -IMiddleWare/uCOS_II/uC-CPU
   -IMiddleWare/uCOS_II/uC-CPU/ARM-Cortex-M4/GNU
   -IMiddleWare/uCOS_II/uC-LIB
@@ -57,7 +66,7 @@ build_flags =
   -IMiddleWare/lwip-2.0.2/src/include/netif
   -IMiddleWare/lwip-2.0.2/src/include/posix
   -IMiddleWare/lwip-2.0.2/src/include/posix/sys
-  ; -IMiddleWare/FATFS
+  -IMiddleWare/FATFS
   ; 定义全局宏
   -D STM32F40_41xxx
   -D USE_STDPERIPH_DRIVER
@@ -72,7 +81,7 @@ build_unflags =
   -DSTM32F4
   -Os
 
-src_filter = +<User/> 
+build_src_filter = +<User/> 
             +<Project/GCC/> 
             +<Libraries/STM32F4xx_StdPeriph_Driver/src/>
             -<Libraries/STM32F4xx_StdPeriph_Driver/src/stm32f4xx_qspi.c>
@@ -81,6 +90,9 @@ src_filter = +<User/>
             -<Libraries/STM32F4xx_StdPeriph_Driver/src/stm32f4xx_cec.c>
             -<Libraries/STM32F4xx_StdPeriph_Driver/src/stm32f4xx_spdifrx.c>
             +<Libraries/STM32F4x7_ETH_Driver/src/>
+            ; +<ILibraries/STM32_USB_Device_Library/Core/src/>
+            ; +<ILibraries/STM32_USB_HOST_Library/Core/src/>
+            ; +<Libraries/STM32_USB_OTG_Driver/src/>
             +<MiddleWare/lwip-2.0.2/src/api>
             +<MiddleWare/lwip-2.0.2/src/core>
             +<MiddleWare/lwip-2.0.2/src/core/ipv4>
@@ -101,13 +113,14 @@ src_filter = +<User/>
             -<MiddleWare/uCOS_II/uCOS-II/Ports/ARM-Cortex-M3>
             -<MiddleWare/uCOS_II/uCOS-II/Ports/ARM-Cortex-M4/Generic/IAR>
             -<MiddleWare/uCOS_II/uCOS-II/Ports/ARM-Cortex-M4/Generic/RealView>
-            ; +<MiddleWare/FATFS>
-            ; -<MiddleWare/FATFS/option/cc932.c>
-            ; -<MiddleWare/FATFS/option/cc949.c>
-            ; -<MiddleWare/FATFS/option/cc950.c>
-            ; -<MiddleWare/FATFS/option/ccsbcs.c>
+            +<MiddleWare/FATFS>
+            -<MiddleWare/FATFS/diskio.c>
+            -<MiddleWare/FATFS/option/cc932.c>
+            -<MiddleWare/FATFS/option/cc949.c>
+            -<MiddleWare/FATFS/option/cc950.c>
+            -<MiddleWare/FATFS/option/ccsbcs.c>
             ; -<MiddleWare/FATFS/option/syscall.c>
-            ; -<MiddleWare/FATFS/option/unicode.c>
+            -<MiddleWare/FATFS/option/unicode.c>
 ; debug_tool = cmsis-dap
 ; upload_protocol = cmsis-dap
 ; debug_tool = jlink