Air101 Getting Started Manual#

Construction of development environment#

Construction of development environment

Burn and view logs#

Burn Tutorial

GPIO Output (lighting)#

This chapter will use the GPIO interface to control the LED lights of the development board to flash and be a lamp god.

Hardware preparation#

One development board and one usb cable

Software usage#

According to the pinout diagram, it can be found that the three lights brought by the development board are PB8,PB8 and PB10 control respectively, and the high level lights up.

image-20211225182704003

Look for the gpio interface in wiki gpio-GPIO operation-LuatOS document

According to the interface, we need to initialize first and then set the level.

The code is as follows

local LED1 = gpio.setup(pin.PB08, 0) -- PB08 Output mode
sys.taskInit(function()
    while 1 do
        log.info("LED Open")
        LED1(0)
        sys.wait(1000)
        log.info("LED Close")
        LED1(1)
        sys.wait(1000)
    end
end)

Burn to equipment to observe phenomenon

Video_20211225_063646_413

Practice#

Write code to make the three lights on the board turn on and off in turn.

GPIO Input-Key#

I learned output control in the previous section. I’ll learn input while the iron is hot.

Hardware preparation#

One development board and one usb cable

Software usage#

The same routine, first look at the pinout diagram, button 2 is connected to BOOT, that is, PA0, then he.

image-20211225185522888

According to the interface of gpio-GPIO operation, simply write a few lines of code

gpio.setup(pin.PA00, function(val)
    log.info("PA0", val)
end, gpio.PULLUP)--The key is pressed to ground, so it needs to be pulled up

Burn, test it. Press the boot key and then release, the log can see the normal trigger interrupt.

image-20211225185409625

Practice#

Write code, button control PB8 lamp status, each press the button to change the status of the lamp.

PWM#

This chapter will use PWM to drive onboard LEDs to achieve a breathing light effect

Introduction#

PWM(Pulse Width Modulation , Pulse width modulation) is a method of digitally encoding the level of an analog signal. The duty cycle of a square wave is used to encode the level of a specific analog signal through pulses of different frequencies, so that a series of pulses of equal amplitude are obtained at the output, and these pulses are used to replace the required waveform.

Hardware preparation#

One development board and one usb cable

Software usage#

As usual, look at pinout first. We can see that Air101 has 5 pwm pins. Let’s choose PWM0 to directly connect a small lamp to GND nearby to make a breathing light.

image-20211225185945270

Check API and find that the use of PWM is very simple. Just open it directly, then adjust the duty cycle to adjust the brightness and code it.

sys.taskInit(function()
    while 1 do
        -- Imitation breathing light effect
        log.info("pwm", ">>>>>")
        for i = 100,1,-1 do
            pwm.open(0, 1000, 100-i) -- Frequency 1000hz, duty cycle 0-100
            sys.wait(20)
        end
        sys.wait(1000)
        for i = 100,1,-1 do
            pwm.open(0, 1000, i)
            sys.wait(20)
        end
        gpio.setup(pin.PB00,0)
        sys.wait(1000)
    end
end)

Burning to see the effect, the light gradually turned on and off, as if breathing.

Video_20211225_071354_243

Practice#

Write code to achieve three kinds of breathing effect, press the button to switch the breathing effect.

UART#

This chapter will learn how to configure the serial port and send the received data back through the serial port.

Introduction#

UART(Universal Asynchronous Receiver/Transmitter)Universal asynchronous transceiver, UART as a kind of asynchronous serial communication protocol, the working principle is to transmit each character of the data one by one. Is the most frequently used data bus in the application development process.

UART The characteristic of the serial port is to transmit data bit by bit in sequence. Two-way communication can be realized as long as two transmission lines. One line sends data while receiving data with another line. There are several important parameters for UART serial communication, namely baud rate, start bit, data bit, stop bit and parity bit. For two ports using UART serial communication, these parameters must match, otherwise the communication will not be completed normally.

Hardware preparation#

One development board, one usb to serial port line

Software usage#

Looking at the document LuatOS document, we can see that Air101 has 4 uarts, of which uart0 is used for download and debugging, so let’s choose uart1. That is, PB6->TX then PB7->RX connect TX to serial line RX,RX to serial line TX, and then don’t forget to share the ground.

After connecting the line, we began to write the code. According to the API interface description of UART , we first initialized the serial port.

local result = uart.setup(
    UART_ID,--Serial port id
    115200,--Baud rate
    8,--data bit
    1--Stop bit
)

In order to do serial port data loopback, we must first learn how to receive serial port data. uart.on(id, event, func) in the interface registers serial port event callback, which is to interrupt callback reception. after receiving it, we will send it out through uart.write interface. theoretical analysis is completed and the code is uploaded.

-- Serial ID, Serial Read Buffer
local UART_ID, sendQueue = 1, {}
-- Serial port timeout, messages posted when the serial port is ready
--The example is 100ms, changed according to demand
local uartimeout, recvReady = 100, "UART_RECV_ID"
--Initialization
local result = uart.setup(
    UART_ID,--Serial port id
    115200,--Baud rate
    8,--data bit
    1--Stop bit
)
uart.on(UART_ID, "receive", function(uid, length)
    local s
    while true do--Ensure that you cannot lose packets after reading.
        s = uart.read(uid, length)
        if #s == 0 then break end
        table.insert(sendQueue, s)
    end
    sys.timerStart(sys.publish, uartimeout, recvReady)
end)
-- Send the received string to the serial port
sys.subscribe(recvReady, function()
    --Splice all received data
    local str = table.concat(sendQueue)
    -- Empty the buffer after reading the serial data
    sendQueue = {}
    --Note that printing will affect the running speed, and comment out after debugging.
    --log.info("uartTask.read length", #str, str:sub(1,100))
    uart.write(UART_ID,str) --Reply
    --Processing the received data here, this is the example
end)

For indefinite data reception, a timeout frame breaking mechanism is generally adopted, that is, a timer is started before a byte is received, and if the next byte is not received within a certain period of time, it is considered to be a packet of data.

Download the viewing effect and loop back the output after receiving the indefinite length data.

image-20211104140609894

Practice#

Write code, use serial port 1 to send and receive instructions, receive A,on turn on LED1, receive A,off turn off LED1

ADC#

This chapter will introduce you to the ADC features of LuatOS. The internal temperature will be read using the Air101 development board and printed in the log.

Introduction#

An analog-to-digital converter, or ADC for short, usually refers to an electronic component that converts an analog signal into a digital signal. A typical analog-to-digital converter converts an input voltage signal to an output digital signal. Air101 chip has two 16-bit ADC, the highest sampling rate 1KHz。

Hardware preparation#

One development board and one adjustable power supply

Software usage#

This time we first look for the document LuatOS document to see that in addition to reading the external IO voltage, we can also read the internal temperature and power supply voltage. This time we choose ADC0, which is the PA1 pin.

We connect the PA1 pin with an adjustable power supply (be sure to pay attention to the voltage not exceeding 2.4V)

According to the interface of the ADC library library, simply write a few lines of code

sys.taskInit(function()
    while 1 do
        adc.open(0) -- ADC0 pin on the module-PA1, 0~2.4v, do not exceed the range of use!!!
        adc.open(10) -- CPU Temperature
        adc.open(11) -- VBAT Voltage, only the latest code is supported
        sys.wait(500)
        log.debug("adc", "adc0", adc.read(0))
        log.debug("adc", "adc_temp", adc.read(10))
        log.debug("adc", "vbat", adc.read(11))
        sys.wait(500)
    end
end)

Burn, observe the log

image-20211225192448114

The measured ADC has a certain error, and it is recommended to use it for threshold detection.

Practice#

Write code to light up PB10 when the internal temperature of the chip is greater than 40 degrees.LED

I2C#

This chapter will introduce you to the I2C features of LuatOS. Will be realized using the Air101 development board I2C to read the temperature and humidity of the SHT30 sensor

Introduction#

I2C(Inter Integrated Circuit)The bus is a half-duplex, bidirectional two-wire synchronous serial bus developed by PHILIPS. The I2C bus only needs two signal lines to transmit data, one is a bidirectional data line SDA(serial data), and the other is a bidirectional clock line SCL(serial clock)

Hardware preparation#

One development board and one SHT30 sensor

Software usage#

First of all, check the document LuatOS document and see the document that PA1 is SCL pin and PA4 is SDA pin. We attach the sensor to the board according to this definition.

Start writing code, first find the interface description i2c-I2C Operation-LuatOS Document, initialize first, need the address, refer to SHT30 manual, the address is determined by the pin level of ADDR, my module is suspended, that is 0x44

image-20211103192701546

Knowing the address, let’s look at how to measure the temperature. The manual says that continuous measurement requires the command 0x2C06. Refer to the interface of LuatOS and direct i2c.send()

image-20211103193151985

Then receive the data and calculate the actual temperature and humidity according to the formula in the manual.

image-20211103193445226

The code is as follows

sys.taskInit(function()
    -- Initialize i2c with id 0
    if i2c.setup(0, i2c.FAST, 0x44) == 1 then
        log.info("Existence i2c0")
    else
        i2c.close(0) -- Turn off
    end
    while 1 do
        local w = i2c.send(0, 0x44, string.char(0x2c, 0x06)) -- Send single acquisition command
        sys.wait(10) -- Waiting for acquisition
        local r = i2c.recv(0, 0x44, 6) -- Read data acquisition results
        log.info("recv", r:toHex())
        local a, b, c, d, e, f, g = string.unpack("BBBBBB", r)
        log.info("a", a, b, c, d, e, f, g)
        local t = ((4375 * (a * 256 + b)) / 16384) - 4500 -- Calculate the temperature and humidity according to the formula given in the SHT30 sensor manual
        local h = ((2500 * (d * 256 + e)) / 16384)
        log.warn("Here is the temperature ", t / 100) -print temperature
        log.warn("Here is humidity ", h / 100) -print humidity
        sys.wait(1000)
    end
end)

Check the results, the room is quite warm with the air conditioner on.

image-20211225193114838

Practice#

Write code to try to drive other I2C devices.

SPI#

This chapter will use the hardware SPI of Air101 to read the flah’s ID and print it.

Introduction#

SPI Serial Peripheral Interface (Serial Peripheral Interface) is an abbreviation, is a high-speed, full-duplex, synchronous communication bus, the device is divided into master and slave, the current Air101 SPI can only be used as a host.

Hardware preparation#

A development board, SPI flash one.

Software usage#

First look for the pin diagram to study how to connect, check the document LuatOS document to see the connection mode, that is, PB2 is connected to CLK,PB5 is connected to MOSI,PB3 is connected to MISO,PB4 is connected to CS. Next, study how to drive.

The flash I use is W25Q128. According to the manual, there is a general command 0x9F for reading flash models. If normal, the W25Q128 should return 0xEF4018

image-20211104124049436

We refer to API Manual to initialize SPI first and create an SPI object spi_device = spi.deviceSetup(0,20,0,0,8,2000000,spi.MSB,1,1)

Send the 0x9F instruction to read the returned 3 bytes recv = spi_device:transfer(string.char(0x9f),nil,3)

The overall code is as follows

sys.taskInit(
    function()
        local spi_device = spi.deviceSetup(0,pin.PB04,0,0,8,2000000,spi.MSB,1,1)
        while 1 do
            local recv = spi_device:transfer(string.char(0x9f),nil,3)
            log.info("spi data",recv:toHex())
            sys.wait(1000)
        end
    end
)

Downloaded to the device, you can see that the value printed in the log is EF4018 in accordance with the manual instructions, SPI communication is normal

image-20211225193619442

Practice#

Write code to try to drive other SPI devices.

SFUD-External flash#

This chapter will use Air101 hardware SPI to mount flash to the file system, read and write directly

Introduction#

In the last chapter, we learned to use SPI to read the ID of external flash. In fact, the read and write instructions of external spi flash are compatible. In the process of daily use, it will be very troublesome if we directly use spi to read and write flash through instructions. As a result, LuatOS has designed a set of interfaces to realize abstract read and write of external spi flash through this set of interfaces and realize simple read and write of Lua io.

Hardware preparation#

A development board, SPI flash a

Software usage#

Wiring diagram see SPI that chapter can not repeat

Refer to sfud Document to write a simple demo. The general idea is to create a spi hardware device object for sfud first, and then operate spi through sfud’s abstract interface to realize the operation of flash.

local spi_flash = spi.deviceSetup(0,20,0,0,8,2000000,spi.MSB,1,1)--PB6
log.info("sfud.init",sfud.init(spi_flash))
log.info("sfud.getDeviceNum",sfud.getDeviceNum())
local sfud_device = sfud.getDeviceTable()
log.info("sfud.write",sfud.write(sfud_device,1024,"sfud"))
log.info("sfud.read",sfud.read(sfud_device,1024,4))

First test whether the equipment can be connected normally and burn. Looking at the log, we can see that sfud’s library automatically helped us obtain flash’s manufacturer, size and other information during initialization. We also read and write normally through sfud.write() and read() interfaces.

image-20211225200005395

But is that enough for us? No! We want to turn flash into a part of the file system and read and write directly through the native lua interface. According to the API’s instructions, we can find the function sfud.mount(), through which we can directly mount to the file system. mount is very similar to linux’s operation, is there any.

Next, practice and add the following code to the previous code.:

log.info("sfud.mount",sfud.mount(sfud_device,"/sfud"))
log.info("fsstat", fs.fsstat("/sfud"))
local f = io.open ("/sfud/a.txt", "wb")
f:write(string.rep("1234567890", 100))
f:close()
log.info("fsize", fs.fsize("/sfud/a.txt"))
f = io.open ("/sfud/a.txt", "rb")
local data = f:read("*a")
log.info("fs", "data", data)

After we mount it to the file system, create a new file and write 1000 bytes of data, then read the file size, and then read the contents of the file.

Burn to see the effect

image-20211225200430722

Fully in line with expectations, can directly operate flash through the file system, is not very convenient.

Practice#

Write code to test flash read and write speed

SDIO#

This chapter will learn to use the SDIO interface to drive the TF card and mount the TF card to the file system for reading and writing.

Introduction#

In the last chapter, we learned to mount flash directly with sfud, but spi is a bit slow on the two data lines and flash capacity is limited. At this time, I thought of a higher-speed SDIO interface. The SDIO interface was designed to read and write SD cards at the beginning. Later, it was also used in many high-speed communications.

Hardware preparation#

Development board a piece, support SDIO tf card or SD card module. Be sure to be similar to the figure below that supports SDIO 4 data lines.

image-20211104171407771

Do not support this only SPI

image-20211104171659482

Software usage#

First look at the document LuatOS document and study how to connect the wires.

Pin

Function

PB_06

GPIO_22 / UART1_TX / SDIO_CLK

PB_07

GPIO_23 / UART1_RX / SDIO_CMD

PB_08

GPIO_24 / SDIO_D0

PB_09

GPIO_25 / SDIO_D1

PB_10

GPIO_26 / SDIO_D2

PB_11

GPIO_27 / SDIO_D3

According to this way to connect the module, don’t forget to put the card.

Next, find API document, and sfud a routine, initialize the hardware, read and write tests (it is better not to do this, direct reading and writing sd card will lead to the loss of partition table, plug in the computer may not recognize), I will be unexpected

For the part that is directly mounted, except for the first two sentences, the rest is all native interfaces. Like sfud, it is directly used after mounting. The complete code is in demo’s sdio folder.

sys.taskInit(
	function()
		sys.wait(2000)
		--Delay to prevent the log from being invisible.
		sdio.init(0)
		sdio.sd_mount(0, "/sd")
		local f = io.open("/sd/a.txt", "wb")
		f:write(string.rep("1234567890", 100))
		f:close()
		log.info("fsize", fs.fsize("/sd/a.txt"))
		f = io.open("/sd/a.txt", "rb")
		local data = f:read("*a")
		log.info("fs", "data", data)
	end
)

Burn and see the results.

image-20211225201633832

SD The card is so simple to use for the first time, and two lines of code are done.

Practice#

Write code to record the data received by the serial port to the SD card

LCD#

In this chapter, we will learn how to drive the LCD screen and display Chinese characters and graphics on the screen.

Introduction#

Usually driving the screen is a very complicated thing, because the LCD screen has a lot of instructions, display data and make a font. LuatOS encapsulates the LCD and has a variety of commonly used screen drivers built in. If it is not in the list, it can also be driven by Lua script configuration instructions. Using the idea of virtual video memory, the hardware driver and image drawing are extracted, which is more simple and convenient to use.

Hardware preparation#

A development board, SPI screen, I use gc9306(240*320)

Software usage#

As usual, first look at the document LuatOS Document to study how to connect the wires. Only the first two pins must use the solution of this table. The latter can be connected casually. I recommend press my connection.

Function

Pin

SCL

PB2

SDA

PB5

RES

PB3

DC

PB1

CS

PB4

BL

PB0

Next, the old rule is to look at [API](https://wiki.luatos.org/api/ LCD .html) with the same thinking. First, initialize the hardware and do other things. According to the description of the interface, we need to start with a spi object, and then configure pins such as RES and DC according to the hardware. I wrote the pin list before, so the initialization code is as follows

local spi_lcd = spi.deviceSetup(0, pin.PB04, 0, 0, 8, 20 * 1000 * 1000, spi.MSB, 1, 1)
log.info(
    "lcd.init",
    lcd.init(
        "gc9306",
        {
            port = "device",
            pin_dc = pin.PB01,
            pin_pwr = pin.PB00,
            pin_rst = pin.PB03,
            direction = 0,
            w = 240,
            h = 320,
            xoffset = 0,
            yoffset = 0
        },
        spi_lcd
    )
)

Next, I will study how to display something. I will simply draw a frame and a circle to display Chinese characters. The API is clearly written, so there is not much explanation here. The font in the place where the font is set can be selected during local compilation or cloud compilation as mentioned earlier. The settings here are also different if the selection is different. Attention should be paid.

log.info("lcd.drawLine", lcd.drawLine(0,20,128,20,0x001F))
log.info("lcd.drawRectangle", lcd.drawRectangle(20,40,120,70,0xF800))
log.info("lcd.drawCircle", lcd.drawCircle(64,100,20,0x0CE0))
lcd.setFont(lcd.font_opposansm16_chinese)
lcd.drawStr(40,66,"Test")

Burn to view the effect

image-20211225202905799

Try changing the screen, just change the screen model gc9306 of the initialization parameters to ili9341, and the ili9341 will be driven.

Look at the effect.

image-20211225203127599

LuatOS The abstraction of display devices is worth learning.

Practice#

Write code to display a table of the order of wiring in this chapter on the screen.

LVGL#

This chapter will briefly introduce how to use LVGL to display a more beautiful picture

Introduction#

In the last chapter, I learned the LCD library and can smoothly draw dots and lines to display Chinese characters. However, if I want to see the interface well, these simple interfaces still cannot meet the requirements. As a result, there are many graphics libraries, LuatOS integrated LVGL graphics library, you can use a large number of controls to write a beautiful interface.

Hardware preparation#

A development board, SPI screen, I use gc9306(240*320)

Software usage#

Note that firmware with LVGL suffix should be used, or cloud compilation by itself.

The chapter on LCD is not repeated. LVGL as a graphics library is the initialization screen of LCD first, and then LVGL is used to render the screen content. Directly on the code

log.info("lvgl", lvgl.init())
lvgl.disp_set_bg_color(nil, 0xFFFFFF)
local scr = lvgl.obj_create(nil, nil)
local btn = lvgl.btn_create(scr)
lvgl.obj_align(btn, lvgl.scr_act(), lvgl.ALIGN_CENTER, 0, 0)
local label = lvgl.label_create(btn)
lvgl.label_set_text(label, "LuatOS!")
lvgl.scr_load(scr)

Look at the effect, this button is much better than the one made by drawing lines.

image-20211225203837781

For more LVGL tutorials, please refer to API and lvgl official documents.

Practice#

The practical requirements of the chapter on writing code using LVGL to implement LCD