STM32 считывает данные датчика температуры и влажности AHT10

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

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

STM32 использует аппаратный I2C для чтения данных с датчика температуры и влажности SHTC3: https://blog.zeruns.com/archives/692.html

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

Результат реализации


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

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

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

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

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

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

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

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

  2. Всего две линии: двунаправленная линия данных SDA и линия синхронизации SCL. SDA передаёт данные, SCL синхронизирует обмен.

  3. Шина подтянута к питанию через резисторы. Когда устройство свободно, оно переходит в высокоимпедансное состояние, и резисторы подтягивают шину к логической «1».

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

Подробнее о работе I2C на STM32: https://url.zeruns.com/JC0Ah

Здесь подробности не приводятся.

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

Описание

AHT10 — китайский датчик температуры и влажности, недорогой, точный и компактный.

Включает новый специализированный ASIC, улучшенный MEMS-конденсаторный элемент для измерения влажности и стандартный температурный датчик на кристалле. Показатели надёжности превосходят предшественников; датчик устойчиво работает в суровых условиях.

Скачать даташит AHT10: https://url.zeruns.com/EDEwF



Краткие характеристики:

  • Диапазон температур: –40 °C … +85 °C
  • Погрешность температуры: ±0,3 °C
  • Диапазон влажности: 0 % … 100 %
  • Погрешность влажности: ±2 %
  • Питающее напряжение: 1,8 В … 3,6 В
  • Интерфейс: I2C
  • Частота тактирования: 100 кГц и 400 кГц

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

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

Для записи команды/данных после старт-бита отправляется 0x70 (0111 0000).
Для чтения данных — 0x71 (0111 0001).
При использовании аппаратного I2C STM32 достаточно указать 0x70; младший бит обрабатывается библиотекой.

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


Из даташита: один цикл измерения состоит из трёх шагов:

  1. Отправка команды измерения
  2. Ожидание завершения измерения
  3. Чтение результата

Алгоритм:

  1. Отправка команды измерения: старт → адрес записи 0x70 → команда 0xAC → параметры 0x33, 0x00.
  2. Ожидание: ≥75 мс (указано в даташите).
  3. Чтение: отправить адрес чтения 0x71, принять 6 байт. Первый байт — статус. Проверить бит калибровки (бит 3), при необходимости отправить команду инициализации; проверить бит готовности (бит 7): 0 — измерение завершено.
  4. Преобразовать полученные данные.

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

По даташиту AHT10:

Пример:
Принятое значение влажности 0x0C6501 = 812 289 десятичное.
Влажность = 812 289 × 100 / 1 048 576 = 77,46 %

Значение температуры 0x056A00 = 354 816 десятичное.
Температура = 354 816 × 200 / 1 048 576 – 50 = 17,67 °C

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

Минимальная плата STM32: https://s.click.taobao.com/bqMwZRu
Модуль AHT10: https://s.click.taobao.com/gIF09Ru
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, AHT10.c и OLED.c; остальные файлы — в архиве по ссылке.

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

SCL и SDA модулей AHT10 и OLED подключены к PB6 и PB7 соответственно.

Использование VSCode вместо Keil для разработки под STM32 и 51: 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();	// инициализация сторожевого таймера
	OLED_Init();			// инициализация OLED
	AHT10_Init();			// инициализация 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); // счётчик для контроля работы
		if(a==999999999)a=0;

		float Temp,Hum;			 // переменные для хранения данных
		
	/*
		https://blog.zeruns.com
	*/

		if(ReadAHT10(&Hum,&Temp)) // чтение температуры и влажности
		{
			if(Temp>=0)
			{
				char String[10];
				sprintf(String, "+%.2fC", Temp); // форматирование
				OLED_ShowString(1, 3, String);	 // вывод температуры

				sprintf(String, " %.2f%%", Hum);
				OLED_ShowString(2, 3, String);	 // вывод влажности
			}else
			{
				char String[10];
				sprintf(String, "-%.2fC", Temp);
				OLED_ShowString(1, 3, String);	 // вывод температуры
				
				sprintf(String, " %.2f%%", Hum);
				OLED_ShowString(2, 3, String);	 // вывод влажности
			}
		}
		else
		{
			err_count++;
			OLED_ShowNum(4,1, err_count, 5); // счётчик ошибок
		}

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

		IWDG_FeedDog(); // сброс сторожевого таймера
	}
}
```**AHT10.c**

```c
#include "stm32f10x.h"
#include "Delay.h"
#include "OLED.h"

/*Адрес AHT10*/
#define AHT10_ADDRESS 0x38<<1 //Адрес ведомого 7-битный, последний бит — направление передачи, поэтому сдвигаем влево на 1

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

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


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

/*Отправка сигнала STOP*/
void AHT10_I2C_STOP(){
    I2C_GenerateSTOP(I2Cx, ENABLE);//Отправка сигнала STOP
}

/**
  * @brief  Отправка 3 байт данных
  * @param  cmd байт команды
  * @param  DATA0 параметр 0
  * @param  DATA1 параметр 1
  * @retval нет
  */
void AHT10_WriteByte(uint8_t cmd, uint8_t DATA0, uint8_t DATA1)
{
	AHT10_I2C_START();  //Отправка сигнала START
	
	I2C_Send7bitAddress(I2Cx, AHT10_ADDRESS, I2C_Direction_Transmitter);    //Отправка адреса записи устройства
	while(I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)==ERROR);  //Проверка события EV6

    I2C_SendData(I2Cx, cmd);//Отправка команды
	while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED));//Проверка события EV8

    I2C_SendData(I2Cx, DATA0);//Отправка старших 8 бит параметра команды
	while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED));//Проверка события EV8
   
	I2C_SendData(I2Cx, DATA1);//Отправка младших 8 бит параметра команды
	while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED));//Проверка события EV8
	
	I2C_GenerateSTOP(I2Cx, ENABLE);//Отправка сигнала STOP	
}


/**
  * @brief  Отправка команды для чтения статуса AHT10
  * @retval байт статуса
  */
/*uint8_t AHT10_ReadStatus(void){
    AHT10_I2C_START();//Отправка сигнала START  
	I2C_Send7bitAddress(I2Cx,AHT10_ADDRESS,I2C_Direction_Receiver);//Отправка адреса чтения устройства
	while( I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED )==ERROR);//Проверка события EV6
    while (!I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_BYTE_RECEIVED));//Проверка события EV7
	I2C_AcknowledgeConfig(I2Cx, DISABLE); //Отключение подтверждения
	uint8_t status = I2C_ReceiveData(I2Cx);//Чтение данных и возврат
	AHT10_I2C_STOP();   //Отправка сигнала STOP
	I2C_AcknowledgeConfig(I2Cx,ENABLE);//Повторное включение подтверждения
	return status;
}*/

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

/*Программный сброс AHT10*/
void AHT10_SoftReset(void)                    
{
    AHT10_I2C_START();  //Отправка сигнала START
	I2C_Send7bitAddress(I2Cx, AHT10_ADDRESS, I2C_Direction_Transmitter);    //Отправка адреса записи устройства
	while(I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)==ERROR);  //Проверка события EV6
    I2C_SendData(I2Cx, 0xBA);//Отправка команды программного сброса
	while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED));//Проверка события EV8
    I2C_GenerateSTOP(I2Cx, ENABLE);//Отправка сигнала STOP
	Delay_ms(20);
}


/*Инициализация выводов*/
void AHT10_Init(void)
{
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,ENABLE);	//Включение тактирования I2C1
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);//Включение тактирования GPIOB
 
	/*Аппаратный I2C1 STM32F103: 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 = 0x88;	//Собственный адрес хоста, не используется — можно указать любой
	I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;	//Разрешение подтверждения
	I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;//Длина адреса 7 бит
	I2C_InitStructure.I2C_ClockSpeed = 400000;	//Скорость I2C, 400 кГц, уточните по даташиту используемого микроконтроллера.
	I2C_Init(I2Cx, &I2C_InitStructure);         //Инициализация I2C

	I2C_Cmd(I2Cx, ENABLE);  //Включение I2C
	Delay_ms(20);//Задержка после включения питания
	AHT10_WriteByte(0XE1,0X08,0x00);//Отправка команды инициализации
	Delay_ms(20);

}

/**
  * @brief  Чтение данных AHT10
  * @param  *Hum влажность
  * @param  *Temp температура
  * @retval 1 — успешно; 0 — ошибка
  */
uint8_t ReadAHT10(float *Hum,float *Temp)
{
    uint8_t Data[5];//Массив для хранения прочитанных данных

	AHT10_WriteByte(0XAC,0X33,0x00);//Отправка команды запуска измерения

	Delay_ms(70);	//Задержка 70 мс для завершения измерения

    AHT10_I2C_START();//Отправка сигнала START  
	I2C_Send7bitAddress(I2Cx,AHT10_ADDRESS,I2C_Direction_Receiver);//Отправка адреса чтения устройства
	while( I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED )==ERROR);//Проверка события EV6
	
	uint8_t i;
	for(i=0;i<6;i++)//Цикл чтения 6 байт
	{
		if (i == 5)	//Отключение подтверждения перед последним байтом
		{
			I2C_AcknowledgeConfig(I2Cx, DISABLE); //Отключение подтверждения
		}
		Data[i] = AHT10_ReadData();	//Чтение данных
		if (i == 5)
			I2C_GenerateSTOP(I2Cx, ENABLE); //Отправка сигнала STOP
	}
	I2C_AcknowledgeConfig(I2Cx,ENABLE);//Повторное включение подтверждения

	if( (Data[0]&0x08) == 0 )//0x08(00001000) Проверка бита калибровки (3-й бит)
	{
		AHT10_WriteByte(0XE1,0X08,0x00);	//Отправка команды инициализации
		Delay_ms(20);
		return 0;
	}
	else if( (Data[0]&0x80) == 0 )//0x80(10000000) Проверка бита занятости (7-й бит)
	{
		
		uint32_t SRH = (Data[1]<<12) | (Data[2]<<4) | (Data[3]>>4);	//Обработка данных влажности
		uint32_t ST = ((Data[3]&0x0f)<<16) | (Data[4]<<8) | Data[5];//Обработка данных температуры

		*Hum = (SRH * 100.0) / 1024.0 / 1024;	   //Пересчёт влажности по формуле из даташита
		*Temp = (ST * 200.0) / 1024.0 / 1024 - 50; //Пересчёт температуры по формуле из даташита

		return 1;
	}

	I2C_GenerateSTOP(I2Cx, ENABLE);//Отправка сигнала STOP
	return 0;	

}

/*
https://blog.zeruns.com
*/
```**OLED.c**

```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
 
	/*Аппаратный I2C STM32F103: 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 = 0x88;	//Собственный I2C-адрес хоста, не используется, можно указать любой
	I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;	//Разрешить подтверждение
	I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;//Длина адреса 7 бит
	I2C_InitStructure.I2C_ClockSpeed = 400000;	//Скорость передачи I2C, 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);
}
 
/**
  * @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  Показать число (десятичное, положительное)
  * @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  Показать число (десятичное, со знаком)
  * @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  Показать число (шестнадцатеричное, положительное)
  * @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  Показать число (двоичное, положительное)
  * @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
}

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

1 лайк