Embedded development-STM32 hardware I2C drive OLED screen

Embedded development-STM32 hardware I2C drive OLED screen

Introduction to I2C

The I2C bus is a simple, two-way two-wire synchronous serial bus developed by Philips. It requires only two wires to transfer information between devices connected to the bus.
The master device is used to start the bus to transmit data and generate the clock to open the transmitting device. At this time, any addressed device is considered as a slave device. The relationship between master and slave, send and receive on the bus is not constant, but depends on the direction of data transmission at this time. If the host wants to send data to the slave device, the host first addresses the slave device, then actively sends data to the slave device, and finally the host terminates the data transmission; if the host wants to receive data from the slave device, the master device first addresses the slave device. Then the host receives the data sent from the device, and finally the host terminates the receiving process. in this case. The host is responsible for generating the timing clock and terminating data transfers.

STM32 I2C

It is rumored that the hardware I2C of STM32 is easy to crash, so the hardware I2C cannot be used, and the punctual atom also emphasizes this point in the tutorial. I personally guess that because Philips has a patent, and ST has made the hardware I2C extremely complicated in order to bypass the patent (as can be seen from the number of related registers and settings), making the hardware I2C very difficult to use and prone to abnormal crashes.

Today I will challenge you. Try to use STM32F103C8T6 to drive OLED screen with hardware I2C.

Hardware connection between MCU and OLED

They are all conventional settings: turn on the external clock, turn on the SWD debugging interface, turn on I2C2, the configuration is default, and the LED can be used or not.
insert image description here

OLED drive function

This is modified by referring to the software-driven I2C routine of the punctual atom, and the relevant code of the software-driven IO port level is changed to the HAL_I2C_Mem_Write() function to drive.

Definition of GRAM

There is a problem. In the code of punctual atoms, there is a problem with the definition of GRAM, as follows:

OLED_GRAM[144][8];	//行定义是Y值,列定义是X值

144 is the value of X, and 8 is the value of Y, which is in line with the rules and there is no problem.
When OLED is driven, it is required to send the value of X continuously, and then send another line after one line is sent. This is also done in the code of punctual atoms. It does not fetch the value by the row of the array, but by the column of the array. No problem for software I2C.
However, when sending data continuously through the HAL_I2C_Mem_Write() function, the sending process is to provide the first address of the array, and then the address is incremented, that is, the 8 data of Y are sent first, and then the entire column is sent continuously, because the array is configured in this way, so that The ____ does not work.
So change the definition of X and Y to the following

OLED_GRAM[8] [144];  //行定义改为X值,列定义改为Y值

In this way, when using the HAL_I2C_Mem_Write() function to send data continuously, it will be normal.

Of course, all related functions such as drawing dots, drawing lines and writing characters need to be modified accordingly, so I won’t list them one by one here. You can download the complete project document if you need it, and there is a link at the end of the article.

Display content

The displayed content is very simple, that is, alternately display 2 lines of characters, so that if the program crashes, the screen will definitely not move
insert image description here
insert image description here

artificial interference

Lead the two lines of SCL and SDA through a 120 ohm resistor, and the pin can be forcibly pulled high or low through the 120 ohm resistor, which is convenient for testing. Then touch the ground separately, and touch each other. It is absolutely not possible to directly lead out the welding wire and then touch it directly, otherwise the power supply will be burned due to the short circuit of the power supply to the ground.
It can be found that once the force is lowered, the screen will not switch display immediately, or the screen will be black directly, depending on the running position of the program at the stuck point.
But the program is still running normally at this time, you can interrupt the point and trace debugging, so it is judged that the fault is that the forced low-level just now caused the OLED screen to run in disorder.

modify the code

Checking the code found that there is a problem with the following statement

void oled_write_onebyte(u8 data, u8 cmd)
{
  HAL_I2C_Mem_Write(&hi2c2, OLED_ADDR, 0, I2C_MEMADD_SIZE_8BIT, &data, 1, 1000);
}


void oled_write_bytes(u8* data, u8 len, u8 cmd)
{
  HAL_I2C_Mem_Write(&hi2c2, OLED_ADDR, cmd, I2C_MEMADD_SIZE_8BIT, data, len, 1000);
}

That is to say, when STM32 writes data into the OLED register, the data is abnormal due to human operation, and the internal register parameters of the OLED are messed up, resulting in abnormal operation.
The targeted solution is also simple and rude. Re-initialize the OLED and power it on again. Change the code as follows:

void oled_write_onebyte(u8 data, u8 cmd)
{
  u32 ret;
  ret = HAL_I2C_Mem_Write(&hi2c2, OLED_ADDR, 0, I2C_MEMADD_SIZE_8BIT, &data, 1, 1000);
  if(ret!=0)
  {
    oled_init();
    OLED_DisPlay_Off();
    HAL_Delay(10);
    OLED_DisPlay_On();
  }
}

void oled_write_bytes(u8* data, u8 len, u8 cmd)
{
  u32 ret;
  ret = HAL_I2C_Mem_Write(&hi2c2, OLED_ADDR, cmd, I2C_MEMADD_SIZE_8BIT, data, len, 1000);
  if(ret!=0)
  {
    oled_init();
    OLED_DisPlay_Off();
    HAL_Delay(10);
    OLED_DisPlay_On();
  }
}

repetitive artificial interference

It is found that the display is still not normal. After checking, it is found that the program has a problem with the I2C_RequestMemoryWrite() function. Simply and rudely initialize the I2C again.

void oled_write_onebyte(u8 data, u8 cmd)
{
  u32 ret;
  ret = HAL_I2C_Mem_Write(&hi2c2, OLED_ADDR, 0, I2C_MEMADD_SIZE_8BIT, &data, 1, 1000);
  if(ret!=0)
  {
    MX_I2C2_Init();
    oled_init();
    OLED_DisPlay_Off();
    HAL_Delay(10);
    OLED_DisPlay_On();
  }
}

void oled_write_bytes(u8* data, u8 len, u8 cmd)
{
  u32 ret;
  ret = HAL_I2C_Mem_Write(&hi2c2, OLED_ADDR, cmd, I2C_MEMADD_SIZE_8BIT, data, len, 1000);
  if(ret!=0)
  {
    MX_I2C2_Init();
    oled_init();
    OLED_DisPlay_Off();
    HAL_Delay(10);
    OLED_DisPlay_On();
  }
}

The famous HardFault_Handler error

At this time, the program works basically normally and can recover from the fault. However, after maintaining the artificial abnormal state for a long time (more than ten seconds), there is still a small chance that the program will be stuck, and the program will jump to HardFault_Handler. It took some time to check this, and finally It was found that it was
a stack overflow, as shown in the figure below:
insert image description here

Pay attention to the drag bar pointed by the arrow. The two functions of oled_init and oled_write_onebyte have been repeated N times. If it continues, the stack overflow will definitely not run.
This problem is because the oled_write_onebyte function is called in the oled_init function, and when this function is running, if it is still in a fault state, it will call oled_init again, so that nesting will be formed, and more nesting will lead to stack overflow .
The solution is still simple and rude, adding delay.
Didn't you take a long time and repeat too many times, causing the stack to overflow? I will add a delay for you so that you can't run so many times in a few tens of minutes.
Of course, this rule of law does not cure the root cause, but who has nothing to do to short-circuit an OLED screen for so long. If anyone has a better solution, please leave a message below to discuss.

void oled_write_onebyte(u8 data, u8 cmd)
{
  u32 ret;
  ret = HAL_I2C_Mem_Write(&hi2c2, OLED_ADDR, 0, I2C_MEMADD_SIZE_8BIT, &data, 1, 1000);
  if(ret!=0)
  {
    MX_I2C2_Init();
HAL_Delay(100);
oled_init();
    OLED_DisPlay_Off();
    HAL_Delay(100);
    OLED_DisPlay_On();
  }
}

void oled_write_bytes(u8* data, u8 len, u8 cmd)
{
  u32 ret;
  ret = HAL_I2C_Mem_Write(&hi2c2, OLED_ADDR, cmd, I2C_MEMADD_SIZE_8BIT, data, len, 1000);
  if(ret!=0)
  {
    MX_I2C2_Init();
    HAL_Delay(100);
    oled_init();
    OLED_DisPlay_Off();
    HAL_Delay(100);
    OLED_DisPlay_On();
  }
}

So far the fault is resolved.
Hey, where is the problem that the legendary I2C is not easy to use? Isn't it enough to reset and start again if something goes wrong? For a company as big as ST, if even an I2C can't be used, can his films still sell so well? Is it possible to achieve tens of billions in annual sales?
So don't copy what others say, if you have a problem, go through it yourself, try it, and maybe you can solve it yourself.

Complete project link

Complete project, including CubeMX project, Keil project, the link is as follows: Embedded Development - STM32 Hardware I2C Drive OLED Screen

Guess you like

Origin blog.csdn.net/13011803189/article/details/127728172