在SensorTile上使用MicroPython(二)
LPS22HB 的 Micropython 程序移植
前面介绍了传感器的接口、主要寄存器、参数计算等方面的内容,下面就介绍用MicroPython驱动LPS22HB的方法。
为了让程序具有通用性,以及系统模块化的要求,我们将为 LPS22HB 单独建立一个 Module,这样也方便其它程序使用。python语言中,一个module和C++的子程序差不多,里面可以包含多个对象(class),每个对象提供一系列函数或方法。但是python语言没有C++那么复杂,也不是面向对象的语言,使用起来简单得多。
一个典型的mudule的结构如下,它由若干class组成,每个class下又由多个函数组成。其中比较特殊的是__init__()函数,它类似C++的构造函数初始化,在定义class变量后就会自动调用__init__()函数,默认需要进行初始化的内容都放在这个函数中。此外,class下的每个函数在定义时的默认第一个参数都是self,但是调用时并不需要使用它,self参数由python系统内部使用。更多关于python语法部分的内容,请大家参考python教程或者参考书,这里就不重复了。
对于LPS22HB传感器,我们先定义一个基本的LPS22HB类:
class LPS22HB(object):
def __init__(self):
xxxx
def func1():
xxxx
def func2():
xxxx
复制代码
然后再将初始化、其它功能函数逐步添加进去,最后就是一个完整的驱动了。
首先需要添加的就是初始化部分,在__init__()函数中,先添加GPIO部分,将CS的GPIO设置为输出,并设置为高电平,这样I2C才能正常工作:
# set CS high
CS_LPS22HB = Pin(LPS22HB_CS_PIN, Pin.OUT)
CS_LPS22HB(1)
CS_AG = Pin(LSM6DSM_CS_PIN, Pin.OUT)
CS_AG(1)
CS_A = Pin(LSM303AGR_CS_A_PIN, Pin.OUT)
CS_A(1)
CS_M = Pin(LSM303AGR_CS_M_PIN, Pin.OUT)
CS_M(1)
复制代码
然后再添加I2C初始化部分的代码:
# soft I2C
self.i2c = machine.I2C(-1, sda=machine.Pin('PB15'), scl=machine.Pin('PB13'))
# set open drain and pull up
sda=machine.Pin('PB15', Pin.OPEN_DRAIN, pull=Pin.PULL_UP)
scl=machine.Pin('PB13', Pin.OPEN_DRAIN, pull=Pin.PULL_UP)
复制代码
再设置LPS22HB的CTRL1_REG寄存器,让LPS22HB默认处于工作模式:
# start LPS22HB
self.setreg(0x18, LPS22HB_CTRL_REG1, LPS22HB_ADDRESS)
self.temp0 = 0
self.press = 0
self.LPS22HB_ON = True
复制代码
self.temp0、self.press、self.LPS22HB_ON是内部变量,用于后面的参数计算和状态设置。它们不是必须的,这里定义它们主要是为了方便同一模块下其它函数调用。
初始化部分完成后,就是添加其它功能函数了。大家可以发现在上面的初始化部分我们使用了一个设置寄存器的函数,因为设置和读取寄存器是一个通用性的操作,所以我们将寄存器的操作也设置成函数,这样也方便将底层和应用层分离。为了方便读取参数,我们还设置了一个读取两个相邻寄存器的函数get2reg,这个函数没有使用传感器自动递增寄存器地址的功能,是因为在传感器的BUD模式下,地址自动递增的功能是无效的,为了让程序有更好的兼容性,所以稍微牺牲了一点性能。
def setreg(self, dat, reg, addr):
buf = bytearray(2)
buf[0] = reg
buf[1] = dat
self.i2c.writeto(addr, buf)
def getreg(self, reg, addr):
buf = bytearray(1)
buf[0] = reg
self.i2c.writeto(addr, buf)
t = self.i2c.readfrom(addr, 1)
return t[0]
def get2reg(self, reg, addr):
l = self.getreg(reg, addr)
h = self.getreg(reg+1, addr)
return l+h*256
复制代码
为了增加程序的可读性和可维护下,我们将寄存器的名称定义为常量,并且将它放在class的前面,这类似于C语言中的#define。寄存器名称前面还加上LPS22HB前缀,这样可以在一个Module中存在多个芯片定义时防止和其它芯片的定义相冲突。
# LPS22HB register
LPS22HB_INTERRUPT_CFG= const(0x0B)
LPS22HB_THS_P_L = const(0x0C)
LPS22HB_THS_P_H = const(0x0D)
LPS22HB_WHO_AM_I = const(0x0F)
LPS22HB_CTRL_REG1 = const(0x10)
LPS22HB_CTRL_REG2 = const(0x11)
LPS22HB_CTRL_REG3 = const(0x12)
LPS22HB_FIFO_CTRL = const(0x14)
LPS22HB_REF_P_XL = const(0x15)
LPS22HB_REF_P_L = const(0x16)
LPS22HB_REF_P_H = const(0x17)
LPS22HB_RPDS_L = const(0x18)
LPS22HB_RPDS_H = const(0x19)
LPS22HB_RES_CONF = const(0x1A)
LPS22HB_INT_SOURCE = const(0x25)
LPS22HB_FIFO_STATUS = const(0x26)
LPS22HB_STATUS = const(0x27)
LPS22HB_PRESS_OUT_XL = const(0x28)
LPS22HB_PRESS_OUT_L = const(0x29)
LPS22HB_PRESS_OUT_H = const(0x2A)
LPS22HB_TEMP_OUT_L = const(0x2B)
LPS22HB_TEMP_OUT_H = const(0x2C)
LPS22HB_LPFP_RES = const(0x33)
复制代码
前面的寄存器操作、初始化等可以看成是准备工作,准备工作完成了,就是具体传感器的操作了。我们使用传感器最重要的目的就是需要获得传感器的参数,因此再定义两个函数,一个用于获取气压,一个获取温度。
def LPS22HB_temp(self):
self.temp0 = self.get2reg(LPS22HB_TEMP_OUT_L, LPS22HB_ADDRESS)
if(self.temp0 > 0x7FFF):
self.temp0 -= 65536
return self.temp0/100
def LPS22HB_press(self):
self.press = self.getreg(LPS22HB_PRESS_OUT_XL, LPS22HB_ADDRESS)
self.press += self.get2reg(LPS22HB_PRESS_OUT_L, LPS22HB_ADDRESS) * 256
return self.press/4096
复制代码
气压函数是先读取三个寄存器的参数,然后将结果除以4096,这就是按照前面介绍的计算方法进行换算的。而温度函数稍微麻烦一点,因为存在负数的问题。在python语言中不像C语言那样可以自动进行类型转换,寄存器的参数不能直接转换为负数,所以需要自己判断和转换。因为这里是一个双字节的数据,最高位就是符号位,因此如果数据大于0x7FFF或者最高位是1,那么就认为它是负数。
另外在一些情况下,需要同时读取温度和气压两个数据,所以我们可以将两个参数放到一个函数中,通过一个列表返回。这里可以直接将前面两个函数放在return的列表中。
def LPS22HB(self):
return [self.LPS22HB_temp(), self.LPS22HB_press()]
复制代码
最后,为了降低功耗,还需要让传感器可以进入掉电模式,因此我们还需要增加两个功耗管理函数:
def LPS22HB_poweron(self):
t = self.getreg(LPS22HB_CTRL_REG1, LPS22HB_ADDRESS) & 0x0F
self.setreg(t|0x10, LPS22HB_CTRL_REG1, LPS22HB_ADDRESS)
self.LPS22HB_ON = True
def LPS22HB_poweroff(self):
t = self.getreg(LPS22HB_CTRL_REG1, LPS22HB_ADDRESS) & 0x0F
self.setreg(t, LPS22HB_CTRL_REG1, LPS22HB_ADDRESS)
self.LPS22HB_ON = False
复制代码
在掉电模式下(Power down),传感器的最低功耗是1uA。其实LPS22HB的功耗也不高,在ODR和LC_EN都是1时也只有3uA。
完成上面的工作后,我们就实现了一个最基本的LPS22HB驱动。我们可以把它保存到一个LPS22HB.py文件中,然后用下面的方法使用它:
>>> from LPS22HB import LPS22HB
>>> lp=LPS22HB()
>>> lp.LPS22HB_temp()
16.17
>>> lp.LPS22HB_press()
1025.827
>>> lp.LPS22HB()
[16.14, 1025.839]
如果用dir(LPS22HB),可以查看全部的函数
>>> dir(LPS22HB)
['__qualname__', 'LPS22HB_poweron', '__module__', 'LPS22HB_press', 'LPS22HB_temp', 'LPS22HB', 'getreg', 'setreg', 'get2reg', 'LPS22HB_poweroff', '__init__']
如果想进一步完善LPS22HB驱动,增加功能,使用中断等,大家可以在此基础上进行改进。