STM32 lee datos del sensor de temperatura y humedad AM2320 mediante I2C por software y los muestra en una pantalla OLED de 0,96 pulgadas.
Utilizo la placa STM32F103C8T6 y el programa está escrito con la biblioteca estándar de ST.
STM32 lee el sensor SHTC3 por I2C hardware: https://blog.zeruns.com/archives/692.html
STM32 lee datos del sensor AHT10: https://blog.zeruns.com/archives/693.html
Grupo de discusión técnica de electrónía/microcontroladores: 2169025065
Resultado visual

Introducción al protocolo I2C
El protocolo de comunicación I2C (Inter-Integrated Circuit) fue desarrollado por Philips. Gracias a su bajo número de pines, implementación hardware sencilla, gran escalabilidad y a que no requiere circuitos externos de transcepción como USART o CAN (esos chips de conversión de niveles), hoy se emplea ampliamente para la comunicación entre múltiples circuitos integrados dentro de un sistema.
I2C solo dispone de un bus de datos SDA (Serial Data Line), por lo que los bits se envían de uno en uno; es una comunicación serie y semidúplex.
Comunicación semidúplex: permite transmisión bidireccional, pero no simultánea; debe alternarse. Se puede ver como una comunicación simplex con dirección conmutable: en un instante solo puede haber una dirección de tráfico y basta con una línea de datos.
Dividimos el protocolo I2C en capa física y capa de protocolo: la física define características mecánicas y eléctricas (hardware), garantizando la transmisión primaria por el medio; la de protocolo regula la lógica de comunicación, unificando los formatos de empaquetado y desempaquetado entre emisor y receptor (software).
Capa física de I2C
Conexión típica entre dispositivos I2C
- Es un bus multi-dispositivo. “Bus” significa que varios dispositivos comparten la misma línea de señales. En un bus I2C pueden conectarse varios maestros y varios esclavos.
- Solo se usan dos líneas: SDA (datos) y SCL (reloj). SDA transporta la información y SCL sincroniza la transmisión.
- El bus se conecta a la alimentación mediante resistencias de pull-up. Cuando un dispositivo I2C está inactivo pasa a estado de alta impedancia; si todos están inactivos, las resistencias de pull-up llevan el bus a nivel alto.
Durante la comunicación I2C los pines GPIO del microcontrolador deben configurarse como salida open-drain; de lo contrario puede producirse un cortocircuito.
Más información sobre I2C en STM32: https://url.zeruns.com/JC0Ah
Aquí no lo detallo.
Sensor AM2320
Introducción
AM2320 es un sensor digital de temperatura y humedad con salida calibrada. Emplea tecnología específica de adquisición para alta fiabilidad y estabilidad a largo plazo. Integra un elemento capacitivo para humedad, un sensor de temperatura de alta precisión y un microprocesador de alto rendimiento. Ofrece excelente calidad, respuesta ultrarrápida, fuerte inmunidad a interferencias y gran relación calidad-precio. Permite comunicación Single-Bus o I2C estándar. La interfaz Single-Bus simplifica la integración; su pequeño tamaño y bajo consumo permiten distancias de más de 20 m. En modo I2C se conecta directamente al bus sin cableado adicional. Ambos modos entregan valores de humedad y temperatura compensados, junto con CRC; no requiere cálculos posteriores ni compensación de humedad respecto a temperatura. El usuario puede cambiar libremente entre modos. Disponible en encapsulado de 4 pines; se ofrecen versiones especiales bajo pedido.
Descarga del datasheet AM2320: https://url.zeruns.com/74o6F
Del datasheet se extrae:
- Rango de temperatura: –40 °C ∼ 80 °C
- Error de temperatura: ±0,5 °C
- Rango de humedad: 0 % ∼ 99,9 %
- Error de humedad: ±3 %
- Voltaje de trabajo: 3,1 V ∼ 5,5 V
- Medios de comunicación: I2C o Single-Bus
- Frecuencia de reloj: ≤ 100 kHz
Dirección del dispositivo y comandos de lectura/escritura
En la práctica, la dirección de AM2320 se combina con el bit de dirección/lectura en un solo byte: los 7 bits superiores son la dirección del dispositivo; el bit 0 indica operación (0 = escritura, 1 = lectura).
Para escribir: tras el START se envía “1011 1000” = 0xB8.
Para leer: se envía “1011 1001” = 0xB9.
En resumen: 0xB8 = escritura, 0xB9 = lectura.
Lectura de datos de temperatura y humedad
Un ciclo de lectura consta de:
- Despertar al sensor
- Enviar orden de lectura
- Leer datos devueltos
Resumen:
- Despertar: START + 0xB8 + espera (> 800 µs) + STOP
- Orden de lectura: START + 0xB8 (SLA) + 0x03 (código función) + 0x00 (dirección inicial) + 0x04 (nº de registros) + STOP
- Recibir datos: enviar 0xB9 y leer 8 bytes: longitud + Hum_H + Hum_L + Temp_H + Temp_L + CRC_L + CRC_H
- Procesar los datos recibidos.
Cálculo de los datos
Según el datasheet:
Ejemplo:
Humedad leída = 0x01F4 → 500 decimal → 500 / 10 = 50,0 %
Temperatura leída = 0x00FA → 250 decimal → 250 / 10 = 25,0 °C
Componentes necesarios
- Placa mínima STM32: [https://s.click.taobao.com/M2LgRPu](https://s.click.taobao.com/t?e=m%3D2%26s%3DIlfejTURsNccQipKwQzePOeEDrYVVa64Dne87AjQPk9yINtkUhsv0F7wsYq2ONVgnPBttjfRAZZwhoUbHqff68jZ753```c
#include “stm32f10x.h” // Device header
#include “Delay.h”
#include “OLED.h”
#include “AM2320.h”
#include “IWDG.h”
int main(void)
{
IWDG_Configuration(); // Inicializar el perro guardian
AM2320_I2C_Init();
OLED_Init();
OLED_ShowString(1, 1, "T:");
OLED_ShowString(2, 1, "H:");
uint16_t i = 0;
uint16_t err_count = 0;
while (1)
{
OLED_ShowNum(4, 1, i, 5);
float Temp, Hum; // Declarar variables para almacenar datos de temperatura y humedad
if (ReadAM2320(&Hum, &Temp)) // Leer datos de temperatura y humedad
{
if (Temp >= 0)
{
char String[10];
sprintf(String, "+%.2fC", Temp); // Formatear cadena de salida a variable string
OLED_ShowString(1, 3, String); // Mostrar temperatura
sprintf(String, " %.2f%%", Hum); // Formatear cadena de salida a variable string
OLED_ShowString(2, 3, String); // Mostrar humedad
}
else
{
char String[10];
sprintf(String, "-%.2fC", Temp); // Formatear cadena de salida a variable string
OLED_ShowString(1, 3, String); // Mostrar temperatura
sprintf(String, " %.2f%%", Hum); // Formatear cadena de salida a variable string
OLED_ShowString(2, 3, String); // Mostrar humedad
}
}
else
{
err_count++;
OLED_ShowNum(3, 1, err_count, 5); // Mostrar contador de errores
}
Delay_ms(100);
i++;
if (i >= 99999)
i = 0;
if (err_count >= 99999)
err_count = 0;
IWDG_FeedDog(); // Alimentar al perro (perro guardian, si no se alimenta en más de 1 segundo se reinicia automáticamente)
}
// blog.zeruns.com
}
### AM2320.c
```c
#include "stm32f10x.h"
#include "Delay.h"
#include "OLED.h"
/*
Autor del blog: https://blog.zeruns.com
WeChat oficial: zeruns-gzh
Página de inicio de Bilibili: https://space.bilibili.com/8320520
*/
/*Dirección AM2320*/
#define AM2320_ADDRESS 0xB8
/*Configuración de pines*/
#define AM2320_SCL GPIO_Pin_12
#define AM2320_SDA GPIO_Pin_13
#define AM2320_W_SCL(x) GPIO_WriteBit(GPIOB, AM2320_SCL, (BitAction)(x))
#define AM2320_W_SDA(x) GPIO_WriteBit(GPIOB, AM2320_SDA, (BitAction)(x))
#define AM2320_R_SDA() GPIO_ReadInputDataBit(GPIOB, AM2320_SDA)
#define AM2320_R_SCL() GPIO_ReadInputDataBit(GPIOB, AM2320_SCL)
/*Cuando el GPIO de STM32 está configurado en modo de salida de drenaje abierto, aún puede leer
el registro de datos de entrada del GPIO para obtener el nivel de entrada externo del pin, es decir, tiene la función del modo de entrada flotante*/
/**
* @brief Cálculo de verificación CRC
* @param *ptr Datos de bytes a calcular (almacenados como variable de array)
* @param len Cantidad de bytes a calcular (longitud del array)
* @retval Código de verificación CRC
*/
unsigned short CRC16(unsigned char *ptr, unsigned char len)
{
unsigned short crc = 0xFFFF;
unsigned char i;
while (len--)
{
crc ^= *ptr++;
for (i = 0; i < 8; i++)
{
if (crc & 0x01)
{
crc >>= 1;
crc ^= 0xA001;
}
else
{
crc >>= 1;
}
}
}
return crc;
}
/**
* @brief Inicio I2C
* @param Ninguno
* @retval Ninguno
*/
void AM2320_I2C_Start(void)
{
AM2320_W_SDA(1);
Delay_us(2); // Retraso de 2 microsegundos
AM2320_W_SCL(1);
Delay_us(4);
AM2320_W_SDA(0);
Delay_us(3);
AM2320_W_SCL(0);
Delay_us(5);
}
/**
* @brief Parada I2C
* @param Ninguno
* @retval Ninguno
*/
void AM2320_I2C_Stop(void)
{
AM2320_W_SDA(0);
Delay_us(3);
AM2320_W_SCL(1);
Delay_us(4);
AM2320_W_SDA(1);
Delay_us(4);
}
/**
* @brief I2C envía un byte
* @param Byte Byte a enviar
* @retval Ninguno
*/
void AM2320_I2C_SendByte(uint8_t Byte)
{
uint8_t i;
for (i = 0; i < 8; i++)
{
AM2320_W_SDA((Byte << i) & 0x80);
AM2320_W_SCL(1);
Delay_us(4);
AM2320_W_SCL(0);
Delay_us(5);
}
AM2320_W_SDA(1); // Liberar bus SDA
}
/**
* @brief Esperar señal de respuesta
* @param Ninguno
* @retval 1-Señal de no respuesta, 0-Señal de respuesta
*/
uint8_t WaitAck(void)
{
uint8_t ret;
AM2320_W_SCL(1);
Delay_us(4);
if (AM2320_R_SDA())
{
ret = 1;
}
else
{
ret = 0;
}
AM2320_W_SCL(0);
Delay_us(5);
return ret;
}
/**
* @brief I2C lee un byte
* @param NACK 1-Señal de no respuesta, 0-Señal de respuesta
* @retval Datos del byte leído
*/
uint8_t AM2320_I2C_ReadByte(uint8_t NACK)
{
uint8_t i, Byte = 0;
AM2320_W_SDA(1); // Liberar bus SDA
for (i = 0; i < 8; i++)
{
AM2320_W_SCL(1);
Delay_us(4);
Byte = Byte | (AM2320_R_SDA() << (7 - i));
AM2320_W_SCL(0);
Delay_us(5);
}
AM2320_W_SDA(NACK); // Enviar señal de respuesta/no respuesta
AM2320_W_SCL(1);
Delay_us(4);
AM2320_W_SCL(0);
Delay_us(5);
AM2320_W_SDA(1); // Liberar bus SDA
return Byte;
}
/*Despertar sensor*/
void AM2320_Wake(void)
{
AM2320_I2C_Start();
AM2320_I2C_SendByte(AM2320_ADDRESS);
WaitAck();
Delay_us(1000); // Retraso de 1000 microsegundos
AM2320_I2C_Stop();
}
/*Inicialización de pines*/
void AM2320_I2C_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); // Habilitar reloj GPIOB
GPIO_InitTypeDef GPIO_InitStructure; // Definir estructura para configurar GPIO
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD; // Modo de salida de drenaje abierto
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Pin = AM2320_SCL;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = AM2320_SDA;
GPIO_Init(GPIOB, &GPIO_InitStructure);
AM2320_W_SCL(1);
AM2320_W_SDA(1);
AM2320_Wake(); // Despertar sensor
}
/**
* @brief Leer datos AM2320
* @param *Hum Humedad
* @param *Temp Temperatura
* @retval 1 - Lectura exitosa; 0 - Lectura fallida
*/
uint8_t ReadAM2320(float *Hum, float *Temp)
{
uint8_t Data[8];
AM2320_I2C_Start(); // Enviar señal de inicio
AM2320_I2C_SendByte(AM2320_ADDRESS);
if (WaitAck()) // Verificar señal de respuesta
{
AM2320_I2C_Stop(); // Enviar señal de parada
Delay_us(50);
// Intentar leer nuevamente
AM2320_I2C_Start(); // Enviar señal de inicio
AM2320_I2C_SendByte(AM2320_ADDRESS);
if (WaitAck()) // Verificar señal de respuesta
{
Delay_us(20);
AM2320_I2C_Stop(); // Enviar señal de parada
return 0;
}
else
{
Delay_us(20);// Aquí AM2320 baja SCL por un tiempo que causa errores en los datos enviados, así que se retrasa 20 microsegundos hasta que AM2320 libere SCL y continúe
AM2320_I2C_SendByte(0x03); // Enviar código de función
WaitAck(); // Esperar señal de respuesta
AM2320_I2C_SendByte(0x00); // Enviar dirección inicial del registro a leer
WaitAck(); // Esperar señal de respuesta
AM2320_I2C_SendByte(0x04); // Enviar longitud del registro a leer
WaitAck(); // Esperar señal de respuesta
Delay_us(20);// Aquí AM2320 baja SCL por un tiempo que causa fallo en el envío de la señal de parada, así que se retrasa 20 microsegundos hasta que AM2320 libere SCL y continúe
AM2320_I2C_Stop(); // Enviar señal de parada
}
}
else
{
Delay_us(20);// Aquí AM2320 baja SCL por un tiempo que causa errores en los datos enviados, así que se retrasa 20 microsegundos hasta que AM2320 libere SCL y continúe
AM2320_I2C_SendByte(0x03); // Enviar código de función
WaitAck(); // Esperar señal de respuesta
AM2320_I2C_SendByte(0x00); // Enviar dirección inicial del registro a leer
WaitAck(); // Esperar señal de respuesta
AM2320_I2C_SendByte(0x04); // Enviar longitud del registro a leer
WaitAck(); // Esperar señal de respuesta
Delay_us(20);// Aquí AM2320 baja SCL por un tiempo que causa fallo en el envío de la señal de parada, así que se retrasa 20 microsegundos hasta que AM2320 libere SCL y continúe
AM2320_I2C_Stop(); // Enviar señal de parada
}
Delay_ms(2); // Retraso de 2 milisegundos
AM2320_I2C_Start();
AM2320_I2C_SendByte(AM2320_ADDRESS | 0x01); // Enviar instrucción de lectura
WaitAck();
Delay_us(35);
uint8_t i;
for (i = 0; i < 8; i++)
{
if (i != 7)
{
Data[i] = AM2320_I2C_ReadByte(0);
}
else
{
Data[i] = AM2320_I2C_ReadByte(1); // Enviar señal de no respuesta al leer el último byte
}
}
AM2320_I2C_Stop();
if (CRC16(Data, 6) == (Data[6] | (Data[7] << 8))) // Verificar datos
{
*Hum = ((((uint16_t)Data[2]) << 8) | Data[3]) / 10.0; // Calcular datos de humedad
if (Data[4] >> 7) // Verificar si el valor de temperatura es negativo
{
*Temp = ((((uint16_t)(Data[4] && 0x7F) << 8)) | Data[5]) / -10.0; // Calcular temperatura negativa
}
else
{
*Temp = ((((uint16_t)Data[4]) << 8) | Data[5]) / 10.0; // Calcular temperatura positiva
}
return 1;
}
return 0;
}
```### OLED.c
```c
#include "stm32f10x.h"
#include "OLED_Font.h"
/*Configuración de pines*/
#define OLED_SCL GPIO_Pin_12
#define OLED_SDA GPIO_Pin_13
#define OLED_W_SCL(x) GPIO_WriteBit(GPIOB, OLED_SCL, (BitAction)(x))
#define OLED_W_SDA(x) GPIO_WriteBit(GPIOB, OLED_SDA, (BitAction)(x))
/*Inicialización de pines*/
void OLED_I2C_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Pin = OLED_SCL;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = OLED_SDA;
GPIO_Init(GPIOB, &GPIO_InitStructure);
OLED_W_SCL(1);
OLED_W_SDA(1);
}
/**
* @brief Inicio I2C
* @param Ninguno
* @retval Ninguno
*/
void OLED_I2C_Start(void)
{
OLED_W_SDA(1);
OLED_W_SCL(1);
OLED_W_SDA(0);
OLED_W_SCL(0);
}
/**
* @brief Parada I2C
* @param Ninguno
* @retval Ninguno
*/
void OLED_I2C_Stop(void)
{
OLED_W_SDA(0);
OLED_W_SCL(1);
OLED_W_SDA(1);
}
/**
* @brief I2C envía un byte
* @param Byte byte a enviar
* @retval Ninguno
*/
void OLED_I2C_SendByte(uint8_t Byte)
{
uint8_t i;
for (i = 0; i < 8; i++)
{
OLED_W_SDA(Byte & (0x80 >> i));
OLED_W_SCL(1);
OLED_W_SCL(0);
}
OLED_W_SDA(1); //liberar el bus SDA
OLED_W_SCL(1); //pulso de reloj adicional, sin procesar señal ACK
OLED_W_SCL(0);
}
/**
* @brief OLED escribe comando
* @param Command comando a escribir
* @retval Ninguno
*/
void OLED_WriteCommand(uint8_t Command)
{
OLED_I2C_Start();
OLED_I2C_SendByte(0x78); //dirección del esclavo
OLED_I2C_SendByte(0x00); //escribir comando
OLED_I2C_SendByte(Command);
OLED_I2C_Stop();
}
/**
* @brief OLED escribe dato
* @param Data dato a escribir
* @retval Ninguno
*/
void OLED_WriteData(uint8_t Data)
{
OLED_I2C_Start();
OLED_I2C_SendByte(0x78); //dirección del esclavo
OLED_I2C_SendByte(0x40); //escribir dato
OLED_I2C_SendByte(Data);
OLED_I2C_Stop();
}
/**
* @brief OLED establece posición del cursor
* @param Y coordenada hacia abajo desde la esquina superior izquierda, rango: 0~7
* @param X coordenada hacia la derecha desde la esquina superior izquierda, rango: 0~127
* @retval Ninguno
*/
void OLED_SetCursor(uint8_t Y, uint8_t X)
{
OLED_WriteCommand(0xB0 | Y); //establecer posición Y
OLED_WriteCommand(0x10 | ((X & 0xF0) >> 4)); //establecer 4 bits bajos de X
OLED_WriteCommand(0x00 | (X & 0x0F)); //establecer 4 bits altos de X
}
/**
* @brief OLED limpia pantalla
* @param Ninguno
* @retval Ninguno
*/
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 limpieza parcial
* @param Line posición de línea, rango: 1~4
* @param start posición inicial de columna, rango: 1~16
* @param end posición final de columna, rango: 1~16
* @retval Ninguno
*/
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); //establecer cursor en mitad superior
for (i = 0; i < 8; i++)
{
OLED_WriteData(0x00); //mostrar mitad superior
}
OLED_SetCursor((Line - 1) * 2 + 1, (Column - 1) * 8); //establecer cursor en mitad inferior
for (i = 0; i < 8; i++)
{
OLED_WriteData(0x00); //mostrar mitad inferior
}
}
}
/**
* @brief OLED muestra un carácter
* @param Line posición de línea, rango: 1~4
* @param Column posición de columna, rango: 1~16
* @param Char carácter a mostrar, rango: caracteres ASCII visibles
* @retval Ninguno
*/
void OLED_ShowChar(uint8_t Line, uint8_t Column, char Char)
{
uint8_t i;
OLED_SetCursor((Line - 1) * 2, (Column - 1) * 8); //establecer cursor en mitad superior
for (i = 0; i < 8; i++)
{
OLED_WriteData(OLED_F8x16[Char - ' '][i]); //mostrar mitad superior
}
OLED_SetCursor((Line - 1) * 2 + 1, (Column - 1) * 8); //establecer cursor en mitad inferior
for (i = 0; i < 8; i++)
{
OLED_WriteData(OLED_F8x16[Char - ' '][i + 8]); //mostrar mitad inferior
}
}
/**
* @brief OLED muestra cadena
* @param Line posición inicial de línea, rango: 1~4
* @param Column posición inicial de columna, rango: 1~16
* @param String cadena a mostrar, rango: caracteres ASCII visibles
* @retval Ninguno
*/
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 Función potencia OLED
* @retval Devuelve X elevado a Y
*/
uint32_t OLED_Pow(uint32_t X, uint32_t Y)
{
uint32_t Result = 1;
while (Y--)
{
Result *= X;
}
return Result;
}
/**
* @brief OLED muestra número (decimal, positivo)
* @param Line posición inicial de línea, rango: 1~4
* @param Column posición inicial de columna, rango: 1~16
* @param Number número a mostrar, rango: 0~4294967295
* @param Length longitud del número a mostrar, rango: 1~10
* @retval Ninguno
*/
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 muestra número (decimal, con signo)
* @param Line posición inicial de línea, rango: 1~4
* @param Column posición inicial de columna, rango: 1~16
* @param Number número a mostrar, rango: -2147483648~2147483647
* @param Length longitud del número a mostrar, rango: 1~10
* @retval Ninguno
*/
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 muestra número (hexadecimal, positivo)
* @param Line posición inicial de línea, rango: 1~4
* @param Column posición inicial de columna, rango: 1~16
* @param Number número a mostrar, rango: 0~0xFFFFFFFF
* @param Length longitud del número a mostrar, rango: 1~8
* @retval Ninguno
*/
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 muestra número (binario, positivo)
* @param Line posición inicial de línea, rango: 1~4
* @param Column posición inicial de columna, rango: 1~16
* @param Number número a mostrar, rango: 0~1111 1111 1111 1111
* @param Length longitud del número a mostrar, rango: 1~16
* @retval Ninguno
*/
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 inicialización
* @param Ninguno
* @retval Ninguno
*/
void OLED_Init(void)
{
uint32_t i, j;
for (i = 0; i < 1000; i++) //retardo al encender
{
for (j = 0; j < 1000; j++)
;
}
OLED_I2C_Init(); //inicializar puertos
OLED_WriteCommand(0xAE); //apagar display
OLED_WriteCommand(0xD5); //establecer divisor de reloj/frecuencia del oscilador
OLED_WriteCommand(0x80);
OLED_WriteCommand(0xA8); //establecer multiplex ratio
OLED_WriteCommand(0x3F);
OLED_WriteCommand(0xD3); //establecer offset de display
OLED_WriteCommand(0x00);
OLED_WriteCommand(0x40); //establecer línea de inicio del display
OLED_WriteCommand(0xA1); //establecer orientación izquierda-derecha, 0xA1 normal 0xA0 invertido
OLED_WriteCommand(0xC8); //establecer orientación arriba-abajo, 0xC8 normal 0xC0 invertido
OLED_WriteCommand(0xDA); //configuración de pines COM
OLED_WriteCommand(0x12);
OLED_WriteCommand(0x81); //establecer control de contraste
OLED_WriteCommand(0xCF);
OLED_WriteCommand(0xD9); //establecer período de precarga
OLED_WriteCommand(0xF1);
OLED_WriteCommand(0xDB); //establecer nivel VCOMH deselect
OLED_WriteCommand(0x30);
OLED_WriteCommand(0xA4); //activar display completo
OLED_WriteCommand(0xA6); //establecer display normal/invertido
OLED_WriteCommand(0x8D); //establecer charge pump
OLED_WriteCommand(0x14);
OLED_WriteCommand(0xAF); //encender display
OLED_Clear(); // limpiar OLED
}
Diagrama de temporización
Diagrama de temporización real del envío de instrucciones y lectura de datos del AM2320.
Recomendaciones de lectura
- Recomendación de VPS/Servidores en la nube de alta relación calidad-precio y baratos: https://blog.vpszj.cn/archives/41.html
- Usar NPS para construir un servidor de penetración de red interna, con panel web: https://blog.vpszj.cn/archives/748.html
- Tutorial de construcción de sitios web en Linux, tutorial de creación de sitios: https://blog.vpszj.cn/archives/1094.html
- Tutorial de construcción de servidor de Minecraft: https://blog.zeruns.com/tag/mc/
- Implementar función de medición de distancia por ultrasonido basada en STM32 y módulo HC-SR04: https://blog.zeruns.com/archives/680.html
- Construcción del entorno de desarrollo ESP8266 y demostración del proyecto: https://blog.zeruns.com/archives/526.html













