打印
[信息]

LIS2DW12运动传感器的MicroPython驱动移植

[复制链接]
396|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
城市花园|  楼主 | 2020-11-5 17:10 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
LIS2DW12是一个低功耗的三轴加速度(运动)传感器,支持多种工作模式,可以用于:
可穿戴设备的运动检测
手势识别和游戏
运动激活功能和用户界面
显示方向
点击/双击识别
自由落体检测
手持设备的智能节能
助听器
便携式医疗设备
无线传感器节点
运动计量装置
LIS2DW12可以通过I2C/SPI方式访问内部寄存器,其中SPI支持3线和4线两种方式。在I2C方式下,设备地址是24/25(7位地址模式),在X-NUCLEO-IKS01A3上的地址是25。

LIS2DW12内部寄存器分为控制寄存器(R/W),输出寄存器(R),状态寄存器(R),FIFO寄存器,TAP(敲击)寄存器等。上电后,首先需要设置控制寄存器,让传感器处于工作模式,否则不能读取数据。

传感器工作模式有低功耗模式、高性能模式和掉电模式,而低功耗模式有4种方式(具体区别可以参考数据手册)。数据输出速率ODR范围是1.6-1600HZ,如果将ODR设为0就进入了掉电模式。这部分功能都是在控制寄存器CTRL1中设置。

控制寄存器CTRL4和CTRL5用来设置中断输出功能,而传感器最大量程是通过CTRL6的FS进行设置,可以设置为±2g/±4g/±8g/±16g。

传感器的数据是通过OUT_X_L/OUT_X_H等寄存器读取,两个寄存器表示一个数据,运动数据可以用12/14位两种方式表示。而温度通过OUT_T_L/OUT_T_H/OUT_T寄存器表示,OUT_T_L和OUT_T_H的数值除以256再加上25,就是环境温度,OUT_T寄存器相当于OUT_T_H,也就是温度的整数部分。有点奇怪的是在单次模式(oneshot)下,温度传感器停止了更新,数据不在发生变化。

下面是使用MicroPython移植的完整驱动,暂时没有包含中断、自由落体检测、敲击检测等功能。为了保持和其它传感器驱动一致,将单次模式统一用oneshot_mode()函数控制。

# LIS2DW12 3-axis motion seneor micropython drive
# ver: 1.0
# License: MIT
# Author: shaoziyang (shaoziyang@micropython.org.cn)
# v1.0 2019.7

LIS2DW12_CTRL1 = const(0x20)
LIS2DW12_CTRL2 = const(0x21)
LIS2DW12_CTRL3 = const(0x22)
LIS2DW12_CTRL6 = const(0x25)
LIS2DW12_STATUS = const(0x27)
LIS2DW12_OUT_T_L = const(0x0D)
LIS2DW12_OUT_X_L = const(0x28)
LIS2DW12_OUT_Y_L = const(0x2A)
LIS2DW12_OUT_Z_L = const(0x2C)
LIS2DW12_2G = const(0)
LIS2DW12_4G = const(1)
LIS2DW12_8G = const(2)
LIS2DW12_16G = const(3)
LIS2DW12_SCALE = ('2g', '4g', '8g', '16g')

class LIS2DW12():
    def __init__(self, i2c, addr = 0x19):
        self.i2c = i2c
        self.addr = addr
        self.tb = bytearray(1)
        self.rb = bytearray(1)
        self.oneshot = False
        self.irq_v = [0, 0, 0]
        self._power = 0x20
        # ODR=2 MODE=0 LP=1
        self.setreg(LIS2DW12_CTRL1, 0x21)
        # BDU=1
        self.setreg(LIS2DW12_CTRL2, 0x0C)
        # SLP_MODE_SEL=1
        self.setreg(LIS2DW12_CTRL3, 0x02)
        # scale=2G
        self._scale = 0
        self.scale(self._scale)
        self.oneshot_mode(False)
        
    def int16(self, d):
        return d if d < 0x8000 else d - 0x10000

    def setreg(self, reg, dat):
        self.tb[0] = dat
        self.i2c.writeto_mem(self.addr, reg, self.tb)

    def getreg(self, reg):
        self.i2c.readfrom_mem_into(self.addr, reg, self.rb)
        return self.rb[0]

    def get2reg(self, reg):
        return self.getreg(reg) + self.getreg(reg+1) * 256

    def r_w_reg(self, reg, dat, mask):
        self.getreg(reg)
        self.rb[0] = (self.rb[0] & mask) | dat
        self.setreg(reg, self.rb[0])

    def oneshot_mode(self, oneshot=None):
        if oneshot is None:
            return self.oneshot
        else:
            self.oneshot = oneshot
            d = 8 if oneshot else 0
            self.r_w_reg(LIS2DW12_CTRL1, d, 0xF3)

    def ONE_SHOT(self):
        if self.oneshot:
            self.r_w_reg(LIS2DW12_CTRL3, 1, 0xFE)
            while 1:
                if self.getreg(LIS2DW12_CTRL1) & 0x01:
                    return

    def x(self):
        self.ONE_SHOT()
        return self.int16(self.get2reg(LIS2DW12_OUT_X_L))>>2

    def y(self):
        self.ONE_SHOT()
        return self.int16(self.get2reg(LIS2DW12_OUT_Y_L))>>2

    def z(self):
        self.ONE_SHOT()
        return self.int16(self.get2reg(LIS2DW12_OUT_Z_L))>>2

    def get(self):
        self.ONE_SHOT()
        self.irq_v[0] = self.int16(self.get2reg(LIS2DW12_OUT_X_L))>>2
        self.irq_v[1] = self.int16(self.get2reg(LIS2DW12_OUT_Y_L))>>2
        self.irq_v[2] = self.int16(self.get2reg(LIS2DW12_OUT_Z_L))>>2
        return self.irq_v

    def temperature(self):
        try:
            return self.int16(self.get2reg(LIS2DW12_OUT_T_L))/256 + 25
        except MemoryError:
            self.temperature_irq()

    def temperature_irq(self):
        self.getreg(LIS2DW12_OUT_T_L+1)
        if self.rb[0] & 0x80: self.rb[0] -= 256
        return self.rb[0] + 25

    def scale(self, dat=None):
        if dat is None:
            return LIS2DW12_SCALE[self._scale]
        else:
            if type(dat) is int:
                if dat > 3 or dat < 0: return
                self._scale = dat
            elif type(dat) is str:
                if not dat in LIS2DW12_SCALE: return
                self._scale = LIS2DW12_SCALE.index(dat)
            else: return
            self.r_w_reg(LIS2DW12_CTRL6, self._scale<<4, 0xCF)

    def power(self, on=None):
        if on is None:
            return self._power > 0
        else:
            if on:
                self.r_w_reg(LIS2DW12_CTRL1, self._power, 0x0F)
                self._power = 0
            else:
                self._power = self.getreg(LIS2DW12_CTRL1) & 0xF0
                self.r_w_reg(LIS2DW12_CTRL1, 0, 0x0F)

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

4

主题

20

帖子

0

粉丝