Using STM32 Hardware I2C to Read AHT10 Temperature and Humidity Sensor Data and Display on 0.96-inch OLED Screen.
The MCU I use is STM32F103C8T6, and the program is written with the ST Standard Peripheral Library.
STM32 uses hardware I2C to read SHTC3 temperature and humidity sensor: https://blog.zeruns.com/archives/692.html
Electronics/MCU Technical Group: 2169025065
Implementation Effect Pictures
Brief Introduction to I2C Protocol
The I2C communication protocol (Inter-Integrated Circuit) was developed by Philips. Thanks to its few pins, simple hardware implementation, strong extensibility, and no need for external transceiver devices like USART or CAN (those level-shifting chips), it is now widely used for communication among multiple ICs within a system.
I2C has only one data bus line, SDA (Serial Data Line), a serial data bus that can only send data bit by bit, belonging to serial communication and using half-duplex communication.
Half-duplex communication: bidirectional communication is possible, but not simultaneously in both directions; it must alternate. It can also be understood as a switchable single-direction communication. At the same moment only one direction is allowed, requiring just one data line.
We divide the I2C protocol into the physical layer and the protocol layer. The physical layer specifies the mechanical and electronic characteristics of the communication system (hardware part), ensuring raw data transmission over the physical medium. The protocol layer mainly defines communication logic, unifying data packaging and unpacking standards for both sender and receiver (software level).
I2C Physical Layer
Common Connection Between I2C Devices
(1) It is a multi-device bus. “Bus” means signal lines shared by multiple devices. In one I2C bus, multiple I2C devices can be connected, supporting multiple masters and multiple slaves.
(2) An I2C bus uses only two lines: one bidirectional serial data line SDA (Serial Data Line), and one serial clock line SCL (Serial Clock Line). The data line represents data, and the clock line synchronizes data transmission.
(3) The bus is pulled up to the supply voltage through pull-up resistors. When an I2C device is idle it outputs high-impedance, and when all devices are idle and output high-impedance, the pull-up resistors pull the bus high.
During I2C communication, the MCU GPIO pins must be configured as open-drain outputs; otherwise a short circuit may occur.
For more STM32 I2C information and usage, see this article: https://url.zeruns.com/JC0Ah
I won’t go into detail here.
AHT10 Temperature and Humidity Sensor
Introduction
AHT10 is a domestic temperature and humidity sensor chip, cheap, accurate, and small.
AHT10 features a newly designed ASIC, an improved MEMS semiconductor capacitive humidity element, and a standard on-chip temperature element. Its performance has greatly surpassed the reliability of previous sensors. The new generation sensor is improved for more stable performance in harsh environments.
AHT10 datasheet download: https://url.zeruns.com/EDEwF
From the datasheet we can get a rough idea:
- Temperature range: –40 °C ~ 85 °C
- Temperature accuracy: ±0.3 °C
- Humidity range: 0 % ~ 100 %
- Humidity accuracy: ±2 %
- Operating voltage: 1.8 V ~ 3.6 V
- Communication: I2C
- Clock frequency: 100 kHz and 400 kHz
Find the following key information
Temperature and Humidity Device Address and Read/Write Commands
In actual use, the AHT10 device address must be combined with the read/write direction bit into one byte sent together. The LSB is the read/write direction bit, and the upper 7 bits are the AHT10 device address.
To write data or commands to AHT10 via I2C, after the I2C start condition, send “0111 0000”, i.e. 0x70, to AHT10. The upper 7 bits “0111 000” address the device, and the LSB “0” tells AHT10 that the next operation is write.
To read data from AHT10 via I2C, after the I2C start condition, send “0111 0001”, i.e. 0x71, to AHT10. The upper 7 bits address the device, and the LSB “1” tells AHT10 that the next operation is read.
In short, 0x70 means write, 0x71 means read. However, when using STM32 hardware I2C, just enter 0x70; the LSB is handled by the standard library.
Reading Temperature and Humidity Data
From the datasheet, one measurement cycle includes three steps:
- Send measurement command
- Wait for measurement to complete
- Read measured data
Summary:
- Send measurement command: first send write command (0x70), then send trigger measurement command (0xAC), then send command parameters (0x33 and 0x00).
- Wait for measurement to complete: datasheet says 75 ms; wait longer than this.
- Receive data: send read command (0x71), continuously receive 6 bytes. The first received byte is the status byte. Check if bit 3 (calibration enable) is 1; if not, send initialization command. Check bit 7 (busy flag); if 0, measurement is complete, proceed.
- Convert and process the received data.
Data Calculation
From the AHT10 datasheet:
Example: collected humidity value is 0x0C6501, decimal 812289.
Then: humidity = 812289 × 100 / 1048576 = 77.46 (unit: %)
Collected temperature value is 0x056A00, decimal 354816.
Then: temperature = (354816 × 200 / 1048576) – 50 = 17.67 (unit: °C)
Required Components
STM32 minimum system board: https://s.click.taobao.com/bqMwZRu
AHT10 module: https://s.click.taobao.com/gIF09Ru
OLED module: https://s.click.taobao.com/aNlvZRu
Dupont wires: https://s.click.taobao.com/xAkAJRu
Breadboard: https://s.click.taobao.com/ShJAJRu
ST-LINK V2: https://s.click.taobao.com/C8ftZRu
Program
Here I only post the three main code files: main.c, AHT10.c and OLED.c. For the rest, please download the zip package below.
Complete project files: https://url.zeruns.com/AHT10
For AHT10 and OLED modules, connect SCL to PB6 and SDA to PB7.
Using VSCode instead of Keil for STM32 and 51 development: https://blog.zeruns.com/archives/690.html
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "IWDG.h"
#include "AHT10.h"
uint16_t numlen(uint16_t num);
int main(void)
{
IWDG_Configuration(); // Initialize watchdog
OLED_Init(); // Initialize OLED screen
AHT10_Init(); // Initialize AHT10
OLED_ShowString(1, 1, "T:");
OLED_ShowString(2, 1, "H:");
uint32_t a=0;
uint16_t err_count=0;
while (1)
{
a++;
OLED_ShowNum(3, 1, a, 9); // Counter display, easy to see if program is running
if(a==999999999)a=0;
float Temp,Hum; // Declare variables for temperature and humidity data
/*
https://blog.zeruns.com
*/
if(ReadAHT10(&Hum,&Temp)) // Read temperature and humidity data
{
if(Temp>=0)
{
char String[10];
sprintf(String, "+%.2fC", Temp); // Format and output to string variable
OLED_ShowString(1, 3, String); // Display temperature
sprintf(String, " %.2f%%", Hum); // Format and output to string variable
OLED_ShowString(2, 3, String); // Display humidity
}else
{
char String[10];
sprintf(String, "-%.2fC", Temp); // Format and output to string variable
OLED_ShowString(1, 3, String); // Display temperature
sprintf(String, " %.2f%%", Hum); // Format and output to string variable
OLED_ShowString(2, 3, String); // Display humidity
}
}
else
{
err_count++;
OLED_ShowNum(4,1, err_count, 5); // Display error count
}
Delay_ms(100); // Delay 100 ms
IWDG_FeedDog(); // Feed the dog (watchdog, auto-reset if not fed within 1 second)
}
}
```**AHT10.c**
```c
#include "stm32f10x.h"
#include "Delay.h"
#include "OLED.h"
/*AHT10 Address*/
#define AHT10_ADDRESS 0x38<<1 //The slave address is 7-bit, the last bit is the transfer direction bit, so shift left by 1
/*Set which I2C to use*/
#define I2Cx I2C1
/*
https://blog.zeruns.com
*/
/*Send start signal*/
void AHT10_I2C_START(){
while( I2C_GetFlagStatus(I2Cx, I2C_FLAG_BUSY));//Wait for bus idle
I2C_GenerateSTART(I2Cx, ENABLE);//Send start signal
while( I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_MODE_SELECT)==ERROR);//Check EV5 event
}
/*Send stop signal*/
void AHT10_I2C_STOP(){
I2C_GenerateSTOP(I2Cx, ENABLE);//Send stop signal
}
/**
* @brief Send 3 bytes of data
* @param cmd Command byte
* @param DATA0 First parameter
* @param DATA1 Second parameter
* @retval None
*/
void AHT10_WriteByte(uint8_t cmd, uint8_t DATA0, uint8_t DATA1)
{
AHT10_I2C_START(); //Send start signal
I2C_Send7bitAddress(I2Cx, AHT10_ADDRESS, I2C_Direction_Transmitter); //Send device write address
while(I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)==ERROR); //Check EV6 event
I2C_SendData(I2Cx, cmd);//Send command
while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED));//Check EV8 event
I2C_SendData(I2Cx, DATA0);//Send high 8-bit data of command parameter
while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED));//Check EV8 event
I2C_SendData(I2Cx, DATA1);//Send low 8-bit data of command parameter
while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED));//Check EV8 event
I2C_GenerateSTOP(I2Cx, ENABLE);//Send stop signal
}
/**
* @brief Send command to read AHT10 status
* @retval Read status byte
*/
/*uint8_t AHT10_ReadStatus(void){
AHT10_I2C_START();//Send start signal
I2C_Send7bitAddress(I2Cx,AHT10_ADDRESS,I2C_Direction_Receiver);//Send device read address
while( I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED )==ERROR);//Check EV6 event
while (!I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_BYTE_RECEIVED));//Check EV7 event
I2C_AcknowledgeConfig(I2Cx, DISABLE); //Disable acknowledge signal
uint8_t status = I2C_ReceiveData(I2Cx);//Read data and return
AHT10_I2C_STOP(); //Send stop signal
I2C_AcknowledgeConfig(I2Cx,ENABLE);//Re-enable acknowledge signal
return status;
}*/
/**
* @brief Read data
* @retval Read byte data
*/
uint8_t AHT10_ReadData(void)
{
while (!I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_BYTE_RECEIVED));//Check EV7 event
return I2C_ReceiveData(I2Cx);//Read data and return
}
/*Software reset AHT10*/
void AHT10_SoftReset(void)
{
AHT10_I2C_START(); //Send start signal
I2C_Send7bitAddress(I2Cx, AHT10_ADDRESS, I2C_Direction_Transmitter); //Send device write address
while(I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)==ERROR); //Check EV6 event
I2C_SendData(I2Cx, 0xBA);//Send soft reset command
while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED));//Check EV8 event
I2C_GenerateSTOP(I2Cx, ENABLE);//Send stop signal
Delay_ms(20);
}
/*Pin initialization*/
void AHT10_Init(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,ENABLE); //Enable I2C1 clock
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);//Enable GPIOB clock
/*STM32F103 chip hardware I2C1: PB6 -- SCL; PB7 -- SDA */
GPIO_InitTypeDef GPIO_InitStructure; //Define structure to configure GPIO
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD; //Set output mode to open-drain output, pull-up resistor required
GPIO_Init(GPIOB, &GPIO_InitStructure); //Initialize GPIO
I2C_DeInit(I2Cx); //Reset I2C peripheral registers to default values
I2C_InitTypeDef I2C_InitStructure; //Define structure to configure I2C
I2C_InitStructure.I2C_Mode = I2C_Mode_I2C; //Operating mode
I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2; //Clock duty cycle, Tlow/Thigh = 2
I2C_InitStructure.I2C_OwnAddress1 = 0x88; //Host I2C address, write randomly if not used, no effect
I2C_InitStructure.I2C_Ack = I2C_Ack_Enable; //Enable acknowledge bit
I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;//Set address length to 7 bits
I2C_InitStructure.I2C_ClockSpeed = 400000; //I2C transfer speed, 400K, check chip datasheet for supported speed.
I2C_Init(I2Cx, &I2C_InitStructure); //Initialize I2C
I2C_Cmd(I2Cx, ENABLE); //Enable I2C
Delay_ms(20);//Power-on delay
AHT10_WriteByte(0XE1,0X08,0x00);//Send command to initialize
Delay_ms(20);
}
/**
* @brief Read AHT10 data
* @param *Hum Humidity
* @param *Temp Temperature
* @retval 1 - Read success; 0 - Read failure
*/
uint8_t ReadAHT10(float *Hum,float *Temp)
{
uint8_t Data[5];//Declare variable to store read data
AHT10_WriteByte(0XAC,0X33,0x00);//Send command to trigger measurement
Delay_ms(70); //Delay 70ms for measurement completion
AHT10_I2C_START();//Send start signal
I2C_Send7bitAddress(I2Cx,AHT10_ADDRESS,I2C_Direction_Receiver);//Send device read address
while( I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED )==ERROR);//Check EV6 event
uint8_t i;
for(i=0;i<6;i++)//Loop 6 times to read 6 bytes of data
{
if (i == 5) //Disable acknowledge signal when reading the last byte
{
I2C_AcknowledgeConfig(I2Cx, DISABLE); //Disable acknowledge signal
}
Data[i] = AHT10_ReadData(); //Read data
if (i == 5)
I2C_GenerateSTOP(I2Cx, ENABLE); //Send stop signal
}
I2C_AcknowledgeConfig(I2Cx,ENABLE);//Re-enable acknowledge signal
if( (Data[0]&0x08) == 0 )//0x08(00001000) Check if bit 3 of status byte (calibration enable bit) is 0
{
AHT10_WriteByte(0XE1,0X08,0x00); //Send command to initialize
Delay_ms(20);
return 0;
}
else if( (Data[0]&0x80) == 0 )//0x80(10000000) Check if bit 7 of status byte (busy flag) is 0
{
uint32_t SRH = (Data[1]<<12) | (Data[2]<<4) | (Data[3]>>4); //Humidity data processing
uint32_t ST = ((Data[3]&0x0f)<<16) | (Data[4]<<8) | Data[5];//Temperature data processing
*Hum = (SRH * 100.0) / 1024.0 / 1024; //Convert humidity data according to formula in datasheet
*Temp = (ST * 200.0) / 1024.0 / 1024 - 50; //Convert temperature data according to formula in datasheet
return 1;
}
I2C_GenerateSTOP(I2Cx, ENABLE);//Send stop signal
return 0;
}
/*
https://blog.zeruns.com
*/
```**OLED.c**
```c
#include "stm32f10x.h"
#include "OLED_Font.h"
/*OLED screen address*/
#define OLED_ADDRESS 0x78
/*Which I2C to use*/
#define I2Cx I2C1
/*
https://blog.zeruns.com
*/
/*Pin initialization*/
void OLED_I2C_Init(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,ENABLE); //Enable I2C1 clock
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);//Enable GPIOB clock
/*STM32F103 hardware I2C: PB6 -- SCL; PB7 -- SDA */
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD; //Set output mode to open-drain, pull-up resistor required
GPIO_Init(GPIOB, &GPIO_InitStructure);
I2C_DeInit(I2Cx); //Reset I2C peripheral registers to default values
I2C_InitTypeDef I2C_InitStructure;
I2C_InitStructure.I2C_Mode = I2C_Mode_I2C; //Operating mode
I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2; //Clock duty cycle, Tlow/Thigh = 2
I2C_InitStructure.I2C_OwnAddress1 = 0x88; //Host I2C address, unused so any value is fine, no effect
I2C_InitStructure.I2C_Ack = I2C_Ack_Enable; //Enable acknowledge bit
I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;//Set address length to 7 bits
I2C_InitStructure.I2C_ClockSpeed = 400000; //I2C transfer speed, 400K, check chip datasheet for supported speeds.
I2C_Init(I2Cx, &I2C_InitStructure);
I2C_Cmd(I2Cx, ENABLE);
}
void I2C_WriteByte(uint8_t addr,uint8_t data)
{
while( I2C_GetFlagStatus(I2Cx, I2C_FLAG_BUSY));
//Send start signal
I2C_GenerateSTART(I2Cx, ENABLE);
//Check EV5 event
while( I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_MODE_SELECT)==ERROR);
//Send device write address
I2C_Send7bitAddress(I2Cx, OLED_ADDRESS, I2C_Direction_Transmitter);
//Check EV6 event
while( I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)==ERROR);
//Send internal address to operate
I2C_SendData(I2Cx, addr);
//Check EV8_2 event
while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
I2C_SendData(I2Cx, data);//Send data
while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
//Send stop signal
I2C_GenerateSTOP(I2Cx, ENABLE);
}
/**
* @brief OLED write command
* @param Command command to write
* @retval none
*/
void OLED_WriteCommand(unsigned char Command)//write command
{
I2C_WriteByte(0x00, Command);
}
/**
* @brief OLED write data
* @param Data data to write
* @retval none
*/
void OLED_WriteData(unsigned char Data)//write data
{
I2C_WriteByte(0x40, Data);
}
/**
* @brief OLED set cursor position
* @param Y coordinate from top-left origin, down direction, range: 0~7
* @param X coordinate from top-left origin, right direction, range: 0~127
* @retval none
*/
void OLED_SetCursor(uint8_t Y, uint8_t X)
{
OLED_WriteCommand(0xB0 | Y); //Set Y position
OLED_WriteCommand(0x10 | ((X & 0xF0) >> 4)); //Set X position low 4 bits
OLED_WriteCommand(0x00 | (X & 0x0F)); //Set X position high 4 bits
}
/**
* @brief OLED clear screen
* @param none
* @retval none
*/
void OLED_Clear(void)
{
uint8_t i, j;
for (j = 0; j < 8; j++)
{
OLED_SetCursor(j, 0);
for(i = 0; i < 128; i++)
{
OLED_WriteData(0x00);
}
}
}
/**
* @brief OLED partial clear
* @param Line row position, range: 1~4
* @param start column start position, range: 1~16
* @param end column end position, range: 1~16
* @retval none
*/
void OLED_Clear_Part(uint8_t Line, uint8_t start, uint8_t end)
{
uint8_t i,Column;
for(Column = start; Column <= end; Column++)
{
OLED_SetCursor((Line - 1) * 2, (Column - 1) * 8); //Set cursor to upper half
for (i = 0; i < 8; i++)
{
OLED_WriteData(0x00); //Clear upper half content
}
OLED_SetCursor((Line - 1) * 2 + 1, (Column - 1) * 8); //Set cursor to lower half
for (i = 0; i < 8; i++)
{
OLED_WriteData(0x00); //Clear lower half content
}
}
}
/**
* @brief OLED display a character
* @param Line row position, range: 1~4
* @param Column column position, range: 1~16
* @param Char character to display, range: ASCII visible characters
* @retval none
*/
void OLED_ShowChar(uint8_t Line, uint8_t Column, char Char)
{
uint8_t i;
OLED_SetCursor((Line - 1) * 2, (Column - 1) * 8); //Set cursor to upper half
for (i = 0; i < 8; i++)
{
OLED_WriteData(OLED_F8x16[Char - ' '][i]); //Display upper half content
}
OLED_SetCursor((Line - 1) * 2 + 1, (Column - 1) * 8); //Set cursor to lower half
for (i = 0; i < 8; i++)
{
OLED_WriteData(OLED_F8x16[Char - ' '][i + 8]); //Display lower half content
}
}
/**
* @brief OLED display string
* @param Line starting row position, range: 1~4
* @param Column starting column position, range: 1~16
* @param String string to display, range: ASCII visible characters
* @retval none
*/
void OLED_ShowString(uint8_t Line, uint8_t Column, char *String)
{
uint8_t i;
for (i = 0; String[i] != '\0'; i++)
{
OLED_ShowChar(Line, Column + i, String[i]);
}
}
/**
* @brief OLED power function
* @retval returns X to the power of Y
*/
uint32_t OLED_Pow(uint32_t X, uint32_t Y)
{
uint32_t Result = 1;
while (Y--)
{
Result *= X;
}
return Result;
}
/**
* @brief OLED display number (decimal, positive)
* @param Line starting row position, range: 1~4
* @param Column starting column position, range: 1~16
* @param Number number to display, range: 0~4294967295
* @param Length length of number to display, range: 1~10
* @retval none
*/
void OLED_ShowNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length)
{
uint8_t i;
for (i = 0; i < Length; i++)
{
OLED_ShowChar(Line, Column + i, Number / OLED_Pow(10, Length - i - 1) % 10 + '0');
}
}
/**
* @brief OLED display number (decimal, signed)
* @param Line starting row position, range: 1~4
* @param Column starting column position, range: 1~16
* @param Number number to display, range: -2147483648~2147483647
* @param Length length of number to display, range: 1~10
* @retval none
*/
void OLED_ShowSignedNum(uint8_t Line, uint8_t Column, int32_t Number, uint8_t Length)
{
uint8_t i;
uint32_t Number1;
if (Number >= 0)
{
OLED_ShowChar(Line, Column, '+');
Number1 = Number;
}
else
{
OLED_ShowChar(Line, Column, '-');
Number1 = -Number;
}
for (i = 0; i < Length; i++)
{
OLED_ShowChar(Line, Column + i + 1, Number1 / OLED_Pow(10, Length - i - 1) % 10 + '0');
}
}
/**
* @brief OLED display number (hexadecimal, positive)
* @param Line starting row position, range: 1~4
* @param Column starting column position, range: 1~16
* @param Number number to display, range: 0~0xFFFFFFFF
* @param Length length of number to display, range: 1~8
* @retval none
*/
void OLED_ShowHexNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length)
{
uint8_t i, SingleNumber;
for (i = 0; i < Length; i++)
{
SingleNumber = Number / OLED_Pow(16, Length - i - 1) % 16;
if (SingleNumber < 10)
{
OLED_ShowChar(Line, Column + i, SingleNumber + '0');
}
else
{
OLED_ShowChar(Line, Column + i, SingleNumber - 10 + 'A');
}
}
}
/**
* @brief OLED display number (binary, positive)
* @param Line starting row position, range: 1~4
* @param Column starting column position, range: 1~16
* @param Number number to display, range: 0~1111 1111 1111 1111
* @param Length length of number to display, range: 1~16
* @retval none
*/
void OLED_ShowBinNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length)
{
uint8_t i;
for (i = 0; i < Length; i++)
{
OLED_ShowChar(Line, Column + i, Number / OLED_Pow(2, Length - i - 1) % 2 + '0');
}
}
/**
* @brief OLED initialization
* @param none
* @retval none
*/
void OLED_Init(void)
{
uint32_t i, j;
for (i = 0; i < 1000; i++) //Power-on delay
{
for (j = 0; j < 1000; j++);
}
OLED_I2C_Init(); //Port initialization
OLED_WriteCommand(0xAE); //Display off
OLED_WriteCommand(0xD5); //Set display clock divide ratio/oscillator frequency
OLED_WriteCommand(0x80);
OLED_WriteCommand(0xA8); //Set multiplex ratio
OLED_WriteCommand(0x3F);
OLED_WriteCommand(0xD3); //Set display offset
OLED_WriteCommand(0x00);
OLED_WriteCommand(0x40); //Set display start line
OLED_WriteCommand(0xA1); //Set segment re-map, 0xA1 normal 0xA0 reversed
OLED_WriteCommand(0xC8); //Set COM output scan direction, 0xC8 normal 0xC0 reversed
OLED_WriteCommand(0xDA); //Set COM pins hardware configuration
OLED_WriteCommand(0x12);
OLED_WriteCommand(0x81); //Set contrast control
OLED_WriteCommand(0xCF);
OLED_WriteCommand(0xD9); //Set pre-charge period
OLED_WriteCommand(0xF1);
OLED_WriteCommand(0xDB); //Set VCOMH deselect level
OLED_WriteCommand(0x30);
OLED_WriteCommand(0xA4); //Set entire display on/off
OLED_WriteCommand(0xA6); //Set normal/inverse display
OLED_WriteCommand(0x8D); //Set charge pump
OLED_WriteCommand(0x14);
OLED_WriteCommand(0xAF); //Display on
OLED_Clear(); //OLED clear screen
}
Recommended Reading
- High Cost-Performance and Cheap VPS/Cloud Server Recommendations: https://blog.vpszj.cn/archives/41.html
- Build an Intranet Penetration Server with NPS, includes Web Panel: https://blog.vpszj.cn/archives/748.html
- Linux Website Building Tutorial, Site Setup Guide: https://blog.vpszj.cn/archives/1094.html
- Minecraft Server Setup Tutorial: https://blog.vpszj.cn/archives/tag/minecraft
- Ultrasonic Distance Measurement with STM32 and HC-SR04 Module: https://blog.zeruns.com/archives/680.html
- ESP8266 Development Environment Setup and Project Demo: https://blog.zeruns.com/archives/526.html








