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
-
Поддержка множества устройств на одной шине. «Шина» означает общие сигнальные линии. На одной шине I2C можно подключить несколько устройств, включая несколько хостов и ведомых.
-
Для работы I2C достаточно двух линий: двунаправленная линия данных SDA (Serial Data Line) и линия синхронизации SCL (Serial Clock Line). Линия данных передаёт информацию, линия синхронизации обеспечивает синхронизацию приёма и передачи.
-
Шина подтянута к питанию через резисторы-подтяжки. Когда устройство 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).
Цикл измерения состоит из четырёх шагов:
- Отправка команды пробуждения.
- Отправка команды измерения.
- Считывание результата.
- Отправка команды сна.
Команды пробуждения и сна приведены в datasheet.
Кратко:
- Разбудить SHTC3: отправить 0xE0, затем старший байт команды пробуждения 0x35, младший 0x17.
- Подождать завершения пробуждения (макс. 240 мкс).
- Отправить команду измерения: 0xE0 + старший и младший байты нужной команды.
- Принять 6 байт: отправить 0xE1 и считать данные. Если команда «сначала температура», то байты 1-2 — температура, 3 — CRC температуры, 4-5 — влажность, 6 — CRC влажности. Для команды «сначала влажность» порядок противоположный.
- Усыпить датчик: отправить 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
Рекомендуем к прочтению
- Рекомендации по VPS/облачным серверам с высоким соотношением цены и качества: https://blog.vpszj.cn/archives/41.html
- Использование NPS для создания сервера проксирования в локальную сеть с веб-панелью: https://blog.vpszj.cn/archives/748.html
- Руководство по созданию сайта на Linux: https://blog.vpszj.cn/archives/1094.html
- Руководство по созданию сервера Minecraft: https://blog.vpszj.cn/archives/tag/minecraft
- Реализация ультразвукового измерения расстояния на базе STM32 и модуля HC-SR04: https://blog.zeruns.com/archives/680.html
- Настройка среды разработки ESP8266 и демонстрация проекта: https://blog.zeruns.com/archives/526.html






