文章目录
- 1 前言
- 2 硬件连接
- 3 官方驱动
- 4 准备工作——角度计算
- 4.1 测试获取与y轴夹角
- 4.2 映射到lcd
- 5 代码——结合传感器
- 6 结果1——计算与北夹角
- 7 结果2——和手机对比
1 前言
调试三轴罗盘仪,传感器获取磁场强度判定方向。
2 硬件连接
i2c通信,和其他的一样,只用到四根。
3 官方驱动
罗盘仪:https://doc.openluat.com/wiki/21?wiki_page_id=2750
--- 模块功能:ADC功能测试.
-- ADC测量精度(12bit)
-- 每隔1s读取一次ADC值
-- @author openLuat
-- @module adc.testAdc
-- @license MIT
-- @copyright openLuat
-- @release 2018.12.19
module(...,package.seeall)
PROJECT = "hmc5883l_demo"
VERSION = "1.0.0"
require "log"
require "sys"
require "misc"
-- i2c ID
local i2cid = 2
-- i2c 速率
--打开vlcd电压域
pmd.ldoset(15,pmd.LDO_VLCD)-- GPIO 0、1、2、3、4
local addr = 0x1e --addr
local speed = 100000 --iic速率
local Config_a = 0x00 --配置寄存器a:设置的数据输出速率和测量配置 0 11 100 00 0X70
local Config_b = 0x01 --配置寄存器b:设置装置的增益 001 00000 0Xe0
local mode = 0x02 --模式寄存器: 默认单一测量模式01,连续00 000000 00 0X00/0x01
local Msb_x = 0x03 --x 高位数据输出
local Lsb_x = 0x04 --x 低位数据输出
local Msb_y = 0x07 --x 高位数据输出
local Lsb_y = 0x08 --x 低位数据输出
local Msb_z = 0x05 --x 高位数据输出
local Lsb_z = 0x06 --x 低位数据输出
local status = 0x09 -- 状态寄存器 0x00
local recogn_a = 0x0a -- 识别寄存器a 0x48
local recogn_b = 0x0b -- 识别寄存器b 0x34
local recogn_c = 0x0c -- 识别寄存器c 0x33
--写数据
local function I2C_Write_Byte(regAddress,val,val2)
i2c.send(i2cid, addr, {regAddress,val,val2})
end
--读取单个字节
local function I2C_Read_Byte(regAddress)
i2c.send(i2cid, addr, regAddress)
local rdstr = i2c.recv(i2cid, addr, 1)
log.info("rdstr:toHex()",rdstr:toHex())
return rdstr:byte(1)--变成10进制数据
end
--读取多个字节
local function I2C_Read_Bytes(regAddress,cnt)
i2c.send(i2cid, addr, regAddress)
local rdstr = i2c.recv(i2cid, addr, cnt)
--log.info("rdstr:toHex()-------",rdstr:toHex())
return rdstr
end
-- 初始化
function init()
if i2c.setup(i2cid, speed, addr) ~= speed then
log.error("i2c", "setup fail", addr)
i2c.close(i2cid)
return
end
log.info("dev i2c init_ok")
return true
end
function hmc5883l_int()
I2C_Write_Byte(Config_a,0x70) --写配置a寄存器数据
I2C_Write_Byte(Config_b,0x20) --写配置b寄存器数据 增益660
I2C_Write_Byte(mode,0x00) --写模式寄存器数据
end
function hmc5883l_read()
local hx=I2C_Read_Byte(Msb_x)
local lx=I2C_Read_Byte(Lsb_x)
local x_data=hx*256+lx
local hy=I2C_Read_Byte(Msb_y)
local ly=I2C_Read_Byte(Lsb_y)
local y_data=hy*256+ly
local hz=I2C_Read_Byte(Msb_z)
local lz=I2C_Read_Byte(Lsb_z)
local z_data=hz*256+lz
if(x_data>32768) then
x_data= -(0xFFFF - x_data + 1)
end
if(y_data>32768) then
y_data = -(0xFFFF - y_data + 1)
end
if(z_data>32768) then
z_data = -(0xFFFF - z_data+ 1)
end
local Angle= math.atan2(y_data,x_data)*(180/3.14159265)+180;--单位:角度 (0~360)
Angle= Angle
log.info("x,y,z-----------", x_data,y_data,z_data )
log.info("Angle",string.format("%.1f", Angle))
return x_data,y_data,z_data
end
sys.taskInit(function()
sys.wait(3000)
while true do
sys.wait(2000)
if init() then
--初始化hmc588配置
hmc5883l_int()
--读取x,y,z数值
hmc5883l_read()
i2c.close(i2cid)
end
end
end)
问题
在lua5.3上,math.atan2()函数被弃用了,需要自己改。
4 准备工作——角度计算
4.1 测试获取与y轴夹角
-- 交换xy求与y轴的夹角
-- y = 1
-- x = -3^(1/2)
y = -1
x = -3^(1/2)
-- 获取与y轴的夹角
function get_y_angle(x,y)
local temp_atan = 0
local pi = 3.1415926
if x > 0 then
if y > 0 then
-- print("1")
temp_atan = math.atan(x,y)/pi*180
else if y<0 then
-- print("2")
temp_atan = math.atan(x,y)/pi*180
end
end
else if x < 0 then
if y > 0 then
-- print("4")
temp_atan = math.atan(x,y)/pi*180 + 360
else if y<0 then
-- print("3")
temp_atan = math.atan(x,y)/pi*180 + 360
end
end
end
end
return temp_atan
end
n = get_y_angle(x,y)
-- print(m)
print(n)
4.2 映射到lcd
-- 交换xy求与y轴的夹角
-- y = 1
-- x = -3^(1/2)
y = -1
x = -3^(1/2)
-- 获取与y轴的夹角
function get_y_angle(x,y)
local temp_atan = 0
local pi = 3.1415926
if x > 0 then
if y > 0 then
-- print("1")
temp_atan = math.atan(x,y)/pi*180
else if y<0 then
-- print("2")
temp_atan = math.atan(x,y)/pi*180
end
if y == 0 then
temp_atan = 90
end
end
else if x < 0 then
if y > 0 then
-- print("4")
temp_atan = math.atan(x,y)/pi*180 + 360
else if y<0 then
-- print("3")
temp_atan = math.atan(x,y)/pi*180 + 360
end
end
if y == 0 then
temp_atan = 270
end
end
end
return temp_atan
end
n = get_y_angle(x,y)
-- print(m)
print(n)
-- 输入数值范围及当前数值,返回与y轴的夹角
function raw2angle_y(temp_x,temp_y,x_max,x_min,y_max,y_min)
local R_x_max = x_max
local R_x_min = x_min
local R_y_max = y_max
local R_y_min = y_min
local R_range_x = R_x_max - R_x_min
local R_range_y = R_y_max - R_y_min
local R00_x = R_x_max - (R_range_x)/2
local R00_y = R_y_max - (R_range_y)/2
print(R00_x,R00_y)
local R_temp_x = temp_x
local R_temp_y = temp_y
local R_bias_x = R_temp_x - R00_x
local R_bias_y = R_temp_y - R00_y
print(get_y_angle(R_bias_x,R_bias_y))
local lcd_x_max = 128
local lcd_y_max = 128
local R_step_x = lcd_x_max / R_range_x
local R_step_y = lcd_y_max / R_range_y
local lcd_x = R_bias_x * R_step_x + 64 -- R00对应(64,64)
local lcd_y = R_bias_y * R_step_y + 64
print("lcd",lcd_x,lcd_y)
end
raw2angle_y(300,400,300,-100,400,0)
5 代码——结合传感器
sen_GY271.lua
--- 模块功能:ADC功能测试. GY271 hmc5883l 三轴罗盘
-- ADC测量精度(12bit)
-- 每隔1s读取一次ADC值
-- @author openLuat
-- @module adc.testAdc
-- @license MIT
-- @copyright openLuat
-- @release 2018.12.19
--[[
VCC:电源正
GND:电源负
SCL:I2C串行时钟线
SDA:I2C串行数据线
DRDY:中断引脚 -- io11
]]
PROJECT = "hmc5883l_demo"
VERSION = "1.0.0"
-- require "log"
-- require "sys"
-- require "misc"
-- i2c ID
local GY271_i2c_id = 0
-- i2c 地址
local GY271_i2c_addr = 0x1e --GY271_i2c_addr
-- i2c 速率
local GY271_i2c_speed = 100000 --iic速率
--打开vlcd电压域
-- pmd.ldoset(15,pmd.LDO_VLCD)-- GPIO 0、1、2、3、4
local Config_a = 0x00 --配置寄存器a:设置的数据输出速率和测量配置 0 11 100 00 0X70
local Config_b = 0x01 --配置寄存器b:设置装置的增益 001 00000 0Xe0
local mode = 0x02 --模式寄存器: 默认单一测量模式01,连续00 000000 00 0X00/0x01
local Msb_x = 0x03 --x 高位数据输出
local Lsb_x = 0x04 --x 低位数据输出
local Msb_y = 0x07 --x 高位数据输出
local Lsb_y = 0x08 --x 低位数据输出
local Msb_z = 0x05 --x 高位数据输出
local Lsb_z = 0x06 --x 低位数据输出
local status = 0x09 -- 状态寄存器 0x00
local recogn_a = 0x0a -- 识别寄存器a 0x48
local recogn_b = 0x0b -- 识别寄存器b 0x34
local recogn_c = 0x0c -- 识别寄存器c 0x33
--写数据
local function I2C_Write_Byte(regAddress,val,val2)
i2c.send(GY271_i2c_id, GY271_i2c_addr, {regAddress,val,val2})
end
--读取单个字节
local function I2C_Read_Byte(regAddress)
i2c.send(GY271_i2c_id, GY271_i2c_addr, regAddress)
local rdstr = i2c.recv(GY271_i2c_id, GY271_i2c_addr, 1)
-- log.info("rdstr:toHex()",rdstr:toHex()) -- 打印原始数据
return rdstr:byte(1)--变成10进制数据
end
--读取多个字节
local function I2C_Read_Bytes(regAddress,cnt)
i2c.send(GY271_i2c_id, GY271_i2c_addr, regAddress)
local rdstr = i2c.recv(GY271_i2c_id, GY271_i2c_addr, cnt)
--log.info("rdstr:toHex()-------",rdstr:toHex())
return rdstr
end
-- 初始化
function init()
if i2c.setup(GY271_i2c_id, GY271_i2c_speed, GY271_i2c_addr) ~= GY271_i2c_speed then
log.error("i2c", "setup fail", GY271_i2c_addr)
i2c.close(GY271_i2c_id)
return
end
log.info("dev i2c init_ok")
sys.wait(1000)
hmc5883l_int()
hmc5883l_read()
x_max = x_data
x_min = x_data
y_max = y_data
y_min = y_data
z_max = z_data
z_min = z_data
return true
end
function hmc5883l_int()
I2C_Write_Byte(Config_a,0x70) --写配置a寄存器数据
I2C_Write_Byte(Config_b,0x20) --写配置b寄存器数据 增益660
I2C_Write_Byte(mode,0x00) --写模式寄存器数据
end
function judge_xyz_range()
if x_data > x_max then
x_max = x_data
else if x_data < x_min then
x_min = x_data
end
end
if y_data > y_max then
y_max = y_data
else if y_data < y_min then
y_min = y_data
end
end
if z_data > z_max then
z_max = z_data
else if z_data < z_min then
z_min = z_data
end
end
print("x range: ",x_max,x_min,"y range: ",y_max,y_min,"z range: ",z_max,z_min)
end
function get_angle()
print("x "..x_data.." | y "..y_data.." | z "..z_data)
x_p00 = x_max - (x_max - x_min)/2
y_p00 = y_max - (y_max - y_min)/2
z_p00 = z_max - (z_max - z_min)/2
print("p00",x_p00,y_p00,z_p00)
hmc5883l_angle = math.atan((y_data - y_p00)/(x_data - x_p00))*(180/3.14159265)+180
-- hmc5883l_angle = math.atan((x_data - x_p00)/(y_data - y_p00))*(180/3.14159265)+180
print("hmc5883l_angle",hmc5883l_angle)
end
function hmc5883l_read()
local hx=I2C_Read_Byte(Msb_x)
local lx=I2C_Read_Byte(Lsb_x)
x_data=hx*256+lx
local hy=I2C_Read_Byte(Msb_y)
local ly=I2C_Read_Byte(Lsb_y)
y_data=hy*256+ly
local hz=I2C_Read_Byte(Msb_z)
local lz=I2C_Read_Byte(Lsb_z)
z_data=hz*256+lz
if(x_data>32768) then
x_data= -(0xFFFF - x_data + 1)
end
if(y_data>32768) then
y_data = -(0xFFFF - y_data + 1)
end
if(z_data>32768) then
z_data = -(0xFFFF - z_data+ 1)
end
log.info("x,y,z-----------", x_data,y_data,z_data )
-- local Angle= math.atan(x_data,y_data)*(180/3.14159265)+180 --单位:角度 (0~360)
-- Angle= Angle
-- log.info("Angle",string.format("%.1f", Angle))
return x_data,y_data,z_data
end
function hmc5883l_test()
sys.wait(3000)
hmc5883l_status = init()
print("hmc5883l_status",hmc5883l_status)
while true do
sys.wait(1000)
if hmc5883l_status then
--初始化hmc588配置
-- hmc5883l_int()
--读取x,y,z数值
hmc5883l_read()
judge_xyz_range()
-- get_angle()
raw2angle_y(x_data,y_data,x_max,x_min,y_max,y_min)
-- i2c.close(GY271_i2c_id)
end
end
end
-- test
-- 交换xy求与y轴的夹角
-- y = 1
-- x = -3^(1/2)
-- y = -1
-- x = -3^(1/2)
-- 获取与y轴的夹角
function get_y_angle(x,y)
local temp_atan = 0
local pi = 3.1415926
if x > 0 then
if y > 0 then
-- print("1")
temp_atan = math.atan(x,y)/pi*180
else if y<0 then
-- print("2")
temp_atan = math.atan(x,y)/pi*180
end
if y == 0 then
temp_atan = 90
end
end
else if x < 0 then
if y > 0 then
-- print("4")
temp_atan = math.atan(x,y)/pi*180 + 360
else if y<0 then
-- print("3")
temp_atan = math.atan(x,y)/pi*180 + 360
end
end
if y == 0 then
temp_atan = 270
end
end
end
return temp_atan
end
-- n = get_y_angle(x,y)
-- -- print(m)
-- print(n)
-- 输入数值范围及当前数值,返回与y轴的夹角
function raw2angle_y(temp_x,temp_y,x_max,x_min,y_max,y_min)
local R_x_max = x_max
local R_x_min = x_min
local R_y_max = y_max
local R_y_min = y_min
local R_range_x = R_x_max - R_x_min
local R_range_y = R_y_max - R_y_min
local R00_x = R_x_max - (R_range_x)/2
local R00_y = R_y_max - (R_range_y)/2
print("R00",R00_x,R00_y)
local R_temp_x = temp_x
local R_temp_y = temp_y
local R_bias_x = R_temp_x - R00_x
local R_bias_y = R_temp_y - R00_y
N_angle = get_y_angle(R_bias_x,R_bias_y) - 90
if N_angle < 0 then
print("N-E")
N_angle = N_angle+360
end
print("get_y_angle",N_angle)
local lcd_x_max = 128
local lcd_y_max = 128
local R_step_x = lcd_x_max / R_range_x
local R_step_y = lcd_y_max / R_range_y
local lcd_x = R_bias_x * R_step_x + 64 -- R00对应(64,64)
local lcd_y = R_bias_y * R_step_y + 64
print("lcd",lcd_x,lcd_y)
end
-- raw2angle_y(300,400,300,-100,400,0)
main.lua
PROJECT = "sensor"
VERSION = "1.0.0"
_G.sys = require("sys")
require("sen_BH1750") -- 光感
require("sen_TCS34725") -- 色彩
require("sen_BMP180") -- 气压
require("sen_GY271")
-- 加载I²C功能测试模块
T1_BH1750 = 0
T2_TCS34725 = 0
T3_BMP180 = 0
T4_GY271 = 1
-- i2c ID
esp32_i2c_id = 0 -- esp32只支持一路i2c, id为0
esp32_spi_id = 2 -- esp32只支持一路spi, id为2
-- i2c 速率
esp32_i2c_speed = i2c.SLOW -- 100000
sys.taskInit(function()
-- ps:有wait不能放在外面
if T2_TCS34725 == 1 then
TCS34725_init(esp32_i2c_id,esp32_i2c_speed) -- TCS34725地址默认为0x29
end
if T1_BH1750 == 1 then
BH1750_init(esp32_i2c_id)
end
if T3_BMP180 == 1 then
-- BMP180_test(esp32_i2c_id)
BMP180_init(esp32_i2c_id)
end
if T4_GY271 == 1 then
hmc5883l_test()
end
-- sys.wait(1500)
while 1 do
if T1_BH1750 == 1 then
BH1750_get()
end
if T2_TCS34725 == 1 then
TCS34725_get() -- TCS34725地址默认为0x29
end
if T3_BMP180 == 1 then
-- BMP180_test(esp32_i2c_id)
BMP180_get_temp_press()
end
sys.wait(100)
end
end)
sys.run()
6 结果1——计算与北夹角
这里的get_y_angle就是N,数值为0时指向正北。
和手机有一点偏差,后续再结合lcd屏幕显示数值吧。
7 结果2——和手机对比
lcd绘制出来成一根直线,看起来像是y=ymax - x 。
如图,以手机作为参考的话,还是相对符合的。
不过需要注意的是,在测试是需要校正(将xy的最值都设置为当前获取值),而且周边干扰较少,否则会影响效果,导致测试到的角度有偏差。