文档章节

在SensorTile上使用MicroPython(一)

shaoziyang
 shaoziyang
发布于 2017/02/11 16:40
字数 3800
阅读 171
收藏 2
点赞 1
评论 1

前几天终于将SensorTile传感器部分的MicroPython驱动移植成功,因此就想将传感器的基本使用方法和大家讨论一下,顺便也写写MicroPython的移植方法。去年就有网友希望介绍移植方面的教程,因为各种原因一直没有时间写,正好借这个机会补上了。因为时间较少,一些地方没有仔细检查,如果发现文中有错误的地方,欢迎大家提出。

SensotTile简介

SensorTile核心板非常紧凑小巧,看起来就是一个可穿戴的原型(智能手表),因此它配置的传感器也是和运动相关的。SensorTile核心板上有4个传感器,它们分别是:

  • LPS22HB,气压+温度传感器
  • LSM6DSM,三轴加速度+三轴角速度传感器
  • LSM303AGR,三轴角速度+三轴磁场传感器
  • MP34DT04,MEMS麦克风传感器

我们先从最简单的LPS22HB开始,逐步介绍传感器的使用和移植方法。而MP34DT04传感器这次没有使用到,它的接口也和其他传感器不同,所以暂时先不看。


LPS22HB气压传感器


 

硬件接口

LPS22HB的原理图如上,从上面我们可以看到,传感器使用了SPI连接方式,但是只用了CS、SCL/SPC、SDA/SDI/SDO这几个脚,SDO脚没有使用,说明它没有使用标准的SPI接口方式。此外,还可以看到INT信号也没有连接,所以也就不能使用INT模式了。通常在INT模式下,可以预先设置一个门限参数,当传感器的输出超过这个门限时,就会自动产生一个INT信号,用来唤醒MCU,然后读取并处理参数,这有助于简化编程,降低系统功耗。

从传感器的用户手册中可以看到,传感器支持SPI/I2C两种接口。这两种接口方式是通过CS脚进行切换的,当CS为低电平时是SPI方式,CS是高电平时是I2C方式。SensorTile在硬件设计时,使用了半双工的SPI接口(又叫3-wire模式),这个模式下主机只使用MOSI做数据线,而从机使用MISO。它的好处在于可以节约一个数据线,缺点就是牺牲了速度。


 

因为MicroPython目前不支持半双工的SPI接口方式(硬件SPI和软件SPI都不支持这个方式),因此要用SPI方式驱动传感器就只能自己通过软件模拟这种SPI方式,这不但增加了软件的复杂性,同时速度也会比较慢,所以我采用了I2C接口方式。因为SPI_SDA(PB15)和SPI_CLK(PB13)引脚并不是硬件I2C接口,所以需要用软件I2C方式。好在micropython底层已经支持软件I2C,使用方法和硬件I2C一样,速度也不慢。

这里先介绍一下软件I2C。为了使用软件I2C,我们需要使用到micropython的machine库,而不能使用pyb库。大家可能也注意到了,CC3200、ESP8266、STM32等分支在硬件底层函数接口上有很多不同,这给我们编程和程序移植带来很多不便。从v1.8版开始,micropython开始增强了machine库的功能,这样有助于统一底层接口。软件I2C的使用方法如下:
 

import machine
i2c = machine.I2C(-1, sda=machine.Pin('PB15'), scl=machine.Pin('PB13'))

其中-1就代表使用了软件I2C,sda和scl就是使用的GPIO,可以使用任何GPIO,在SensorTile上就必须使用PB15和PB13。

直接这样定义后,大家会发现I2C还是不能工作,这是因为在SensorTile内部没有设置I2C的上拉电阻,这样I2C总线的状态无法确定,所以我们还需要使能GPIO内部的上拉电阻。注意这个步骤需要放在I2C初始化之后,因为在I2C初始化的时候,会重新设置GPIO状态和参数。直接在I2C定义中的sda=machine.Pin('PB15', pull=Pin.PULL_UP)加入上拉电阻定义也是不能工作的,因为在设置I2C时会忽略这个参数。在I2C定义后,再加入下面的定义,I2C就可以正常工作了,如果这时使用i2c.scan()函数,就可以搜索到4个设备。
 

sda=machine.Pin('PB15', Pin.OPEN_DRAIN, pull=Pin.PULL_UP) 
scl=machine.Pin('PB13', Pin.OPEN_DRAIN, pull=Pin.PULL_UP)



传感器寄存器

ST公司为SensorTile kit提供了多个例程,例程中包含了传感器的底层驱动函数。如果使用C++编程,可以使用这些驱动函数。而我们要使用micropython进行编程,所以无法直接使用ST的底层函数,需要自己对寄存器进行操作(其实ST的底层驱动函数也是对这些寄存器进行操作,只是它已经封装好了,不用再看寄存器说明了)。为了使用LPS22HB,需要对传感器的寄存器有初步的了解。


 

上图是LPS22HB的寄存器列表,它的寄存器不是太多,除去保留的寄存器(Reserved)外,一共大约有二十几个。每个寄存器都有一个地址,寄存器输出是8位的,大部分寄存器可以读写(RW),少量寄存器是只读的(R)。如果按照功能进行划分,传感器的寄存器大致可以分为下面几类:

  • 功能设置
  • 传感器状态
  • 参数输出

功能设置寄存器可以设置传感器的工作模式、参数输出频率、参数范围、中断等参数,只有设置了正确的参数后,传感器才能工作。在上电/复位后,我们也需要先设置功能寄存器(初始化),否则传感器是没有输出的,因为默认情况下传感器是处于掉电模式(Power down)。

状态寄存器通常是只读的,可以通过它查询传感器当前的状态或者某种标志位。特别在中断工作模式下,需要通过状态寄存器查询发生的中断。

参数输出就是传感器的输出,如气压、温度等参数。很多参数使用了双字节甚至更多字节,需要读取后在组合起来。
因为寄存器较多,所以我们只介绍主要使用到的传感器,其他传感器大家可以慢慢研究(寄存器说明请见LPS22HB数据手册的第9节:Register description)。


  • 设备识别寄存器:WHO_AM_I (0Fh)

可以用来识别芯片的型号。这个寄存器是只读的,输出是 0xB1,也就是十进制的177。


  • 控制寄存器:CTRL_REG1 (10h)

这是最重要的一个寄存器,主要参数都在这里设置

7
        
6
        
5
        
4
        
3
        
2
        
1
        
0
        
0
        
ODR2
        
ODR1
        
ODR0
        
EN_LPFP
        
LPFP_CFG
        
BDU
        
SIM
        


ODR代表采样频率,当ODR=0时,传感器进入掉电模式,设置成其它参数时,传感器就按照指定频率开始采样。
 

EN_LPFP代表使用内部低通滤波器,默认是关闭的。
LPFP_CFG是低通滤波器带宽设置,它需要和EN_LPFP配合使用。
BDU是Block data update的缩小,它代表只有读取输出数据后才更新寄存器
SIM是选择3线/4线SPI方式。


  • 气压寄存器

气压参数由三个寄存器组成,分别是PRESS_OUT_XL (28h)、PRESS_OUT_L (29h)、PRESS_OUT_H (2Ah)

 

气压的计算方法是

    气压 = PRESS_OUT_H·PRESS_OUT_L·PRESS_OUT_XL / 4096

就是将三个寄存器的值组合起来,然后除以4096。如果精度要求不高,也可以只取PRESS_OUT_H和PRESS_OUT_L,然后除以16。气压传感器的精度是±0.1hPa,所以保留一位小数就可以了。

如果希望通过气压计算高度,通常是用查表计算。不过因为气压容易受到温度、湿度、风力等多个条件影响,通过气压计算绝对高度的误差较大,所以通常是测量相对高度(高度变化)。


  • 温度寄存器

温度参数由两个寄存器组成:
 

温度的计算方法是:

    温度 = TEMP_OUT_H·TEMP_OUT_L / 100

考虑到低于0°时是负数,所以需要将这个参数做为有符号数处理。温度传感器的精度是±1.5℃。

如果没有特殊要求,使用上述几个寄存器就可以实现基本的数据采集功能。如果希望进一步降低功耗、改变模式、使用中断、使用FIFO、使用参考值等功能,还需要进一步研究其它寄存器才行。


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驱动,增加功能,使用中断等,大家可以在此基础上进行改进。


附:

© 著作权归作者所有

共有 人打赏支持
shaoziyang
粉丝 11
博文 75
码字总数 25881
作品 0
武汉
程序员
加载中

评论(1)

clouddyy
clouddyy
牛了!
人生苦短,用 RT-Thread MicroPython 享受编程的乐趣

在 i.MX RT1050 EVK 开发板上实现这样的一个读取板载的 6 轴传感器信息和系统 CPU 利用率等信息,实时显示到网页中效果,你猜需要多少代码? 事实上 RT-Thread 的同事利用 RT-Thread MicroP...

RTThread物联网操作系统
05/02
0
0
MicroPython入门:能跑MicroPython开发板大盘点!

相信对于很多刚刚接触MicroPython的DIY创客来说,首先是被一些大神的作品吸引,然后有冲动自己动手,却发现入门很难,特别是面对各式各样的开发板不知如何选择。目前市面上关于MicroPython开...

bodasisiter
07/06
0
0
Micropython TPYBoard拼插编程之按键控制LED灯

一、什么是TPYBoard开发板 TPYBoard是以遵照MIT许可的MicroPython为基础,由TurnipSmart公司制作的一款MicroPython开发板,它基于STM32F405单片机,通过USB接口进行数据传输。该开发板内置4...

bodasisiter
04/18
0
0
[Micropython]TPYBoard v10x拼插编程实验 点亮心形点阵

一、什么是TPYBoard开发板 TPYBoard是以遵照MIT许可的MicroPython为基础,由TurnipSmart公司制作的一款MicroPython开发板,它基于STM32F405单片机,通过USB接口进行数据传输。该开发板内置4...

bodasisiter
04/17
0
0
TPYBoard—MicroPython开发板免费试用!你最想抱走哪款?

  TPYBoard开发板自上市开售以来,受到了广大硬件及MicroPython爱好者的一致好评,许多人提出想试用开发板的申请。为此,TPYBoard特推出多款开发板免费试用活动,感兴趣的朋友抓紧申请吧!...

bodasisiter
2017/10/24
0
0
精彩源于起点——2018年潍坊市首次青少年Python编程公开课

有一种语言叫计算机语言 I want to talk with Computer 春遇到冬,有了岁月 天遇到地,有了永恒 我们拥有的, 不止是长大, 还有那份长大的悲欢经历。 未来会有很多可能, 但一切源于万象开始...

bodasisiter
05/07
0
0
第一款Micropython图形化编辑器—Python Editor

当你看到这篇文章的时候,显然你已经阅读了很多类似的文章,那么本篇文章会深入讲解一些新手的需求,提供详细的建议,当然我不会告诉你具体怎么做,一切答案都是肤浅的。 就拿我自己来说,初...

bodasisiter
04/23
0
0
Micropython之萝卜狗远程门禁控制系统

  * 本文作者:_橙子 ゝ,本文属FreeBuf原创奖励计划,未经许可禁止转载   当代社会,我们进公司需要门禁卡,出入小区需要门禁卡,门禁系统又称出入管理控制系统(ACCESS CONTROL SYSTEM)...

FreeBuf
05/24
0
0
Micropython TurnipBit的蓝牙使用

为了部落 什么是TurnipBit开发板 TurnipBit开发板由TurnipSmart公司制作的一款MicroPython开发板,基于nrf51822芯片为主控芯片,以MKL26Z128VFM4芯片作为边载辅助芯片,板载5*5LED灯,板载加...

bodasisiter
05/19
0
0
Python修炼之路-循序渐进

Python已经演变为一个庞大的生态系统,由于其底层全部用c写成,而且全部开放源码,因此几乎可以完成其它任何编程语言能干的事情。python虽然简单,但要成为高手也非一日之功。如何通过日常的...

openthings
2015/01/21
0
2

没有更多内容

加载失败,请刷新页面

加载更多

下一页

SpringBoot | 第十章:Swagger2的集成和使用

前言 前一章节介绍了mybatisPlus的集成和简单使用,本章节开始接着上一章节的用户表,进行Swagger2的集成。现在都奉行前后端分离开发和微服务大行其道,分微服务及前后端分离后,前后端开发的...

oKong
今天
4
0
Python 最小二乘法 拟合 二次曲线

Python 二次拟合 随机生成数据,并且加上噪声干扰 构造需要拟合的函数形式,使用最小二乘法进行拟合 输出拟合后的参数 将拟合后的函数与原始数据绘图后进行对比 import numpy as npimport...

阿豪boy
今天
1
0
云拿 无人便利店

附近(上海市-航南路)开了家无人便利店.特意进去体验了一下.下面把自己看到的跟大家分享下. 经得现场工作人员同意后拍了几张照片.从外面看是这样.店门口的指导里强调:不要一次扫码多个人进入....

周翔
昨天
1
0
Java设计模式学习之工厂模式

在Java(或者叫做面向对象语言)的世界中,工厂模式被广泛应用于项目中,也许你并没有听说过,不过也许你已经在使用了。 简单来说,工厂模式的出现源于增加程序序的可扩展性,降低耦合度。之...

路小磊
昨天
165
1
npm profile 新功能介绍

转载地址 npm profile 新功能介绍 npm新版本新推来一个功能,npm profile,这个可以更改自己简介信息的命令,以后可以不用去登录网站来修改自己的简介了 具体的这个功能的支持大概是在6这个版...

durban
昨天
1
0
Serial2Ethernet Bi-redirection

Serial Tool Serial Tool is a utility for developing serial communications, custom protocols or device testing. You can set up bytes to send accordingly to your protocol and save......

zungyiu
昨天
1
0
python里求解物理学上的双弹簧质能系统

物理的模型如下: 在这个系统里有两个物体,它们的质量分别是m1和m2,被两个弹簧连接在一起,伸缩系统为k1和k2,左端固定。假定没有外力时,两个弹簧的长度为L1和L2。 由于两物体有重力,那么...

wangxuwei
昨天
0
0
apolloxlua 介绍

##项目介绍 apolloxlua 目前支持javascript到lua的翻译。可以在openresty和luajit里使用。这个工具分为两种模式, 一种是web模式,可以通过网页使用。另外一种是tool模式, 通常作为大规模翻...

钟元OSS
昨天
2
0
Mybatis入门

简介: 定义:Mybatis是一个支持普通SQL查询、存储过程和高级映射的持久层框架。 途径:MyBatis通过XML文件或者注解的形式配置映射,实现数据库查询。 特性:动态SQL语句。 文件结构:Mybat...

霍淇滨
昨天
2
0
开发技术瓶颈期,如何突破

前言 读书、学习的那些事情,以前我也陆续叨叨了不少,但总觉得 “学习方法” 就是一个永远在路上的话题。个人的能力、经验积累与习惯方法不尽相同,而且一篇文章甚至一本书都很难将学习方法...

_小迷糊
昨天
1
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部