w25qxx is a MISRA-checked driver for a dozen SPI flash capacities
W25QXX(W25Q10, W25Q20, W25Q40, W25Q80, W25Q16, W25Q32, W25Q64, W25Q128, W25Q256, W25Q512, W25Q01, W25Q02) full-featured driver library for general-purpose MCU and Linux.
At a glance
- What is it?
- LibDriver W25QXX is a full-featured C driver for the Winbond W25Q series serial flash, covering the W25Q01 through W25Q512 parts, and its distinguishing feature is that the transport is a template you fill in rather than a port you patch. The project ships interface templates for SPI and QSPI, a datasheet, offline documentation, and the MISRA scan results.
- Who is it for?
- Adopt LibDriver W25QXX if you are bringing up a W25Q flash on a microcontroller or an embedded Linux board and want a vendor-neutral driver with a documented API and a MISRA scan on record, since the same source works for both a bare-metal target and Linux. Skip it if you want a driver that already talks to your specific SPI controller out of the box, because the transport is a template you have to implement yourself.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 96 days ago.
- What is it written in?
- Mainly C, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 20, 2026, and from our analysis. They are not legal advice.
Editorial analysis
One driver for twelve capacities of the same flash family
The W25Q series is a family of serial NOR flash from Winbond, and the driver covers a wide slice of it: the W25Q01, W25Q02, W25Q10, W25Q16, W25Q20, W25Q32, W25Q40, W25Q64, W25Q80, W25Q128, W25Q256 and W25Q512.
The reason that family is worth a single library is in the device description. The W25Q series provides a storage solution for systems with limited space, pins and power, offers flexibility and performance beyond ordinary serial flash devices, and is ideal for code shadowing to RAM, executing code directly from dual or quad SPI, and storing voice, text and data. It operates on a single 2.7V to 3.6V supply with current consumption as low as 4mA active and 1 microamp in power-down, and all devices come in space-saving packages.
Code shadowing to RAM and XIP execution are the two use cases that make a common driver worth having. If your firmware is too large for internal flash, you load it from an external SPI part at boot, and the driver is the part that has to be correct.
The driver is C, MIT licensed, from the LibDriver project, and the README carries a MISRA compliant badge alongside the API reference link. The homepage is libdriver.com, the last push was on 2026-07-19 with releases v1.0.17 from 2026-07-19, v1.0.16 from 2026-07-04 and v1.0.15 from 2026-04-27, so the release cadence is a few weeks apart.
The transport is a template, and that is the design decision
The install instructions are short, and the key sentence is the first one. Reference the SPI or QSPI platform independent template and finish your platform SPI or QSPI driver.
So the transport layer is not provided. You take the template in the interface directory and implement it against your own controller, whether that is an MCU SPI peripheral, a Linux spidev device or something else. Then you add the src directory, the interface driver for your platform, and your own drivers to your project, and if you want the default example drivers, you add the example directory as well.
That is a different philosophy from a vendor driver that assumes a particular HAL. The same driver source then works for a bare-metal microcontroller and for embedded Linux, because only the bottom layer changes. For a project that has to run on both, that is a real saving.
The cost is that you have to write the platform layer before anything runs, and you have to get it right, because a subtle timing bug in the SPI callback will look like a flash driver bug.
The rest of the directory layout supports the same lifecycle. The src directory holds the driver source files, the interface directory holds the SPI or QSPI platform independent template, the test directory holds driver test code that can test the chip's necessary functions simply, the example directory holds the sample code, the doc directory holds the offline document, the datasheet directory holds the W25QXX datasheet, the project directory holds the common Linux and MCU development board sample code where all projects use the shell script to debug the driver with the detailed instruction in each project's README, and the misra directory holds the LibDriver MISRA code scanning results.
The basic example is the shape of every call
The basic example is short and shows the calling convention, which is the same in every LibDriver driver.
#include "driver_w25qxx_basic.h"
uint8_t res;
uint8_t manufacturer;
uint8_t device_id;
uint8_t data[8];
res = w25qxx_basic_init(W25Q128, W25QXX_INTERFACE_SPI, W25QXX_BOOL_TRUE);
if (res != 0)
{
return 1;
}The init call takes three things: the device type, which here is W25Q128, the interface, which here is W25QXX_INTERFACE_SPI, and a boolean, W25QXX_BOOL_TRUE, which in this API is the enable or mode flag.
Every function returns a status code, and the pattern in the example is consistent about it. A non-zero result means failure, and the example deinitialises the driver before returning:
res = w25qxx_basic_get_id((uint8_t *)&manufacturer, (uint8_t *)&device_id);
if (res != 0)
{
(void)w25qxx_basic_deinit();
return 1;
}
w25qxx_interface_debug_print("w25qxx: manufacturer is 0x%02X device id is 0x%02X.\n", manufacturer, device_id);The get_id call reads the JEDEC identification bytes, and the debug print goes through the interface layer, so the log output follows the same transport you implemented rather than depending on a printf.
The write and read pair takes an address and a buffer:
res = w25qxx_basic_write(0x00000000, (uint8_t *)data, 8);and the read is the same shape with read in place of write. The address is a 32-bit value, which is what you would expect for a part that goes up to 512 megabits, and the deinit call at the end releases the device.
The advance example is the same driver with the full command set
The advanced example is worth reading next to the basic one, because the difference between them is the difference between a smoke test and a usable driver.
#include "driver_w25qxx_advance.h"
uint8_t res;
uint8_t manufacturer;
uint8_t device_id;
uint8_t data[8];
res = w25qxx_advance_init(W25Q128, W25QXX_INTERFACE_SPI, W25QXX_BOOL_TRUE);The structure is identical: same three-argument init, same status-code convention, same deinit on the error path, same get_id, same write and read at address zero with an 8 byte buffer.
So the two examples are two entry points into the same driver, with basic and advance as the API level rather than two different implementations. The basic one is the subset you need to identify the chip and move bytes around it, which is enough to prove the wiring works. The advance one is the full command set, and it is the one you would base a real firmware loader on.
The interface choice is the other axis. W25QXX_INTERFACE_SPI selects single-lane SPI and the QSPI variant selects the quad interface, and since the W25Q family is specified for dual and quad SPI with XIP execution in mind, the quad path is the one that matters for the code shadowing use case. The init flag and the interface are independent parameters, so the same driver covers both the wiring and the width of the bus.
One thing the examples do not show is the flash geometry, and that is worth noticing. There is no call setting page size or sector size, which suggests those are configured at init time from the device type you pass in, since W25Q01 and W25Q512 have very different geometries and the driver is told which part it is talking to.
MISRA results and a datasheet in the tree are unusual and useful
Two things in this repository are not typical of a driver library, and both point at the kind of customer LibDriver is writing for.
The first is the misra directory, which holds the LibDriver MISRA code scanning results, and the badge in the README that declares the project MISRA compliant. For automotive and safety-critical work, a MISRA scan on record is not a nice-to-have, it is often a procurement requirement, and publishing the results means a reviewer can check rather than take the claim on trust.
The second is the datasheet directory, which holds the W25QXX datasheet. Vendors are usually reluctant to redistribute a third party's datasheet, and shipping it alongside the driver means the person integrating the part has everything in one place, including the register map and timing figures the driver was written against.
Combined with the offline documentation in the doc directory, with the online version at libdriver.com/docs/w25qxx/index.html and the offline one at doc/html/index.html, the repository is designed to be usable without a network connection, which matters for an embedded development environment that may not have one.
The test directory fits the same pattern. It holds driver test code that can test the chip's necessary functions simply, which is a low bar to clear and catches the mistakes that matter most in a flash driver, such as wrong identification bytes and a write that does not read back.
The project directory goes further, with common Linux and MCU development board sample code, where all projects use the shell script to debug the driver and the detailed instruction can be found in each project's README.
Editorial conclusion
Adopt LibDriver W25QXX if you are bringing up a W25Q flash on a microcontroller or an embedded Linux board and want a vendor-neutral driver with a documented API and a MISRA scan on record, since the same source works for both a bare-metal target and Linux. Skip it if you want a driver that already talks to your specific SPI controller out of the box, because the transport is a template you have to implement yourself. Verify first which W25Q capacity you have, since the init call takes the device type as an argument, that your platform's SPI or QSPI controller fits the interface template's expected callbacks, and that you read the advance example rather than the basic one if you need the full command set.
Frequently asked questions
Which flash devices does LibDriver W25QXX support?
It covers the Winbond W25Q series: the W25Q01, W25Q02, W25Q10, W25Q16, W25Q20, W25Q32, W25Q40, W25Q64, W25Q80, W25Q128, W25Q256 and W25Q512, over SPI or QSPI.
How do I port the W25QXX driver to my platform?
Take the SPI or QSPI platform independent template from the interface directory and finish your platform driver, then add the src directory, your interface driver and your own drivers to your project, and the example directory if you want the default example drivers. The example directory holds driver_w25qxx_basic and driver_w25qxx_advance sample code.
What is the difference between the basic and advance W25QXX examples?
They are two API levels of the same driver rather than two implementations. Both take the device type, the interface and a boolean at init, both return a status code, and both read the identification bytes and write and read a buffer. The basic one is a subset for checking the wiring, and the advance one exposes the full command set.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/libdriver-w25qxx)Was this useful?