硬體:STM32F103ZET6 + GY9250
軟體:Keil MDK(v5.27)+ STM32CUBEMX(v5.2.1)
這裡我在使用STM32CUBEMX最新版本(v5.5.0)生成Keil工程時出現了“MDK-ARM v5.27 project generation have a problem.”的問題(并不是路徑問題)。換用了較低的v5.2.1版本後順利生成工程。
STM32CUBEMX使用HAL庫建立工程。
在方法完全相同的情況下(STM32CUBEMX工程配置相同,main.c檔案代碼相同),使用IAR(v8.32.4)建立的工程無法順利讀取磁力計資料。(???)
由于MPU9250内部采用I2C與磁力計通信,使用I2C讀取會相對簡單。
Here we go!
1、使用STM32CubeMX配置工程
(1)RCC選取HSE:Crystal/Ceramic Resonator(石英/陶瓷 晶振);
(2)在Clock Configuration中設定HCLK為72(MHz);
(3)配置USART1的MODE為Asynchronous,用來向上位機發送接收到的資料;
(4)配置PB6、PB7為GPIO_Output,修改GPIO mode為Output Open Drain(開漏輸出),将Maximum output speed設定為Medium(10MHz);
其餘設定保持預設,記得勾選“Generate peripheral initialization as a pair of '.c/.h' files per peripheral”,然後生成代碼。
2、USART1發送
#define usart1_print(...) HAL_UART_Transmit(&huart1, u_buf, sprintf((char *)u_buf, __VA_ARGS__), 0xffff);
在使用前需要包含頭檔案“stdio.h”并且定義緩沖區。
#include "stdio.h"
uint8_t u_buf[0xff];
3、宏定義GPIO操作
#define SCL_H GPIOB->BSRR = 0x0040 //GPIO_Pin_6
#define SCL_L GPIOB->BRR = 0x0040 //GPIO_Pin_6
#define SDA_H GPIOB->BSRR = 0x0080 //GPIO_Pin_7
#define SDA_L GPIOB->BRR = 0x0080 //GPIO_Pin_7
#define SCL_read GPIOB->IDR & 0x0040 //GPIO_Pin_6
#define SDA_read GPIOB->IDR & 0x0080 //GPIO_Pin_7
4、模拟I2C
函數聲明
void I2C_Delay(void);
int I2C_Start(void);
void I2C_Stop(void);
void I2C_Ack(void);
void I2C_NoAck(void);
int I2C_WaitAck(void);
void I2C_SendByte(uint8_t SendByte);
unsigned char I2C_ReadByte(void);
int Single_Write(unsigned char SlaveAddress, unsigned char REG_Address, unsigned char REG_data);
unsigned char Single_Read(unsigned char SlaveAddress, unsigned char REG_Address);
函數定義
void I2C_Delay(void)
{
uint8_t i=20; //可以适當調整
while(i)
{
i--;
}
}
int I2C_Start(void)
{
SDA_H;
SCL_H;
I2C_Delay();
if(!SDA_read)return 0; //SDA線為低電平則總線忙,退出
SDA_L;
I2C_Delay();
if(SDA_read)return 0; //SDA線為高電平則總線出錯,退出
SDA_L;
I2C_Delay();
return 1;
}
void I2C_Stop(void)
{
SCL_L;
I2C_Delay();
SDA_L;
I2C_Delay();
SCL_H;
I2C_Delay();
SDA_H;
I2C_Delay();
}
void I2C_Ack(void)
{
SCL_L;
I2C_Delay();
SDA_L;
I2C_Delay();
SCL_H;
I2C_Delay();
SCL_L;
I2C_Delay();
}
void I2C_NoAck(void)
{
SCL_L;
I2C_Delay();
SDA_H;
I2C_Delay();
SCL_H;
I2C_Delay();
SCL_L;
I2C_Delay();
}
int I2C_WaitAck(void)
{
SCL_L;
I2C_Delay();
SDA_H;
I2C_Delay();
SCL_H;
I2C_Delay();
if(SDA_read)
{
SCL_L;
I2C_Delay();
return 0;
}
SCL_L;
I2C_Delay();
return 1;
}
void I2C_SendByte(uint8_t SendByte)
{
uint8_t i = 8;
while(i--)
{
SCL_L;
I2C_Delay();
if(SendByte & 0x80)
SDA_H;
else
SDA_L;
SendByte<<=1;
I2C_Delay();
SCL_H;
I2C_Delay();
}
SCL_L;
}
unsigned char I2C_ReadByte(void)
{
uint8_t i = 8;
uint8_t ReceiveByte = 0;
SDA_H;
while(i--)
{
ReceiveByte<<=1;
SCL_L;
I2C_Delay();
SCL_H;
I2C_Delay();
if(SDA_read)
{
ReceiveByte | = 0x01;
}
}
SCL_L;
return ReceiveByte;
}
int Single_Write(unsigned char SlaveAddress, unsigned char REG_Address, unsigned char REG_data)
{
if(!I2C_Start())return 0;
I2C_SendByte(SlaveAddress);
if(!I2C_WaitAck()){I2C_Stop();return 0;}
I2C_SendByte(REG_Address);
I2C_WaitAck();
I2C_SendByte(REG_data);
I2C_WaitAck();
I2C_Stop();
HAL_Delay(2);
return 1;
}
unsigned char Single_Read(unsigned char SlaveAddress, unsigned char REG_Address)
{
unsigned char REG_data;
if(!I2C_Start())return 0;
I2C_SendByte(SlaveAddress);
if(!I2C_WaitAck()){I2C_Stop(); return 0;}
I2C_SendByte((uint8_t) REG_Address);
I2C_WaitAck();
I2C_Start();
I2C_SendByte(SlaveAddress+1);
I2C_WaitAck();
REG_data=I2C_ReadByte();
I2C_NoAck();
I2C_Stop();
return REG_data;
}
5、根據 MPU9250 的 Register Map and Descriptions 宏定義
#define SMPLRT_DIV 0x19 //陀螺儀采樣率,典型值:0x07(125Hz)
#define GYRO_CONFIG 0x1B //陀螺儀自檢及測量範圍,典型值:0x18(不自檢,2000deg/s)
#define ACCEL_CONFIG 0x1C //加速計自檢、測量範圍及高通濾波頻率,典型值:0x01(不自檢,2G,5Hz)
#define PWR_MGMT_1 0x6B //電源管理,典型值:0x00(正常啟用)
#define WHO_AM_I 0x75 //IIC位址寄存器(預設數值0x68,隻讀)
#define ACCEL_XOUT_H 0x3B
#define ACCEL_XOUT_L 0x3C
#define ACCEL_YOUT_H 0x3D
#define ACCEL_YOUT_L 0x3E
#define ACCEL_ZOUT_H 0x3F
#define ACCEL_ZOUT_L 0x40
#define TEMP_OUT_H 0x41
#define TEMP_OUT_L 0x42
#define GYRO_XOUT_H 0x43
#define GYRO_XOUT_L 0x44
#define GYRO_YOUT_H 0x45
#define GYRO_YOUT_L 0x46
#define GYRO_ZOUT_H 0x47
#define GYRO_ZOUT_L 0x48
#define MAG_XOUT_L 0x03
#define MAG_XOUT_H 0x04
#define MAG_YOUT_L 0x05
#define MAG_YOUT_H 0x06
#define MAG_ZOUT_L 0x07
#define MAG_ZOUT_H 0x08
#define GYRO_ADDRESS 0xD0
#define MAG_ADDRESS 0x18
#define ACCEL_ADDRESS 0xD0
6、MPU9250的相關操作
void Init_MPU9250(void)
{
Single_Write(GYRO_ADDRESS,PWR_MGMT_1,0x00);
Single_Write(GYRO_ADDRESS,SMPLRT_DIV,0x07);
Single_Write(GYRO_ADDRESS,CONFIG,0x06);
Single_Write(GYRO_ADDRESS,GYRO_CONFIG,0x18);
Single_Write(GYRO_ADDRESS,ACCEL_CONFIG,0x01);
}
void READ_MPU9250_ACCEL(void)
{
BUF[0]=Single_Read(ACCEL_ADDRESS,ACCEL_XOUT_L);
BUF[1]=Single_Read(ACCEL_ADDRESS,ACCEL_XOUT_H);
T_X=(BUF[1]<<8)|BUF[0];
T_X/=164;
BUF[2]=Single_Read(ACCEL_ADDRESS,ACCEL_YOUT_L);
BUF[3]=Single_Read(ACCEL_ADDRESS,ACCEL_YOUT_H);
T_Y=(BUF[3]<<8)|BUF[2];
T_Y/=164;
BUF[4]=Single_Read(ACCEL_ADDRESS,ACCEL_ZOUT_L);
BUF[5]=Single_Read(ACCEL_ADDRESS,ACCEL_ZOUT_H);
T_Z=(BUF[5]<<8)|BUF[4];
T_Z/=164;
}
void READ_MPU9250_GYRO(void)
{
BUF[0]=Single_Read(GYRO_ADDRESS,GYRO_XOUT_L);
BUF[1]=Single_Read(GYRO_ADDRESS,GYRO_XOUT_H);
T_X=(BUF[1]<<8)|BUF[0];
T_X/=16.4;
BUF[2]=Single_Read(GYRO_ADDRESS,GYRO_YOUT_L);
BUF[3]=Single_Read(GYRO_ADDRESS,GYRO_YOUT_H);
T_Y=(BUF[3]<<8)|BUF[2];
T_Y/=16.4;
BUF[4]=Single_Read(GYRO_ADDRESS,GYRO_ZOUT_L);
BUF[5]=Single_Read(GYRO_ADDRESS,GYRO_ZOUT_H);
T_Z=(BUF[5]<<8)|BUF[4];
T_Z/=16.4;
BUF[6]=Single_Read(GYRO_ADDRESS,TEMP_OUT_L);
BUF[7]=Single_Read(GYRO_ADDRESS,TEMP_OUT_H);
T_T=(BUF[7]<<8)|BUF[6];
T_T=(T_T-21)/333.87 +21; //順便讀溫度
}
void READ_MPU9250_MAG(void)
{
Single_Write(GYRO_ADDRESS,0x37,0x02); //turn on Bypass Mode
HAL_Delay(20);
Single_Write(MAG_ADDRESS,0x0A,0x01);
HAL_Delay(20);
BUF[0]=Single_Read(MAG_ADDRESS,MAG_XOUT_L);
BUF[1]=Single_Read(MAG_ADDRESS,MAG_XOUT_H);
T_X=(BUF[1]<<8)|BUF[0];
BUF[2]=Single_Read(MAG_ADDRESS,MAG_YOUT_L);
BUF[3]=Single_Read(MAG_ADDRESS,MAG_YOUT_H);
T_Y=(BUF[3]<<8)|BUF[2];
BUF[4]=Single_Read(MAG_ADDRESS,MAG_ZOUT_L);
BUF[5]=Single_Read(MAG_ADDRESS,MAG_ZOUT_H);
T_Z=(BUF[5]<<8)|BUF[4];
}
7、格式化處理資料
void Format(unsigned char *s,short temp_data)
{
if(temp_data<0)
{
temp_data=-temp_data;
*s++='-';
}
else *s=' ';
*s++=temp_data/100+0x30;
temp_data=temp_data%100;
*s++=temp_data/10+0x30;
temp_data=temp_data%10;
*s++=temp_data+0x30;
*s=0;
}
8、Private variables
unsigned char BUF[20]; //接收資料緩存區
unsigned char TX_DATA[8]; //顯示資料緩存區
short T_X, T_Y, T_Z, T_T; //X、Y、Z軸資料,時間
9、在主循環開始前初始化
Init_MPU9250();
10、主循環中的代碼
READ_MPU9250_ACCEL(); //加速度計
Format(TX_DATA,T_X);
usart1_print("AX:%s\t", TX_DATA);
Format(TX_DATA,T_Y);
usart1_print("AY:%s\t", TX_DATA);
Format(TX_DATA,T_Z);
usart1_print("AZ:%s\t", TX_DATA);
usart1_print("\n");
READ_MPU9250_GYRO(); //陀螺儀
Format(TX_DATA,T_X);
usart1_print("GX:%s\t", TX_DATA);
Format(TX_DATA,T_Y);
usart1_print("GY:%s\t", TX_DATA);
Format(TX_DATA,T_Z);
usart1_print("GZ:%s\t", TX_DATA);
usart1_print("\n");
READ_MPU9250_MAG(); //磁力計
Format(TX_DATA,T_X);
usart1_print("MX:%s\t", TX_DATA);
Format(TX_DATA,T_Y);
usart1_print("MY:%s\t", TX_DATA);
Format(TX_DATA,T_Z);
usart1_print("MZ:%s\t", TX_DATA);
usart1_print("\n");
Format(TX_DATA,T_T);
usart1_print("T:%s\t", TX_DATA);
usart1_print("\n");
HAL_Delay(200);