LittleFS Flash File System: Difference between revisions
No edit summary |
No edit summary |
||
Line 152: | Line 152: | ||
If you have a Saleae Logic analyzer, you would see something like the following: | If you have a Saleae Logic analyzer, you would see something like the following: | ||
[[File:SaleaeSpiCapture.jpg]]<br> | [[File:SaleaeSpiCapture.jpg]]<br> | ||
Note: It appears that using the HAL_SPI_Receive() function to clock in receive data may transmit | |||
garbage on the MOSI line. This doesn't appear to bother the Winbond part. | |||
===The littlFS Code=== | ===The littlFS Code=== | ||
I Cloned '''littleFS''' to my local machine. | I Cloned '''littleFS''' to my local machine. |
Revision as of 20:00, 10 October 2020
LittleFS Flash File System
I recently learned about a compact embedded Flash File System known as SpiFFS.
While researching SpiFFS, I soon learned of another compact embedded Flash File System, LittleFS.
LittleFS appears to be even more compact and has additional security in that it creates and maintains a CRC for each file, and verifies the CRC each time the file is read.
- Compact
- Power-loss resilience
- Dynamic wear leveling
- Bounded RAM/ROM
- Maintains revision count and CRC (security features)
The Hardware
Let's put this file system onto an STM32 board and check it out.
I'll be using a NUCLEO-F103RB ($10.34), with a Winbond W25Q128FVSG SPI Flash module from Ebay. https://www.ebay.com/itm/264290947181 If the link doesn't work, just search Ebay for "W25Q128 Module". I bought two for $8.26 (free shipping). Download the PDF for the W25Q128FVSG: https://www.winbond.com/resource-files/w25q128fv%20rev.m%2005132016%20kms.pdf
The W25Q128FVSG uses 2.7V to 3.6V for operation. Here's a diagram for the the chip's pins and signals,
along with the Ebay "W25QXX" module" photo:
Since the NUCLEO-F103RB powers its STM32F103RB processor with 3.3V, this "W25QXX" module" is compatible with the NUCLEO-F103RB. (The STM32-F103RB is capable of running from 2.0V to 3.6V.)
Let's look at the signal pins on the NUCLEO-F103RB:
On the NUCLEO board, the SPI1_SCK signal is being used to drive the board's Green LED.
So, we will use SPI2 bus and its signal pins.
To connect up the signals for power and the SPI BUS, here's the wiring diagram that was used:
Winbond W25Q128FVSG SPI Flash Module wiring:
Signal Windbond Pin W25QXX Pin NUCLEO-F103RB Signal & Pin DI 5 1 PB15 SPI2_MOSI CN10-26 CLK 6 2 PB13 SPI2_SCK CN10-30 GND 4 3 GND CN10-20 DO 2 4 PB14 SPI2_MISO CN10-28 CS 1 5 PB12 SPI2_NSS CN10-16 VCC 8 6 +3V3 CN7-16
The Software
The SPI and Serial Interface
Let's start by getting enough in place to read the Manufacturer and Device ID from the Winbond part. Within the STM32CubeMX package for your project: * Enable SPI2 as "Full-Duplex Master"., * Hardware NSS Signal: "Disable" - We will use GPIO control for this signal. - Don't bother trying to use some form of SPI hardware control for this signal pin. * Frame Format - Motorola, 8-Bits, MSB First * Clock Prescaler 256, (140,625KBits/s - this can be changed later) * Clock Polarity - CPOL: Low (signal is normally low) * Clock Phase - CPHA: 1 Edge (first rising edge) * CRC Calculation: Disabled * NSS Signal Type: Software (GPIO contol) * Select PB12 Pin, change signal to GPIO_Output, name it "SPI2_NCS" (so we will be using the same names) * Configure the HCLK for 72MHz (this is rather optional). (The Winbond part will function well beyond what this STM32 processor can clock over the SPI bus) Optional configuration and code that will ease debugging Select and configure USART2 (The USART2 signals are routed to the NEUCLEO's ST-LINK chip, creating a serial connection to your host computer over USB.) * Mode: Asychronous, Hardware Flow Control (RS232): Disable * Baud Rate: 115200, 8 bits, No Parity, 1 Stop Bit, Data Direction: Receive and Transmit
Serial Output, Serial Input, and printf() Support
To use the printf() library, pushing the output to the USB serial port, we need define our own fputc().
I put the following in main.c, in the "Private user code" section, #0 as follows:
/* Private user code ---------------------------------------------------------*/ /* USER CODE BEGIN 0 */ // Defining an fputc() function allows the printf() to work as expected int fputc(int ch, FILE *f) { uint8_t c = (uint8_t)ch; HAL_StatusTypeDef status = HAL_UART_Transmit(&huart2,&c,1,50); // send character to UART2 with 50ms timeout if(HAL_OK == status) return ch; else return EOF; }
// For serial input - read a character from the serial port:
int fgetc(FILE *f) { uint8_t c; HAL_StatusTypeDef status = HAL_UART_Receive(&huart2,&c,1,50); // read character from UART2 with 50ms timeout if(HAL_OK == status) return c; else return EOF; } /* USER CODE END 0 */
Add the following in main.c, in the "Private includes" section as follows:
/* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ #include <stdio.h> // printf() #include "w25q128.h" // Winbond block driver /* USER CODE END Includes */
Add the following in main.c, at the end of MX_SPI2_Init() as follows:
/* USER CODE BEGIN SPI2_Init 2 */ // Drive SPI2_NCS high to inactive state HAL_GPIO_WritePin(SPI2_NCS_GPIO_Port, SPI2_NCS_Pin, GPIO_PIN_SET); // Drive /CS high /* USER CODE END SPI2_Init 2 */
Create a new C file for our Winbond Block Driver code, "w25q128.c"
// w25q128.c // Author: Jim Merkle, 10/10/2020 #include "w25q128.h" #include "main.h" // STM32 HAL defines #include <stdio.h> // printf() // Return 3 byte Manufacturer and device ID (requires 3 byte buffer) int W25_ReadID(uint8_t *buf, int bufSize) { int retval; uint8_t idcmd = W25_CMD_READID; if(bufSize < W25_ID_BUF_SIZE) return HAL_ERROR; // buffer too small W25_CS_ENABLE(); // Drive Winbond chip select, /CS low HAL_SPI_Transmit(&hspi, &idcmd , sizeof(idcmd ), TIMEOUT); // Send the ID command retval = HAL_SPI_Receive(&hspi, buf, W25_ID_BUF_SIZE, TIMEOUT); W25_CS_DISABLE(); printf("%s: retval %d, 0x%02X, 0x%02X, 0x%02X\r\n",__func__, retval, buf[0],buf[1],buf[2]); return retval; } // W25_ReadID()
Create a new header file, "w25q128.h"
// w25q128.h // Author: Jim Merkle, 10/10/2020 #ifndef _w25q128_h_ #define _w25q128_h_ #include <stdint.h> // uint8_t #include "main.h" // HAL header files, SPI and GPIO defines #define W25_CMD_READID 0x9F extern SPI_HandleTypeDef hspi2; // STM32 SPI instance #define TIMEOUT 100 // MS #define W25_ID_BUF_SIZE 3 // bytes /* W25Q128 chip select is active LOW */ #define W25_CS_ENABLE() HAL_GPIO_WritePin(SPI2_NCS_GPIO_Port, SPI2_NCS_Pin, GPIO_PIN_RESET) #define W25_CS_DISABLE() HAL_GPIO_WritePin(SPI2_NCS_GPIO_Port, SPI2_NCS_Pin, GPIO_PIN_SET) // Return 3 byte Manufacturer and device ID (requires 3 byte buffer) int W25_ReadID(uint8_t *buf, size_t bufSize); #endif // _w25q128_h_
Now, we can call the W25_ReadID() function in main() to read the chip ID
Add the following line to main.c, just before the while(1) loop as follows:
/* Infinite loop */ /* USER CODE BEGIN WHILE */ uint8_t idbuffer[W25_ID_BUF_SIZE]; // buffer for W25_ReadID() W25_ReadID(idbuffer,sizeof(idbuffer)); // Read the Manufacturer ID and device ID from the Winbond part while (1)
Running the program should produce the following output: "W25_ReadID: retval 0, 0xEF, 0x40, 0x18"
If you have a Saleae Logic analyzer, you would see something like the following:
Note: It appears that using the HAL_SPI_Receive() function to clock in receive data may transmit garbage on the MOSI line. This doesn't appear to bother the Winbond part.
The littlFS Code
I Cloned littleFS to my local machine.
Reference Information:
https://uimeter.com/2018-04-12-Try-LittleFS-on-STM32-and-SPI-Flash/ https://github.com/ARMmbed/littlefs https://github.com/ARMmbed/littlefs/blob/master/README.md