[STM32 learning] - STM32-I2C peripherals & hardware read and write MPU6050 & software and hardware read and write waveform comparison

Table of contents

foreword

1. I2C peripherals

2. Hardware I2C operation process

1. Host sending timing

3. Other timing

4. Expansion: software/hardware I2C waveform comparison

3. Hardware I2C read and write MPU6050

Summarize


foreword

Disclaimer: The study notes are from the tutorials at Station B of Jiangke University Self-Chemical Association, and are for learning and exchange only! !

Continuing from the I2C protocol and software reading and writing MPU6050 learned last time, continue to learn the I2C peripherals of STM32 and hardware reading and writing I2C! Attitude sensors are widely used in balance cars, drones, etc.!

Different from the serial port, because the I2C bit synchronous communication software simulation protocol is also widely used! However, the hardware implementation of the protocol also has unique advantages : high execution efficiency, saving software resources, powerful functions to realize a complete multi-host communication model, regular waveform timing, and fast communication speed. This study only needs to master the one-master-multiple-slave, 7-bit address mode.


1. I2C peripherals

overview

1. The STM32 integrates the hardware I2C transceiver circuit, which can automatically perform functions such as clock generation, start and end condition generation, response bit sending and receiving, and data sending and receiving by the hardware, reducing the burden on the CPU. The software only needs to write the control register CR and the data register DR to implement the protocol. Of course, in order to realize the real-time monitoring of the timing status, the software also needs to read the status register SR.

2. Support multi-host model . It is divided into fixed multi-master and variable multi-master. "Fixed" means that certain devices are always the master, and "variable" means that a device may switch master-slave at any time, and bus arbitration is performed in case of conflict. The I2C of STM32 is a variable multi-master design. Get to know first!

3. Support 7-bit/10-bit address mode. If there are more than 128 devices or too many chips of the same model, a 10-bit address mode is used, with 1024 possibilities. The first two bytes are used for addressing, and the extra bits are flag bits.

4. Support different communication speeds , standard speed (up to 100 kHz), fast speed (up to 400 kHz).

5. Support DMA . Multi-byte transmission can improve transmission efficiency, such as specifying the timing of reading/writing multiple bytes at an address, and using DMA to automatically transfer data. Get to know first!

6. Compatible with SMBus protocol . System Management Bus, the system management bus, is based on the improvement of the I2C bus and is mainly used in power management systems . Get to know first!

STM32F103C8T6 hardware I2C resources : I2C1, I2C2. Only two hardware I2C buses are allowed, but software I2C is generally unlimited.

block diagram

        

On the left are the SDA and SCL pins, and SMBALERT is for SMBus. The pins of these peripherals generally use the multiplexing mode of GPIO. For example, according to the pin definition table, I2C1_SCL is PB6, I2C1_SDA is PB7, I2C2_SCL is PB10, and I2C2_SDA is PB11.

The green box is the data control part related to SDA, the core is DATA REGISTER and data shift register . When data needs to be sent , the data can be written to DR. When there is no , the value (byte) in DR will be further transferred to the shift register; when the data is shifted , it can be Put the next data in DR and wait, seamless connection. When the data is transferred from DR to the shift register , the TXE bit of the status register SR will be set to 1, indicating that the sending register is empty . When inputting data , the data is moved from the SDA pin to the shift register bit by bit. When a byte is collected, the data is transferred from the shift register to DR as a whole , and the flag bit RXNE is set at the same time , indicating that the receiving register is not empty. , then the data can be read from DR. Similar to the serial port, but because the serial port is full-duplex, the sending and receiving parts are designed separately. As for when to receive, when to send, start bit, stop bit, response bit, etc. need to be written into the corresponding bit of the control register CR , the data control circuit will complete the corresponding function. This part is the key to master!

After the data is sent and received, there are two functions, one is the comparator and its own address register, the double address register , and the other is the frame error check (PEC) register and the frame error check (PEC) calculation . The first function is used in slave mode. When STM32 is used as a slave, its own address register specifies its own slave address. The comparator is used to compare the external call address with its own slave address, and STM supports simultaneous response to two slaves. Machine address, so there is a double address register. The second function is used for data verification. When sending a multi-byte data frame, the hardware can automatically perform CRC verification calculation. CRC is a very common data verification algorithm, and finally a data verification bit is appended to the data. Later, after receiving the data, the STM32 hardware can also automatically perform the verification judgment. If it is wrong, the data error flag will be set, similar to the serial parity check. This part can be understood first, and it will not be used for the time being!

Basket, write the clock control register , the clock control circuit will execute the corresponding function; write the control register , the control logic circuit can control the entire circuit; read the status register , you can know the working status of the circuit; interrupt , when the internal After some flags are set to 1, you can apply for an interrupt. If the interrupt is enabled, the program can jump to the interrupt function to handle the event after the event occurs; DMA request and response , the timing of reading/writing multiple bytes at the specified address, and automatically help transfer data with DMA ,Improve efficiency!

The simplest diagram of I2C peripherals (one master and multiple slaves)

2. Hardware I2C operation process

The following content must be understood according to the chip manual. Chapter 24 introduces each bit of the CR, DR, and SR registers!

At the same time, when writing programs, you should also write corresponding pictures!

1. Host sending timing

7-bit main transmission , one byte after the start condition is addressing; 10-bit main transmission , two bytes after the start condition are addressing, the content of the previous byte frame header is 5-bit flag 11110+2 Bit address + 1 bit for reading and writing, the latter byte is a pure 8-bit address, forming 10-bit addressing.

After initialization, the bus defaults to idle state. STM32 defaults to slave mode. In order to generate a start condition, STM32 needs to write to the control register (the Start bit is written 1 to start, and the hardware automatically clears it to 0 after the start condition is issued), and then STM32 From the slave mode to the master mode (that is, under the multi-host model, if the STM32 has data to send, it will jump out). After controlling the hardware circuit, check the flag bit to see if the hardware has reached the desired state: EV5 (Event5) event will occur after the start bit, which can be regarded as a large flag bit combining multiple flag bits. In the library function There is also a function to check whether an EVx event occurs, see Figure Explanation and Chip Manual 24.6.6 Status Register. (BTF: Byte Transfer Finished)

The whole timing process looks like a lot of operations, but in simple terms : write the control register CR or data register DR to control the occurrence of the timing unit. Viewing EVx events requires viewing the Status Register, SR . It is very simple to operate with library functions! !

2. Host receiving timing

The timing given in the figure above is the timing of reading the current address . The composite format for reading the specified address is not given here, and we need to stitch it together according to the principle!

3. Other timing

There are also two timing sequences of slave sending and slave receiving in the manual , but this is in multi-master mode, and it is related when STM32 is used as a slave, which is not involved in this study!

4. Expansion: software/hardware I2C waveform comparison

 The above is the timing of reading the specified address . From the perspective of the change trend of the pin level , the two waveforms are the same, and the corresponding data is also the same.

From the regularity of the clock line , the hardware I2C is more regular, and the cycle and duty cycle are very consistent; while the software I2C has a delay after the operation pin, resulting in irregularity, but because I2C is a synchronous timing, the SCL line is also irregular. does not affect.

I have learned before that SCL writes at low level and reads at high level. Although the entire level can be read and written at any time, it is generally necessary to ensure the principle of as soon as possible , so it can be considered that SCL is written on the falling edge and read on the rising edge . After the falling edge of software I2C, there will be some delay after operating the port, so it will wait for a while to write; but in hardware I2C, data writing is close to the falling edge of SCL, and reading is also close to the rising edge (marked on the figure The line is a bit inaccurate).

3. Hardware I2C read and write MPU6050

wiring diagram

The wiring diagram is the same as the last software reading and writing, but the GPIO interface here cannot be changed at will! Here PB10, PB11 explain the I2C2 used !

The program code is different from the software I2C in the underlying MyI2C.c file of communication! It's not needed here anymore, remove it! Then the place where MyI2C.c is used in MPU6050.c should also be implemented with hardware configuration , and the rest of the MPU650’s 1 configuration and read data code do not need to be changed

#include "stm32f10x.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();
//
//	MyI2C_Stop();
	//此处参考学习笔记注意事项1
	I2C_GenerateSTART(I2C2,ENABLE);
    while(I2C_CheckEvent(I2C2,I2C_EVENT_MASTER_MODE_SELECT) != SUCCESS);//等待EV5
	
	I2C_Send7bitAddress(I2C2,MPU6050_ADDRESS ,I2C_Direction_Transmitter);//自带接收应答过程(如果错误会通过置标志位或中断提示)
	while(I2C_CheckEvent(I2C2,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) != SUCCESS);//等待EV6
	
	//EV8_1事件不需要等待,它是告诉该写入DR发送数据了,下一步直接写入数据
	I2C_SendData(I2C2,RegAddress);
	while(I2C_CheckEvent(I2C2,I2C_EVENT_MASTER_BYTE_TRANSMITTING) != SUCCESS);//等待EV8
	
	I2C_SendData(I2C2,Data);//这里就发送1个字节
	while(I2C_CheckEvent(I2C2,I2C_EVENT_MASTER_BYTE_TRANSMITTED) != SUCCESS);//最后等待EV8_2事件
	
	I2C_GenerateSTOP(I2C2,ENABLE);
	
	
}

uint8_t MPU6050_ReadReg(uint8_t RegAddress){//指定地址读函数,控制外设电路实现
	uint8_t Data;
//	MyI2C_Start();
//	MyI2C_SendByte(MPU6050_ADDRESS);
//	MyI2C_ReceiveAck();
//	MyI2C_SendByte(RegAddress);
//	MyI2C_ReceiveAck();
	I2C_GenerateSTART(I2C2,ENABLE);
    while(I2C_CheckEvent(I2C2,I2C_EVENT_MASTER_MODE_SELECT) != SUCCESS);//等待EV5
	I2C_Send7bitAddress(I2C2,MPU6050_ADDRESS ,I2C_Direction_Transmitter);//第三个参数为读写标志
	while(I2C_CheckEvent(I2C2,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) != SUCCESS);//等待EV6
	I2C_SendData(I2C2,RegAddress);
	while(I2C_CheckEvent(I2C2,I2C_EVENT_MASTER_BYTE_TRANSMITTING) != SUCCESS);//等待EV8	
//	MyI2C_Start();
//	MyI2C_SendByte(MPU6050_ADDRESS | 0x01);
//	MyI2C_ReceiveAck();
//	Data = MyI2C_ReceiveByte();
//	MyI2C_SendAck(1);
//	MyI2C_Stop();
	I2C_GenerateSTART(I2C2,ENABLE);
	while(I2C_CheckEvent(I2C2,I2C_EVENT_MASTER_MODE_SELECT) != SUCCESS);//等待EV5
	I2C_Send7bitAddress(I2C2,MPU6050_ADDRESS,I2C_Direction_Receiver);//第三个参数为读写标志
	while(I2C_CheckEvent(I2C2,I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED) != SUCCESS);//等待EV6
	
	//此处参考下学习笔记注意事项2
	I2C_AcknowledgeConfig(I2C2,DISABLE);
	I2C_GenerateSTOP(I2C2,ENABLE);//这两步按规定提前到了接收一个字节之前
	//...接收一个字节过程
	while(I2C_CheckEvent(I2C2,I2C_EVENT_MASTER_BYTE_RECEIVED) != SUCCESS);//等待EV7。等待到EV7后,一个字节的数据就在DR里面了
	Data = I2C_ReceiveData(I2C1);//读取数据
	
	I2C_AcknowledgeConfig(I2C2,ENABLE);//默认情况下ACK为1,方便之后改进代码接收多个字节
	return Data;
}

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


void MPU6050_Init(void){
    //MyI2C_Init();
	//配置硬件I2C2外设,实现初始化:
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD ;//复用开漏输出模式(并不意味只可输出)
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10| GPIO_Pin_11;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB,&GPIO_InitStructure);
	
	I2C_InitTypeDef I2C_InitStructure;
	I2C_InitStructure.I2C_Mode= I2C_Mode_I2C;
	I2C_InitStructure.I2C_ClockSpeed= 50000;//最大400kHz
	I2C_InitStructure.I2C_DutyCycle= I2C_DutyCycle_2;//这个占空比是为了快速传输设计的
	I2C_InitStructure.I2C_Ack= I2C_Ack_Enable;
	I2C_InitStructure.I2C_AcknowledgedAddress= I2C_AcknowledgedAddress_7bit;//STM32作为从机时的响应地址位数
	I2C_InitStructure.I2C_OwnAddress1= 0x00;//STM32作为从机时,设定的从机地址
	I2C_Init(I2C2,&I2C_InitStructure);
	
	I2C_Cmd(I2C2,ENABLE);
	//硬件I2C初始化完毕
	
	MPU6050_WriteReg(MPU6050_PWR_MGMT_1,0x01);
	MPU6050_WriteReg(MPU6050_PWR_MGMT_2,0x00);
	MPU6050_WriteReg(MPU6050_SMPLRT_DIV,0x09);
	MPU6050_WriteReg(MPU6050_CONFIG	,0x06);
	MPU6050_WriteReg(MPU6050_GYRO_CONFIG,0x18);
	MPU6050_WriteReg(MPU6050_ACCEL_CONFIG,0x18);
}

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);
	DataL = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_L);
	*AccX = (DataH << 8)|DataL;
	
	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);
	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){
	return MPU6050_ReadReg(MPU6050_WHO_AM_I	);
}

Note 1 : The software I2C commented out above has a Delay operation inside each step, which is a blocking process, that is, (each step) after the function is completed, the corresponding waveform must also be sent, and the functions are followed closely. . The following hardware I2C (each step) function only sets 1 to a specific register position, regardless of whether the waveform is sent or not, it belongs to the non-blocking mode, so it must wait for the corresponding flag bit later to ensure that the function is executed in place.

Note 2 : After entering the host receiving mode here, it starts to receive the data waveform sent by the slave. According to the timing diagram, there is an EV6_1 event when receiving a data byte. This event has no flag and does not need to wait. It is suitable for the case of receiving a byte , and we demonstrate here that it is also receiving a byte, and the generation bit of the response and stop condition must be cleared. That is, at this time, the response bit ACK should be set to 0, and the stop condition generation bit STOP should be set to 1 at the same time. Is it necessary to set the stop bit to 1 when a byte is not received? Yes, it is stipulated here that ACK should be set to 0 and STOP should be set to 1 in advance . Receive another byte.

Note 3 : It can be seen that there are a large number of while infinite loop waiting flags in the program. This kind of infinite loop is very dangerous to the program. Once an event has not occurred, the entire program will be stuck, so you can add it after the infinite loop wait. The last timeout exit mechanism is improved by taking the first while infinite loop as an example:

uint32_t Timeout;

I2C_GenerateSTART(I2C2,ENABLE);
Timeout = 10000;//实际需要多长时间可实验下
while(I2C_CheckEvent(I2C2,I2C_EVENT_MASTER_MODE_SELECT) != SUCCESS){
    Timeout--;
    if(Timeout == 0){
        break;
        }
}

But it's not very beautiful to write like this. Encapsulate a function:

void MPU6050_WaitEnvent(I2C_TypeDef *I2Cx, uint32_t I2C_EVENT){
    uint32_t Timeout;
    I2C_GenerateSTART(I2C2,ENABLE);
    Timeout = 10000;//实际需要多长时间可实验下
    while(I2C_CheckEvent(I2Cx,I2C_EVENT) != SUCCESS){//传递参数,可通用
        Timeout--;
        if(Timeout == 0){
            break;//当然这里可写一些紧急处理函数,比如显示报错等
            }
    }

}
...

//然后把while(I2C_CheckEvent(I2C2,I2C_EVENT_MASTER_MODE_SELECT) != SUCCESS);改为
MPU6050_WaitEnvent(I2C2,I2C_EVENT_MASTER_MODE_SELECT);

...

Summarize

When you encounter setbacks, you must have the courage to move forward, act immediately, stick to the end, and never give up. Those who succeed will never give up, and those who give up will never succeed. On the road to success, there will definitely be failures; for failures, we must treat and treat them correctly, those who are not afraid of failures will surely succeed; those who are afraid of failures will be useless and will fail even more.
This is the end of today's learning sharing, see you next time! !

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/129863900