Beaglebone Black – 连接 GY-91 MPU9250+BMP280 九轴传感器(2)

这次用 SPI。BBB 有两套 SPI 接口可用,两套都是默认 disable,需要用 overlay 方式启用,即:

echo BB-SPIDEV0 > /sys/devices/bone_capemgr.9/slots

我的 BBB 当前配置当前配置

/opt/source/Userspace-Arduino/overlay/BB-SPI0-01-00A0.dts

/dts-v1/;
/plugin/;

/ {
    compatible = "ti,beaglebone", "ti,beaglebone-black";

    /* identification */
    part-number = "spi0pinmux";

    [email protected] {
        target = <&am33xx_pinmux>;
        __overlay__ {
            spi0_pins_s0: spi0_pins_s0 {
                pinctrl-single,pins = <
                  0x150 0x30  /* spi0_sclk, INPUT_PULLUP | MODE0 */
                  0x154 0x30  /* spi0_d0, INPUT_PULLUP | MODE0 */
                  0x158 0x10  /* spi0_d1, OUTPUT_PULLUP | MODE0 */
                  0x15c 0x10  /* spi0_cs0, OUTPUT_PULLUP | MODE0 */
                >;
            };
        };
    };

    [email protected] {
        target = <&spi0>;
        __overlay__ {
             #address-cells = <1>;
             #size-cells = <0>;

             status = "okay";
             pinctrl-names = "default";
             pinctrl-0 = <&spi0_pins_s0>;

             [email protected] {
                 spi-max-frequency = <24000000>;
                 reg = <0>;
                 compatible = "linux,spidev";
            };
        };
    };
};

说明书第三十页,BMP280 支持 Mode 00 和 11,自动选的。可三线或四线连接。

dts 档内可见,当前就是 mode 0,可以直接连 BMP280了。

SPI 接线 – BMP280

原理图在上一篇博文,从图可以得知,BMP280 的片选是 CSB,NCS 是九轴 MPU9250 的片选引脚。

发黑到接近废掉了的烙铁头给了我合理借口,但也请无视我那烂到掉渣的焊接。SPI0 的接线如下:

BBB GY-91 说明
P9_1 GND
P9_3 3V3 电源
P9_22 SCL 时钟
P9_17 CSB 片选
P9_18 SDA MOSI
P9_21 SA0 MISO

启用 SPI0 :

echo BB-SPIDEV0 > /sys/devices/bone_capemgr.9/slots

然后在 /dev 就会出现了:

spidev1.0 代表总线号 1 片选号 0。

SPI 读取 BMP280 的 ID Register 值

注意事项如上图,图片截取自 BMP280 Datasheet,BMP280 的 SPI Read 方式从 BBB 发出的一个 byte 是 Control byte,Control byte 的 bit 7 用来控制读写,地址是后面余下的 7 个 bits。上图Control byte 后面的 Data byte 是接收的,比如我四线情况,它在 MISO 出现的,上图这样表达比较难理解……。

看一下顺序图,看这个我比较好理解一些:

通讯过程中片选 pull down。

毕竟主角是九轴,BMP280温度气压就快速试一次,试试而已。C 代码。

有好几段小插曲,我首先想用 Python,易写易改,找到了SPIDEV,可是无论如何都无法用双工连,最终由于时间关系,放弃了。PY-SPIDEV见以此链接:

https://pypi.python.org/pypi/spidev 。后来改用 C,各种百度FQ再 Google,多半是 Arduino 和用上 Adfruit 的类库,看到各种 Digital Write,最后也放弃了。最后直接用 linux/spi/spidev 自己参考一下别人就直接写。

#include <stdio.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/spi/spidev.h>

char buf[1];
char buf2[10];

struct spi_ioc_transfer xfer[2];

int spi_init(char filename[40]) {
        int file;
        __u8 mode = 0;
	__u8 lsb = 0;
        __u8 bits = 8;
        __u32 speed = 10000000;

        if ((file = open(filename,O_RDWR)) < 0) {
                printf("Failed to open the bus.");
                exit(1);
        }

        if (ioctl(file, SPI_IOC_WR_MODE, &mode)<0) {
                perror("can‘t set spi mode");
                return;
        }

        if (ioctl(file, SPI_IOC_RD_MODE, &mode) < 0) {
                perror("SPI rd_mode");
                return;
        }
        if (ioctl(file, SPI_IOC_RD_LSB_FIRST, &lsb) < 0) {
                perror("SPI rd_lsb_fist");
                return;
        }
        if (ioctl(file, SPI_IOC_WR_BITS_PER_WORD, &bits)<0) {
                perror("can‘t set bits per word");
                return;
        }
        if (ioctl(file, SPI_IOC_RD_BITS_PER_WORD, &bits) < 0) {
                perror("SPI bits_per_word");
                return;
        }
        if (ioctl(file, SPI_IOC_WR_MAX_SPEED_HZ, &speed)<0) {
                perror("can‘t set max speed hz");
                return;
        }
        if (ioctl(file, SPI_IOC_RD_MAX_SPEED_HZ, &speed) < 0) {
                perror("SPI max_speed_hz");
                return;
        }
        printf("%s: spi mode %d, %d bits %sper word, %d Hz max\n",filename, mode, bits, lsb ? "(lsb first) " : "", speed);
        return file;
}

char * spi_read(int addr,int nbytes,int file) {
        int status;

        memset(buf, 0, sizeof buf);
        memset(buf2, 0, sizeof buf2);
        buf[0] = addr | 128;
        xfer[0].tx_buf = (unsigned long)buf;
        xfer[0].len = 1;
        xfer[1].rx_buf = (unsigned long) buf2;
        xfer[1].len = nbytes;
        status = ioctl(file, SPI_IOC_MESSAGE(2), xfer);
        if (status < 0) {
                perror("SPI_IOC_MESSAGE");
                return;
        }
        return buf2;
}

int main(){
        char * buffer;
        int file=spi_init("/dev/spidev1.0");
        buffer = spi_read(0xD0,1,file);
        printf("0x%x\n",*buffer);
}

代码改自这里:http://linux-sunxi.org/SPIdev,原版是 SUNXI 的,我已修改并去掉些不合理地方。

首先配置, 默认 mode 0 ,8 bit per word 再把速度调到BMP280 的上限 10MHz,然后用 read 方法,两个 xfer 丢进去,一个是写 0xD0,代表我想读取 ID Register,一个是读取的 buf2,返回 buf2 到 main 再打印出来。read 里面的 bitwise OR 是为了确保 bit 7 是 1,以免我敲错地址错误地操作了写入。效果:

返回 0x58,ID 正确。

SPI 写入 BMP280 Register,读取当前温度

对 BMP280 的 ctrl_meas 写入值,连续测量,最高精度,只检测温度不管气压(地址和值见上一篇),然后读取气温原始数据和温度补偿,再转换为摄氏 printf 出来,代码:

#include <stdio.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/spi/spidev.h>

#define DIG_START 0x88
#define TEMP_START 0xFA
#define CTRL_MEAS 0xF4
#define TEMP_ONLY_NORMAL_MODE 0xE3 // 111 000 11

char buf[10];
char buf2[10];

struct spi_ioc_transfer xfer[2];

int spi_init(char filename[40]) {
        int file;
        __u8  lsb = 0;
        __u8 mode = 0;
        __u8 bits = 8;
        __u32 speed = 10000000;

        if ((file = open(filename,O_RDWR)) < 0) {
                printf("Failed to open the bus.");
                exit(1);
        }

        if (ioctl(file, SPI_IOC_WR_MODE, &mode)<0) {
                perror("can‘t set spi mode");
                return;
        }

        if (ioctl(file, SPI_IOC_RD_MODE, &mode) < 0) {
                perror("SPI rd_mode");
                return;
        }
        if (ioctl(file, SPI_IOC_RD_LSB_FIRST, &lsb) < 0) {
                perror("SPI rd_lsb_fist");
                return;
        }
        if (ioctl(file, SPI_IOC_WR_BITS_PER_WORD, &bits)<0) {
                perror("can‘t set bits per word");
                return;
        }
        if (ioctl(file, SPI_IOC_RD_BITS_PER_WORD, &bits) < 0) {
                perror("SPI bits_per_word");
                return;
        }
        if (ioctl(file, SPI_IOC_WR_MAX_SPEED_HZ, &speed)<0) {
                perror("can‘t set max speed hz");
                return;
        }
        if (ioctl(file, SPI_IOC_RD_MAX_SPEED_HZ, &speed) < 0) {
                perror("SPI max_speed_hz");
                return;
        }
        return file;
}

char * spi_read(int addr,int nbytes,int file) {
        int status;

        memset(buf, 0, sizeof buf);
        memset(buf2, 0, sizeof buf2);
        buf[0] = addr | 128;
        xfer[0].tx_buf = (unsigned long)buf;
        xfer[0].len = 1;
        xfer[1].rx_buf = (unsigned long) buf2;
        xfer[1].len = nbytes;
        status = ioctl(file, SPI_IOC_MESSAGE(2), xfer);
        if (status < 0) {
                perror("SPI_IOC_MESSAGE");
                return;
        }
        return buf2;
}

void spi_write(int addr, char value, int file) {
        int status;

        memset(buf, 0, sizeof buf);
        buf[0] = addr & 127;
        buf[1] = value;
        xfer[0].tx_buf = (unsigned long)buf;
        xfer[0].len = 2;
        status = ioctl(file, SPI_IOC_MESSAGE(1), xfer);
        if (status < 0) {
                perror("SPI_IOC_MESSAGE");
        }
}

float myFunc(uint32_t adc_T, unsigned short dig_T1, short dig_T2, short dig_T3){
        uint32_t var1, var2;
        float T;
        var1 = (((double)adc_T)/16384.0-((double)dig_T1)/1024.0)*((double)dig_T2);
        var2 = ((((double)adc_T)/131072.0-((double)dig_T1)/8192.0)*(((double)adc_T)/131072.0-((double)dig_T1)/8192.0))*((double)dig_T2);
        T = (var1+var2)/5120.0;
        return T;
}

int main() {
        int i;
        char * id;
        char * mode;
        char dig_buff[6];
        char tmp_buff[3];

        int file=spi_init("/dev/spidev1.0");
        id = spi_read(0xD0,1,file);
        printf("ID: 0x%02x\n",*id);
        spi_write(CTRL_MEAS,TEMP_ONLY_NORMAL_MODE,file);
        mode = spi_read(CTRL_MEAS,1,file);
        printf("Mode: 0x%02x\n", *mode);

        memcpy(dig_buff, spi_read(DIG_START, 6, file), 6);
        memcpy(tmp_buff,  spi_read(TEMP_START, 3, file), 3);

        printf("Dump:\n");
        for (i=0; i<6; i++){
                printf("%02x ",dig_buff[i]);
        }
        printf("\n");
        for (i=0; i<3; i++){
                printf("%02x ",tmp_buff[i]);
        }
        printf("\n");
        int adc_T = ((tmp_buff[0]<<16)|(tmp_buff[1]<<8)|(tmp_buff[2]))>>4;
        unsigned short dig_T1 = (dig_buff[1]<<8)|(dig_buff[0]);
        short dig_T2 = (dig_buff[3]<<8)|(dig_buff[2]);
        short dig_T3 = (dig_buff[5]<<8)|(dig_buff[4]);
        printf("adc_T is : %d \n", adc_T);
        printf("dig_T1 is : %d \n", dig_T1);
        printf("dig_T2 is : %d \n", dig_T2);
        printf("dig_T3 is : %d \n", dig_T3);
        printf("Temperature is : %f \n", myFunc(adc_T, dig_T1, dig_T2, dig_T3));
        return 0;
}

效果:

关于温度补偿和实际摄氏温度计算,请看上一篇 I2C 示范。spi_write 里面的 bitwise AND 是为了确保 bit 7 是零,写入模式。由于我全部用相同的 buf 和 buf2,实际应用 read 时候需要 copy 出来。

我纠结于尝试用 Python 做 SPI 双工浪费了很多时间,最终还是没结果做不出来。最后放弃那一层层的封装,直接用 C 的 spidev,顿时春天了,28°C。

下一篇,用 SPI 调用加速传感和倾斜角。

时间: 2024-08-25 10:45:27

Beaglebone Black – 连接 GY-91 MPU9250+BMP280 九轴传感器(2)的相关文章

Beaglebone Black &ndash; 连接 GY-91 MPU9250+BMP280 九轴传感器(1)

最初看到淘宝上面有卖 GY91 MPU9250+BMP280 时候,它写着九轴,是九个轴.地球上原来除了 X Y Z 之外,难道还有四五维空间里面的 6 个轴,看清楚后,原来所谓的九轴,是陀螺仪三轴,加速度三轴,磁感应三轴(指南针),才叫做九轴.MPU9250 是 InvenSense 出品,它本身就是一个模块集成了三个东西,还有个自身的温度计(芯片温度),FIFO,Digital Motion Processor(DMP,就是姿势识别,计步器那些),Interrupt(可惜 INT 脚在 GY

陀螺仪以及三轴陀螺仪和六轴陀螺仪的区别_六轴陀螺仪和九轴陀螺仪的区别

来源:电子发烧友 链接:http://www.elecfans.com/article/88/142/2017/20171201590857.html 陀螺仪,是一种用来感测与维持方向的装置,基於角动量不灭的理论设计出来的.陀螺仪主要是由一个位於轴心可以旋转的轮子构成. 陀螺仪一旦开始旋转,由於轮子的角动量,陀螺仪有抗拒方向改变的趋向.陀螺仪多用於导航.定位等系统. 1850年法国的物理学家福柯(J.Foucault)为了研究地球自转,首先发现高速转动中的转子(rotor),由于惯性作用它的旋转

九轴陀螺仪--读接口数据

1.使用i2c链接到树莓派的scl , sda 接口vcc给3v引脚,gnd接树莓派gnd就ok. 2.要操作mpu必须使用mpu的寄存器实现对参数的设定以及读取,取官方下载资料看了一下,在github上找了一个python代码,运行不了bug太多了,然后精简了一下.终于能读出数据了,读出来的数据都是6个字节的,后来发现这哥们用python 读取mpu没有做字节合并,重写了一下,后来发现数据都是整数,不管我怎么旋转数字都是正的,看了网上的一片文章说寄存器度出来的是一个无符号整数.后来想了半天,用

《痞子衡嵌入式半月刊》 第 2 期

痞子衡嵌入式半月刊: 第 2 期 这里分享嵌入式领域有用有趣的项目/工具以及一些热点新闻,农历年分二十四节气,希望在每个交节之日准时发布一期. 本期刊是开源项目(GitHub: JayHeng/pzh-mcu-bi-weekly),欢迎提交 issue,投稿或推荐你知道的嵌入式那些事儿. 上期回顾 :<痞子衡嵌入式半月刊: 第 1 期> 唠两句 如果你第一时间阅读本期,此时应正是立春与雨水交节之时(2020年02月19日 12:56:53).雨水节气标示着降雨开始.雨量渐增,俗话说"

六轴加速度传感器MPU6050官方DMP库到瑞萨RL78/G13的移植

2015年的电赛已经结束了.赛前接到器件清单的时候,看到带防护圈的多旋翼飞行器赫然在列,又给了一个瑞萨RL78/G13的MCU,于是自然联想到13年的电赛,觉得多半是拿RL78/G13做四旋翼的主控,虽然事后证实我的猜测是错的,但是在赛前我还是完成了相关代码的准备,这其中就包括了MPU6050的DMP库移植.在移植前我大概搜了一下,发现网上还没有相关的源代码.一起准备电赛的同学还买过一份RL78/G13的飞控代码,虽然也是使用MPU6050进行姿态获取,但是对MPU6050的读取并不是通过DMP

VR了解

转载自知乎 陆小山ccc & 天天: 按照目前VR的硬件形态来划分,VR头戴设备主要分为三种: 1.移动端头显(俗称手机VR) 2.一体机头显(VR一体机) 3.外接头戴式设备(需要外接主机). 移动端头显也就是所谓的VR眼镜盒子,只要放入手机即可观看.如小宅z4,暴风魔镜等等,相当于纸盒vr的进阶,价格在几十到数百不等. 一体机头显,它具有独立CPU.输入和输出显示功能,完全摆脱外置设备.如现在比较火的大朋vr.一体机形态,算得上是真正意义上的VR独立产品,不受空间约束和其他外部影响,但目前V

蚁视新家show,小伙伴们快来加入有范儿有爱的蚁视大家庭吧!

蚁视搬新家啦.新家在学院路上,离中关村很近,离宇宙的中心很近,离好多同学也很近有木有! 新家绝对是超级创客范儿,绝对理想中的工作环境有木有!愿意加入蚁视的小伙伴们,快快来吧! 下面是蚁视新办公室神秘出场~感觉自己萌萌哒! 进门就是蚁视"ANTVR"的大LOGO 左边的格子里摆满了各种有意思的东西. 有ANTVR KIT的各代原型机,可以随意把玩: 有Google Glass.SONY HMZ.Oculus Rift.Epson Glass等等各种智能头盔和眼镜,各种神器各种膜拜: 有X

BeagleBone Black教程之BeagleBone Black设备的连接

BeagleBone Black教程之BeagleBone Black设备的连接 BeagleBone Black开发前须要准备的材料 经过上面的介绍.相信你已经对BeagleBone有了大致的了解,你须要注意到完毕一件事情可能有多种不同的方式. 所以,依赖于你要完毕的项目,须要的材料也会不同,以下的材料能够助你完毕本书的大部分项目.但不是必须的: q  BeagleBone或者Beaglebone Black q  5V直流适配器 q  网线 q  USB A到mini B线 q  面包板 q

BeagleBone Black项目实训手册(大学霸内部资料)

BeagleBone Black项目实训手册(大学霸内部资料) 介绍:本教程是<BeagleBone Black快速入门教程>的后续教程.本教程以项目操作为主,讲解LED项目.声音项目.传感器项目以及显示项目,并对Beaglebone Black的GPIO.PWM以及I2C等特殊的接口进行详细讲解. 试读下载地址:http://pan.baidu.com/s/1eQozxnG BeagleBone Black项目实训手册 目  录 第1章  准备开始 1 1.1  启动你的Beaglebone