带 STM32 的 MPU9250 - 磁力计读数恒定
MPU9250 with STM32 - Magnetometer readings are constant
尝试将 MPU9250 IMU 传感器与我的 STM32G431RB NUCLEO 板一起使用。 IMU 的加速度计和陀螺仪工作正常。然而,磁力计给出恒定值。
我认为磁力计电源模式设置为只读一次或关闭。但是,我没明白是什么问题。
在 GitHub,我尝试实现 kriswiner 为 Arduino 编写的 MPU9250 库。但是不知道成功了还是失败了
我实现的原代码:
https://github.com/kriswiner/MPU9250/blob/master/MPU9250BasicAHRS.ino
我为STM32写的CubeIDE代码,用于配置和读取磁力计:
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "i2c.h"
#include "usart.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <stdio.h>
#include <math.h>
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
//Magnetometer Registers
#define AK8963_ADDRESS 0x0C<<1
#define AK8963_WHO_AM_I 0x00 // should return 0x48
#define AK8963_INFO 0x01
#define AK8963_ST1 0x02 // data ready status bit 0
#define AK8963_XOUT_L 0x03 // data
#define AK8963_XOUT_H 0x04
#define AK8963_YOUT_L 0x05
#define AK8963_YOUT_H 0x06
#define AK8963_ZOUT_L 0x07
#define AK8963_ZOUT_H 0x08
#define AK8963_ST2 0x09 // Data overflow bit 3 and data read error status bit 2
#define AK8963_CNTL 0x0A // Power down (0000), single-measurement (0001), self-test (1000) and Fuse ROM (1111) modes on bits 3:0
#define AK8963_ASTC 0x0C // Self test control
#define AK8963_I2CDIS 0x0F // I2C disable
#define AK8963_ASAX 0x10 // Fuse ROM x-axis sensitivity adjustment value
#define AK8963_ASAY 0x11 // Fuse ROM y-axis sensitivity adjustment value
#define AK8963_ASAZ 0x12 // Fuse ROM z-axis sensitivity adjustment value
#define WHO_AM_I_MPU9250 0x75 // Should return 0x71
#define MPU9250_ADDRESS 0x68<<1
#define INT_PIN_CFG 0x37
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
enum Mscale {
MFS_14BITS = 0, // 0.6 mG per LSB
MFS_16BITS // 0.15 mG per LSB
};
uint8_t Mscale = MFS_16BITS; // Choose either 14-bit or 16-bit magnetometer resolution
uint8_t Mmode = 0x02;
const uint16_t i2c_timeoutB = 100;
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
int _write(int file, char *ptr, int len)
{
HAL_UART_Transmit(&hlpuart1, (uint8_t*)ptr, len, HAL_MAX_DELAY);
return len;
}
float MagnetoX;
float MagnetoY;
float MagnetoZ;
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_LPUART1_UART_Init();
MX_I2C3_Init();
MX_I2C2_Init();
/* USER CODE BEGIN 2 */
printf("**************************** \r\n");
printf("MPU9250 STM32 Implementation \r\n");
printf("**************************** \r\n");
//pre-def. vars
uint8_t readData;
uint8_t writeData;
// Check MPU and Mag---------------------------------------------------------------------------------------------------
//read MPU9255 WHOAMI
HAL_I2C_Mem_Read(&hi2c2, MPU9250_ADDRESS, WHO_AM_I_MPU9250, 1, &readData, 1, i2c_timeoutB);
printf("MPU WHO AM I is (Must return 113): %d\r\n", readData);
//enable Mag bypass
writeData = 0x22;
HAL_I2C_Mem_Write(&hi2c2, MPU9250_ADDRESS, INT_PIN_CFG, 1, &writeData, 1, i2c_timeoutB);
//read AK8963 WHOAMI
HAL_I2C_Mem_Read(&hi2c2, AK8963_ADDRESS, AK8963_WHO_AM_I, 1, &readData, 1, i2c_timeoutB);
printf("MAG WHO AM I is (Must return 72): %d\r\n", readData);
printf("------------------------------------------------\r\n");
//Init Mag-------------------------------------------------------------------------------------------------------------
//Power down magnetometer
writeData = 0x00;
HAL_I2C_Mem_Write(&hi2c2, AK8963_ADDRESS, AK8963_CNTL, 1, &writeData, 1, i2c_timeoutB);
HAL_Delay(100);
//Enter Fuse ROM access mode
writeData = 0x0F;
HAL_I2C_Mem_Write(&hi2c2, AK8963_ADDRESS, AK8963_CNTL, 1, &writeData, 1, i2c_timeoutB);
HAL_Delay(100);
//Read the x-, y-, and z-axis calibration values
uint8_t rawMagCalData[3];
HAL_I2C_Mem_Read(&hi2c2, AK8963_ADDRESS, AK8963_ASAX, 1, &rawMagCalData[0], 3, i2c_timeoutB);
float calMagX = (float)(rawMagCalData[0] - 128)/256. + 1.; // Return x-axis sensitivity adjustment values, etc.
float calMagY = (float)(rawMagCalData[1] - 128)/256. + 1.;
float calMagZ = (float)(rawMagCalData[2] - 128)/256. + 1.;
printf("Mag cal off X: %f\r\n", calMagX);
printf("Mag cal off Y: %f\r\n", calMagY);
printf("Mag cal off Z: %f\r\n", calMagZ);
HAL_Delay(100);
//Set magnetometer data resolution and sample ODR
writeData = Mscale << 4 | Mmode;
printf("writeData: %d\r\n", writeData);
HAL_I2C_Mem_Write(&hi2c2, AK8963_ADDRESS, AK8963_CNTL, 1, &writeData, 1, i2c_timeoutB);
HAL_Delay(100);
printf("------------------------------------------------\r\n");
//Read Mag-------------------------------------------------------------------------------------------------------------
//Read Mag data
uint8_t rawMagData[6];
HAL_I2C_Mem_Read(&hi2c2, AK8963_ADDRESS, AK8963_XOUT_L, 1, &rawMagData[0], 6, i2c_timeoutB);
float MagX = ((int16_t)rawMagData[1] << 8) | rawMagData[0];
float MagY = ((int16_t)rawMagData[3] << 8) | rawMagData[2];
float MagZ = ((int16_t)rawMagData[5] << 8) | rawMagData[4];
printf("Mag X: %f\r\n", MagX);
printf("Mag Y: %f\r\n", MagY);
printf("Mag Z: %f\r\n", MagZ);
HAL_Delay(100);
printf("------------------------------------------------\r\n");
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
//Read Mag data
uint8_t rawMagData[6];
HAL_I2C_Mem_Read(&hi2c2, AK8963_ADDRESS, AK8963_XOUT_L, 1, &rawMagData[0], 6, i2c_timeoutB);
MagX = ((int16_t)rawMagData[1] << 8) | rawMagData[0];
MagY = ((int16_t)rawMagData[3] << 8) | rawMagData[2];
MagZ = ((int16_t)rawMagData[5] << 8) | rawMagData[4];
//Print to Com port via STLINK
printf("Mag X: %f\r\n", MagX);
printf("Mag Y: %f\r\n", MagY);
printf("Mag Z: %f\r\n", MagZ);
printf("------------------------------------------------\r\n");
HAL_Delay(1000);
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
用于读数的 UART 选项卡:
MPU9250 和 Mag. 的 WHOAMI 读数很好。
从磁力计读取 ST2
寄存器,以便在每次读取测量寄存器后更新数据。
来自datasheet:
When any of measurement data register (HXL ~ HZH) or ST2 register is
read, AK8963 judges that data reading is started.
...
When ST2 register is read, AK8963 judges that data reading is
finished. Stored measurement data is protected during data reading and
data is not updated.
为避免接收到不完整或重复的数据,您应该执行上述数据表第 6.4.3.2. Normal Read Sequence
部分中指定的正确读取顺序
当我进一步研究代码时,我发现我犯了一些严重的错误。
我post这个问题后,看到一篇关于从MPU9250读取数据的文章。
https://longnight975551865.wordpress.com/2018/02/11/how-to-read-data-from-mpu9250/
虽然是写给Arduino的文章,但是很好的解释了MPU9250的寄存器操作,特别是电源管理。我看到我错误地配置了AK8963(磁力计),我意识到我试图错误地从AK8963读取数据。
我会逐步解释我的错误和解决方法。
1)
访问FUSE ROM后,我应该重置AK8963但我没有重置就继续了。
在写入数据分辨率和样本 ODR 值之前,我们需要通过在 AK8963_CNTL 寄存器中键入 0x00 来重置电源模式。
//Power down magnetometer
writeData = 0x00;
HAL_I2C_Mem_Write(&hi2c2, AK8963_ADDRESS, AK8963_CNTL, 1, &writeData, 1, i2c_timeoutB);
HAL_Delay(100);
此外,在我分享的代码中,我试图在初始化磁力计后立即读取数据,但这也是一种不准确的方法,因为读取数据的方式与加速度计或陀螺仪不同.这也是我删除这部分的原因。
3)
磁力计的数据读取过程一定要准确。首先需要读取寄存器AK8963_ST1并使能数据更新
HAL_I2C_Mem_Read(&hi2c2, AK8963_ADDRESS, AK8963_ST1, 1, &readData, 1, i2c_timeoutB);
printf("Read Data: %d\r\n", readData);
printf("Read Data2: %d\r\n", (readData & 0x01));
if( (readData & 0x01) == 0x01 ){
//Read Mag data
uint8_t rawMagData[7];
HAL_I2C_Mem_Read(&hi2c2, AK8963_ADDRESS, AK8963_XOUT_L, 1, &rawMagData[0], 7, i2c_timeoutB);
uint8_t c = rawMagData[6];
if(!(c & 0x08)) {
MagX = ((int16_t)rawMagData[1] << 8) | rawMagData[0];
MagY = ((int16_t)rawMagData[3] << 8) | rawMagData[2];
MagZ = ((int16_t)rawMagData[5] << 8) | rawMagData[4];
//Print to Com port via STLINK
printf("Mag X: %f\r\n", MagX);
printf("Mag Y: %f\r\n", MagY);
printf("Mag Z: %f\r\n", MagZ);
printf("------------------------------------------------\r\n");
}
}
else {
printf("No Data? \r\n");
printf("------------------------------------------------\r\n");
}
HAL_Delay(500);
新的、更新的代码:
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "i2c.h"
#include "usart.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <stdio.h>
#include <math.h>
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
//Magnetometer Registers
#define AK8963_ADDRESS 0x0C<<1
#define AK8963_WHO_AM_I 0x00 // should return 0x48
#define AK8963_INFO 0x01
#define AK8963_ST1 0x02 // data ready status bit 0
#define AK8963_XOUT_L 0x03 // data
#define AK8963_XOUT_H 0x04
#define AK8963_YOUT_L 0x05
#define AK8963_YOUT_H 0x06
#define AK8963_ZOUT_L 0x07
#define AK8963_ZOUT_H 0x08
#define AK8963_ST2 0x09 // Data overflow bit 3 and data read error status bit 2
#define AK8963_CNTL 0x0A // Power down (0000), single-measurement (0001), self-test (1000) and Fuse ROM (1111) modes on bits 3:0
#define AK8963_ASTC 0x0C // Self test control
#define AK8963_I2CDIS 0x0F // I2C disable
#define AK8963_ASAX 0x10 // Fuse ROM x-axis sensitivity adjustment value
#define AK8963_ASAY 0x11 // Fuse ROM y-axis sensitivity adjustment value
#define AK8963_ASAZ 0x12 // Fuse ROM z-axis sensitivity adjustment value
#define WHO_AM_I_MPU9250 0x75 // Should return 0x71
#define MPU9250_ADDRESS 0x68<<1
#define INT_PIN_CFG 0x37
#define DATA_READY_MASK 0x01
#define MAGIC_OVERFLOW_MASK 0x8
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
float MagX;
float MagY;
float MagZ;
/* USER CODE BEGIN PV */
enum Mscale {
MFS_14BITS = 0, // 0.6 mG per LSB
MFS_16BITS // 0.15 mG per LSB
};
uint8_t Mscale = MFS_16BITS; // Choose either 14-bit or 16-bit magnetometer resolution
uint8_t Mmode = 0x02;
const uint16_t i2c_timeoutB = 100;
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
int _write(int file, char *ptr, int len)
{
HAL_UART_Transmit(&hlpuart1, (uint8_t*)ptr, len, HAL_MAX_DELAY);
return len;
}
float MagnetoX;
float MagnetoY;
float MagnetoZ;
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_LPUART1_UART_Init();
MX_I2C3_Init();
MX_I2C2_Init();
/* USER CODE BEGIN 2 */
printf("**************************** \r\n");
printf("MPU9250 STM32 Implementation \r\n");
printf("**************************** \r\n");
//pre-def. vars
uint8_t readData;
uint8_t writeData;
// Check MPU and Mag---------------------------------------------------------------------------------------------------
//read MPU9255 WHOAMI
HAL_I2C_Mem_Read(&hi2c2, MPU9250_ADDRESS, WHO_AM_I_MPU9250, 1, &readData, 1, i2c_timeoutB);
printf("MPU WHO AM I is (Must return 113): %d\r\n", readData);
//enable Mag bypass
writeData = 0x22;
HAL_I2C_Mem_Write(&hi2c2, MPU9250_ADDRESS, INT_PIN_CFG, 1, &writeData, 1, i2c_timeoutB);
//read AK8963 WHOAMI
HAL_I2C_Mem_Read(&hi2c2, AK8963_ADDRESS, AK8963_WHO_AM_I, 1, &readData, 1, i2c_timeoutB);
printf("MAG WHO AM I is (Must return 72): %d\r\n", readData);
printf("------------------------------------------------\r\n");
//Init Mag-------------------------------------------------------------------------------------------------------------
//Power down magnetometer
writeData = 0x00;
HAL_I2C_Mem_Write(&hi2c2, AK8963_ADDRESS, AK8963_CNTL, 1, &writeData, 1, i2c_timeoutB);
HAL_Delay(100);
//Enter Fuse ROM access mode
writeData = 0x0F;
HAL_I2C_Mem_Write(&hi2c2, AK8963_ADDRESS, AK8963_CNTL, 1, &writeData, 1, i2c_timeoutB);
HAL_Delay(100);
//Read the x-, y-, and z-axis calibration values
uint8_t rawMagCalData[3];
HAL_I2C_Mem_Read(&hi2c2, AK8963_ADDRESS, AK8963_ASAX, 1, &rawMagCalData[0], 3, i2c_timeoutB);
float calMagX = (float)(rawMagCalData[0] - 128)/256. + 1.; // Return x-axis sensitivity adjustment values, etc.
float calMagY = (float)(rawMagCalData[1] - 128)/256. + 1.;
float calMagZ = (float)(rawMagCalData[2] - 128)/256. + 1.;
printf("Mag cal off X: %f\r\n", calMagX);
printf("Mag cal off Y: %f\r\n", calMagY);
printf("Mag cal off Z: %f\r\n", calMagZ);
HAL_Delay(100);
//Power down magnetometer
writeData = 0x00;
HAL_I2C_Mem_Write(&hi2c2, AK8963_ADDRESS, AK8963_CNTL, 1, &writeData, 1, i2c_timeoutB);
HAL_Delay(100);
//Set magnetometer data resolution and sample ODR
writeData = Mscale << 4 | 0x02;
//writeData = 0x16;
printf("writeData: %d\r\n", writeData);
HAL_I2C_Mem_Write(&hi2c2, AK8963_ADDRESS, AK8963_CNTL, 1, &writeData, 1, i2c_timeoutB);
HAL_Delay(100);
printf("------------------------------------------------\r\n");
/*
//Read Mag-------------------------------------------------------------------------------------------------------------
//Read Mag data
uint8_t rawMagData[6];
HAL_I2C_Mem_Read(&hi2c2, AK8963_ADDRESS, AK8963_XOUT_L, 1, &rawMagData[0], 6, i2c_timeoutB);
MagX = ((int16_t)rawMagData[1] << 8) | rawMagData[0];
MagY = ((int16_t)rawMagData[3] << 8) | rawMagData[2];
MagZ = ((int16_t)rawMagData[5] << 8) | rawMagData[4];
printf("Mag X: %f\r\n", MagX);
printf("Mag Y: %f\r\n", MagY);
printf("Mag Z: %f\r\n", MagZ);
HAL_Delay(100);
printf("------------------------------------------------\r\n");
*/
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
HAL_I2C_Mem_Read(&hi2c2, AK8963_ADDRESS, AK8963_ST1, 1, &readData, 1, i2c_timeoutB);
printf("Read Data: %d\r\n", readData);
printf("Read Data2: %d\r\n", (readData & 0x01));
if( (readData & 0x01) == 0x01 ){
//Read Mag data
uint8_t rawMagData[7];
HAL_I2C_Mem_Read(&hi2c2, AK8963_ADDRESS, AK8963_XOUT_L, 1, &rawMagData[0], 7, i2c_timeoutB);
uint8_t c = rawMagData[6];
if(!(c & 0x08)) {
MagX = ((int16_t)rawMagData[1] << 8) | rawMagData[0];
MagY = ((int16_t)rawMagData[3] << 8) | rawMagData[2];
MagZ = ((int16_t)rawMagData[5] << 8) | rawMagData[4];
//Print to Com port via STLINK
printf("Mag X: %f\r\n", MagX);
printf("Mag Y: %f\r\n", MagY);
printf("Mag Z: %f\r\n", MagZ);
printf("------------------------------------------------\r\n");
}
}
else {
printf("No Data? \r\n");
printf("------------------------------------------------\r\n");
}
HAL_Delay(500);
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
这是一个简单的错误,因为我没有真正阅读数据表。我希望这对遇到同样错误的人来说是一个很好的资源。 :)
尝试将 MPU9250 IMU 传感器与我的 STM32G431RB NUCLEO 板一起使用。 IMU 的加速度计和陀螺仪工作正常。然而,磁力计给出恒定值。
我认为磁力计电源模式设置为只读一次或关闭。但是,我没明白是什么问题。
在 GitHub,我尝试实现 kriswiner 为 Arduino 编写的 MPU9250 库。但是不知道成功了还是失败了
我实现的原代码:
https://github.com/kriswiner/MPU9250/blob/master/MPU9250BasicAHRS.ino
我为STM32写的CubeIDE代码,用于配置和读取磁力计:
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "i2c.h"
#include "usart.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <stdio.h>
#include <math.h>
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
//Magnetometer Registers
#define AK8963_ADDRESS 0x0C<<1
#define AK8963_WHO_AM_I 0x00 // should return 0x48
#define AK8963_INFO 0x01
#define AK8963_ST1 0x02 // data ready status bit 0
#define AK8963_XOUT_L 0x03 // data
#define AK8963_XOUT_H 0x04
#define AK8963_YOUT_L 0x05
#define AK8963_YOUT_H 0x06
#define AK8963_ZOUT_L 0x07
#define AK8963_ZOUT_H 0x08
#define AK8963_ST2 0x09 // Data overflow bit 3 and data read error status bit 2
#define AK8963_CNTL 0x0A // Power down (0000), single-measurement (0001), self-test (1000) and Fuse ROM (1111) modes on bits 3:0
#define AK8963_ASTC 0x0C // Self test control
#define AK8963_I2CDIS 0x0F // I2C disable
#define AK8963_ASAX 0x10 // Fuse ROM x-axis sensitivity adjustment value
#define AK8963_ASAY 0x11 // Fuse ROM y-axis sensitivity adjustment value
#define AK8963_ASAZ 0x12 // Fuse ROM z-axis sensitivity adjustment value
#define WHO_AM_I_MPU9250 0x75 // Should return 0x71
#define MPU9250_ADDRESS 0x68<<1
#define INT_PIN_CFG 0x37
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
enum Mscale {
MFS_14BITS = 0, // 0.6 mG per LSB
MFS_16BITS // 0.15 mG per LSB
};
uint8_t Mscale = MFS_16BITS; // Choose either 14-bit or 16-bit magnetometer resolution
uint8_t Mmode = 0x02;
const uint16_t i2c_timeoutB = 100;
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
int _write(int file, char *ptr, int len)
{
HAL_UART_Transmit(&hlpuart1, (uint8_t*)ptr, len, HAL_MAX_DELAY);
return len;
}
float MagnetoX;
float MagnetoY;
float MagnetoZ;
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_LPUART1_UART_Init();
MX_I2C3_Init();
MX_I2C2_Init();
/* USER CODE BEGIN 2 */
printf("**************************** \r\n");
printf("MPU9250 STM32 Implementation \r\n");
printf("**************************** \r\n");
//pre-def. vars
uint8_t readData;
uint8_t writeData;
// Check MPU and Mag---------------------------------------------------------------------------------------------------
//read MPU9255 WHOAMI
HAL_I2C_Mem_Read(&hi2c2, MPU9250_ADDRESS, WHO_AM_I_MPU9250, 1, &readData, 1, i2c_timeoutB);
printf("MPU WHO AM I is (Must return 113): %d\r\n", readData);
//enable Mag bypass
writeData = 0x22;
HAL_I2C_Mem_Write(&hi2c2, MPU9250_ADDRESS, INT_PIN_CFG, 1, &writeData, 1, i2c_timeoutB);
//read AK8963 WHOAMI
HAL_I2C_Mem_Read(&hi2c2, AK8963_ADDRESS, AK8963_WHO_AM_I, 1, &readData, 1, i2c_timeoutB);
printf("MAG WHO AM I is (Must return 72): %d\r\n", readData);
printf("------------------------------------------------\r\n");
//Init Mag-------------------------------------------------------------------------------------------------------------
//Power down magnetometer
writeData = 0x00;
HAL_I2C_Mem_Write(&hi2c2, AK8963_ADDRESS, AK8963_CNTL, 1, &writeData, 1, i2c_timeoutB);
HAL_Delay(100);
//Enter Fuse ROM access mode
writeData = 0x0F;
HAL_I2C_Mem_Write(&hi2c2, AK8963_ADDRESS, AK8963_CNTL, 1, &writeData, 1, i2c_timeoutB);
HAL_Delay(100);
//Read the x-, y-, and z-axis calibration values
uint8_t rawMagCalData[3];
HAL_I2C_Mem_Read(&hi2c2, AK8963_ADDRESS, AK8963_ASAX, 1, &rawMagCalData[0], 3, i2c_timeoutB);
float calMagX = (float)(rawMagCalData[0] - 128)/256. + 1.; // Return x-axis sensitivity adjustment values, etc.
float calMagY = (float)(rawMagCalData[1] - 128)/256. + 1.;
float calMagZ = (float)(rawMagCalData[2] - 128)/256. + 1.;
printf("Mag cal off X: %f\r\n", calMagX);
printf("Mag cal off Y: %f\r\n", calMagY);
printf("Mag cal off Z: %f\r\n", calMagZ);
HAL_Delay(100);
//Set magnetometer data resolution and sample ODR
writeData = Mscale << 4 | Mmode;
printf("writeData: %d\r\n", writeData);
HAL_I2C_Mem_Write(&hi2c2, AK8963_ADDRESS, AK8963_CNTL, 1, &writeData, 1, i2c_timeoutB);
HAL_Delay(100);
printf("------------------------------------------------\r\n");
//Read Mag-------------------------------------------------------------------------------------------------------------
//Read Mag data
uint8_t rawMagData[6];
HAL_I2C_Mem_Read(&hi2c2, AK8963_ADDRESS, AK8963_XOUT_L, 1, &rawMagData[0], 6, i2c_timeoutB);
float MagX = ((int16_t)rawMagData[1] << 8) | rawMagData[0];
float MagY = ((int16_t)rawMagData[3] << 8) | rawMagData[2];
float MagZ = ((int16_t)rawMagData[5] << 8) | rawMagData[4];
printf("Mag X: %f\r\n", MagX);
printf("Mag Y: %f\r\n", MagY);
printf("Mag Z: %f\r\n", MagZ);
HAL_Delay(100);
printf("------------------------------------------------\r\n");
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
//Read Mag data
uint8_t rawMagData[6];
HAL_I2C_Mem_Read(&hi2c2, AK8963_ADDRESS, AK8963_XOUT_L, 1, &rawMagData[0], 6, i2c_timeoutB);
MagX = ((int16_t)rawMagData[1] << 8) | rawMagData[0];
MagY = ((int16_t)rawMagData[3] << 8) | rawMagData[2];
MagZ = ((int16_t)rawMagData[5] << 8) | rawMagData[4];
//Print to Com port via STLINK
printf("Mag X: %f\r\n", MagX);
printf("Mag Y: %f\r\n", MagY);
printf("Mag Z: %f\r\n", MagZ);
printf("------------------------------------------------\r\n");
HAL_Delay(1000);
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
用于读数的 UART 选项卡:
MPU9250 和 Mag. 的 WHOAMI 读数很好。
从磁力计读取 ST2
寄存器,以便在每次读取测量寄存器后更新数据。
来自datasheet:
When any of measurement data register (HXL ~ HZH) or ST2 register is read, AK8963 judges that data reading is started.
...
When ST2 register is read, AK8963 judges that data reading is finished. Stored measurement data is protected during data reading and data is not updated.
为避免接收到不完整或重复的数据,您应该执行上述数据表第 6.4.3.2. Normal Read Sequence
当我进一步研究代码时,我发现我犯了一些严重的错误。
我post这个问题后,看到一篇关于从MPU9250读取数据的文章。
https://longnight975551865.wordpress.com/2018/02/11/how-to-read-data-from-mpu9250/
虽然是写给Arduino的文章,但是很好的解释了MPU9250的寄存器操作,特别是电源管理。我看到我错误地配置了AK8963(磁力计),我意识到我试图错误地从AK8963读取数据。
我会逐步解释我的错误和解决方法。
1)
访问FUSE ROM后,我应该重置AK8963但我没有重置就继续了。
在写入数据分辨率和样本 ODR 值之前,我们需要通过在 AK8963_CNTL 寄存器中键入 0x00 来重置电源模式。
//Power down magnetometer
writeData = 0x00;
HAL_I2C_Mem_Write(&hi2c2, AK8963_ADDRESS, AK8963_CNTL, 1, &writeData, 1, i2c_timeoutB);
HAL_Delay(100);
此外,在我分享的代码中,我试图在初始化磁力计后立即读取数据,但这也是一种不准确的方法,因为读取数据的方式与加速度计或陀螺仪不同.这也是我删除这部分的原因。
3)
磁力计的数据读取过程一定要准确。首先需要读取寄存器AK8963_ST1并使能数据更新
HAL_I2C_Mem_Read(&hi2c2, AK8963_ADDRESS, AK8963_ST1, 1, &readData, 1, i2c_timeoutB);
printf("Read Data: %d\r\n", readData);
printf("Read Data2: %d\r\n", (readData & 0x01));
if( (readData & 0x01) == 0x01 ){
//Read Mag data
uint8_t rawMagData[7];
HAL_I2C_Mem_Read(&hi2c2, AK8963_ADDRESS, AK8963_XOUT_L, 1, &rawMagData[0], 7, i2c_timeoutB);
uint8_t c = rawMagData[6];
if(!(c & 0x08)) {
MagX = ((int16_t)rawMagData[1] << 8) | rawMagData[0];
MagY = ((int16_t)rawMagData[3] << 8) | rawMagData[2];
MagZ = ((int16_t)rawMagData[5] << 8) | rawMagData[4];
//Print to Com port via STLINK
printf("Mag X: %f\r\n", MagX);
printf("Mag Y: %f\r\n", MagY);
printf("Mag Z: %f\r\n", MagZ);
printf("------------------------------------------------\r\n");
}
}
else {
printf("No Data? \r\n");
printf("------------------------------------------------\r\n");
}
HAL_Delay(500);
新的、更新的代码:
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "i2c.h"
#include "usart.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <stdio.h>
#include <math.h>
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
//Magnetometer Registers
#define AK8963_ADDRESS 0x0C<<1
#define AK8963_WHO_AM_I 0x00 // should return 0x48
#define AK8963_INFO 0x01
#define AK8963_ST1 0x02 // data ready status bit 0
#define AK8963_XOUT_L 0x03 // data
#define AK8963_XOUT_H 0x04
#define AK8963_YOUT_L 0x05
#define AK8963_YOUT_H 0x06
#define AK8963_ZOUT_L 0x07
#define AK8963_ZOUT_H 0x08
#define AK8963_ST2 0x09 // Data overflow bit 3 and data read error status bit 2
#define AK8963_CNTL 0x0A // Power down (0000), single-measurement (0001), self-test (1000) and Fuse ROM (1111) modes on bits 3:0
#define AK8963_ASTC 0x0C // Self test control
#define AK8963_I2CDIS 0x0F // I2C disable
#define AK8963_ASAX 0x10 // Fuse ROM x-axis sensitivity adjustment value
#define AK8963_ASAY 0x11 // Fuse ROM y-axis sensitivity adjustment value
#define AK8963_ASAZ 0x12 // Fuse ROM z-axis sensitivity adjustment value
#define WHO_AM_I_MPU9250 0x75 // Should return 0x71
#define MPU9250_ADDRESS 0x68<<1
#define INT_PIN_CFG 0x37
#define DATA_READY_MASK 0x01
#define MAGIC_OVERFLOW_MASK 0x8
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
float MagX;
float MagY;
float MagZ;
/* USER CODE BEGIN PV */
enum Mscale {
MFS_14BITS = 0, // 0.6 mG per LSB
MFS_16BITS // 0.15 mG per LSB
};
uint8_t Mscale = MFS_16BITS; // Choose either 14-bit or 16-bit magnetometer resolution
uint8_t Mmode = 0x02;
const uint16_t i2c_timeoutB = 100;
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
int _write(int file, char *ptr, int len)
{
HAL_UART_Transmit(&hlpuart1, (uint8_t*)ptr, len, HAL_MAX_DELAY);
return len;
}
float MagnetoX;
float MagnetoY;
float MagnetoZ;
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_LPUART1_UART_Init();
MX_I2C3_Init();
MX_I2C2_Init();
/* USER CODE BEGIN 2 */
printf("**************************** \r\n");
printf("MPU9250 STM32 Implementation \r\n");
printf("**************************** \r\n");
//pre-def. vars
uint8_t readData;
uint8_t writeData;
// Check MPU and Mag---------------------------------------------------------------------------------------------------
//read MPU9255 WHOAMI
HAL_I2C_Mem_Read(&hi2c2, MPU9250_ADDRESS, WHO_AM_I_MPU9250, 1, &readData, 1, i2c_timeoutB);
printf("MPU WHO AM I is (Must return 113): %d\r\n", readData);
//enable Mag bypass
writeData = 0x22;
HAL_I2C_Mem_Write(&hi2c2, MPU9250_ADDRESS, INT_PIN_CFG, 1, &writeData, 1, i2c_timeoutB);
//read AK8963 WHOAMI
HAL_I2C_Mem_Read(&hi2c2, AK8963_ADDRESS, AK8963_WHO_AM_I, 1, &readData, 1, i2c_timeoutB);
printf("MAG WHO AM I is (Must return 72): %d\r\n", readData);
printf("------------------------------------------------\r\n");
//Init Mag-------------------------------------------------------------------------------------------------------------
//Power down magnetometer
writeData = 0x00;
HAL_I2C_Mem_Write(&hi2c2, AK8963_ADDRESS, AK8963_CNTL, 1, &writeData, 1, i2c_timeoutB);
HAL_Delay(100);
//Enter Fuse ROM access mode
writeData = 0x0F;
HAL_I2C_Mem_Write(&hi2c2, AK8963_ADDRESS, AK8963_CNTL, 1, &writeData, 1, i2c_timeoutB);
HAL_Delay(100);
//Read the x-, y-, and z-axis calibration values
uint8_t rawMagCalData[3];
HAL_I2C_Mem_Read(&hi2c2, AK8963_ADDRESS, AK8963_ASAX, 1, &rawMagCalData[0], 3, i2c_timeoutB);
float calMagX = (float)(rawMagCalData[0] - 128)/256. + 1.; // Return x-axis sensitivity adjustment values, etc.
float calMagY = (float)(rawMagCalData[1] - 128)/256. + 1.;
float calMagZ = (float)(rawMagCalData[2] - 128)/256. + 1.;
printf("Mag cal off X: %f\r\n", calMagX);
printf("Mag cal off Y: %f\r\n", calMagY);
printf("Mag cal off Z: %f\r\n", calMagZ);
HAL_Delay(100);
//Power down magnetometer
writeData = 0x00;
HAL_I2C_Mem_Write(&hi2c2, AK8963_ADDRESS, AK8963_CNTL, 1, &writeData, 1, i2c_timeoutB);
HAL_Delay(100);
//Set magnetometer data resolution and sample ODR
writeData = Mscale << 4 | 0x02;
//writeData = 0x16;
printf("writeData: %d\r\n", writeData);
HAL_I2C_Mem_Write(&hi2c2, AK8963_ADDRESS, AK8963_CNTL, 1, &writeData, 1, i2c_timeoutB);
HAL_Delay(100);
printf("------------------------------------------------\r\n");
/*
//Read Mag-------------------------------------------------------------------------------------------------------------
//Read Mag data
uint8_t rawMagData[6];
HAL_I2C_Mem_Read(&hi2c2, AK8963_ADDRESS, AK8963_XOUT_L, 1, &rawMagData[0], 6, i2c_timeoutB);
MagX = ((int16_t)rawMagData[1] << 8) | rawMagData[0];
MagY = ((int16_t)rawMagData[3] << 8) | rawMagData[2];
MagZ = ((int16_t)rawMagData[5] << 8) | rawMagData[4];
printf("Mag X: %f\r\n", MagX);
printf("Mag Y: %f\r\n", MagY);
printf("Mag Z: %f\r\n", MagZ);
HAL_Delay(100);
printf("------------------------------------------------\r\n");
*/
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
HAL_I2C_Mem_Read(&hi2c2, AK8963_ADDRESS, AK8963_ST1, 1, &readData, 1, i2c_timeoutB);
printf("Read Data: %d\r\n", readData);
printf("Read Data2: %d\r\n", (readData & 0x01));
if( (readData & 0x01) == 0x01 ){
//Read Mag data
uint8_t rawMagData[7];
HAL_I2C_Mem_Read(&hi2c2, AK8963_ADDRESS, AK8963_XOUT_L, 1, &rawMagData[0], 7, i2c_timeoutB);
uint8_t c = rawMagData[6];
if(!(c & 0x08)) {
MagX = ((int16_t)rawMagData[1] << 8) | rawMagData[0];
MagY = ((int16_t)rawMagData[3] << 8) | rawMagData[2];
MagZ = ((int16_t)rawMagData[5] << 8) | rawMagData[4];
//Print to Com port via STLINK
printf("Mag X: %f\r\n", MagX);
printf("Mag Y: %f\r\n", MagY);
printf("Mag Z: %f\r\n", MagZ);
printf("------------------------------------------------\r\n");
}
}
else {
printf("No Data? \r\n");
printf("------------------------------------------------\r\n");
}
HAL_Delay(500);
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
这是一个简单的错误,因为我没有真正阅读数据表。我希望这对遇到同样错误的人来说是一个很好的资源。 :)