上半年利用业余时间使用国产芯片-合宙Air101 做了个小玩具,有点小成果,奈何现在忙带娃,基本没有时间继续了,已经吃灰了一段时间,做个总结,送有缘人。
XT-E804是阿里平头哥的MCU,属于CSKY架构(与RISC-V接近),合宙Air101芯片是XT-E804的QFN32封装,4mm*4mm封装大小。具有32位高性能内核,主频最高可达240MHz。内置UART、SPI、I2C、GPIO、ADC、PWM等外设接口,内置2MB Flash,作为一般的IOT设备开发足够。
部分原理图如下:
使用的嘉立创EDA 专业版,布线图如下:
背面接口见下图,采用常用的PH2.0卧式,3pin和4pin;供电使用常用的6pin TYPE-C;按键采用3*4*2.5轻触按键。屏幕采用1.14寸TFT显示屏,135*240分辨率,ST7789V驱动。同接口规格的还可以使用0.96寸显示屏,80*160分分辨率,ST7735驱动。板子整体尺寸为40mm*20mm。
CN1做输入口可接单总线传感器(ds18b20)或NTC热敏电阻;CN2输出口可接5v继电器或MOS管模块,输出PWM信号和开关信号;CN3为usart接口,用于下载程序和通信;CN4为模拟I2C接口(硬件I2C被ADC占用),接传感器。
开始显示屏使用了8PIN插接款,后来发现接头有一定高度,后来改为焊线款,屏幕可以贴合到mcu上,更紧凑。
3D效果图(开始显示屏使用了8PIN插接款,后来发现接头有一定高度,后来改为焊线款,屏幕可以贴合到mcu上,更紧凑):
换个角度看 :
软件部分,开发采用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()之后后面不要加任何语句!!!!!
实物效果图两张: