MYD-YA157C系列定制板JY901(MPU9250+AK8963)模块适配笔记


 

MYD-YA157C系列定制板JY901(MPU9250+AK8963)模块适配笔记 

Coder-wys  2022.4.13


一、模块简介

JY901模块(MPU9250+AK8963)集成高精度的陀螺仪、 加速度计、 地磁场传感器, 采用高性能的微处理器和先进的动力学解算与卡尔曼动态滤波算法, 能够快速求解出模块当前的实时运动姿态。支持串口和 IIC 两种数字接口。 串口速率2400bps~921600bps 可调, IIC 接口支持全速 400K 速率。该定制板已接入USART3与i2c1,如图所示。本次使用I2C+Python3方式读取传感器数据。

 

 

 由上图可知,我们将JY901陀螺仪对应的VCC、SDA、SCL、GND与YA157C定制板进行连接,对应的管脚分别为I2C1_SCL/PB8,I2C1_SDA/PD13,通过 IIC 接口连接 MCU, 连接方法如下图所示。

注意, 为了能在 IIC总线上面挂接多个模块, 模块的 IIC 总线是开漏输出的, MCU 在连接模块时需要将 IIC 总线通过一个 4.7K 的电阻上拉到 VCC。

注意: VCC 为 3.3V, 要另外接电源供电。 直接用模块上面的电源供电, 可能会产生压降, 使模块实际电压没有 3.3~5V。主要: 单片机内部上拉为弱上拉, 驱动能力有限, 需要硬件上的外部上拉

 二、设备树过程

   正确连接定制板后(出厂默认已连接好),我们需要检测一下该模块是否成功接入。首先我们使用以下命令来检测目前开发板已成功加载的i2c总线

root@myir:~# i2cdetect -l

  终端打印出以下数据:

  由图可知,目前系统已加载两条i2c bus adapter,分别为I2C-0(地址40013000),I2C-1(地址5c002000)

 接下来,我们使用i2cdetect工具检测成功接入i2c的设备

root@myir:~# i2cdetect -y 1 
root@myir:~# i2cdetect -y 0
/*
Usage: i2cdetect [-y] [-a] [-q|-r] I2CBUS [FIRST LAST]
           i2cdetect -F I2CBUS
           i2cdetect -l
    I2CBUS is an integer or an I2C bus name
    If provided, FIRST and LAST limit the probing range.
*/

由上图可知,在i2c-1总线下,在0x28地址,显示UU,代表已被驱动占用。

在i2c-0总线下,在0x44地址,成功挂载i2c设备,经过查询可知,JY-901 模块 IIC 通信速率最大支持 400khz, 从机地址为为7bit, 默认地址为 0x50,本定制板中0x44地址为SHT31温湿度模块,未在0x50地址发现JY-901模块。

经查询核心板前置设备树文件stm32mp151.dtsi可知,STM32MP157核心板的i2c1地址应在i2c@40012000地址加载

接下来,我们需要了解该地址是否已被内核成功加载,需要查询内核启动日志,命令如下:

root@myir:~# dmesg | grep i2c

 由上图可知,该地址未能被内核成功加载,可能存在管脚未在设备树中定义、设备树未正确配置、管脚占用等可能。我们需重新配置设备树

解压厂商提供的镜像文件MYiR-STM32-kernel.tar,设备树文件在MYiR-STM32-kernel/myir-st-linux/arch/arm/boot/dts文件夹中。配置设备树需了解该模块接入了定制板的哪几个管脚,我们通过前面的篇幅得知,JY-901模块通过I2C1_SCL/PB8,I2C1_SDA/PD13与MPU进行连接,查阅STM32MP157A芯片手册,通过搜索关键字“PB8”与“PD13”,经过观察图表得知,

 

    I2C1_SCL/PB8通过AF4、I2C1_SDA/PD13通过AF5与定制板连通。

 

通过观察表格我们还发现,“PB8”、“PD13”可能存在管脚复用情况,接下来,我们查询设备树,看一下是否存在管脚复用。 

在MYiR-STM32-kernel/myir-st-linux/arch/arm/boot/dts文件夹中,打开stm32mp157-ya157c-pinctrl.dtsi,搜索关键字“'B', 8”与“'D', 13,”(去掉最外侧引号,但保留内侧引号),并将其所在行注释,如下图所示:

如上图所示,可能会搜索到多个结果,分别将它们注释掉。然后在/MYiR-STM32-kernel/myir-st-linux/arch/arm/boot/dts/stm32mp15-pinctrl.dtsi中进行相同的注释操作。

以上操作完成之后,我们进行设备树的配置操作:在/MYiR-STM32-kernel/myir-st-linux/arch/arm/boot/dts/stm32mp15-pinctrl.dtsistm32mp157-ya157c-pinctrl.dtsi文件中的相应位置,新增以下内容用以新增配置i2c1_pins_a与i2c1_pins_sleep_a:

/*20220406start*/
			i2c1_pins_a: i2c1-0 {
                pins {
                     pinmux = <STM32_PINMUX('B', 8, AF4)>, /* I2C1_SCL */
                          <STM32_PINMUX('D', 13, AF5)>; /* I2C1_SDA */
                    bias-disable;
                    drive-open-drain;
                    slew-rate = <0>;
                };
            };

            i2c1_pins_sleep_a: i2c1-1 {
                pins {
                     pinmux = <STM32_PINMUX('B', 8, ANALOG)>, /* I2C1_SCL */
                          <STM32_PINMUX('D', 13, ANALOG)>; /* I2C1_SDA */
                };
            };
			/*end*/

  

 

 接下来,在/MYiR-STM32-kernel/myir-st-linux/arch/arm/boot/dts/stm32mp15xx-ya157c.dtsi文件中,在相应位置定义&i2c1,

&i2c1 {
	pinctrl-names = "default", "sleep";
	pinctrl-0 = <&i2c1_pins_a>;
	pinctrl-1 = <&i2c1_pins_sleep_a>;
	i2c-scl-rising-time-ns = <100>;
	i2c-scl-falling-time-ns = <7>;
	status = "okay";
	/delete-property/dmas;
	/delete-property/dma-names;
};

  

 以上操作配置完成后,我们发现在文件最开始,有#include "stm32mp157-m4-srm-pinctrl.dtsi"操作,我们打开这个文件,搜索关键字“m4_i2c1_pins_a”,将下图框选范围内容修改为以下配置:

/*上图框选部分修改为下面的内容*/
	m4_i2c1_pins_a: m4-i2c1-0 {
		pins {
			pinmux = <STM32_PINMUX('B', 8, RSVD)>, /* I2C1_SCL */
				 <STM32_PINMUX('D', 13, RSVD)>; /* I2C1_SDA */
		};
	};

  依次保存刚刚修改过的所有设备树文件。

三、编译并更新kernel

进入内核目录/MYiR-STM32-kernel/myir-st-linux,创建输出文件夹build

PC$ mkdir -p ../build

  加载SDK环境变量

PC$ source /opt/st/myir/3.1-snapshot/environment-setup-cortexa7t2hf-neon-vfpv4-ostl-linux-gnueabi

  配置内核

PC$ make ARCH=arm O="$PWD/../build" myc-ya157c_defconfig
/*注:以上加载默认配置,如需配置内核或者想打开内核某一个驱动功能可进入build目录后执行make menuconfig。

编译内核

PC$ make -j16 ARCH=arm uImage vmlinux dtbs LOADADDR=0xC2000040 O="$PWD/../build"
PC$ -j16 make ARCH=arm modules O="$PWD/../build"
/*以上-j16代表16线程同时编译,请根据实际情况合理分配,使用高性能工作站分配16线程同时编译可在2分钟内结束编译过程,这段时间请耐心等待编译完成。

生成输出文件

PC$ make ARCH=arm INSTALL_MOD_PATH="$PWD/../build/install_artifact" modules_install O="$PWD/../build"
PC$ mkdir -p $PWD/../build/install_artifact/boot/
PC$ cp $PWD/../build/arch/arm/boot/uImage $PWD/../build/install_artifact/boot/
PC$ cp $PWD/../build/arch/arm/boot/dts/st*.dtb $PWD/../build/install_artifact/boot/

更新到开发板(本次采用网络更新,演示ip地址为192.168.31.180)

在生成输出文件下 build/install_artifact/使用 scp 方式传输需要更新的文件。
更新 uImage 与设备树:

PC$ scp -r boot/* root@192.168.31.180:/boot/

  删除编译生成文件 build/install_artifact/lib 下的软连接文件

PC$ rm lib/modules/<kernel version>/source lib/modules/<kernel version>/build

  更新内核 modules:

PC$ scp -r lib/modules/* root@192.168.31.180:/lib/modules/

  使用SSH连接开发板,执行更新

PC$ sync & reboot

  若使用emmc启动,需要将mmcblk2p2挂载到/mnt下,然后把uImage和设备树拷贝到/mnt(以下命令中未展示此部分,自行scp拷贝或者拖到finalshell中对应位置即可),执行sync并重启,命令如下:

root@myir:~# df -h
Filesystem      Size  Used Avail Use% Mounted on
devtmpfs        147M     0  147M   0% /dev
/dev/mmcblk2p4  1.1G  976M   19M  99% /
tmpfs           213M   64K  213M   1% /dev/shm
tmpfs           213M  9.0M  204M   5% /run
tmpfs           213M     0  213M   0% /sys/fs/cgroup
tmpfs           213M     0  213M   0% /tmp
tmpfs           213M  220K  213M   1% /var/volatile
/dev/mmcblk2p3   15M  6.7M  6.8M  50% /vendor
/dev/mmcblk2p5  120M  1.6M  110M   2% /usr/local
tmpfs            43M     0   43M   0% /run/user/0
root@myir:~# mount /dev/mmcblk2p2 /mnt/
root@myir:~# ls /mnt/
boot.scr.uimg  mmc0_extlinux  splash.bmp                      stm32mp157c-ya157c-lcd-v2.dtb  uImage
lost+found     mmc1_extlinux  stm32mp157c-ya157c-hdmi-v2.dtb  stm32mp157c-ya157c-v2.dtb      uInitrd
root@myir:~# sync & reboot

    四、读取JY-901模块数据

使用i2cdetect检测i2c总线情况

root@myir:~# i2cdetect -l

   

 由上图可知,设备树配置完成后,新增了两条总线,i2c1(0x40012000)也能够被系统成功加载了,我们执行如下操作,检测JY-901模块是否加载成功

root@myir:~# i2cdetect -y 0

  

 由上图可以看到,0x50已能够被系统挂载,模块适配成功。查看该地址下的设备寄存器信息:

i2cdump -y 1 0x50

  

 通过查阅JY-901模块说明书得知,模块的 IIC 协议采用寄存器地址访问的方式。 每个地址内的数据均为 16 位数据, 占 2个字节。 寄存器的地址及含义如下表:

地址

符号

含义

0x00

SAVE

保存当前配置

0x01

CALSW

校准

0x02

RSW

回传数据内容

0x03

RATE

回传数据速率

0x04

BAUD

串口波特率

0x05

AXOFFSET

X轴加速度零偏

0x06

AYOFFSET

Y轴加速度零偏

0x07

AZOFFSET

Z轴加速度零偏

0x08

GXOFFSET

X轴角速度零偏

0x09

GYOFFSET

Y轴角速度零偏

0x0a

GZOFFSET

Z轴角速度零偏

0x0b

HXOFFSET

X轴磁场零偏

0x0c

HYOFFSET

Y轴磁场零偏

0x0d

HZOFFSET

Z轴磁场零偏

0x0e

D0MODE

D0模式

0x0f

D1MODE

D1模式

0x10

D2MODE

D2模式

0x11

D3MODE

D3模式

0x12

D0PWMH

D0PWM高电平宽度

0x13

D1PWMH

D1PWM高电平宽度

0x14

D2PWMH

D2PWM高电平宽度

0x15

D3PWMH

D3PWM高电平宽度

0x16

D0PWMT

D0PWM周期

0x17

D1PWMT

D1PWM周期

0x18

D2PWMT

D2PWM周期

0x19

D3PWMT

D3PWM周期

0x1a

IICADDR

IIC地址

0x1b

LEDOFF

关闭LED指示灯

0x1c

GPSBAUD

GPS连接波特率

0x30

YYMM

年、月

0x31

DDHH

日、时

0x32

MMSS

分、秒

0x33

MS

毫秒

0x34

AX

X轴加速度

0x35

AY

Y轴加速度

0x36

AZ

Z轴加速度

0x37

GX

X轴角速度

0x38

GY

Y轴角速度

0x39

GZ

Z轴角速度

0x3a

HX

X轴磁场

0x3b

HY

Y轴磁场

0x3c

HZ

Z轴磁场

0x3d

Roll

X轴角度

0x3e

Pitch

Y轴角度

0x3f

Yaw

Z轴角度

0x40

TEMP

模块温度

0x41

D0Status

端口D0状态

0x42

D1Status

端口D1状态

0x43

D2Status

端口D2状态

0x44

D3Status

端口D3状态

0x45

PressureL

气压低字

0x46

PressureH

气压高字

0x47

HeightL

高度低字

0x48

HeightH

高度高字

0x49

LonL

经度低字

0x4a

LonH

经度高字

0x4b

LatL

纬度低字

0x4c

LatH

纬度高字

0x4d

GPSHeight

GPS高度

0x4e

GPSYaw

GPS航向角

0x4f

GPSVL

GPS地速低字

0x50

GPSVH

GPS地速高字

0x51

Q0

四元素Q0

0x52

Q1

四元素Q1

0x53

Q2

四元素Q2

0x54

Q3

四元素Q3

通过表格可以得知加速度、角速度、角度所在的寄存器地址,接下来我们通过Python3进行JY-901模块的数据读取,代码在CSDN博主的基础上进行修改。

#!/usr/bin/env python3        /*开发板需要加载Python3后才可以使用

import smbus
import time
 
 
class Gyro(object):
 
    def __init__(self, addr):
        self.addr = addr
        self.i2c = smbus.SMBus(0)
 
    def get_acc(self):
        try:
            self.raw_acc_x = self.i2c.read_i2c_block_data(self.addr, 0x34, 2)
            self.raw_acc_y = self.i2c.read_i2c_block_data(self.addr, 0x35, 2)
            self.raw_acc_z = self.i2c.read_i2c_block_data(self.addr, 0x36, 2)
        except IOError:
            print("ReadError: gyro_acc")
            return (0, 0, 0)
        else:
            self.k_acc = 16 * 9.8
 
            self.acc_x = (self.raw_acc_x[1] << 8 | self.raw_acc_x[0]) / 32768 * self.k_acc
            self.acc_y = (self.raw_acc_y[1] << 8 | self.raw_acc_y[0]) / 32768 * self.k_acc
            self.acc_z = (self.raw_acc_z[1] << 8 | self.raw_acc_z[0]) / 32768 * self.k_acc
 
            if self.acc_x >= self.k_acc:
                self.acc_x -= 2 * self.k_acc 
        
            if self.acc_y >= self.k_acc:
               self.acc_y -= 2 * self.k_acc
                
            if self.acc_z >= self.k_acc:
                self.acc_z -= 2 * self.k_acc
 
            return (self.acc_x, self.acc_y, self.acc_z)
 
    def get_gyro(self):
        try:
            self.raw_gyro_x = self.i2c.read_i2c_block_data(self.addr, 0x37, 2)
            self.raw_gyro_y = self.i2c.read_i2c_block_data(self.addr, 0x38, 2)
            self.raw_gyro_z = self.i2c.read_i2c_block_data(self.addr, 0x39, 2)
        except IOError:
            print("ReadError: gyro_gyro")
            return (0, 0, 0)
        else:
            self.k_gyro = 2000
 
            self.gyro_x = (self.raw_gyro_x[1] << 8 | self.raw_gyro_x[0]) / 32768 * self.k_gyro
            self.gyro_y = (self.raw_gyro_y[1] << 8 | self.raw_gyro_y[0]) / 32768 * self.k_gyro
            self.gyro_z = (self.raw_gyro_z[1] << 8 | self.raw_gyro_z[0]) / 32768 * self.k_gyro
 
            if self.gyro_x >= self.k_gyro:
                self.gyro_x -= 2 * self.k_gyro
        
            if self.gyro_y >= self.k_gyro:
               self.gyro_y -= 2 * self.k_gyro
                
            if self.gyro_z >= self.k_gyro:
                self.gyro_z -= 2 * self.k_gyro
 
            return (self.gyro_x, self.gyro_y, self.gyro_z)
         
 
    def get_angle(self):
        try:
            self.raw_angle_x = self.i2c.read_i2c_block_data(self.addr, 0x3d, 2)
            self.raw_angle_y = self.i2c.read_i2c_block_data(self.addr, 0x3e, 2)
            self.raw_angle_z = self.i2c.read_i2c_block_data(self.addr, 0x3f, 2)
        except IOError:
            print("ReadError: gyro_angle")
            return (0, 0, 0)
        else:
            self.k_angle = 180
 
            self.angle_x = (self.raw_angle_x[1] << 8 | self.raw_angle_x[0]) / 32768 * self.k_angle
            self.angle_y = (self.raw_angle_y[1] << 8 | self.raw_angle_y[0]) / 32768 * self.k_angle
            self.angle_z = (self.raw_angle_z[1] << 8 | self.raw_angle_z[0]) / 32768 * self.k_angle
 
            if self.angle_x >= self.k_angle:
                self.angle_x -= 2 * self.k_angle
        
            if self.angle_y >= self.k_angle:
                self.angle_y -= 2 * self.k_angle
                
            if self.angle_z >= self.k_angle:
                self.angle_z -= 2 * self.k_angle
 
            return (self.angle_x, self.angle_y, self.angle_z)
 
def main():
    head_gyro = Gyro(0x50)
    while(True):
        print(" Acc:  " + repr(head_gyro.get_acc()) + "\n", "Gyro: " + repr(head_gyro.get_gyro()) + "\n", "Angle:" + repr(head_gyro.get_angle()) + "\n")
        time.sleep(0.2)
 
main()

  需要注意的是,开发板需要加载Python3后才可以使用,需在文件开头增加#!/usr/bin/env python3,将以上代码复制保存为*.py文件后chmod 777授予可执行权限。输出的数据中acc代表加速度传感器,gyro代表陀螺仪,angle代表角度传感器。

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM