[STM32 learning] - I2C communication protocol & MPU6050 attitude sensor & software I2C read and write MPU6050

Table of contents

foreword

1. I2C communication protocol

1 Introduction

2. Hardware circuit design

3. I2C timing (software)

2. MPU6050 attitude sensor

1. Introduction to MPU6050

 2. MPU6050 parameters and circuit

3. Software I2C reads MPU6050

Summarize


Tip: After the article is written, the table of contents can be automatically generated. How to generate it can refer to the help document on the right

foreword

Disclaimer: The study notes are from the STM32 tutorial at Station B of the Jiangxi University of Science and Technology Association, and are for learning and exchange only! !

I2C communication learning is divided into two parts here. First, learn the protocol rules and implement the protocol in the form of software simulation ; second, learn the I2C peripherals of STM32, and then implement the protocol with hardware. Because I2C is a synchronous protocol, software simulation protocol is very convenient, and there are many such codes at present! Both software and hardware have advantages and disadvantages.

This program: Software I2C reads and writes MPU6050, hardware I2C reads and writes MPU6050. In the 51 tutorial, there is a comparison of the I2C of the AT24C02 storage module.


提示:以下是本篇文章正文内容,下面案例可供参考

1. I2C communication protocol

1 Introduction

The I2C bus (Inter IC BUS) is a general-purpose data bus developed by Philips, and has two communication lines: SCL (Serial Clock) and SDA (Serial Data) . Common examples of using I2C, MPU6050 attitude sensor, OLED, AT24C02 memory module, DS3231 real-time clock module:

Synchronous, half-duplex. Asynchronous communication (such as serial port) if the sender stops sending half of the transmission, the receiver will not know it. This is not allowed. Asynchrony has a strong dependence on the hardware peripheral USART circuit. Strict time requirements so generally do not use software. Due to the existence of the clock line SCL in the synchronization protocol , the time requirements are not high, and the sender can stop processing other things at any time (because the clock line is also suspended while the transmission is suspended). Half-duplex can also greatly save hardware resources.

Response with data supports multiple devices mounted on the bus (one master with multiple slaves, multiple masters with multiple slaves). One master and multiple slaves , the single chip microcomputer as the master dominates the operation of the I2C bus, and all peripheral modules mounted on the I2C bus are slaves, and the slaves can only control the I2C bus after being named by the master. Multi-master and multi-slave means that there are multiple masters. Any module on the bus can actively jump out to become a master, but there can only be one master at a time. At this time, it is equivalent to a bus conflict. The I2C protocol will perform arbitration . The winning party takes control, and the agreement is more complicated, so you can understand it yourself.

2. Hardware circuit design

Design Points:

①The SCL of all I2C devices is connected together, and the SDA is connected together.

② Both SCL and SDA of the device should be configured as open-drain output mode.

③SCL and SDA each add a pull-up resistor, the resistance is generally about 4.7KΩ.

 Master-slave power:

The CPU in the figure represents the single-chip microcomputer, as the master of the bus : ①Completely controls the clock line SCL. ②In the idle state, it can actively initiate the control of SDA, and only when the slave sends data and the slave responds, it will transfer the control right of SDA to the slave.

4 controlled IC slaves : ①The SCL clock line can only be read passively at any time. ②It is not allowed to actively initiate the control of SDA, only after the master sends the command to read the slave or when the slave responds, the control right can be obtained briefly.

Power short circuit problem:

The SDA of the host and the slave will repeatedly switch between input and output. This time should be coordinated well. In order to avoid the problem of power short circuit due to lack of coordination: I2C design prohibits all devices from outputting high levels with strong pull-ups, so external Set the circuit structure of weak pull-up resistor and open-drain output.

The SCLK and DATA on the right are the internal circuits of the master/slave SCL pin and the SDA pin respectively : the signal of the pin comes in through a data buffer or a Schmitt trigger (triangle) for input, because the input has no effect on the circuit. impact, so any device can input at any time. However, the output adopts an open-drain output configuration. xxxxN1 OUT outputs a low level, the switch is turned on, and the pin is directly grounded, which is a strong pull-down; the output is high, the switch is disconnected, the pin is not connected to anything, and is in a floating state. Empty state, so that all devices can only output low level and cannot output high level. In order to avoid the pin floating caused by high level, it is necessary to install a pull-up resistor on the outside (left picture) SCL and SDA respectively, and pulling it to a high level through a resistor is a weak pull-up. Benefits: ① Eliminate power short circuit and ensure safety; ② Avoid frequent switching of pin modes.

3. I2C timing (software)

I2C timing basic unit:

①Start condition: During the high level of SCL , SDA switches from high level to low level.

When the I2C bus is in an idle state, both SCL and DA are in a high state. Then after the SDA drops, the host will "pull down" the SCL again. On the one hand, it occupies this bus, and on the other hand, it is also to facilitate the splicing of the basic sequential units. That is, it will ensure that in addition to the start and end conditions, each sequential unit All SCLs start with a low level and end with a low level, so that the SCL can be continuous when the units are spliced ​​together

Termination condition: During the high level of SCL , SDA switches from low level to high level. The start and end conditions are similar to the start bit and stop bit in the serial data frame. Both the start and end are generated by the master, and the slave cannot be generated, so the slave must not touch the bus in the idle state. (Of course, multi-host mode may be possible)

 ③The host sends a byte: the first byte after the start must be sent by the host. During the low level of SCL , the host puts the data bits on the SDA line in turn ( high bit first, serial port is low bit first ), and then releases SCL, the slave will read data bits during the high level of SCL, so SDA is not allowed to have data changes during the high level of SCL , and the above process is repeated 8 times in turn to send a byte.

After SCL is at a high level, the slave needs to read SDA as soon as possible . Generally, the slave has completed reading at the rising edge of SCL , because the clock line is controlled by the master, and the slave does not know when it will drop. along. The master also needs to put data on SDA as soon as possible after the falling edge of SCL, but it doesn't have to be in such a hurry.

 ④The host receives a byte: During the low level of SCL , the slave puts the data bits on the SDA line in turn (high bit first), and then releases SCL, the host will read the data bits during the high level of SCL, so the SCL high level SDA does not allow data changes during the normal period , and the above process is repeated 8 times in turn to receive a byte ( the host needs to release SDA before receiving, that is, SDA is set to 1 )

 ⑤Send response: After receiving a byte, the host sends a bit of data at the next clock, data 0 means response , data 1 means non-response.

Receiving response: After sending a byte, the host receives a bit of data at the next clock to judge whether the slave responds. Data 0 indicates a response, and data 1 indicates a non-response (the host needs to release SDA before receiving).

 Full Timing (Full Data Frame)

The complete timing of I2C mainly includes three types: specified address write , current address read and specified address read . The address of each slave device is different. In the I2C protocol standard, it is divided into 7-bit address and 10-bit address . Currently, only 7-bit address is mentioned. It is simple and widely used. This address is specified by the chip manufacturer and can be found in the manual. Generally, the addresses of chips of the same type are the same. When mounted on the same bus, the lower bits of the address can be switched through specific pins.

①Specified address writing: The host writes the specified data (Data ) at the specified address (internal register address Reg Address) for the specified device (slave address ) .

The first 7 bits of Slave Address in the figure below are the selected slave address, the 8th bit is 0 for writing, 1 for reading, which is called the read-write flag.

If you only write one byte, then stop at P at the end; if you want to write multiple bytes , you can repeat Send Byte: 0xAA (Data), RA several times!

 ②Current address read: For the specified device (Slave Address), read the slave data (Data) under the address indicated by the current address pointer . Not commonly used .

In the I2C protocol , once the host reads and writes the flag to "1" when addressing, the next byte must enter the timing of reading immediately. The host has no time to indicate the register address. If the register address is not specified, which unit data should be read? ? This requires the current address pointer . All registers in the slave are allocated to a linear area, and there is a separate pointer variable indicating one of the registers. This pointer generally points to address 0 by default when it is powered on, and every time a byte is written And after reading a byte, this pointer will automatically increase once and move to the next position. For example, assuming that the timing of writing the specified address above is called , and 0xAA is written at the position of 0x19, then the pointer will move to the position of 0x1A by +1, and then the timing of reading the current address is called , and the value under the address of 0x1A is returned , if it is called again, it will return the value under the address of 0x1B, and so on.

Specified address read: For the specified device (Slave Address), read the slave data (Data) under the specified address (Reg Address). RA is Read Ack (appears after sending bytes), SA is Send Ack (appears after reading bytes), "0" is response, "1" is non-response.

Why can this timing specify the address of the read register? Add the part of the specified address before the timing of writing the specified address to the front of the reading of the current address to get the timing of reading the specified address, which is also called a composite format . The register address written earlier will be stored in , and will not disappear with the change of timing. Then start after the response bit RA, and then send a byte (the device is still the same as before but the read and write flag is 1), and the follow-up Start reading bytes.

 If you just read one byte , write SA=”1” (non-response) and P to stop at the end. Non-response means that the host does not pull SDA low when the host responds. When the slave reads SDA as “1”, it means that the host has not Answer, you know that the host does not want to continue, the slave will release the bus, and return the SDA control to the host; if you want to read multiple bytes , you need to read a byte, and give the slave a response SA="0", By analogy, the last byte gives non-response SA="1", stop P.

2. MPU6050 attitude sensor

1. Introduction to MPU6050

For details, please refer to the chip manual!

① MPU6050 is a 6-axis attitude sensor , which can measure the acceleration and angular velocity parameters of the X, Y, and Z axes of the chip itself. Through data fusion, the attitude angle can be further obtained . It is often used in scenarios such as self-balancing vehicles and aircraft that need to detect their own attitude. If a 3-axis magnetic field sensor is integrated in the chip to measure the magnetic field strength of the XYZ axis, it will be a 9-axis , and an air pressure sensor is integrated to measure the air pressure, which is called a 10-axis attitude sensor. Generally, the air pressure value reflects the altitude information.

The attitude angle is also called the Euler angle . Taking the aircraft as an example, the Euler angle is the angle between the aircraft fuselage and the initial XYZ 3 axes. The nose of the aircraft tilts down/up , and the angle between it and the y-axis is called the pitch angle Pitch ; the fuselage rolls left/right , and the angle between it and the x-axis is called Roll ; the fuselage of the plane is kept horizontal, and the nose turns left/right Steering , the angle with the z axis is called yaw Yaw . Data fusion is the meaning of combining multiple sensors to obtain accurate Euler angles. Common data fusion algorithms include complementary filtering , Kalman filtering , etc., which involve the knowledge of attitude calculation in the field of inertial navigation.

3-axis accelerometer (Accelerometer): referred to as Accel or Acc or A , measuring the acceleration of the X, Y, and Z axes . In fact, it can also get the angle of the object when it is stationary, such as a car parked on a slope. Accelerometers have static stability, not dynamic stability .

3-axis gyroscope sensor (Gyroscope): referred to as Gyro or G , which measures the angular velocity of the X, Y, and Z axes . The yellow part in the middle is a rotating wheel with a certain mass , and the gray part outside is a balance ring with three axes . When the rotating wheel in the middle rotates at a high speed, according to the conservation of angular momentum, it has a tendency to maintain its original angular momentum. This tendency It is possible to keep the axis of rotation unchanged. When the orientation of the outer object turns, the inner axis of rotation does not turn, and an angular misalignment occurs at the gimbal joint. If a rotary potentiometer is placed at the connection, the angle of rotation can be obtained by measuring the voltage of the potentiometer .

However, the gyroscope in the MPU6050 chip measures the angular velocity . If you want to obtain the angle through the angular velocity, you only need to integrate the angular velocity, but this integration also has limitations: when the object is stationary, the angular velocity value cannot be completely returned to zero due to noise . Accumulation will cause the obtained angle to drift. Gyroscopes have dynamic stability, not static stability .

Of course, this kind of mechanical structure sensor is not directly in the chip, it may be realized by electricity (manufacturer's secret)!

Application scenarios: image stabilizer, location service, game controller, 3D remote control, toy, balance car, drone, etc.

 2. MPU6050 parameters and circuit

Some common parameters

①The 16-bit ADC collects the analog signal of the sensor, and the quantization range is -32768~32767.

② Accelerometer full scale selection: ±2, ±4, ±8, ±16 (g). 1 g=9.8m/s2

③Gyroscope full scale selection: ±250, ±500, ±1000, ±2000 (°/sec). Degrees per second is the unit of angular velocity. If the measured object moves very violently, the larger of the two ranges can be selected. The smaller the full range, the finer the measurement.

④ Configurable digital low-pass filter . If the output data jitters violently, the register can be configured to select low-pass filtering for the output data.

⑤Configurable clock source and sampling frequency division . The clock source can provide clock for AD conversion and other internal circuits after sampling frequency division.

⑥I2C slave address : 110 1000 (AD0=0), 110 1001 (AD0=1).

When writing, it is converted to hexadecimal , such as 110 1000 is directly converted to 0x68H, but this direct conversion does not match "the upper 7 bits of the first byte in timing are used as the slave address, and the lowest bit is the read and write bit", so To read and write bits (0x68<<1)|, read 1 and write 0. You can also directly use the slave address 1101 0000 as the slave address 0xD0, and then read and write bits as needed.

hardware circuit

 The right side is the MPU6050 chip , the lower left corner is an 8-pin header , and the upper left corner is a low-dropout linear regulator ( LDO ). The chip itself has a lot of pins, including some minimum systems that are fixedly connected inside. The 8-pin in the lower left corner is used to lead out, and two 4.7K pull-up resistors have been built in the SDA and ACL modules. When wiring, directly connect SCL and SDA can be connected to the GPIO port.

The design of XCL and XDA is to expand the function of the chip, and the 3-axis magnetometer sensor is added to make up for the defect of inaccurate yaw angle. The UAV also needs to add a barometer to achieve fixed altitude flight. At this time, it is necessary to use it when expanding XCL and XDA, you can connect an external magnetometer or barometer , and read its data into the MPU6050 for attitude calculation. If you don’t need the calculation function, you can also mount them directly on SCL and SDA.

INT can configure some events inside the chip to trigger the output of the interrupt pin . For example, data preparation is complete, I2C host error, etc. Small functions inside the chip: free fall detection, motion detection, zero motion detection, etc. These signals can trigger the INT pin to generate a level jump

The LDO part is the power supply logic . The VDD power supply of the MPU6050 chip in the manual is 2.375~3.46V, which can be connected to 3.3V, but not to 5V. The input terminal VCC_5V can be between 3.3~5V, and the chip is powered by a 3.3VLDO voltage regulator , and the blue diode is the power indicator. If you already have a stable 3.3V power supply, don't design this part of the circuit.

block diagram

The upper left corner is the clock input and output pins, but the internal clock is generally used , refer to the hardware circuit just now CLKIN is directly grounded, CLKOUT is not drawn out, so there is no need to worry too much about this part.

The gray part is the internal sensor of the chip . There is a temperature sensor at the bottom. After the AD conversion of the sensor data, it is placed in the Sensor Register . The conversion is automatic. After we configure the conversion frequency , each data will be automatically converted to The set frequency is refreshed to the data register, and the data register can be read directly when needed.

The Self test self-test unit is used to verify whether the chip is good or bad. When the self-test is started, the chip will simulate an external force applied to the sensor, resulting in a larger sensor data than usual. Self-test process : Enable self-test, read data, disable self-test, read data, subtract the two data to get the self-test response. If the self-test response is within the specified range, it means that there is no problem with the chip, otherwise there is a problem.

Charge Pump is a charge pump/charge pump. The CPOUT pin needs an external capacitor. The charge pump is a boost circuit, such as in OLED. Boosting principle : For example, a 5V battery is connected in parallel with a capacitor, and the battery charges the capacitor. After it is fully charged, the capacitor is equivalent to a 5V battery. Then modify the connection method to be in series, and the output is 10V. However, the capacitor charge is small, and the duration of use is not long, so the switching speed from parallel to series should be very fast, and a power filter is added later.

The green circle on the right is the register and communication interface part. The Interr Status Register controls which internal events are output to the interrupt pin; the first-in-first-out register (FIFO) caches the data stream (not used in this section temporarily); the configuration register (Config Register) configures various internal circuits; Factory Calibration means that all internal sensors are calibrated (no need to understand). The digital motion processor (DMP) is a hardware algorithm for attitude calculation that comes with the chip, and it can be used for attitude calculation with the official DMP library. FSYNC is frame synchronization , not used for now.

The part in the red circle is the communication interface part, 8, 9, 13 , 14 are the I2C and SPI communication interfaces of the slave, used to communicate with STM32 (MPU6050 as a slave); 6, 7 are the I2C communication interface of the host, used to communicate with Extended devices communicate (MPU6050 as host). The interface bypass selector (Serial Interface Bypass Mux) is a switch. If it is turned on, the auxiliary pins (6, 7) will be connected with the normal pins (8, 9, 13, 14). At this time STM32 is the "big brother" that can control all devices; otherwise, the two buses are separated.

3. Software I2C reads MPU6050

wiring diagram

 Software I2C refers to the use of ordinary GPIO ports to manually (software) flip the level to implement the protocol. It does not require the support of peripheral resources inside the STM32, so the GPIO port can be specified arbitrarily. Here, SCL is PB10 and SDA is PB11. Only the functions of GPIO are needed, and the library functions for configuring I2C are not used. The hardware circuit does not need a pull-up resistor, because the chip already comes with it.

Note that the "reading R" and "writing W" involved in the program are all based on the host, that is, STM32!

program ideas

1. Establish the .c and .h modules of the I2C communication layer, and write the GPIO initialization and 6 timing basic units of the I2C bottom layer.

2. Establish the .c and .h modules of the MPU6050. Here, based on the I2C communication layer module, the specified address is read and the specified address is written, and then the chip is configured by writing the register, and the sensor data is obtained by reading the register.

3. Call the MPU6050 module in main.c, initialize, get data, and display data.

//MyI2C.c
#include "stm32f10x.h"                  // Device header
#include "Delay.h"

//对操作端口的库函数进行封装
void MyI2C_W_SCL(uint8_t BitValue){//主机输出SCL线
	GPIO_WriteBit(GPIOB,GPIO_Pin_10,(BitAction)BitValue);
	Delay_us(10);//防止MPU6050跟不上响应
}
void MyI2C_W_SDA(uint8_t BitValue){//主机写SDA
	GPIO_WriteBit(GPIOB,GPIO_Pin_11,(BitAction)BitValue);
	Delay_us(10);
}
uint8_t MyI2C_R_SDA(void){//主机读SDA
	uint8_t BitValue;
	BitValue = GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_11);
	Delay_us(10);
	return BitValue;//返回读到SDA线的电平
}

//初始化任务:SCL和SDA初始化为开漏输出;将SCL和SDA置高电平
void MyI2C_Init(void){
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD ;//开漏输出
	//开漏输出但不代表只可输出,也可输入,输入时先输出1,再直接读取输入数据寄存器就行
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10| GPIO_Pin_11;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStructure);//配置好默认就是输出低电平
	GPIO_SetBits(GPIOB,GPIO_Pin_10|GPIO_Pin_11);//置为高电平
}

//6个时序单元
void MyI2C_Start(void){
	MyI2C_W_SDA(1);
	MyI2C_W_SCL(1);
	//SCL高时可对SDA进行读操作,为防止误判(Sr重复起始时易发生),所以先释放SDA(置1)再释放SCL
	MyI2C_W_SDA(0);
	MyI2C_W_SCL(0);
	//除了Stop,其他单元一般都会保证SCL以低电平0结束,便于各单元拼接
}
void MyI2C_Stop(void){
	MyI2C_W_SDA(0);
	MyI2C_W_SCL(1);
	MyI2C_W_SDA(1);
}
void MyI2C_SendByte(uint8_t Byte){
	uint8_t i;
	for (i=0;i<=8;i++){
	MyI2C_W_SDA(Byte & 0x80>>i);//取位发送到SDA。此时SCL为低,可向SDA线发一位数据(高位先行)
	//第一次按位与结果0x80或0x00,因为参数类型为BitAction,只能非1即0,所以结果就变为1或0
	MyI2C_W_SCL(1);//释放SCL,一般在上升沿时,数据就被从机读走
	MyI2C_W_SCL(0);//拉低SCL,驱动时钟走一个脉冲
	}
}
uint8_t MyI2C_ReceiveByte(void){
	uint8_t i,Byte = 0x00;
	MyI2C_W_SDA(1);//主机在接收前要先释放SDA,即置SDA为1。这时从机就会往SDA发数据
	for(i=0;i<=8;i++){
		MyI2C_W_SCL(1);//置高SCL,开始读了,此时SDA数据禁止变化
		if (MyI2C_R_SDA()==1){
			Byte |= (0x80 >> i);//如果读到SDA是1就转存到Byte对应位写1。置1用或,清0用与
		}
		MyI2C_W_SCL(0);
	}
	return Byte;
}
//发送应答和接收应答其实就是上面两个函数1位的简化版
void MyI2C_SendAck(uint8_t AckBit){
	MyI2C_W_SDA(AckBit);
	MyI2C_W_SCL(1);
	MyI2C_W_SCL(0);
}
uint8_t MyI2C_ReceiveAck(void){
	uint8_t AckBit;
	MyI2C_W_SDA(1);//读之前先释放SDA。这时从机向主机发送Ack到SDA
	//I2C引脚是开漏输出弱上拉,主机输出1并不是强制SDA为高电平,而是释放SDA
	//从机有义务将SDA再拉低的(弹簧杆子),当然它想发送1另说。
	//所以即使上面SDA置1了,下面读到的值也可能为0。
	MyI2C_W_SCL(1);
	AckBit = MyI2C_R_SDA();//读SDA线上的Ack
	return AckBit;
}

//MPU6050.c
#include "stm32f10x.h"                  
#include "MyI2C.h"
#include "MPU6050_Reg.h"

#define MPU6050_ADDRESS  0xD0

void MPU6050_WriteReg(uint8_t RegAddress,uint8_t Data){//指定地址写函数
	MyI2C_Start();
	MyI2C_SendByte(MPU6050_ADDRESS);
	MyI2C_ReceiveAck();//这里为了突出重点就没判断应答
	MyI2C_SendByte(RegAddress);
	MyI2C_ReceiveAck();
	
	MyI2C_SendByte(Data);
	MyI2C_ReceiveAck();
	//若发送多字节可把这两句用for循环括起来把一个数组的数据依次发出去
	
	MyI2C_Stop();
}

uint8_t MPU6050_ReadReg(uint8_t RegAddress){//指定地址读函数
	uint8_t Data;
	MyI2C_Start();
	MyI2C_SendByte(MPU6050_ADDRESS);
	MyI2C_ReceiveAck();
	MyI2C_SendByte(RegAddress);
	MyI2C_ReceiveAck();//前面一段借用指定地址写的代码指定寄存器地址,下面再转入读的时序
	
	//转入读的时序,需重新指定读写位,重新开始
	MyI2C_Start();
	MyI2C_SendByte(MPU6050_ADDRESS | 0x01);//读1写0
	MyI2C_ReceiveAck();
	Data = MyI2C_ReceiveByte();
	MyI2C_SendAck(1);//非应答。如果接收多个字节同理给应答,循环接收,最后再给非应答
	MyI2C_Stop();
	
	return Data;
}

/*************************************************************************************/


void MPU6050_Init(void){
	MyI2C_Init();
	//普通存储器里的数据没什么实际意义,而寄存器中的每一位对应了电路的某种状态,可与外设硬件电路互动
	//根据芯片手册配置MPU6050相应寄存器,就像回到了51时期用寄存器进行开发
	MPU6050_WriteReg(MPU6050_PWR_MGMT_1,0x01);//解除睡眠,选择陀螺仪时钟(更精确)
	MPU6050_WriteReg(MPU6050_PWR_MGMT_2,0x00);//6个轴均不待机
	MPU6050_WriteReg(MPU6050_SMPLRT_DIV,0x09);//采样分频为10
	MPU6050_WriteReg(MPU6050_CONFIG	,0x06);//滤波参数给最大
	MPU6050_WriteReg(MPU6050_GYRO_CONFIG,0x18);//陀螺仪最大量程
	MPU6050_WriteReg(MPU6050_ACCEL_CONFIG,0x18);//加速度计最大量程
	//配置完这些之后,内部就在连续不断地进行数据转换,输出的数据放在数据寄存器里
	//想获取数据的话,下面再写个函数:
}

//这个函数需要返回6个int16_t数据,但函数返回值只能1个。
//多返回值函数设计,最简单一种方法:
//在函数外面定义6个全局变量,子函数读到的数据直接写到全局变量,在主函数里进行共享。但不利于封装
//第二种:用指针进行变量的地址传递实现多返回值。
//第三种:用结构体对多个变量打包,然后再统一进行传递。类似于STM32库函数利用结构体输入参数,只不过这里是输出
void MPU6050_GetData(int16_t *AccX,int16_t *AccY,int16_t *AccZ,
	                 int16_t *GyroX,int16_t *GyroY,int16_t *GyroZ)
{
	uint16_t DataH, DataL;
	
	DataH = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_H);//加速度X轴高8位
	DataL = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_L);
	*AccX = (DataH << 8)|DataL;//加速度计X轴的16位数据
	//用指针引用传递进来的地址,把读到的数据通过指针传递出去。对比主函数使用
	
	DataH = MPU6050_ReadReg(MPU6050_ACCEL_YOUT_H);
	DataL = MPU6050_ReadReg(MPU6050_ACCEL_YOUT_L);
	*AccY = (DataH << 8)|DataL;
	
	DataH = MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_H);
	DataL = MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_L);
	*AccZ = (DataH << 8)|DataL;
	
	DataH = MPU6050_ReadReg(MPU6050_GYRO_XOUT_H);//陀螺仪X轴高8位
	DataL = MPU6050_ReadReg(MPU6050_GYRO_XOUT_L);
	*GyroX = (DataH << 8)|DataL;
	
	DataH = MPU6050_ReadReg(MPU6050_GYRO_YOUT_H);
	DataL = MPU6050_ReadReg(MPU6050_GYRO_YOUT_L);
	*GyroY = (DataH << 8)|DataL;
	
	DataH = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_H);
	DataL = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_L);
	*GyroZ = (DataH << 8)|DataL;
	
	//因为这些寄存器地址是连续的,可优化连续读取。上面的方法太长
}

uint8_t MPU6050_GetID(void){//获取芯片ID号
	return MPU6050_ReadReg(MPU6050_WHO_AM_I	);
}
//main.c这里只是读取MPU6050数据简单显示下
#include "stm32f10x.h"   // Device header
#include "Delay.h"   
#include "OLED.h"
#include "MPU6050.h"  //包含了“MyI2C.h”

uint8_t ID;
int16_t AX,AY,AZ,GX,GY,GZ;

int main(void){
	OLED_Init();
	MPU6050_Init();
	
	OLED_ShowString(1,1,"ID:");
	ID = MPU6050_GetID();
	OLED_ShowHexNum(1,4,ID,2);
	
	while(1){
		MPU6050_GetData(&AX,&AY,&AZ,&GX,&GY,&GZ);//参数是6个指针变量,“指了六条路”这样就可拿到6个数据
		OLED_ShowSignedNum(2,1,AX,5);
		OLED_ShowSignedNum(3,1,AY,5);
		OLED_ShowSignedNum(4,1,AZ,5);
		OLED_ShowSignedNum(5,1,GX,5);
		OLED_ShowSignedNum(6,1,GY,5);
		OLED_ShowSignedNum(7,1,GZ,5);
	}
}

The above program uses a multi-layer module architecture . The lowest level of the I2C protocol layer , the main focus is which pins, what configuration, when is the timing high and when is it low? These are protocol-related. Above the I2C protocol layer is the driver layer of MPU6050 . The main focus is how to read and write registers, how to configure registers, and how to read data? These drive related content. Finally, there is the main function application layer , which only needs the MPU6050_GetData function to get the data, and then use these data for further functional design.

Extension:

The MPU6050_GetData function needs to return 6 int16_t data, but the function can only return 1 value. There are three common types of multi-return value function design:

The simplest one : define 6 global variables outside the function, and the data read by the sub-functions are directly written to the global variables and shared in the main function. But it is not conducive to packaging.
The second method : use the pointer to transfer the address of the variable to realize multiple return values.
The third method : use a structure to package multiple variables, and then transfer them uniformly. Similar to the STM32 library function using the structure input parameters, but here is the output.


Summarize

I2C learning is divided into two parts. First, learn the protocol rules and implement the protocol in the form of software simulation ; second, learn the I2C peripherals of STM32, and then implement the protocol with hardware. The above is the first piece, and next time I will further study the I2C peripherals of STM32 and the hardware implementation of the protocol!
see you later! !

Previous highlights:
STM32 timer input capture (IC)
STM32 timer output comparison (PWM wave)
STM32 timing interrupt
STM32 external interrupt
STM32GPIO intensive talk
...

Guess you like

Origin blog.csdn.net/weixin_51658186/article/details/129821841