STM32 читает данные датчика температуры и влажности серии SHT3x (SHT30, SHT31, SHT35) и отображает их на OLED-экране 0,96 дюйма.
Ниже я предоставляю два набора кода: один использует стандартную библиотеку с аппаратным I2C, другой использует библиотеку HAL с программным моделированием IIC.
Используемый мною микроконтроллер — STM32F103C8T6, датчик температуры и влажности — SHT30.
STM32 программный I2C для чтения данных датчика температуры и влажности AM2320: https://blog.zeruns.com/archives/695.html
STM32 использует аппаратный I2C для чтения датчика температуры и влажности SHTC3: https://blog.zeruns.com/archives/692.html
STM32 микроконтроллер читает данные датчика температуры и влажности AHT10: https://blog.zeruns.com/archives/693.html
Группа обмена опытом в области электроники/микроконтроллеров: 2169025065
Результат реализации
Введение в протокол I2C
Протокол связи I2C (Inter-Integrated Circuit) был разработан компанией Philips. Благодаря небольшому количеству выводов, простоте аппаратной реализации и высокой расширяемости, он не требует внешних приемопередающих устройств, таких как USART, CAN и других протоколов связи (микросхемы преобразования уровня), и в настоящее время широко используется для связи между несколькими интегральными схемами (IC) в системе.
I2C имеет только одну линию данных SDA (Serial Data Line) — линию последовательной передачи данных, которая может передавать данные только по одному биту за раз, относится к последовательной связи и использует полудуплексную связь.
Полудуплексная связь: может реализовать двусторонний обмен данными, но не может осуществляться одновременно в обе стороны, должна выполняться поочередно. По сути, это можно понимать как односторонний обмен с переключаемым направлением, в каждый момент времени передача может осуществляться только в одном направлении, требуется только одна линия данных.
Для протокола связи I2C его можно разделить на физический уровень и уровень протокола. Физический уровень определяет характеристики механических и электронных функциональных частей системы связи (аппаратная часть), обеспечивая передачу исходных данных по физической среде. Уровень протокола в основном определяет логику связи, унифицирует стандарты упаковки и распаковки данных между отправителем и получателем (программный уровень).
Физический уровень I2C
Обычный способ подключения устройств связи I2C
(1) Это шина, поддерживающая несколько устройств. “Шина” означает линию сигнала, используемую несколькими устройствами. На одной шине I2C может быть подключено несколько устройств связи I2C, поддерживающих несколько главных и несколько подчиненных устройств связи.
(2) Шина I2C использует только две линии: одну двусторонную линию последовательной передачи данных SDA (Serial Data Line) и одну линию последовательного синхросигнала SCL (Serial Clock Line). Линия данных используется для представления данных, линия синхросигнала используется для синхронизации передачи и приема данных.
(3) Шина подключена к источнику питания через подтягивающие резисторы. Когда устройство I2C неактивно, оно выводит высокий импеданс, и когда все устройства неактивны и выводят высокий импеданс, подтягивающий резистор подтягивает шину к высокому уровню.
При связи I2C вывод GPIO микроконтроллера должен быть установлен в режим открытого стока, иначе может произойти короткое замыкание.
Дополнительную информацию об I2C STM32 и методы использования можно найти в этой статье: https://url.zeruns.com/JC0Ah
Я не буду подробно объяснять это здесь.
Датчик температуры и влажности SHT3x
Адрес загрузки технического описания SHT3x:
Оригинал: https://url.zeruns.com/uWNnP Код извлечения: icqu
Китайская версия (машинный перевод): https://url.zeruns.com/n9b9t Код извлечения: vedy
Просмотрев техническое описание, можно получить общую информацию о том, что SHT30 — это датчик, способный обнаруживать температуру и влажность.
Диапазон температур: -40℃~125℃
Диапазон влажности: 0%~100%
Рабочее напряжение: 2,4 В~5,5 В
Способ связи: i2c
Частота синхросигнала: 0 ~ 1000 кГц
Найдены следующие ключевые сведения:
Адрес устройства SHT3x и команды чтения/записи
Адрес SHT3x можно установить, подключив второй вывод к высокому или низкому уровню. Модуль, который я купил на Taobao, имеет вывод 2 датчика, подключенный через резистор к VCC, то есть по умолчанию 0x44.
В процессе фактического использования адрес устройства SHT3x должен быть объединен с битом направления чтения/записи данных/команды в один байт и отправлен одновременно. Младший бит байта — это бит направления чтения/записи данных/команды, старшие 7 бит — адрес устройства SHT3x.
Если вы хотите записать данные или команду в SHT3x через I2C, после сигнала начала I2C необходимо отправить “1000 1000”, то есть 0x88 в SHT3x. Помимо адресации через адрес устройства старших 7 бит “1000 100” (0x44), младший бит “0” уведомляет SHT3x о том, что далее следует операция записи данных или команды.
Если вы хотите прочитать данные из SHT3x через I2C, после сигнала начала I2C необходимо отправить “1000 1001”, то есть 0x89 в SHT3x. Помимо адресации через адрес устройства старших 7 бит “1000 100”, младший бит “1” уведомляет SHT3x о том, что далее следует операция чтения данных.
Проще говоря, 0x88 означает запись данных, 0x89 означает чтение данных. Однако при использовании аппаратного I2C STM32 нужно только ввести 0x88, младший бит будет обработан стандартной библиотекой.
Чтение данных температуры и влажности
Как видно, в зависимости от команды способ получения данных различается. Существуют режимы однократного измерения и периодического измерения, а также различие между включением и отключением растяжения синхросигнала (Clock Stretching).
Clock Stretching означает растяжение синхросигнала. Если использовать команду Clock Stretching Enable, то после отправки команды измерения SHT3x будет удерживать линию синхросигнала I2C SCL в низком состоянии во время процесса измерения температуры и влажности, тем самым запрещая главному устройству отправлять команды в SHT3x. Только когда SHT3x завершит измерение данных температуры и влажности, он освободит линию синхросигнала SCL.
Если использовать команду Clock Stretching Disable, то во время процесса измерения данных SHT3x не будет удерживать линию синхросигнала I2C SCL в низком состоянии. Однако если главное устройство отправит команду или данные во время измерения данных SHT3x, при отправке команды чтения SHT3x вернет сигнал отсутствия подтверждения. Только после завершения измерения отправка команды чтения вернет сигнал подтверждения.
Режим периодического измерения позволяет датчику автоматически измерять и сохранять данные. Можно установить измерение 0,5/1/2/4/10 раз в секунду, а затем прочитать последний результат измерения с помощью команды чтения 0xE000.
Из технического описания видно, что один цикл измерения включает 2 этапа:
- Отправка команды измерения
- Чтение данных после завершения измерения.
Вышеуказанные команды измерения и команды чтения можно найти в техническом описании.
Резюме:
- Отправка команды измерения: сначала отправить команду записи (0x88), затем отправить старший байт команды пробуждения (0x2C), затем отправить младший байт команды пробуждения (0x0D).
- Чтение данных и ожидание завершения измерения: отправить команду чтения (0x89) и ждать, пока подчиненное устройство освободит шину SCL.
- Получение данных: последовательно получить 6 байтов данных. Первые 2 байта из этих 6 байтов — это значение температуры, 3-й байт — контрольная сумма температуры. 4-5-й байты — это значение влажности, 6-й байт — контрольная сумма влажности. После получения последнего байта отправить сигнал отсутствия подтверждения.
- Обработка данных: выполнить проверку CRC и обработку данных.
Расчет данных
Из технического описания SHT3x известно:
Например: полученное значение влажности — 0x6501, преобразованное в десятичное — 25857.
Тогда: Влажность = 100 * 25857 / (65536 - 1) = 39,45 (единица: %)
Полученное значение температуры — 0x6600, преобразованное в десятичное — 26112.
Тогда: Температура = -45 + 175 * 26112 / (65536 - 1) = 24,72 (единица: ℃)
Требуемые компоненты
Минимальная система STM32: https://s.click.taobao.com/AJEGiNu
Модуль SHT3x: https://s.click.taobao.com/55xGiNu
Модуль OLED: https://s.click.taobao.com/0dlG0Ou
Провода DuPont: https://s.click.taobao.com/xAkAJRu
Макетная плата: https://s.click.taobao.com/ShJAJRu
ST-LINK V2: https://s.click.taobao.com/C8ftZRu
Программа
Здесь представлены основные коды стандартной библиотеки main.c, SHT3x.c и oled.c. Остальное, пожалуйста, загрузите из архива по ссылке ниже.
Версия стандартной библиотеки: https://url.zeruns.com/a49EX Код извлечения: 8nn5
Версия библиотеки HAL: https://url.zeruns.com/p3og4 Код извлечения: v9wc
SCL модулей SHT3x и OLED подключены к PB6, SDA подключены к 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 "SHT3x.h"
uint16_t numlen(uint16_t num);
int main(void)
{
IWDG_Configuration(); //инициализация сторожевого таймера
OLED_Init(); //инициализация OLED-экрана
SHT3x_Init(); //инициализация SHT3x
/*OLED_ShowString(1, 1, "T:");
OLED_ShowString(3, 1, "H:");*/
uint8_t Chinese[]={0,1};
OLED_ShowChinese(1,1,Chinese,2);//температура
uint8_t Chinese1[]={2,3};
OLED_ShowChinese(3,1,Chinese1,2);//влажность
uint8_t Chinese2[]={4};
OLED_ShowChinese(1,11,Chinese2,1);//℃
uint32_t a=0;
uint16_t err_count=0;
while (1)
{
a++;
OLED_ShowNum(2, 14, a, 2);
if(a==99)a=0;
float Temp,Hum; //объявление переменных для хранения данных температуры и влажности
``````c
if(ReadSHT3x(&Hum,&Temp)) //Чтение данных температуры и влажности
{
if(Temp>=0)
{
char String[10];
sprintf(String, "+%.2f", Temp);//Форматирование строки и вывод в переменную строки
OLED_ShowString(1, 5, String); //Отображение температуры
sprintf(String, " %.2f%%", Hum); //Форматирование строки и вывод в переменную строки
OLED_ShowString(3, 5, String); //Отображение влажности
}else
{
char String[10];
sprintf(String, "%.2f", Temp);//Форматирование строки и вывод в переменную строки
OLED_ShowString(1, 5, String); //Отображение температуры
sprintf(String, " %.2f%%", Hum); //Форматирование строки и вывод в переменную строки
OLED_ShowString(3, 5, String); //Отображение влажности
}
}
else
{
err_count++;
OLED_ShowNum(1,14, err_count, 2); //Отображение счетчика количества ошибок
}
/*
https://blog.zeruns.com
*/
Delay_ms(200); //Задержка 200 миллисекунд
IWDG_FeedDog(); //Кормление собаки (сторожевой таймер, автоматический сброс при отсутствии кормления более 1 секунды)
}
}
/**
* @brief Вычисление длины целого числа
* @param num целое число, длину которого нужно вычислить
* @retval значение длины
*/
uint16_t numlen(uint16_t num)
{
uint16_t len = 0; // Начальная длина 0
for(; num > 0; ++len) // Проверка, больше ли num 0, если да, то длина +1
num /= 10; // Использование деления для вычисления, пока num не станет меньше 1
return len; // Возврат значения длины
}
SHT3x.c
#include "stm32f10x.h"
#include "Delay.h"
/*Адрес SHT3x*/
#define SHT3x_ADDRESS 0x44<<1 //Адрес ведомого устройства 7 бит, поэтому сдвиг влево на один бит
/*Установка используемого I2C*/
#define I2Cx I2C1
/*
https://blog.zeruns.com
*/
/**
* @brief Проверка CRC, полином CRC: x^8+x^5+x^4+1, то есть 0x31
* @param DAT данные для проверки
* @retval код проверки
*/
uint8_t SHT3x_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 SHT3x_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 SHT3x_I2C_STOP(){
I2C_GenerateSTOP(I2Cx, ENABLE);//Отправка сигнала остановки
}
/**
* @brief Отправка двух байтов данных
* @param MSB старший 8 бит
* @param LSB младший 8 бит
* @retval нет
*/
void SHT3x_WriteByte(uint8_t MSB,uint8_t LSB)
{
SHT3x_I2C_START(); //Отправка сигнала начала
I2C_Send7bitAddress(I2Cx, SHT3x_ADDRESS, I2C_Direction_Transmitter); //Отправка адреса записи устройства
while(I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)==ERROR); //Обнаружение события EV6
I2C_SendData(I2Cx, MSB);//Отправка старшего 8-битного данных
while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED));//Обнаружение события EV8
I2C_SendData(I2Cx, LSB);//Отправка младшего 8-битного данных
while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED));//Обнаружение события EV8
I2C_GenerateSTOP(I2Cx, ENABLE);//Отправка сигнала остановки
}
/**
* @brief Чтение данных
* @retval прочитанные байтовые данные
*/
uint8_t SHT3x_ReadData()
{
while (!I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_BYTE_RECEIVED));//Обнаружение события EV7
return I2C_ReceiveData(I2Cx);//Чтение и возврат данных
}
/*Программный сброс SHT3x*/
void SHT3x_SoftReset(void)
{
SHT3x_WriteByte(0x30,0xA2); //Переинициализация SHT3x
}
/*Инициализация выводов*/
void SHT3x_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 = 0x30; //Адрес I2C главного устройства, если не используется, можно написать любое значение, это не влияет
I2C_InitStructure.I2C_Ack = I2C_Ack_Enable; //Включение бита подтверждения
I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;//Установка длины адреса 7 бит
I2C_InitStructure.I2C_ClockSpeed = 400000; //Скорость передачи I2C, 400K, проверьте справочник вашего микроконтроллера для поддерживаемых скоростей.
I2C_Init(I2Cx, &I2C_InitStructure); //Инициализация I2C
I2C_Cmd(I2Cx, ENABLE); //Включение I2C
//SHT3x_WriteByte(0X27,0X21); //Режим периодического сбора данных (10 раз в секунду, Medium Repeatability)
Delay_us(300);//Задержка 300 микросекунд
}
/**
* @brief Чтение данных SHT3x
* @param *Hum влажность
* @param *Temp температура
* @retval 1 - чтение успешно; 0 - чтение не удалось
*/
uint8_t ReadSHT3x(float *Hum,float *Temp)
{
uint16_t HumData,TempData,HumCRC,TempCRC;//Объявление переменных для хранения прочитанных данных
//SHT3x_WriteByte(0XE0,0X00); //Отправка команды для получения данных, используется в режиме периодического сбора данных
SHT3x_WriteByte(0X2C,0X0D); //Отправка команды однократного измерения (включение растяжения тактового сигнала, Medium Repeatability)
SHT3x_I2C_START();//Отправка сигнала начала
I2C_Send7bitAddress(I2Cx,SHT3x_ADDRESS,I2C_Direction_Receiver);//Отправка адреса чтения устройства
while( I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED )==ERROR);//Обнаружение события EV6
TempData = SHT3x_ReadData();//Чтение старшего 8-битного данных температуры
TempData=TempData<<8; //Сдвиг влево на 8 бит
TempData |= SHT3x_ReadData();//Чтение младшего 8-битного данных температуры
TempCRC = SHT3x_ReadData(); //Чтение данных проверки CRC температуры
HumData = SHT3x_ReadData(); //Чтение старшего 8-битного данных влажности
HumData=HumData<<8; //Сдвиг влево на 8 бит
HumData |= SHT3x_ReadData();//Чтение младшего 8-битного данных влажности
I2C_AcknowledgeConfig(I2Cx,DISABLE); //Отключение сигнала подтверждения
HumCRC = SHT3x_ReadData(); //Чтение данных проверки CRC влажности
SHT3x_I2C_STOP(); //Отправка сигнала остановки
I2C_AcknowledgeConfig(I2Cx,ENABLE); //Включение сигнала подтверждения
if( SHT3x_CRC_CAL(HumData)==HumCRC && SHT3x_CRC_CAL(TempData)==TempCRC ){ //Проверка CRC полученных данных
*Hum = (float)HumData*100/(65536-1); //Преобразование полученных 16-битных двоичных данных в десятичные данные влажности
*Temp = (float)TempData*175/(65536-1)-45; //Преобразование полученных 16-битных двоичных данных в десятичные данные температуры
return 1;
}
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
/*Аппаратный 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 = 0x30; //Адрес I2C главного устройства, если не используется, можно написать любое значение, это не влияет
I2C_InitStructure.I2C_Ack = I2C_Ack_Enable; //Включение бита подтверждения
I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;//Установка длины адреса 7 бит
I2C_InitStructure.I2C_ClockSpeed = 400000; //Скорость передачи I2C, 400K, проверьте справочник вашего микроконтроллера для поддерживаемых скоростей.
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);
}
}
}
``````c
/**
* @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 Chinese Позиция китайского иероглифа в массиве шрифта
* @retval Нет
*/
void OLED_ShowWord(uint8_t Line, uint8_t Column, uint8_t Chinese)
{
uint8_t i;
OLED_SetCursor((Line - 1) * 2, (Column - 1) * 8);
for (i = 0; i < 8; i++)
{
OLED_WriteData(OLED_F16x16[Chinese][i]);
}
OLED_SetCursor((Line - 1) * 2, (Column - 1) * 8 + 8);
for (i = 1; i < 8; i++)
{
OLED_WriteData(OLED_F16x16[Chinese][i+8]);
}
OLED_SetCursor((Line - 1) * 2 +1, (Column - 1) * 8);
for (i = 0; i < 8; i++)
{
OLED_WriteData(OLED_F16x16[Chinese][i+16]);
}
OLED_SetCursor((Line - 1) * 2 +1, (Column - 1) * 8 + 8);
for (i = 1; i < 8; i++)
{
OLED_WriteData(OLED_F16x16[Chinese][i+16+8]);
}
}
/**
* @brief Отображение строки китайских иероглифов на OLED
* @param Line Позиция строки, диапазон: 1~4
* @param Column Позиция столбца, диапазон: 1~16
* @param Chinese[] Позиция китайских иероглифов в массиве шрифта, массив содержит позицию каждого иероглифа
* @param Length Длина отображаемых китайских иероглифов, диапазон: 1~8
* @retval Нет
*/
void OLED_ShowChinese(uint8_t Line, uint8_t Column, uint8_t *Chinese,uint8_t Length)
{
uint8_t i;
for (i = 0; i < Length; i++)
{
OLED_ShowWord(Line, Column + i*2,Chinese[i]);
}
}
/**
* @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
}
Рекомендуемое чтение
- Рекомендация недорогих 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






