STM32 - SPI флешка W25Qxx
Здравствуйте.
Библиотека для работы микроконтроллера stm32 c SPI-флешками W25Qxx…
W25Q256
W25Q128
W25Q64
W25Q32
W25Q16
W25Q80
W25Q40
W25Q20
W25Q10
Так же должно работать с
Со всеми я конечно не проверял, но
Почитать подробнее про сабж можно здесь, и в даташите.
Эти флешки устроены так же как флеш-память у stm32, то есть память у них разбита на условные страницы по 256 байт, страницы объединяются в сектора по 4096 байт, а сектора в блоки по 65536 байт. Организацию флешки можно посмотреть на схеме…
схема
Страниц здесь не показано (только сектора и блоки), но условно они есть.
Страниц здесь не показано (только сектора и блоки), но условно они есть.
Перед тем как что-то записать, нужно стереть сектор (4096 байт) или блок (65536 байт). Можно стереть несколько секторов или блоков, или весь чип полностью. Во время стирания ячейки заполняются значениями 0xFF.
почему 0xFF
Почему нужно стирать (заполнять значениями 0xFF) память?
Дело в том, что когда происходит запись байта в ячейку, то биты в этой ячейки не просто так берут и перезаписываются, а совершается операция логического «И» над тем что есть в ячейке и новым значением.
Наглядно это выглядит так. Ячейка у нас стёрта (биты заполнены единицами) и мы записываем в неё число 7 (0х07)…
Выполняется логическое «И», и в ячейку записывается нужное нам число 7.
А теперь допустим что мы хотим записать в эту же ячейку число 13 (0x0d) поверх старого значения…
Выполняется логическое «И» между старым значением 0х07 и новым 0x0d. В результате вместо желаемого числа 13, в ячейку записывается число 5.
Дело в том, что когда происходит запись байта в ячейку, то биты в этой ячейки не просто так берут и перезаписываются, а совершается операция логического «И» над тем что есть в ячейке и новым значением.
Наглядно это выглядит так. Ячейка у нас стёрта (биты заполнены единицами) и мы записываем в неё число 7 (0х07)…
Выполняется логическое «И», и в ячейку записывается нужное нам число 7.
А теперь допустим что мы хотим записать в эту же ячейку число 13 (0x0d) поверх старого значения…
Выполняется логическое «И» между старым значением 0х07 и новым 0x0d. В результате вместо желаемого числа 13, в ячейку записывается число 5.
Сектор, это минимальный кусочек памяти, который можно стереть. То есть, если нам нужно записать один, два, пять или сто байт, то в любом случае придётся стирать весь сектор. Однако как вы уже поняли
При старте программа покажет маркировку чипа, размеры и количество страниц, секторов и блоков...
Стирание
Сектора…
W25qxx_EraseSector(0);
Аргумент, это номер сектора, в данном случае будет стёрт нулевой сектор, то есть с нулевой по 4095-ую ячейки. Если указать 1, тогда будут стерты ячейки с 4096 по 8192, и т.д.
Блока…
W25qxx_EraseBlock(0);
Аргумент, это номер блока, в данном случае будет стёрт нулевой блок, то есть с нулевой по 65535-ую ячейки. Если указать 1, тогда будут стерты ячейки с 65536 по 131072, и т.д.
Стирание чипа полностью…
W25qxx_EraseChip();
Запись и чтение данных
Записывать данные можно по разному. Если нужно записать один или пару-тройку байт тогда достаточно функции
uint8_t b0 = 's';
uint8_t b1 = 't';
uint8_t b2 = 'D';
W25qxx_WriteByte(b0, 25);
W25qxx_WriteByte(b1, 26);
W25qxx_WriteByte(b2, 27);
Записываем три байта в 25-ую, 26-ую и 27-ую ячейки.
Эта функция не привязана к страницам, секторам или блокам, мы просто указываем любой адрес от нуля до максимального размера флешки.
uint8_t buf[64] = {0,};
W25qxx_ReadByte(&buf[0], 25);
W25qxx_ReadByte(&buf[1], 26);
W25qxx_ReadByte(&buf[2], 27);
Когда нужно записывать большое количество данных, тогда используются другие функции, но прежде чем говорить о них надо сделать некоторые пояснения. Есть функции для записи страницы, сектора и блока, однако за один раз можно записать не больше 256 байт (одну страницу).
Это связано с тем, что у флешки есть внутренний буфер на 256 байт
Таким образом получается следующее:
Если нужно записать не больше 256 байт, используем функцию для записи страницы…
uint8_t w_buf[] = "istarik.ru";
W25qxx_WritePage(w_buf, 0, 0, 10);
Первый аргумент это буфер, который хотим записать. Второй — это порядковый номер страницы
Чтоб было понятнее: если бы мы хотели записать эту строчку после тех трёх байт, тогда сдвинули бы запись на 28 ячеек, то есть так…
W25qxx_WritePage(w_buf, 0, 28, 10);
Если бы хотели записать в начало первой страницы, тогда второй аргумент был бы 1…
W25qxx_WritePage(w_buf, 1, 0, 10);
Думаю с нумерацией всё понятно. Сектора и блоки нумеруются так же.
Последний аргумент, это количество байт для записи.
Теперь очень важный нюанс. Если мы хотим записать 256 байт, тогда нужно указывать начало страницы, сдвиг делать нельзя. То есть, если мы сделаем так…
W25qxx_WritePage(w_buf, 0, 20, 256);
… тогда 236 байт запишутся как положено, а остальные не перейдут на следующую страницу, они потеряются. То есть, если вы записываете какое-то количество байт не в начало страницы, тогда это количество должно умещаться в этот остаток.
uint8_t buf[256] = {0,};
W25qxx_ReadPage(buf, 0, 28, 10);
Первый аргумент это буфер в который запишутся данные, остальные аргументы делают то же самое что и при записи. Это так же относится к чтению секторов и блоков.
Если нужно записать не больше 4096 байт, используем функцию для записи сектора…
W25qxx_WriteSector(w_buf, 0, 1350, 2500);
С аргументами всё то же самое что и при записи страницы.
W25qxx_ReadSector(buf, 0, 1350, 10);
Ну и наконец если нужно записать не больше 65536 байт, используем функцию для записи блока…
W25qxx_WriteBlock(w_buf, 0, 9350, 10);
И с аргументами опять таки всё то же.
Если данных больше 65536 байт, тогда крутим запись блока в цикле увеличивая порядковый номер (второй аргумент).
Функции записи сектора и блока содержат в себе функцию записи страницы, так что запись в любом случае ведётся по 256 байт. Таким образом размер поступающих данных не превышает внутреннего буфера флешки.
W25qxx_ReadBlock(buf, 0, 9350, 10);
Помимо функций чтения страницы, сектора и блока, есть универсальная функция, которая может читать любое кол-во байт с любого места…
W25qxx_ReadBytes(buf, 9350, 10);
Здесь просто указывается конкретная ячейка с которой нужно начинать чтение, и количество байт.
Проверка стерта ли страница (сектор, блок) полностью или частично
Для страницы это делается так…
uint8_t clear = W25qxx_IsEmptyPage(0, 0);
if(clear) HAL_UART_Transmit(&huart1, (uint8_t*)"Clear\n", 6, 100);
else HAL_UART_Transmit(&huart1, (uint8_t*)"Not clear\n", 10, 100);
Функция возвращает либо 1 — стёрта, либо 0 не стёрта.
Первый аргумент это номер страницы, второй — это сдвиг. То есть в данном случае мы проверяем нулевую страницу полностью, с самого начала.
Если хотим проверить не всю страницу, а начиная с пятой ячейки, тогда аргументы функции будут такими…
uint8_t clear = W25qxx_IsEmptyPage(0, 5);
Для сектора…
uint8_t clear = W25qxx_IsEmptySector(0, 0);
Для блока…
uint8_t clear = W25qxx_IsEmptyBlock(0, 0);
Аргументы те же что и для страницы.
Все описанные функции продемонстрированы в библиотеке.
Подключение
Питание от 2.7 до 3.6 В.
Это подключение для работы в обычном режиме SPI. Ещё есть режимы Dual и Quad SPI, в которых можно работать с удвоенной и учетверённой скоростью. В этих режимах и подключение другое, и работать нужно по другому. Я не пробовал поэтому ничего не скажу.
Программа
В Кубе настраиваем какой-нибудь SPI, у меня будет второй…
Скорость SPI можно указать повыше (если камень позволяет), флешка поддерживает до 80МГц.
Настраиваем какую-нибудь ножку как GPIO_Output, это будет Chip Select (на картинке он обозначен как NSS)…
Output level ставим в High, и даём ей название
Ну и настраиваем какой-нибудь USART для вывода инфы. Всё, можно генерить проект.
Теперь скачиваем библиотеку и добавляем в проект файлы
В
/* USER CODE BEGIN Includes */
#include "w25qxx.h"
/* USER CODE END Includes */
В файле
extern SPI_HandleTypeDef hspi2;
extern UART_HandleTypeDef huart1;
#define DEBUG_UART &huart1
#define W25QXX_SPI_PTR &hspi2
Собственно на этом всё, перед бесконечным циклом пишем функцию инициализации…
W25qxx_Init();
… и дальше смотрите в моём
Да, чуть не забыл, это не моя библиотека, я её только немного модифицировал, оригинал находится здесь.
Всем спасибо
Телеграм-чат istarik
Телеграм-чат STM32
- 0
- stD
41101
Поддержать автора
Комментарии (0)