norflash(2564).c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440
  1. /* includes ----------------------------------------------------------*/
  2. #include "norflash.h"
  3. /* typedef -----------------------------------------------------------*/
  4. /* define ------------------------------------------------------------*/
  5. /* macro -------------------------------------------------------------*/
  6. /* variables ---------------------------------------------------------*/
  7. uint16_t g_norflash_type = NM25Q128; /* 默认时NM25Q128 */
  8. /* function prototypes -----------------------------------------------*/
  9. /**
  10. * @brief 初始化SPI NOR FLASH
  11. * @param NONE
  12. * @retval NONE
  13. */
  14. void norflash_init(void)
  15. {
  16. uint8_t temp;
  17. // NORFLASH_CS_GPIO_CLK_ENABLE(); /* NORFLASH CS脚 时钟使能 */
  18. //
  19. // GPIO_InitTypeDef gpio_init_struct;
  20. // gpio_init_struct.Pin = NORFLASH_CS_GPIO_PIN;
  21. // gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP;
  22. // gpio_init_struct.Pull = GPIO_PULLUP;
  23. // gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;
  24. // HAL_GPIO_Init(NORFLASH_CS_GPIO_PORT, &gpio_init_struct); /* CS引脚模式设置(复用输出) */
  25. NORFLASH_CS(1); /* 取消片选 */
  26. // spi2_init(); /* 初始化SPI2 */
  27. spi_set_speed(SPI_SPEED_2); /* SPI 切换到高速状态 18Mhz */
  28. g_norflash_type = norflash_read_id(); /* 读取FLASH ID. */
  29. if (g_norflash_type == W25Q256) /* SPI FLASH为W25Q256, 必须使能4字节地址模式 */
  30. {
  31. temp = norflash_read_sr(3); /* 读取状态寄存器3,判断地址模式 */
  32. if ((temp & 0X01) == 0) /* 如果不是4字节地址模式,则进入4字节地址模式 */
  33. {
  34. norflash_write_enable(); /* 写使能 */
  35. temp |= 1 << 1; /* ADP=1, 上电4位地址模式 */
  36. norflash_write_sr(3, temp); /* 写SR3 */
  37. NORFLASH_CS(0);
  38. spi_read_write_byte(FLASH_Enable4ByteAddr); /* 使能4字节地址指令 */
  39. NORFLASH_CS(1);
  40. }
  41. }
  42. //printf("ID:%x\r\n", g_norflash_type);
  43. }
  44. /**
  45. * @brief 等待空闲
  46. * @param NONE
  47. * @retval NONE
  48. */
  49. static void norflash_wait_busy(void)
  50. {
  51. while ((norflash_read_sr(1) & 0x01) == 0x01); /* 等待BUSY位清空 */
  52. }
  53. /**
  54. * @brief 25QXX写使能
  55. * @note 将S1寄存器的WEL置位
  56. * @param NONE
  57. * @retval NONE
  58. */
  59. void norflash_write_enable(void)
  60. {
  61. NORFLASH_CS(0);
  62. spi_read_write_byte(FLASH_WriteEnable); /* 发送写使能 */
  63. NORFLASH_CS(1);
  64. }
  65. /**
  66. * @brief 25QXX发送地址
  67. * @note 根据芯片型号不同,发送24bit/32bit地址
  68. * @param address : 要发送的地址
  69. * @retval NONE
  70. */
  71. static void norflash_send_address(uint32_t address)
  72. {
  73. if (g_norflash_type == W25Q256) /* 只有W25Q256支持4字节地址模式 */
  74. {
  75. spi_read_write_byte((uint8_t)((address)>>24)); /* 发送 bit31 ~ bit24 地址 */
  76. }
  77. spi_read_write_byte((uint8_t)((address)>>16)); /* 发送 bit23 ~ bit16 地址 */
  78. spi_read_write_byte((uint8_t)((address)>>8)); /* 发送 bit15 ~ bit8 地址 */
  79. spi_read_write_byte((uint8_t)address); /* 发送 bit7 ~ bit0 地址 */
  80. }
  81. /**
  82. * @brief 读取25QXX的状态寄存器,25QXX一共有3个状态寄存器
  83. * @note 状态寄存器1:
  84. * BIT7 6 5 4 3 2 1 0
  85. * SPR RV TB BP2 BP1 BP0 WEL BUSY
  86. * SPR:默认0,状态寄存器保护位,配合WP使用
  87. * TB,BP2,BP1,BP0:FLASH区域写保护设置
  88. * WEL:写使能锁定
  89. * BUSY:忙标记位(1,忙;0,空闲)
  90. * 默认:0x00
  91. *
  92. * 状态寄存器2:
  93. * BIT7 6 5 4 3 2 1 0
  94. * SUS CMP LB3 LB2 LB1 (R) QE SRP1
  95. *
  96. * 状态寄存器3:
  97. * BIT7 6 5 4 3 2 1 0
  98. * HOLD/RST DRV1 DRV0 (R) (R) WPS ADP ADS
  99. *
  100. * @param regno: 状态寄存器号,范围:1~3
  101. * @retval 状态寄存器值
  102. */
  103. uint8_t norflash_read_sr(uint8_t regno)
  104. {
  105. uint8_t byte = 0, command = 0;
  106. switch (regno)
  107. {
  108. case 1:
  109. command = FLASH_ReadStatusReg1; /* 读状态寄存器1指令 */
  110. break;
  111. case 2:
  112. command = FLASH_ReadStatusReg2; /* 读状态寄存器2指令 */
  113. break;
  114. case 3:
  115. command = FLASH_ReadStatusReg3; /* 读状态寄存器3指令 */
  116. break;
  117. default:
  118. command = FLASH_ReadStatusReg1;
  119. break;
  120. }
  121. NORFLASH_CS(0);
  122. spi_read_write_byte(command); /* 发送读寄存器命令 */
  123. byte = spi_read_write_byte(0Xff); /* 读取一个字节 */
  124. NORFLASH_CS(1);
  125. return byte;
  126. }
  127. /**
  128. * @brief 写25QXX状态寄存器
  129. * @note 寄存器说明见norflash_read_sr函数说明
  130. * @param regno: 状态寄存器号,范围:1~3
  131. * @param sr : 要写入状态寄存器的值
  132. * @retval NONE
  133. */
  134. void norflash_write_sr(uint8_t regno, uint8_t sr)
  135. {
  136. uint8_t command = 0;
  137. switch (regno)
  138. {
  139. case 1:
  140. command = FLASH_WriteStatusReg1; /* 写状态寄存器1指令 */
  141. break;
  142. case 2:
  143. command = FLASH_WriteStatusReg2; /* 写状态寄存器2指令 */
  144. break;
  145. case 3:
  146. command = FLASH_WriteStatusReg3; /* 写状态寄存器3指令 */
  147. break;
  148. default:
  149. command = FLASH_WriteStatusReg1;
  150. break;
  151. }
  152. NORFLASH_CS(0);
  153. spi_read_write_byte(command); /* 发送读寄存器命令 */
  154. spi_read_write_byte(sr); /* 写一个字节 */
  155. NORFLASH_CS(1);
  156. }
  157. /**
  158. * @brief 读取芯片ID
  159. * @param NONE
  160. * @retval FLASH芯片ID
  161. * @note 芯片ID列表见: norflash.h, 芯片列表部分
  162. */
  163. uint16_t norflash_read_id(void)
  164. {
  165. uint16_t deviceid;
  166. NORFLASH_CS(0);
  167. spi_read_write_byte(FLASH_ManufactDeviceID); /* 发送读 ID 命令 */
  168. spi_read_write_byte(0); /* 写入一个字节 */
  169. spi_read_write_byte(0);
  170. spi_read_write_byte(0);
  171. deviceid = spi_read_write_byte(0xFF) << 8; /* 读取高8位字节 */
  172. deviceid |= spi_read_write_byte(0xFF); /* 读取低8位字节 */
  173. NORFLASH_CS(1);
  174. return deviceid;
  175. }
  176. /**
  177. * @brief 读取SPI FLASH
  178. * @note 在指定地址开始读取指定长度的数据
  179. * @param pbuf : 数据存储区
  180. * @param addr : 开始读取的地址(最大32bit)
  181. * @param datalen : 要读取的字节数(最大65535)
  182. * @retval NONE
  183. */
  184. void norflash_read(uint8_t *pbuf, uint32_t addr, uint16_t datalen)
  185. {
  186. uint16_t i;
  187. NORFLASH_CS(0);
  188. spi_read_write_byte(FLASH_ReadData); /* 发送读取命令 */
  189. norflash_send_address(addr); /* 发送地址 */
  190. for(i=0;i<datalen;i++)
  191. {
  192. pbuf[i] = spi_read_write_byte(0XFF); /* 循环读取 */
  193. }
  194. NORFLASH_CS(1);
  195. }
  196. /**
  197. * @brief SPI在一页(0~65535)内写入少于256个字节的数据
  198. * @note 在指定地址开始写入最大256字节的数据
  199. * @param pbuf : 数据存储区
  200. * @param addr : 开始写入的地址(最大32bit)
  201. * @param datalen : 要写入的字节数(最大256),?该数不应该超过该页剩余字节数
  202. * @retval NONE
  203. */
  204. static void norflash_write_page(uint8_t *pbuf, uint32_t addr, uint16_t datalen)
  205. {
  206. uint16_t i;
  207. norflash_write_enable(); /* 写使能 */
  208. NORFLASH_CS(0);
  209. spi_read_write_byte(FLASH_PageProgram); /* 发送写页命令 */
  210. norflash_send_address(addr); /* 发送地址 */
  211. for(i=0;i<datalen;i++)
  212. {
  213. spi_read_write_byte(pbuf[i]); /* 循环写入 */
  214. }
  215. NORFLASH_CS(1);
  216. norflash_wait_busy(); /* 等待写入结束 */
  217. }
  218. /**
  219. * @brief 无检验写SPI FLASH
  220. * @note 必须确保所写的地址范围内的数据全部为0xff,否则在非0xff处写入的数据将失败
  221. * 具有自动换页的功能
  222. * 在指定地址开始写入指定长度的数据,但是要确保地址不越界
  223. *
  224. * @param pbuf : 数据存储区
  225. * @param addr : 开始写入的地址(最大32bit)
  226. * @param datalen : 要写入的字节数(最大65535)
  227. * @retval NONE
  228. */
  229. static void norflash_write_nocheck(uint8_t *pbuf, uint32_t addr, uint16_t datalen)
  230. {
  231. uint16_t pageremain;
  232. pageremain = 256 - addr % 256; /* 单页剩余的字节数 */
  233. if (datalen <= pageremain) /* 不大于256个字节 */
  234. {
  235. pageremain = datalen;
  236. }
  237. while (1)
  238. {
  239. /* 当写入字节比页内剩余地址还少的时候,一次性写完
  240. * 当写入字节比页内剩余地址还多的时候,先写完整个页内剩余地址,然后根据剩余长度进行不同处理
  241. */
  242. norflash_write_page(pbuf, addr, pageremain);
  243. if (datalen == pageremain) /* 写入结束了 */
  244. {
  245. break;
  246. }
  247. else /* datalen > pageremain */
  248. {
  249. pbuf += pageremain; /* pbuf指针地址偏移,前面已经写了pageremain字节 */
  250. addr += pageremain; /* 写地址偏移,前面已经写了pageremain字节 */
  251. datalen -= pageremain; /* 写入总长度减去已经写入了的字节数 */
  252. if (datalen > 256) /* 剩余数据还大于一页,可以一次写一页 */
  253. {
  254. pageremain = 256; /* 一次可以写入256字节 */
  255. }
  256. else /* 剩余数据大于一页,可以一次写完 */
  257. {
  258. pageremain = datalen; /* 不够256个字节了 */
  259. }
  260. }
  261. }
  262. }
  263. /**
  264. * @brief 写SPI FLASH
  265. * @note 在指定地址开始写入指定长度的数据,该函数带擦除操作
  266. * SPI FLASH 一般是: 256个字节为一个Page, 4Kbytes为1个Sector, 16个扇区为1个Block
  267. * 擦除的最小单位为Sector.
  268. *
  269. * @param pbuf : 数据存储区
  270. * @param addr : addr : 开始写入的地址(最大32bit)
  271. * @param datalen : datalen : 要写入的字节数(最大65535)
  272. * @retval NONE
  273. */
  274. uint8_t g_norflash_buf[4096]; /* 扇区缓存 */
  275. void norflash_write(uint8_t *pbuf, uint32_t addr, uint16_t datalen)
  276. {
  277. uint32_t secpos;
  278. uint16_t secoff;
  279. uint16_t secremain;
  280. uint16_t i;
  281. uint8_t *norflash_buf;
  282. norflash_buf = g_norflash_buf;
  283. secpos = addr / 4096; /* 扇区地址 */
  284. secoff = addr % 4096; /* 在扇区内的偏移 */
  285. secremain = 4096 - secoff; /* 扇区剩余空间大小 */
  286. //printf("ad:%X,nb:%X\r\n", addr, datalen); /* 测试用 */
  287. if (datalen <= secremain)
  288. {
  289. secremain = datalen; /* 不大于4096个字节 */
  290. }
  291. while (1)
  292. {
  293. norflash_read(norflash_buf, secpos * 4096, 4096); /* 读出整个扇区的内容 */
  294. for (i = 0; i < secremain; i++) /* 校验数据 */
  295. {
  296. if (norflash_buf[secoff + i] != 0XFF)
  297. {
  298. break; /* 需要擦除,直接退出for循环 */
  299. }
  300. }
  301. if (i < secremain) /* 需要擦除 */
  302. {
  303. norflash_erase_sector(secpos); /* 擦除这个扇区 */
  304. for (i = 0; i < secremain; i++) /* 复制 */
  305. {
  306. norflash_buf[i + secoff] = pbuf[i];
  307. }
  308. norflash_write_nocheck(norflash_buf, secpos * 4096, 4096); /* 写入整个扇区 */
  309. }
  310. else /* 写已经擦除了的,直接写入扇区剩余区间 */
  311. {
  312. norflash_write_nocheck(pbuf, addr, secremain); /* 直接写扇区 */
  313. }
  314. if (datalen == secremain)
  315. {
  316. break; /* 写入结束了 */
  317. }
  318. else /* 写入未结束 */
  319. {
  320. secpos++; /* 扇区地址增1 */
  321. secoff = 0; /* 偏移位置未0 */
  322. pbuf += secremain; /* 指针偏移 */
  323. addr += secremain; /* 写地址偏移 */
  324. datalen -= secremain; /* 字节数递减 */
  325. if (datalen > 4096)
  326. {
  327. secremain = 4096; /* 下一个扇区还是写不完 */
  328. }
  329. else
  330. {
  331. secremain = datalen;/* 下一个扇区可以写完了 */
  332. }
  333. }
  334. }
  335. }
  336. /**
  337. * @brief 擦除整个芯片
  338. * @note 等待时间超长...
  339. * @param NONE
  340. * @retval NONE
  341. */
  342. void norflash_erase_chip(void)
  343. {
  344. norflash_write_enable(); /* 写使能 */
  345. norflash_wait_busy(); /* 等待空闲 */
  346. NORFLASH_CS(0);
  347. spi_read_write_byte(FLASH_ChipErase); /* 发送读寄存器命令 */
  348. NORFLASH_CS(1);
  349. norflash_wait_busy(); /* 等待芯片擦除结束 */
  350. }
  351. /**
  352. * @brief 擦除一个扇区
  353. * @note 注意,这里是扇区地址,不是字节地址
  354. * 擦除一个扇区的最少时间:150ms
  355. *
  356. * @param saddr : 扇区地址,根据实际容量设置
  357. * @retval NONE
  358. */
  359. void norflash_erase_sector(uint32_t saddr)
  360. {
  361. //printf("fe:%x\r\n", saddr); /* 监视flash擦除情况,测试用 */
  362. saddr *= 4096;
  363. norflash_write_enable(); /* 写使能 */
  364. norflash_wait_busy(); /* 等待空闲 */
  365. NORFLASH_CS(0);
  366. spi_read_write_byte(FLASH_SectorErase); /* 发送写页指令 */
  367. norflash_send_address(saddr); /* 发送地址 */
  368. NORFLASH_CS(1);
  369. norflash_wait_busy(); /* 等待扇区擦除完成 */
  370. }