STM32 использует аппаратный I2C для считывания датчика температуры и влажности SHTC3

STM32 использует аппаратный I2C для считывания данных с датчика температуры и влажности SHTC3 и отображает их на OLED-дисплее 0,96 дюйма.

Я использую STM32F103C8T6, программа написана на стандартной библиотеке ST.

Группа технического обмена по электронике/микроконтроллерам: 2169025065

Результат выполнения


Краткое описание протокола I2C

Протокол связи I2C (Inter-Integrated Circuit) был разработан компанией Philips. Благодаря малому количеству выводов, простоте аппаратной реализации, высокой расширяемости и отсутствию необходимости во внешних приёмопередатчиках (таких как преобразователи уровня), как в протоколах USART, CAN и др., он широко используется для связи между несколькими интегральными схемами (IC) внутри системы.

I2C имеет только одну линию данных SDA (Serial Data Line), последовательную шину данных, передающую данные по одному биту, и относится к последовательной полудуплексной связи.

Полудуплексная связь: возможность двустороннего обмена, но не одновременно — передача осуществляется поочерёдно. Можно считать переключаемой однонаправленной связью: в один момент времени данные передаются только в одном направлении, достаточно одной линии данных.

Протокол I2C условно делится на физический и канальный уровни. Физический уровень определяет механические и электрические характеристики системы (аппаратная часть), обеспечивая передачу сырых данных по физической среде. Канальный уровень задаёт логику обмена, унифицирует правила упаковки и распаковки данных между передатчиком и приёмником (программный уровень).

Физический уровень I2C

Типичное подключение устройств I2C

  1. Поддержка множества устройств на одной шине. «Шина» означает общие сигнальные линии. На одной шине I2C можно подключить несколько устройств, включая несколько хостов и ведомых.

  2. Для работы I2C достаточно двух линий: двунаправленная линия данных SDA (Serial Data Line) и линия синхронизации SCL (Serial Clock Line). Линия данных передаёт информацию, линия синхронизации обеспечивает синхронизацию приёма и передачи.

  3. Шина подтянута к питанию через резисторы-подтяжки. Когда устройство I2C находится в простое, оно переходит в высокоимпеданное состояние, и, пока все устройства в простое, резисторы-подтяжки удерживают шину в высоком уровне.

Для работы I2C выводы GPIO микроконтроллера должны быть настроены на открытый коллекторный вывод, иначе возможно короткое замыкание.

Больше информации о I2C на STM35 см. в статье: https://url.zeruns.com/JC0Ah

Здесь подробно не останавливаюсь.

Датчик температуры и влажности SHTC3

Ссылка на datasheet SHTC3: https://url.zeruns.com/WpLDy

Из datasheet следует, что SHTC3 измеряет температуру и влажность:
Диапазон температур: –40 °C…+125 °C
Диапазон влажности: 0 %…100 %
Рабочее напряжение: 1,6 В…3,6 В
Интерфейс: I²C
Частота тактирования: три режима — 0–100 кГц, 0–400 кГц, 0–1000 кГц

Ключевая информация:

Адрес устройства и команды чтения/записи

При передаче адрес объединяется с битом направления в один байт: старшие 7 бит — адрес SHTC3, младший бит — направление (0 — запись, 1 — чтение).

Для записи команды/данных: после старт-бита отправляется «1110 0000» (0xE0).
Для чтения данных: после старт-бита отправляется «1110 0001» (0xE1).
При использовании аппаратного I2C STM32 достаточно указать 0xE0 — младший бит обработает библиотека.

Считывание данных температуры и влажности

Разные команды отличаются порядком выдачи данных и наличием Clock Stretching (Enable/Disable).

Clock Stretching — растяжение тактового сигнала. Если использовать команду с Clock Stretching Enable, то после команды измерения SHTC3 удерживает линию SCL в низком уровне до завершения измерения, запрещая мастеру передавать новые команды. После завершения измерения линия SCL освобождается.

Если использовать Clock Stretching Disable, линия SCL не удерживается. Мастер может определить готовность данных по наличию/отсутствию подтверждения (ACK).

Цикл измерения состоит из четырёх шагов:

  1. Отправка команды пробуждения.
  2. Отправка команды измерения.
  3. Считывание результата.
  4. Отправка команды сна.

Команды пробуждения и сна приведены в datasheet.

Кратко:

  1. Разбудить SHTC3: отправить 0xE0, затем старший байт команды пробуждения 0x35, младший 0x17.
  2. Подождать завершения пробуждения (макс. 240 мкс).
  3. Отправить команду измерения: 0xE0 + старший и младший байты нужной команды.
  4. Принять 6 байт: отправить 0xE1 и считать данные. Если команда «сначала температура», то байты 1-2 — температура, 3 — CRC температуры, 4-5 — влажность, 6 — CRC влажности. Для команды «сначала влажность» порядок противоположный.
  5. Усыпить датчик: отправить 0xE0 и команду сна.

Расчёт значений

Согласно datasheet:

Пример: принятое значение влажности 0x6501 = 25810.
Влажность = 100 × 25810 / 65536 = 39,45 %
Значение температуры 0x6600 = 26112.
Температура = –45 + 175 × 26112 / 65536 = 24,72 °C

Необходимые компоненты

Минимальная плата STM32: https://s.click.taobao.com/bqMwZRu
Модуль SHTC3: https://s.click.taobao.com/WxACJRu
OLED-модуль: https://s.click.taobao.com/aNlvZRu
Провода «папа-папа»: https://s.click.taobao.com/xAkAJRu
Макетная плата: https://s.click.taobao.com/ShJAJRu
ST-LINK V2: https://s.click.taobao.com/C8ftZRu

Программа

Ниже приведены только main.c, shtc3.c и oled.c. Остальные файлы скачивайте по ссылке ниже.

Полный проект: https://url.zeruns.com/EXCvo

SCL модулей SHTC3 и OLED подключён к PB6, SDA — к PB7.

Разработка STM32 и 51-микроконтроллеров в VSCode вместо Keil: https://blog.zeruns.com/archives/690.html

main.c

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "IWDG.h"
#include "SHTC3.h"

uint16_t numlen(uint16_t num);

int main(void)
{
	IWDG_Configuration();	//初始化看门狗
	OLED_Init();			//初始化OLED屏
	SHTC3_I2C_Init();		//初始化SHTC3
	
	OLED_ShowString(1, 1, "T:");
	OLED_ShowString(2, 1, "H:");
	OLED_ShowString(4, 1, "err_count:");

	uint32_t a=0;
	uint16_t err_count=0;
	
	while (1)
	{
		a++;
		OLED_ShowNum(3, 1, a, 9);
		if(a==999999999)a=0;

		float Temp,Hum;			//声明变量存放温湿度数据

		if(ReadSHTC3(&Hum,&Temp))	//读取温湿度数据
		{
			if(Temp>=0)
			{
				char String[10];
				sprintf(String, "%.2fC", Temp);	//格式化字符串输出到字符串变量
				OLED_ShowString(1, 3, String);	//显示温度
				/*
				OLED_ShowNum(1,3, (uint8_t)Temp, numlen((uint8_t)Temp));//显示温度整数部分
				OLED_ShowChar(1, 3+numlen((uint8_t)Temp), '.');			//显示小数点
				OLED_ShowNum(1,3+numlen((uint8_t)Temp)+1, (uint8_t)(Temp*100)%100, 2);	//显示温度小数部分
				OLED_ShowChar(1, 3+numlen((uint8_t)Temp)+1+2, 'C');		//显示符号
				*/
```sprintf(String, \"%.2f%%\", Hum);	//форматирование строки и вывод в строковую переменную
				OLED_ShowString(2, 3, String);	//отображение влажности
				/*
				OLED_ShowNum(2,3, (uint8_t)Hum, numlen((uint8_t)Hum));	//отображение целой части влажности
				OLED_ShowChar(2, 3+numlen((uint8_t)Hum), '.');			//отображение десятичной точки
				OLED_ShowNum(2,3+numlen((uint8_t)Hum)+1, (uint8_t)(Hum*100)%100, 2);	//отображение дробной части влажности
				OLED_ShowChar(2, 3+numlen((uint8_t)Hum)+1+2, '%');			//отображение символа
				*/
			}else
			{
				char String[10];
				sprintf(String, \"-%.2fC\", Temp);//форматирование строки и вывод в строковую переменную
				OLED_ShowString(1, 3, String);	//отображение температуры
				/*
				OLED_ShowChar(1, 3, '-');			//отображение минуса
				OLED_ShowNum(1,3+1, (uint8_t)Temp, numlen((uint8_t)Temp));	//отображение целой части температуры
				OLED_ShowChar(1, 3+1+numlen((uint8_t)Temp), '.');			//отображение десятичной точки
				OLED_ShowNum(1,3+1+numlen((uint8_t)Temp)+1, (uint8_t)(Temp*100)%100, 2);	//отображение дробной части температуры
				OLED_ShowChar(1, 3+1+numlen((uint8_t)Temp)+1+2, 'C');		//отображение символа
				*/

				sprintf(String, \"%.2f%%\", Hum);	//форматирование строки и вывод в строковую переменную
				OLED_ShowString(2, 3, String);	//отображение влажности
				/*
				OLED_ShowNum(2,3, (uint8_t)Hum, numlen((uint8_t)Hum));	//отображение целой части влажности
				OLED_ShowChar(2, 3+numlen((uint8_t)Hum), '.');			//отображение десятичной точки
				OLED_ShowNum(2,3+numlen((uint8_t)Hum)+1, (uint8_t)(Hum*100)%100, 2);	//отображение дробной части влажности
				OLED_ShowChar(2, 3+numlen((uint8_t)Hum)+1+2, '%');			//отображение символа
				*/
			}
		}
		else
		{
			err_count++;
			OLED_ShowNum(4,11, err_count, numlen(err_count));	//отображение счётчика ошибок
		}
	/*
	https://blog.zeruns.com
	*/

		Delay_ms(100);	//задержка 100 мс

		IWDG_FeedDog();	//подача сигнала сторожевого таймера (при отсутствии сигнала более 1 с — автоматический сброс)
	}
}

/**
  * @brief  вычисление длины целого числа
  * @param  num целое число, длину которого нужно вычислить
  * @retval длина
  */
uint16_t numlen(uint16_t num)
{
    uint16_t len = 0;        // начальная длина 0
    for(; num > 0; ++len)    // пока num больше 0, увеличиваем длину
        num /= 10;	         // делим на 10, пока num не станет меньше 1
    return len;              // возвращаем длину
}

SHTC3.c

#include "stm32f10x.h"
#include "Delay.h"

/*адрес SHTC3*/
#define SHTC3_ADDRESS 0xE0

/*выбор используемого I2C*/
#define I2Cx I2C1

/*
https://blog.zeruns.com
*/

/**
  * @brief  CRC-проверка, полином: x^8+x^5+x^4+1, т.е. 0x31
  * @param  DAT данные для проверки
  * @retval контрольная сумма
  */
uint8_t SHTC3_CRC_CAL(uint16_t DAT)
{
	uint8_t i,t,temp;
	uint8_t CRC_BYTE;

	CRC_BYTE = 0xFF;
	temp = (DAT>>8) & 0xFF;

	for(t = 0; t < 2; t++)
	{
		CRC_BYTE ^= temp;
		for(i = 0;i < 8;i ++)
		{
			if(CRC_BYTE & 0x80)
			{
				CRC_BYTE <<= 1;
				CRC_BYTE ^= 0x31;
			}
			else
			{
				CRC_BYTE <<= 1;
			}
		}

		if(t == 0)
		{
			temp = DAT & 0xFF;
		}
	}

	return CRC_BYTE;
}

/*отправка стартового сигнала*/
void SHTC3_I2C_START(){
    while( I2C_GetFlagStatus(I2Cx, I2C_FLAG_BUSY));//ожидание освобождения шины
	I2C_GenerateSTART(I2Cx, ENABLE);//отправка стартового сигнала
	while( I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_MODE_SELECT)==ERROR);//проверка события EV5
}

/*отправка стоп-сигнала*/
void SHTC3_I2C_STOP(){
    I2C_GenerateSTOP(I2Cx, ENABLE);//отправка стоп-сигнала
}

/**
  * @brief  отправка двух байт данных
  * @param  MSB старший байт
  * @param  LSB младший байт
  * @retval нет
  */
void SHTC3_WriteByte(uint8_t MSB,uint8_t LSB)
{
	SHTC3_I2C_START();  //отправка стартового сигнала
	
	I2C_Send7bitAddress(I2Cx, SHTC3_ADDRESS, I2C_Direction_Transmitter);    //отправка адреса записи устройства
	while(I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)==ERROR);  //проверка события EV6

    I2C_SendData(I2Cx, MSB);//отправка старшего байта
	while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED));//проверка события EV8
   
	I2C_SendData(I2Cx, LSB);//отправка младшего байта
	while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED));//проверка события EV8
	
	I2C_GenerateSTOP(I2Cx, ENABLE);//отправка стоп-сигнала
	
}

/**
  * @brief  чтение данных
  * @retval прочитанный байт
  */
uint8_t SHTC3_ReadData()
{
    while (!I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_BYTE_RECEIVED));//проверка события EV7
	return I2C_ReceiveData(I2Cx);//чтение и возврат данных
}

/*программный сброс SHTC3*/
void SHTC3_SoftReset(void)                    
{
    SHTC3_WriteByte(0x80,0x5D);    //сброс SHTC3
}

/*инициализация выводов*/
void SHTC3_I2C_Init(void)
{
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,ENABLE);	//включение тактирования I2C1
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);//включение тактирования GPIOB
 
	/*STM32F103 аппаратный I2C1: PB6 -- SCL; PB7 -- SDA */
	GPIO_InitTypeDef  GPIO_InitStructure;               //структура для конфигурации 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;		//открытый коллектор, требует подтягивающих резисторов
	GPIO_Init(GPIOB, &GPIO_InitStructure);              //инициализация GPIO
	
	I2C_DeInit(I2Cx);	//сброс регистров I2C
	I2C_InitTypeDef  I2C_InitStructure;                 //структура для конфигурации I2C
	I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;			//режим работы
	I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;	//скважность, Tlow/Thigh = 2
	I2C_InitStructure.I2C_OwnAddress1 = 0x30;	//собственный адрес, не используется
	I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;	//разрешение подтверждения
	I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;//7-битный адрес
	I2C_InitStructure.I2C_ClockSpeed = 400000;	//скорость 400 кГц, смотреть в даташите
	I2C_Init(I2Cx, &I2C_InitStructure);         //инициализация I2C

	I2C_Cmd(I2Cx, ENABLE);  //включение I2C

	SHTC3_WriteByte(0X35,0X17);//пробуждение SHTC3
	
	Delay_us(200);
}

/**
  * @brief  чтение данных SHTC3
  * @param  *Hum влажность
  * @param  *Temp температура
  * @retval 1 — успешно; 0 — ошибка
  */
uint8_t ReadSHTC3(float *Hum,float *Temp)
{
    uint16_t HumData,TempData,HumCRC,TempCRC;//переменные для данных
    
    /*SHTC3_WriteByte(0X35,0X17);//пробуждение SHTC3
	
	Delay_us(300);*/
    
    SHTC3_WriteByte(0X5C,0X24);//команда: сначала влажность, clock stretching
	 
    SHTC3_I2C_START();//старт
    
	I2C_Send7bitAddress(I2Cx,SHTC3_ADDRESS,I2C_Direction_Receiver);//адрес чтения
	
	while( I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED )==ERROR);//EV6
	
    HumData = SHTC3_ReadData(); //старший байт влажности
    HumData=HumData<<8;         
	HumData |= SHTC3_ReadData();//младший байт влажности
    HumCRC = SHTC3_ReadData();  //CRC влажности

    TempData = SHTC3_ReadData();//старший байт температуры
    TempData=TempData<<8;       
	TempData |= SHTC3_ReadData();//младший байт температуры
    TempCRC = SHTC3_ReadData(); //CRC температуры

    SHTC3_I2C_STOP();   //стоп
		
	//SHTC3_WriteByte(0XB0,0X98);//команда сна

    if( SHTC3_CRC_CAL(HumData)==HumCRC && SHTC3_CRC_CAL(TempData)==TempCRC ){   //проверка CRC
       *Hum = (float)HumData*100/65536;        //перевод влажности
       *Temp = (float)TempData*175/65536-45;   //перевод температуры
       return 1;
    }
    else{
        return 0;
    }
}

OLED.c

#include "stm32f10x.h"
#include "OLED_Font.h"

/*адрес OLED*/
#define OLED_ADDRESS 0x78

/*выбор I2C*/
#define I2Cx I2C1

/*
https://blog.zeruns.com
*/

/*инициализация выводов*/
void OLED_I2C_Init(void)
{
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,ENABLE);	//включение тактирования I2C1
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);//включение тактирования GPIOB
 
	/*STM32F103 аппаратный 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;		//открытый коллектор, требует подтягивающих резисторов
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	
	I2C_DeInit(I2Cx);	//сброс регистров I2C
	I2C_InitTypeDef  I2C_InitStructure;
	I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;			//режим
	I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;	//скважность Tlow/Thigh = 2
	I2C_InitStructure.I2C_OwnAddress1 = 0x30;	//собственный адрес, не используется
	I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;	//подтверждение
	I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;//7-битный адрес
	I2C_InitStructure.I2C_ClockSpeed = 400000;	//400 кГц
	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));
	
	//старт
	I2C_GenerateSTART(I2Cx, ENABLE);
	//EV5
	while( I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_MODE_SELECT)==ERROR);
	//адрес записи
	I2C_Send7bitAddress(I2Cx, OLED_ADDRESS, I2C_Direction_Transmitter);
	//EV6
	while( I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)==ERROR);
	
	//внутренний адрес устройства
	I2C_SendData(I2Cx, addr);
	//EV8_2
	while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
	
	I2C_SendData(I2Cx, data);//данные
	while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED));

	//стоп
	I2C_GenerateSTOP(I2Cx, ENABLE);
}
 
/**
  * @brief  запись команды OLED
  * @param  Command команда
  * @retval нет
  */
void OLED_WriteCommand(unsigned char Command)//запись команды
{
	I2C_WriteByte(0x00, Command);
}
 
``````c
/**
  * @brief  OLED запись данных
  * @param  Data данные для записи
  * @retval нет
*/
void OLED_WriteData(unsigned char Data)//запись данных
{
	I2C_WriteByte(0x40, Data);
}

/**
  * @brief  OLED установка позиции курсора
  * @param  Y координата сверху вниз от левого верхнего угла, диапазон: 0~7
  * @param  X координата слева направо от левого верхнего угла, диапазон: 0~127
  * @retval нет
  */
void OLED_SetCursor(uint8_t Y, uint8_t X)
{
	OLED_WriteCommand(0xB0 | Y);					//установка позиции Y
	OLED_WriteCommand(0x10 | ((X & 0xF0) >> 4));	//установка младших 4 бит X
	OLED_WriteCommand(0x00 | (X & 0x0F));			//установка старших 4 бит X
}

/**
  * @brief  OLED очистка экрана
  * @param  нет
  * @retval нет
  */
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 частичная очистка экрана
  * @param  Line позиция строки, диапазон: 1~4
  * @param  start начальная позиция столбца, диапазон: 1~16
  * @param  end конечная позиция столбца, диапазон: 1~16
  * @retval нет
  */
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);		//установка курсора в верхней половине
		for (i = 0; i < 8; i++)
		{
			OLED_WriteData(0x00);			//отображение верхней половины
		}
		OLED_SetCursor((Line - 1) * 2 + 1, (Column - 1) * 8);	//установка курсора в нижней половине
		for (i = 0; i < 8; i++)
		{
			OLED_WriteData(0x00);		//отображение нижней половины
		}
	}
}

/**
  * @brief  OLED отображение одного символа
  * @param  Line позиция строки, диапазон: 1~4
  * @param  Column позиция столбца, диапазон: 1~16
  * @param  Char символ для отображения, диапазон: видимые ASCII символы
  * @retval нет
  */
void OLED_ShowChar(uint8_t Line, uint8_t Column, char Char)
{      	
	uint8_t i;
	OLED_SetCursor((Line - 1) * 2, (Column - 1) * 8);		//установка курсора в верхней половине
	for (i = 0; i < 8; i++)
	{
		OLED_WriteData(OLED_F8x16[Char - ' '][i]);			//отображение верхней половины
	}
	OLED_SetCursor((Line - 1) * 2 + 1, (Column - 1) * 8);	//установка курсора в нижней половине
	for (i = 0; i < 8; i++)
	{
		OLED_WriteData(OLED_F8x16[Char - ' '][i + 8]);		//отображение нижней половины
	}
}

/**
  * @brief  OLED отображение строки
  * @param  Line начальная позиция строки, диапазон: 1~4
  * @param  Column начальная позиция столбца, диапазон: 1~16
  * @param  String строка для отображения, диапазон: видимые ASCII символы
  * @retval нет
  */
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 функция возведения в степень
  * @retval возвращает X в степени Y
  */
uint32_t OLED_Pow(uint32_t X, uint32_t Y)
{
	uint32_t Result = 1;
	while (Y--)
	{
		Result *= X;
	}
	return Result;
}

/**
  * @brief  OLED отображение числа (десятичное, положительное)
  * @param  Line начальная позиция строки, диапазон: 1~4
  * @param  Column начальная позиция столбца, диапазон: 1~16
  * @param  Number число для отображения, диапазон: 0~4294967295
  * @param  Length длина числа для отображения, диапазон: 1~10
  * @retval нет
  */
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 отображение числа (десятичное, со знаком)
  * @param  Line начальная позиция строки, диапазон: 1~4
  * @param  Column начальная позиция столбца, диапазон: 1~16
  * @param  Number число для отображения, диапазон: -2147483648~2147483647
  * @param  Length длина числа для отображения, диапазон: 1~10
  * @retval нет
  */
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 отображение числа (шестнадцатеричное, положительное)
  * @param  Line начальная позиция строки, диапазон: 1~4
  * @param  Column начальная позиция столбца, диапазон: 1~16
  * @param  Number число для отображения, диапазон: 0~0xFFFFFFFF
  * @param  Length длина числа для отображения, диапазон: 1~8
  * @retval нет
  */
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 отображение числа (двоичное, положительное)
  * @param  Line начальная позиция строки, диапазон: 1~4
  * @param  Column начальная позиция столбца, диапазон: 1~16
  * @param  Number число для отображения, диапазон: 0~1111 1111 1111 1111
  * @param  Length длина числа для отображения, диапазон: 1~16
  * @retval нет
  */
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 инициализация
  * @param  нет
  * @retval нет
  */
void OLED_Init(void)
{
	uint32_t i, j;
	
	for (i = 0; i < 1000; i++)			//задержка после включения питания
	{
		for (j = 0; j < 1000; j++);
	}
	
	OLED_I2C_Init();			//инициализация порта
	
	OLED_WriteCommand(0xAE);	//выключить дисплей
	
	OLED_WriteCommand(0xD5);	//установить делитель частоты дисплея/частоту генератора
	OLED_WriteCommand(0x80);
	
	OLED_WriteCommand(0xA8);	//установить коэффициент мультиплексирования
	OLED_WriteCommand(0x3F);
	
	OLED_WriteCommand(0xD3);	//установить смещение дисплея
	OLED_WriteCommand(0x00);
	
	OLED_WriteCommand(0x40);	//установить начальную строку дисплея
	
	OLED_WriteCommand(0xA1);	//установить направление слева направо, 0xA1 нормально 0xA0 зеркально
	
	OLED_WriteCommand(0xC8);	//установить направление сверху вниз, 0xC8 нормально 0xC0 перевернуто

	OLED_WriteCommand(0xDA);	//установить аппаратную конфигурацию выводов COM
	OLED_WriteCommand(0x12);
	
	OLED_WriteCommand(0x81);	//установить контрастность
	OLED_WriteCommand(0xCF);

	OLED_WriteCommand(0xD9);	//установить период предзарядки
	OLED_WriteCommand(0xF1);

	OLED_WriteCommand(0xDB);	//установить уровень отмены VCOMH
	OLED_WriteCommand(0x30);

	OLED_WriteCommand(0xA4);	//установить включение/выключение всего дисплея

	OLED_WriteCommand(0xA6);	//установить нормальный/инвертированный режим отображения

	OLED_WriteCommand(0x8D);	//установить насос напряжения
	OLED_WriteCommand(0x14);

	OLED_WriteCommand(0xAF);	//включить дисплей
		
	OLED_Clear();				//очистка OLED
}

Часть контента взята из следующих двух статей:

https://blog.csdn.net/mj475002864/article/details/114027993

https://blog.csdn.net/k666499436/article/details/124686559

Рекомендуем к прочтению

1 лайк