上半年利用业余时间使用国产芯片-合宙Air101 做了个小玩具,有点小成果,奈何现在忙带娃,基本没有时间继续了,已经吃灰了一段时间,做个总结,送有缘人。

         XT-E804是阿里平头哥的MCU,属于CSKY架构(与RISC-V接近),合宙Air101芯片是XT-E804的QFN32封装,4mm*4mm封装大小。具有32位高性能内核,主频最高可达240MHz。内置UART、SPI、I2C、GPIO、ADC、PWM等外设接口,内置2MB Flash,作为一般的IOT设备开发足够。

        部分原理图如下:

合宙LuatOS系统 合宙air101_合宙LuatOS系统

 使用的嘉立创EDA 专业版,布线图如下:

合宙LuatOS系统 合宙air101_单片机_02

 背面接口见下图,采用常用的PH2.0卧式,3pin和4pin;供电使用常用的6pin TYPE-C;按键采用3*4*2.5轻触按键。屏幕采用1.14寸TFT显示屏,135*240分辨率,ST7789V驱动。同接口规格的还可以使用0.96寸显示屏,80*160分分辨率,ST7735驱动。板子整体尺寸为40mm*20mm。

合宙LuatOS系统 合宙air101_合宙LuatOS系统_03

        CN1做输入口可接单总线传感器(ds18b20)或NTC热敏电阻;CN2输出口可接5v继电器或MOS管模块,输出PWM信号和开关信号;CN3为usart接口,用于下载程序和通信;CN4为模拟I2C接口(硬件I2C被ADC占用),接传感器。 

        开始显示屏使用了8PIN插接款,后来发现接头有一定高度,后来改为焊线款,屏幕可以贴合到mcu上,更紧凑。

        3D效果图(开始显示屏使用了8PIN插接款,后来发现接头有一定高度,后来改为焊线款,屏幕可以贴合到mcu上,更紧凑):

合宙LuatOS系统 合宙air101_封装_04

合宙LuatOS系统 合宙air101_合宙LuatOS系统_05

换个角度看 : 

合宙LuatOS系统 合宙air101_封装_06

 软件部分,开发采用vscode,安装LuatIDE插件,采用合宙推荐的LuatOS开发,图形界面采用LVGL库。

-- LuaTools需要PROJECT和VERSION这两个信息
PROJECT = "lvgldemo"
VERSION = "1.0.0"

log.info("main", PROJECT, VERSION)

-- sys库是标配
_G.sys = require("sys")

--添加硬狗防止程序卡死
if wdt then
    wdt.init(15000)--初始化watchdog设置为15s
    sys.timerLoopStart(wdt.feed, 10000)--10s喂一次狗
end

-- UI带屏的项目一般不需要低功耗了吧, 设置到最高性能
if mcu then
    mcu.setClk(240)
end

log.info("main", "ask for help", "https://wiki.luatos.com/")

--air101 v2 13pin
spi_lcd = spi.deviceSetup(0,pin.PB08,0,0,8,10*1000*1000,spi.MSB,1,1)
lcd.init("st7789",{port = "device",pin_dc = pin.PB10, pin_rst = pin.PB09,direction = 2,w = 240,h = 135,xoffset = 40,yoffset = 53},spi_lcd)


SignalOn = false
function getSignalState(t)
    if heatingMode == 1 then
        if SignalOn == false and t < trigerValue then 
            SignalOn = true
            gpio.set(pin.PB01,1)
        end
        if SignalOn == true and t > trigerValue+returnDifference then
            SignalOn = false
            gpio.set(pin.PB01,0)
        end
    else
        if SignalOn == false and t > trigerValue then 
            SignalOn = true
            gpio.set(pin.PB01,1)
        end
        if SignalOn == true and t < trigerValue-returnDifference then
            SignalOn = false
            gpio.set(pin.PB01,0)
        end
    end
    return SignalOn
end

function f0(v)
    if sensorID == 0 then--vcc voltage in mV
        lvgl.gauge_set_value(gauge,0,v//100)
    elseif sensorID == 2 then-- external voltage
        lvgl.gauge_set_value(gauge,0,v//100)
    elseif sensorID == 3 or sensorID == 4 then--external tempreture
        lvgl.gauge_set_value(gauge,0,math.floor(v))
        if getSignalState(v) == true then
            lvgl.switch_on(sw1, lvgl.ANIM_ON)
        else
            lvgl.switch_off(sw1, lvgl.ANIM_ON)
        end
    end
end
function drawGauge()--0
    local val = 30
    sensorOn = true
    sw1 = lvgl.switch_create(lvgl.scr_act())
    lvgl.obj_align(sw1, nil, lvgl.ALIGN_CENTER, 96, -51)
    --lvgl.obj_set_size(sw1, 20, 20)
    lvgl.switch_off(sw1, lvgl.ANIM_ON)
    gauge = lvgl.gauge_create(lvgl.scr_act())
    lvgl.obj_align(gauge, nil, lvgl.ALIGN_CENTER, -21, 30)
    lvgl.obj_set_size(gauge, 238, 238)
    lvgl.gauge_set_range(gauge, -20, 100)
    lvgl.gauge_set_scale(gauge,180,31,7)
    --lvgl.gauge_set_needle_count(g1, 1, lvgl.color_hex(0xFF0000))
    lvgl.gauge_set_critical_value(gauge, 80)
    sys.subscribe("value",f0)
    while true do
        local r,k = sys.waitUntil("key")
        if k=="3" then
            sys.unsubscribe("value",f0)
            lvgl.obj_clean(gauge)
            lvgl.obj_del(gauge)
            lvgl.obj_clean(sw1)
            lvgl.obj_del(sw1)
            log.info("key",k)
            return 2
        elseif k == "4" then
            sys.unsubscribe("value",f0)
            lvgl.obj_clean(gauge)
            lvgl.obj_del(gauge)
            lvgl.obj_clean(sw1)
            lvgl.obj_del(sw1)
            log.info("key",k)
            return 3
        elseif k=="5" then
            sys.unsubscribe("value",f0)
            lvgl.obj_clean(gauge)
            lvgl.obj_del(gauge)
            lvgl.obj_clean(sw1)
            lvgl.obj_del(sw1)
            log.info("key",k)
            return 1
        end
    end
end

function f1(v)
    if sensorID == 0 then
        lvgl.chart_set_next(chart, ser1, v//100)
    elseif sensorID == 2 then
        lvgl.chart_set_next(chart, ser1, v//100)
    elseif sensorID == 3 then
        lvgl.chart_set_next(chart, ser1, math.floor(v))
    elseif sensorID == 4 then
        lvgl.chart_set_next(chart, ser1, math.floor(v))
    end
    lvgl.chart_refresh(chart)
end
function drawChart()--1
    chart = lvgl.chart_create(lvgl.scr_act(), nil)
    lvgl.obj_set_size(chart, 240, 135)
    lvgl.obj_align(chart, nil, lvgl.ALIGN_CENTER, 0, 0)
    --lvgl.obj_set_stype_local_value_str(chart,lvgl.CONT_PART_MAIN,lvgl.STATE_DEFAULT,"                                                                     ")
    lvgl.chart_set_type(chart, lvgl.CHART_TYPE_LINE)-- 设置 Chart 的显示模式 (折线图)
    lvgl.chart_set_range(chart,-20,100)
    lvgl.chart_set_div_line_count(chart,5,8)
    lvgl.chart_set_point_count(chart,20)
    --lvgl.chart_set_y_tick_length(chart,0,0)
    --lvgl.chart_set_y_tick_texts(chart, "100\n80\n60\n40\n20\n0\n-20", 3, lvgl.CHART_AXIS_DRAW_LAST_TICK);
    ser1 = lvgl.chart_add_series(chart, lvgl.color_hex(0xFF0000))
    --ser2 = lvgl.chart_add_series(chart, lvgl.color_hex(0x008000))
    sys.subscribe("value",f1)
    while true do
        local r,k = sys.waitUntil("key")
        if k == "3" then
            sys.unsubscribe("value",f1)
            lvgl.obj_clean(chart)
            lvgl.obj_del(chart)
            return 0
        elseif k == "4" then
            log.info("key",k)
            sys.unsubscribe("value",f1)
            lvgl.obj_clean(chart)
            lvgl.obj_del(chart)
            return 3
        elseif k == "5" then
            sys.unsubscribe("value",f1)
            lvgl.obj_clean(chart)
            lvgl.obj_del(chart)
            return 2
        end
    end
end

function f2(v)
    lvgl.obj_clean(text)
    if sensorID == 0 then
        lvgl.label_set_text(text, string.format("%d mV",v))
    elseif sensorID == 2 then
            lvgl.label_set_text(text, string.format("%d mV",v))
    elseif sensorID == 3 then
        lvgl.label_set_text(text, string.format("%.1f °C",v))
    elseif sensorID == 4 then
        lvgl.label_set_text(text, string.format("%.1f °C",v))
    end
end
function drawText()--2
    local label1 = lvgl.label_create(lvgl.scr_act())
    lvgl.obj_align(label1, nil, lvgl.ALIGN_CENTER, -40, -40)
    if sensorID == 0 then
        lvgl.label_set_text(label1, "VCC Voltage:")
    elseif sensorID == 3 then
        lvgl.label_set_text(label1,"DS18B20:")
    elseif sensorID == 4 then
        lvgl.label_set_text(label1,"NTC 10K B2950:")
    else
        lvgl.label_set_text(label1, "label1:")            
    end
    text = lvgl.label_create(lvgl.scr_act())
    lvgl.obj_align(text, nil, lvgl.ALIGN_CENTER, -40, 10)
    sys.subscribe("value",f2)
    while true do
        local r,k = sys.waitUntil("key")
        if k == "3" then
            sys.unsubscribe("value",f2)
            lvgl.obj_clean(label1)
            lvgl.obj_del(label1)
            lvgl.obj_clean(text)
            lvgl.obj_del(text)
            log.info("key",k)
            return 1
        elseif k == "4" then
            sys.unsubscribe("value",f2)
            lvgl.obj_clean(label1)
            lvgl.obj_del(label1)
            lvgl.obj_clean(text)
            lvgl.obj_del(text)
            log.info("key",k)
            return 3
        elseif k == "5" then
            sys.unsubscribe("value",f2)
            lvgl.obj_clean(label1)
            lvgl.obj_del(label1)
            lvgl.obj_clean(text)
            lvgl.obj_del(text)
            log.info("key",k)
            return 0
        end
    end
end

sensorID = 0
function setSensor()--3
    sensorOn = false
    local i = sensorID
    local label1 = lvgl.label_create(lvgl.scr_act())
    lvgl.obj_align(label1, nil, lvgl.ALIGN_CENTER, -90, -40)
    lvgl.label_set_text(label1, "Data Source:")
    local dd = lvgl.dropdown_create(lvgl.scr_act(), nil)
    lvgl.dropdown_set_options(dd, [[VCC Voltage
    Chip Tempreture
    External Voltage
    DS18b20
    NTC 10k B:2950]])
    lvgl.obj_align(dd, nil, lvgl.ALIGN_CENTER, -40, 0)
    lvgl.obj_set_width(dd,180)
    lvgl.dropdown_set_selected(dd, i)
    while true do
        local r,k = sys.waitUntil("key")
        if k == "3" then
            i = (i+4)%5
            lvgl.dropdown_set_selected(dd, i)
        end
        if k == "4" then
            sensorID = lvgl.dropdown_get_selected(dd)
            log.info("sensorID",sensorID)
            lvgl.obj_clean(dd)
            lvgl.obj_del(dd)
            lvgl.obj_clean(label1)
            lvgl.obj_del(label1)
            return 4
        end
        if k == "5" then
            i = (i+1)%5
            lvgl.dropdown_set_selected(dd, i)
        end
    end
end

heatingMode = false --1 for heating, 0 for refrigerating mode
function setHeatingMode()
    local i = 0
    if heatingMode == true then
        i = 1
    end
    local label1 = lvgl.label_create(lvgl.scr_act())
    lvgl.obj_align(label1, nil, lvgl.ALIGN_CENTER, -90, -40)
    lvgl.label_set_text(label1, "Application Mode:")
    local dd = lvgl.dropdown_create(lvgl.scr_act(), nil)
    lvgl.dropdown_set_options(dd, [[Refrigerating Mode
    Heating Mode]])
    lvgl.obj_align(dd, nil, lvgl.ALIGN_CENTER, -40, 0)
    lvgl.obj_set_width(dd,200)
    lvgl.dropdown_set_selected(dd, i)
    while true do
        local r,k = sys.waitUntil("key")
        if k == "4" then
            i = lvgl.dropdown_get_selected(dd)
            if i == 1 then 
                heatingMode = true
            else
                heatingMode = false
            end
            log.info("heatingMode",heatingMode)
            lvgl.obj_clean(dd)
            lvgl.obj_del(dd)
            lvgl.obj_clean(label1)
            lvgl.obj_del(label1)
            return 5
        else
            i = (i+1)%2
            lvgl.dropdown_set_selected(dd, i)
        end
    end
end

signalMode = 1 --5
function setSignalMode()--0 for PWM mode 1 for Relay Mode
    local i=signalMode
    local label1 = lvgl.label_create(lvgl.scr_act())
    lvgl.obj_align(label1, nil, lvgl.ALIGN_CENTER, -90, -40)
    lvgl.label_set_text(label1, "Signal:")
    local dd = lvgl.dropdown_create(lvgl.scr_act(), nil)
    lvgl.dropdown_set_options(dd, [[ON/OFF
    PWM ON/OFF]])
    lvgl.obj_align(dd, nil, lvgl.ALIGN_CENTER, -40, 0)
    lvgl.obj_set_width(dd,180)
    lvgl.dropdown_set_selected(dd, i)
    while true do
        local r,k = sys.waitUntil("key")
        if k == "4" then
            signalMode = lvgl.dropdown_get_selected(dd)
            log.info("signalMode",signalMode)
            lvgl.obj_clean(dd)
            lvgl.obj_del(dd)
            lvgl.obj_clean(label1)
            lvgl.obj_del(label1)
            return 6
        else
            i = (i+1)%2
            lvgl.dropdown_set_selected(dd, i)
        end
    end
end

trigerValue = 30.0
function setTrigerValue()--6
    local label1 = lvgl.label_create(lvgl.scr_act())
    lvgl.obj_align(label1, nil, lvgl.ALIGN_CENTER, -90, -40)
    lvgl.label_set_text(label1, "Trigger Value:")
    local sb1 = lvgl.spinbox_create(lvgl.scr_act(),nil)
    lvgl.obj_set_width(sb1,120)
    lvgl.obj_align(sb1, nil, lvgl.ALIGN_CENTER, 0, 0)
    lvgl.spinbox_set_digit_format(sb1, 4, 3)
    lvgl.spinbox_set_range(sb1, -200, 1000)
    lvgl.spinbox_set_value(sb1,math.floor(trigerValue*10))
    while 1 do
        local r,k = sys.waitUntil("key")
        if k == "3" then
            lvgl.spinbox_decrement(sb1)
        elseif k == "4" then
            trigerValue = lvgl.spinbox_get_value(sb1)/10
            log.info("trigerValue",trigerValue)
            lvgl.obj_clean(sb1)
            lvgl.obj_del(sb1)
            lvgl.obj_clean(label1)
            lvgl.obj_del(label1)
            return 7
        elseif k == "5" then
            lvgl.spinbox_increment(sb1)
        end
    end
end

returnDifference = 5.0
function setReturnDifference()--7
    local label1 = lvgl.label_create(lvgl.scr_act())
    lvgl.obj_align(label1, nil, lvgl.ALIGN_CENTER, -90, -40)
    lvgl.label_set_text(label1, "Return Difference:")
    local sb2 = lvgl.spinbox_create(lvgl.scr_act(),nil)
    lvgl.obj_set_width(sb2,80)
    lvgl.obj_align(sb2, nil, lvgl.ALIGN_CENTER, 0, 0)
    lvgl.spinbox_set_digit_format(sb2, 3, 2)
    lvgl.spinbox_set_range(sb2, 0, 1000)
    lvgl.spinbox_set_value(sb2,math.floor(returnDifference*10))
    while 1 do
        local r,k = sys.waitUntil("key")
        if k == "3" then
            lvgl.spinbox_decrement(sb2)
        elseif k == "4" then
            returnDifference = lvgl.spinbox_get_value(sb2)/10
            log.info("returnDifference",returnDifference)
            lvgl.obj_clean(sb2)
            lvgl.obj_del(sb2)
            lvgl.obj_clean(label1)
            lvgl.obj_del(label1)
            return 8
        elseif k == "5" then
            lvgl.spinbox_increment(sb2)
        end
    end
end

function screen(n)
    
    if n == 0 then
        return drawGauge()--仪表盘
    end
    if n == 1 then
        return drawChart()--折线图
    end
    if n==2 then
        return drawText()--输出模式,只用文字输出
    end
    if n==3 then
        return setSensor()--下拉选项框,传感器选择
    end
    if n==4 then
        return setHeatingMode()--下拉选项框,工作模式 加热 降温
    end
    if n==5 then
        return n+1  --不选PWM 模式
        --return setSignalMode()--下拉选项框,信号模式,0 pwm mode 1 继电器模式
    end
    if n==6 then
        return setTrigerValue()--下拉选项框,触发值
    end
    if n==7 then
        return setReturnDifference()--下拉选项框,回差值
    end
    if n==8 then
        --创建表格
        return n+1
    end
    --触发值
    if n==9 then
        return n+1
    end
    --回差
    if n==10 then
        return n+1
    end
    --回差
    if n==11 then
        return n+1
    end
    --回差
    if n==12 then
        return n+1
    end
    --回差
    if n==13 then
        return n+1
    end
    --回差
    if n==14 then
        return n+1
    end
    --回差
    if n==15 then
        return 0
    end
end

--------------------------------------
function getVccVolt()
    adc.open(11) -- VCC电压,最新代码才支持
    local v1,v2 = adc.read(11)
    adc.close(11)
    log.info("vccVolt",v1)
    return v1
end
function getChipTemp()
    adc.open(10) -- CPU温度
    local t1,t2 = adc.read(10)
    adc.close(10)
    return t1
end
function getPortVolt()
    adc.open(1) -- 模块上的ADC1脚-PA4, 0~2.4v,不要超过范围使用!!!
    local value,volt = adc.read(1)
    adc.close(1)
    log.info("adc",volt)
    return volt
end
function getDs18b20()
    while true do
        local value,status = sensor.ds18b20(4,false)
        if status == true then
            return value
        end
    end
end
function getNtcTemp()
    adc.open(1) -- 模块上的ADC1脚-PA4, 0~2.4v,不要超过范围使用!!!
    local value,volt = adc.read(1)
    adc.close(1)
    adc.open(11) -- VCC电压,最新代码才支持
    local v1,v2 = adc.read(11)
    adc.close(11)
    local resistant = 10000*volt/(v1-volt)
    local temp = 1/(1/298.15 + math.log(resistant/10000)/3950) - 273.15
    log.info("ntc",temp)
    return temp
end
-------------------get sensor value-------------------
sys.taskInit(function()
    while true do
        sys.wait(1000)
        if sensorOn == true then
            if sensorID == 0 then
                sys.publish("value",getVccVolt())
            end
            if sensorID == 1 then
                log.debug("value","chip_temp","not support")
            end
            if sensorID == 2 then--检测分压电压
                sys.publish("value",getPortVolt())
            end
            if sensorID == 3 then--DS18B20
                sys.publish("value",getDs18b20()/10)
            end
            if sensorID == 4 then
                sys.publish("value",getNtcTemp())
            end
        end
    end
end)

----按键检测
sys.taskInit(function()
    gpio.setup(pin.PB11, nil, gpio.PULLUP)--27,up
    gpio.setup(pin.PB04, nil, gpio.PULLUP)--20,center
    gpio.setup(pin.PB00, nil, gpio.PULLUP)--16,down
    gpio.setup(pin.PB01,0)                --PWM Signal
    while true do
        if gpio.get(27) == gpio.LOW then
            sys.publish("key","3")
        end
        if gpio.get(20) == gpio.LOW then
            sys.publish("key","4")
        end
        if gpio.get(16) == gpio.LOW then
            sys.publish("key","5")
        end
        sys.wait(150)
    end
end)

--SCREEN WINDOWS
sys.taskInit(function()
    sys.wait(100)
    log.info("lvgl", lvgl.init())
        -- ("default")("mono")("empty")("material_light")material_dark
    --lvgl.theme_set_act("mono")
        -- ("material_no_transition")("material_no_focus")
    scr = lvgl.scr_act()
    local font_20 = lvgl.font_load("/luadb/20_test_fonts.bin")
    
    lvgl.obj_set_style_local_text_font(lvgl.scr_act(), lvgl.LABEL_PART_MAIN, lvgl.STATE_DEFAULT, font_20)
    local screen_n = 0
    while true do
        screen_n = screen(screen_n)
    end
end)

-- 用户代码已结束---------------------------------------------
-- 结尾总是这一句
sys.run()
-- sys.run()之后后面不要加任何语句!!!!!

实物效果图两张:

合宙LuatOS系统 合宙air101_单片机_07

合宙LuatOS系统 合宙air101_嵌入式硬件_08