air101#

This chapter describes how to use the spi library for LuatOS

Introduction#

spi library provides communication between LuatOS and peripherals using spi protocol

SPI It is an abbreviation of English (Serial Peripheral interface) and, as the name implies, is a serial peripheral interface. was first defined by Motorola on its MC68HCXX family of processors

SPI The interface is mainly used in EEPROM,FLASH, real-time clock, AD converter, and digital signal processor and digital signal decoder. SPI is a high-speed, full-duplex, synchronous communication bus, and only takes up four lines on the pins of the chip, saving the pins of the chip, saving space and providing convenience for the layout of the PCB.

Referring to air101_chip spec_v1.1.pdf, Air101 has 1 SPI channel 0

Hardware preparation#

W25Q128 Is a 128M-Bit that supports SPI communication protocol FLASH

Wiring Schematic

        SPI_CS/GPIO20    -----  CS
        SPI_MISO/GPIO19  -----  DO
        SPI_MOSI/GPIO21  -----  DI
air101  SPI_CLK/GPIO18   -----  CLK   SPI_FLASH
        3.3V             -----  VCC
        GND              -----  GND

Software part#

Interface documentation can be found at:spi library

SPI There are two ways to operate, one is to directly read and write the SPI channel, and the other is to abstract it into an SPI device and then read and write it.

Read and write directly to the SPI channel#

Initialize CS Pin Manual Control#

When using Air101, we need to manually control CS to pull down to enable the device

Encapsulate spi.transfer into a new function and surround it with CS operations before and after.

The code is as follows

function transfer(CS, spiID, data, sendLen, recvLen)
    CS(0)
    local res = spi.transfer(spiID, data, sendLen, recvLen)
    CS(1)
    return res
end

Initialization SPI#

Initialize SPI Channel 0,24MHz CLK, Half Duplex Mode

The code is as follows

local spiID, CS_GPIO = 0, 20
-- CS The parameter 255 represents manual control of the CS pin, and the last mode parameter 0 represents half-duplex mode
local setupRes = spi.setup(spiID, 255, 0, 0, 8, 24 * 1000 * 1000, spi.MSB, 1, 0)
if setupRes ~= 0 then
    log.error(PROJECT .. ".setup", "ERROR")
    return
end

Manufacturers and devices that read SPI FLASH ID#

Looking up the [W25Q128 Data Sheet](https://www.winbond.com/resource-files/w25q128jv_dtr revc 03272018 plus.pdf), we can see that the instructions for querying manufacturer and device ID are as follows0x90

Instructions are described in the following figure

Read Manufacturer / Device ID (90h)

The code is as follows

log.info(PROJECT .. ".chipID", string.toHex(transfer(CS, spiID, string.char(0x90, 0, 0, 0), 4, 2)))

The log is as follows

I/user.spi.chipID EF17 4

In the return value 0xEF17, EF is the manufacturer Winbond Serial Flash, and 17 is the modelW25Q128JV

Erase specified address sector#

Looking up [W25Q128 Data Sheet](https://www.winbond.com/resource-files/w25q128jv_dtr REVVC 03272018 PLUS. pdf), we can see that the instruction to erase the specified address sector is0x20

A write enable instruction needs to be executed before an erase instruction can be executed0x06

Erase 4K-bytes size sector starting at address 0x01

The code is as follows

transfer(CS, spiID, string.char(0x06), 1, 0)
sys.wait(100)
transfer(CS, spiID, string.char(0x20, 0x00, 0x00, 0x01), 4, 0)
-- Erasing takes some time
sys.wait(1000)

Read and write SPI FLASH#

Looking up the [W25Q128 Data Sheet](https://www.winbond.com/resource-files/w25q128jv_dtr revc 03272018 plus.pdf), we can see that the instruction written on the page is0x 02, the maximum amount of data written at a time is 256 bytes, and the instruction for reading data is0x03

A write enable instruction needs to be executed before an erase instruction can be executed0x06

The code is as follows

transfer(CS, spiID, string.char(0x06), 1, 0)
sys.wait(100)
transfer(CS, spiID, string.char(0x02, 0x00, 0x00, 0x01) .. PROJECT, 4 + string.len(PROJECT), 0)
sys.wait(100)
local readRes = transfer(CS, spiID, string.char(0x03, 0x00, 0x00, 0x01), 4, string.len(PROJECT))
log.info(PROJECT .. ".readRes", readRes)

The log is as follows

I/user.spi.readRes spi

Looking at the log, the data read out is consistent with the data we wrote.

Disable write enable and turn off the corresponding SPI channel and CS GPIO#

Looking up the [W25Q128 Data Sheet](https://www.winbond.com/resource-files/w25q128jv_dtr revc 03272018 plus.pdf), it can be seen that the instruction to disable write enable is0x04

The code is as follows

transfer(CS, spiID, string.char(0x04), 1, 0)
gpio.close(CS_GPIO)
spi.close(spiID)

Abstraction for a SPI device and then read and write#

Abstract SPi Peripherals#

Abstract SPI channel 0 SPI FLASH,24MHz CLK, half duplex mode

The code is as follows

local spiID, CS_GPIO = 0, 20
-- The last mode parameter, 0, represents half-duplex mode.
local spiFlash = spi.deviceSetup(spiID, CS_GPIO, 0, 0, 8, 24 * 1000 * 1000, spi.MSB, 1, 0)

Manufacturers and devices that read SPI FLASH ID#

Looking up the [W25Q128 Data Sheet](https://www.winbond.com/resource-files/w25q128jv_dtr revc 03272018 plus.pdf), we can see that the instructions for querying manufacturer and device ID are as follows0x90

Instructions are described in the following figure

Read Manufacturer / Device ID (90h)

The code is as follows

log.info(PROJECT .. ".chipID", string.toHex(spiFlash:transfer(string.char(0x90, 0, 0, 0), 4, 2)))

The log is as follows

I/user.spi.chipID EF17 4

In the return value 0xEF17, EF is the manufacturer Winbond Serial Flash, and 17 is the modelW25Q128JV

Erase specified address sector#

Looking up [W25Q128 Data Sheet](https://www.winbond.com/resource-files/w25q128jv_dtr REVVC 03272018 PLUS. pdf), we can see that the instruction to erase the specified address sector is0x20

A write enable instruction needs to be executed before an erase instruction can be executed0x06

Erase 4K-bytes size sector starting at address 0x01

The code is as follows

spiFlash:send(string.char(0x06))
sys.wait(100)
spiFlash:send(string.char(0x20, 0x00, 0x00, 0x01))
-- Erasing takes some time
sys.wait(1000)

Read and write SPI FLASH#

Looking up the [W25Q128 Data Sheet](https://www.winbond.com/resource-files/w25q128jv_dtr revc 03272018 plus.pdf), we can see that the instruction written on the page is0x 02, the maximum amount of data written at a time is 256 bytes, and the instruction for reading data is0x03

A write enable instruction needs to be executed before an erase instruction can be executed0x06

The code is as follows

spiFlash:send(string.char(0x06))
sys.wait(100)
spiFlash:send(string.char(0x02, 0x00, 0x00, 0x01) .. PROJECT)
sys.wait(100)
local readRes = spiFlash:transfer(string.char(0x03, 0x00, 0x00, 0x01), 4, string.len(PROJECT))
log.info(PROJECT .. ".readRes", readRes)

The log is as follows

I/user.spi.readRes spi

Looking at the log, the data read out is consistent with the data we wrote.

Disable write enable and shut down the corresponding SPI channel#

Looking up the [W25Q128 Data Sheet](https://www.winbond.com/resource-files/w25q128jv_dtr revc 03272018 plus.pdf), it can be seen that the instruction to disable write enable is0x04

The code is as follows

spiFlash:send(string.char(0x04))
log.info(PROJECT .. ".device_close", spiFlash:close())

The log is as follows

I/user.spi.device_close true

Full Code#

PROJECT = "spi"
VERSION = "1.0.0"
sys = require("sys")

function transfer(CS, spiID, data, sendLen, recvLen)
    CS(0)
    local res = spi.transfer(spiID, data, sendLen, recvLen)
    CS(1)
    return res
end

local function test()
    local spiID, CS_GPIO = 0, 20
    local setupRes = spi.setup(spiID, 255, 0, 0, 8, 10 * 1000 * 1000, spi.MSB, 1, 0)
    if setupRes ~= 0 then
        log.error(PROJECT .. ".setup", "ERROR")
        return
    end
    local CS = gpio.setup(CS_GPIO, 0)
    log.info(PROJECT .. ".chipID", string.toHex(transfer(CS, spiID, string.char(0x90, 0, 0, 0), 4, 2)))
    transfer(CS, spiID, string.char(0x06), 1, 0)
    sys.wait(100)
    transfer(CS, spiID, string.char(0x20, 0x00, 0x00, 0x01), 4, 0)
    sys.wait(1000)
    transfer(CS, spiID, string.char(0x06), 1, 0)
    sys.wait(100)
    transfer(CS, spiID, string.char(0x02, 0x00, 0x00, 0x01) .. PROJECT, 4 + string.len(PROJECT), 0)
    sys.wait(100)
    local readRes = transfer(CS, spiID, string.char(0x03, 0x00, 0x00, 0x01), 4, string.len(PROJECT))
    log.info(PROJECT .. ".readRes", readRes)
    transfer(CS, spiID, string.char(0x04), 1, 0)
    gpio.close(CS_GPIO)
    spi.close(spiID)
    local spiFlash = spi.deviceSetup(spiID, CS_GPIO, 0, 0, 8, 10 * 1000 * 1000, spi.MSB, 1, 0)
    log.info(PROJECT .. ".chipID", string.toHex(spiFlash:transfer(string.char(0x90, 0, 0, 0), 4, 2)))
    spiFlash:send(string.char(0x06))
    sys.wait(100)
    spiFlash:send(string.char(0x20, 0x00, 0x00, 0x01))
    sys.wait(1000)
    spiFlash:send(string.char(0x06))
    sys.wait(100)
    spiFlash:send(string.char(0x02, 0x00, 0x00, 0x01) .. PROJECT)
    sys.wait(100)
    local readRes = spiFlash:transfer(string.char(0x03, 0x00, 0x00, 0x01), 4, string.len(PROJECT))
    log.info(PROJECT .. ".readRes", readRes)
    spiFlash:send(string.char(0x04))
    log.info(PROJECT .. ".device_close", spiFlash:close())
end

sys.taskInit(test)

sys.run()