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